aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-22 15:08:51 +0000
committer_ov4 <ov4@ov4.org>2026-10-22 15:21:11 +0000
commit8b6ba7222d9990bc19e355eabcf046a7ea773545 (patch)
treeadf98eaaebd455168806cdb6f5dbebb11fb3dd89 /lib/CodeGen
parentfa83b7df5a0711bf5f4a5162cc33b0a4123a53c7 (diff)
fix(arrays): free array buffers when their scope ends
Register every array as a stack object so its buffer is freed on scope end, re-DECLARE, RETURN and unwinding; run the element-destroy loop only for types that need destruction. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen')
-rw-r--r--lib/CodeGen/ArrayParameters.cc4
-rw-r--r--lib/CodeGen/ConstructorGen.cc47
2 files changed, 31 insertions, 20 deletions
diff --git a/lib/CodeGen/ArrayParameters.cc b/lib/CodeGen/ArrayParameters.cc
index 57dc523..319f1b5 100644
--- a/lib/CodeGen/ArrayParameters.cc
+++ b/lib/CodeGen/ArrayParameters.cc
@@ -27,8 +27,8 @@ void ArrayHandler::bindArrayParameter(const std::string &Name,
Value *Ptr = DataPtr;
if (MakeCopy) {
// BYVAL: whole-buffer copy so callee writes stay invisible to the
- // caller. The copy is malloc'd and never freed, consistent with the
- // project-wide string policy.
+ // caller. The copy is malloc'd and registered below, so it is freed
+ // when the callee ends.
Value *TotalBytes = Builder->CreateMul(TotalElements, Meta.ElementSizeC,
Name + "_copy_bytes");
Value *Copy = Builder->CreateCall(MallocFunc, TotalBytes, Name + "_copy");
diff --git a/lib/CodeGen/ConstructorGen.cc b/lib/CodeGen/ConstructorGen.cc
index 15d9300..8ca0258 100644
--- a/lib/CodeGen/ConstructorGen.cc
+++ b/lib/CodeGen/ConstructorGen.cc
@@ -256,7 +256,8 @@ bool CodeGen::emitDestroy(const std::string &TypeName, Value *Addr, int Line) {
size_t CodeGen::registerStackObject(Value *Addr, const std::string &TypeName, int Line,
bool IsArray) {
- if (!typeNeedsDestruction(TypeName)) return size_t(-1);
+ // Arrays always register: their buffer is freed when the scope ends.
+ if (!IsArray && !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");
@@ -291,23 +292,33 @@ void CodeGen::emitStackObjectCleanup(const StackObject &Object) {
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;
+ auto *Free = BasicBlock::Create(*TheContext, "free_array", F);
+ if (typeNeedsDestruction(Object.TypeName)) {
+ 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, Free);
+ 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);
+ } else {
+ Builder->CreateBr(Free);
+ }
+ // Release the buffer; the slot is nulled so a later cleanup of the
+ // same (inactive) object can never free it twice.
+ Builder->SetInsertPoint(Free);
+ FunctionCallee FreeFunc = TheModule->getOrInsertFunction(
+ "free", FunctionType::get(Builder->getVoidTy(), {PointerType::getUnqual(*TheContext)}, false));
+ Builder->CreateCall(FreeFunc, {Base});
+ Builder->CreateStore(ConstantPointerNull::get(PointerType::getUnqual(*TheContext)), Object.Addr);
}
Builder->CreateBr(Done);
Builder->SetInsertPoint(Done);