Chromium Code Reviews| 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 |