From a484f314b719aec5ed7bdc6f08403342b2125561 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Tue, 13 Oct 2026 14:52:57 +0000 Subject: feat(compiler)!: add try-catch with C++ stack unwinding Add TRY/CATCH/ENDTRY and anonymous THROW statements. Use LLVM exception handling and the C++ ABI to propagate exceptions across function calls, clean up catch state, and handle uncaught exceptions at the program boundary. Register exception runtime symbols for the statically linked embedded runner. Verify runtime checks, nested catches, returns, loops, and invalid syntax with smoke programs; exercise unwinding with MCJIT, lli, and native binaries. BREAKING CHANGE: Runtime check failures now throw anonymous exceptions and print only a generic message when uncaught. Native binaries require linking the C++ runtime. Co-Authored-By: Claude Fable 5.1 --- CMakeLists.txt | 1 + include/cps/AST.h | 12 ++++ include/cps/CodeGen.h | 3 + include/cps/ExceptionHandler.h | 48 +++++++++++++++ include/cps/FileHandler.h | 7 +-- include/cps/FunctionGen.h | 6 +- include/cps/Lexer.h | 7 ++- include/cps/Parser.h | 1 + include/cps/RuntimeCheck.h | 28 +++------ lib/CodeGen/CodeGen.cc | 76 ++++++++++++++++++++---- lib/CodeGen/ExceptionHandler.cc | 128 ++++++++++++++++++++++++++++++++++++++++ lib/CodeGen/FileHandler.cc | 51 ++++++---------- lib/CodeGen/FunctionGen.cc | 17 ++++-- lib/CodeGen/RuntimeCheck.cc | 59 ++++-------------- lib/Lexer/Lexer.cc | 5 ++ lib/Parser/Parser.cc | 31 ++++++++++ lib/Parser/ParserFunction.cc | 3 +- tools/driver/EmbeddedRunner.cc | 34 +++++++++++ 18 files changed, 391 insertions(+), 126 deletions(-) create mode 100644 include/cps/ExceptionHandler.h create mode 100644 lib/CodeGen/ExceptionHandler.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index edfe174..6048a9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,7 @@ add_library(CPSCodeGen STATIC lib/CodeGen/Designator.cc lib/CodeGen/ArrayHandler.cc lib/CodeGen/RuntimeCheck.cc + lib/CodeGen/ExceptionHandler.cc lib/CodeGen/FunctionGen.cc lib/CodeGen/ArithmeticHandler.cc lib/CodeGen/StringHandler.cc diff --git a/include/cps/AST.h b/include/cps/AST.h index d98613d..e3c6bc5 100644 --- a/include/cps/AST.h +++ b/include/cps/AST.h @@ -93,6 +93,18 @@ public: virtual ~StmtAST() = default; }; +class TryStmtAST : public StmtAST { + std::vector> Body, CatchBody; +public: + TryStmtAST(std::vector> Body, + std::vector> CatchBody) + : Body(std::move(Body)), CatchBody(std::move(CatchBody)) {} + const std::vector> &getBody() const { return Body; } + const std::vector> &getCatchBody() const { return CatchBody; } +}; + +class ThrowStmtAST : public StmtAST {}; + class DeclareStmtAST : public StmtAST { std::string Name; std::string Type; diff --git a/include/cps/CodeGen.h b/include/cps/CodeGen.h index 2c837fd..acd6cb2 100644 --- a/include/cps/CodeGen.h +++ b/include/cps/CodeGen.h @@ -19,6 +19,7 @@ namespace cps { class ArrayHandler; class FileHandler; +class ExceptionHandler; class CodeGen { std::unique_ptr TheContext; @@ -28,6 +29,7 @@ class CodeGen { std::unique_ptr Types; std::unique_ptr Arrays; + std::unique_ptr Exceptions; std::unique_ptr RuntimeChecker; std::unique_ptr FuncGen; @@ -87,6 +89,7 @@ class CodeGen { void emitOutputValue(llvm::Value *Val, const TypeInfo *TypeInfo, bool AppendNewline = true); void emitIfStmt(IfStmtAST *Stmt); + void emitTryStmt(TryStmtAST *Stmt); void emitWhileStmt(WhileStmtAST *Stmt); void emitRepeatStmt(RepeatStmtAST *Stmt); void emitForStmt(ForStmtAST *Stmt); diff --git a/include/cps/ExceptionHandler.h b/include/cps/ExceptionHandler.h new file mode 100644 index 0000000..5c846ae --- /dev/null +++ b/include/cps/ExceptionHandler.h @@ -0,0 +1,48 @@ +#pragma once +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" +#include +#include + +namespace cps { + +// C++ ABI exception emission. State is keyed by function because file helper +// bodies and user functions are emitted using the same builder as their caller. +class ExceptionHandler { + struct Handler { + llvm::BasicBlock *Target; + llvm::AllocaInst *Exception; + unsigned CatchDepth; + }; + struct FunctionState { + std::vector Handlers; + unsigned CatchDepth = 0; + }; + + llvm::LLVMContext &Context; + llvm::IRBuilder<> &Builder; + llvm::Module &Module; + std::map States; + llvm::FunctionCallee Allocate, Throw, BeginCatch, EndCatch, Personality; + llvm::GlobalVariable *IntTypeInfo = nullptr; + + void ensureRuntime(); + llvm::BasicBlock *emitUnwindDestination(); + +public: + ExceptionHandler(llvm::LLVMContext &C, llvm::IRBuilder<> &B, llvm::Module &M) + : Context(C), Builder(B), Module(M) {} + + void prepareFunction(llvm::Function *F); + void pushHandler(llvm::BasicBlock *Target); + llvm::AllocaInst *popHandler(); + void beginCatch(llvm::AllocaInst *Exception); + void endCatch(); + void emitReturnCleanup(); + + llvm::Value *emitCall(llvm::FunctionCallee Callee, llvm::ArrayRef Args, + const llvm::Twine &Name = ""); + void emitThrow(); +}; + +} // namespace cps diff --git a/include/cps/FileHandler.h b/include/cps/FileHandler.h index 3898a57..8b8ddf4 100644 --- a/include/cps/FileHandler.h +++ b/include/cps/FileHandler.h @@ -7,6 +7,7 @@ namespace cps { class RuntimeCheck; +class ExceptionHandler; // Emits the text-file runtime: a fixed-size open-file table plus internal // __cps_file_* helper functions, synthesized once on first use so programs @@ -20,6 +21,7 @@ class FileHandler { llvm::IRBuilder<> &Builder; llvm::Module &Module; RuntimeCheck &Checker; + ExceptionHandler &Exceptions; static constexpr uint64_t TableSize = 16; bool RuntimeEmitted = false; @@ -35,9 +37,6 @@ class FileHandler { llvm::GlobalVariable *FileModes = nullptr; // [TableSize x i8] 0 = free slot, else FileMode llvm::Value *ModeReadStr = nullptr, *ModeWriteStr = nullptr, *ModeAppendStr = nullptr; - llvm::Value *ErrOpenFail = nullptr, *ErrAlreadyOpen = nullptr, *ErrTableFull = nullptr, - *ErrNotOpen = nullptr, *ErrNotRead = nullptr, *ErrNotWrite = nullptr, - *ErrPastEof = nullptr; // synthesized helpers (internal linkage; pseudocode identifiers cannot // start with '_', so these names are un-collidable) @@ -60,7 +59,7 @@ class FileHandler { public: FileHandler(llvm::LLVMContext &Ctx, llvm::IRBuilder<> &B, llvm::Module &M, - RuntimeCheck &RC); + RuntimeCheck &RC, ExceptionHandler &EH); // NameStr/DataStr are already-coerced STRING ptrs; Line is the statement's // source line for runtime diagnostics. All methods null-check and bail; diff --git a/include/cps/FunctionGen.h b/include/cps/FunctionGen.h index d914162..a7cead5 100644 --- a/include/cps/FunctionGen.h +++ b/include/cps/FunctionGen.h @@ -12,6 +12,8 @@ namespace cps { +class ExceptionHandler; + // Array-parameter ABI: each rank-R array parameter is flattened into the // data pointer followed by kBoundArgsPerDim i64 bounds per dimension. inline constexpr unsigned kBoundArgsPerDim = 2; // lower, upper @@ -51,6 +53,7 @@ private: llvm::Module &Module; llvm::IRBuilder<> &Builder; TypeSystem &Types; + ExceptionHandler &Exceptions; std::map &Symbols; bool &HadError; @@ -65,9 +68,10 @@ public: llvm::Module &M, llvm::IRBuilder<> &B, TypeSystem &TS, + ExceptionHandler &EH, std::map &Sym, bool &HadErrorFlag) - : Context(C), Module(M), Builder(B), Types(TS), Symbols(Sym), HadError(HadErrorFlag) {} + : Context(C), Module(M), Builder(B), Types(TS), Exceptions(EH), Symbols(Sym), HadError(HadErrorFlag) {} llvm::Type *getLLVMType(const std::string &TypeName); diff --git a/include/cps/Lexer.h b/include/cps/Lexer.h index 3652df0..2876a58 100644 --- a/include/cps/Lexer.h +++ b/include/cps/Lexer.h @@ -88,7 +88,12 @@ enum Token { tok_class = -604, tok_endclass = -605, tok_public = -606, - tok_private = -607 + tok_private = -607, + + tok_try = -700, + tok_catch = -701, + tok_endtry = -702, + tok_throw = -703 }; class Lexer { diff --git a/include/cps/Parser.h b/include/cps/Parser.h index 7344015..49a9791 100644 --- a/include/cps/Parser.h +++ b/include/cps/Parser.h @@ -40,6 +40,7 @@ class Parser { std::optional>> ParseExprList(char CloseDelim, bool AllowEmpty); void syncToStatementStart(); std::unique_ptr ParseIfStmt(); + std::unique_ptr ParseTryStmt(); std::unique_ptr ParseWhileStmt(); std::unique_ptr ParseRepeatStmt(); std::unique_ptr ParseForStmt(); diff --git a/include/cps/RuntimeCheck.h b/include/cps/RuntimeCheck.h index 51a07aa..d9a69b6 100644 --- a/include/cps/RuntimeCheck.h +++ b/include/cps/RuntimeCheck.h @@ -7,24 +7,18 @@ namespace cps { +class ExceptionHandler; + class RuntimeCheck { - llvm::Module &TheModule; llvm::LLVMContext &TheContext; llvm::IRBuilder<> &Builder; - - llvm::FunctionCallee PrintfFunc; - llvm::FunctionCallee ExitFunc; - llvm::Value *DivZeroMsg; - llvm::Value *OutOfBoundsMsg; - llvm::Value *EnumRangeMsg; - llvm::Value *NullDerefMsg; - llvm::Value *ArrayArgBoundsMsg; - - void setupExternalFunctions(); - void emitErrorAndExit(llvm::Value *Condition, llvm::Value *Msg, int Line); + ExceptionHandler &Exceptions; public: - RuntimeCheck(llvm::Module &M, llvm::LLVMContext &C, llvm::IRBuilder<> &B); + RuntimeCheck(llvm::LLVMContext &C, llvm::IRBuilder<> &B, ExceptionHandler &EH); + + // Split the block, throwing an anonymous exception when Condition is true. + void emitCheck(llvm::Value *Condition); void emitDivZeroCheck(llvm::Value *Divisor, int Line); void emitIndexCheck(llvm::Value *Index, llvm::Value *Lower, llvm::Value *Upper, int Line); @@ -33,7 +27,7 @@ public: void emitEnumRangeCheck(llvm::Value *Ordinal, uint64_t ValueCount, const std::string &TypeName, int Line); - // Dereferencing an unset (null) pointer dies instead of segfaulting. + // Dereferencing an unset (null) pointer throws instead of segfaulting. void emitNullDerefCheck(llvm::Value *Ptr, int Line); // A bounded array parameter is a contract: the argument's runtime bounds @@ -41,12 +35,6 @@ public: void emitArrayArgBoundsCheck(llvm::Value *ActualLower, llvm::Value *ActualUpper, int64_t DeclaredLower, int64_t DeclaredUpper, int Line); - // Runtime-computed variant: FmtArgs are the printf varargs after Msg - // (e.g. {i32 line, ptr filename}). Needed when the failure site is inside - // a synthesized helper function, where the line number is an argument - // rather than a compile-time constant. - void emitErrorAndExit(llvm::Value *Condition, llvm::Value *Msg, - llvm::ArrayRef FmtArgs); }; } diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index dcf665a..f69fc03 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -2,6 +2,7 @@ #include "cps/ArrayHandler.h" #include "cps/FileAST.h" #include "cps/FileHandler.h" +#include "cps/ExceptionHandler.h" #include "cps/Lexer.h" #include "cps/TypeAST.h" #include "llvm/IR/Instructions.h" @@ -69,7 +70,8 @@ CodeGen::CodeGen() { Builder = std::make_unique>(*TheContext); Types = std::make_unique(*TheContext); - RuntimeChecker = std::make_unique(*TheModule, *TheContext, *Builder); + Exceptions = std::make_unique(*TheContext, *Builder, *TheModule); + RuntimeChecker = std::make_unique(*TheContext, *Builder, *Exceptions); Arrays = std::make_unique(*TheContext, *Builder, @@ -82,6 +84,7 @@ CodeGen::CodeGen() { *TheModule, *Builder, *Types, + *Exceptions, Symbols, HadError); @@ -89,7 +92,8 @@ CodeGen::CodeGen() { *RuntimeChecker); StrHandler = std::make_unique(*TheContext, *Builder, *TheModule); StrConvHandler = std::make_unique(*TheContext, *Builder, *TheModule); - Files = std::make_unique(*TheContext, *Builder, *TheModule, *RuntimeChecker); + Files = std::make_unique(*TheContext, *Builder, *TheModule, *RuntimeChecker, + *Exceptions); // Array parameters are bound into ArrayHandler through this inversion so // FunctionGen never sees the array subsystem. @@ -517,6 +521,10 @@ void CodeGen::compile(const std::vector> &Statements) { BasicBlock *BB = BasicBlock::Create(*TheContext, "entry", F); Builder->SetInsertPoint(BB); + Exceptions->prepareFunction(F); + BasicBlock *UnhandledBB = BasicBlock::Create(*TheContext, "unhandled_exception", F); + Exceptions->pushHandler(UnhandledBB); + // User types first: prototypes may name them in parameters/returns. runTypePrePass(Statements); @@ -536,6 +544,15 @@ void CodeGen::compile(const std::vector> &Statements) { Builder->CreateRet(ConstantInt::get(*TheContext, APInt(32, 0))); } + auto *Exception = Exceptions->popHandler(); + Builder->SetInsertPoint(UnhandledBB); + Exceptions->beginCatch(Exception); + Exceptions->endCatch(); + Value *Msg = Builder->CreateGlobalStringPtr("Unhandled exception. Program terminated.\n", + "err_unhandled", 0, TheModule.get()); + Builder->CreateCall(PrintfFunc, {Msg}); + Builder->CreateRet(ConstantInt::get(Type::getInt32Ty(*TheContext), 1)); + if (verifyModule(*TheModule, &errs())) { errs() << "fatal error: LLVM module verification failed\n"; HadError = true; @@ -849,6 +866,27 @@ bool CodeGen::marshalCallArgs(const std::string &Callee, return true; } +void CodeGen::emitTryStmt(TryStmtAST *Stmt) { + auto *F = Builder->GetInsertBlock()->getParent(); + auto *CatchBB = BasicBlock::Create(*TheContext, "catch", F); + auto *AfterBB = BasicBlock::Create(*TheContext, "try_cont", F); + auto SavedArrays = Arrays->snapshotTable(); + + Exceptions->pushHandler(CatchBB); + for (const auto &S : Stmt->getBody()) emitStmt(S.get()); + auto *Exception = Exceptions->popHandler(); + if (!Builder->GetInsertBlock()->getTerminator()) Builder->CreateBr(AfterBB); + Arrays->exchangeTable(SavedArrays); + + Builder->SetInsertPoint(CatchBB); + Exceptions->beginCatch(Exception); + for (const auto &S : Stmt->getCatchBody()) emitStmt(S.get()); + Exceptions->endCatch(); + if (!Builder->GetInsertBlock()->getTerminator()) Builder->CreateBr(AfterBB); + Arrays->exchangeTable(std::move(SavedArrays)); + Builder->SetInsertPoint(AfterBB); +} + void CodeGen::emitIfStmt(IfStmtAST *Stmt) { Value *CondV = emitCoercedExpr(Stmt->getCond(), resolveType("BOOLEAN")); if (!CondV) return; @@ -1000,16 +1038,6 @@ void CodeGen::emitForStmt(ForStmtAST *Stmt) { void CodeGen::emitStmt(StmtAST *Stmt) { if (!Stmt) return; - if (auto *ArrDecl = dynamic_cast(Stmt)) { - Arrays->emitArrayDeclare(ArrDecl, *this); - return; - } - - if (auto *ArrAssign = dynamic_cast(Stmt)) { - Arrays->emitArrayAssign(ArrAssign, *this); - return; - } - if (auto *FuncDef = dynamic_cast(Stmt)) { BasicBlock *SavedBlock = Builder->GetInsertBlock(); // Function bodies get a fresh array scope (metadata SSA values belong @@ -1025,6 +1053,30 @@ void CodeGen::emitStmt(StmtAST *Stmt) { return; } + // Keep diagnosing statements after RETURN/THROW without appending code + // to a terminated block or making those statements reachable. + if (Builder->GetInsertBlock()->getTerminator()) { + auto *F = Builder->GetInsertBlock()->getParent(); + Builder->SetInsertPoint(BasicBlock::Create(*TheContext, "unreachable_code", F)); + } + + if (auto *Try = dynamic_cast(Stmt)) { + emitTryStmt(Try); + return; + } + if (dynamic_cast(Stmt)) { + Exceptions->emitThrow(); + return; + } + if (auto *ArrDecl = dynamic_cast(Stmt)) { + Arrays->emitArrayDeclare(ArrDecl, *this); + return; + } + if (auto *ArrAssign = dynamic_cast(Stmt)) { + Arrays->emitArrayAssign(ArrAssign, *this); + return; + } + if (auto *Call = dynamic_cast(Stmt)) { std::vector Args; if (!marshalCallArgs(Call->getCallee(), Call->getArgs(), Call->getLine(), Args)) return; diff --git a/lib/CodeGen/ExceptionHandler.cc b/lib/CodeGen/ExceptionHandler.cc new file mode 100644 index 0000000..048f8a6 --- /dev/null +++ b/lib/CodeGen/ExceptionHandler.cc @@ -0,0 +1,128 @@ +#include "cps/ExceptionHandler.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Instructions.h" +#include + +using namespace llvm; +using namespace cps; + +void ExceptionHandler::ensureRuntime() { + if (IntTypeInfo) return; + auto *Ptr = PointerType::getUnqual(Context); + auto *Void = Type::getVoidTy(Context); + Allocate = Module.getOrInsertFunction("__cxa_allocate_exception", + FunctionType::get(Ptr, {Type::getInt64Ty(Context)}, false)); + Throw = Module.getOrInsertFunction("__cxa_throw", + FunctionType::get(Void, {Ptr, Ptr, Ptr}, false)); + BeginCatch = Module.getOrInsertFunction("__cxa_begin_catch", + FunctionType::get(Ptr, {Ptr}, false)); + EndCatch = Module.getOrInsertFunction("__cxa_end_catch", + FunctionType::get(Void, false)); +#if defined(__SEH__) + const char *PersonalityName = "__gxx_personality_seh0"; +#elif defined(__USING_SJLJ_EXCEPTIONS__) + const char *PersonalityName = "__gxx_personality_sj0"; +#else + const char *PersonalityName = "__gxx_personality_v0"; +#endif + Personality = Module.getOrInsertFunction(PersonalityName, + FunctionType::get(Type::getInt32Ty(Context), true)); + IntTypeInfo = new GlobalVariable(Module, Ptr, true, GlobalValue::ExternalLinkage, + nullptr, "_ZTIi"); + cast(Allocate.getCallee())->setDoesNotThrow(); + cast(BeginCatch.getCallee())->setDoesNotThrow(); + // Only anonymous integer exceptions are thrown; there is no throwing + // exception-object destructor to invoke when a catch ends. + cast(EndCatch.getCallee())->setDoesNotThrow(); + cast(Throw.getCallee())->setDoesNotReturn(); +} + +void ExceptionHandler::prepareFunction(Function *F) { + F->setUWTableKind(UWTableKind::Default); +} + +void ExceptionHandler::pushHandler(BasicBlock *Target) { + auto *F = Builder.GetInsertBlock()->getParent(); + IRBuilder<> Entry(&F->getEntryBlock(), F->getEntryBlock().begin()); + auto *Exception = Entry.CreateAlloca(PointerType::getUnqual(Context), nullptr, + "caught_exception"); + auto &State = States[F]; + State.Handlers.push_back({Target, Exception, State.CatchDepth}); +} + +AllocaInst *ExceptionHandler::popHandler() { + auto &Handlers = States[Builder.GetInsertBlock()->getParent()].Handlers; + assert(!Handlers.empty()); + auto *Exception = Handlers.back().Exception; + Handlers.pop_back(); + return Exception; +} + +void ExceptionHandler::beginCatch(AllocaInst *Exception) { + ensureRuntime(); + auto *Ptr = Builder.CreateLoad(PointerType::getUnqual(Context), Exception); + Builder.CreateCall(BeginCatch, {Ptr}); + ++States[Builder.GetInsertBlock()->getParent()].CatchDepth; +} + +void ExceptionHandler::endCatch() { + auto &Depth = States[Builder.GetInsertBlock()->getParent()].CatchDepth; + assert(Depth != 0); + --Depth; + if (!Builder.GetInsertBlock()->getTerminator()) Builder.CreateCall(EndCatch); +} + +void ExceptionHandler::emitReturnCleanup() { + auto &State = States[Builder.GetInsertBlock()->getParent()]; + for (unsigned I = 0; I < State.CatchDepth; ++I) Builder.CreateCall(EndCatch); +} + +BasicBlock *ExceptionHandler::emitUnwindDestination() { + auto *F = Builder.GetInsertBlock()->getParent(); + auto &State = States[F]; + if (State.Handlers.empty() && State.CatchDepth == 0) return nullptr; + ensureRuntime(); + F->setPersonalityFn(cast(Personality.getCallee())); + auto Saved = Builder.saveIP(); + auto *Unwind = BasicBlock::Create(Context, "exception_unwind", F); + Builder.SetInsertPoint(Unwind); + auto *LPType = StructType::get(PointerType::getUnqual(Context), Type::getInt32Ty(Context)); + auto *LP = Builder.CreateLandingPad(LPType, 1, "exception"); + const Handler *Handler = State.Handlers.empty() ? nullptr : &State.Handlers.back(); + unsigned TargetDepth = Handler ? Handler->CatchDepth : 0; + if (Handler) LP->addClause(ConstantPointerNull::get(PointerType::getUnqual(Context))); + if (State.CatchDepth > TargetDepth) LP->setCleanup(true); + // Finish catches we are leaving before entering the outer handler (or + // resuming propagation through a function with no local handler). + for (unsigned I = TargetDepth; I < State.CatchDepth; ++I) Builder.CreateCall(EndCatch); + if (Handler) { + Builder.CreateStore(Builder.CreateExtractValue(LP, 0), Handler->Exception); + Builder.CreateBr(Handler->Target); + } else { + Builder.CreateResume(LP); + } + Builder.restoreIP(Saved); + return Unwind; +} + +Value *ExceptionHandler::emitCall(FunctionCallee Callee, ArrayRef Args, + const Twine &Name) { + auto *Unwind = emitUnwindDestination(); + if (!Unwind) return Builder.CreateCall(Callee, Args, Name); + auto *Cont = BasicBlock::Create(Context, "invoke_cont", Builder.GetInsertBlock()->getParent()); + auto *Call = Builder.CreateInvoke(Callee, Cont, Unwind, Args, Name); + Builder.SetInsertPoint(Cont); + return Call; +} + +void ExceptionHandler::emitThrow() { + ensureRuntime(); + // sizeof(int) is a target-folded null GEP, as for every other allocation. + auto *I32 = Type::getInt32Ty(Context); + auto *Null = ConstantPointerNull::get(PointerType::getUnqual(Context)); + auto *Size = ConstantExpr::getSizeOf(I32); + auto *Object = Builder.CreateCall(Allocate, {Size}, "anonymous_exception"); + Builder.CreateStore(ConstantInt::get(I32, 0), Object); + emitCall(Throw, {Object, IntTypeInfo, Null}); + Builder.CreateUnreachable(); +} diff --git a/lib/CodeGen/FileHandler.cc b/lib/CodeGen/FileHandler.cc index 275139a..66a7606 100644 --- a/lib/CodeGen/FileHandler.cc +++ b/lib/CodeGen/FileHandler.cc @@ -1,5 +1,6 @@ #include "cps/FileHandler.h" #include "cps/RuntimeCheck.h" +#include "cps/ExceptionHandler.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" @@ -9,8 +10,8 @@ using namespace llvm; using namespace cps; FileHandler::FileHandler(LLVMContext &Ctx, IRBuilder<> &B, llvm::Module &M, - RuntimeCheck &RC) - : Context(Ctx), Builder(B), Module(M), Checker(RC) { + RuntimeCheck &RC, ExceptionHandler &EH) + : Context(Ctx), Builder(B), Module(M), Checker(RC), Exceptions(EH) { // Nothing is declared or emitted here: the whole runtime is synthesized // lazily by ensureRuntime() so file-free programs keep their IR unchanged. } @@ -57,21 +58,6 @@ void FileHandler::setupGlobals() { ModeReadStr = Builder.CreateGlobalStringPtr("r", "file_mode_r", 0, &Module); ModeWriteStr = Builder.CreateGlobalStringPtr("w", "file_mode_w", 0, &Module); ModeAppendStr = Builder.CreateGlobalStringPtr("a", "file_mode_a", 0, &Module); - - ErrOpenFail = Builder.CreateGlobalStringPtr( - "[Fatal] line %d: Cannot open file '%s'\n", "err_file_open", 0, &Module); - ErrAlreadyOpen = Builder.CreateGlobalStringPtr( - "[Fatal] line %d: File '%s' is already open\n", "err_file_already_open", 0, &Module); - ErrTableFull = Builder.CreateGlobalStringPtr( - "[Fatal] line %d: Too many open files\n", "err_file_table_full", 0, &Module); - ErrNotOpen = Builder.CreateGlobalStringPtr( - "[Fatal] line %d: File '%s' is not open\n", "err_file_not_open", 0, &Module); - ErrNotRead = Builder.CreateGlobalStringPtr( - "[Fatal] line %d: File '%s' is not open for READ\n", "err_file_not_read", 0, &Module); - ErrNotWrite = Builder.CreateGlobalStringPtr( - "[Fatal] line %d: File '%s' is not open for WRITE\n", "err_file_not_write", 0, &Module); - ErrPastEof = Builder.CreateGlobalStringPtr( - "[Fatal] line %d: Read past end of file '%s'\n", "err_file_past_eof", 0, &Module); } void FileHandler::ensureRuntime() { @@ -97,6 +83,7 @@ void FileHandler::ensureRuntime() { Function *FileHandler::createHelper(const char *Name, FunctionType *FT) { Function *F = Function::Create(FT, Function::InternalLinkage, Name, &Module); + Exceptions.prepareFunction(F); BasicBlock *Entry = BasicBlock::Create(Context, "entry", F); Builder.SetInsertPoint(Entry); return F; @@ -108,11 +95,11 @@ Value *FileHandler::emitSlotGEP(GlobalVariable *Table, Type *ElemTy, Value *Idx) } // Shared prologue of every helper that operates on an already-open file: -// look the name up, die with "not open" on a miss, return the slot index. +// look the name up, throw on a miss, return the slot index. Value *FileHandler::emitLookupOrFail(Value *Name, Value *Line) { Value *Idx = Builder.CreateCall(FindFn, {Name}, "slot"); Value *NotOpen = Builder.CreateICmpSLT(Idx, ConstantInt::get(Context, APInt(64, 0)), "not_open"); - Checker.emitErrorAndExit(NotOpen, ErrNotOpen, {Line, Name}); + Checker.emitCheck(NotOpen); return Idx; } @@ -184,7 +171,7 @@ void FileHandler::buildOpenFn() { Value *Existing = Builder.CreateCall(FindFn, {Name}, "existing"); Value *AlreadyOpen = Builder.CreateICmpSGE(Existing, ConstantInt::get(Context, APInt(64, 0)), "already_open"); - Checker.emitErrorAndExit(AlreadyOpen, ErrAlreadyOpen, {Line, Name}); + Checker.emitCheck(AlreadyOpen); // First-free-slot scan; SlotVar stays -1 when the table is full. AllocaInst *IdxVar = Builder.CreateAlloca(I64, nullptr, "idx"); @@ -221,7 +208,7 @@ void FileHandler::buildOpenFn() { Builder.SetInsertPoint(AfterBB); Value *Slot = Builder.CreateLoad(I64, SlotVar, "free_slot"); Value *NoFree = Builder.CreateICmpSLT(Slot, ConstantInt::get(Context, APInt(64, 0)), "table_full"); - Checker.emitErrorAndExit(NoFree, ErrTableFull, {Line}); + Checker.emitCheck(NoFree); // Branch-free fopen-mode selection: 1 -> "r", 2 -> "w", 3 -> "a". Value *IsWrite = Builder.CreateICmpEQ(Mode, ConstantInt::get(I8, uint8_t(FileMode::Write))); @@ -231,7 +218,7 @@ void FileHandler::buildOpenFn() { Value *Handle = Builder.CreateCall(FopenFunc, {Name, ModeStr}, "handle"); Value *OpenFailed = Builder.CreateICmpEQ(Handle, ConstantPointerNull::get(PtrTy), "open_failed"); - Checker.emitErrorAndExit(OpenFailed, ErrOpenFail, {Line, Name}); + Checker.emitCheck(OpenFailed); Builder.CreateStore(Name, emitSlotGEP(FileNames, PtrTy, Slot)); Builder.CreateStore(Handle, emitSlotGEP(FileHandles, PtrTy, Slot)); @@ -241,7 +228,7 @@ void FileHandler::buildOpenFn() { // ptr __cps_file_read(ptr name, i32 line): read one line into a fresh heap // STRING ('\n' consumed and stripped, trailing '\r' dropped). Reading at EOF -// is a fatal error — CIE programs are expected to guard with EOF(). +// throws an exception — CIE programs are expected to guard with EOF(). void FileHandler::buildReadFn() { PointerType *PtrTy = PointerType::getUnqual(Context); Type *I64 = Type::getInt64Ty(Context); @@ -257,13 +244,13 @@ void FileHandler::buildReadFn() { Value *Idx = emitLookupOrFail(Name, Line); Value *Mode = Builder.CreateLoad(I8, emitSlotGEP(FileModes, I8, Idx), "mode"); Value *NotRead = Builder.CreateICmpNE(Mode, ConstantInt::get(I8, uint8_t(FileMode::Read)), "not_read"); - Checker.emitErrorAndExit(NotRead, ErrNotRead, {Line, Name}); + Checker.emitCheck(NotRead); Value *Handle = Builder.CreateLoad(PtrTy, emitSlotGEP(FileHandles, PtrTy, Idx), "handle"); Value *MinusOne = ConstantInt::get(Context, APInt(32, -1, /*isSigned=*/true)); Value *Peek = Builder.CreateCall(FgetcFunc, {Handle}, "peek"); Value *AtEof = Builder.CreateICmpEQ(Peek, MinusOne, "at_eof"); - Checker.emitErrorAndExit(AtEof, ErrPastEof, {Line, Name}); + Checker.emitCheck(AtEof); Builder.CreateCall(UngetcFunc, {Peek, Handle}); // Growing buffer: cap doubles, always leaving room for byte + NUL. @@ -361,7 +348,7 @@ void FileHandler::buildWriteFn() { Value *Idx = emitLookupOrFail(Name, Line); Value *Mode = Builder.CreateLoad(I8, emitSlotGEP(FileModes, I8, Idx), "mode"); Value *IsReadMode = Builder.CreateICmpEQ(Mode, ConstantInt::get(I8, uint8_t(FileMode::Read)), "read_only"); - Checker.emitErrorAndExit(IsReadMode, ErrNotWrite, {Line, Name}); + Checker.emitCheck(IsReadMode); Value *Handle = Builder.CreateLoad(PtrTy, emitSlotGEP(FileHandles, PtrTy, Idx), "handle"); Builder.CreateCall(FputsFunc, {Data, Handle}); @@ -407,7 +394,7 @@ void FileHandler::buildEofFn() { Value *Idx = emitLookupOrFail(Name, Line); Value *Mode = Builder.CreateLoad(I8, emitSlotGEP(FileModes, I8, Idx), "mode"); Value *NotRead = Builder.CreateICmpNE(Mode, ConstantInt::get(I8, uint8_t(FileMode::Read)), "not_read"); - Checker.emitErrorAndExit(NotRead, ErrNotRead, {Line, Name}); + Checker.emitCheck(NotRead); Value *Handle = Builder.CreateLoad(PtrTy, emitSlotGEP(FileHandles, PtrTy, Idx), "handle"); Value *Ch = Builder.CreateCall(FgetcFunc, {Handle}, "c"); @@ -420,7 +407,7 @@ void FileHandler::buildEofFn() { void FileHandler::emitOpen(Value *NameStr, FileMode Mode, int Line) { if (!NameStr) return; ensureRuntime(); - Builder.CreateCall(OpenFn, {NameStr, + Exceptions.emitCall(OpenFn, {NameStr, ConstantInt::get(Type::getInt8Ty(Context), uint8_t(Mode)), ConstantInt::get(Context, APInt(32, Line))}); } @@ -428,25 +415,25 @@ void FileHandler::emitOpen(Value *NameStr, FileMode Mode, int Line) { Value *FileHandler::emitRead(Value *NameStr, int Line) { if (!NameStr) return nullptr; ensureRuntime(); - return Builder.CreateCall(ReadFn, {NameStr, ConstantInt::get(Context, APInt(32, Line))}, + return Exceptions.emitCall(ReadFn, {NameStr, ConstantInt::get(Context, APInt(32, Line))}, "file_line"); } void FileHandler::emitWrite(Value *NameStr, Value *DataStr, int Line) { if (!NameStr || !DataStr) return; ensureRuntime(); - Builder.CreateCall(WriteFn, {NameStr, DataStr, ConstantInt::get(Context, APInt(32, Line))}); + Exceptions.emitCall(WriteFn, {NameStr, DataStr, ConstantInt::get(Context, APInt(32, Line))}); } void FileHandler::emitClose(Value *NameStr, int Line) { if (!NameStr) return; ensureRuntime(); - Builder.CreateCall(CloseFn, {NameStr, ConstantInt::get(Context, APInt(32, Line))}); + Exceptions.emitCall(CloseFn, {NameStr, ConstantInt::get(Context, APInt(32, Line))}); } Value *FileHandler::emitEof(Value *NameStr, int Line) { if (!NameStr) return nullptr; ensureRuntime(); - return Builder.CreateCall(EofFn, {NameStr, ConstantInt::get(Context, APInt(32, Line))}, + return Exceptions.emitCall(EofFn, {NameStr, ConstantInt::get(Context, APInt(32, Line))}, "file_eof"); } diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc index 2589797..afa6074 100644 --- a/lib/CodeGen/FunctionGen.cc +++ b/lib/CodeGen/FunctionGen.cc @@ -1,5 +1,6 @@ #include "cps/FunctionGen.h" #include "cps/CodeGen.h" +#include "cps/ExceptionHandler.h" #include using namespace llvm; @@ -13,7 +14,9 @@ static bool isReservedRuntimeName(const std::string &Name) { "strcpy", "strcat", "toupper", "tolower", "sprintf", "strtol", "strtod", "memset", "exit", "fopen", "fclose", "fgetc", "ungetc", "fputs", "fputc", - "realloc", "strcmp", + "realloc", "strcmp", "__cxa_allocate_exception", "__cxa_throw", + "__cxa_begin_catch", "__cxa_end_catch", "__gxx_personality_v0", + "__gxx_personality_seh0", "__gxx_personality_sj0", "_Unwind_Resume", "_ZTIi", }; for (const char *R : Reserved) { if (Name == R) return true; @@ -149,6 +152,7 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) { if (!F) { F = Function::Create(FT, Function::ExternalLinkage, Proto->getName(), &Module); } + Exceptions.prepareFunction(F); // Keep the first registration: on a duplicate definition the first one // wins and the second is rejected by emitFunctionDef. @@ -204,7 +208,7 @@ Function *FunctionGen::emitFunctionDef(FunctionDefAST *FuncAST, } static Value *GenerateCall(llvm::Module &Module, - llvm::IRBuilder<> &Builder, + ExceptionHandler &Exceptions, const std::string &CalleeName, const std::vector &Args, bool &HadError) { @@ -216,21 +220,22 @@ static Value *GenerateCall(llvm::Module &Module, } if (CalleeF->getReturnType()->isVoidTy()) { - return Builder.CreateCall(CalleeF, Args); + return Exceptions.emitCall(CalleeF, Args); } - return Builder.CreateCall(CalleeF, Args, "calltmp"); + return Exceptions.emitCall(CalleeF, Args, "calltmp"); } llvm::Value *FunctionGen::emitCallExpr(CallExprAST *Call, const std::vector &Args) { - return GenerateCall(Module, Builder, Call->getCallee(), Args, HadError); + return GenerateCall(Module, Exceptions, Call->getCallee(), Args, HadError); } void FunctionGen::emitCallStmt(CallStmtAST *Call, const std::vector &Args) { - GenerateCall(Module, Builder, Call->getCallee(), Args, HadError); + GenerateCall(Module, Exceptions, Call->getCallee(), Args, HadError); } void FunctionGen::emitReturn(ReturnStmtAST *Ret, llvm::Value *RetVal) { + Exceptions.emitReturnCleanup(); if (RetVal) { Builder.CreateRet(RetVal); } else { diff --git a/lib/CodeGen/RuntimeCheck.cc b/lib/CodeGen/RuntimeCheck.cc index 8075873..ee270e5 100644 --- a/lib/CodeGen/RuntimeCheck.cc +++ b/lib/CodeGen/RuntimeCheck.cc @@ -1,4 +1,5 @@ #include "cps/RuntimeCheck.h" +#include "cps/ExceptionHandler.h" #include "cps/TypeSystem.h" // kEnumOrdinalBase #include "llvm/IR/Function.h" #include "llvm/IR/Type.h" @@ -7,52 +8,16 @@ using namespace llvm; using namespace cps; -RuntimeCheck::RuntimeCheck(Module &M, LLVMContext &C, IRBuilder<> &B) - : TheModule(M), TheContext(C), Builder(B) { - setupExternalFunctions(); -} - -void RuntimeCheck::setupExternalFunctions() { - std::vector PrintfArgs; - PrintfArgs.push_back(PointerType::getUnqual(TheContext)); - FunctionType *PrintfType = FunctionType::get(Type::getInt32Ty(TheContext), PrintfArgs, true); - PrintfFunc = TheModule.getOrInsertFunction("printf", PrintfType); - - std::vector ExitArgs; - ExitArgs.push_back(Type::getInt32Ty(TheContext)); - FunctionType *ExitType = FunctionType::get(Type::getVoidTy(TheContext), ExitArgs, false); - ExitFunc = TheModule.getOrInsertFunction("exit", ExitType); - - DivZeroMsg = Builder.CreateGlobalStringPtr("[Fatal] line %d: Division by zero\n", "err_div_zero", 0, &TheModule); - OutOfBoundsMsg = Builder.CreateGlobalStringPtr("[Fatal] line %d: Array index out of bounds\n", "err_bounds", 0, &TheModule); - EnumRangeMsg = Builder.CreateGlobalStringPtr("[Fatal] line %d: Value out of range for enum %s\n", "err_enum_range", 0, &TheModule); - NullDerefMsg = Builder.CreateGlobalStringPtr("[Fatal] line %d: Dereference of an unset pointer\n", "err_null_deref", 0, &TheModule); - ArrayArgBoundsMsg = Builder.CreateGlobalStringPtr( - "[Fatal] line %d: Array argument bounds [%lld:%lld] do not match the declared parameter bounds [%lld:%lld]\n", - "err_arr_arg_bounds", 0, &TheModule); -} - -void RuntimeCheck::emitErrorAndExit(Value *Condition, Value *Msg, int Line) { - emitErrorAndExit(Condition, Msg, {ConstantInt::get(TheContext, APInt(32, Line))}); -} +RuntimeCheck::RuntimeCheck(LLVMContext &C, IRBuilder<> &B, ExceptionHandler &EH) + : TheContext(C), Builder(B), Exceptions(EH) {} -void RuntimeCheck::emitErrorAndExit(Value *Condition, Value *Msg, ArrayRef FmtArgs) { +void RuntimeCheck::emitCheck(Value *Condition) { Function *TheFunction = Builder.GetInsertBlock()->getParent(); - BasicBlock *FailBB = BasicBlock::Create(TheContext, "check_fail", TheFunction); BasicBlock *ContBB = BasicBlock::Create(TheContext, "check_cont", TheFunction); - Builder.CreateCondBr(Condition, FailBB, ContBB); - Builder.SetInsertPoint(FailBB); - std::vector PrintArgs; - PrintArgs.push_back(Msg); - PrintArgs.insert(PrintArgs.end(), FmtArgs.begin(), FmtArgs.end()); - Builder.CreateCall(PrintfFunc, PrintArgs); - - Builder.CreateCall(ExitFunc, ConstantInt::get(TheContext, APInt(32, 1))); - Builder.CreateUnreachable(); - + Exceptions.emitThrow(); Builder.SetInsertPoint(ContBB); } @@ -63,7 +28,7 @@ void RuntimeCheck::emitDivZeroCheck(Value *Divisor, int Line) { } else { IsZero = Builder.CreateICmpEQ(Divisor, ConstantInt::get(Divisor->getType(), 0), "is_zero"); } - emitErrorAndExit(IsZero, DivZeroMsg, Line); + emitCheck(IsZero); } void RuntimeCheck::emitIndexCheck(Value *Index, Value *Lower, Value *Upper, int Line) { @@ -71,7 +36,7 @@ void RuntimeCheck::emitIndexCheck(Value *Index, Value *Lower, Value *Upper, int Value *TooHigh = Builder.CreateICmpSGT(Index, Upper, "too_high"); Value *OutOfBounds = Builder.CreateOr(TooLow, TooHigh, "out_of_bounds"); - emitErrorAndExit(OutOfBounds, OutOfBoundsMsg, Line); + emitCheck(OutOfBounds); } void RuntimeCheck::emitEnumRangeCheck(Value *Ordinal, uint64_t ValueCount, @@ -83,16 +48,14 @@ void RuntimeCheck::emitEnumRangeCheck(Value *Ordinal, uint64_t ValueCount, Value *TooHigh = Builder.CreateICmpSGE(Ordinal, Hi, "enum_too_high"); Value *OutOfRange = Builder.CreateOr(TooLow, TooHigh, "enum_out_of_range"); - Value *NameStr = Builder.CreateGlobalStringPtr(TypeName, "enum_name", 0, &TheModule); - emitErrorAndExit(OutOfRange, EnumRangeMsg, - {ConstantInt::get(TheContext, APInt(32, Line)), NameStr}); + emitCheck(OutOfRange); } void RuntimeCheck::emitNullDerefCheck(Value *Ptr, int Line) { Value *IsNull = Builder.CreateICmpEQ(Ptr, ConstantPointerNull::get(PointerType::getUnqual(TheContext)), "ptr_is_null"); - emitErrorAndExit(IsNull, NullDerefMsg, Line); + emitCheck(IsNull); } void RuntimeCheck::emitArrayArgBoundsCheck(Value *ActualLower, Value *ActualUpper, @@ -104,7 +67,5 @@ void RuntimeCheck::emitArrayArgBoundsCheck(Value *ActualLower, Value *ActualUppe Value *HiDiff = Builder.CreateICmpNE(ActualUpper, DeclHi, "arg_hi_ne"); Value *Mismatch = Builder.CreateOr(LoDiff, HiDiff, "arg_bounds_ne"); - emitErrorAndExit(Mismatch, ArrayArgBoundsMsg, - {ConstantInt::get(TheContext, APInt(32, Line)), - ActualLower, ActualUpper, DeclLo, DeclHi}); + emitCheck(Mismatch); } diff --git a/lib/Lexer/Lexer.cc b/lib/Lexer/Lexer.cc index a71b37c..d366afc 100644 --- a/lib/Lexer/Lexer.cc +++ b/lib/Lexer/Lexer.cc @@ -86,6 +86,11 @@ int Lexer::gettok() { if (IdentifierStr == "ELSE") return tok_else; if (IdentifierStr == "ENDIF") return tok_endif; + if (IdentifierStr == "TRY") return tok_try; + if (IdentifierStr == "CATCH") return tok_catch; + if (IdentifierStr == "ENDTRY") return tok_endtry; + if (IdentifierStr == "THROW") return tok_throw; + if (IdentifierStr == "WHILE") return tok_while; if (IdentifierStr == "DO") return tok_do; if (IdentifierStr == "ENDWHILE") return tok_endwhile; diff --git a/lib/Parser/Parser.cc b/lib/Parser/Parser.cc index 098ef82..aeb81fe 100644 --- a/lib/Parser/Parser.cc +++ b/lib/Parser/Parser.cc @@ -276,6 +276,7 @@ void Parser::syncToStatementStart() { case tok_openfile: case tok_readfile: case tok_writefile: case tok_closefile: case tok_type: case tok_define: case tok_endtype: case tok_class: case tok_endclass: + case tok_try: case tok_catch: case tok_endtry: case tok_throw: return; default: getNextToken(); @@ -399,6 +400,23 @@ std::unique_ptr Parser::ParseDeclare() { } } +std::unique_ptr Parser::ParseTryStmt() { + getNextToken(); + auto Body = ParseBlock({tok_catch, tok_endtry}); + if (CurTok != tok_catch) { + fprintf(stderr, "error: Expected CATCH in TRY at line %d\n", Lex.getLine()); + return nullptr; + } + getNextToken(); + auto CatchBody = ParseBlock({tok_endtry, tok_catch}); + if (CurTok != tok_endtry) { + fprintf(stderr, "error: Expected ENDTRY after CATCH at line %d\n", Lex.getLine()); + return nullptr; + } + getNextToken(); + return std::make_unique(std::move(Body), std::move(CatchBody)); +} + std::unique_ptr Parser::ParseIfStmt() { getNextToken(); auto Cond = ParseExpression(); @@ -569,6 +587,19 @@ std::unique_ptr Parser::ParseStatementImpl() { else if (CurTok == tok_if) { return ParseIfStmt(); } + else if (CurTok == tok_try) { + return ParseTryStmt(); + } + else if (CurTok == tok_throw) { + getNextToken(); + return std::make_unique(); + } + else if (CurTok == tok_catch || CurTok == tok_endtry) { + fprintf(stderr, "error: Unexpected %s without matching TRY at line %d\n", + CurTok == tok_catch ? "CATCH" : "ENDTRY", Lex.getLine()); + getNextToken(); + return nullptr; + } else if (CurTok == tok_while) { return ParseWhileStmt(); } diff --git a/lib/Parser/ParserFunction.cc b/lib/Parser/ParserFunction.cc index f4e39de..25375f2 100644 --- a/lib/Parser/ParserFunction.cc +++ b/lib/Parser/ParserFunction.cc @@ -224,7 +224,8 @@ std::unique_ptr Parser::ParseReturnStmt() { // A bare RETURN is recognized right before any block terminator. if (CurTok != tok_endif && CurTok != tok_else && CurTok != tok_endfunction && CurTok != tok_endprocedure && CurTok != tok_endwhile && CurTok != tok_until && - CurTok != tok_next && CurTok != tok_eof) { + CurTok != tok_next && CurTok != tok_catch && CurTok != tok_endtry && + CurTok != tok_eof) { Expr = ParseExpression(); if (!Expr) return nullptr; } diff --git a/tools/driver/EmbeddedRunner.cc b/tools/driver/EmbeddedRunner.cc index 83489b1..56e3195 100644 --- a/tools/driver/EmbeddedRunner.cc +++ b/tools/driver/EmbeddedRunner.cc @@ -7,6 +7,39 @@ #include "llvm/Support/TargetSelect.h" #include "llvm/TargetParser/Host.h" #include "llvm/Transforms/Utils/Cloning.h" +#include +#include +#include + +// Static C++ runtime symbols are not available through dlsym. Register their +// actual addresses so the JIT and its unwind tables use the host's runtime. +extern "C" int +#if defined(__SEH__) +__gxx_personality_seh0 +#elif defined(__USING_SJLJ_EXCEPTIONS__) +__gxx_personality_sj0 +#else +__gxx_personality_v0 +#endif +(int, _Unwind_Action, _Unwind_Exception_Class, _Unwind_Exception *, _Unwind_Context *); + +static void registerExceptionSymbols() { + using llvm::sys::DynamicLibrary; + DynamicLibrary::AddSymbol("__cxa_allocate_exception", reinterpret_cast(&__cxxabiv1::__cxa_allocate_exception)); + DynamicLibrary::AddSymbol("__cxa_throw", reinterpret_cast(&__cxxabiv1::__cxa_throw)); + DynamicLibrary::AddSymbol("__cxa_begin_catch", reinterpret_cast(&__cxxabiv1::__cxa_begin_catch)); + DynamicLibrary::AddSymbol("__cxa_end_catch", reinterpret_cast(&__cxxabiv1::__cxa_end_catch)); + DynamicLibrary::AddSymbol("_ZTIi", const_cast(&typeid(int))); +#if defined(__SEH__) + DynamicLibrary::AddSymbol("__gxx_personality_seh0", reinterpret_cast(&__gxx_personality_seh0)); +#elif defined(__USING_SJLJ_EXCEPTIONS__) + DynamicLibrary::AddSymbol("__gxx_personality_sj0", reinterpret_cast(&__gxx_personality_sj0)); + DynamicLibrary::AddSymbol("_Unwind_SjLj_Resume", reinterpret_cast(&_Unwind_SjLj_Resume)); +#else + DynamicLibrary::AddSymbol("__gxx_personality_v0", reinterpret_cast(&__gxx_personality_v0)); +#endif + DynamicLibrary::AddSymbol("_Unwind_Resume", reinterpret_cast(&_Unwind_Resume)); +} int runEmbedded(const cps::CodeGen &CG) { if (llvm::InitializeNativeTarget() || llvm::InitializeNativeTargetAsmPrinter()) { @@ -18,6 +51,7 @@ int runEmbedded(const cps::CodeGen &CG) { llvm::errs() << "error: cannot load process symbols: " << Error << "\n"; return 1; } + registerExceptionSymbols(); // Keep CodeGen's module and context alive while the engine owns its clone. auto Module = llvm::CloneModule(CG.getModule()); Module->setTargetTriple(llvm::sys::getProcessTriple()); -- cgit v1.3.1-4-g156e