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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
|
//===-- JIT.cpp - LLVM Just in Time Compiler ------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This tool implements a just-in-time compiler for LLVM, allowing direct
// execution of LLVM bytecode in an efficient manner.
//
//===----------------------------------------------------------------------===//
#include "JIT.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Function.h"
#include "llvm/GlobalVariable.h"
#include "llvm/ModuleProvider.h"
#include "llvm/CodeGen/MachineCodeEmitter.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/ExecutionEngine/GenericValue.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetJITInfo.h"
#include "Support/DynamicLinker.h"
using namespace llvm;
JIT::JIT(ModuleProvider *MP, TargetMachine &tm, TargetJITInfo &tji)
: ExecutionEngine(MP), TM(tm), TJI(tji), PM(MP) {
setTargetData(TM.getTargetData());
// Initialize MCE
MCE = createEmitter(*this);
// Add target data
PM.add (new TargetData (TM.getTargetData ()));
// Compile LLVM Code down to machine code in the intermediate representation
TJI.addPassesToJITCompile(PM);
// Turn the machine code intermediate representation into bytes in memory that
// may be executed.
if (TM.addPassesToEmitMachineCode(PM, *MCE)) {
std::cerr << "lli: target '" << TM.getName()
<< "' doesn't support machine code emission!\n";
abort();
}
}
JIT::~JIT() {
delete MCE;
delete &TM;
}
/// run - Start execution with the specified function and arguments.
///
GenericValue JIT::runFunction(Function *F,
const std::vector<GenericValue> &ArgValues) {
assert (F && "Function *F was null at entry to run()");
GenericValue rv;
if (ArgValues.size() == 3) {
int (*PF)(int, char **, const char **) =
(int(*)(int, char **, const char **))getPointerToFunction(F);
assert(PF && "Pointer to fn's code was null after getPointerToFunction");
// Call the function.
int ExitCode = PF(ArgValues[0].IntVal, (char **) GVTOP (ArgValues[1]),
(const char **) GVTOP (ArgValues[2]));
rv.IntVal = ExitCode;
} else {
// FIXME: This code should handle a couple of common cases efficiently, but
// it should also implement the general case by code-gening a new anonymous
// nullary function to call.
assert(ArgValues.size() == 1);
void (*PF)(int) = (void(*)(int))getPointerToFunction(F);
assert(PF && "Pointer to fn's code was null after getPointerToFunction");
PF(ArgValues[0].IntVal);
}
return rv;
}
/// runJITOnFunction - Run the FunctionPassManager full of
/// just-in-time compilation passes on F, hopefully filling in
/// GlobalAddress[F] with the address of F's machine code.
///
void JIT::runJITOnFunction(Function *F) {
static bool isAlreadyCodeGenerating = false;
assert(!isAlreadyCodeGenerating && "Error: Recursive compilation detected!");
// JIT the function
isAlreadyCodeGenerating = true;
PM.run(*F);
isAlreadyCodeGenerating = false;
// If the function referred to a global variable that had not yet been
// emitted, it allocates memory for the global, but doesn't emit it yet. Emit
// all of these globals now.
while (!PendingGlobals.empty()) {
const GlobalVariable *GV = PendingGlobals.back();
PendingGlobals.pop_back();
EmitGlobalVariable(GV);
}
}
/// getPointerToFunction - This method is used to get the address of the
/// specified function, compiling it if neccesary.
///
void *JIT::getPointerToFunction(Function *F) {
if (void *Addr = getPointerToGlobalIfAvailable(F))
return Addr; // Check if function already code gen'd
// Make sure we read in the function if it exists in this Module
try {
MP->materializeFunction(F);
} catch (...) {
std::cerr << "Error parsing bytecode file!\n";
abort();
}
if (F->isExternal()) {
void *Addr = getPointerToNamedFunction(F->getName());
addGlobalMapping(F, Addr);
return Addr;
}
runJITOnFunction(F);
void *Addr = getPointerToGlobalIfAvailable(F);
assert(Addr && "Code generation didn't add function to GlobalAddress table!");
return Addr;
}
// getPointerToFunctionOrStub - If the specified function has been
// code-gen'd, return a pointer to the function. If not, compile it, or use
// a stub to implement lazy compilation if available.
//
void *JIT::getPointerToFunctionOrStub(Function *F) {
// If we have already code generated the function, just return the address.
if (void *Addr = getPointerToGlobalIfAvailable(F))
return Addr;
// If the target supports "stubs" for functions, get a stub now.
if (void *Ptr = TJI.getJITStubForFunction(F, *MCE))
return Ptr;
// Otherwise, if the target doesn't support it, just codegen the function.
return getPointerToFunction(F);
}
/// getOrEmitGlobalVariable - Return the address of the specified global
/// variable, possibly emitting it to memory if needed. This is used by the
/// Emitter.
void *JIT::getOrEmitGlobalVariable(const GlobalVariable *GV) {
void *Ptr = getPointerToGlobalIfAvailable(GV);
if (Ptr) return Ptr;
// If the global is external, just remember the address.
if (GV->isExternal()) {
Ptr = GetAddressOfSymbol(GV->getName().c_str());
if (Ptr == 0) {
std::cerr << "Could not resolve external global address: "
<< GV->getName() << "\n";
abort();
}
} else {
// If the global hasn't been emitted to memory yet, allocate space. We will
// actually initialize the global after current function has finished
// compilation.
Ptr =new char[getTargetData().getTypeSize(GV->getType()->getElementType())];
PendingGlobals.push_back(GV);
}
addGlobalMapping(GV, Ptr);
return Ptr;
}
/// recompileAndRelinkFunction - This method is used to force a function
/// which has already been compiled, to be compiled again, possibly
/// after it has been modified. Then the entry to the old copy is overwritten
/// with a branch to the new copy. If there was no old copy, this acts
/// just like JIT::getPointerToFunction().
///
void *JIT::recompileAndRelinkFunction(Function *F) {
void *OldAddr = getPointerToGlobalIfAvailable(F);
// If it's not already compiled there is no reason to patch it up.
if (OldAddr == 0) { return getPointerToFunction(F); }
// Delete the old function mapping.
addGlobalMapping(F, 0);
// Recodegen the function
runJITOnFunction(F);
// Update state, forward the old function to the new function.
void *Addr = getPointerToGlobalIfAvailable(F);
assert(Addr && "Code generation didn't add function to GlobalAddress table!");
TJI.replaceMachineCodeForFunction(OldAddr, Addr);
return Addr;
}
|