diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-17 08:34:42 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-17 08:34:42 +0000 |
| commit | abd56ef20bc8061bbecec29edd1301d324c2cb34 (patch) | |
| tree | 8da9a14372a60d4df8bf58693c62c187765e7240 /lib/CodeGen/ConstructorGen.cc | |
| parent | 59e3c50bcd58130e81e1f57b21d6e57b4d4aba6d (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.cc | 77 |
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); + } +} |
