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 --- lib/CodeGen/FunctionGen.cc | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'lib/CodeGen/FunctionGen.cc') 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 { -- cgit v1.3.1-4-g156e