aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen
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
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')
-rw-r--r--lib/CodeGen/ArrayHandler.cc7
-rw-r--r--lib/CodeGen/ArrayParameters.cc2
-rw-r--r--lib/CodeGen/CodeGen.cc5
-rw-r--r--lib/CodeGen/ConstructorGen.cc77
-rw-r--r--lib/CodeGen/FunctionGen.cc2
-rw-r--r--lib/CodeGen/StatementGen.cc6
-rw-r--r--lib/CodeGen/ValueTypeGen.cc4
7 files changed, 99 insertions, 4 deletions
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<Value*> Lows;
std::vector<Value*> 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<std::unique_ptr<StmtAST>> &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<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);
+ }
+}
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<ParamDecl
IRBuilder<> 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::vector<ParamDecl
} else {
Symbols[P.Name] = {Alloca, P.TypeName, false};
}
+ if (RegisterCopy) RegisterCopy(Alloca, P.TypeName, P.Line);
}
}
diff --git a/lib/CodeGen/StatementGen.cc b/lib/CodeGen/StatementGen.cc
index a7c8eda..df8c396 100644
--- a/lib/CodeGen/StatementGen.cc
+++ b/lib/CodeGen/StatementGen.cc
@@ -31,11 +31,15 @@ void CodeGen::emitStmt(StmtAST *Stmt) {
// to the emitting function), mirroring FunctionGen's Symbols handling.
auto SavedArrays = Arrays->exchangeTable({});
+ 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);
}