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/ExceptionHandler.cc | 46 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) (limited to 'lib/CodeGen/ExceptionHandler.cc') 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; -- cgit v1.3.1-4-g156e