From abd56ef20bc8061bbecec29edd1301d324c2cb34 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sat, 17 Oct 2026 08:34:42 +0000 Subject: feat(classes): destroy stack objects at scope exit Co-Authored-By: Claude Opus 5.5 --- lib/CodeGen/ArrayHandler.cc | 7 ++-- lib/CodeGen/ArrayParameters.cc | 2 ++ lib/CodeGen/CodeGen.cc | 5 +++ lib/CodeGen/ConstructorGen.cc | 77 ++++++++++++++++++++++++++++++++++++++++++ lib/CodeGen/FunctionGen.cc | 2 ++ lib/CodeGen/StatementGen.cc | 6 +++- lib/CodeGen/ValueTypeGen.cc | 4 ++- 7 files changed, 99 insertions(+), 4 deletions(-) (limited to 'lib/CodeGen') diff --git a/lib/CodeGen/ArrayHandler.cc b/lib/CodeGen/ArrayHandler.cc index 8da6cd3..7eefc20 100644 --- a/lib/CodeGen/ArrayHandler.cc +++ b/lib/CodeGen/ArrayHandler.cc @@ -97,6 +97,10 @@ void ArrayHandler::emitArrayDeclare(ArrayDeclareStmtAST *Stmt, CodeGen &CG) { if (!CG.prepareSymbolDeclaration(Name)) return; + Function *TheFunction = Builder->GetInsertBlock()->getParent(); + AllocaInst *Alloca = CG.CreateEntryBlockAlloca(TheFunction, PointerType::getUnqual(*TheContext), Name); + size_t Lifetime = CG.registerStackObject(Alloca, ElemInfo->Name, Stmt->getLine(), true); + std::vector Lows; std::vector Highs; @@ -134,8 +138,6 @@ void ArrayHandler::emitArrayDeclare(ArrayDeclareStmtAST *Stmt, CodeGen &CG) { Builder->CreateCall(MemsetFunc, {Ptr, ConstantInt::get(Type::getInt32Ty(*TheContext), 0), TotalBytes}); - Function *TheFunction = Builder->GetInsertBlock()->getParent(); - AllocaInst *Alloca = CG.CreateEntryBlockAlloca(TheFunction, PointerType::getUnqual(*TheContext), Name); Builder->CreateStore(Ptr, Alloca); CG.registerSymbol(Name, Alloca, ElemInfo->Name, true); @@ -157,6 +159,7 @@ void ArrayHandler::emitArrayDeclare(ArrayDeclareStmtAST *Stmt, CodeGen &CG) { Builder->CreateBr(Loop); Builder->SetInsertPoint(Done); } + CG.activateStackObject(Lifetime, TotalElements); } void ArrayHandler::computeDimsAndMultipliers(const std::string &Name, diff --git a/lib/CodeGen/ArrayParameters.cc b/lib/CodeGen/ArrayParameters.cc index 09017c9..3359c66 100644 --- a/lib/CodeGen/ArrayParameters.cc +++ b/lib/CodeGen/ArrayParameters.cc @@ -59,6 +59,8 @@ void ArrayHandler::bindArrayParameter(const std::string &Name, Builder->CreateStore(Ptr, Alloca); CG.registerSymbol(Name, Alloca, ElemInfo->Name, true); + if (MakeCopy) + CG.activateStackObject(CG.registerStackObject(Alloca, ElemInfo->Name, 0, true, true), TotalElements); } bool ArrayHandler::emitArrayArgument(ExprAST *ArgExpr, diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index 68dec5c..8117434 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -74,6 +74,10 @@ CodeGen::CodeGen() { /*MakeCopy=*/P.Mode == PassMode::ByVal, *this); }); + FuncGen->setCopyRegistrar([this](Value *Addr, const std::string &TypeName, int Line) { + activateStackObject(registerStackObject(Addr, TypeName, Line, false, true)); + }); + SetupExternalFunctions(); } @@ -185,6 +189,7 @@ void CodeGen::compile(const std::vector> &Statements) { Builder->CreateRet(ConstantInt::get(*TheContext, APInt(32, 0))); } + finishStackObjects(F); auto *Exception = Exceptions->popHandler(); Builder->SetInsertPoint(UnhandledBB); Exceptions->beginCatch(Exception); diff --git a/lib/CodeGen/ConstructorGen.cc b/lib/CodeGen/ConstructorGen.cc index 9e6b0b3..c0e50ac 100644 --- a/lib/CodeGen/ConstructorGen.cc +++ b/lib/CodeGen/ConstructorGen.cc @@ -107,3 +107,80 @@ bool CodeGen::emitDestroy(const std::string &TypeName, Value *Addr, int Line) { } return true; } + +size_t CodeGen::registerStackObject(Value *Addr, const std::string &TypeName, int Line, + bool IsArray, bool IsCopy) { + if (!typeNeedsDestruction(TypeName)) return size_t(-1); + auto *F = Builder->GetInsertBlock()->getParent(); + IRBuilder<> Entry(&F->getEntryBlock(), F->getEntryBlock().begin()); + auto *Alive = Entry.CreateAlloca(Entry.getInt1Ty(), nullptr, "object_alive"); + Entry.CreateStore(Entry.getFalse(), Alive); + AllocaInst *Count = nullptr; + if (IsArray) { + Count = Entry.CreateAlloca(Entry.getInt64Ty(), nullptr, "object_count"); + Entry.CreateStore(Entry.getInt64(0), Count); + } + StackObjects.push_back({Addr, TypeName, Alive, Count, Line, IsCopy}); + // This DECLARE can execute repeatedly, including after a backward loop edge. + emitStackObjectCleanup(StackObjects.back()); + return StackObjects.size() - 1; +} + +void CodeGen::activateStackObject(size_t Index, Value *Count) { + if (Index == size_t(-1)) return; + const auto &Object = StackObjects[Index]; + if (Object.Count) Builder->CreateStore(Count, Object.Count); + Builder->CreateStore(Builder->getTrue(), Object.Alive); +} + +void CodeGen::emitStackObjectCleanup(const StackObject &Object) { + auto *F = Builder->GetInsertBlock()->getParent(); + auto *Destroy = BasicBlock::Create(*TheContext, "destroy_object", F); + auto *Done = BasicBlock::Create(*TheContext, "destroyed_object", F); + Builder->CreateCondBr(Builder->CreateLoad(Builder->getInt1Ty(), Object.Alive), Destroy, Done); + Builder->SetInsertPoint(Destroy); + // Clear before the user method: a throwing destructor must never run twice. + Builder->CreateStore(Builder->getFalse(), Object.Alive); + if (!Object.Count) { + emitDestroy(Object.TypeName, Object.Addr, Object.Line); + } else { + Value *Base = Builder->CreateLoad(PointerType::getUnqual(*TheContext), Object.Addr); + Value *Count = Builder->CreateLoad(Builder->getInt64Ty(), Object.Count); + auto *Preheader = Builder->GetInsertBlock(); + auto *Loop = BasicBlock::Create(*TheContext, "destroy_elements", F); + auto *Body = BasicBlock::Create(*TheContext, "destroy_element", F); + Builder->CreateBr(Loop); + Builder->SetInsertPoint(Loop); + auto *Index = Builder->CreatePHI(Builder->getInt64Ty(), 2); + Index->addIncoming(Count, Preheader); + Builder->CreateCondBr(Builder->CreateICmpNE(Index, Builder->getInt64(0)), Body, Done); + Builder->SetInsertPoint(Body); + Value *Previous = Builder->CreateSub(Index, Builder->getInt64(1)); + Value *Element = Builder->CreateGEP(resolveType(Object.TypeName)->LLVMType, Base, Previous); + emitDestroy(Object.TypeName, Element, Object.Line); + Index->addIncoming(Previous, Builder->GetInsertBlock()); + Builder->CreateBr(Loop); + Builder->SetInsertPoint(Done); + return; + } + Builder->CreateBr(Done); + Builder->SetInsertPoint(Done); +} + +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); + for (auto It = StackObjects.rbegin(); It != StackObjects.rend(); ++It) + emitStackObjectCleanup(*It); + Builder->CreateBr(Continuation); + } +} diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc index e344410..9d9f60d 100644 --- a/lib/CodeGen/FunctionGen.cc +++ b/lib/CodeGen/FunctionGen.cc @@ -89,6 +89,7 @@ void FunctionGen::createArgumentAllocas(Function *F, const std::vector TmpB(&F->getEntryBlock(), F->getEntryBlock().begin()); AllocaInst *Alloca = TmpB.CreateAlloca(ArgType, nullptr, P.Name); + // Shallow BYVAL copy. The common lifetime hook is the future copy-constructor point. Builder.CreateStore(ArgVal, Alloca); const TypeInfo *Info = Types.resolve(P.TypeName); if (Info && Info->IsClass) { @@ -98,6 +99,7 @@ void FunctionGen::createArgumentAllocas(Function *F, const std::vectorexchangeTable({}); + auto SavedObjects = std::move(StackObjects); + StackObjects.clear(); auto SavedScopes = std::move(ConstantScopes); ConstantScopes = {{NextScopeID++, {}}}; - FuncGen->emitFunctionDef(FuncDef, [this](StmtAST *S) { + Function *Emitted = FuncGen->emitFunctionDef(FuncDef, [this](StmtAST *S) { this->emitStmt(S); }, ConstantScopes.back().ID); + if (Emitted) finishStackObjects(Emitted); + StackObjects = std::move(SavedObjects); ConstantScopes = std::move(SavedScopes); Arrays->exchangeTable(std::move(SavedArrays)); diff --git a/lib/CodeGen/ValueTypeGen.cc b/lib/CodeGen/ValueTypeGen.cc index fbe6dd5..5e926cf 100644 --- a/lib/CodeGen/ValueTypeGen.cc +++ b/lib/CodeGen/ValueTypeGen.cc @@ -268,6 +268,7 @@ void CodeGen::emitDeclareStmt(DeclareStmtAST *Stmt) { Function *TheFunction = Builder->GetInsertBlock()->getParent(); AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, Info->LLVMType, Stmt->getName()); + size_t Lifetime = registerStackObject(Alloca, Info->Name, Stmt->getLine()); Value *InitVal = nullptr; if (Info->isString()) { InitVal = EmptyStringStr; @@ -291,5 +292,6 @@ void CodeGen::emitDeclareStmt(DeclareStmtAST *Stmt) { reportError("Constructor arguments require a class type"); return; } - emitConstruct(Info->Name, Alloca, Stmt->getArgs(), Stmt->getLine()); + if (emitConstruct(Info->Name, Alloca, Stmt->getArgs(), Stmt->getLine())) + activateStackObject(Lifetime); } -- cgit v1.3.1-4-g156e