1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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");
}
|