Skip to content

Commit bf340ec

Browse files
committed
[JSC] Integrate Int52 result into MultiGetByVal
https://bugs.webkit.org/show_bug.cgi?id=291958 rdar://149857634 Reviewed by Yijia Huang. When MultiGetByVal is emitted with DoubleArray | Uint32Array for example, the result is considered as JSValue. Then we need to box and unbox values while we already know that the result is used under the context of Int52. In this patch, we attempt to put Int52Result flag to MultiGetByVal. Then it will generate Int52 result directly when it is specified. To find the beneficial case, we extend ValueRep reduction phase to detect MultiGetByVal nodes which is only used under Int52 context. Then we attach Int52Result flag to that node and FTL will generate a code which directly generates Int52 results. * Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h: (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): * Source/JavaScriptCore/dfg/DFGValueRepReductionPhase.cpp: (JSC::DFG::ValueRepReductionPhase::run): (JSC::DFG::ValueRepReductionPhase::convertValueRepsToUnboxed): (JSC::DFG::ValueRepReductionPhase::convertValueRepsToDouble): Deleted. * Source/JavaScriptCore/dfg/DFGValueRepReductionPhase.h: * Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp: (JSC::FTL::DFG::LowerDFGToB3::compileInt52Rep): (JSC::FTL::DFG::LowerDFGToB3::compileMultiGetByVal): (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq): Canonical link: https://commits.webkit.org/294067@main
1 parent ab6288d commit bf340ec

File tree

4 files changed

+240
-55
lines changed

4 files changed

+240
-55
lines changed

Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2982,7 +2982,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
29822982
if (node->hasDoubleResult()) {
29832983
// We say SpecFullDouble since it will involve Float16 / Float32 / Float64 TypedArrays.
29842984
setNonCellTypeForNode(node, SpecFullDouble);
2985-
} else
2985+
} else if (node->hasInt52Result())
2986+
setNonCellTypeForNode(node, SpecInt52Any);
2987+
else
29862988
makeHeapTopForNode(node);
29872989
break;
29882990
}

Source/JavaScriptCore/dfg/DFGValueRepReductionPhase.cpp

Lines changed: 174 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -49,23 +49,30 @@ class ValueRepReductionPhase : public Phase {
4949
bool run()
5050
{
5151
ASSERT(m_graph.m_form == SSA);
52-
return convertValueRepsToDouble();
52+
bool changed = false;
53+
changed |= convertValueRepsToUnboxed<DoubleRepUse>();
54+
changed |= convertValueRepsToUnboxed<Int52RepUse>();
55+
return changed;
5356
}
5457

5558
private:
56-
bool convertValueRepsToDouble()
59+
template<UseKind useKind>
60+
bool convertValueRepsToUnboxed()
5761
{
5862
UncheckedKeyHashSet<Node*> candidates;
5963
for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
6064
for (Node* node : *block) {
6165
switch (node->op()) {
6266
case ValueRep: {
63-
if (node->child1().useKind() == DoubleRepUse)
67+
if (node->child1().useKind() == useKind)
6468
candidates.add(node);
6569
break;
6670
}
6771

6872
case DoubleRep: {
73+
if constexpr (useKind != DoubleRepUse)
74+
break;
75+
6976
if (!isARM64() && !isX86_64_AVX())
7077
break;
7178

@@ -88,6 +95,50 @@ class ValueRepReductionPhase : public Phase {
8895
break;
8996
}
9097

98+
case Int52Rep: {
99+
if constexpr (useKind != Int52RepUse)
100+
break;
101+
102+
Edge& child1 = node->child1();
103+
switch (child1->op()) {
104+
case MultiGetByVal: {
105+
if (child1->arrayMode().isOutOfBounds())
106+
break;
107+
108+
if (child1->arrayMode().isInBoundsSaneChain())
109+
break;
110+
111+
if (m_graph.hasExitSite(child1->origin.semantic, Int52Overflow))
112+
break;
113+
114+
constexpr ArrayModes supportedArrays = 0
115+
| asArrayModesIgnoringTypedArrays(ArrayWithInt32)
116+
| asArrayModesIgnoringTypedArrays(ArrayWithDouble)
117+
| Int8ArrayMode
118+
| Int16ArrayMode
119+
| Int32ArrayMode
120+
| Uint8ArrayMode
121+
| Uint8ClampedArrayMode
122+
| Uint16ArrayMode
123+
| Uint32ArrayMode
124+
| 0;
125+
126+
if (!(child1->arrayModes() & supportedArrays))
127+
break;
128+
129+
if (child1.useKind() == AnyIntUse) {
130+
if (child1->origin.exitOK)
131+
candidates.add(child1.node());
132+
break;
133+
}
134+
break;
135+
}
136+
default:
137+
break;
138+
}
139+
break;
140+
}
141+
91142
default:
92143
break;
93144
}
@@ -117,8 +168,21 @@ class ValueRepReductionPhase : public Phase {
117168
case GetGlobalLexicalVariable:
118169
case MultiGetByOffset:
119170
case GetByOffset: {
171+
if constexpr (useKind != DoubleRepUse)
172+
break;
173+
120174
if (!isARM64() && !isX86_64_AVX())
121175
break;
176+
177+
if (candidates.contains(node))
178+
usersOf.add(node, Vector<Node*>());
179+
break;
180+
}
181+
182+
case MultiGetByVal: {
183+
if constexpr (useKind != Int52RepUse)
184+
break;
185+
122186
if (candidates.contains(node))
123187
usersOf.add(node, Vector<Node*>());
124188
break;
@@ -155,7 +219,7 @@ class ValueRepReductionPhase : public Phase {
155219
// makes Phi-1 a candidate too.
156220
do {
157221
UncheckedKeyHashSet<Node*> eligiblePhis;
158-
for (Node* candidate : candidates) {
222+
for (auto* candidate : candidates) {
159223
if (candidate->op() == Phi) {
160224
phiChildren.forAllIncomingValues(candidate, [&] (Node* incoming) {
161225
if (incoming->op() == Phi)
@@ -193,7 +257,7 @@ class ValueRepReductionPhase : public Phase {
193257
// - A JSConstant(some number "x") => DoubleConstant("x")
194258
// - ValueRep(DoubleRepUse:@x) => @x
195259
// - A Phi so long as that Phi is not escaped.
196-
for (Node* candidate : candidates) {
260+
for (auto* candidate : candidates) {
197261
bool ok = true;
198262

199263
auto dumpEscape = [&](const char* description, Node* node) {
@@ -213,9 +277,17 @@ class ValueRepReductionPhase : public Phase {
213277
phiChildren.forAllIncomingValues(candidate, [&] (Node* node) {
214278
switch (node->op()) {
215279
case JSConstant: {
216-
if (!node->asJSValue().isNumber()) {
217-
ok = false;
218-
dumpEscape("Phi Incoming JSConstant not a number: ", node);
280+
if constexpr (useKind == DoubleRepUse) {
281+
if (!node->asJSValue().isNumber()) {
282+
ok = false;
283+
dumpEscape("Phi Incoming JSConstant not a number: ", node);
284+
}
285+
}
286+
if constexpr (useKind == Int52RepUse) {
287+
if (!node->asJSValue().isAnyInt()) {
288+
ok = false;
289+
dumpEscape("Phi Incoming JSConstant not a anyint: ", node);
290+
}
219291
}
220292
break;
221293
}
@@ -231,17 +303,39 @@ class ValueRepReductionPhase : public Phase {
231303
break;
232304
}
233305

306+
if constexpr (useKind != DoubleRepUse) {
307+
ok = false;
308+
dumpEscape("Phi Incoming Get is escaped: ", node);
309+
break;
310+
}
311+
312+
if (isEscaped(node)) {
313+
ok = false;
314+
dumpEscape("Phi Incoming Get is escaped: ", node);
315+
break;
316+
}
317+
break;
318+
}
319+
320+
case MultiGetByVal: {
321+
if constexpr (useKind != Int52RepUse) {
322+
ok = false;
323+
dumpEscape("Phi Incoming Get is escaped: ", node);
324+
break;
325+
}
326+
234327
if (isEscaped(node)) {
235328
ok = false;
236329
dumpEscape("Phi Incoming Get is escaped: ", node);
330+
break;
237331
}
238332
break;
239333
}
240334

241335
case ValueRep: {
242-
if (node->child1().useKind() != DoubleRepUse) {
336+
if (node->child1().useKind() != useKind) {
243337
ok = false;
244-
dumpEscape("Phi Incoming ValueRep not DoubleRepUse: ", node);
338+
dumpEscape("Phi Incoming ValueRep not DoubleRepUse / Int52RepUse: ", node);
245339
}
246340
break;
247341
}
@@ -271,6 +365,11 @@ class ValueRepReductionPhase : public Phase {
271365
for (Node* user : getUsersOf(candidate)) {
272366
switch (user->op()) {
273367
case DoubleRep: {
368+
if constexpr (useKind != DoubleRepUse) {
369+
ok = false;
370+
dumpEscape("DoubleRep escape: ", user);
371+
break;
372+
}
274373
switch (user->child1().useKind()) {
275374
case RealNumberUse:
276375
case NumberUse:
@@ -284,6 +383,24 @@ class ValueRepReductionPhase : public Phase {
284383
break;
285384
}
286385

386+
case Int52Rep: {
387+
if constexpr (useKind != Int52RepUse) {
388+
ok = false;
389+
dumpEscape("Int52RepUse escape: ", user);
390+
break;
391+
}
392+
switch (user->child1().useKind()) {
393+
case AnyIntUse:
394+
break;
395+
default: {
396+
ok = false;
397+
dumpEscape("Int52Rep escape: ", user);
398+
break;
399+
}
400+
}
401+
break;
402+
}
403+
287404
case PutHint:
288405
case MovHint:
289406
break;
@@ -293,6 +410,12 @@ class ValueRepReductionPhase : public Phase {
293410
case MultiPutByOffset:
294411
case PutClosureVar:
295412
case PutGlobalVariable:
413+
if constexpr (useKind != DoubleRepUse) {
414+
ok = false;
415+
dumpEscape("Normal escape: ", user);
416+
break;
417+
}
418+
296419
if (!isARM64() && !isX86_64_AVX()) {
297420
dumpEscape("Normal escape: ", user);
298421
ok = false;
@@ -336,8 +459,8 @@ class ValueRepReductionPhase : public Phase {
336459
NodeOrigin originForConstant = m_graph.block(0)->at(0)->origin;
337460
UncheckedKeyHashSet<Node*> doubleRepRealCheckLocations;
338461

339-
for (Node* candidate : candidates) {
340-
dataLogLnIf(verbose, "Optimized: ", candidate);
462+
for (auto* candidate : candidates) {
463+
dataLogLnIf(verbose, "Optimized: ", candidate, " with ", useKind);
341464

342465
Node* resultNode = nullptr;
343466
switch (candidate->op()) {
@@ -349,14 +472,22 @@ class ValueRepReductionPhase : public Phase {
349472
Node* newChild = nullptr;
350473
switch (incomingValue->op()) {
351474
case JSConstant: {
352-
double number = incomingValue->asJSValue().asNumber();
353-
newChild = m_insertionSet.insertConstant(0, originForConstant, jsDoubleNumber(number), DoubleConstant);
475+
if constexpr (useKind == DoubleRepUse) {
476+
double number = incomingValue->asJSValue().asNumber();
477+
newChild = m_insertionSet.insertConstant(0, originForConstant, jsDoubleNumber(number), DoubleConstant);
478+
break;
479+
}
480+
if constexpr (useKind == Int52RepUse) {
481+
int64_t number = incomingValue->asJSValue().asAnyInt();
482+
newChild = m_insertionSet.insertConstant(0, originForConstant, jsNumber(number), Int52Constant);
483+
break;
484+
}
354485
break;
355486
}
356487
case ValueRep: {
357488
// We don't care about the incoming value being an impure NaN because users of
358489
// this Phi are either OSR exit, DoubleRep(RealNumberUse:@phi), or PurifyNaN(@phi).
359-
ASSERT(incomingValue->child1().useKind() == DoubleRepUse);
490+
ASSERT(incomingValue->child1().useKind() == useKind);
360491
newChild = incomingValue->child1().node();
361492
break;
362493
}
@@ -376,15 +507,23 @@ class ValueRepReductionPhase : public Phase {
376507
break;
377508
}
378509

510+
case MultiGetByVal: {
511+
newChild = incomingValue;
512+
break;
513+
}
514+
379515
default:
380516
RELEASE_ASSERT_NOT_REACHED();
381517
break;
382518
}
383519

384-
upsilon->child1() = Edge(newChild, DoubleRepUse);
520+
upsilon->child1() = Edge(newChild, useKind);
385521
}
386522

387-
candidate->setResult(NodeResultDouble);
523+
if constexpr (useKind == DoubleRepUse)
524+
candidate->setResult(NodeResultDouble);
525+
if constexpr (useKind == Int52RepUse)
526+
candidate->setResult(NodeResultInt52);
388527
break;
389528
}
390529

@@ -405,6 +544,12 @@ class ValueRepReductionPhase : public Phase {
405544
break;
406545
}
407546

547+
case MultiGetByVal: {
548+
candidate->setResult(NodeResultInt52);
549+
resultNode = candidate;
550+
break;
551+
}
552+
408553
default:
409554
RELEASE_ASSERT_NOT_REACHED();
410555
break;
@@ -420,37 +565,42 @@ class ValueRepReductionPhase : public Phase {
420565
user->convertToPurifyNaN(resultNode);
421566
break;
422567
}
423-
568+
569+
case Int52Rep: {
570+
user->convertToIdentityOn(resultNode);
571+
break;
572+
}
573+
424574
case PutHint:
425-
user->child2() = Edge(resultNode, DoubleRepUse);
575+
user->child2() = Edge(resultNode, useKind);
426576
break;
427577

428578
case MovHint:
429-
user->child1() = Edge(resultNode, DoubleRepUse);
579+
user->child1() = Edge(resultNode, useKind);
430580
break;
431581

432582
case PutByOffset:
433583
if (!isARM64() && !isX86_64_AVX())
434584
RELEASE_ASSERT_NOT_REACHED();
435-
user->child3() = Edge(resultNode, DoubleRepUse);
585+
user->child3() = Edge(resultNode, useKind);
436586
break;
437587

438588
case MultiPutByOffset:
439589
if (!isARM64() && !isX86_64_AVX())
440590
RELEASE_ASSERT_NOT_REACHED();
441-
user->child2() = Edge(resultNode, DoubleRepUse);
591+
user->child2() = Edge(resultNode, useKind);
442592
break;
443593

444594
case PutClosureVar:
445595
if (!isARM64() && !isX86_64_AVX())
446596
RELEASE_ASSERT_NOT_REACHED();
447-
user->child2() = Edge(resultNode, DoubleRepUse);
597+
user->child2() = Edge(resultNode, useKind);
448598
break;
449599

450600
case PutGlobalVariable:
451601
if (!isARM64() && !isX86_64_AVX())
452602
RELEASE_ASSERT_NOT_REACHED();
453-
user->child2() = Edge(resultNode, DoubleRepUse);
603+
user->child2() = Edge(resultNode, useKind);
454604
break;
455605

456606
case Upsilon: {

Source/JavaScriptCore/dfg/DFGValueRepReductionPhase.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ namespace JSC { namespace DFG {
3131

3232
class Graph;
3333

34-
// Tries to eliminate DoubleRep(ValueRep) roundtrips.
34+
// Tries to eliminate DoubleRep(ValueRep) / Int52Rep(ValueRep) roundtrips.
3535

3636
bool performValueRepReduction(Graph&);
3737

0 commit comments

Comments
 (0)