forked from JuliaLang/julia
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathllvm-lower-handlers.cpp
251 lines (232 loc) · 8.59 KB
/
llvm-lower-handlers.cpp
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
// This file is a part of Julia. License is MIT: https://julialang.org/license
#include "llvm-version.h"
#include <llvm-c/Core.h>
#include <llvm-c/Types.h>
#include <llvm/ADT/DepthFirstIterator.h>
#include <llvm/Analysis/CFG.h>
#include <llvm/IR/BasicBlock.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/IntrinsicInst.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Value.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/Pass.h>
#include <llvm/Support/Debug.h>
#include <llvm/Transforms/Utils/BasicBlockUtils.h>
#include "julia.h"
#include "julia_assert.h"
#define DEBUG_TYPE "lower_handlers"
#undef DEBUG
using namespace llvm;
/* Lowers Julia Exception Handlers and colors EH frames.
*
* Our task is to lower:
* call void @julia.except_enter()
* <...>
* call void jl_pop_handler(1)
*
* to
*
* call void @jl_enter_handler(jl_handler *%buff)
* <...>
* call void jl_pop_handler(1)
*
* Where buff is an appropriate stack slot handler.
*
* We make the following assumptions:
* - All EH frames are completely nested.
* - The exception nestedness of a BB is not dynamic. I.e. we don't allow
* the following:
* br i1 %cond, %left, %right
* / \
* except.enter br mid
* br mid |
* \ /
* br i1 %cond, %left2, %right2
* / \
* jl_pop_hander ret
* ret
*
* The frontend doesn't emit structures like this. However, the optimizer
* could easily introduce them, so this pass should run early after IRGen.
*
* Because of these assumptions, the algorithm is very simple. We simply label
* the handler depth at every basic block using a DFS search. For each enter
* we encounter, we record the current depth and then allocate an exception
* handler frame for every level.
*
* As an additional optimization, we also insert lifetime intrinsics for the
* handler structures to tell LLVM that it is free to re-use the stack slot
* while the handler is not being used.
*/
struct LowerExcHandlers : public FunctionPass {
static char ID;
LowerExcHandlers() : FunctionPass(ID)
{}
private:
Function *except_enter_func;
Function *leave_func;
Function *jlenter_func;
Function *setjmp_func;
Function *lifetime_start;
Function *lifetime_end;
bool doInitialization(Module &M) override;
bool runOnFunction(Function &F) override;
};
/*
* If the module doesn't have declarations for the jl_enter_handler and setjmp
* functions, insert them.
*/
static void ensure_enter_function(Module &M)
{
auto T_int8 = Type::getInt8Ty(M.getContext());
auto T_pint8 = PointerType::get(T_int8, 0);
auto T_void = Type::getVoidTy(M.getContext());
auto T_int32 = Type::getInt32Ty(M.getContext());
if (!M.getNamedValue("jl_enter_handler")) {
std::vector<Type*> ehargs(0);
ehargs.push_back(T_pint8);
Function::Create(FunctionType::get(T_void, ehargs, false),
Function::ExternalLinkage, "jl_enter_handler", &M);
}
if (!M.getNamedValue(jl_setjmp_name)) {
std::vector<Type*> args2(0);
args2.push_back(T_pint8);
#ifndef _OS_WINDOWS_
args2.push_back(T_int32);
#endif
Function::Create(FunctionType::get(T_int32, args2, false),
Function::ExternalLinkage, jl_setjmp_name, &M)
->addFnAttr(Attribute::ReturnsTwice);
}
}
bool LowerExcHandlers::doInitialization(Module &M) {
except_enter_func = M.getFunction("julia.except_enter");
if (!except_enter_func)
return false;
ensure_enter_function(M);
leave_func = M.getFunction("jl_pop_handler");
jlenter_func = M.getFunction("jl_enter_handler");
setjmp_func = M.getFunction(jl_setjmp_name);
auto T_pint8 = Type::getInt8PtrTy(M.getContext(), 0);
lifetime_start = Intrinsic::getDeclaration(&M, Intrinsic::lifetime_start, { T_pint8 });
lifetime_end = Intrinsic::getDeclaration(&M, Intrinsic::lifetime_end, { T_pint8 });
return true;
}
bool LowerExcHandlers::runOnFunction(Function &F) {
if (!except_enter_func)
return false; // No EH frames in this module
/* Step 1: EH Depth Numbering */
std::map<llvm::CallInst *, int> EnterDepth;
std::map<llvm::CallInst *, int> LeaveDepth;
std::map<BasicBlock *, int> ExitDepth;
int MaxDepth = 0;
// Compute EH Depth at each basic block using a DFS traversal.
for (df_iterator<BasicBlock *> I = df_begin(&F.getEntryBlock()),
E = df_end(&F.getEntryBlock()); I != E; ++I) {
auto *BB = *I;
int Depth = 0;
/* Here we use the assumption that all incoming edges have the same
* EH depth.
*/
for (auto *Pred : predecessors(BB)) {
auto it = ExitDepth.find(Pred);
if (it != ExitDepth.end()) {
Depth = it->second;
break;
}
}
/* Compute the depth within the basic block */
for (auto &I : *BB) {
auto *CI = dyn_cast<CallInst>(&I);
if (!CI)
continue;
Function *Callee = CI->getCalledFunction();
if (!Callee)
continue;
if (Callee == except_enter_func)
EnterDepth[CI] = Depth++;
else if (Callee == leave_func) {
LeaveDepth[CI] = Depth;
Depth -= cast<ConstantInt>(CI->getArgOperand(0))->getLimitedValue();
}
assert(Depth >= 0);
if (Depth > MaxDepth)
MaxDepth = Depth;
}
/* Remember the depth at the BB boundary */
ExitDepth[BB] = Depth;
}
/* Step 2: EH Frame lowering */
// Allocate stack space for each handler. We allocate these as separate
// allocas so the optimizer can later merge and reaarange them if it wants
// to.
Value *handler_sz = ConstantInt::get(Type::getInt32Ty(F.getContext()),
sizeof(jl_handler_t));
Value *handler_sz64 = ConstantInt::get(Type::getInt64Ty(F.getContext()),
sizeof(jl_handler_t));
Instruction *firstInst = &F.getEntryBlock().front();
std::vector<AllocaInst *> buffs;
for (int i = 0; i < MaxDepth; ++i) {
auto *buff = new AllocaInst(Type::getInt8Ty(F.getContext()), 0,
handler_sz, Align(16), "", firstInst);
buffs.push_back(buff);
}
// Lower enter funcs
for (auto it : EnterDepth) {
assert(it.second >= 0);
AllocaInst *buff = buffs[it.second];
CallInst *enter = it.first;
auto new_enter = CallInst::Create(jlenter_func, buff, "", enter);
Value *lifetime_args[] = {
handler_sz64,
buff
};
CallInst::Create(lifetime_start, lifetime_args, "", new_enter);
#ifndef _OS_WINDOWS_
// For LLVM 3.3 compatibility
Value *args[] = {buff,
ConstantInt::get(Type::getInt32Ty(F.getContext()), 0)};
auto sj = CallInst::Create(setjmp_func, args, "", enter);
#else
auto sj = CallInst::Create(setjmp_func, buff, "", enter);
#endif
// We need to mark this on the call site as well. See issue #6757
sj->setCanReturnTwice();
if (auto dbg = enter->getMetadata(LLVMContext::MD_dbg)) {
new_enter->setMetadata(LLVMContext::MD_dbg, dbg);
sj->setMetadata(LLVMContext::MD_dbg, dbg);
}
enter->replaceAllUsesWith(sj);
enter->eraseFromParent();
}
// Insert lifetime end intrinsics after every leave.
for (auto it : LeaveDepth) {
int StartDepth = it.second - 1;
int npops = cast<ConstantInt>(it.first->getArgOperand(0))->getLimitedValue();
for (int i = 0; i < npops; ++i) {
assert(StartDepth-i >= 0);
Value *lifetime_args[] = {
handler_sz64,
buffs[StartDepth-i]
};
auto LifetimeEnd = CallInst::Create(lifetime_end, lifetime_args);
LifetimeEnd->insertAfter(it.first);
}
}
return true;
}
char LowerExcHandlers::ID = 0;
static RegisterPass<LowerExcHandlers> X("LowerExcHandlers", "Lower Julia Exception Handlers",
false /* Only looks at CFG */,
false /* Analysis Pass */);
Pass *createLowerExcHandlersPass()
{
return new LowerExcHandlers();
}
extern "C" JL_DLLEXPORT void LLVMExtraAddLowerExcHandlersPass(LLVMPassManagerRef PM)
{
unwrap(PM)->add(createLowerExcHandlersPass());
}