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

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

Issue 24777002: Add PNaClSjLjEH pass to implement C++ exception handling using setjmp()+longjmp() (Closed) Base URL: http://git.chromium.org/native_client/pnacl-llvm.git@master
Patch Set: Retry upload Created 7 years, 2 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
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 // 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 }
OLDNEW
« no previous file with comments | « lib/Transforms/NaCl/ExceptionInfoWriter.cpp ('k') | test/Transforms/NaCl/pnacl-eh-exception-info.ll » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698