diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-27 13:27:14 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-27 13:27:14 +0000 |
| commit | 0dd0b0908300eee08a06ee51d9eb48b85fd97d74 (patch) | |
| tree | 8e18df810a1d88f686f2b85ed2f05e3aa0985119 | |
| parent | 790e0fd7c5a81b9a27a6d84e0f3f4c7a0b17d04b (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>
28 files changed, 296 insertions, 278 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 62dcbfc..de94612 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,7 +81,7 @@ add_library(CPSCodeGen STATIC lib/CodeGen/StringValueGen.cc lib/CodeGen/TypeSystem.cc lib/CodeGen/NativeTypeSystem.cc - lib/CodeGen/MemoryHandler.cc + lib/CodeGen/MemoryGen.cc lib/CodeGen/FileHandler.cc lib/CodeGen/FileRuntimeIO.cc lib/CodeGen/CABI/ClangABI.cc diff --git a/include/cps/CodeGen.h b/include/cps/CodeGen.h index 45660b8..0962753 100644 --- a/include/cps/CodeGen.h +++ b/include/cps/CodeGen.h @@ -21,7 +21,6 @@ namespace cps { class ArrayHandler; class FileHandler; -class MemoryHandler; class ExceptionHandler; class CastExprAST; class TemplateDeclAST; @@ -157,7 +156,6 @@ class CodeGen { std::unique_ptr<ArithmeticHandler> ArithHandler; std::unique_ptr<FileHandler> Files; - std::unique_ptr<MemoryHandler> Memory; llvm::FunctionCallee PrintfFunc; @@ -208,7 +206,7 @@ class CodeGen { bool emitLValue(ExprAST *E, LValueInfo &Out); // Read a value from resolved storage. llvm::Value *loadFromLValue(const LValueInfo &LV); - llvm::Value *emitAllocationSize(ExprAST *Expr); + llvm::Value *emitMemoryCall(const char *Name, llvm::Value *Argument, int Line); static bool isLogicalOperand(const TypeInfo *T); llvm::Value *emitShortCircuit(BinaryExprAST *Bin, const TypeInfo *LT, const TypeInfo *RT); llvm::Value *emitIntBuiltin(ExprAST *Arg, int Line); diff --git a/include/cps/MemoryHandler.h b/include/cps/MemoryHandler.h deleted file mode 100644 index 45c24dc..0000000 --- a/include/cps/MemoryHandler.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Module.h" - -namespace cps { - -class TypeSystem; -class RuntimeCheck; -class ExceptionHandler; - -// Explicit memory builtins and keyword NEW own entries in this allocation list. -// Internal STRING/ARRAY storage retains its existing lifetime rules. -class MemoryHandler { - llvm::LLVMContext &Context; - llvm::IRBuilder<> &Builder; - llvm::Module &Module; - TypeSystem &Types; - RuntimeCheck &Checker; - ExceptionHandler &Exceptions; - llvm::IntegerType *SizeTy = nullptr; - llvm::StructType *NodeTy = nullptr; - llvm::GlobalVariable *Head = nullptr; - llvm::FunctionCallee Malloc, Free, Realloc; - llvm::Function *AllocateFn = nullptr, *ReleaseFn = nullptr, *FindFn = nullptr, - *ResizeFn = nullptr; - llvm::Value *AllocationError = nullptr, *TrackingError = nullptr, *InvalidAddress = nullptr; - - void ensureRuntime(); - llvm::Function *createHelper(const char *Name, llvm::ArrayRef<llvm::Type *> Args); - void buildFind(); - void buildAllocate(); - void buildRelease(); - void buildResize(); - -public: - MemoryHandler(llvm::LLVMContext &C, llvm::IRBuilder<> &B, llvm::Module &M, - TypeSystem &TS, RuntimeCheck &RC, ExceptionHandler &EH) - : Context(C), Builder(B), Module(M), Types(TS), Checker(RC), Exceptions(EH) {} - - llvm::Value *emitAllocate(llvm::Value *Size, int Line); - void emitValidate(llvm::Value *Address, int Line); - llvm::Value *emitRelease(llvm::Value *Address, int Line); - llvm::Value *emitResize(llvm::Value *Address, llvm::Value *Size, int Line); -}; - -} // namespace cps 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)) { diff --git a/pseudocode-lib/memory.cps b/pseudocode-lib/memory.cps new file mode 100644 index 0000000..26287c3 --- /dev/null +++ b/pseudocode-lib/memory.cps @@ -0,0 +1,99 @@ +#LIB +INCLUDE_OBJ "memory.o" +TYPE _MEMORY_ADDRESS = C_TYPE("void*") +TYPE _MEMORY_SIZE = C_TYPE("unsigned long long") +TYPE _MEMORY_C_BOOL = C_TYPE("_Bool") +TYPE _MEMORY_C_SIGNED_CHAR = C_TYPE("signed char") +TYPE _MEMORY_C_UNSIGNED_CHAR = C_TYPE("unsigned char") +TYPE _MEMORY_C_SHORT = C_TYPE("short") +TYPE _MEMORY_C_UNSIGNED_SHORT = C_TYPE("unsigned short") +TYPE _MEMORY_C_UNSIGNED_INT = C_TYPE("unsigned int") +TYPE _MEMORY_C_LONG = C_TYPE("long") +TYPE _MEMORY_C_UNSIGNED_LONG = C_TYPE("unsigned long") +TYPE _MEMORY_C_LONG_LONG = C_TYPE("long long") +EXTERN "C" EXTERN_FUNCTION _memory_impl_malloc(_MEMORY_SIZE) RETURNS _MEMORY_ADDRESS +EXTERN "C" EXTERN_FUNCTION _memory_impl_realloc(_MEMORY_ADDRESS, _MEMORY_SIZE) RETURNS _MEMORY_ADDRESS +EXTERN "C" EXTERN_FUNCTION _memory_impl_free(_MEMORY_ADDRESS) RETURNS _MEMORY_ADDRESS +EXTERN "C" EXTERN_PROCEDURE _memory_impl_validate(_MEMORY_ADDRESS) + +OVERLOAD FUNCTION MALLOC(Bytes : INTEGER) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_malloc((_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION REALLOC(Address : _MEMORY_ADDRESS, Bytes : INTEGER) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_realloc(Address, (_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION MALLOC(Bytes : _MEMORY_C_BOOL) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_malloc((_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION REALLOC(Address : _MEMORY_ADDRESS, Bytes : _MEMORY_C_BOOL) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_realloc(Address, (_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION MALLOC(Bytes : C_CHAR) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_malloc((_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION REALLOC(Address : _MEMORY_ADDRESS, Bytes : C_CHAR) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_realloc(Address, (_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION MALLOC(Bytes : _MEMORY_C_SIGNED_CHAR) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_malloc((_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION REALLOC(Address : _MEMORY_ADDRESS, Bytes : _MEMORY_C_SIGNED_CHAR) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_realloc(Address, (_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION MALLOC(Bytes : _MEMORY_C_UNSIGNED_CHAR) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_malloc((_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION REALLOC(Address : _MEMORY_ADDRESS, Bytes : _MEMORY_C_UNSIGNED_CHAR) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_realloc(Address, (_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION MALLOC(Bytes : _MEMORY_C_SHORT) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_malloc((_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION REALLOC(Address : _MEMORY_ADDRESS, Bytes : _MEMORY_C_SHORT) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_realloc(Address, (_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION MALLOC(Bytes : _MEMORY_C_UNSIGNED_SHORT) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_malloc((_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION REALLOC(Address : _MEMORY_ADDRESS, Bytes : _MEMORY_C_UNSIGNED_SHORT) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_realloc(Address, (_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION MALLOC(Bytes : C_INT) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_malloc((_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION REALLOC(Address : _MEMORY_ADDRESS, Bytes : C_INT) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_realloc(Address, (_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION MALLOC(Bytes : _MEMORY_C_UNSIGNED_INT) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_malloc((_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION REALLOC(Address : _MEMORY_ADDRESS, Bytes : _MEMORY_C_UNSIGNED_INT) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_realloc(Address, (_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION MALLOC(Bytes : _MEMORY_C_LONG) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_malloc((_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION REALLOC(Address : _MEMORY_ADDRESS, Bytes : _MEMORY_C_LONG) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_realloc(Address, (_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION MALLOC(Bytes : _MEMORY_C_UNSIGNED_LONG) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_malloc((_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION REALLOC(Address : _MEMORY_ADDRESS, Bytes : _MEMORY_C_UNSIGNED_LONG) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_realloc(Address, (_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION MALLOC(Bytes : _MEMORY_C_LONG_LONG) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_malloc((_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION REALLOC(Address : _MEMORY_ADDRESS, Bytes : _MEMORY_C_LONG_LONG) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_realloc(Address, (_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION MALLOC(Bytes : _MEMORY_SIZE) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_malloc((_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION REALLOC(Address : _MEMORY_ADDRESS, Bytes : _MEMORY_SIZE) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_realloc(Address, (_MEMORY_SIZE)Bytes) +ENDFUNCTION +OVERLOAD FUNCTION FREE(Address : _MEMORY_ADDRESS) RETURNS _MEMORY_ADDRESS + RETURN _memory_impl_free(Address) +ENDFUNCTION diff --git a/pseudocode-lib/std.cps b/pseudocode-lib/std.cps index 0ec4b9d..6068bd0 100644 --- a/pseudocode-lib/std.cps +++ b/pseudocode-lib/std.cps @@ -1,2 +1,3 @@ #LIB INCLUDE "string.cps" +INCLUDE "memory.cps" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c5b33a1..b7b2e45 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -31,16 +31,18 @@ foreach(suite i ii iii) endforeach() endforeach() -# Keep the library beside its built objects without assuming a build directory name. -set(cpsc_library_case "${CMAKE_BINARY_DIR}/test-cases/i/exp-string") -foreach(file program.cps case.json expected.txt expected-stderr.txt) - configure_file("library/exp-string/${file}" "${cpsc_library_case}/${file}" COPYONLY) +# Keep the libraries beside their built objects without assuming a build directory name. +foreach(library exp-string exp-memory) + set(cpsc_library_case "${CMAKE_BINARY_DIR}/test-cases/i/${library}") + foreach(file program.cps case.json expected.txt expected-stderr.txt) + configure_file("library/${library}/${file}" "${cpsc_library_case}/${file}" COPYONLY) + endforeach() + add_test(NAME "i/${library}" COMMAND "${PERL_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/run.pl" + --case "${cpsc_library_case}/case.json" --cpsc "$<TARGET_FILE:cpsc>" + --run-mode "${CPSC_RUN_MODE}" --perl "${PERL_EXECUTABLE}" + --cc "${CMAKE_C_COMPILER}" --cxx "${CMAKE_CXX_COMPILER}" + --seed "${CPSC_TEST_SEED}" --count "${CPSC_TEST_COUNT}" + --results "${CMAKE_BINARY_DIR}/test-results") + set_tests_properties("i/${library}" PROPERTIES LABELS "i" TIMEOUT 900) endforeach() -add_test(NAME "i/exp-string" COMMAND "${PERL_EXECUTABLE}" - "${CMAKE_CURRENT_SOURCE_DIR}/run.pl" - --case "${cpsc_library_case}/case.json" --cpsc "$<TARGET_FILE:cpsc>" - --run-mode "${CPSC_RUN_MODE}" --perl "${PERL_EXECUTABLE}" - --cc "${CMAKE_C_COMPILER}" --cxx "${CMAKE_CXX_COMPILER}" - --seed "${CPSC_TEST_SEED}" --count "${CPSC_TEST_COUNT}" - --results "${CMAKE_BINARY_DIR}/test-results") -set_tests_properties("i/exp-string" PROPERTIES LABELS "i" TIMEOUT 900) diff --git a/tests/iii/memory-call-arity/case.json b/tests/iii/memory-call-arity/case.json index 3159635..08583d7 100644 --- a/tests/iii/memory-call-arity/case.json +++ b/tests/iii/memory-call-arity/case.json @@ -1,7 +1,7 @@ { "errors": [ { - "reason": "FREE\\ expects\\ 1\\ arg", + "reason": "No matching overload for FREE", "line": 1 } ] diff --git a/tests/iii/memory-no-std/case.json b/tests/iii/memory-no-std/case.json new file mode 100644 index 0000000..34a3e90 --- /dev/null +++ b/tests/iii/memory-no-std/case.json @@ -0,0 +1,27 @@ +{ + "compiler_args": [ + "-no-std" + ], + "errors": [ + { + "reason": "Call to undefined function MALLOC", + "line": 6 + }, + { + "reason": "Call to undefined function REALLOC", + "line": 7 + }, + { + "reason": "Call to undefined function FREE", + "line": 8 + }, + { + "reason": "NEW/DELETE requires memory.cps", + "line": 9 + }, + { + "reason": "NEW/DELETE requires memory.cps", + "line": 10 + } + ] +} diff --git a/tests/iii/memory-no-std/program.cps b/tests/iii/memory-no-std/program.cps new file mode 100644 index 0000000..69941d6 --- /dev/null +++ b/tests/iii/memory-no-std/program.cps @@ -0,0 +1,10 @@ +CLASS Item + Value : INTEGER +ENDCLASS +TYPE Raw = C_TYPE("void*") +DECLARE Obj : Item +MALLOC(8) +REALLOC((Raw)0, 8) +FREE((Raw)0) +Obj <- NEW Item() +DELETE Obj diff --git a/tests/iii/memory-pointer-type/case.json b/tests/iii/memory-pointer-type/case.json index 557c4b9..973a4c9 100644 --- a/tests/iii/memory-pointer-type/case.json +++ b/tests/iii/memory-pointer-type/case.json @@ -1,7 +1,7 @@ { "errors": [ { - "reason": "Cannot\\ implicitly\\ convert", + "reason": "No matching overload for FREE", "line": 4 } ] diff --git a/tests/iii/memory-size-type/case.json b/tests/iii/memory-size-type/case.json index 6354ba8..d5848f2 100644 --- a/tests/iii/memory-size-type/case.json +++ b/tests/iii/memory-size-type/case.json @@ -1,11 +1,11 @@ { "errors": [ { - "reason": "Allocation\\ size\\ must\\ be\\ INTEGER\\ or\\ a\\ C\\ integer", + "reason": "No matching overload for MALLOC", "line": 1 }, { - "reason": "Allocation size must be INTEGER or a C integer", + "reason": "No matching overload for REALLOC", "line": 4 } ] diff --git a/tests/iii/template-explicit-errors/case.json b/tests/iii/template-explicit-errors/case.json index 657f69e..e705356 100644 --- a/tests/iii/template-explicit-errors/case.json +++ b/tests/iii/template-explicit-errors/case.json @@ -4,7 +4,7 @@ {"reason": "No matching overload for Max", "line": 20}, {"reason": "Explicit template arguments require a function template: Plain", "line": 21}, {"reason": "Explicit template arguments are not allowed on builtin EOF", "line": 22}, - {"reason": "Explicit template arguments are not allowed on builtin MALLOC", "line": 23}, + {"reason": "Explicit template arguments require a function template: MALLOC", "line": 23}, {"reason": "Ambiguous call to Tied", "line": 24} ] } diff --git a/tests/library/exp-memory/case.json b/tests/library/exp-memory/case.json new file mode 100644 index 0000000..7a60233 --- /dev/null +++ b/tests/library/exp-memory/case.json @@ -0,0 +1 @@ +{"sources":["program.cps","../../../pseudocode-lib/memory.cps"],"compiler_args":["-no-std"]} diff --git a/tests/library/exp-memory/expected-stderr.txt b/tests/library/exp-memory/expected-stderr.txt new file mode 100644 index 0000000..9ade8ef --- /dev/null +++ b/tests/library/exp-memory/expected-stderr.txt @@ -0,0 +1,3 @@ +[Fatal] line 63: Invalid memory address (not an active allocation) +[Fatal] line 68: Memory allocation failed +[Fatal] line 73: Memory allocation failed diff --git a/tests/library/exp-memory/expected.txt b/tests/library/exp-memory/expected.txt new file mode 100644 index 0000000..b2272a2 --- /dev/null +++ b/tests/library/exp-memory/expected.txt @@ -0,0 +1,17 @@ +TRUETRUE +TRUETRUE +TRUETRUE +TRUETRUE +TRUETRUE +TRUETRUE +TRUETRUE +TRUETRUE +TRUETRUE +TRUETRUE +TRUETRUE +TRUETRUE +7 +17 +1 +2 +3 diff --git a/tests/library/exp-memory/notes.md b/tests/library/exp-memory/notes.md new file mode 100644 index 0000000..bc983f5 --- /dev/null +++ b/tests/library/exp-memory/notes.md @@ -0,0 +1,3 @@ +# Explicit memory library trace + +This compatibility fixture imports memory.cps with -no-std and no STRING library. Each supported C integer type produces null for zero allocation and resizing, then supports a nested allocation/resize/release. NEW storage retains 7 through REALLOC and is accepted by DELETE. MALLOC storage holds 17 and is accepted by DELETE. FREE releases NEW storage, so a subsequent DELETE throws before destruction. Negative signed-char and maximum unsigned-long-long sizes both fail allocation, confirming signed extension and unsigned range preservation. Expected values are derived directly from these assignments; human review pending. diff --git a/tests/library/exp-memory/program.cps b/tests/library/exp-memory/program.cps new file mode 100644 index 0000000..5b0eb07 --- /dev/null +++ b/tests/library/exp-memory/program.cps @@ -0,0 +1,76 @@ +INCLUDE "../../../pseudocode-lib/memory.cps" +TYPE Raw = C_TYPE("void*") +DECLARE Empty : Raw +TYPE Size0 = C_TYPE("_Bool") +TYPE Size1 = C_TYPE("char") +TYPE Size2 = C_TYPE("signed char") +TYPE Size3 = C_TYPE("unsigned char") +TYPE Size4 = C_TYPE("short") +TYPE Size5 = C_TYPE("unsigned short") +TYPE Size6 = C_TYPE("int") +TYPE Size7 = C_TYPE("unsigned int") +TYPE Size8 = C_TYPE("long") +TYPE Size9 = C_TYPE("unsigned long") +TYPE Size10 = C_TYPE("long long") +TYPE Size11 = C_TYPE("unsigned long long") +OUTPUT malloc((Size0)0) = Empty, realloc(Empty, (Size0)0) = Empty +FREE(REALLOC(MALLOC((Size0)1), (Size0)2)) +OUTPUT malloc((Size1)0) = Empty, realloc(Empty, (Size1)0) = Empty +FREE(REALLOC(MALLOC((Size1)1), (Size1)2)) +OUTPUT malloc((Size2)0) = Empty, realloc(Empty, (Size2)0) = Empty +FREE(REALLOC(MALLOC((Size2)1), (Size2)2)) +OUTPUT malloc((Size3)0) = Empty, realloc(Empty, (Size3)0) = Empty +FREE(REALLOC(MALLOC((Size3)1), (Size3)2)) +OUTPUT malloc((Size4)0) = Empty, realloc(Empty, (Size4)0) = Empty +FREE(REALLOC(MALLOC((Size4)1), (Size4)2)) +OUTPUT malloc((Size5)0) = Empty, realloc(Empty, (Size5)0) = Empty +FREE(REALLOC(MALLOC((Size5)1), (Size5)2)) +OUTPUT malloc((Size6)0) = Empty, realloc(Empty, (Size6)0) = Empty +FREE(REALLOC(MALLOC((Size6)1), (Size6)2)) +OUTPUT malloc((Size7)0) = Empty, realloc(Empty, (Size7)0) = Empty +FREE(REALLOC(MALLOC((Size7)1), (Size7)2)) +OUTPUT malloc((Size8)0) = Empty, realloc(Empty, (Size8)0) = Empty +FREE(REALLOC(MALLOC((Size8)1), (Size8)2)) +OUTPUT malloc((Size9)0) = Empty, realloc(Empty, (Size9)0) = Empty +FREE(REALLOC(MALLOC((Size9)1), (Size9)2)) +OUTPUT malloc((Size10)0) = Empty, realloc(Empty, (Size10)0) = Empty +FREE(REALLOC(MALLOC((Size10)1), (Size10)2)) +OUTPUT malloc((Size11)0) = Empty, realloc(Empty, (Size11)0) = Empty +FREE(REALLOC(MALLOC((Size11)1), (Size11)2)) +CLASS Item + Value : INTEGER +ENDCLASS +TYPE ItemPointer = ^Item +DECLARE Obj : Item +DECLARE P : ItemPointer +DECLARE Address : Raw +Obj <- NEW Item() +Obj.Value <- 7 +P <- ^Obj +Address <- REALLOC((Raw)P, 16) +P <- (ItemPointer)Address +OUTPUT P^.Value +DELETE P^ +P <- (ItemPointer)MALLOC(8) +P^.Value <- 17 +OUTPUT P^.Value +DELETE P^ +Obj <- NEW Item() +P <- ^Obj +Address <- (Raw)P +FREE(Address) +TRY + DELETE Obj +CATCH + OUTPUT 1 +ENDTRY +TRY + MALLOC((Size2)-1) +CATCH + OUTPUT 2 +ENDTRY +TRY + MALLOC((Size11)-1) +CATCH + OUTPUT 3 +ENDTRY diff --git a/tests/library/exp-string/case.json b/tests/library/exp-string/case.json index 4e37b1c..824231f 100644 --- a/tests/library/exp-string/case.json +++ b/tests/library/exp-string/case.json @@ -1 +1 @@ -{"sources":["program.cps","../../../pseudocode-lib/string.cps"],"compiler_args":["-no-std"]} +{"sources":["program.cps","../../../pseudocode-lib/std.cps","../../../pseudocode-lib/string.cps","../../../pseudocode-lib/memory.cps"],"compiler_args":["-no-std"]} diff --git a/tests/library/exp-string/program.cps b/tests/library/exp-string/program.cps index 2e45dc3..d66a01f 100644 --- a/tests/library/exp-string/program.cps +++ b/tests/library/exp-string/program.cps @@ -1,4 +1,4 @@ -INCLUDE "../../../pseudocode-lib/string.cps" +INCLUDE "../../../pseudocode-lib/std.cps" FUNCTION Echo(S : STRING) RETURNS STRING RETURN S ENDFUNCTION |
