/src/hermes/lib/BCGen/HBC/Passes.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #include "hermes/BCGen/HBC/Passes.h" |
9 | | |
10 | | #include "hermes/BCGen/BCOpt.h" |
11 | | #include "hermes/BCGen/HBC/BytecodeGenerator.h" |
12 | | #include "hermes/BCGen/HBC/BytecodeStream.h" |
13 | | #include "hermes/BCGen/HBC/HBC.h" |
14 | | #include "hermes/BCGen/HBC/ISel.h" |
15 | | #include "hermes/BCGen/Lowering.h" |
16 | | |
17 | | #include "llvh/ADT/SetVector.h" |
18 | | |
19 | | #define DEBUG_TYPE "hbc-backend" |
20 | | |
21 | | namespace hermes { |
22 | | namespace hbc { |
23 | | |
24 | | namespace { |
25 | | /// In blockToFix, change all incoming Phi values from previousBlock to instead |
26 | | /// come from newBlock. |
27 | | void updateIncomingPhiValues( |
28 | | BasicBlock *blockToFix, |
29 | | BasicBlock *previousBlock, |
30 | 0 | BasicBlock *newBlock) { |
31 | 0 | for (auto &inst : *blockToFix) { |
32 | 0 | auto *phi = llvh::dyn_cast<PhiInst>(&inst); |
33 | 0 | if (!phi) |
34 | 0 | return; |
35 | | |
36 | 0 | for (int i = 0, e = phi->getNumEntries(); i < e; i++) { |
37 | 0 | auto entry = phi->getEntry(i); |
38 | 0 | if (entry.second == previousBlock) { |
39 | 0 | phi->updateEntry(i, entry.first, newBlock); |
40 | 0 | } |
41 | 0 | } |
42 | 0 | } |
43 | 0 | } |
44 | | // Get the next instruction(s) after this Instruction. Creates branches as |
45 | | // necessary. |
46 | | llvh::SmallVector<Instruction *, 4> getInsertionPointsAfter( |
47 | | IRBuilder &builder, |
48 | 292k | Instruction *inst) { |
49 | 292k | llvh::SmallVector<Instruction *, 4> points; |
50 | | |
51 | 292k | if (!llvh::isa<TerminatorInst>(inst)) { |
52 | | // Easy case for non-terminators. Just return the next inst. |
53 | 292k | auto pos = inst->getIterator(); |
54 | 292k | pos++; |
55 | 292k | points.push_back(&*pos); |
56 | 292k | return points; |
57 | 292k | } |
58 | | |
59 | 0 | for (int i = 0, e = inst->getNumOperands(); i < e; i++) { |
60 | 0 | BasicBlock *target = llvh::dyn_cast<BasicBlock>(inst->getOperand(i)); |
61 | 0 | if (!target) |
62 | 0 | continue; |
63 | | |
64 | 0 | auto *detour = builder.createBasicBlock(target->getParent()); |
65 | 0 | builder.setInsertionBlock(detour); |
66 | 0 | auto *bounce = builder.createBranchInst(target); |
67 | 0 | inst->setOperand(detour, i); |
68 | 0 | updateIncomingPhiValues(target, inst->getParent(), detour); |
69 | 0 | points.push_back(bounce); |
70 | 0 | } |
71 | 0 | return points; |
72 | 292k | } |
73 | | |
74 | | /// Update the insertion point of the builder to the "entry insertion point" |
75 | | /// of the function, which is where we insert new lowered instructions that must |
76 | | /// execute on entry. |
77 | 56.1k | void updateToEntryInsertionPoint(IRBuilder &builder, Function *F) { |
78 | 56.1k | auto &BB = F->front(); |
79 | 56.1k | auto it = BB.begin(); |
80 | 56.1k | auto end = BB.end(); |
81 | | // Skip all HBCCreateEnvironmentInst. |
82 | 112k | while (it != end && llvh::isa<HBCCreateEnvironmentInst>(*it)) |
83 | 56.1k | ++it; |
84 | | |
85 | 56.1k | builder.setInsertionPoint(&*it); |
86 | 56.1k | } |
87 | | |
88 | | } // namespace |
89 | | |
90 | 921k | bool LoadConstants::operandMustBeLiteral(Instruction *Inst, unsigned opIndex) { |
91 | | // HBCLoadConstInst is meant to load a constant |
92 | 921k | if (llvh::isa<HBCLoadConstInst>(Inst)) |
93 | 116 | return true; |
94 | | |
95 | | // The operand of HBCLoadParamInst is a literal index. |
96 | 921k | if (llvh::isa<HBCLoadParamInst>(Inst)) |
97 | 0 | return true; |
98 | | |
99 | 921k | if (llvh::isa<HBCAllocObjectFromBufferInst>(Inst)) |
100 | 0 | return true; |
101 | | |
102 | | // All operands of AllocArrayInst are literals. |
103 | 921k | if (llvh::isa<AllocArrayInst>(Inst)) |
104 | 109k | return true; |
105 | | |
106 | 812k | if (llvh::isa<AllocObjectInst>(Inst)) { |
107 | | // The AllocObjectInst::SizeIdx is a literal. |
108 | 215 | if (opIndex == AllocObjectInst::SizeIdx) |
109 | 215 | return true; |
110 | | // AllocObjectInst::ParentObjectIdx is a literal if it is the EmptySentinel. |
111 | 0 | if (opIndex == AllocObjectInst::ParentObjectIdx && |
112 | 0 | llvh::isa<EmptySentinel>(Inst->getOperand(opIndex))) |
113 | 0 | return true; |
114 | | |
115 | 0 | return false; |
116 | 0 | } |
117 | | |
118 | | // SwitchInst's rest of the operands are case values, |
119 | | // hence they will stay as constant. |
120 | 812k | if (llvh::isa<SwitchInst>(Inst) && opIndex > 0) |
121 | 0 | return true; |
122 | | |
123 | | // StoreOwnPropertyInst and StoreNewOwnPropertyInst. |
124 | 812k | if (auto *SOP = llvh::dyn_cast<StoreOwnPropertyInst>(Inst)) { |
125 | 306k | if (opIndex == StoreOwnPropertyInst::PropertyIdx) { |
126 | 102k | if (llvh::isa<StoreNewOwnPropertyInst>(Inst)) { |
127 | | // In StoreNewOwnPropertyInst the property name must be a literal. |
128 | 0 | return true; |
129 | 0 | } |
130 | | |
131 | | // If the propery is a LiteralNumber, the property is enumerable, and it |
132 | | // is a valid array index, it is coming from an array initialization and |
133 | | // we will emit it as PutByIndex. |
134 | 102k | if (auto *LN = llvh::dyn_cast<LiteralNumber>(Inst->getOperand(opIndex))) { |
135 | 102k | if (SOP->getIsEnumerable() && LN->convertToArrayIndex().hasValue()) |
136 | 102k | return true; |
137 | 102k | } |
138 | 102k | } |
139 | | |
140 | | // StoreOwnPropertyInst's isEnumerable is a boolean constant. |
141 | 204k | if (opIndex == StoreOwnPropertyInst::IsEnumerableIdx) |
142 | 102k | return true; |
143 | | |
144 | 102k | return false; |
145 | 204k | } |
146 | | |
147 | | // If StorePropertyInst's property ID is a LiteralString, we will keep it |
148 | | // untouched and emit try_put_by_id eventually. |
149 | 506k | if (llvh::isa<StorePropertyInst>(Inst) && |
150 | 506k | opIndex == StorePropertyInst::PropertyIdx && |
151 | 506k | llvh::isa<LiteralString>(Inst->getOperand(opIndex))) |
152 | 1.08k | return true; |
153 | | |
154 | | // If LoadPropertyInst's property ID is a LiteralString, we will keep it |
155 | | // untouched and emit try_put_by_id eventually. |
156 | 504k | if (llvh::isa<LoadPropertyInst>(Inst) && |
157 | 504k | opIndex == LoadPropertyInst::PropertyIdx && |
158 | 504k | llvh::isa<LiteralString>(Inst->getOperand(opIndex))) |
159 | 94.4k | return true; |
160 | | |
161 | | // If DeletePropertyInst's property ID is a LiteralString, we will keep it |
162 | | // untouched and emit try_put_by_id eventually. |
163 | 410k | if (llvh::isa<DeletePropertyInst>(Inst) && |
164 | 410k | opIndex == DeletePropertyInst::PropertyIdx && |
165 | 410k | llvh::isa<LiteralString>(Inst->getOperand(opIndex))) |
166 | 0 | return true; |
167 | | |
168 | | // StoreGetterSetterInst's isEnumerable is a boolean constant. |
169 | 410k | if (llvh::isa<StoreGetterSetterInst>(Inst) && |
170 | 410k | opIndex == StoreGetterSetterInst::IsEnumerableIdx) |
171 | 0 | return true; |
172 | | |
173 | | // Both pattern and flags operands of the CreateRegExpInst |
174 | | // are literal strings. |
175 | 410k | if (llvh::isa<CreateRegExpInst>(Inst)) |
176 | 46 | return true; |
177 | | |
178 | 410k | if (llvh::isa<SwitchImmInst>(Inst) && |
179 | 410k | (opIndex == SwitchImmInst::MinValueIdx || |
180 | 0 | opIndex == SwitchImmInst::SizeIdx || |
181 | 0 | opIndex >= SwitchImmInst::FirstCaseIdx)) |
182 | 0 | return true; |
183 | | |
184 | | /// CallBuiltin's callee and "this" should always be literals. |
185 | 410k | if (llvh::isa<CallBuiltinInst>(Inst) && |
186 | 410k | (opIndex == CallBuiltinInst::CalleeIdx || |
187 | 123k | opIndex == CallBuiltinInst::ThisIdx)) |
188 | 1.03k | return true; |
189 | | |
190 | | /// Call's new.target must be literal if it is undefined (well, it doesn't, |
191 | | /// but an undefined new.target won't be emitted to bytecode, hence it doesn't |
192 | | /// need to be loaded). |
193 | 409k | if (llvh::isa<CallInst>(Inst) && opIndex == CallInst::NewTargetIdx) |
194 | 1.05k | return true; |
195 | | |
196 | | /// GetBuiltinClosureInst's builtin index is always literal. |
197 | 408k | if (llvh::isa<GetBuiltinClosureInst>(Inst) && |
198 | 408k | opIndex == GetBuiltinClosureInst::BuiltinIndexIdx) |
199 | 0 | return true; |
200 | | |
201 | | #ifdef HERMES_RUN_WASM |
202 | | /// CallIntrinsic's IntrinsicIndexIdx should always be literals. |
203 | | if (llvh::isa<CallIntrinsicInst>(Inst) && |
204 | | (opIndex == CallIntrinsicInst::IntrinsicIndexIdx)) |
205 | | return true; |
206 | | #endif |
207 | | |
208 | 408k | if (llvh::isa<IteratorCloseInst>(Inst) && |
209 | 408k | opIndex == IteratorCloseInst::IgnoreInnerExceptionIdx) { |
210 | 2.73k | return true; |
211 | 2.73k | } |
212 | | |
213 | 405k | if (llvh::isa<ThrowIfHasRestrictedGlobalPropertyInst>(Inst) && |
214 | 405k | opIndex == ThrowIfHasRestrictedGlobalPropertyInst::PropertyIdx) { |
215 | 0 | return true; |
216 | 0 | } |
217 | | // DirectEvalInst's isStrict is a boolean constant. |
218 | 405k | if (llvh::isa<DirectEvalInst>(Inst) && |
219 | 405k | opIndex == DirectEvalInst::IsStrictIdx) { |
220 | 0 | return true; |
221 | 0 | } |
222 | | |
223 | 405k | return false; |
224 | 405k | } |
225 | | |
226 | 28.0k | bool LoadConstants::runOnFunction(Function *F) { |
227 | 28.0k | IRBuilder builder(F); |
228 | 28.0k | bool changed = false; |
229 | | |
230 | | /// Inserts and returns a load instruction for \p literal before \p where. |
231 | 507k | auto createLoadLiteral = [&builder](Literal *literal, Instruction *where) { |
232 | 507k | builder.setInsertionPoint(where); |
233 | 507k | return llvh::isa<GlobalObject>(literal) |
234 | 507k | ? cast<Instruction>(builder.createHBCGetGlobalObjectInst()) |
235 | 507k | : cast<Instruction>(builder.createHBCLoadConstInst(literal)); |
236 | 507k | }; |
237 | | |
238 | 129k | for (BasicBlock &BB : *F) { |
239 | 590k | for (auto &I : BB) { |
240 | 590k | if (auto *phi = llvh::dyn_cast<PhiInst>(&I)) { |
241 | | // Since PhiInsts must always be at the start of a basic block, we have |
242 | | // to insert the load instruction in the predecessor. This lowering is |
243 | | // sub-optimal: for conditional branches, the load constant operation |
244 | | // will be performed before the branch decides which path to take. |
245 | 2.40k | for (unsigned i = 0, e = phi->getNumEntries(); i < e; ++i) { |
246 | 1.60k | auto [val, bb] = phi->getEntry(i); |
247 | 1.60k | if (auto *literal = llvh::dyn_cast<Literal>(val)) { |
248 | 116 | auto *load = createLoadLiteral(literal, bb->getTerminator()); |
249 | 116 | phi->updateEntry(i, load, bb); |
250 | 116 | changed = true; |
251 | 116 | } |
252 | 1.60k | } |
253 | 800 | continue; |
254 | 800 | } |
255 | | // For all other instructions, insert load constants right before the they |
256 | | // are needed. This minimizes their live range and therefore reduces |
257 | | // register pressure. CodeMotion and CSE can later hoist and deduplicate |
258 | | // them. |
259 | 2.17M | for (unsigned i = 0, e = I.getNumOperands(); i < e; ++i) { |
260 | 1.58M | if (auto *literal = llvh::dyn_cast<Literal>(I.getOperand(i))) { |
261 | 921k | if (!operandMustBeLiteral(&I, i)) { |
262 | 507k | auto *load = createLoadLiteral(literal, &I); |
263 | 507k | I.setOperand(load, i); |
264 | 507k | changed = true; |
265 | 507k | } |
266 | 921k | } |
267 | 1.58M | } |
268 | 589k | } |
269 | 129k | } |
270 | 28.0k | return changed; |
271 | 28.0k | } |
272 | | |
273 | 28.0k | bool LoadParameters::runOnFunction(Function *F) { |
274 | 28.0k | IRBuilder builder(F); |
275 | 28.0k | bool changed = false; |
276 | | |
277 | 28.0k | updateToEntryInsertionPoint(builder, F); |
278 | | |
279 | | // Index of 0 is the "this" parameter. |
280 | 28.0k | unsigned index = 1; |
281 | 28.0k | for (Parameter *p : F->getParameters()) { |
282 | 27.6k | auto *load = |
283 | 27.6k | builder.createHBCLoadParamInst(builder.getLiteralNumber(index)); |
284 | 27.6k | p->replaceAllUsesWith(load); |
285 | 27.6k | index++; |
286 | 27.6k | changed = true; |
287 | 27.6k | } |
288 | | |
289 | | // Lower accesses to "this". |
290 | 28.0k | auto *thisParam = F->getThisParameter(); |
291 | 28.0k | if (thisParam && thisParam->hasUsers()) { |
292 | | // In strict mode just use param 0 directly. In non-strict, we must coerce |
293 | | // it to an object. |
294 | 6 | Value *getThisInst = F->isStrictMode() |
295 | 6 | ? cast<Value>( |
296 | 0 | builder.createHBCLoadParamInst(builder.getLiteralNumber(0))) |
297 | 6 | : cast<Value>(builder.createHBCGetThisNSInst()); |
298 | 6 | thisParam->replaceAllUsesWith(getThisInst); |
299 | 6 | changed = true; |
300 | 6 | } |
301 | 28.0k | return changed; |
302 | 28.0k | } |
303 | | |
304 | | ScopeCreationInst *LowerLoadStoreFrameInst::getScope( |
305 | | IRBuilder &builder, |
306 | | Variable *var, |
307 | 28.0k | ScopeCreationInst *environment) { |
308 | 28.0k | if (var->getParent() == environment->getCreatedScopeDesc()) { |
309 | | // Var's scope belongs to the current function. |
310 | 28.0k | assert( |
311 | 28.0k | var->getParent()->getFunction() == builder.getFunction() && |
312 | 28.0k | "Scope should only be found if var is not captured from another " |
313 | 28.0k | "function"); |
314 | 28.0k | return environment; |
315 | 28.0k | } |
316 | | |
317 | 37 | auto *environmentWithParent = |
318 | 37 | llvh::dyn_cast<NestedScopeCreationInst>(environment); |
319 | 37 | if (!environmentWithParent) { |
320 | | // Failed to find the variable in the current Function -- the first scope in |
321 | | // a Function is always an non-NestedScopeCreationInst. |
322 | 37 | assert( |
323 | 37 | var->getParent()->getFunction() != builder.getFunction() && |
324 | 37 | "Failed to find scope in current function for local variable."); |
325 | 37 | return builder.createHBCResolveEnvironment( |
326 | 37 | environment->getCreatedScopeDesc(), var->getParent()); |
327 | 37 | } |
328 | | |
329 | | // Keep looking. |
330 | 0 | return getScope(builder, var, environmentWithParent->getParentScope()); |
331 | 37 | } |
332 | | |
333 | 28.0k | bool LowerLoadStoreFrameInst::runOnFunction(Function *F) { |
334 | 28.0k | IRBuilder builder(F); |
335 | 28.0k | bool changed = false; |
336 | | |
337 | 28.0k | bool fnScopeCreated = false; |
338 | 129k | for (BasicBlock &BB : F->getBasicBlockList()) { |
339 | 720k | for (auto I = BB.begin(), E = BB.end(); I != E; /* nothing */) { |
340 | 590k | Instruction *Inst = &*I; |
341 | 590k | ++I; |
342 | 590k | if (auto *csi = llvh::dyn_cast<CreateScopeInst>(Inst)) { |
343 | 28.0k | fnScopeCreated |= |
344 | 28.0k | csi->getCreatedScopeDesc() == F->getFunctionScopeDesc(); |
345 | 28.0k | builder.setInsertionPoint(csi); |
346 | 28.0k | Instruction *llInst = |
347 | 28.0k | builder.createHBCCreateEnvironmentInst(csi->getCreatedScopeDesc()); |
348 | 28.0k | Inst->replaceAllUsesWith(llInst); |
349 | 28.0k | Inst->eraseFromParent(); |
350 | 28.0k | changed = true; |
351 | 562k | } else if (auto cisi = llvh::dyn_cast<CreateInnerScopeInst>(Inst)) { |
352 | 0 | builder.setInsertionPoint(cisi); |
353 | 0 | assert( |
354 | 0 | llvh::isa<HBCCreateEnvironmentInst>(cisi->getParentScope()) || |
355 | 0 | llvh::isa<HBCCreateInnerEnvironmentInst>(cisi->getParentScope())); |
356 | | |
357 | 0 | Instruction *llInst = builder.createHBCCreateInnerEnvironmentInst( |
358 | 0 | cisi->getParentScope(), cisi->getCreatedScopeDesc()); |
359 | 0 | Inst->replaceAllUsesWith(llInst); |
360 | 0 | Inst->eraseFromParent(); |
361 | 0 | changed = true; |
362 | 0 | } |
363 | 590k | } |
364 | 129k | } |
365 | | |
366 | | // At this point all scopes used in F should be materialized. However, not |
367 | | // materializing F's scope potentially breaks lazy compilation as the compiler |
368 | | // always assumes all "external" scopes (i.e., those that have already been |
369 | | // compiled) have been materialized. Therefore, materialize the function scope |
370 | | // now. This instruction will be optimized out if unused in optimized builds. |
371 | 28.0k | assert(fnScopeCreated && "Function body scope not materialized."); |
372 | 28.0k | (void)fnScopeCreated; |
373 | | |
374 | 129k | for (BasicBlock &BB : F->getBasicBlockList()) { |
375 | 720k | for (auto I = BB.begin(), E = BB.end(); I != E; /* nothing */) { |
376 | | // Keep the reference and increment iterator first. |
377 | 590k | Instruction *Inst = &*I; |
378 | 590k | ++I; |
379 | | |
380 | 590k | builder.setLocation(Inst->getLocation()); |
381 | 590k | builder.setCurrentSourceLevelScope(Inst->getSourceLevelScope()); |
382 | | |
383 | 590k | switch (Inst->getKind()) { |
384 | 39 | case ValueKind::LoadFrameInstKind: { |
385 | 39 | auto *LFI = cast<LoadFrameInst>(Inst); |
386 | 39 | auto *var = LFI->getLoadVariable(); |
387 | 39 | auto *environment = LFI->getEnvironment(); |
388 | | |
389 | 39 | builder.setInsertionPoint(Inst); |
390 | 39 | ScopeCreationInst *scope = getScope(builder, var, environment); |
391 | 39 | Instruction *newInst = |
392 | 39 | builder.createHBCLoadFromEnvironmentInst(scope, var); |
393 | | |
394 | 39 | Inst->replaceAllUsesWith(newInst); |
395 | 39 | Inst->eraseFromParent(); |
396 | 39 | changed = true; |
397 | 39 | break; |
398 | 0 | } |
399 | 28.0k | case ValueKind::StoreFrameInstKind: { |
400 | 28.0k | auto *SFI = cast<StoreFrameInst>(Inst); |
401 | 28.0k | auto *var = SFI->getVariable(); |
402 | 28.0k | auto *val = SFI->getValue(); |
403 | 28.0k | auto *environment = SFI->getEnvironment(); |
404 | | |
405 | 28.0k | builder.setInsertionPoint(Inst); |
406 | 28.0k | ScopeCreationInst *scope = getScope(builder, var, environment); |
407 | 28.0k | builder.createHBCStoreToEnvironmentInst(scope, val, var); |
408 | | |
409 | 28.0k | Inst->eraseFromParent(); |
410 | 28.0k | changed = true; |
411 | 28.0k | break; |
412 | 0 | } |
413 | 27.8k | case ValueKind::CreateFunctionInstKind: { |
414 | 27.8k | auto *CFI = cast<CreateFunctionInst>(Inst); |
415 | 27.8k | auto *environment = CFI->getEnvironment(); |
416 | | |
417 | 27.8k | builder.setInsertionPoint(Inst); |
418 | 27.8k | auto *newInst = builder.createHBCCreateFunctionInst( |
419 | 27.8k | CFI->getFunctionCode(), environment); |
420 | | |
421 | 27.8k | Inst->replaceAllUsesWith(newInst); |
422 | 27.8k | Inst->eraseFromParent(); |
423 | 27.8k | changed = true; |
424 | 27.8k | break; |
425 | 0 | } |
426 | 2 | case ValueKind::CreateGeneratorInstKind: { |
427 | 2 | auto *CFI = cast<CreateGeneratorInst>(Inst); |
428 | 2 | auto *environment = CFI->getEnvironment(); |
429 | | |
430 | 2 | builder.setInsertionPoint(Inst); |
431 | 2 | auto *newInst = builder.createHBCCreateGeneratorInst( |
432 | 2 | CFI->getFunctionCode(), environment); |
433 | | |
434 | 2 | Inst->replaceAllUsesWith(newInst); |
435 | 2 | Inst->eraseFromParent(); |
436 | 2 | changed = true; |
437 | 2 | break; |
438 | 0 | } |
439 | 534k | default: |
440 | 534k | break; |
441 | 590k | } |
442 | 590k | } |
443 | 129k | } |
444 | 28.0k | return changed; |
445 | 28.0k | } |
446 | | |
447 | 28.0k | CreateArgumentsInst *LowerArgumentsArray::getCreateArgumentsInst(Function *F) { |
448 | | // CreateArgumentsInst is always in the first block in normal functions, |
449 | | // but is in the second block in GeneratorInnerFunctions. |
450 | 28.0k | if (llvh::isa<GeneratorInnerFunction>(F)) { |
451 | 4 | for (BasicBlock *succ : F->front().getTerminator()->successors()) { |
452 | 4 | for (auto &inst : *succ) { |
453 | 4 | if (auto *target = llvh::dyn_cast<CreateArgumentsInst>(&inst)) { |
454 | 0 | return target; |
455 | 0 | } |
456 | 4 | } |
457 | 4 | } |
458 | 28.0k | } else { |
459 | 306k | for (auto &inst : F->front()) { |
460 | 306k | if (auto *target = llvh::dyn_cast<CreateArgumentsInst>(&inst)) { |
461 | 2 | return target; |
462 | 2 | } |
463 | 306k | } |
464 | 28.0k | } |
465 | 28.0k | return nullptr; |
466 | 28.0k | } |
467 | | |
468 | 28.0k | bool LowerArgumentsArray::runOnFunction(Function *F) { |
469 | 28.0k | IRBuilder builder(F); |
470 | 28.0k | updateToEntryInsertionPoint(builder, F); |
471 | | |
472 | 28.0k | CreateArgumentsInst *createArguments = getCreateArgumentsInst(F); |
473 | 28.0k | if (!createArguments) { |
474 | 28.0k | return false; |
475 | 28.0k | } |
476 | | |
477 | 2 | builder.setInsertionPoint(createArguments); |
478 | 2 | AllocStackInst *lazyReg = builder.createAllocStackInst("arguments"); |
479 | 2 | builder.createStoreStackInst(builder.getLiteralUndefined(), lazyReg); |
480 | | |
481 | | // Process all LoadPropertyInst's first because they may add another user |
482 | | // to the list of users of createArguments. |
483 | | // Specifically the case when `arguments[arguments]` is accessed. |
484 | | // Note that in such a case, a single LoadPropertyInst will appear twice in |
485 | | // the use list. Use a set so we only remove it once. |
486 | 2 | llvh::SmallSetVector<Instruction *, 16> uniqueUsers; |
487 | 2 | uniqueUsers.insert( |
488 | 2 | createArguments->getUsers().begin(), createArguments->getUsers().end()); |
489 | 11 | for (Value *user : uniqueUsers) { |
490 | 11 | auto *load = llvh::dyn_cast<LoadPropertyInst>(user); |
491 | 11 | if (load && load->getObject() == createArguments) { |
492 | 0 | builder.setInsertionPoint(load); |
493 | 0 | builder.setLocation(load->getLocation()); |
494 | 0 | builder.setCurrentSourceLevelScope(load->getSourceLevelScope()); |
495 | 0 | auto *propertyString = llvh::dyn_cast<LiteralString>(load->getProperty()); |
496 | 0 | if (propertyString && propertyString->getValue().str() == "length") { |
497 | | // For `arguments.length`, get the length. |
498 | 0 | auto *length = builder.createHBCGetArgumentsLengthInst(lazyReg); |
499 | 0 | load->replaceAllUsesWith(length); |
500 | 0 | load->eraseFromParent(); |
501 | 0 | } else { |
502 | | // For all other property loads, get by index. |
503 | 0 | auto *get = builder.createHBCGetArgumentsPropByValInst( |
504 | 0 | load->getProperty(), lazyReg); |
505 | 0 | load->replaceAllUsesWith(get); |
506 | 0 | load->eraseFromParent(); |
507 | 0 | } |
508 | 0 | } |
509 | 11 | } |
510 | | |
511 | 2 | uniqueUsers.clear(); |
512 | 2 | uniqueUsers.insert( |
513 | 2 | createArguments->getUsers().begin(), createArguments->getUsers().end()); |
514 | 11 | for (Value *user : uniqueUsers) { |
515 | 11 | if (auto *phi = llvh::dyn_cast<PhiInst>(user)) { |
516 | | // We have to insert another branch where we can reify the value. |
517 | 0 | for (int i = 0, n = phi->getNumEntries(); i < n; i++) { |
518 | 0 | auto entry = phi->getEntry(i); |
519 | 0 | if (entry.first != createArguments) |
520 | 0 | continue; |
521 | | |
522 | 0 | auto *previousBlock = cast<BasicBlock>(entry.second); |
523 | 0 | auto *thisBlock = phi->getParent(); |
524 | |
|
525 | 0 | auto *newBlock = builder.createBasicBlock(F); |
526 | 0 | builder.setInsertionBlock(newBlock); |
527 | 0 | builder.createHBCReifyArgumentsInst(lazyReg); |
528 | 0 | auto *reifiedValue = builder.createLoadStackInst(lazyReg); |
529 | 0 | builder.createBranchInst(thisBlock); |
530 | |
|
531 | 0 | phi->updateEntry(i, reifiedValue, newBlock); |
532 | | // Update all other PHI nodes in thisBlock that currently reference |
533 | | // previousBlock so they instead reference newBlock. |
534 | 0 | updateIncomingPhiValues(thisBlock, previousBlock, newBlock); |
535 | |
|
536 | 0 | auto *branch = previousBlock->getTerminator(); |
537 | 0 | for (int j = 0, m = branch->getNumOperands(); j < m; j++) |
538 | 0 | if (branch->getOperand(j) == thisBlock) |
539 | 0 | branch->setOperand(newBlock, j); |
540 | 0 | } |
541 | 11 | } else if (auto *inst = llvh::dyn_cast<Instruction>(user)) { |
542 | | // For other users, insert a reification so we can replace |
543 | | // the usage with this array. |
544 | 11 | builder.setInsertionPoint(inst); |
545 | 11 | builder.setLocation(inst->getLocation()); |
546 | 11 | builder.setCurrentSourceLevelScope(inst->getSourceLevelScope()); |
547 | 11 | builder.createHBCReifyArgumentsInst(lazyReg); |
548 | 11 | auto *array = builder.createLoadStackInst(lazyReg); |
549 | 33 | for (int i = 0, n = inst->getNumOperands(); i < n; i++) { |
550 | 22 | if (inst->getOperand(i) == createArguments) { |
551 | 11 | inst->setOperand(array, i); |
552 | 11 | } |
553 | 22 | } |
554 | 11 | } else { |
555 | 0 | hermes_fatal("CreateArguments used for a non-Instruction."); |
556 | 0 | } |
557 | 11 | } |
558 | | |
559 | 2 | createArguments->eraseFromParent(); |
560 | 2 | return true; |
561 | 2 | } |
562 | | |
563 | 28.0k | bool DedupReifyArguments::runOnFunction(Function *F) { |
564 | 28.0k | bool changed = false; |
565 | | |
566 | | // Check if there are any HBCReifyArgumentsInst in the function before |
567 | | // calculating dominator tree and reverse post order to save compile time. |
568 | 28.0k | bool hasRAI = false; |
569 | 100k | for (auto &BB : *F) { |
570 | 473k | for (auto &inst : BB.getInstList()) { |
571 | 473k | if (llvh::isa<HBCReifyArgumentsInst>(&inst)) { |
572 | 2 | hasRAI = true; |
573 | 2 | break; |
574 | 2 | } |
575 | 473k | } |
576 | 100k | if (hasRAI) |
577 | 2 | break; |
578 | 100k | } |
579 | | |
580 | 28.0k | if (!hasRAI) |
581 | 28.0k | return false; |
582 | | |
583 | 2 | DominanceInfo domInfo{F}; |
584 | 2 | PostOrderAnalysis PO(F); |
585 | 2 | IRBuilder::InstructionDestroyer destroyer; |
586 | | |
587 | 2 | llvh::SmallVector<BasicBlock *, 16> reversePO(PO.rbegin(), PO.rend()); |
588 | 2 | llvh::SmallVector<HBCReifyArgumentsInst *, 4> reifications; |
589 | | |
590 | 33.8k | for (auto *BB : reversePO) { |
591 | 133k | for (auto &inst : BB->getInstList()) { |
592 | 133k | if (auto *reify = llvh::dyn_cast<HBCReifyArgumentsInst>(&inst)) { |
593 | 11 | HBCReifyArgumentsInst *dominator = nullptr; |
594 | 11 | for (auto *possibleParent : reifications) { |
595 | 9 | if (domInfo.properlyDominates(possibleParent, reify)) { |
596 | 9 | dominator = possibleParent; |
597 | 9 | break; |
598 | 9 | } |
599 | 9 | } |
600 | | |
601 | 11 | if (dominator) { |
602 | 9 | destroyer.add(reify); |
603 | 9 | changed = true; |
604 | 9 | } else { |
605 | 2 | reifications.push_back(reify); |
606 | 2 | } |
607 | 11 | } |
608 | 133k | } |
609 | 33.8k | } |
610 | 2 | return changed; |
611 | 28.0k | } |
612 | | |
613 | 28.0k | bool LowerConstruction::runOnFunction(Function *F) { |
614 | 28.0k | IRBuilder builder(F); |
615 | 28.0k | auto *prototypeString = builder.getLiteralString("prototype"); |
616 | | |
617 | 129k | for (BasicBlock &BB : F->getBasicBlockList()) { |
618 | 129k | IRBuilder::InstructionDestroyer destroyer; |
619 | 590k | for (Instruction &I : BB) { |
620 | 590k | if (auto *constructor = llvh::dyn_cast<ConstructInst>(&I)) { |
621 | 0 | builder.setInsertionPoint(constructor); |
622 | 0 | builder.setLocation(constructor->getLocation()); |
623 | 0 | builder.setCurrentSourceLevelScope(constructor->getSourceLevelScope()); |
624 | 0 | auto closure = constructor->getCallee(); |
625 | 0 | auto prototype = |
626 | 0 | builder.createLoadPropertyInst(closure, prototypeString); |
627 | 0 | auto thisObject = builder.createHBCCreateThisInst(prototype, closure); |
628 | |
|
629 | 0 | llvh::SmallVector<Value *, 8> args; |
630 | 0 | for (int i = 1, n = constructor->getNumArguments(); i < n; i++) { |
631 | 0 | args.push_back(constructor->getArgument(i)); |
632 | 0 | } |
633 | 0 | auto newConstructor = builder.createHBCConstructInst( |
634 | 0 | closure, constructor->getNewTarget(), thisObject, args); |
635 | 0 | auto finalValue = builder.createHBCGetConstructedObjectInst( |
636 | 0 | thisObject, newConstructor); |
637 | 0 | constructor->replaceAllUsesWith(finalValue); |
638 | 0 | destroyer.add(constructor); |
639 | 0 | } |
640 | 590k | } |
641 | 129k | } |
642 | 28.0k | return true; |
643 | 28.0k | } |
644 | | |
645 | 28.0k | bool LowerCalls::runOnFunction(Function *F) { |
646 | 28.0k | IRBuilder builder(F); |
647 | 28.0k | bool changed = false; |
648 | | |
649 | 129k | for (auto &BB : *F) { |
650 | 1.12M | for (auto &I : BB) { |
651 | 1.12M | auto *call = llvh::dyn_cast<CallInst>(&I); |
652 | | // This also matches constructors. |
653 | 1.12M | if (!call) |
654 | 1.12M | continue; |
655 | 1.05k | builder.setInsertionPoint(call); |
656 | 1.05k | changed = true; |
657 | | |
658 | 1.05k | auto reg = RA_.getLastRegister().getIndex() - |
659 | 1.05k | HVMRegisterAllocator::CALL_EXTRA_REGISTERS; |
660 | | |
661 | 244k | for (int i = 0, e = call->getNumArguments(); i < e; i++, --reg) { |
662 | | // If this is a Call instruction, emit explicit Movs to the argument |
663 | | // registers. If this is a CallN instruction, emit ImplicitMovs |
664 | | // instead, to express that these registers get written to by the CallN, |
665 | | // even though they are not the destination. |
666 | | // Lastly, if this is argument 0 of CallBuiltinInst emit ImplicitMov to |
667 | | // encode that the "this" register is implicitly set to undefined. |
668 | 243k | Value *arg = call->getArgument(i); |
669 | 243k | if (llvh::isa<HBCCallNInst>(call) || |
670 | 243k | (i == 0 && llvh::isa<CallBuiltinInst>(call))) { |
671 | 519 | auto *imov = builder.createImplicitMovInst(arg); |
672 | 519 | RA_.updateRegister(imov, Register(reg)); |
673 | 243k | } else { |
674 | 243k | auto *mov = builder.createMovInst(arg); |
675 | 243k | RA_.updateRegister(mov, Register(reg)); |
676 | 243k | call->setArgument(mov, i); |
677 | 243k | } |
678 | 243k | } |
679 | 1.05k | } |
680 | 129k | } |
681 | 28.0k | return changed; |
682 | 28.0k | } |
683 | | |
684 | 0 | bool RecreateCheapValues::runOnFunction(Function *F) { |
685 | 0 | IRBuilder builder(F); |
686 | 0 | llvh::SmallPtrSet<Instruction *, 4> potentiallyUnused; |
687 | 0 | bool changed = false; |
688 | |
|
689 | 0 | for (auto &BB : *F) { |
690 | 0 | IRBuilder::InstructionDestroyer destroyer; |
691 | 0 | for (auto &I : BB) { |
692 | 0 | auto *mov = llvh::dyn_cast<MovInst>(&I); |
693 | 0 | if (!mov) |
694 | 0 | continue; |
695 | 0 | auto *load = llvh::dyn_cast<HBCLoadConstInst>(mov->getSingleOperand()); |
696 | 0 | if (!load) |
697 | 0 | continue; |
698 | 0 | Literal *literal = load->getConst(); |
699 | |
|
700 | 0 | switch (literal->getKind()) { |
701 | 0 | case ValueKind::LiteralUndefinedKind: |
702 | 0 | case ValueKind::LiteralNullKind: |
703 | 0 | case ValueKind::LiteralBoolKind: |
704 | 0 | break; |
705 | 0 | case ValueKind::LiteralNumberKind: |
706 | 0 | if (cast<LiteralNumber>(literal)->isPositiveZero()) { |
707 | 0 | break; |
708 | 0 | } |
709 | 0 | continue; |
710 | 0 | default: |
711 | 0 | continue; |
712 | 0 | } |
713 | | |
714 | 0 | builder.setInsertionPoint(mov); |
715 | 0 | auto *recreation = builder.createHBCLoadConstInst(literal); |
716 | 0 | RA_.updateRegister(recreation, RA_.getRegister(mov)); |
717 | 0 | mov->replaceAllUsesWith(recreation); |
718 | 0 | destroyer.add(mov); |
719 | 0 | potentiallyUnused.insert(load); |
720 | 0 | changed = true; |
721 | 0 | } |
722 | 0 | } |
723 | | |
724 | 0 | { |
725 | 0 | IRBuilder::InstructionDestroyer destroyer; |
726 | 0 | for (auto *inst : potentiallyUnused) { |
727 | 0 | if (!inst->hasUsers()) { |
728 | 0 | destroyer.add(inst); |
729 | 0 | } |
730 | 0 | } |
731 | 0 | } |
732 | 0 | return changed; |
733 | 0 | } |
734 | | |
735 | 0 | bool LoadConstantValueNumbering::runOnFunction(Function *F) { |
736 | 0 | IRBuilder builder(F); |
737 | 0 | bool changed = false; |
738 | |
|
739 | 0 | for (auto &BB : *F) { |
740 | 0 | IRBuilder::InstructionDestroyer destroyer; |
741 | | // Maps a register number to the instruction that last modified it. |
742 | | // Every Instruction is either an HBCLoadConstInst or a Mov whose |
743 | | // operand is a HBCLoadConstInst |
744 | 0 | llvh::DenseMap<unsigned, Instruction *> regToInstMap{}; |
745 | 0 | for (auto &I : BB) { |
746 | 0 | HBCLoadConstInst *loadI{nullptr}; |
747 | | // Value numbering currently only tracks the values of registers that |
748 | | // have a constant in them, or that have had a constant moved in them. |
749 | 0 | if (!(loadI = llvh::dyn_cast<HBCLoadConstInst>(&I))) { |
750 | 0 | if (auto *movI = llvh::dyn_cast<MovInst>(&I)) { |
751 | 0 | loadI = llvh::dyn_cast<HBCLoadConstInst>(movI->getSingleOperand()); |
752 | 0 | } |
753 | 0 | } |
754 | |
|
755 | 0 | if (RA_.isAllocated(&I)) { |
756 | 0 | unsigned reg = RA_.getRegister(&I).getIndex(); |
757 | 0 | if (loadI) { |
758 | 0 | auto it = regToInstMap.find(reg); |
759 | 0 | if (it != regToInstMap.end()) { |
760 | 0 | auto prevI = it->second; |
761 | 0 | HBCLoadConstInst *prevLoad{nullptr}; |
762 | | // If the key is found, the instruction must be either an |
763 | | // HBCLoadConstInst, or a Mov whose operand is an HBCLoadConstInst. |
764 | 0 | if (!(prevLoad = llvh::dyn_cast<HBCLoadConstInst>(prevI))) { |
765 | 0 | prevLoad = llvh::dyn_cast<HBCLoadConstInst>(prevI->getOperand(0)); |
766 | 0 | } |
767 | 0 | if (prevLoad->isIdenticalTo(loadI)) { |
768 | 0 | I.replaceAllUsesWith(prevI); |
769 | 0 | destroyer.add(&I); |
770 | 0 | changed = true; |
771 | 0 | continue; |
772 | 0 | } |
773 | 0 | } |
774 | 0 | regToInstMap[reg] = &I; |
775 | 0 | continue; |
776 | 0 | } |
777 | 0 | regToInstMap.erase(reg); |
778 | 0 | } |
779 | | |
780 | 0 | for (auto index : I.getChangedOperands()) { |
781 | 0 | auto *operand = I.getOperand(index); |
782 | 0 | unsigned reg = RA_.getRegister(cast<Instruction>(operand)).getIndex(); |
783 | 0 | regToInstMap.erase(reg); |
784 | 0 | } |
785 | 0 | } |
786 | 0 | } |
787 | 0 | return changed; |
788 | 0 | } |
789 | | |
790 | 950k | bool SpillRegisters::requiresShortOutput(Instruction *I) { |
791 | 950k | if (llvh::isa<TerminatorInst>(I)) { |
792 | | // None of our terminators produce results at all |
793 | | // (though GetNextPNameInst modifies operands). |
794 | 33.8k | return false; |
795 | 33.8k | } |
796 | | |
797 | | // Instructions that produce no output, don't use the register, even when |
798 | | // allocated. |
799 | 917k | if (I->getType().isNoType()) |
800 | 40 | return false; |
801 | | |
802 | 917k | switch (I->getKind()) { |
803 | | // Some instructions become Movs or other opcodes with long variants: |
804 | 295k | case ValueKind::HBCSpillMovInstKind: |
805 | 302k | case ValueKind::LoadStackInstKind: |
806 | 573k | case ValueKind::MovInstKind: |
807 | 574k | case ValueKind::PhiInstKind: |
808 | | // Some instructions aren't actually encoded at all: |
809 | 582k | case ValueKind::AllocStackInstKind: |
810 | 584k | case ValueKind::TryEndInstKind: |
811 | 584k | case ValueKind::TryStartInstKind: |
812 | 584k | return false; |
813 | 332k | default: |
814 | 332k | return true; |
815 | 917k | } |
816 | 917k | } |
817 | | |
818 | 891k | bool SpillRegisters::requiresShortOperand(Instruction *I, int op) { |
819 | 891k | switch (I->getKind()) { |
820 | 706 | case ValueKind::PhiInstKind: |
821 | 271k | case ValueKind::MovInstKind: |
822 | 566k | case ValueKind::HBCSpillMovInstKind: |
823 | 574k | case ValueKind::LoadStackInstKind: |
824 | 574k | case ValueKind::StoreStackInstKind: |
825 | 574k | return false; |
826 | 120k | case ValueKind::CallInstKind: |
827 | 120k | case ValueKind::ConstructInstKind: |
828 | 241k | case ValueKind::CallBuiltinInstKind: |
829 | 241k | case ValueKind::HBCConstructInstKind: |
830 | 241k | case ValueKind::HBCCallDirectInstKind: |
831 | 241k | return op == CallInst::CalleeIdx; |
832 | 75.4k | default: |
833 | 75.4k | return true; |
834 | 891k | } |
835 | 891k | } |
836 | | |
837 | 48.0k | bool SpillRegisters::modifiesOperandRegister(Instruction *I, int op) { |
838 | 48.0k | return I->getChangedOperands().at((unsigned)op); |
839 | 48.0k | } |
840 | | |
841 | 28.0k | bool SpillRegisters::runOnFunction(Function *F) { |
842 | 28.0k | if (RA_.getMaxRegisterUsage() < boundary_) { |
843 | 28.0k | return false; |
844 | 28.0k | } |
845 | 8 | reserveLowRegisters(F); |
846 | | |
847 | 8 | IRBuilder builder(F); |
848 | 8 | llvh::SmallVector<std::pair<Instruction *, Register>, 2> toSpill; |
849 | | |
850 | 33.8k | for (BasicBlock &BB : F->getBasicBlockList()) { |
851 | 950k | for (Instruction &inst : BB) { |
852 | 950k | if (!RA_.isAllocated(&inst)) { |
853 | | // This instruction is dead. Don't bother spilling. |
854 | 0 | continue; |
855 | 0 | } |
856 | | |
857 | 950k | int tempReg = 0; |
858 | 950k | toSpill.clear(); |
859 | 950k | bool replaceWithFirstSpill = false; |
860 | 950k | builder.setLocation(inst.getLocation()); |
861 | 950k | builder.setCurrentSourceLevelScope(inst.getSourceLevelScope()); |
862 | | |
863 | 950k | auto myRegister = RA_.getRegister(&inst); |
864 | 950k | if (requiresShortOutput(&inst) && !isShort(myRegister)) { |
865 | 292k | auto temp = getReserved(tempReg++); |
866 | 292k | RA_.updateRegister(&inst, temp); |
867 | 292k | toSpill.push_back( |
868 | 292k | std::pair<Instruction *, Register>(&inst, myRegister)); |
869 | 292k | replaceWithFirstSpill = true; |
870 | 292k | } |
871 | | |
872 | 2.17M | for (int i = 0, e = inst.getNumOperands(); i < e; i++) { |
873 | 1.22M | auto *op = llvh::dyn_cast<Instruction>(inst.getOperand(i)); |
874 | 1.22M | if (!op || !RA_.isAllocated(op)) { |
875 | | // This is either not an instruction, or a dead instruction. |
876 | | // Either way, we don't have to do anything. |
877 | 331k | continue; |
878 | 331k | } |
879 | 891k | auto opRegister = RA_.getRegister(op); |
880 | | |
881 | 891k | if (requiresShortOperand(&inst, i) && !isShort(opRegister)) { |
882 | 48.0k | auto temp = getReserved(tempReg++); |
883 | | |
884 | 48.0k | builder.setInsertionPoint(&inst); |
885 | 48.0k | auto *load = builder.createHBCSpillMovInst(op); |
886 | 48.0k | RA_.updateRegister(load, temp); |
887 | 48.0k | inst.setOperand(load, i); |
888 | | |
889 | 48.0k | if (modifiesOperandRegister(&inst, i)) { |
890 | 2.63k | toSpill.push_back( |
891 | 2.63k | std::pair<Instruction *, Register>(load, opRegister)); |
892 | 2.63k | } |
893 | 48.0k | } |
894 | 891k | } |
895 | | |
896 | 950k | if (toSpill.size()) { |
897 | 292k | auto spillPoints = getInsertionPointsAfter(builder, &inst); |
898 | | |
899 | 292k | assert( |
900 | 292k | (!replaceWithFirstSpill || spillPoints.size() <= 1) && |
901 | 292k | "Asked to spill the value of a TerminatorInst. Our terminators " |
902 | 292k | "shouldn't produce values. It wouldn't have mattered, but it " |
903 | 292k | "also has multiple branches so users might need PhiInsts."); |
904 | | |
905 | 292k | for (auto *point : spillPoints) { |
906 | 292k | builder.setInsertionPoint(point); |
907 | 295k | for (auto store : toSpill) { |
908 | 295k | auto *storeInst = builder.createHBCSpillMovInst(store.first); |
909 | 295k | RA_.updateRegister(storeInst, store.second); |
910 | | |
911 | 295k | if (!replaceWithFirstSpill) |
912 | 2.63k | continue; |
913 | | // Replace all uses of the inst with the spilling inst |
914 | 292k | inst.replaceAllUsesWith(storeInst); |
915 | | // Except for the actual spill of course |
916 | 292k | storeInst->setOperand(&inst, 0); |
917 | | // Disable now that the job is done. |
918 | 292k | replaceWithFirstSpill = false; |
919 | 292k | } |
920 | 292k | } |
921 | 292k | } |
922 | 950k | } |
923 | 33.8k | } |
924 | 8 | return true; |
925 | 8 | } |
926 | | |
927 | 28.0k | bool LowerSwitchIntoJumpTables::runOnFunction(Function *F) { |
928 | 28.0k | bool changed = false; |
929 | 28.0k | llvh::SmallVector<SwitchInst *, 4> switches; |
930 | | // Collect all switch instructions. |
931 | 28.0k | for (BasicBlock &BB : *F) |
932 | 590k | for (auto &it : BB) { |
933 | 590k | if (auto *S = llvh::dyn_cast<SwitchInst>(&it)) |
934 | 0 | switches.push_back(S); |
935 | 590k | } |
936 | | |
937 | 28.0k | for (auto *S : switches) { |
938 | 0 | if (lowerIntoJumpTable(S)) |
939 | 0 | changed = true; |
940 | 0 | } |
941 | | |
942 | 28.0k | return changed; |
943 | 28.0k | } |
944 | | |
945 | 0 | bool LowerSwitchIntoJumpTables::lowerIntoJumpTable(SwitchInst *switchInst) { |
946 | | // if its a constant switch don't bother |
947 | 0 | if (llvh::isa<Literal>(switchInst->getInputValue())) { |
948 | 0 | return false; |
949 | 0 | } |
950 | 0 | IRBuilder builder(switchInst->getParent()->getParent()); |
951 | 0 | unsigned numCases = switchInst->getNumCasePair(); |
952 | 0 | uint32_t minValue = 0; |
953 | 0 | uint32_t maxValue = 0; |
954 | |
|
955 | 0 | llvh::SmallVector<LiteralNumber *, 8> values; |
956 | 0 | llvh::SmallVector<BasicBlock *, 8> blocks; |
957 | |
|
958 | 0 | for (unsigned i = 0; i != numCases; ++i) { |
959 | 0 | auto casePair = switchInst->getCasePair(i); |
960 | 0 | auto *lit = casePair.first; |
961 | 0 | auto *num = llvh::dyn_cast<LiteralNumber>(lit); |
962 | 0 | if (!num) |
963 | 0 | return false; |
964 | | |
965 | | // Check whether it is representable as uint32. |
966 | 0 | if (auto ival = num->isIntTypeRepresentible<uint32_t>()) { |
967 | 0 | values.push_back(num); |
968 | 0 | blocks.push_back(casePair.second); |
969 | |
|
970 | 0 | if (i == 0) { |
971 | 0 | minValue = maxValue = ival.getValue(); |
972 | 0 | } else { |
973 | 0 | minValue = std::min(minValue, ival.getValue()); |
974 | 0 | maxValue = std::max(maxValue, ival.getValue()); |
975 | 0 | } |
976 | 0 | } else { |
977 | 0 | return false; |
978 | 0 | } |
979 | 0 | } |
980 | | |
981 | 0 | assert(minValue <= maxValue && "Minimum cannot exceed maximum"); |
982 | 0 | uint32_t range = maxValue - minValue; |
983 | | // We can't generate a table for a zero-sized range. |
984 | 0 | if (range == 0) |
985 | 0 | return false; |
986 | | |
987 | | // The number of cases is range + 1, which must fit in a uint32. |
988 | 0 | if (range == std::numeric_limits<uint32_t>::max()) |
989 | 0 | return false; |
990 | | |
991 | | // Check the "denseness" of the cases. |
992 | | // Don't convert small switches. |
993 | 0 | if (range / numCases > 5 || numCases < 10) |
994 | 0 | return false; |
995 | | |
996 | 0 | builder.setInsertionPoint(switchInst); |
997 | 0 | auto *switchImmInst = builder.createSwitchImmInst( |
998 | 0 | switchInst->getInputValue(), |
999 | 0 | switchInst->getDefaultDestination(), |
1000 | 0 | builder.getLiteralNumber(minValue), |
1001 | 0 | builder.getLiteralNumber(range + 1), |
1002 | 0 | values, |
1003 | 0 | blocks); |
1004 | |
|
1005 | 0 | switchInst->replaceAllUsesWith(switchImmInst); |
1006 | 0 | switchInst->eraseFromParent(); |
1007 | 0 | return true; |
1008 | 0 | } |
1009 | | |
1010 | | } // namespace hbc |
1011 | | } // namespace hermes |
1012 | | |
1013 | | #undef DEBUG_TYPE |