diff options
Diffstat (limited to 'lib/CodeGen')
| -rw-r--r-- | lib/CodeGen/CodeGen.cc | 76 | ||||
| -rw-r--r-- | lib/CodeGen/ExceptionHandler.cc | 128 | ||||
| -rw-r--r-- | lib/CodeGen/FileHandler.cc | 51 | ||||
| -rw-r--r-- | lib/CodeGen/FunctionGen.cc | 17 | ||||
| -rw-r--r-- | lib/CodeGen/RuntimeCheck.cc | 59 |
5 files changed, 232 insertions, 99 deletions
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<IRBuilder<>>(*TheContext); Types = std::make_unique<TypeSystem>(*TheContext); - RuntimeChecker = std::make_unique<RuntimeCheck>(*TheModule, *TheContext, *Builder); + Exceptions = std::make_unique<ExceptionHandler>(*TheContext, *Builder, *TheModule); + RuntimeChecker = std::make_unique<RuntimeCheck>(*TheContext, *Builder, *Exceptions); Arrays = std::make_unique<ArrayHandler>(*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<StringHandler>(*TheContext, *Builder, *TheModule); StrConvHandler = std::make_unique<StringConversionHandler>(*TheContext, *Builder, *TheModule); - Files = std::make_unique<FileHandler>(*TheContext, *Builder, *TheModule, *RuntimeChecker); + Files = std::make_unique<FileHandler>(*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<std::unique_ptr<StmtAST>> &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<std::unique_ptr<StmtAST>> &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<ArrayDeclareStmtAST*>(Stmt)) { - Arrays->emitArrayDeclare(ArrDecl, *this); - return; - } - - if (auto *ArrAssign = dynamic_cast<ArrayAssignStmtAST*>(Stmt)) { - Arrays->emitArrayAssign(ArrAssign, *this); - return; - } - if (auto *FuncDef = dynamic_cast<FunctionDefAST*>(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<TryStmtAST*>(Stmt)) { + emitTryStmt(Try); + return; + } + if (dynamic_cast<ThrowStmtAST*>(Stmt)) { + Exceptions->emitThrow(); + return; + } + if (auto *ArrDecl = dynamic_cast<ArrayDeclareStmtAST*>(Stmt)) { + Arrays->emitArrayDeclare(ArrDecl, *this); + return; + } + if (auto *ArrAssign = dynamic_cast<ArrayAssignStmtAST*>(Stmt)) { + Arrays->emitArrayAssign(ArrAssign, *this); + return; + } + if (auto *Call = dynamic_cast<CallStmtAST*>(Stmt)) { std::vector<Value*> 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 <cassert> + +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<Function>(Allocate.getCallee())->setDoesNotThrow(); + cast<Function>(BeginCatch.getCallee())->setDoesNotThrow(); + // Only anonymous integer exceptions are thrown; there is no throwing + // exception-object destructor to invoke when a catch ends. + cast<Function>(EndCatch.getCallee())->setDoesNotThrow(); + cast<Function>(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<Constant>(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<Value *> 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 <cstdio> 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<llvm::Value*> &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<llvm::Value*> &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<llvm::Value*> &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<Type*> PrintfArgs; - PrintfArgs.push_back(PointerType::getUnqual(TheContext)); - FunctionType *PrintfType = FunctionType::get(Type::getInt32Ty(TheContext), PrintfArgs, true); - PrintfFunc = TheModule.getOrInsertFunction("printf", PrintfType); - - std::vector<Type*> 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<Value *> 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<Value*> 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); } |
