From 6a3b31af5dbb22749e6d5a1cad9dfaab6863b615 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sat, 17 Oct 2026 08:36:19 +0000 Subject: feat(classes): destroy stack objects during exception unwinding Co-Authored-By: Claude Opus 5.5 --- lib/CodeGen/CodeGen.cc | 2 +- lib/CodeGen/ConstructorGen.cc | 16 +++----------- lib/CodeGen/ExceptionHandler.cc | 46 ++++++++++++++++++++++++++++++++++++----- lib/CodeGen/FunctionGen.cc | 14 ++++++------- 4 files changed, 51 insertions(+), 27 deletions(-) (limited to 'lib/CodeGen') diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index 8117434..0107910 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -186,7 +186,7 @@ void CodeGen::compile(const std::vector> &Statements) { } if (!Builder->GetInsertBlock()->hasTerminator()) { - Builder->CreateRet(ConstantInt::get(*TheContext, APInt(32, 0))); + Exceptions->recordExit(Builder->CreateRet(ConstantInt::get(*TheContext, APInt(32, 0)))); } finishStackObjects(F); diff --git a/lib/CodeGen/ConstructorGen.cc b/lib/CodeGen/ConstructorGen.cc index c0e50ac..e1033c3 100644 --- a/lib/CodeGen/ConstructorGen.cc +++ b/lib/CodeGen/ConstructorGen.cc @@ -1,5 +1,6 @@ #include "cps/CodeGen.h" #include "cps/MemoryHandler.h" +#include "cps/ExceptionHandler.h" #include "cps/TypeAST.h" #include #include @@ -168,19 +169,8 @@ void CodeGen::emitStackObjectCleanup(const StackObject &Object) { } void CodeGen::finishStackObjects(Function *F) { - IRBuilderBase::InsertPointGuard Guard(*Builder); - std::vector Exits; - for (auto &BB : *F) - if (BB.hasTerminator() && isa(BB.getTerminator())) - Exits.push_back(BB.getTerminator()); - for (auto *Exit : Exits) { - // Keep the original terminator in a continuation while cleanup builds branches. - auto *BB = Exit->getParent(); - auto *Continuation = BB->splitBasicBlock(Exit->getIterator(), "after_destruction"); - BB->getTerminator()->eraseFromParent(); - Builder->SetInsertPoint(BB); + Exceptions->fillCleanups(F, [this] { for (auto It = StackObjects.rbegin(); It != StackObjects.rend(); ++It) emitStackObjectCleanup(*It); - Builder->CreateBr(Continuation); - } + }); } diff --git a/lib/CodeGen/ExceptionHandler.cc b/lib/CodeGen/ExceptionHandler.cc index 21eb011..3f69417 100644 --- a/lib/CodeGen/ExceptionHandler.cc +++ b/lib/CodeGen/ExceptionHandler.cc @@ -1,6 +1,7 @@ #include "cps/ExceptionHandler.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Instructions.h" +#include "llvm/Support/SaveAndRestore.h" #include using namespace llvm; @@ -72,15 +73,50 @@ void ExceptionHandler::endCatch() { if (!Builder.GetInsertBlock()->hasTerminator()) Builder.CreateCall(EndCatch); } -void ExceptionHandler::emitReturnCleanup() { +Instruction *ExceptionHandler::emitReturnCleanup() { auto &State = States[Builder.GetInsertBlock()->getParent()]; - for (unsigned I = 0; I < State.CatchDepth; ++I) Builder.CreateCall(EndCatch); + Instruction *First = nullptr; + for (unsigned I = 0; I < State.CatchDepth; ++I) { + auto *Call = Builder.CreateCall(EndCatch); + if (!First) First = Call; + } + return First; +} + +void ExceptionHandler::enableCleanups(Function *F) { + // Enable before emitting any call: a later DECLARE can already be alive + // when an earlier call executes again through a loop back edge. + States[F].HasCleanups = true; +} + +void ExceptionHandler::recordExit(Instruction *Before, bool Unwinding) { + auto *F = Before->getFunction(); + CleanupPoints[F].push_back({Before, States[F], Unwinding}); +} + +void ExceptionHandler::fillCleanups(Function *F, const std::function &Destroy) { + IRBuilderBase::InsertPointGuard Guard(Builder); + SaveAndRestore StateGuard(States[F]); + auto &Points = CleanupPoints[F]; + // Filling a normal exit may add an unwind point for a throwing destructor. + for (size_t I = 0; I < Points.size(); ++I) { + auto Point = Points[I]; + States[F] = Point.State; + SaveAndRestore UnwindGuard(FillingUnwind, Point.Unwinding); + auto *BB = Point.Before->getParent(); + auto *Continuation = BB->splitBasicBlock(Point.Before->getIterator(), "after_destruction"); + BB->getTerminator()->eraseFromParent(); + Builder.SetInsertPoint(BB); + Destroy(); + Builder.CreateBr(Continuation); + } + Points.clear(); } BasicBlock *ExceptionHandler::emitUnwindDestination() { auto *F = Builder.GetInsertBlock()->getParent(); auto &State = States[F]; - if (State.Handlers.empty() && State.CatchDepth == 0) return nullptr; + if (FillingUnwind || (State.Handlers.empty() && State.CatchDepth == 0 && !State.HasCleanups)) return nullptr; ensureRuntime(); F->setPersonalityFn(cast(Personality.getCallee())); auto Saved = Builder.saveIP(); @@ -91,7 +127,7 @@ BasicBlock *ExceptionHandler::emitUnwindDestination() { 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); + if (!Handler || 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); @@ -99,7 +135,7 @@ BasicBlock *ExceptionHandler::emitUnwindDestination() { Builder.CreateStore(Builder.CreateExtractValue(LP, 0), Handler->Exception); Builder.CreateBr(Handler->Target); } else { - Builder.CreateResume(LP); + recordExit(Builder.CreateResume(LP), true); } Builder.restoreIP(Saved); return Unwind; diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc index 9d9f60d..59e9bbe 100644 --- a/lib/CodeGen/FunctionGen.cc +++ b/lib/CodeGen/FunctionGen.cc @@ -212,6 +212,7 @@ Function *FunctionGen::emitFunctionDef(FunctionDefAST *FuncAST, BasicBlock *BB = BasicBlock::Create(Context, "entry", TheFunction); Builder.SetInsertPoint(BB); + Exceptions.enableCleanups(TheFunction); IdentifierMap OldSymbols = Symbols; Symbols.clear(); @@ -243,9 +244,9 @@ Function *FunctionGen::emitFunctionDef(FunctionDefAST *FuncAST, if (!Builder.GetInsertBlock()->hasTerminator()) { if (sameIdentifier(Proto->getReturnType(), "VOID")) { - Builder.CreateRetVoid(); + Exceptions.recordExit(Builder.CreateRetVoid()); } else { - Builder.CreateRet(Constant::getNullValue(TheFunction->getReturnType())); + Exceptions.recordExit(Builder.CreateRet(Constant::getNullValue(TheFunction->getReturnType()))); } } @@ -278,10 +279,7 @@ llvm::Value *FunctionGen::emitCall(const std::string &Callee, const std::vector< } void FunctionGen::emitReturn(ReturnStmtAST *Ret, llvm::Value *RetVal) { - Exceptions.emitReturnCleanup(); - if (RetVal) { - Builder.CreateRet(RetVal); - } else { - Builder.CreateRetVoid(); - } + Instruction *CatchCleanup = Exceptions.emitReturnCleanup(); + Instruction *RetInst = RetVal ? Builder.CreateRet(RetVal) : Builder.CreateRetVoid(); + Exceptions.recordExit(CatchCleanup ? CatchCleanup : RetInst); } -- cgit v1.3.1-4-g156e