Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(206)

Side by Side Diff: lib/Transforms/NaCl/PNaClSjLjEH.cpp

Issue 939073008: Rebased PNaCl localmods in LLVM to 223109 (Closed)
Patch Set: undo localmod Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/Transforms/NaCl/PNaClABISimplify.cpp ('k') | lib/Transforms/NaCl/PromoteI1Ops.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 // frame.next = __pnacl_eh_stack;
49 // frame.clause_list_id = 123;
50 // __pnacl_eh_stack = &frame; // Add frame to stack
51 // int result;
52 // if (!catcher_func_setjmp_caller(external_func, &frame.jmpbuf, &result)) {
53 // __pnacl_eh_stack = frame.next; // Remove frame from stack
54 // return result + 100;
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_obj;
59 // return exc.value + 200;
60 // }
61 // }
62 //
63 // // Helper function
64 // static int catcher_func_setjmp_caller(int (*func)(void), jmp_buf jmpbuf,
65 // int *result) {
66 // if (!setjmp(jmpbuf)) {
67 // *result = func();
68 // return 0;
69 // }
70 // return 1;
71 // }
72 //
73 // We use a helper function so that setjmp() is not called directly
74 // from catcher_func(), due to a quirk of how setjmp() and longjmp()
75 // are specified in C.
76 //
77 // func() might modify variables (allocas) that are local to
78 // catcher_func() (if the variables' addresses are taken). The C
79 // standard says that these variables' values would become undefined
80 // after longjmp() returned if setjmp() were called from
81 // catcher_func(). Specifically, LLVM's GVN pass can optimize away
82 // stores to allocas between setjmp() and longjmp() (see
83 // pnacl-sjlj-eh-bug.ll for an example). But this only applies to
84 // allocas inside the caller of setjmp(), not to allocas inside the
85 // caller of the caller of setjmp(), so doing the setjmp() call inside
86 // a helper function that catcher_func() calls avoids the problem.
87 //
88 // The pass makes the following changes to IR:
89 //
90 // * Convert "invoke" and "landingpad" instructions.
91 // * Convert "resume" instructions into __pnacl_eh_resume() calls.
92 // * Replace each call to llvm.eh.typeid.for() with an integer
93 // constant representing the exception type.
94 //
95 //===----------------------------------------------------------------------===//
96
97 #include "llvm/ADT/DenseMap.h"
98 #include "llvm/IR/Instructions.h"
99 #include "llvm/IR/IntrinsicInst.h"
100 #include "llvm/IR/Intrinsics.h"
101 #include "llvm/IR/Module.h"
102 #include "llvm/Pass.h"
103 #include "llvm/Support/raw_ostream.h"
104 #include "llvm/Transforms/NaCl.h"
105 #include "ExceptionInfoWriter.h"
106
107 using namespace llvm;
108
109 namespace {
110 // This is a ModulePass so that it can introduce new global variables.
111 class PNaClSjLjEH : public ModulePass {
112 public:
113 static char ID; // Pass identification, replacement for typeid
114 PNaClSjLjEH() : ModulePass(ID) {
115 initializePNaClSjLjEHPass(*PassRegistry::getPassRegistry());
116 }
117
118 virtual bool runOnModule(Module &M);
119 };
120
121 class FuncRewriter {
122 Type *ExceptionFrameTy;
123 ExceptionInfoWriter *ExcInfoWriter;
124 Function *Func;
125
126 // FrameInitialized indicates whether the following variables have
127 // been initialized.
128 bool FrameInitialized;
129 Function *SetjmpIntrinsic; // setjmp() intrinsic function
130 Instruction *EHStackTlsVar; // Bitcast of thread-local __pnacl_eh_stack var
131 Instruction *Frame; // Frame allocated for this function
132 Instruction *FrameJmpBuf; // Frame's jmp_buf field
133 Instruction *FrameNextPtr; // Frame's next field
134 Instruction *FrameExcInfo; // Frame's clause_list_id field
135
136 Function *EHResumeFunc; // __pnacl_eh_resume() function
137
138 // Initialize values that are shared across all "invoke"
139 // instructions within the function.
140 void initializeFrame();
141
142 public:
143 FuncRewriter(Type *ExceptionFrameTy, ExceptionInfoWriter *ExcInfoWriter,
144 Function *Func):
145 ExceptionFrameTy(ExceptionFrameTy),
146 ExcInfoWriter(ExcInfoWriter),
147 Func(Func),
148 FrameInitialized(false),
149 SetjmpIntrinsic(NULL), EHStackTlsVar(NULL),
150 Frame(NULL), FrameJmpBuf(NULL), FrameNextPtr(NULL), FrameExcInfo(NULL),
151 EHResumeFunc(NULL) {}
152
153 Value *createSetjmpWrappedCall(InvokeInst *Invoke);
154 void expandInvokeInst(InvokeInst *Invoke);
155 void expandResumeInst(ResumeInst *Resume);
156 void expandFunc();
157 };
158 }
159
160 char PNaClSjLjEH::ID = 0;
161 INITIALIZE_PASS(PNaClSjLjEH, "pnacl-sjlj-eh",
162 "Lower C++ exception handling to use setjmp()",
163 false, false)
164
165 static const int kPNaClJmpBufSize = 1024;
166 static const int kPNaClJmpBufAlign = 8;
167
168 void FuncRewriter::initializeFrame() {
169 if (FrameInitialized)
170 return;
171 FrameInitialized = true;
172 Module *M = Func->getParent();
173
174 SetjmpIntrinsic = Intrinsic::getDeclaration(M, Intrinsic::nacl_setjmp);
175
176 Value *EHStackTlsVarUncast = M->getGlobalVariable("__pnacl_eh_stack");
177 if (!EHStackTlsVarUncast)
178 report_fatal_error("__pnacl_eh_stack not defined");
179 EHStackTlsVar = new BitCastInst(
180 EHStackTlsVarUncast, ExceptionFrameTy->getPointerTo()->getPointerTo(),
181 "pnacl_eh_stack");
182 Func->getEntryBlock().getInstList().push_front(EHStackTlsVar);
183
184 // Allocate the new exception frame. This is reused across all
185 // invoke instructions in the function.
186 Type *I32 = Type::getInt32Ty(M->getContext());
187 Frame = new AllocaInst(ExceptionFrameTy, ConstantInt::get(I32, 1),
188 kPNaClJmpBufAlign, "invoke_frame");
189 Func->getEntryBlock().getInstList().push_front(Frame);
190
191 // Calculate addresses of fields in the exception frame.
192 Value *JmpBufIndexes[] = { ConstantInt::get(I32, 0),
193 ConstantInt::get(I32, 0),
194 ConstantInt::get(I32, 0) };
195 FrameJmpBuf = GetElementPtrInst::Create(Frame, JmpBufIndexes,
196 "invoke_jmp_buf");
197 FrameJmpBuf->insertAfter(Frame);
198
199 Value *NextPtrIndexes[] = { ConstantInt::get(I32, 0),
200 ConstantInt::get(I32, 1) };
201 FrameNextPtr = GetElementPtrInst::Create(Frame, NextPtrIndexes,
202 "invoke_next");
203 FrameNextPtr->insertAfter(Frame);
204
205 Value *ExcInfoIndexes[] = { ConstantInt::get(I32, 0),
206 ConstantInt::get(I32, 2) };
207 FrameExcInfo = GetElementPtrInst::Create(Frame, ExcInfoIndexes,
208 "exc_info_ptr");
209 FrameExcInfo->insertAfter(Frame);
210 }
211
212 // Creates the helper function that will do the setjmp() call and
213 // function call for implementing Invoke. Creates the call to the
214 // helper function. Returns a Value which is zero on the normal
215 // execution path and non-zero if the landingpad block should be
216 // entered.
217 Value *FuncRewriter::createSetjmpWrappedCall(InvokeInst *Invoke) {
218 Type *I32 = Type::getInt32Ty(Func->getContext());
219
220 // Allocate space for storing the invoke's result temporarily (so
221 // that the helper function can return multiple values). We don't
222 // need to do this if the result is unused, and we can't if its type
223 // is void.
224 Instruction *ResultAlloca = NULL;
225 if (!Invoke->use_empty()) {
226 ResultAlloca = new AllocaInst(Invoke->getType(), "invoke_result_ptr");
227 Func->getEntryBlock().getInstList().push_front(ResultAlloca);
228 }
229
230 // Create type for the helper function.
231 SmallVector<Type *, 10> ArgTypes;
232 for (unsigned I = 0, E = Invoke->getNumArgOperands(); I < E; ++I)
233 ArgTypes.push_back(Invoke->getArgOperand(I)->getType());
234 ArgTypes.push_back(Invoke->getCalledValue()->getType());
235 ArgTypes.push_back(FrameJmpBuf->getType());
236 if (ResultAlloca)
237 ArgTypes.push_back(Invoke->getType()->getPointerTo());
238 FunctionType *FTy = FunctionType::get(I32, ArgTypes, false);
239
240 // Create the helper function.
241 Function *HelperFunc = Function::Create(
242 FTy, GlobalValue::InternalLinkage, Func->getName() + "_setjmp_caller");
243 Func->getParent()->getFunctionList().insertAfter(Func, HelperFunc);
244 BasicBlock *EntryBB = BasicBlock::Create(Func->getContext(), "", HelperFunc);
245 BasicBlock *NormalBB = BasicBlock::Create(Func->getContext(), "normal",
246 HelperFunc);
247 BasicBlock *ExceptionBB = BasicBlock::Create(Func->getContext(), "exception",
248 HelperFunc);
249
250 // Unpack the helper function's arguments.
251 Function::arg_iterator ArgIter = HelperFunc->arg_begin();
252 SmallVector<Value *, 10> InnerCallArgs;
253 for (unsigned I = 0, E = Invoke->getNumArgOperands(); I < E; ++I) {
254 ArgIter->setName("arg");
255 InnerCallArgs.push_back(ArgIter++);
256 }
257 Argument *CalleeArg = ArgIter++;
258 Argument *JmpBufArg = ArgIter++;
259 CalleeArg->setName("func_ptr");
260 JmpBufArg->setName("jmp_buf");
261
262 // Create setjmp() call.
263 Value *SetjmpArgs[] = { JmpBufArg };
264 CallInst *SetjmpCall = CallInst::Create(SetjmpIntrinsic, SetjmpArgs,
265 "invoke_sj", EntryBB);
266 CopyDebug(SetjmpCall, Invoke);
267 // Setting the "returns_twice" attribute here prevents optimization
268 // passes from inlining HelperFunc into its caller.
269 SetjmpCall->setCanReturnTwice();
270 // Check setjmp()'s result.
271 Value *IsZero = CopyDebug(new ICmpInst(*EntryBB, CmpInst::ICMP_EQ, SetjmpCall,
272 ConstantInt::get(I32, 0),
273 "invoke_sj_is_zero"), Invoke);
274 CopyDebug(BranchInst::Create(NormalBB, ExceptionBB, IsZero, EntryBB), Invoke);
275 // Handle the normal, non-exceptional code path.
276 CallInst *InnerCall = CallInst::Create(CalleeArg, InnerCallArgs, "",
277 NormalBB);
278 CopyDebug(InnerCall, Invoke);
279 InnerCall->setAttributes(Invoke->getAttributes());
280 InnerCall->setCallingConv(Invoke->getCallingConv());
281 if (ResultAlloca) {
282 InnerCall->setName("result");
283 Argument *ResultArg = ArgIter++;
284 ResultArg->setName("result_ptr");
285 CopyDebug(new StoreInst(InnerCall, ResultArg, NormalBB), Invoke);
286 }
287 ReturnInst::Create(Func->getContext(), ConstantInt::get(I32, 0), NormalBB);
288 // Handle the exceptional code path.
289 ReturnInst::Create(Func->getContext(), ConstantInt::get(I32, 1), ExceptionBB);
290
291 // Create the outer call to the helper function.
292 SmallVector<Value *, 10> OuterCallArgs;
293 for (unsigned I = 0, E = Invoke->getNumArgOperands(); I < E; ++I)
294 OuterCallArgs.push_back(Invoke->getArgOperand(I));
295 OuterCallArgs.push_back(Invoke->getCalledValue());
296 OuterCallArgs.push_back(FrameJmpBuf);
297 if (ResultAlloca)
298 OuterCallArgs.push_back(ResultAlloca);
299 CallInst *OuterCall = CallInst::Create(HelperFunc, OuterCallArgs,
300 "invoke_is_exc", Invoke);
301 CopyDebug(OuterCall, Invoke);
302
303 // Retrieve the function return value stored in the alloca. We only
304 // need to do this on the non-exceptional path, but we currently do
305 // it unconditionally because that is simpler.
306 if (ResultAlloca) {
307 Value *Result = new LoadInst(ResultAlloca, "", Invoke);
308 Result->takeName(Invoke);
309 Invoke->replaceAllUsesWith(Result);
310 }
311 return OuterCall;
312 }
313
314 static void convertInvokeToCall(InvokeInst *Invoke) {
315 SmallVector<Value*, 16> CallArgs(Invoke->op_begin(), Invoke->op_end() - 3);
316 // Insert a normal call instruction.
317 CallInst *NewCall = CallInst::Create(Invoke->getCalledValue(),
318 CallArgs, "", Invoke);
319 CopyDebug(NewCall, Invoke);
320 NewCall->takeName(Invoke);
321 NewCall->setCallingConv(Invoke->getCallingConv());
322 NewCall->setAttributes(Invoke->getAttributes());
323 Invoke->replaceAllUsesWith(NewCall);
324
325 // Insert an unconditional branch to the normal destination.
326 BranchInst::Create(Invoke->getNormalDest(), Invoke);
327 // Remove any PHI node entries from the exception destination.
328 Invoke->getUnwindDest()->removePredecessor(Invoke->getParent());
329 Invoke->eraseFromParent();
330 }
331
332 void FuncRewriter::expandInvokeInst(InvokeInst *Invoke) {
333 // Calls to ReturnsTwice functions, i.e. setjmp(), can't be moved
334 // into a helper function. setjmp() can't throw an exception
335 // anyway, so convert the invoke to a call.
336 if (Invoke->hasFnAttr(Attribute::ReturnsTwice)) {
337 convertInvokeToCall(Invoke);
338 return;
339 }
340
341 initializeFrame();
342
343 LandingPadInst *LP = Invoke->getLandingPadInst();
344 Type *I32 = Type::getInt32Ty(Func->getContext());
345 Value *ExcInfo = ConstantInt::get(
346 I32, ExcInfoWriter->getIDForLandingPadClauseList(LP));
347
348 // Append the new frame to the list.
349 Value *OldList = CopyDebug(
350 new LoadInst(EHStackTlsVar, "old_eh_stack", Invoke), Invoke);
351 CopyDebug(new StoreInst(OldList, FrameNextPtr, Invoke), Invoke);
352 CopyDebug(new StoreInst(ExcInfo, FrameExcInfo, Invoke), Invoke);
353 CopyDebug(new StoreInst(Frame, EHStackTlsVar, Invoke), Invoke);
354 Value *IsException = createSetjmpWrappedCall(Invoke);
355 // Restore the old frame list. We only need to do this on the
356 // non-exception code path, but we currently do it unconditionally
357 // because that is simpler. (The PNaCl C++ runtime library restores
358 // the old frame list on the exceptional path; doing it again here
359 // redundantly is OK.)
360 CopyDebug(new StoreInst(OldList, EHStackTlsVar, Invoke), Invoke);
361
362 Value *IsZero = CopyDebug(new ICmpInst(Invoke, CmpInst::ICMP_EQ, IsException,
363 ConstantInt::get(I32, 0),
364 "invoke_sj_is_zero"), Invoke);
365 CopyDebug(BranchInst::Create(Invoke->getNormalDest(), Invoke->getUnwindDest(),
366 IsZero, Invoke),
367 Invoke);
368
369 Invoke->eraseFromParent();
370 }
371
372 void FuncRewriter::expandResumeInst(ResumeInst *Resume) {
373 if (!EHResumeFunc) {
374 EHResumeFunc = Func->getParent()->getFunction("__pnacl_eh_resume");
375 if (!EHResumeFunc)
376 report_fatal_error("__pnacl_eh_resume() not defined");
377 }
378
379 // The "resume" instruction gets passed the landingpad's full result
380 // (struct LandingPadResult above). Extract the exception_obj field
381 // to pass to __pnacl_eh_resume(), which doesn't need the
382 // matched_clause_id field.
383 unsigned Indexes[] = { 0 };
384 Value *ExceptionPtr =
385 CopyDebug(ExtractValueInst::Create(Resume->getValue(), Indexes,
386 "resume_exc", Resume), Resume);
387
388 // Cast to the pointer type that __pnacl_eh_resume() expects.
389 if (EHResumeFunc->getFunctionType()->getFunctionNumParams() != 1)
390 report_fatal_error("Bad type for __pnacl_eh_resume()");
391 Type *ArgType = EHResumeFunc->getFunctionType()->getFunctionParamType(0);
392 ExceptionPtr = new BitCastInst(ExceptionPtr, ArgType, "resume_cast", Resume);
393
394 Value *Args[] = { ExceptionPtr };
395 CopyDebug(CallInst::Create(EHResumeFunc, Args, "", Resume), Resume);
396 new UnreachableInst(Func->getContext(), Resume);
397 Resume->eraseFromParent();
398 }
399
400 void FuncRewriter::expandFunc() {
401 Type *I32 = Type::getInt32Ty(Func->getContext());
402
403 // We need to do two passes: When we process an invoke we need to
404 // look at its landingpad, so we can't remove the landingpads until
405 // all the invokes have been processed.
406 for (Function::iterator BB = Func->begin(), E = Func->end(); BB != E; ++BB) {
407 for (BasicBlock::iterator Iter = BB->begin(), E = BB->end(); Iter != E; ) {
408 Instruction *Inst = Iter++;
409 if (InvokeInst *Invoke = dyn_cast<InvokeInst>(Inst)) {
410 expandInvokeInst(Invoke);
411 } else if (ResumeInst *Resume = dyn_cast<ResumeInst>(Inst)) {
412 expandResumeInst(Resume);
413 } else if (IntrinsicInst *Intrinsic = dyn_cast<IntrinsicInst>(Inst)) {
414 if (Intrinsic->getIntrinsicID() == Intrinsic::eh_typeid_for) {
415 Value *ExcType = Intrinsic->getArgOperand(0);
416 Value *Val = ConstantInt::get(
417 I32, ExcInfoWriter->getIDForExceptionType(ExcType));
418 Intrinsic->replaceAllUsesWith(Val);
419 Intrinsic->eraseFromParent();
420 }
421 }
422 }
423 }
424 for (Function::iterator BB = Func->begin(), E = Func->end(); BB != E; ++BB) {
425 for (BasicBlock::iterator Iter = BB->begin(), E = BB->end(); Iter != E; ) {
426 Instruction *Inst = Iter++;
427 if (LandingPadInst *LP = dyn_cast<LandingPadInst>(Inst)) {
428 initializeFrame();
429 Value *LPPtr = new BitCastInst(
430 FrameJmpBuf, LP->getType()->getPointerTo(), "landingpad_ptr", LP);
431 Value *LPVal = CopyDebug(new LoadInst(LPPtr, "", LP), LP);
432 LPVal->takeName(LP);
433 LP->replaceAllUsesWith(LPVal);
434 LP->eraseFromParent();
435 }
436 }
437 }
438 }
439
440 bool PNaClSjLjEH::runOnModule(Module &M) {
441 Type *JmpBufTy = ArrayType::get(Type::getInt8Ty(M.getContext()),
442 kPNaClJmpBufSize);
443
444 // Define "struct ExceptionFrame".
445 StructType *ExceptionFrameTy = StructType::create(M.getContext(),
446 "ExceptionFrame");
447 Type *ExceptionFrameFields[] = {
448 JmpBufTy, // jmp_buf
449 ExceptionFrameTy->getPointerTo(), // struct ExceptionFrame *next
450 Type::getInt32Ty(M.getContext()) // Exception info (clause list ID)
451 };
452 ExceptionFrameTy->setBody(ExceptionFrameFields);
453
454 ExceptionInfoWriter ExcInfoWriter(&M.getContext());
455 for (Module::iterator Func = M.begin(), E = M.end(); Func != E; ++Func) {
456 FuncRewriter Rewriter(ExceptionFrameTy, &ExcInfoWriter, Func);
457 Rewriter.expandFunc();
458 }
459 ExcInfoWriter.defineGlobalVariables(&M);
460 return true;
461 }
462
463 ModulePass *llvm::createPNaClSjLjEHPass() {
464 return new PNaClSjLjEH();
465 }
OLDNEW
« no previous file with comments | « lib/Transforms/NaCl/PNaClABISimplify.cpp ('k') | lib/Transforms/NaCl/PromoteI1Ops.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698