aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen
diff options
context:
space:
mode:
Diffstat (limited to 'lib/CodeGen')
-rw-r--r--lib/CodeGen/CodeGen.cc76
-rw-r--r--lib/CodeGen/ExceptionHandler.cc128
-rw-r--r--lib/CodeGen/FileHandler.cc51
-rw-r--r--lib/CodeGen/FunctionGen.cc17
-rw-r--r--lib/CodeGen/RuntimeCheck.cc59
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);
}