aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-27 13:27:14 +0000
committer_ov4 <ov4@ov4.org>2026-10-27 13:27:14 +0000
commit0dd0b0908300eee08a06ee51d9eb48b85fd97d74 (patch)
tree8e18df810a1d88f686f2b85ed2f05e3aa0985119 /lib/CodeGen
parent790e0fd7c5a81b9a27a6d84e0f3f4c7a0b17d04b (diff)
refactor(memory)!: move allocation functions into memory.cps
Route keyword NEW and DELETE through the same library runtime and retain allocation tracking, runtime diagnostics and exception behavior. BREAKING CHANGE: -no-std programs using NEW or DELETE must explicitly import memory.cps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen')
-rw-r--r--lib/CodeGen/Builtins.h3
-rw-r--r--lib/CodeGen/CallGen.cc1
-rw-r--r--lib/CodeGen/CodeGen.cc4
-rw-r--r--lib/CodeGen/ConstructorGen.cc4
-rw-r--r--lib/CodeGen/ExpressionGen.cc36
-rw-r--r--lib/CodeGen/FunctionGen.cc3
-rw-r--r--lib/CodeGen/MemoryGen.cc30
-rw-r--r--lib/CodeGen/MemoryHandler.cc159
-rw-r--r--lib/CodeGen/StatementGen.cc5
9 files changed, 36 insertions, 209 deletions
diff --git a/lib/CodeGen/Builtins.h b/lib/CodeGen/Builtins.h
index 84ba765..66ac9e4 100644
--- a/lib/CodeGen/Builtins.h
+++ b/lib/CodeGen/Builtins.h
@@ -15,9 +15,6 @@ struct BuiltinInfo {
inline constexpr BuiltinInfo Builtins[] = {
{"EOF", 1, "BOOLEAN"},
- {"MALLOC", 1, "C_TYPE(\"void*\")"},
- {"REALLOC", 2, "C_TYPE(\"void*\")"},
- {"FREE", 1, "C_TYPE(\"void*\")"},
{"INT", 1, "INTEGER"},
};
diff --git a/lib/CodeGen/CallGen.cc b/lib/CodeGen/CallGen.cc
index 30f0d61..f3a6a9d 100644
--- a/lib/CodeGen/CallGen.cc
+++ b/lib/CodeGen/CallGen.cc
@@ -2,7 +2,6 @@
#include "cps/CodeGen.h"
#include "cps/ArrayHandler.h"
#include "cps/FileHandler.h"
-#include "cps/MemoryHandler.h"
#include "cps/ExceptionHandler.h"
#include "Builtins.h"
#include "TemplateClone.h"
diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc
index 64ed7c4..49b470c 100644
--- a/lib/CodeGen/CodeGen.cc
+++ b/lib/CodeGen/CodeGen.cc
@@ -2,7 +2,6 @@
#include "cps/CodeGen.h"
#include "cps/ArrayHandler.h"
#include "cps/FileHandler.h"
-#include "cps/MemoryHandler.h"
#include "cps/ExceptionHandler.h"
#include "Builtins.h"
#include "TemplateClone.h"
@@ -61,9 +60,6 @@ CodeGen::CodeGen() {
Files = std::make_unique<FileHandler>(*TheContext, *Builder, *TheModule, *RuntimeChecker,
*Exceptions);
- Memory = std::make_unique<MemoryHandler>(*TheContext, *Builder, *TheModule, *Types,
- *RuntimeChecker, *Exceptions);
-
// Array parameters are bound into ArrayHandler through this inversion so
// FunctionGen never sees the array subsystem.
FuncGen->setArrayParamBinder(
diff --git a/lib/CodeGen/ConstructorGen.cc b/lib/CodeGen/ConstructorGen.cc
index f598494..b029385 100644
--- a/lib/CodeGen/ConstructorGen.cc
+++ b/lib/CodeGen/ConstructorGen.cc
@@ -1,5 +1,4 @@
#include "cps/CodeGen.h"
-#include "cps/MemoryHandler.h"
#include "cps/ExceptionHandler.h"
#include "cps/TypeAST.h"
#include <climits>
@@ -165,7 +164,8 @@ void CodeGen::emitAssignment(const LValueInfo &Target, ExprAST *Expr, const Symb
// a non-null object address (MALLOC(0) deliberately returns null).
Size = Builder->CreateSelect(Builder->CreateICmpEQ(Size, ConstantInt::get(Size->getType(), 0)),
ConstantInt::get(Size->getType(), 1), Size);
- Value *Object = Memory->emitAllocate(Size, New->getLine());
+ Value *Object = emitMemoryCall("_memory_impl_malloc", Size, New->getLine());
+ if (!Object) return;
Builder->CreateStore(Types->getZeroValue(Class->Name), Object);
if (!emitConstruct(Class->Name, Object, New->getArgs(), New->getLine())) return;
if (Symbol && Symbol->IsReference) {
diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc
index 18c934d..9f68a82 100644
--- a/lib/CodeGen/ExpressionGen.cc
+++ b/lib/CodeGen/ExpressionGen.cc
@@ -1,13 +1,10 @@
#include "cps/CodeGen.h"
#include "cps/ArrayHandler.h"
#include "cps/FileHandler.h"
-#include "cps/MemoryHandler.h"
#include "cps/Lexer.h"
#include "cps/TypeAST.h"
#include "cps/Operator.h"
#include "Builtins.h"
-#include <climits>
-#include <cstddef>
using namespace llvm;
using namespace cps;
@@ -300,23 +297,6 @@ Value *CodeGen::emitCall(const std::string &Callee,
return nullptr;
}
}
- if (Name == "MALLOC") {
- Value *Size = emitAllocationSize(ArgExprs[0].get());
- if (!Size) return nullptr;
- return Memory->emitAllocate(Size, Line);
- }
- if (Name == "REALLOC") {
- Value *Address = emitCoercedExpr(ArgExprs[0].get(), resolveType("C_TYPE(\"void*\")"));
- if (!Address) return nullptr;
- Value *Size = emitAllocationSize(ArgExprs[1].get());
- if (!Size) return nullptr;
- return Memory->emitResize(Address, Size, Line);
- }
- if (Name == "FREE") {
- Value *Address = emitCoercedExpr(ArgExprs[0].get(), resolveType("C_TYPE(\"void*\")"));
- if (!Address) return nullptr;
- return Memory->emitRelease(Address, Line);
- }
if (Name == "INT") return emitIntBuiltin(ArgExprs[0].get(), Line);
if (Name == "EOF") {
Value *FileName = emitFileNameOperand(ArgExprs[0].get(), "EOF");
@@ -354,19 +334,3 @@ Value *CodeGen::emitIntBuiltin(ExprAST *Arg, int Line) {
RuntimeChecker->emitCheck(OutOfRange, Msg, {RuntimeChecker->lineValue(Line)});
return Builder->CreateFPToSI(V, Type::getInt64Ty(*TheContext), "int_value");
}
-
-Value *CodeGen::emitAllocationSize(ExprAST *Expr) {
- SaveAndRestore<int> Location(DiagnosticLine, Expr->getLine() ? Expr->getLine() : DiagnosticLine);
- const TypeInfo *Info = getExprTypeInfo(Expr);
- if (isWholeArrayVar(Expr) || !Info ||
- !(Info->Kind == TypeKind::Integer ||
- (Info->Kind == TypeKind::CScalar && Info->isIntegral()))) {
- reportError("Allocation size must be INTEGER or a C integer");
- return nullptr;
- }
- Value *Size = emitExpr(Expr);
- if (!Size) return nullptr;
- auto *SizeTy = Type::getIntNTy(*TheContext, sizeof(std::size_t) * CHAR_BIT);
- return Builder->CreateIntCast(Size, SizeTy,
- Info->Kind == TypeKind::Integer || Info->CSigned, "allocation_size");
-}
diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc
index 2e927f9..c162b46 100644
--- a/lib/CodeGen/FunctionGen.cc
+++ b/lib/CodeGen/FunctionGen.cc
@@ -158,7 +158,8 @@ bool FunctionGen::validatePrototype(PrototypeAST *Proto) {
return false;
}
if (Proto->getOwnerClass().empty()) {
- if (!Proto->isExternal() && isReservedRuntimeName(Proto->getName())) {
+ // OVERLOAD symbols carry a signature and cannot replace libc symbols.
+ if (!Proto->isExternal() && !Proto->isOverload() && isReservedRuntimeName(Proto->getName())) {
reportError("'%s' is a reserved runtime symbol and cannot be a FUNCTION/PROCEDURE name",
Proto->getName().c_str());
return false;
diff --git a/lib/CodeGen/MemoryGen.cc b/lib/CodeGen/MemoryGen.cc
new file mode 100644
index 0000000..453f137
--- /dev/null
+++ b/lib/CodeGen/MemoryGen.cc
@@ -0,0 +1,30 @@
+#include "cps/CodeGen.h"
+
+using namespace llvm;
+using namespace cps;
+
+// Keyword NEW/DELETE use the same visible C helpers as memory.cps. Ordinary
+// MALLOC/REALLOC/FREE calls are resolved entirely through the library.
+Value *CodeGen::emitMemoryCall(const char *Name, Value *Argument, int Line) {
+ SaveAndRestore<int> Location(DiagnosticLine, Line);
+ auto Declarations = FuncGen->getOverloads(Name);
+ if (Declarations.empty()) {
+ reportError("NEW/DELETE requires memory.cps; include the memory library");
+ return nullptr;
+ }
+ const FuncSig &Sig = *Declarations.front();
+ bool Allocate = sameIdentifier(Name, "_memory_impl_malloc");
+ bool Validate = sameIdentifier(Name, "_memory_impl_validate");
+ const TypeInfo *Parameter = resolveType(Allocate ? "C_TYPE(\"unsigned long long\")"
+ : "C_TYPE(\"void*\")");
+ const TypeInfo *Result = resolveType(Validate ? "VOID" : "C_TYPE(\"void*\")");
+ if (!Sig.IsExternal || Sig.IsVariadic || Sig.Params.size() != 1 ||
+ Sig.Params[0].IsArray || Sig.Params[0].IsByRef ||
+ !Types->sameType(Sig.Params[0].TypeName, Parameter->Name) ||
+ !Types->sameType(Sig.ReturnTypeName, Result->Name)) {
+ reportError("Invalid memory runtime declaration for %s", Name);
+ return nullptr;
+ }
+ if (Allocate) Argument = Builder->CreateIntCast(Argument, Parameter->LLVMType, false);
+ return FuncGen->emitCall(Sig, {Argument});
+}
diff --git a/lib/CodeGen/MemoryHandler.cc b/lib/CodeGen/MemoryHandler.cc
deleted file mode 100644
index 7cb62b3..0000000
--- a/lib/CodeGen/MemoryHandler.cc
+++ /dev/null
@@ -1,159 +0,0 @@
-#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));
- Realloc = Module.getOrInsertFunction("realloc", FunctionType::get(Ptr, {Ptr, SizeTy}, 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();
- ResizeFn = createHelper("__cps_memory_resize", {Ptr, SizeTy, I32});
- buildResize();
-}
-
-// 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, Checker.lineValue(Line)}, "allocated");
-}
-
-Value *MemoryHandler::emitRelease(Value *Address, int Line) {
- ensureRuntime();
- return Exceptions.emitCall(ReleaseFn, {Address, Checker.lineValue(Line)}, "released");
-}
-
-void MemoryHandler::buildResize() {
- auto *Ptr = PointerType::getUnqual(Context);
- Value *Address = ResizeFn->getArg(0), *Size = ResizeFn->getArg(1), *Line = ResizeFn->getArg(2);
- auto *Allocate = BasicBlock::Create(Context, "allocate", ResizeFn);
- auto *Validate = BasicBlock::Create(Context, "validate", ResizeFn);
- auto *Release = BasicBlock::Create(Context, "release", ResizeFn);
- auto *Resize = BasicBlock::Create(Context, "resize", ResizeFn);
- Builder.CreateCondBr(Builder.CreateIsNull(Address), Allocate, Validate);
- Builder.SetInsertPoint(Allocate);
- Builder.CreateRet(Exceptions.emitCall(AllocateFn, {Size, Line}, "allocation"));
- Builder.SetInsertPoint(Validate);
- Value *Link = Exceptions.emitCall(FindFn, {Address, Line}, "link");
- Value *Node = Builder.CreateLoad(Ptr, Link);
- Builder.CreateCondBr(Builder.CreateICmpEQ(Size, ConstantInt::get(SizeTy, 0)), Release, Resize);
- Builder.SetInsertPoint(Release);
- Exceptions.emitCall(ReleaseFn, {Address, Line});
- Builder.CreateRet(ConstantPointerNull::get(Ptr));
- Builder.SetInsertPoint(Resize);
- Value *NewAddress = Builder.CreateCall(Realloc, {Address, Size}, "resized");
- // libc preserves the original block on nonzero-size failure. Leave the
- // tracking entry untouched until the check has succeeded as well.
- Checker.emitCheck(Builder.CreateIsNull(NewAddress), AllocationError, {Line});
- Builder.CreateStore(NewAddress, Builder.CreateStructGEP(NodeTy, Node, 0));
- Builder.CreateRet(NewAddress);
-}
-
-Value *MemoryHandler::emitResize(Value *Address, Value *Size, int Line) {
- ensureRuntime();
- return Exceptions.emitCall(ResizeFn, {Address, Size, Checker.lineValue(Line)}, "resized");
-}
-
-void MemoryHandler::emitValidate(Value *Address, int Line) {
- ensureRuntime();
- Exceptions.emitCall(FindFn, {Address, Checker.lineValue(Line)});
-}
diff --git a/lib/CodeGen/StatementGen.cc b/lib/CodeGen/StatementGen.cc
index c161049..3cacfe9 100644
--- a/lib/CodeGen/StatementGen.cc
+++ b/lib/CodeGen/StatementGen.cc
@@ -2,7 +2,6 @@
#include "cps/ArrayHandler.h"
#include "cps/FileAST.h"
#include "cps/FileHandler.h"
-#include "cps/MemoryHandler.h"
#include "cps/ExceptionHandler.h"
#include "cps/TypeAST.h"
#include "cps/TemplateAST.h"
@@ -54,9 +53,9 @@ void CodeGen::emitStmtImpl(StmtAST *Stmt) {
reportError("DELETE requires a class object");
return;
}
- Memory->emitValidate(Object.Addr, Stmt->getLine());
+ if (!emitMemoryCall("_memory_impl_validate", Object.Addr, Stmt->getLine())) return;
if (emitDestroy(Object.TypeName, Object.Addr, Stmt->getLine()))
- Memory->emitRelease(Object.Addr, Stmt->getLine());
+ emitMemoryCall("_memory_impl_free", Object.Addr, Stmt->getLine());
return;
}
if (auto *Try = dynamic_cast<TryStmtAST*>(Stmt)) {