OLD | NEW |
---|---|
(Empty) | |
1 //===- PNaClSjLjEH.cpp - Lower C++ exception handling to use setjmp()------===// | |
2 // | |
3 // The LLVM Compiler Infrastructure | |
4 // | |
5 // This file is distributed under the University of Illinois Open Source | |
6 // License. See LICENSE.TXT for details. | |
7 // | |
8 //===----------------------------------------------------------------------===// | |
9 // | |
10 // The PNaClSjLjEH pass is part of an implementation of C++ exception | |
11 // handling for PNaCl that uses setjmp() and longjmp() to handle C++ | |
12 // exceptions. The pass lowers LLVM "invoke" instructions to use | |
13 // setjmp(). | |
14 // | |
15 // For example, consider the following C++ code fragment: | |
16 // | |
17 // int catcher_func() { | |
18 // try { | |
19 // int result = external_func(); | |
20 // return result + 100; | |
21 // } catch (MyException &exc) { | |
22 // return exc.value + 200; | |
23 // } | |
24 // } | |
25 // | |
26 // PNaClSjLjEH converts the IR for that function to the following | |
27 // pseudo-code: | |
28 // | |
29 // struct LandingPadResult { | |
30 // void *exception_obj; // For passing to __cxa_begin_catch() | |
31 // int matched_clause_id; // See ExceptionInfoWriter.cpp | |
32 // }; | |
33 // | |
34 // struct ExceptionFrame { | |
35 // union { | |
36 // jmp_buf jmpbuf; // Context for jumping to landingpad block | |
37 // struct LandingPadResult result; // Data returned to landingpad block | |
38 // }; | |
39 // struct ExceptionFrame *next; // Next frame in linked list | |
40 // int clause_list_id; // Reference to landingpad's exception info | |
41 // }; | |
42 // | |
43 // // Thread-local exception state | |
44 // __thread struct ExceptionFrame *__pnacl_eh_stack; | |
45 // | |
46 // int catcher_func() { | |
47 // struct ExceptionFrame frame; | |
48 // int result; | |
49 // if (!setjmp(&frame.jmpbuf)) { // Save context | |
50 // frame.next = __pnacl_eh_stack; | |
51 // frame.clause_list_id = 123; | |
52 // __pnacl_eh_stack = &frame; // Add frame to stack | |
53 // result = external_func(); | |
54 // __pnacl_eh_stack = frame.next; // Remove frame from stack | |
55 // } else { | |
56 // // Handle exception. This is a simplification. Real code would | |
57 // // call __cxa_begin_catch() to extract the thrown object. | |
58 // MyException &exc = *(MyException *) frame.result.exception; | |
Derek Schuff
2013/10/15 17:39:26
frame.result.exception_obj
Mark Seaborn
2013/10/15 21:41:18
Done.
| |
59 // return exc.value + 200; | |
60 // } | |
61 // return result + 100; | |
62 // } | |
63 // | |
64 // The pass makes the following changes to IR: | |
65 // | |
66 // * Convert "invoke" and "landingpad" instructions. | |
67 // * Convert "resume" instructions into __pnacl_eh_resume() calls. | |
68 // * Replace each call to llvm.eh.typeid.for() with an integer | |
69 // constant representing the exception type. | |
70 // | |
71 //===----------------------------------------------------------------------===// | |
72 | |
73 #include "llvm/ADT/DenseMap.h" | |
74 #include "llvm/IR/Instructions.h" | |
75 #include "llvm/IR/IntrinsicInst.h" | |
76 #include "llvm/IR/Intrinsics.h" | |
77 #include "llvm/IR/Module.h" | |
78 #include "llvm/Pass.h" | |
79 #include "llvm/Support/raw_ostream.h" | |
80 #include "llvm/Transforms/NaCl.h" | |
81 #include "ExceptionInfoWriter.h" | |
82 | |
83 using namespace llvm; | |
84 | |
85 namespace { | |
86 // This is a ModulePass so that it can introduce new global variables. | |
87 class PNaClSjLjEH : public ModulePass { | |
88 public: | |
89 static char ID; // Pass identification, replacement for typeid | |
90 PNaClSjLjEH() : ModulePass(ID) { | |
91 initializePNaClSjLjEHPass(*PassRegistry::getPassRegistry()); | |
92 } | |
93 | |
94 virtual bool runOnModule(Module &M); | |
95 }; | |
96 | |
97 class FuncRewriter { | |
98 Type *ExceptionFrameTy; | |
99 ExceptionInfoWriter *ExcInfoWriter; | |
100 Function *Func; | |
101 | |
102 // FrameInitialized indicates whether the following variables have | |
103 // been initialized. | |
104 bool FrameInitialized; | |
105 Function *SetjmpIntrinsic; // setjmp() intrinsic function | |
106 Instruction *EHStackTlsVar; // __pnacl_eh_stack thread-local variable | |
Derek Schuff
2013/10/15 17:39:26
maybe mention that this is a bitcast, since it loo
Mark Seaborn
2013/10/15 21:41:18
Done.
| |
107 Instruction *Frame; // Frame allocated for this function | |
108 Instruction *FrameJmpBuf; // Frame's jmp_buf field | |
109 Instruction *FrameNextPtr; // Frame's next field | |
110 Instruction *FrameExcInfo; // Frame's clause_list_id field | |
111 | |
112 Function *EHResumeFunc; // __pnacl_eh_resume() function | |
113 | |
114 // Initialize values that are shared across all "invoke" | |
115 // instructions within the function. | |
116 void initializeFrame(); | |
117 | |
118 public: | |
119 FuncRewriter(Type *ExceptionFrameTy, ExceptionInfoWriter *ExcInfoWriter, | |
120 Function *Func): | |
121 ExceptionFrameTy(ExceptionFrameTy), | |
122 ExcInfoWriter(ExcInfoWriter), | |
123 Func(Func), | |
124 FrameInitialized(false), | |
125 SetjmpIntrinsic(NULL), EHStackTlsVar(NULL), | |
126 Frame(NULL), FrameJmpBuf(NULL), FrameNextPtr(NULL), FrameExcInfo(NULL), | |
127 EHResumeFunc(NULL) {} | |
128 | |
129 void expandInvokeInst(InvokeInst *Invoke); | |
130 void expandResumeInst(ResumeInst *Resume); | |
131 void expandFunc(); | |
132 }; | |
133 } | |
134 | |
135 char PNaClSjLjEH::ID = 0; | |
136 INITIALIZE_PASS(PNaClSjLjEH, "pnacl-sjlj-eh", | |
137 "Lower C++ exception handling to use setjmp()", | |
138 false, false) | |
139 | |
140 static const int kPNaClJmpBufSize = 1024; | |
141 static const int kPNaClJmpBufAlign = 8; | |
142 | |
143 void FuncRewriter::initializeFrame() { | |
144 if (FrameInitialized) | |
145 return; | |
146 FrameInitialized = true; | |
147 Module *M = Func->getParent(); | |
148 | |
149 SetjmpIntrinsic = Intrinsic::getDeclaration(M, Intrinsic::nacl_setjmp); | |
150 | |
151 Value *EHStackTlsVarUncast = M->getGlobalVariable("__pnacl_eh_stack"); | |
152 if (!EHStackTlsVarUncast) | |
153 report_fatal_error("__pnacl_eh_stack not defined"); | |
154 EHStackTlsVar = new BitCastInst( | |
155 EHStackTlsVarUncast, ExceptionFrameTy->getPointerTo()->getPointerTo(), | |
156 "pnacl_eh_stack"); | |
157 Func->getEntryBlock().getInstList().push_front(EHStackTlsVar); | |
158 | |
159 // Allocate the new exception frame. This is reused across all | |
160 // invoke instructions in the function. | |
161 Type *I32 = Type::getInt32Ty(M->getContext()); | |
162 Frame = new AllocaInst(ExceptionFrameTy, ConstantInt::get(I32, 1), | |
163 kPNaClJmpBufAlign, "invoke_frame"); | |
164 Func->getEntryBlock().getInstList().push_front(Frame); | |
165 | |
166 // Calculate addresses of fields in the exception frame. | |
167 Value *JmpBufIndexes[] = { ConstantInt::get(I32, 0), | |
168 ConstantInt::get(I32, 0), | |
169 ConstantInt::get(I32, 0) }; | |
170 FrameJmpBuf = GetElementPtrInst::Create(Frame, JmpBufIndexes, | |
171 "invoke_jmp_buf"); | |
172 FrameJmpBuf->insertAfter(Frame); | |
173 | |
174 Value *NextPtrIndexes[] = { ConstantInt::get(I32, 0), | |
175 ConstantInt::get(I32, 1) }; | |
176 FrameNextPtr = GetElementPtrInst::Create(Frame, NextPtrIndexes, | |
177 "invoke_next"); | |
178 FrameNextPtr->insertAfter(Frame); | |
179 | |
180 Value *ExcInfoIndexes[] = { ConstantInt::get(I32, 0), | |
181 ConstantInt::get(I32, 2) }; | |
182 FrameExcInfo = GetElementPtrInst::Create(Frame, ExcInfoIndexes, | |
183 "exc_info_ptr"); | |
184 FrameExcInfo->insertAfter(Frame); | |
185 } | |
186 | |
187 static void updateEdge(BasicBlock *Dest, | |
188 BasicBlock *OldIncoming, | |
189 BasicBlock *NewIncoming) { | |
190 for (BasicBlock::iterator Inst = Dest->begin(); Inst != Dest->end(); ++Inst) { | |
191 PHINode *Phi = dyn_cast<PHINode>(Inst); | |
192 if (!Phi) | |
193 break; | |
194 for (unsigned I = 0, E = Phi->getNumIncomingValues(); I < E; ++I) { | |
195 if (Phi->getIncomingBlock(I) == OldIncoming) | |
196 Phi->setIncomingBlock(I, NewIncoming); | |
197 } | |
198 } | |
199 } | |
200 | |
201 void FuncRewriter::expandInvokeInst(InvokeInst *Invoke) { | |
202 initializeFrame(); | |
203 | |
204 LandingPadInst *LP = Invoke->getLandingPadInst(); | |
205 Type *I32 = Type::getInt32Ty(Func->getContext()); | |
206 Value *ExcInfo = ConstantInt::get( | |
207 I32, ExcInfoWriter->getIDForLandingPadClauseList(LP)); | |
208 | |
209 // Create setjmp() call. | |
210 Value *SetjmpArgs[] = { FrameJmpBuf }; | |
211 Value *SetjmpCall = CopyDebug(CallInst::Create(SetjmpIntrinsic, SetjmpArgs, | |
212 "invoke_sj", Invoke), Invoke); | |
213 // Check setjmp()'s result. | |
214 Value *IsZero = CopyDebug(new ICmpInst(Invoke, CmpInst::ICMP_EQ, SetjmpCall, | |
215 ConstantInt::get(I32, 0), | |
216 "invoke_sj_is_zero"), Invoke); | |
217 | |
218 BasicBlock *CallBB = BasicBlock::Create(Func->getContext(), "invoke_do_call", | |
219 Func); | |
220 CallBB->moveAfter(Invoke->getParent()); | |
221 | |
222 // Append the new frame to the list. | |
223 Value *OldList = CopyDebug( | |
224 new LoadInst(EHStackTlsVar, "old_eh_stack", CallBB), Invoke); | |
225 CopyDebug(new StoreInst(OldList, FrameNextPtr, CallBB), Invoke); | |
226 CopyDebug(new StoreInst(ExcInfo, FrameExcInfo, CallBB), Invoke); | |
227 CopyDebug(new StoreInst(Frame, EHStackTlsVar, CallBB), Invoke); | |
228 | |
229 SmallVector<Value *, 10> CallArgs; | |
230 for (unsigned I = 0, E = Invoke->getNumArgOperands(); I < E; ++I) | |
231 CallArgs.push_back(Invoke->getArgOperand(I)); | |
232 CallInst *NewCall = CallInst::Create(Invoke->getCalledValue(), CallArgs, "", | |
233 CallBB); | |
234 CopyDebug(NewCall, Invoke); | |
235 NewCall->takeName(Invoke); | |
236 NewCall->setAttributes(Invoke->getAttributes()); | |
237 NewCall->setCallingConv(Invoke->getCallingConv()); | |
238 // Restore the old frame list. We only need to do this on the | |
239 // non-exception code path. If an exception is raised, the frame | |
240 // list state will be restored for us. | |
241 CopyDebug(new StoreInst(OldList, EHStackTlsVar, CallBB), Invoke); | |
242 | |
243 CopyDebug(BranchInst::Create(CallBB, Invoke->getUnwindDest(), IsZero, Invoke), | |
244 Invoke); | |
245 CopyDebug(BranchInst::Create(Invoke->getNormalDest(), CallBB), Invoke); | |
246 | |
247 updateEdge(Invoke->getNormalDest(), Invoke->getParent(), CallBB); | |
248 | |
249 Invoke->replaceAllUsesWith(NewCall); | |
250 Invoke->eraseFromParent(); | |
251 } | |
252 | |
253 void FuncRewriter::expandResumeInst(ResumeInst *Resume) { | |
254 if (!EHResumeFunc) { | |
255 EHResumeFunc = Func->getParent()->getFunction("__pnacl_eh_resume"); | |
256 if (!EHResumeFunc) | |
257 report_fatal_error("__pnacl_eh_resume() not defined"); | |
258 } | |
259 | |
260 // The "resume" instruction gets passed the landingpad's full result | |
261 // (struct LandingPadResult above). Extract the exception_obj field | |
262 // to pass to __pnacl_eh_resume(), which doesn't need the | |
263 // matched_clause_id field. | |
264 unsigned Indexes[] = { 0 }; | |
265 Value *ExceptionPtr = | |
266 CopyDebug(ExtractValueInst::Create(Resume->getValue(), Indexes, | |
267 "resume_exc", Resume), Resume); | |
268 | |
269 // Cast to the pointer type that __pnacl_eh_resume() expects. | |
270 if (EHResumeFunc->getFunctionType()->getFunctionNumParams() != 1) | |
271 report_fatal_error("Bad type for __pnacl_eh_resume()"); | |
272 Type *ArgType = EHResumeFunc->getFunctionType()->getFunctionParamType(0); | |
273 ExceptionPtr = new BitCastInst(ExceptionPtr, ArgType, "resume_cast", Resume); | |
274 | |
275 Value *Args[] = { ExceptionPtr }; | |
276 CopyDebug(CallInst::Create(EHResumeFunc, Args, "", Resume), Resume); | |
277 new UnreachableInst(Func->getContext(), Resume); | |
278 Resume->eraseFromParent(); | |
279 } | |
280 | |
281 void FuncRewriter::expandFunc() { | |
282 Type *I32 = Type::getInt32Ty(Func->getContext()); | |
283 | |
284 // We need to do two passes: When we process an invoke we need to | |
285 // look at its landingpad, so we can't remove the landingpads until | |
286 // all the invokes have been processed. | |
287 for (Function::iterator BB = Func->begin(), E = Func->end(); BB != E; ++BB) { | |
288 for (BasicBlock::iterator Iter = BB->begin(), E = BB->end(); Iter != E; ) { | |
289 Instruction *Inst = Iter++; | |
290 if (InvokeInst *Invoke = dyn_cast<InvokeInst>(Inst)) { | |
291 expandInvokeInst(Invoke); | |
292 } else if (ResumeInst *Resume = dyn_cast<ResumeInst>(Inst)) { | |
293 expandResumeInst(Resume); | |
294 } else if (IntrinsicInst *Intrinsic = dyn_cast<IntrinsicInst>(Inst)) { | |
295 if (Intrinsic->getIntrinsicID() == Intrinsic::eh_typeid_for) { | |
296 Value *ExcType = Intrinsic->getArgOperand(0); | |
297 Value *Val = ConstantInt::get( | |
298 I32, ExcInfoWriter->getIDForExceptionType(ExcType)); | |
299 Intrinsic->replaceAllUsesWith(Val); | |
300 Intrinsic->eraseFromParent(); | |
301 } | |
302 } | |
303 } | |
304 } | |
305 for (Function::iterator BB = Func->begin(), E = Func->end(); BB != E; ++BB) { | |
306 for (BasicBlock::iterator Iter = BB->begin(), E = BB->end(); Iter != E; ) { | |
307 Instruction *Inst = Iter++; | |
308 if (LandingPadInst *LP = dyn_cast<LandingPadInst>(Inst)) { | |
309 initializeFrame(); | |
310 Value *LPPtr = new BitCastInst( | |
311 FrameJmpBuf, LP->getType()->getPointerTo(), "landingpad_ptr", LP); | |
312 Value *LPVal = CopyDebug(new LoadInst(LPPtr, "", LP), LP); | |
313 LPVal->takeName(LP); | |
314 LP->replaceAllUsesWith(LPVal); | |
315 LP->eraseFromParent(); | |
316 } | |
317 } | |
318 } | |
319 } | |
320 | |
321 bool PNaClSjLjEH::runOnModule(Module &M) { | |
322 Type *JmpBufTy = ArrayType::get(Type::getInt8Ty(M.getContext()), | |
323 kPNaClJmpBufSize); | |
324 | |
325 // Define "struct ExceptionFrame". | |
326 StructType *ExceptionFrameTy = StructType::create(M.getContext(), | |
327 "ExceptionFrame"); | |
328 Type *ExceptionFrameFields[] = { | |
329 JmpBufTy, // jmp_buf | |
330 ExceptionFrameTy->getPointerTo(), // struct ExceptionFrame *next | |
331 Type::getInt32Ty(M.getContext()) // Exception info (clause list ID) | |
332 }; | |
333 ExceptionFrameTy->setBody(ExceptionFrameFields); | |
334 | |
335 ExceptionInfoWriter ExcInfoWriter(&M.getContext()); | |
336 for (Module::iterator Func = M.begin(), E = M.end(); Func != E; ++Func) { | |
337 FuncRewriter Rewriter(ExceptionFrameTy, &ExcInfoWriter, Func); | |
338 Rewriter.expandFunc(); | |
339 } | |
340 ExcInfoWriter.defineGlobalVariables(&M); | |
341 return true; | |
342 } | |
343 | |
344 ModulePass *llvm::createPNaClSjLjEHPass() { | |
345 return new PNaClSjLjEH(); | |
346 } | |
OLD | NEW |