diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-13 14:52:57 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-13 14:52:57 +0000 |
| commit | a484f314b719aec5ed7bdc6f08403342b2125561 (patch) | |
| tree | 531e331cf823517eae4c350d37bca6a500b2de9f /lib/CodeGen/RuntimeCheck.cc | |
| parent | 4297086c86f4d11e8a14b76b50705b419d6b7f13 (diff) | |
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 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen/RuntimeCheck.cc')
| -rw-r--r-- | lib/CodeGen/RuntimeCheck.cc | 59 |
1 files changed, 10 insertions, 49 deletions
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); } |
