aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen/MemoryHandler.cc
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-17 05:30:57 +0000
committer_ov4 <ov4@ov4.org>2026-10-17 05:30:57 +0000
commit4dd85fd6ecd9a67e5c2b68380873e6e114714355 (patch)
treeb9dad1d0845f4c2ae7e6efed171fbee40ccd1f31 /lib/CodeGen/MemoryHandler.cc
parente782898e420c7f2abd83b97c160194d0e70598dd (diff)
feat(memory): add tracked MALLOC and FREE builtins
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen/MemoryHandler.cc')
-rw-r--r--lib/CodeGen/MemoryHandler.cc120
1 files changed, 120 insertions, 0 deletions
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 <climits>
+#include <cstddef>
+
+using namespace llvm;
+using namespace cps;
+
+Function *MemoryHandler::createHelper(const char *Name, ArrayRef<Type *> 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");
+}