aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--include/cps/CodeGen.h4
-rw-r--r--include/cps/MemoryHandler.h46
-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
-rw-r--r--pseudocode-lib/memory.cps99
-rw-r--r--pseudocode-lib/std.cps1
-rw-r--r--tests/CMakeLists.txt26
-rw-r--r--tests/iii/memory-call-arity/case.json2
-rw-r--r--tests/iii/memory-no-std/case.json27
-rw-r--r--tests/iii/memory-no-std/program.cps10
-rw-r--r--tests/iii/memory-pointer-type/case.json2
-rw-r--r--tests/iii/memory-size-type/case.json4
-rw-r--r--tests/iii/template-explicit-errors/case.json2
-rw-r--r--tests/library/exp-memory/case.json1
-rw-r--r--tests/library/exp-memory/expected-stderr.txt3
-rw-r--r--tests/library/exp-memory/expected.txt17
-rw-r--r--tests/library/exp-memory/notes.md3
-rw-r--r--tests/library/exp-memory/program.cps76
-rw-r--r--tests/library/exp-string/case.json2
-rw-r--r--tests/library/exp-string/program.cps2
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