Branch data Line data Source code
1 : : // This file is a part of Julia. License is MIT: https://julialang.org/license
2 : :
3 : : #include "llvm-version.h"
4 : : #include "passes.h"
5 : :
6 : : #include <llvm-c/Core.h>
7 : : #include <llvm-c/Types.h>
8 : :
9 : : #include <llvm/ADT/DepthFirstIterator.h>
10 : : #include <llvm/Analysis/CFG.h>
11 : : #include <llvm/IR/BasicBlock.h>
12 : : #include <llvm/IR/Constants.h>
13 : : #include <llvm/IR/Function.h>
14 : : #include <llvm/IR/Instructions.h>
15 : : #include <llvm/IR/IntrinsicInst.h>
16 : : #include <llvm/IR/Module.h>
17 : : #include <llvm/IR/Value.h>
18 : : #include <llvm/IR/LegacyPassManager.h>
19 : : #include <llvm/Pass.h>
20 : : #include <llvm/Support/Debug.h>
21 : : #include <llvm/Transforms/Utils/BasicBlockUtils.h>
22 : :
23 : : #include "julia.h"
24 : : #include "julia_assert.h"
25 : : #include "codegen_shared.h"
26 : : #include <map>
27 : :
28 : : #define DEBUG_TYPE "lower_handlers"
29 : : #undef DEBUG
30 : :
31 : : using namespace llvm;
32 : :
33 : : /* Lowers Julia Exception Handlers and colors EH frames.
34 : : *
35 : : * Our task is to lower:
36 : : * call void @julia.except_enter()
37 : : * <...>
38 : : * call void jl_pop_handler(1)
39 : : *
40 : : * to
41 : : *
42 : : * call void @jl_enter_handler(jl_handler *%buff)
43 : : * <...>
44 : : * call void jl_pop_handler(1)
45 : : *
46 : : * Where buff is an appropriate stack slot handler.
47 : : *
48 : : * We make the following assumptions:
49 : : * - All EH frames are completely nested.
50 : : * - The exception nestedness of a BB is not dynamic. I.e. we don't allow
51 : : * the following:
52 : : * br i1 %cond, %left, %right
53 : : * / \
54 : : * except.enter br mid
55 : : * br mid |
56 : : * \ /
57 : : * br i1 %cond, %left2, %right2
58 : : * / \
59 : : * jl_pop_hander ret
60 : : * ret
61 : : *
62 : : * The frontend doesn't emit structures like this. However, the optimizer
63 : : * could easily introduce them, so this pass should run early after IRGen.
64 : : *
65 : : * Because of these assumptions, the algorithm is very simple. We simply label
66 : : * the handler depth at every basic block using a DFS search. For each enter
67 : : * we encounter, we record the current depth and then allocate an exception
68 : : * handler frame for every level.
69 : : *
70 : : * As an additional optimization, we also insert lifetime intrinsics for the
71 : : * handler structures to tell LLVM that it is free to re-use the stack slot
72 : : * while the handler is not being used.
73 : : */
74 : :
75 : : namespace {
76 : : /*
77 : : * If the module doesn't have declarations for the jl_enter_handler and setjmp
78 : : * functions, insert them.
79 : : */
80 : 62715 : static void ensure_enter_function(Module &M)
81 : : {
82 : 62715 : auto T_int8 = Type::getInt8Ty(M.getContext());
83 : 62715 : auto T_pint8 = PointerType::get(T_int8, 0);
84 : 62715 : auto T_void = Type::getVoidTy(M.getContext());
85 : 62715 : auto T_int32 = Type::getInt32Ty(M.getContext());
86 [ + + ]: 62715 : if (!M.getNamedValue(XSTR(jl_enter_handler))) {
87 : 2426 : std::vector<Type*> ehargs(0);
88 : 2426 : ehargs.push_back(T_pint8);
89 : 2426 : Function::Create(FunctionType::get(T_void, ehargs, false),
90 : : Function::ExternalLinkage, XSTR(jl_enter_handler), &M);
91 : : }
92 [ + + ]: 62715 : if (!M.getNamedValue(jl_setjmp_name)) {
93 : 2426 : std::vector<Type*> args2(0);
94 : 2426 : args2.push_back(T_pint8);
95 : : #ifndef _OS_WINDOWS_
96 : 2426 : args2.push_back(T_int32);
97 : : #endif
98 : : Function::Create(FunctionType::get(T_int32, args2, false),
99 : : Function::ExternalLinkage, jl_setjmp_name, &M)
100 : 2426 : ->addFnAttr(Attribute::ReturnsTwice);
101 : : }
102 : 62715 : }
103 : :
104 : 137811 : static bool lowerExcHandlers(Function &F) {
105 : 137811 : Module &M = *F.getParent();
106 : 137811 : Function *except_enter_func = M.getFunction("julia.except_enter");
107 [ + + ]: 137811 : if (!except_enter_func)
108 : 75096 : return false; // No EH frames in this module
109 : 62715 : ensure_enter_function(M);
110 : 62715 : Function *leave_func = M.getFunction(XSTR(jl_pop_handler));
111 : 62715 : Function *jlenter_func = M.getFunction(XSTR(jl_enter_handler));
112 : 62715 : Function *setjmp_func = M.getFunction(jl_setjmp_name);
113 : :
114 : 62715 : auto T_pint8 = Type::getInt8PtrTy(M.getContext(), 0);
115 : 62715 : Function *lifetime_start = Intrinsic::getDeclaration(&M, Intrinsic::lifetime_start, { T_pint8 });
116 : 62715 : Function *lifetime_end = Intrinsic::getDeclaration(&M, Intrinsic::lifetime_end, { T_pint8 });
117 : :
118 : : /* Step 1: EH Depth Numbering */
119 : 125430 : std::map<llvm::CallInst *, int> EnterDepth;
120 : 125430 : std::map<llvm::CallInst *, int> LeaveDepth;
121 : 125430 : std::map<BasicBlock *, int> ExitDepth;
122 : 62715 : int MaxDepth = 0;
123 : : // Compute EH Depth at each basic block using a DFS traversal.
124 : 761546 : for (df_iterator<BasicBlock *> I = df_begin(&F.getEntryBlock()),
125 [ + + ]: 698831 : E = df_end(&F.getEntryBlock()); I != E; ++I) {
126 : 636116 : auto *BB = *I;
127 : 636116 : int Depth = 0;
128 : : /* Here we use the assumption that all incoming edges have the same
129 : : * EH depth.
130 : : */
131 [ + + ]: 708754 : for (auto *Pred : predecessors(BB)) {
132 : 646039 : auto it = ExitDepth.find(Pred);
133 [ + + ]: 646039 : if (it != ExitDepth.end()) {
134 : 573401 : Depth = it->second;
135 : 573401 : break;
136 : : }
137 : : }
138 : : /* Compute the depth within the basic block */
139 [ + + ]: 10220800 : for (auto &I : *BB) {
140 : 9584730 : auto *CI = dyn_cast<CallInst>(&I);
141 [ + + ]: 9584730 : if (!CI)
142 : 8991040 : continue;
143 : 626877 : Function *Callee = CI->getCalledFunction();
144 [ + + ]: 626877 : if (!Callee)
145 : 33191 : continue;
146 [ + + ]: 593686 : if (Callee == except_enter_func)
147 : 4838 : EnterDepth[CI] = Depth++;
148 [ + + ]: 588848 : else if (Callee == leave_func) {
149 : 10046 : LeaveDepth[CI] = Depth;
150 : 10046 : Depth -= cast<ConstantInt>(CI->getArgOperand(0))->getLimitedValue();
151 : : }
152 : : assert(Depth >= 0);
153 [ + + ]: 593686 : if (Depth > MaxDepth)
154 : 4126 : MaxDepth = Depth;
155 : : }
156 : : /* Remember the depth at the BB boundary */
157 : 636116 : ExitDepth[BB] = Depth;
158 : : }
159 : :
160 : : /* Step 2: EH Frame lowering */
161 : : // Allocate stack space for each handler. We allocate these as separate
162 : : // allocas so the optimizer can later merge and rearrange them if it wants
163 : : // to.
164 : 62715 : Value *handler_sz = ConstantInt::get(Type::getInt32Ty(F.getContext()),
165 : : sizeof(jl_handler_t));
166 : 62715 : Value *handler_sz64 = ConstantInt::get(Type::getInt64Ty(F.getContext()),
167 : : sizeof(jl_handler_t));
168 : 62715 : Instruction *firstInst = &F.getEntryBlock().front();
169 : 62715 : std::vector<AllocaInst *> buffs;
170 [ + + ]: 66841 : for (int i = 0; i < MaxDepth; ++i) {
171 : 4126 : auto *buff = new AllocaInst(Type::getInt8Ty(F.getContext()), 0,
172 : 4126 : handler_sz, Align(16), "", firstInst);
173 : 4126 : buffs.push_back(buff);
174 : : }
175 : :
176 : : // Lower enter funcs
177 [ + + ]: 67553 : for (auto it : EnterDepth) {
178 : : assert(it.second >= 0);
179 : 4838 : AllocaInst *buff = buffs[it.second];
180 : 4838 : CallInst *enter = it.first;
181 : 4838 : auto new_enter = CallInst::Create(jlenter_func, buff, "", enter);
182 : : Value *lifetime_args[] = {
183 : : handler_sz64,
184 : : buff
185 : 4838 : };
186 : 4838 : CallInst::Create(lifetime_start, lifetime_args, "", new_enter);
187 : : #ifndef _OS_WINDOWS_
188 : : // For LLVM 3.3 compatibility
189 : : Value *args[] = {buff,
190 : 4838 : ConstantInt::get(Type::getInt32Ty(F.getContext()), 0)};
191 : 4838 : auto sj = CallInst::Create(setjmp_func, args, "", enter);
192 : : #else
193 : : auto sj = CallInst::Create(setjmp_func, buff, "", enter);
194 : : #endif
195 : : // We need to mark this on the call site as well. See issue #6757
196 : 4838 : sj->setCanReturnTwice();
197 [ + + ]: 4838 : if (auto dbg = enter->getMetadata(LLVMContext::MD_dbg)) {
198 : 4822 : new_enter->setMetadata(LLVMContext::MD_dbg, dbg);
199 : 4822 : sj->setMetadata(LLVMContext::MD_dbg, dbg);
200 : : }
201 : 4838 : enter->replaceAllUsesWith(sj);
202 : 4838 : enter->eraseFromParent();
203 : : }
204 : : // Insert lifetime end intrinsics after every leave.
205 [ + + ]: 72761 : for (auto it : LeaveDepth) {
206 : 10046 : int StartDepth = it.second - 1;
207 : 10046 : int npops = cast<ConstantInt>(it.first->getArgOperand(0))->getLimitedValue();
208 [ + + ]: 20154 : for (int i = 0; i < npops; ++i) {
209 : : assert(StartDepth-i >= 0);
210 : : Value *lifetime_args[] = {
211 : : handler_sz64,
212 : 10108 : buffs[StartDepth-i]
213 : 10108 : };
214 : 10108 : auto LifetimeEnd = CallInst::Create(lifetime_end, lifetime_args);
215 : 10108 : LifetimeEnd->insertAfter(it.first);
216 : : }
217 : : }
218 : 62715 : return true;
219 : : }
220 : :
221 : : } // anonymous namespace
222 : :
223 : 0 : PreservedAnalyses LowerExcHandlers::run(Function &F, FunctionAnalysisManager &AM)
224 : : {
225 [ # # ]: 0 : if (lowerExcHandlers(F)) {
226 : 0 : return PreservedAnalyses::allInSet<CFGAnalyses>();
227 : : }
228 : 0 : return PreservedAnalyses::all();
229 : : }
230 : :
231 : :
232 : : struct LowerExcHandlersLegacy : public FunctionPass {
233 : : static char ID;
234 : 17 : LowerExcHandlersLegacy() : FunctionPass(ID)
235 : 17 : {}
236 : 137811 : bool runOnFunction(Function &F) {
237 : 137811 : return lowerExcHandlers(F);
238 : : }
239 : : };
240 : :
241 : : char LowerExcHandlersLegacy::ID = 0;
242 : : static RegisterPass<LowerExcHandlersLegacy> X("LowerExcHandlers", "Lower Julia Exception Handlers",
243 : : false /* Only looks at CFG */,
244 : : false /* Analysis Pass */);
245 : :
246 : 17 : Pass *createLowerExcHandlersPass()
247 : : {
248 : 17 : return new LowerExcHandlersLegacy();
249 : : }
250 : :
251 : 0 : extern "C" JL_DLLEXPORT void LLVMExtraAddLowerExcHandlersPass_impl(LLVMPassManagerRef PM)
252 : : {
253 : 0 : unwrap(PM)->add(createLowerExcHandlersPass());
254 : 0 : }
|