aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen/ConstructorGen.cc
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-17 08:34:42 +0000
committer_ov4 <ov4@ov4.org>2026-10-17 08:34:42 +0000
commitabd56ef20bc8061bbecec29edd1301d324c2cb34 (patch)
tree8da9a14372a60d4df8bf58693c62c187765e7240 /lib/CodeGen/ConstructorGen.cc
parent59e3c50bcd58130e81e1f57b21d6e57b4d4aba6d (diff)
feat(classes): destroy stack objects at scope exit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen/ConstructorGen.cc')
-rw-r--r--lib/CodeGen/ConstructorGen.cc77
1 files changed, 77 insertions, 0 deletions
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<Instruction *> Exits;
+ for (auto &BB : *F)
+ if (BB.hasTerminator() && isa<ReturnInst>(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);
+ }
+}