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_obj; |
| 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; // Bitcast of thread-local __pnacl_eh_stack var |
| 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 |