Skip to content

Commit fb09016

Browse files
committed
[JSC] Optimize PutByVal with for-in
https://bugs.webkit.org/show_bug.cgi?id=230801 Reviewed by Saam Barati. JSTests: * stress/for-in-sentinel.js: Added. (shouldBe): (test): Source/JavaScriptCore: We found that some of Speedometer2 subtests are heavily using for-in with PutByVal or the other DFG nodes. And we also found that we are using polluted non-good type for the property names from for-in: String | Other. The reason is that we are returning null when op_enumerator_next finishes instead of string. And this design forces DFG and FTL to return null from EnumeratorNextUpdatePropertyName at the end of iteration. This pollutes the type of property names as String | Other instead of String, and leading to suboptimal DFG nodes. In this patch, we add special sentinel string in vm.smallString.sentinelString(). We know that this string cell pointer will be never returned from EnumeratorNextUpdatePropertyName in the normal for-in iteration. This is easy since we are always allocating a JSString when creating JSPropertyNameEnumerator. So this string cell (not the content) is always different from pre-allocated vm.smallString.sentinelString(). So, we use this special string pointer as a sentinel instead of null so that we can avoid polluting return type of EnumeratorNextUpdatePropertyName. To check the sentinel in LLInt / Baseline, this patch adds jeq_ptr, which performs cell pointer comparison and do not check string content equality. We do not need to have an implementation in DFG since we already have CompareEqPtr for existing jneq_ptr bytecode. We also clean up DFG operation related to PutByVal. ---------------------------------------------------------------------------------------------------------------------------------- | subtest | ms | ms | b / a | pValue (significance using False Discovery Rate) | ---------------------------------------------------------------------------------------------------------------------------------- | Elm-TodoMVC |116.010000 |112.701667 |0.971482 | 0.000000 (significant) | | VueJS-TodoMVC |22.995000 |23.023333 |1.001232 | 0.907086 | | EmberJS-TodoMVC |125.498333 |125.525000 |1.000212 | 0.932546 | | BackboneJS-TodoMVC |45.700000 |45.975000 |1.006018 | 0.084799 | | Preact-TodoMVC |16.681667 |16.610000 |0.995704 | 0.722758 | | AngularJS-TodoMVC |123.753333 |123.740000 |0.999892 | 0.971431 | | Vanilla-ES2015-TodoMVC |61.255000 |61.380000 |1.002041 | 0.300654 | | Inferno-TodoMVC |58.646667 |58.948333 |1.005144 | 0.267611 | | Flight-TodoMVC |73.283333 |72.801667 |0.993427 | 0.207389 | | Angular2-TypeScript-TodoMVC |39.746667 |40.015000 |1.006751 | 0.449821 | | VanillaJS-TodoMVC |50.096667 |49.823333 |0.994544 | 0.162020 | | jQuery-TodoMVC |212.870000 |213.196667 |1.001535 | 0.371944 | | EmberJS-Debug-TodoMVC |331.878333 |332.710000 |1.002506 | 0.094499 | | React-TodoMVC |83.078333 |82.726667 |0.995767 | 0.076143 | | React-Redux-TodoMVC |136.018333 |133.935000 |0.984683 | 0.000000 (significant) | | Vanilla-ES2015-Babel-Webpack-TodoMVC |59.743333 |59.643333 |0.998326 | 0.393671 | ---------------------------------------------------------------------------------------------------------------------------------- a mean = 271.75873 b mean = 272.45804 pValue = 0.0263030803 (Bigger means are better.) 1.003 times better Results ARE significant * builtins/BuiltinNames.h: * bytecode/BytecodeList.rb: * bytecode/BytecodeUseDef.cpp: (JSC::computeUsesForBytecodeIndexImpl): (JSC::computeDefsForBytecodeIndexImpl): * bytecode/LinkTimeConstant.h: * bytecode/Opcode.h: (JSC::isBranch): * bytecode/PreciseJumpTargetsInlines.h: * bytecompiler/BytecodeGenerator.cpp: (JSC::GenericLabel<JSGeneratorTraits>::setLocation): (JSC::BytecodeGenerator::emitJumpIfSentinelString): * bytecompiler/BytecodeGenerator.h: * bytecompiler/NodesCodegen.cpp: (JSC::ForInNode::emitBytecode): * dfg/DFGAbstractInterpreterInlines.h: (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::parseBlock): * dfg/DFGCapabilities.cpp: (JSC::DFG::capabilityLevel): * dfg/DFGOperations.cpp: (JSC::DFG::putByVal): (JSC::DFG::putByValInternal): (JSC::DFG::putByValCellInternal): (JSC::DFG::JSC_DEFINE_JIT_OPERATION): * dfg/DFGOperations.h: * dfg/DFGPredictionPropagationPhase.cpp: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileEnumeratorNextUpdatePropertyName): * ftl/FTLLowerDFGToB3.cpp: (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq): * jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): * jit/JIT.h: * jit/JITOpcodes.cpp: (JSC::JIT::emit_op_jeq_ptr): * jit/JITOpcodes32_64.cpp: (JSC::JIT::emit_op_jeq_ptr): * jit/JITPropertyAccess.cpp: (JSC::JIT::emit_op_enumerator_next): * llint/LowLevelInterpreter32_64.asm: * llint/LowLevelInterpreter64.asm: * runtime/CommonSlowPaths.cpp: (JSC::JSC_DEFINE_COMMON_SLOW_PATH): * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::init): * runtime/SmallStrings.cpp: (JSC::SmallStrings::initializeCommonStrings): (JSC::SmallStrings::visitStrongReferences): * runtime/SmallStrings.h: (JSC::SmallStrings::sentinelString const): Canonical link: https://commits.webkit.org/242153@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@283095 268f45cc-cd09-0410-ab3c-d52691b4dbfc
1 parent 33f370a commit fb09016

29 files changed

Lines changed: 256 additions & 33 deletions

‎JSTests/ChangeLog‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
2021-09-26 Yusuke Suzuki <ysuzuki@apple.com>
2+
3+
[JSC] Optimize PutByVal with for-in
4+
https://bugs.webkit.org/show_bug.cgi?id=230801
5+
6+
Reviewed by Saam Barati.
7+
8+
* stress/for-in-sentinel.js: Added.
9+
(shouldBe):
10+
(test):
11+
112
2021-09-24 Rick Waldron <waldron.rick@gmail.com>
213

314
[JSC] Add checkScriptSyntax function to jsc host environment

‎JSTests/stress/for-in-sentinel.js‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
function shouldBe(actual, expected) {
2+
if (actual !== expected)
3+
throw new Error('bad value: ' + actual);
4+
}
5+
6+
var object = {
7+
$: 32,
8+
test: 33,
9+
hey: 34,
10+
};
11+
12+
function test(object) {
13+
var count = 0;
14+
for (var i in object) {
15+
++count;
16+
}
17+
return count;
18+
}
19+
noInline(test);
20+
21+
for (var i = 0; i < 1e6; ++i)
22+
shouldBe(test(object), 3);

‎Source/JavaScriptCore/ChangeLog‎

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,108 @@
1+
2021-09-26 Yusuke Suzuki <ysuzuki@apple.com>
2+
3+
[JSC] Optimize PutByVal with for-in
4+
https://bugs.webkit.org/show_bug.cgi?id=230801
5+
6+
Reviewed by Saam Barati.
7+
8+
We found that some of Speedometer2 subtests are heavily using for-in with PutByVal or the other DFG nodes.
9+
And we also found that we are using polluted non-good type for the property names from for-in: String | Other.
10+
The reason is that we are returning null when op_enumerator_next finishes instead of string. And this design
11+
forces DFG and FTL to return null from EnumeratorNextUpdatePropertyName at the end of iteration. This pollutes
12+
the type of property names as String | Other instead of String, and leading to suboptimal DFG nodes.
13+
14+
In this patch, we add special sentinel string in vm.smallString.sentinelString(). We know that this string cell
15+
pointer will be never returned from EnumeratorNextUpdatePropertyName in the normal for-in iteration. This is easy
16+
since we are always allocating a JSString when creating JSPropertyNameEnumerator. So this string cell (not the content)
17+
is always different from pre-allocated vm.smallString.sentinelString(). So, we use this special string pointer
18+
as a sentinel instead of null so that we can avoid polluting return type of EnumeratorNextUpdatePropertyName.
19+
20+
To check the sentinel in LLInt / Baseline, this patch adds jeq_ptr, which performs cell pointer comparison and do
21+
not check string content equality. We do not need to have an implementation in DFG since we already have CompareEqPtr
22+
for existing jneq_ptr bytecode.
23+
24+
We also clean up DFG operation related to PutByVal.
25+
26+
----------------------------------------------------------------------------------------------------------------------------------
27+
| subtest | ms | ms | b / a | pValue (significance using False Discovery Rate) |
28+
----------------------------------------------------------------------------------------------------------------------------------
29+
| Elm-TodoMVC |116.010000 |112.701667 |0.971482 | 0.000000 (significant) |
30+
| VueJS-TodoMVC |22.995000 |23.023333 |1.001232 | 0.907086 |
31+
| EmberJS-TodoMVC |125.498333 |125.525000 |1.000212 | 0.932546 |
32+
| BackboneJS-TodoMVC |45.700000 |45.975000 |1.006018 | 0.084799 |
33+
| Preact-TodoMVC |16.681667 |16.610000 |0.995704 | 0.722758 |
34+
| AngularJS-TodoMVC |123.753333 |123.740000 |0.999892 | 0.971431 |
35+
| Vanilla-ES2015-TodoMVC |61.255000 |61.380000 |1.002041 | 0.300654 |
36+
| Inferno-TodoMVC |58.646667 |58.948333 |1.005144 | 0.267611 |
37+
| Flight-TodoMVC |73.283333 |72.801667 |0.993427 | 0.207389 |
38+
| Angular2-TypeScript-TodoMVC |39.746667 |40.015000 |1.006751 | 0.449821 |
39+
| VanillaJS-TodoMVC |50.096667 |49.823333 |0.994544 | 0.162020 |
40+
| jQuery-TodoMVC |212.870000 |213.196667 |1.001535 | 0.371944 |
41+
| EmberJS-Debug-TodoMVC |331.878333 |332.710000 |1.002506 | 0.094499 |
42+
| React-TodoMVC |83.078333 |82.726667 |0.995767 | 0.076143 |
43+
| React-Redux-TodoMVC |136.018333 |133.935000 |0.984683 | 0.000000 (significant) |
44+
| Vanilla-ES2015-Babel-Webpack-TodoMVC |59.743333 |59.643333 |0.998326 | 0.393671 |
45+
----------------------------------------------------------------------------------------------------------------------------------
46+
a mean = 271.75873
47+
b mean = 272.45804
48+
pValue = 0.0263030803
49+
(Bigger means are better.)
50+
1.003 times better
51+
Results ARE significant
52+
53+
* builtins/BuiltinNames.h:
54+
* bytecode/BytecodeList.rb:
55+
* bytecode/BytecodeUseDef.cpp:
56+
(JSC::computeUsesForBytecodeIndexImpl):
57+
(JSC::computeDefsForBytecodeIndexImpl):
58+
* bytecode/LinkTimeConstant.h:
59+
* bytecode/Opcode.h:
60+
(JSC::isBranch):
61+
* bytecode/PreciseJumpTargetsInlines.h:
62+
* bytecompiler/BytecodeGenerator.cpp:
63+
(JSC::GenericLabel<JSGeneratorTraits>::setLocation):
64+
(JSC::BytecodeGenerator::emitJumpIfSentinelString):
65+
* bytecompiler/BytecodeGenerator.h:
66+
* bytecompiler/NodesCodegen.cpp:
67+
(JSC::ForInNode::emitBytecode):
68+
* dfg/DFGAbstractInterpreterInlines.h:
69+
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
70+
* dfg/DFGByteCodeParser.cpp:
71+
(JSC::DFG::ByteCodeParser::parseBlock):
72+
* dfg/DFGCapabilities.cpp:
73+
(JSC::DFG::capabilityLevel):
74+
* dfg/DFGOperations.cpp:
75+
(JSC::DFG::putByVal):
76+
(JSC::DFG::putByValInternal):
77+
(JSC::DFG::putByValCellInternal):
78+
(JSC::DFG::JSC_DEFINE_JIT_OPERATION):
79+
* dfg/DFGOperations.h:
80+
* dfg/DFGPredictionPropagationPhase.cpp:
81+
* dfg/DFGSpeculativeJIT.cpp:
82+
(JSC::DFG::SpeculativeJIT::compileEnumeratorNextUpdatePropertyName):
83+
* ftl/FTLLowerDFGToB3.cpp:
84+
(JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
85+
* jit/JIT.cpp:
86+
(JSC::JIT::privateCompileMainPass):
87+
* jit/JIT.h:
88+
* jit/JITOpcodes.cpp:
89+
(JSC::JIT::emit_op_jeq_ptr):
90+
* jit/JITOpcodes32_64.cpp:
91+
(JSC::JIT::emit_op_jeq_ptr):
92+
* jit/JITPropertyAccess.cpp:
93+
(JSC::JIT::emit_op_enumerator_next):
94+
* llint/LowLevelInterpreter32_64.asm:
95+
* llint/LowLevelInterpreter64.asm:
96+
* runtime/CommonSlowPaths.cpp:
97+
(JSC::JSC_DEFINE_COMMON_SLOW_PATH):
98+
* runtime/JSGlobalObject.cpp:
99+
(JSC::JSGlobalObject::init):
100+
* runtime/SmallStrings.cpp:
101+
(JSC::SmallStrings::initializeCommonStrings):
102+
(JSC::SmallStrings::visitStrongReferences):
103+
* runtime/SmallStrings.h:
104+
(JSC::SmallStrings::sentinelString const):
105+
1106
2021-09-26 Commit Queue <commit-queue@webkit.org>
2107

3108
Unreviewed, reverting r283083 and r283088.

‎Source/JavaScriptCore/builtins/BuiltinNames.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ namespace JSC {
182182
macro(createPrivateSymbol) \
183183
macro(entries) \
184184
macro(outOfLineReactionCounts) \
185-
macro(emptyPropertyNameEnumerator)
185+
macro(emptyPropertyNameEnumerator) \
186186

187187

188188
namespace Symbols {

‎Source/JavaScriptCore/bytecode/BytecodeList.rb‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,13 @@
762762
targetLabel: BoundLabel,
763763
}
764764

765+
op :jeq_ptr,
766+
args: {
767+
value: VirtualRegister,
768+
specialPointer: VirtualRegister,
769+
targetLabel: BoundLabel,
770+
}
771+
765772
op :jneq_ptr,
766773
args: {
767774
value: VirtualRegister,

‎Source/JavaScriptCore/bytecode/BytecodeUseDef.cpp‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ void computeUsesForBytecodeIndexImpl(VirtualRegister scopeRegister, const Instru
8080
// No uses.
8181
case op_new_regexp:
8282
case op_debug:
83-
case op_jneq_ptr:
8483
case op_loop_hint:
8584
case op_jmp:
8685
case op_new_object:
@@ -135,6 +134,9 @@ void computeUsesForBytecodeIndexImpl(VirtualRegister scopeRegister, const Instru
135134
USES(OpJnstricteq, lhs, rhs)
136135
USES(OpJbelow, lhs, rhs)
137136
USES(OpJbeloweq, lhs, rhs)
137+
USES(OpJeqPtr, value, specialPointer)
138+
USES(OpJneqPtr, value, specialPointer)
139+
138140
USES(OpSetFunctionName, function, name)
139141
USES(OpLogShadowChickenTail, thisValue, scope)
140142

@@ -365,6 +367,7 @@ void computeDefsForBytecodeIndexImpl(unsigned numVars, const Instruction* instru
365367
case op_jneq_null:
366368
case op_jundefined_or_null:
367369
case op_jnundefined_or_null:
370+
case op_jeq_ptr:
368371
case op_jneq_ptr:
369372
case op_jless:
370373
case op_jlesseq:

‎Source/JavaScriptCore/bytecode/Opcode.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ inline bool isBranch(OpcodeID opcodeID)
199199
case op_jneq_null:
200200
case op_jundefined_or_null:
201201
case op_jnundefined_or_null:
202+
case op_jeq_ptr:
202203
case op_jneq_ptr:
203204
case op_jless:
204205
case op_jlesseq:

‎Source/JavaScriptCore/bytecode/PreciseJumpTargetsInlines.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ namespace JSC {
4343
CASE_OP(OpJneqNull) \
4444
CASE_OP(OpJundefinedOrNull) \
4545
CASE_OP(OpJnundefinedOrNull) \
46+
CASE_OP(OpJeqPtr) \
4647
CASE_OP(OpJneqPtr) \
4748
\
4849
CASE_OP(OpJless) \

‎Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ void GenericLabel<JSGeneratorTraits>::setLocation(BytecodeGenerator& generator,
105105
CASE(OpJeq)
106106
CASE(OpJstricteq)
107107
CASE(OpJneq)
108+
CASE(OpJeqPtr)
108109
CASE(OpJneqPtr)
109110
CASE(OpJnstricteq)
110111
CASE(OpJless)
@@ -1497,6 +1498,11 @@ void BytecodeGenerator::emitJumpIfNotFunctionApply(RegisterID* cond, Label& targ
14971498
OpJneqPtr::emit(this, cond, moveLinkTimeConstant(nullptr, LinkTimeConstant::applyFunction), target.bind(this));
14981499
}
14991500

1501+
void BytecodeGenerator::emitJumpIfSentinelString(RegisterID* cond, Label& target)
1502+
{
1503+
OpJeqPtr::emit(this, cond, emitLoad(nullptr, JSValue(vm().smallStrings.sentinelString())), target.bind(this));
1504+
}
1505+
15001506
unsigned BytecodeGenerator::emitWideJumpIfNotFunctionHasOwnProperty(RegisterID* cond, Label& target)
15011507
{
15021508
OpJneqPtr::emit<OpcodeSize::Wide32>(this, cond, moveLinkTimeConstant(nullptr, LinkTimeConstant::hasOwnPropertyFunction), target.bind(this));

‎Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,7 @@ namespace JSC {
858858
void emitJumpIfFalse(RegisterID* cond, Label& target);
859859
void emitJumpIfNotFunctionCall(RegisterID* cond, Label& target);
860860
void emitJumpIfNotFunctionApply(RegisterID* cond, Label& target);
861+
void emitJumpIfSentinelString(RegisterID* cond, Label& target);
861862
unsigned emitWideJumpIfNotFunctionHasOwnProperty(RegisterID* cond, Label& target);
862863
void recordHasOwnPropertyInForInLoop(ForInContext&, unsigned branchOffset, Label& genericPath);
863864

0 commit comments

Comments
 (0)