From 4dd85fd6ecd9a67e5c2b68380873e6e114714355 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sat, 17 Oct 2026 05:30:57 +0000 Subject: feat(memory): add tracked MALLOC and FREE builtins Co-Authored-By: Claude Fable 5.1 --- lib/CodeGen/MemoryHandler.cc | 120 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 lib/CodeGen/MemoryHandler.cc (limited to 'lib/CodeGen/MemoryHandler.cc') diff --git a/lib/CodeGen/MemoryHandler.cc b/lib/CodeGen/MemoryHandler.cc new file mode 100644 index 0000000..fc6d982 --- /dev/null +++ b/lib/CodeGen/MemoryHandler.cc @@ -0,0 +1,120 @@ +#include "cps/MemoryHandler.h" +#include "cps/ExceptionHandler.h" +#include "cps/RuntimeCheck.h" +#include "cps/TypeSystem.h" +#include +#include + +using namespace llvm; +using namespace cps; + +Function *MemoryHandler::createHelper(const char *Name, ArrayRef Args) { + auto *F = Function::Create(FunctionType::get(PointerType::getUnqual(Context), Args, false), + Function::InternalLinkage, Name, Module); + Exceptions.prepareFunction(F); + Builder.SetInsertPoint(BasicBlock::Create(Context, "entry", F)); + return F; +} + +void MemoryHandler::ensureRuntime() { + if (AllocateFn) return; + IRBuilderBase::InsertPointGuard Guard(Builder); + auto *Ptr = PointerType::getUnqual(Context); + auto *I32 = Type::getInt32Ty(Context); + SizeTy = Type::getIntNTy(Context, sizeof(std::size_t) * CHAR_BIT); + NodeTy = StructType::create(Context, {Ptr, Ptr}, "__cps_memory_node"); + Head = new GlobalVariable(Module, Ptr, false, GlobalValue::InternalLinkage, + ConstantPointerNull::get(Ptr), "__cps_memory_head"); + Malloc = Module.getOrInsertFunction("malloc", FunctionType::get(Ptr, {SizeTy}, false)); + Free = Module.getOrInsertFunction("free", FunctionType::get(Type::getVoidTy(Context), {Ptr}, false)); + AllocationError = Builder.CreateGlobalString("[Fatal] line %d: Memory allocation failed\n", + "err_allocation", 0, &Module); + TrackingError = Builder.CreateGlobalString("[Fatal] line %d: Allocation tracking failed\n", + "err_tracking", 0, &Module); + InvalidAddress = Builder.CreateGlobalString("[Fatal] line %d: Invalid memory address (not an active allocation)\n", + "err_memory_address", 0, &Module); + FindFn = createHelper("__cps_memory_find", {Ptr, I32}); + buildFind(); + AllocateFn = createHelper("__cps_memory_allocate", {SizeTy, I32}); + buildAllocate(); + ReleaseFn = createHelper("__cps_memory_release", {Ptr, I32}); + buildRelease(); +} + +// Return the address of the list link that owns the matching node. This also +// lets release unlink the head or an interior node through the same operation. +void MemoryHandler::buildFind() { + auto *Ptr = PointerType::getUnqual(Context); + Value *Address = FindFn->getArg(0), *Line = FindFn->getArg(1); + auto *Entry = Builder.GetInsertBlock(); + auto *Loop = BasicBlock::Create(Context, "search", FindFn); + auto *Found = BasicBlock::Create(Context, "found", FindFn); + Builder.CreateBr(Loop); + Builder.SetInsertPoint(Loop); + auto *Link = Builder.CreatePHI(Ptr, 2, "link"); + Link->addIncoming(Head, Entry); + Value *Node = Builder.CreateLoad(Ptr, Link, "node"); + Checker.emitCheck(Builder.CreateIsNull(Node), InvalidAddress, {Line}); + Value *Stored = Builder.CreateLoad(Ptr, Builder.CreateStructGEP(NodeTy, Node, 0)); + Value *NextLink = Builder.CreateStructGEP(NodeTy, Node, 1); + Link->addIncoming(NextLink, Builder.GetInsertBlock()); + Builder.CreateCondBr(Builder.CreateICmpEQ(Stored, Address), Found, Loop); + Builder.SetInsertPoint(Found); + Builder.CreateRet(Link); +} + +void MemoryHandler::buildAllocate() { + auto *Ptr = PointerType::getUnqual(Context); + Value *Size = AllocateFn->getArg(0), *Line = AllocateFn->getArg(1); + auto *Zero = BasicBlock::Create(Context, "zero_size", AllocateFn); + auto *Allocate = BasicBlock::Create(Context, "allocate", AllocateFn); + Builder.CreateCondBr(Builder.CreateICmpEQ(Size, ConstantInt::get(SizeTy, 0)), Zero, Allocate); + Builder.SetInsertPoint(Zero); + Builder.CreateRet(ConstantPointerNull::get(Ptr)); + Builder.SetInsertPoint(Allocate); + Value *NodeSize = Builder.CreateIntCast(Types.getSizeOfConstant(NodeTy), SizeTy, false); + Value *Node = Builder.CreateCall(Malloc, {NodeSize}, "new_node"); + Checker.emitCheck(Builder.CreateIsNull(Node), TrackingError, {Line}); + Value *Address = Builder.CreateCall(Malloc, {Size}, "allocation"); + Value *Failed = Builder.CreateIsNull(Address); + auto *Cleanup = BasicBlock::Create(Context, "cleanup_node", AllocateFn); + auto *Checked = BasicBlock::Create(Context, "check_allocation", AllocateFn); + Builder.CreateCondBr(Failed, Cleanup, Checked); + Builder.SetInsertPoint(Cleanup); + Builder.CreateCall(Free, {Node}); + Builder.CreateBr(Checked); + Builder.SetInsertPoint(Checked); + Checker.emitCheck(Failed, AllocationError, {Line}); + Builder.CreateStore(Address, Builder.CreateStructGEP(NodeTy, Node, 0)); + Builder.CreateStore(Builder.CreateLoad(Ptr, Head), Builder.CreateStructGEP(NodeTy, Node, 1)); + Builder.CreateStore(Node, Head); + Builder.CreateRet(Address); +} + +void MemoryHandler::buildRelease() { + auto *Ptr = PointerType::getUnqual(Context); + Value *Address = ReleaseFn->getArg(0), *Line = ReleaseFn->getArg(1); + auto *Done = BasicBlock::Create(Context, "done", ReleaseFn); + auto *Release = BasicBlock::Create(Context, "release", ReleaseFn); + Builder.CreateCondBr(Builder.CreateIsNull(Address), Done, Release); + Builder.SetInsertPoint(Release); + Value *Link = Exceptions.emitCall(FindFn, {Address, Line}, "link"); + Value *Node = Builder.CreateLoad(Ptr, Link); + Value *Next = Builder.CreateLoad(Ptr, Builder.CreateStructGEP(NodeTy, Node, 1)); + Builder.CreateStore(Next, Link); + Builder.CreateCall(Free, {Address}); + Builder.CreateCall(Free, {Node}); + Builder.CreateBr(Done); + Builder.SetInsertPoint(Done); + Builder.CreateRet(Address); +} + +Value *MemoryHandler::emitAllocate(Value *Size, int Line) { + ensureRuntime(); + return Exceptions.emitCall(AllocateFn, {Size, Builder.getInt32(Line)}, "allocated"); +} + +Value *MemoryHandler::emitRelease(Value *Address, int Line) { + ensureRuntime(); + return Exceptions.emitCall(ReleaseFn, {Address, Builder.getInt32(Line)}, "released"); +} -- cgit v1.3.1-4-g156e