1 | /* |
2 | * Copyright (C) 2015-2019 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "B3LowerMacros.h" |
28 | |
29 | #if ENABLE(B3_JIT) |
30 | |
31 | #include "AllowMacroScratchRegisterUsage.h" |
32 | #include "B3AtomicValue.h" |
33 | #include "B3BasicBlockInlines.h" |
34 | #include "B3BlockInsertionSet.h" |
35 | #include "B3CCallValue.h" |
36 | #include "B3CaseCollectionInlines.h" |
37 | #include "B3ConstPtrValue.h" |
38 | #include "B3FenceValue.h" |
39 | #include "B3InsertionSetInlines.h" |
40 | #include "B3MemoryValueInlines.h" |
41 | #include "B3PatchpointValue.h" |
42 | #include "B3PhaseScope.h" |
43 | #include "B3ProcedureInlines.h" |
44 | #include "B3StackmapGenerationParams.h" |
45 | #include "B3SwitchValue.h" |
46 | #include "B3UpsilonValue.h" |
47 | #include "B3UseCounts.h" |
48 | #include "B3ValueInlines.h" |
49 | #include "CCallHelpers.h" |
50 | #include "LinkBuffer.h" |
51 | #include <cmath> |
52 | #include <wtf/BitVector.h> |
53 | |
54 | namespace JSC { namespace B3 { |
55 | |
56 | namespace { |
57 | |
58 | class LowerMacros { |
59 | public: |
60 | LowerMacros(Procedure& proc) |
61 | : m_proc(proc) |
62 | , m_blockInsertionSet(proc) |
63 | , m_insertionSet(proc) |
64 | , m_useCounts(proc) |
65 | { |
66 | } |
67 | |
68 | bool run() |
69 | { |
70 | RELEASE_ASSERT(!m_proc.hasQuirks()); |
71 | |
72 | for (BasicBlock* block : m_proc) { |
73 | m_block = block; |
74 | processCurrentBlock(); |
75 | } |
76 | m_changed |= m_blockInsertionSet.execute(); |
77 | if (m_changed) { |
78 | m_proc.resetReachability(); |
79 | m_proc.invalidateCFG(); |
80 | } |
81 | |
82 | // This indicates that we've |
83 | m_proc.setHasQuirks(true); |
84 | |
85 | return m_changed; |
86 | } |
87 | |
88 | private: |
89 | void processCurrentBlock() |
90 | { |
91 | for (m_index = 0; m_index < m_block->size(); ++m_index) { |
92 | m_value = m_block->at(m_index); |
93 | m_origin = m_value->origin(); |
94 | switch (m_value->opcode()) { |
95 | case Mod: { |
96 | if (m_value->isChill()) { |
97 | if (isARM64()) { |
98 | BasicBlock* before = m_blockInsertionSet.splitForward(m_block, m_index, &m_insertionSet); |
99 | BasicBlock* zeroDenCase = m_blockInsertionSet.insertBefore(m_block); |
100 | BasicBlock* normalModCase = m_blockInsertionSet.insertBefore(m_block); |
101 | |
102 | before->replaceLastWithNew<Value>(m_proc, Branch, m_origin, m_value->child(1)); |
103 | before->setSuccessors( |
104 | FrequentedBlock(normalModCase, FrequencyClass::Normal), |
105 | FrequentedBlock(zeroDenCase, FrequencyClass::Rare)); |
106 | |
107 | Value* divResult = normalModCase->appendNew<Value>(m_proc, chill(Div), m_origin, m_value->child(0), m_value->child(1)); |
108 | Value* multipliedBack = normalModCase->appendNew<Value>(m_proc, Mul, m_origin, divResult, m_value->child(1)); |
109 | Value* result = normalModCase->appendNew<Value>(m_proc, Sub, m_origin, m_value->child(0), multipliedBack); |
110 | UpsilonValue* normalResult = normalModCase->appendNew<UpsilonValue>(m_proc, m_origin, result); |
111 | normalModCase->appendNew<Value>(m_proc, Jump, m_origin); |
112 | normalModCase->setSuccessors(FrequentedBlock(m_block)); |
113 | |
114 | UpsilonValue* zeroResult = zeroDenCase->appendNew<UpsilonValue>( |
115 | m_proc, m_origin, |
116 | zeroDenCase->appendIntConstant(m_proc, m_value, 0)); |
117 | zeroDenCase->appendNew<Value>(m_proc, Jump, m_origin); |
118 | zeroDenCase->setSuccessors(FrequentedBlock(m_block)); |
119 | |
120 | Value* phi = m_insertionSet.insert<Value>(m_index, Phi, m_value->type(), m_origin); |
121 | normalResult->setPhi(phi); |
122 | zeroResult->setPhi(phi); |
123 | m_value->replaceWithIdentity(phi); |
124 | before->updatePredecessorsAfter(); |
125 | m_changed = true; |
126 | } else |
127 | makeDivisionChill(Mod); |
128 | break; |
129 | } |
130 | |
131 | auto* fmodDouble = tagCFunctionPtr<double (*)(double, double)>(fmod, B3CCallPtrTag); |
132 | if (m_value->type() == Double) { |
133 | Value* functionAddress = m_insertionSet.insert<ConstPtrValue>(m_index, m_origin, fmodDouble); |
134 | Value* result = m_insertionSet.insert<CCallValue>(m_index, Double, m_origin, |
135 | Effects::none(), |
136 | functionAddress, |
137 | m_value->child(0), |
138 | m_value->child(1)); |
139 | m_value->replaceWithIdentity(result); |
140 | m_changed = true; |
141 | } else if (m_value->type() == Float) { |
142 | Value* numeratorAsDouble = m_insertionSet.insert<Value>(m_index, FloatToDouble, m_origin, m_value->child(0)); |
143 | Value* denominatorAsDouble = m_insertionSet.insert<Value>(m_index, FloatToDouble, m_origin, m_value->child(1)); |
144 | Value* functionAddress = m_insertionSet.insert<ConstPtrValue>(m_index, m_origin, fmodDouble); |
145 | Value* doubleMod = m_insertionSet.insert<CCallValue>(m_index, Double, m_origin, |
146 | Effects::none(), |
147 | functionAddress, |
148 | numeratorAsDouble, |
149 | denominatorAsDouble); |
150 | Value* result = m_insertionSet.insert<Value>(m_index, DoubleToFloat, m_origin, doubleMod); |
151 | m_value->replaceWithIdentity(result); |
152 | m_changed = true; |
153 | } else if (isARM64()) { |
154 | Value* divResult = m_insertionSet.insert<Value>(m_index, chill(Div), m_origin, m_value->child(0), m_value->child(1)); |
155 | Value* multipliedBack = m_insertionSet.insert<Value>(m_index, Mul, m_origin, divResult, m_value->child(1)); |
156 | Value* result = m_insertionSet.insert<Value>(m_index, Sub, m_origin, m_value->child(0), multipliedBack); |
157 | m_value->replaceWithIdentity(result); |
158 | m_changed = true; |
159 | } |
160 | break; |
161 | } |
162 | |
163 | case UMod: { |
164 | if (isARM64()) { |
165 | Value* divResult = m_insertionSet.insert<Value>(m_index, UDiv, m_origin, m_value->child(0), m_value->child(1)); |
166 | Value* multipliedBack = m_insertionSet.insert<Value>(m_index, Mul, m_origin, divResult, m_value->child(1)); |
167 | Value* result = m_insertionSet.insert<Value>(m_index, Sub, m_origin, m_value->child(0), multipliedBack); |
168 | m_value->replaceWithIdentity(result); |
169 | m_changed = true; |
170 | } |
171 | break; |
172 | } |
173 | |
174 | case Div: { |
175 | if (m_value->isChill()) |
176 | makeDivisionChill(Div); |
177 | break; |
178 | } |
179 | |
180 | case Switch: { |
181 | SwitchValue* switchValue = m_value->as<SwitchValue>(); |
182 | Vector<SwitchCase> cases; |
183 | for (SwitchCase switchCase : switchValue->cases(m_block)) |
184 | cases.append(switchCase); |
185 | std::sort( |
186 | cases.begin(), cases.end(), |
187 | [] (const SwitchCase& left, const SwitchCase& right) { |
188 | return left.caseValue() < right.caseValue(); |
189 | }); |
190 | FrequentedBlock fallThrough = m_block->fallThrough(); |
191 | m_block->values().removeLast(); |
192 | recursivelyBuildSwitch(cases, fallThrough, 0, false, cases.size(), m_block); |
193 | m_proc.deleteValue(switchValue); |
194 | m_block->updatePredecessorsAfter(); |
195 | m_changed = true; |
196 | break; |
197 | } |
198 | |
199 | case Depend: { |
200 | if (isX86()) { |
201 | // Create a load-load fence. This codegens to nothing on X86. We use it to tell the |
202 | // compiler not to block load motion. |
203 | FenceValue* fence = m_insertionSet.insert<FenceValue>(m_index, m_origin); |
204 | fence->read = HeapRange(); |
205 | fence->write = HeapRange::top(); |
206 | |
207 | // Kill the Depend, which should unlock a bunch of code simplification. |
208 | m_value->replaceWithBottom(m_insertionSet, m_index); |
209 | |
210 | m_changed = true; |
211 | } |
212 | break; |
213 | } |
214 | |
215 | case AtomicWeakCAS: |
216 | case AtomicStrongCAS: { |
217 | AtomicValue* atomic = m_value->as<AtomicValue>(); |
218 | Width width = atomic->accessWidth(); |
219 | |
220 | if (isCanonicalWidth(width)) |
221 | break; |
222 | |
223 | Value* expectedValue = atomic->child(0); |
224 | |
225 | if (!isX86()) { |
226 | // On ARM, the load part of the CAS does a load with zero extension. Therefore, we need |
227 | // to zero-extend the input. |
228 | Value* maskedExpectedValue = m_insertionSet.insert<Value>( |
229 | m_index, BitAnd, m_origin, expectedValue, |
230 | m_insertionSet.insertIntConstant(m_index, expectedValue, mask(width))); |
231 | |
232 | atomic->child(0) = maskedExpectedValue; |
233 | m_changed = true; |
234 | } |
235 | |
236 | if (atomic->opcode() == AtomicStrongCAS) { |
237 | Value* newValue = m_insertionSet.insert<Value>( |
238 | m_index, signExtendOpcode(width), m_origin, |
239 | m_insertionSet.insertClone(m_index, atomic)); |
240 | |
241 | atomic->replaceWithIdentity(newValue); |
242 | m_changed = true; |
243 | } |
244 | |
245 | break; |
246 | } |
247 | |
248 | case AtomicXchgAdd: |
249 | case AtomicXchgAnd: |
250 | case AtomicXchgOr: |
251 | case AtomicXchgSub: |
252 | case AtomicXchgXor: |
253 | case AtomicXchg: { |
254 | // On X86, these may actually return garbage in the high bits. On ARM64, these sorta |
255 | // zero-extend their high bits, except that the high bits might get polluted by high |
256 | // bits in the operand. So, either way, we need to throw a sign-extend on these |
257 | // things. |
258 | |
259 | if (isX86()) { |
260 | if (m_value->opcode() == AtomicXchgSub && m_useCounts.numUses(m_value)) { |
261 | // On x86, xchgadd is better than xchgsub if it has any users. |
262 | m_value->setOpcodeUnsafely(AtomicXchgAdd); |
263 | m_value->child(0) = m_insertionSet.insert<Value>( |
264 | m_index, Neg, m_origin, m_value->child(0)); |
265 | } |
266 | |
267 | bool exempt = false; |
268 | switch (m_value->opcode()) { |
269 | case AtomicXchgAnd: |
270 | case AtomicXchgOr: |
271 | case AtomicXchgSub: |
272 | case AtomicXchgXor: |
273 | exempt = true; |
274 | break; |
275 | default: |
276 | break; |
277 | } |
278 | if (exempt) |
279 | break; |
280 | } |
281 | |
282 | AtomicValue* atomic = m_value->as<AtomicValue>(); |
283 | Width width = atomic->accessWidth(); |
284 | |
285 | if (isCanonicalWidth(width)) |
286 | break; |
287 | |
288 | Value* newValue = m_insertionSet.insert<Value>( |
289 | m_index, signExtendOpcode(width), m_origin, |
290 | m_insertionSet.insertClone(m_index, atomic)); |
291 | |
292 | atomic->replaceWithIdentity(newValue); |
293 | m_changed = true; |
294 | break; |
295 | } |
296 | |
297 | case Load8Z: |
298 | case Load16Z: { |
299 | if (isX86()) |
300 | break; |
301 | |
302 | MemoryValue* memory = m_value->as<MemoryValue>(); |
303 | if (!memory->hasFence()) |
304 | break; |
305 | |
306 | // Sub-width load-acq on ARM64 always sign extends. |
307 | Value* newLoad = m_insertionSet.insertClone(m_index, memory); |
308 | newLoad->setOpcodeUnsafely(memory->opcode() == Load8Z ? Load8S : Load16S); |
309 | |
310 | Value* newValue = m_insertionSet.insert<Value>( |
311 | m_index, BitAnd, m_origin, newLoad, |
312 | m_insertionSet.insertIntConstant( |
313 | m_index, m_origin, Int32, mask(memory->accessWidth()))); |
314 | |
315 | m_value->replaceWithIdentity(newValue); |
316 | m_changed = true; |
317 | break; |
318 | } |
319 | |
320 | default: |
321 | break; |
322 | } |
323 | } |
324 | m_insertionSet.execute(m_block); |
325 | } |
326 | |
327 | void makeDivisionChill(Opcode nonChillOpcode) |
328 | { |
329 | ASSERT(nonChillOpcode == Div || nonChillOpcode == Mod); |
330 | |
331 | // ARM supports this instruction natively. |
332 | if (isARM64()) |
333 | return; |
334 | |
335 | // We implement "res = Div<Chill>/Mod<Chill>(num, den)" as follows: |
336 | // |
337 | // if (den + 1 <=_unsigned 1) { |
338 | // if (!den) { |
339 | // res = 0; |
340 | // goto done; |
341 | // } |
342 | // if (num == -2147483648) { |
343 | // res = isDiv ? num : 0; |
344 | // goto done; |
345 | // } |
346 | // } |
347 | // res = num (/ or %) dev; |
348 | // done: |
349 | m_changed = true; |
350 | |
351 | Value* num = m_value->child(0); |
352 | Value* den = m_value->child(1); |
353 | |
354 | Value* one = m_insertionSet.insertIntConstant(m_index, m_value, 1); |
355 | Value* isDenOK = m_insertionSet.insert<Value>( |
356 | m_index, Above, m_origin, |
357 | m_insertionSet.insert<Value>(m_index, Add, m_origin, den, one), |
358 | one); |
359 | |
360 | BasicBlock* before = m_blockInsertionSet.splitForward(m_block, m_index, &m_insertionSet); |
361 | |
362 | BasicBlock* normalDivCase = m_blockInsertionSet.insertBefore(m_block); |
363 | BasicBlock* shadyDenCase = m_blockInsertionSet.insertBefore(m_block); |
364 | BasicBlock* zeroDenCase = m_blockInsertionSet.insertBefore(m_block); |
365 | BasicBlock* neg1DenCase = m_blockInsertionSet.insertBefore(m_block); |
366 | BasicBlock* intMinCase = m_blockInsertionSet.insertBefore(m_block); |
367 | |
368 | before->replaceLastWithNew<Value>(m_proc, Branch, m_origin, isDenOK); |
369 | before->setSuccessors( |
370 | FrequentedBlock(normalDivCase, FrequencyClass::Normal), |
371 | FrequentedBlock(shadyDenCase, FrequencyClass::Rare)); |
372 | |
373 | UpsilonValue* normalResult = normalDivCase->appendNew<UpsilonValue>( |
374 | m_proc, m_origin, |
375 | normalDivCase->appendNew<Value>(m_proc, nonChillOpcode, m_origin, num, den)); |
376 | normalDivCase->appendNew<Value>(m_proc, Jump, m_origin); |
377 | normalDivCase->setSuccessors(FrequentedBlock(m_block)); |
378 | |
379 | shadyDenCase->appendNew<Value>(m_proc, Branch, m_origin, den); |
380 | shadyDenCase->setSuccessors( |
381 | FrequentedBlock(neg1DenCase, FrequencyClass::Normal), |
382 | FrequentedBlock(zeroDenCase, FrequencyClass::Rare)); |
383 | |
384 | UpsilonValue* zeroResult = zeroDenCase->appendNew<UpsilonValue>( |
385 | m_proc, m_origin, |
386 | zeroDenCase->appendIntConstant(m_proc, m_value, 0)); |
387 | zeroDenCase->appendNew<Value>(m_proc, Jump, m_origin); |
388 | zeroDenCase->setSuccessors(FrequentedBlock(m_block)); |
389 | |
390 | int64_t badNumeratorConst = 0; |
391 | switch (m_value->type()) { |
392 | case Int32: |
393 | badNumeratorConst = std::numeric_limits<int32_t>::min(); |
394 | break; |
395 | case Int64: |
396 | badNumeratorConst = std::numeric_limits<int64_t>::min(); |
397 | break; |
398 | default: |
399 | ASSERT_NOT_REACHED(); |
400 | badNumeratorConst = 0; |
401 | } |
402 | |
403 | Value* badNumerator = |
404 | neg1DenCase->appendIntConstant(m_proc, m_value, badNumeratorConst); |
405 | |
406 | neg1DenCase->appendNew<Value>( |
407 | m_proc, Branch, m_origin, |
408 | neg1DenCase->appendNew<Value>( |
409 | m_proc, Equal, m_origin, num, badNumerator)); |
410 | neg1DenCase->setSuccessors( |
411 | FrequentedBlock(intMinCase, FrequencyClass::Rare), |
412 | FrequentedBlock(normalDivCase, FrequencyClass::Normal)); |
413 | |
414 | Value* intMinResult = nonChillOpcode == Div ? badNumerator : intMinCase->appendIntConstant(m_proc, m_value, 0); |
415 | UpsilonValue* intMinResultUpsilon = intMinCase->appendNew<UpsilonValue>( |
416 | m_proc, m_origin, intMinResult); |
417 | intMinCase->appendNew<Value>(m_proc, Jump, m_origin); |
418 | intMinCase->setSuccessors(FrequentedBlock(m_block)); |
419 | |
420 | Value* phi = m_insertionSet.insert<Value>( |
421 | m_index, Phi, m_value->type(), m_origin); |
422 | normalResult->setPhi(phi); |
423 | zeroResult->setPhi(phi); |
424 | intMinResultUpsilon->setPhi(phi); |
425 | |
426 | m_value->replaceWithIdentity(phi); |
427 | before->updatePredecessorsAfter(); |
428 | } |
429 | |
430 | void recursivelyBuildSwitch( |
431 | const Vector<SwitchCase>& cases, FrequentedBlock fallThrough, unsigned start, bool hardStart, |
432 | unsigned end, BasicBlock* before) |
433 | { |
434 | Value* child = m_value->child(0); |
435 | Type type = child->type(); |
436 | |
437 | // It's a good idea to use a table-based switch in some cases: the number of cases has to be |
438 | // large enough and they have to be dense enough. This could probably be improved a lot. For |
439 | // example, we could still use a jump table in cases where the inputs are sparse so long as we |
440 | // shift off the uninteresting bits. On the other hand, it's not clear that this would |
441 | // actually be any better than what we have done here and it's not clear that it would be |
442 | // better than a binary switch. |
443 | const unsigned minCasesForTable = 7; |
444 | const unsigned densityLimit = 4; |
445 | if (end - start >= minCasesForTable) { |
446 | int64_t firstValue = cases[start].caseValue(); |
447 | int64_t lastValue = cases[end - 1].caseValue(); |
448 | if ((lastValue - firstValue + 1) / (end - start) < densityLimit) { |
449 | BasicBlock* switchBlock = m_blockInsertionSet.insertAfter(m_block); |
450 | Value* index = before->appendNew<Value>( |
451 | m_proc, Sub, m_origin, child, |
452 | before->appendIntConstant(m_proc, m_origin, type, firstValue)); |
453 | before->appendNew<Value>( |
454 | m_proc, Branch, m_origin, |
455 | before->appendNew<Value>( |
456 | m_proc, Above, m_origin, index, |
457 | before->appendIntConstant(m_proc, m_origin, type, lastValue - firstValue))); |
458 | before->setSuccessors(fallThrough, FrequentedBlock(switchBlock)); |
459 | |
460 | size_t tableSize = lastValue - firstValue + 1; |
461 | |
462 | if (index->type() != pointerType() && index->type() == Int32) |
463 | index = switchBlock->appendNew<Value>(m_proc, ZExt32, m_origin, index); |
464 | |
465 | PatchpointValue* patchpoint = |
466 | switchBlock->appendNew<PatchpointValue>(m_proc, Void, m_origin); |
467 | |
468 | // Even though this loads from the jump table, the jump table is immutable. For the |
469 | // purpose of alias analysis, reading something immutable is like reading nothing. |
470 | patchpoint->effects = Effects(); |
471 | patchpoint->effects.terminal = true; |
472 | |
473 | patchpoint->appendSomeRegister(index); |
474 | patchpoint->numGPScratchRegisters = 2; |
475 | // Technically, we don't have to clobber macro registers on X86_64. This is probably |
476 | // OK though. |
477 | patchpoint->clobber(RegisterSet::macroScratchRegisters()); |
478 | |
479 | BitVector handledIndices; |
480 | for (unsigned i = start; i < end; ++i) { |
481 | FrequentedBlock block = cases[i].target(); |
482 | int64_t value = cases[i].caseValue(); |
483 | switchBlock->appendSuccessor(block); |
484 | size_t index = value - firstValue; |
485 | ASSERT(!handledIndices.get(index)); |
486 | handledIndices.set(index); |
487 | } |
488 | |
489 | bool hasUnhandledIndex = false; |
490 | for (unsigned i = 0; i < tableSize; ++i) { |
491 | if (!handledIndices.get(i)) { |
492 | hasUnhandledIndex = true; |
493 | break; |
494 | } |
495 | } |
496 | |
497 | if (hasUnhandledIndex) |
498 | switchBlock->appendSuccessor(fallThrough); |
499 | |
500 | patchpoint->setGenerator( |
501 | [=] (CCallHelpers& jit, const StackmapGenerationParams& params) { |
502 | AllowMacroScratchRegisterUsage allowScratch(jit); |
503 | |
504 | using JumpTableCodePtr = MacroAssemblerCodePtr<JSSwitchPtrTag>; |
505 | JumpTableCodePtr* jumpTable = static_cast<JumpTableCodePtr*>( |
506 | params.proc().addDataSection(sizeof(JumpTableCodePtr) * tableSize)); |
507 | |
508 | GPRReg index = params[0].gpr(); |
509 | GPRReg scratch = params.gpScratch(0); |
510 | |
511 | jit.move(CCallHelpers::TrustedImmPtr(jumpTable), scratch); |
512 | jit.load64(CCallHelpers::BaseIndex(scratch, index, CCallHelpers::timesPtr()), scratch); |
513 | jit.jump(scratch, JSSwitchPtrTag); |
514 | |
515 | // These labels are guaranteed to be populated before either late paths or |
516 | // link tasks run. |
517 | Vector<Box<CCallHelpers::Label>> labels = params.successorLabels(); |
518 | |
519 | jit.addLinkTask( |
520 | [=] (LinkBuffer& linkBuffer) { |
521 | if (hasUnhandledIndex) { |
522 | JumpTableCodePtr fallThrough = linkBuffer.locationOf<JSSwitchPtrTag>(*labels.last()); |
523 | for (unsigned i = tableSize; i--;) |
524 | jumpTable[i] = fallThrough; |
525 | } |
526 | |
527 | unsigned labelIndex = 0; |
528 | for (unsigned tableIndex : handledIndices) |
529 | jumpTable[tableIndex] = linkBuffer.locationOf<JSSwitchPtrTag>(*labels[labelIndex++]); |
530 | }); |
531 | }); |
532 | return; |
533 | } |
534 | } |
535 | |
536 | // See comments in jit/BinarySwitch.cpp for a justification of this algorithm. The only |
537 | // thing we do differently is that we don't use randomness. |
538 | |
539 | const unsigned leafThreshold = 3; |
540 | |
541 | unsigned size = end - start; |
542 | |
543 | if (size <= leafThreshold) { |
544 | bool allConsecutive = false; |
545 | |
546 | if ((hardStart || (start && cases[start - 1].caseValue() == cases[start].caseValue() - 1)) |
547 | && end < cases.size() |
548 | && cases[end - 1].caseValue() == cases[end].caseValue() - 1) { |
549 | allConsecutive = true; |
550 | for (unsigned i = 0; i < size - 1; ++i) { |
551 | if (cases[start + i].caseValue() + 1 != cases[start + i + 1].caseValue()) { |
552 | allConsecutive = false; |
553 | break; |
554 | } |
555 | } |
556 | } |
557 | |
558 | unsigned limit = allConsecutive ? size - 1 : size; |
559 | |
560 | for (unsigned i = 0; i < limit; ++i) { |
561 | BasicBlock* nextCheck = m_blockInsertionSet.insertAfter(m_block); |
562 | before->appendNew<Value>( |
563 | m_proc, Branch, m_origin, |
564 | before->appendNew<Value>( |
565 | m_proc, Equal, m_origin, child, |
566 | before->appendIntConstant( |
567 | m_proc, m_origin, type, |
568 | cases[start + i].caseValue()))); |
569 | before->setSuccessors(cases[start + i].target(), FrequentedBlock(nextCheck)); |
570 | |
571 | before = nextCheck; |
572 | } |
573 | |
574 | before->appendNew<Value>(m_proc, Jump, m_origin); |
575 | if (allConsecutive) |
576 | before->setSuccessors(cases[end - 1].target()); |
577 | else |
578 | before->setSuccessors(fallThrough); |
579 | return; |
580 | } |
581 | |
582 | unsigned medianIndex = (start + end) / 2; |
583 | |
584 | BasicBlock* left = m_blockInsertionSet.insertAfter(m_block); |
585 | BasicBlock* right = m_blockInsertionSet.insertAfter(m_block); |
586 | |
587 | before->appendNew<Value>( |
588 | m_proc, Branch, m_origin, |
589 | before->appendNew<Value>( |
590 | m_proc, LessThan, m_origin, child, |
591 | before->appendIntConstant( |
592 | m_proc, m_origin, type, |
593 | cases[medianIndex].caseValue()))); |
594 | before->setSuccessors(FrequentedBlock(left), FrequentedBlock(right)); |
595 | |
596 | recursivelyBuildSwitch(cases, fallThrough, start, hardStart, medianIndex, left); |
597 | recursivelyBuildSwitch(cases, fallThrough, medianIndex, true, end, right); |
598 | } |
599 | |
600 | Procedure& m_proc; |
601 | BlockInsertionSet m_blockInsertionSet; |
602 | InsertionSet m_insertionSet; |
603 | UseCounts m_useCounts; |
604 | BasicBlock* m_block; |
605 | unsigned m_index; |
606 | Value* m_value; |
607 | Origin m_origin; |
608 | bool m_changed { false }; |
609 | }; |
610 | |
611 | } // anonymous namespace |
612 | |
613 | bool lowerMacros(Procedure& proc) |
614 | { |
615 | PhaseScope phaseScope(proc, "B3::lowerMacros" ); |
616 | LowerMacros lowerMacros(proc); |
617 | return lowerMacros.run(); |
618 | } |
619 | |
620 | } } // namespace JSC::B3 |
621 | |
622 | #endif // ENABLE(B3_JIT) |
623 | |
624 | |