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

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

Issue 1692803002: Remove Emscripten support (Closed) Base URL: https://chromium.googlesource.com/a/native_client/pnacl-llvm.git@master
Patch Set: Created 4 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/LowerEmExceptionsPass.cpp ('k') | lib/Transforms/NaCl/NoExitRuntime.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 //===- LowerEmSetjmp - Lower setjmp/longjmp for Emscripten/JS -----------===//
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 // Lowers setjmp to a reasonably-performant approach for emscripten. The idea
11 // is that each block with a setjmp is broken up into the part right after
12 // the setjmp, and a new basic block is added which is either reached from
13 // the setjmp, or later from a longjmp. To handle the longjmp, all calls that
14 // might longjmp are checked immediately afterwards.
15 //
16 //===----------------------------------------------------------------------===//
17
18 #include "llvm/Transforms/Scalar.h"
19 #include "llvm/ADT/SmallVector.h"
20 #include "llvm/ADT/Statistic.h"
21 #include "llvm/IR/Constants.h"
22 #include "llvm/IR/DerivedTypes.h"
23 #include "llvm/IR/Instructions.h"
24 #include "llvm/IR/Intrinsics.h"
25 #include "llvm/IR/LLVMContext.h"
26 #include "llvm/IR/Module.h"
27 #include "llvm/Pass.h"
28 #include "llvm/Support/CommandLine.h"
29 #include "llvm/Target/TargetLowering.h"
30 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
31 #include "llvm/Transforms/Utils/Local.h"
32 #include "llvm/Transforms/NaCl.h"
33 #include "llvm/IR/Dominators.h"
34 #include "llvm/Transforms/Utils/PromoteMemToReg.h"
35 #include <vector>
36 #include <set>
37 #include <list>
38
39 #include "llvm/Support/raw_ostream.h"
40
41 #ifdef NDEBUG
42 #undef assert
43 #define assert(x) { if (!(x)) report_fatal_error(#x); }
44 #endif
45
46 using namespace llvm;
47
48 // Utilities for mem/reg: based on Reg2Mem and MemToReg
49
50 bool valueEscapes(const Instruction *Inst) {
51 const BasicBlock *BB = Inst->getParent();
52 for (Value::const_user_iterator UI = Inst->user_begin(),E = Inst->user_end();
53 UI != E; ++UI) {
54 const User *U = *UI;
55 const Instruction *I = cast<Instruction>(U);
56 if (I->getParent() != BB || isa<PHINode>(I))
57 return true;
58 }
59 return false;
60 }
61
62 void doRegToMem(Function &F) { // see Reg2Mem.cpp
63 // Insert all new allocas into entry block.
64 BasicBlock *BBEntry = &F.getEntryBlock();
65 assert(pred_begin(BBEntry) == pred_end(BBEntry) &&
66 "Entry block to function must not have predecessors!");
67
68 // Find first non-alloca instruction and create insertion point. This is
69 // safe if block is well-formed: it always have terminator, otherwise
70 // we'll get and assertion.
71 BasicBlock::iterator I = BBEntry->begin();
72 while (isa<AllocaInst>(I)) ++I;
73
74 CastInst *AllocaInsertionPoint =
75 new BitCastInst(Constant::getNullValue(Type::getInt32Ty(F.getContext())),
76 Type::getInt32Ty(F.getContext()),
77 "reg2mem alloca point", I);
78
79 // Find the escaped instructions. But don't create stack slots for
80 // allocas in entry block.
81 std::list<Instruction*> WorkList;
82 for (Function::iterator ibb = F.begin(), ibe = F.end();
83 ibb != ibe; ++ibb)
84 for (BasicBlock::iterator iib = ibb->begin(), iie = ibb->end();
85 iib != iie; ++iib) {
86 if (!(isa<AllocaInst>(iib) && iib->getParent() == BBEntry) &&
87 valueEscapes(iib)) {
88 WorkList.push_front(&*iib);
89 }
90 }
91
92 // Demote escaped instructions
93 for (std::list<Instruction*>::iterator ilb = WorkList.begin(),
94 ile = WorkList.end(); ilb != ile; ++ilb)
95 DemoteRegToStack(**ilb, false, AllocaInsertionPoint);
96
97 WorkList.clear();
98
99 // Find all phi's
100 for (Function::iterator ibb = F.begin(), ibe = F.end();
101 ibb != ibe; ++ibb)
102 for (BasicBlock::iterator iib = ibb->begin(), iie = ibb->end();
103 iib != iie; ++iib)
104 if (isa<PHINode>(iib))
105 WorkList.push_front(&*iib);
106
107 // Demote phi nodes
108 for (std::list<Instruction*>::iterator ilb = WorkList.begin(),
109 ile = WorkList.end(); ilb != ile; ++ilb)
110 DemotePHIToStack(cast<PHINode>(*ilb), AllocaInsertionPoint);
111 }
112
113 void doMemToReg(Function &F) {
114 std::vector<AllocaInst*> Allocas;
115
116 BasicBlock &BB = F.getEntryBlock(); // Get the entry node for the function
117
118 DominatorTreeWrapperPass DTW;
119 DTW.runOnFunction(F);
120 DominatorTree& DT = DTW.getDomTree();
121
122 while (1) {
123 Allocas.clear();
124
125 // Find allocas that are safe to promote, by looking at all instructions in
126 // the entry node
127 for (BasicBlock::iterator I = BB.begin(), E = --BB.end(); I != E; ++I)
128 if (AllocaInst *AI = dyn_cast<AllocaInst>(I)) // Is it an alloca?
129 if (isAllocaPromotable(AI))
130 Allocas.push_back(AI);
131
132 if (Allocas.empty()) break;
133
134 PromoteMemToReg(Allocas, DT);
135 }
136 }
137
138 // LowerEmSetjmp
139
140 namespace {
141 class LowerEmSetjmp : public ModulePass {
142 Module *TheModule;
143
144 public:
145 static char ID; // Pass identification, replacement for typeid
146 explicit LowerEmSetjmp() : ModulePass(ID), TheModule(NULL) {
147 initializeLowerEmSetjmpPass(*PassRegistry::getPassRegistry());
148 }
149 bool runOnModule(Module &M);
150 };
151 }
152
153 char LowerEmSetjmp::ID = 0;
154 INITIALIZE_PASS(LowerEmSetjmp, "loweremsetjmp",
155 "Lower setjmp and longjmp for js/emscripten",
156 false, false)
157
158 bool LowerEmSetjmp::runOnModule(Module &M) {
159 TheModule = &M;
160
161 Function *Setjmp = TheModule->getFunction("setjmp");
162 Function *Longjmp = TheModule->getFunction("longjmp");
163 if (!Setjmp && !Longjmp) return false;
164
165 Type *i32 = Type::getInt32Ty(M.getContext());
166 Type *Void = Type::getVoidTy(M.getContext());
167
168 // Add functions
169
170 Function *EmSetjmp = NULL;
171
172 if (Setjmp) {
173 SmallVector<Type*, 2> EmSetjmpTypes;
174 EmSetjmpTypes.push_back(Setjmp->getFunctionType()->getParamType(0));
175 EmSetjmpTypes.push_back(i32); // extra param that says which setjmp in the f unction it is
176 FunctionType *EmSetjmpFunc = FunctionType::get(i32, EmSetjmpTypes, false);
177 EmSetjmp = Function::Create(EmSetjmpFunc, GlobalValue::ExternalLinkage, "ems cripten_setjmp", TheModule);
178 }
179
180 Function *EmLongjmp = Longjmp ? Function::Create(Longjmp->getFunctionType(), G lobalValue::ExternalLinkage, "emscripten_longjmp", TheModule) : NULL;
181
182 SmallVector<Type*, 1> IntArgTypes;
183 IntArgTypes.push_back(i32);
184 FunctionType *IntIntFunc = FunctionType::get(i32, IntArgTypes, false);
185
186 Function *CheckLongjmp = Function::Create(IntIntFunc, GlobalValue::ExternalLin kage, "emscripten_check_longjmp", TheModule); // gets control flow
187
188 Function *GetLongjmpResult = Function::Create(IntIntFunc, GlobalValue::Externa lLinkage, "emscripten_get_longjmp_result", TheModule); // gets int value longjmp 'd
189
190 FunctionType *VoidFunc = FunctionType::get(Void, false);
191 Function *PrepSetjmp = Function::Create(VoidFunc, GlobalValue::ExternalLinkage , "emscripten_prep_setjmp", TheModule);
192
193 Function *CleanupSetjmp = Function::Create(VoidFunc, GlobalValue::ExternalLink age, "emscripten_cleanup_setjmp", TheModule);
194
195 Function *PreInvoke = TheModule->getFunction("emscripten_preinvoke");
196 if (!PreInvoke) PreInvoke = Function::Create(VoidFunc, GlobalValue::ExternalLi nkage, "emscripten_preinvoke", TheModule);
197
198 FunctionType *IntFunc = FunctionType::get(i32, false);
199 Function *PostInvoke = TheModule->getFunction("emscripten_postinvoke");
200 if (!PostInvoke) PostInvoke = Function::Create(IntFunc, GlobalValue::ExternalL inkage, "emscripten_postinvoke", TheModule);
201
202 // Process all callers of setjmp and longjmp. Start with setjmp.
203
204 typedef std::vector<PHINode*> Phis;
205 typedef std::map<Function*, Phis> FunctionPhisMap;
206 FunctionPhisMap SetjmpOutputPhis;
207 std::vector<Instruction*> ToErase;
208
209 if (Setjmp) {
210 for (Instruction::user_iterator UI = Setjmp->user_begin(), UE = Setjmp->user _end(); UI != UE; ++UI) {
211 User *U = *UI;
212 if (CallInst *CI = dyn_cast<CallInst>(U)) {
213 BasicBlock *SJBB = CI->getParent();
214 // The tail is everything right after the call, and will be reached once when setjmp is
215 // called, and later when longjmp returns to the setjmp
216 BasicBlock *Tail = SplitBlock(SJBB, CI->getNextNode());
217 // Add a phi to the tail, which will be the output of setjmp, which indi cates if this is the
218 // first call or a longjmp back. The phi directly uses the right value b ased on where we
219 // arrive from
220 PHINode *SetjmpOutput = PHINode::Create(i32, 2, "", Tail->getFirstNonPHI ());
221 SetjmpOutput->addIncoming(ConstantInt::get(i32, 0), SJBB); // setjmp ini tial call returns 0
222 CI->replaceAllUsesWith(SetjmpOutput); // The proper output is now this, not the setjmp call itself
223 // longjmp returns to the setjmp will add themselves to this phi
224 Phis& P = SetjmpOutputPhis[SJBB->getParent()];
225 P.push_back(SetjmpOutput);
226 // fix call target
227 SmallVector<Value *, 2> Args;
228 Args.push_back(CI->getArgOperand(0));
229 Args.push_back(ConstantInt::get(i32, P.size())); // our index in the fun ction is our place in the array + 1
230 CallInst::Create(EmSetjmp, Args, "", CI);
231 ToErase.push_back(CI);
232 } else {
233 errs() << **UI << "\n";
234 report_fatal_error("bad use of setjmp, should only call it");
235 }
236 }
237 }
238
239 // Update longjmp FIXME: we could avoid throwing in longjmp as an optimization when longjmping back into the current function perhaps?
240
241 if (Longjmp) Longjmp->replaceAllUsesWith(EmLongjmp);
242
243 // Update all setjmping functions
244
245 for (FunctionPhisMap::iterator I = SetjmpOutputPhis.begin(); I != SetjmpOutput Phis.end(); I++) {
246 Function *F = I->first;
247 Phis& P = I->second;
248
249 CallInst::Create(PrepSetjmp, "", F->begin()->begin());
250
251 // Update each call that can longjmp so it can return to a setjmp where rele vant
252
253 for (Function::iterator BBI = F->begin(), E = F->end(); BBI != E; ) {
254 BasicBlock *BB = BBI++;
255 for (BasicBlock::iterator Iter = BB->begin(), E = BB->end(); Iter != E; ) {
256 Instruction *I = Iter++;
257 CallInst *CI;
258 if ((CI = dyn_cast<CallInst>(I))) {
259 Value *V = CI->getCalledValue();
260 if (V == PrepSetjmp || V == EmSetjmp || V == CheckLongjmp || V == GetL ongjmpResult || V == PreInvoke || V == PostInvoke) continue;
261 if (Function *CF = dyn_cast<Function>(V)) if (CF->isIntrinsic()) conti nue;
262 // TODO: proper analysis of what can actually longjmp. Currently we as sume anything but setjmp can.
263 // This may longjmp, so we need to check if it did. Split at that poin t, and
264 // envelop the call in pre/post invoke, if we need to
265 CallInst *After;
266 Instruction *Check = NULL;
267 if (Iter != E && (After = dyn_cast<CallInst>(Iter)) && After->getCalle dValue() == PostInvoke) {
268 // use the pre|postinvoke that exceptions lowering already made
269 Check = Iter++;
270 }
271 BasicBlock *Tail = SplitBlock(BB, Iter); // Iter already points to the next instruction, as we need
272 TerminatorInst *TI = BB->getTerminator();
273 if (!Check) {
274 // no existing pre|postinvoke, create our own
275 CallInst::Create(PreInvoke, "", CI);
276 Check = CallInst::Create(PostInvoke, "", TI); // CI is at the end of the block
277
278 // If we are calling a function that is noreturn, we must remove tha t attribute. The code we
279 // insert here does expect it to return, after we catch the exceptio n.
280 if (CI->doesNotReturn()) {
281 if (Function *F = dyn_cast<Function>(CI->getCalledValue())) {
282 F->removeFnAttr(Attribute::NoReturn);
283 }
284 CI->setAttributes(CI->getAttributes().removeAttribute(TheModule->g etContext(), AttributeSet::FunctionIndex, Attribute::NoReturn));
285 assert(!CI->doesNotReturn());
286 }
287 }
288
289 // We need to replace the terminator in Tail - SplitBlock makes BB go straight to Tail, we need to check if a longjmp occurred, and
290 // go to the right setjmp-tail if so
291 SmallVector<Value *, 1> Args;
292 Args.push_back(Check);
293 Instruction *LongjmpCheck = CallInst::Create(CheckLongjmp, Args, "", B B);
294 Instruction *LongjmpResult = CallInst::Create(GetLongjmpResult, Args, "", BB);
295 SwitchInst *SI = SwitchInst::Create(LongjmpCheck, Tail, 2, BB);
296 // -1 means no longjmp happened, continue normally (will hit the defau lt switch case). 0 means a longjmp that is not ours to handle, needs a rethrow. Otherwise
297 // the index mean is the same as the index in P+1 (to avoid 0).
298 for (unsigned i = 0; i < P.size(); i++) {
299 SI->addCase(cast<ConstantInt>(ConstantInt::get(i32, i+1)), P[i]->get Parent());
300 P[i]->addIncoming(LongjmpResult, BB);
301 }
302 ToErase.push_back(TI); // new terminator is now the switch
303
304 // we are splitting the block here, and must continue to find other ca lls in the block - which is now split. so continue
305 // to traverse in the Tail
306 BB = Tail;
307 Iter = BB->begin();
308 E = BB->end();
309 } else if (InvokeInst *CI = dyn_cast<InvokeInst>(I)) { // XXX check if t arget is setjmp
310 (void)CI;
311 report_fatal_error("TODO: invoke inside setjmping functions");
312 }
313 }
314 }
315
316 // add a cleanup before each return
317 for (Function::iterator BBI = F->begin(), E = F->end(); BBI != E; ) {
318 BasicBlock *BB = BBI++;
319 TerminatorInst *TI = BB->getTerminator();
320 if (isa<ReturnInst>(TI)) {
321 CallInst::Create(CleanupSetjmp, "", TI);
322 }
323 }
324 }
325
326 for (unsigned i = 0; i < ToErase.size(); i++) {
327 ToErase[i]->eraseFromParent();
328 }
329
330 // Finally, our modifications to the cfg can break dominance of SSA variables. For example,
331 // if (x()) { .. setjmp() .. }
332 // if (y()) { .. longjmp() .. }
333 // We must split the longjmp block, and it can jump into the setjmp one. But t hat means that when
334 // we split the setjmp block, it's first part no longer dominates its second p art - there is
335 // a theoretically possible control flow path where x() is false, then y() is true and we
336 // reach the second part of the setjmp block, without ever reaching the first part. So,
337 // we recalculate regs vs. mem
338 for (FunctionPhisMap::iterator I = SetjmpOutputPhis.begin(); I != SetjmpOutput Phis.end(); I++) {
339 Function *F = I->first;
340 doRegToMem(*F);
341 doMemToReg(*F);
342 }
343
344 return true;
345 }
346
347 ModulePass *llvm::createLowerEmSetjmpPass() {
348 return new LowerEmSetjmp();
349 }
OLDNEW
« no previous file with comments | « lib/Transforms/NaCl/LowerEmExceptionsPass.cpp ('k') | lib/Transforms/NaCl/NoExitRuntime.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698