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 // 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 } |
OLD | NEW |