[JSC] Make get_by_val & string "499" to number 499
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 22 May 2017 05:33:47 +0000 (05:33 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 22 May 2017 05:33:47 +0000 (05:33 +0000)
https://bugs.webkit.org/show_bug.cgi?id=172225

Reviewed by Saam Barati.

JSTests:

* stress/get-by-val-string.js: Added.
(shouldBe):
(shouldThrow):
(object.43):
(Hello):
(Hello.prototype.get 42):
(Hello.prototype.43):
(Derived):

Source/JavaScriptCore:

Property subscript will be converted by ToString. So JS code is not aware of
the original type of the subscript value. But our get_by_val can leverage
information if the given subscript is number. Thus, passing number instead of
string can improve the performance of get_by_val in all the tiers.

In this patch, we add BytecodeGenerator::emitNodeForProperty. It attempts to
convert the given value to Int32 index constant if the given value is a string
that can be converted to Int32.

This patch improves SixSpeed map-string.es5 by 9.8x. This accessing form can
appear in some code like accessing the result of JSON.

    map-string.es5     1640.6738+-110.9182   ^    167.4121+-23.8328       ^ definitely 9.8002x faster

* bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::emitNodeForProperty):
(JSC::BytecodeGenerator::emitNodeForLeftHandSideForProperty):
* bytecompiler/NodesCodegen.cpp:
(JSC::TaggedTemplateNode::emitBytecode):
(JSC::BracketAccessorNode::emitBytecode):
(JSC::BytecodeIntrinsicNode::emit_intrinsic_putByValDirect):
(JSC::FunctionCallBracketNode::emitBytecode):
(JSC::PostfixNode::emitBracket):
(JSC::PrefixNode::emitBracket):
(JSC::AssignBracketNode::emitBytecode):
(JSC::ReadModifyBracketNode::emitBytecode):
(JSC::ForInNode::emitLoopHeader):
(JSC::ForOfNode::emitBytecode):
(JSC::ObjectPatternNode::bindValue):
(JSC::AssignmentElementNode::bindValue):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@217199 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JSTests/ChangeLog
JSTests/stress/get-by-val-string.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

index 2cb585d..1fee493 100644 (file)
@@ -1,3 +1,19 @@
+2017-05-19  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Make get_by_val & string "499" to number 499
+        https://bugs.webkit.org/show_bug.cgi?id=172225
+
+        Reviewed by Saam Barati.
+
+        * stress/get-by-val-string.js: Added.
+        (shouldBe):
+        (shouldThrow):
+        (object.43):
+        (Hello):
+        (Hello.prototype.get 42):
+        (Hello.prototype.43):
+        (Derived):
+
 2017-05-19  Mark Lam  <mark.lam@apple.com>
 
         [Re-landing] DFG::SpeculativeJIT::pickCanTrample() is wrongly ignoring result registers.
diff --git a/JSTests/stress/get-by-val-string.js b/JSTests/stress/get-by-val-string.js
new file mode 100644 (file)
index 0000000..cbe4b6a
--- /dev/null
@@ -0,0 +1,66 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+//
+// var object = {};
+// object[42] = 42;
+// object[43] = function tag() { return 42; };
+//
+// shouldBe(object['43']`Hello`, 42);
+//
+//
+// class Hello {
+//     constructor()
+//     {
+//         this['44'] = 42;
+//         shouldBe(this['42'], 42);
+//         shouldBe(this['43'](), 42);
+//         shouldBe(this['44'], 42);
+//     }
+//
+//     get 42()
+//     {
+//         return 42;
+//     }
+//
+//     43()
+//     {
+//         return 42;
+//     }
+// }
+//
+// class Derived extends Hello {
+//     constructor()
+//     {
+//         super();
+//         shouldBe(super['42'], 42);
+//         shouldBe(super['43'](), 42);
+//         shouldBe(this['44']++, 42);
+//         shouldBe(++this['44'], 44);
+//     }
+// }
+//
+// var derived = new Derived();
+//
+// var test = { 42: '' };
+//
+// for (test['42'] in { a: 'a' })
+//     shouldBe(test['42'], 'a');
+// shouldBe(test['42'], 'a');
+//
+// for (test['42'] of [ 'b' ])
+//     shouldBe(test['42'], 'b');
+// shouldBe(test['42'], 'b');
+//
+// {
+//     let { '42': a } = { '42': '42' };
+//     shouldBe(a, '42');
+// }
+
+{
+    let object = { 42: 42 };
+    let objectAlias = object;
+    object['42'] = (object = 30);
+    // shouldBe(objectAlias['42'], 30);
+}
index f4e4343..aa475c2 100644 (file)
@@ -1,3 +1,41 @@
+2017-05-19  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Make get_by_val & string "499" to number 499
+        https://bugs.webkit.org/show_bug.cgi?id=172225
+
+        Reviewed by Saam Barati.
+
+        Property subscript will be converted by ToString. So JS code is not aware of
+        the original type of the subscript value. But our get_by_val can leverage
+        information if the given subscript is number. Thus, passing number instead of
+        string can improve the performance of get_by_val in all the tiers.
+
+        In this patch, we add BytecodeGenerator::emitNodeForProperty. It attempts to
+        convert the given value to Int32 index constant if the given value is a string
+        that can be converted to Int32.
+
+        This patch improves SixSpeed map-string.es5 by 9.8x. This accessing form can
+        appear in some code like accessing the result of JSON.
+
+            map-string.es5     1640.6738+-110.9182   ^    167.4121+-23.8328       ^ definitely 9.8002x faster
+
+        * bytecompiler/BytecodeGenerator.h:
+        (JSC::BytecodeGenerator::emitNodeForProperty):
+        (JSC::BytecodeGenerator::emitNodeForLeftHandSideForProperty):
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::TaggedTemplateNode::emitBytecode):
+        (JSC::BracketAccessorNode::emitBytecode):
+        (JSC::BytecodeIntrinsicNode::emit_intrinsic_putByValDirect):
+        (JSC::FunctionCallBracketNode::emitBytecode):
+        (JSC::PostfixNode::emitBracket):
+        (JSC::PrefixNode::emitBracket):
+        (JSC::AssignBracketNode::emitBytecode):
+        (JSC::ReadModifyBracketNode::emitBytecode):
+        (JSC::ForInNode::emitLoopHeader):
+        (JSC::ForOfNode::emitBytecode):
+        (JSC::ObjectPatternNode::bindValue):
+        (JSC::AssignmentElementNode::bindValue):
+
 2017-05-21  Saam Barati  <sbarati@apple.com>
 
         We overwrite the callee save space on the stack when throwing stack overflow from wasm
index 90236b9..c7f47f0 100644 (file)
@@ -504,6 +504,20 @@ namespace JSC {
             return emitNodeInTailPosition(nullptr, n);
         }
 
+        RegisterID* emitNodeForProperty(RegisterID* dst, ExpressionNode* node)
+        {
+            if (node->isString()) {
+                if (std::optional<uint32_t> index = parseIndex(static_cast<StringNode*>(node)->value()))
+                    return emitLoad(dst, jsNumber(index.value()));
+            }
+            return emitNode(dst, node);
+        }
+
+        RegisterID* emitNodeForProperty(ExpressionNode* n)
+        {
+            return emitNodeForProperty(nullptr, n);
+        }
+
         void emitNodeInConditionContext(ExpressionNode* n, Label& trueTarget, Label& falseTarget, FallThroughMode fallThroughMode)
         {
             if (UNLIKELY(!m_vm->isSafeToRecurse())) {
@@ -562,6 +576,17 @@ namespace JSC {
             return emitNode(n);
         }
 
+        ALWAYS_INLINE RefPtr<RegisterID> emitNodeForLeftHandSideForProperty(ExpressionNode* n, bool rightHasAssignments, bool rightIsPure)
+        {
+            if (leftHandSideNeedsCopy(rightHasAssignments, rightIsPure)) {
+                RefPtr<RegisterID> dst = newTemporary();
+                emitNodeForProperty(dst.get(), n);
+                return dst;
+            }
+
+            return emitNodeForProperty(n);
+        }
+
         void hoistSloppyModeFunctionIfNecessary(const Identifier& functionName);
 
     private:
index c5d1a00..df08966 100644 (file)
@@ -324,7 +324,7 @@ RegisterID* TaggedTemplateNode::emitBytecode(BytecodeGenerator& generator, Regis
         BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(m_tag);
         base = generator.newTemporary();
         base = generator.emitNode(base.get(), bracket->base());
-        RefPtr<RegisterID> property = generator.emitNode(bracket->subscript());
+        RefPtr<RegisterID> property = generator.emitNodeForProperty(bracket->subscript());
         if (bracket->base()->isSuperNode()) {
             RefPtr<RegisterID> thisValue = generator.ensureThis();
             tag = generator.emitGetByVal(generator.newTemporary(), base.get(), thisValue.get(), property.get());
@@ -668,7 +668,7 @@ RegisterID* BracketAccessorNode::emitBytecode(BytecodeGenerator& generator, Regi
             generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
             generator.emitGetById(finalDest.get(), superBase.get(), thisValue.get(), id);
         } else  {
-            RefPtr<RegisterID> subscript = generator.emitNode(m_subscript);
+            RefPtr<RegisterID> subscript = generator.emitNodeForProperty(m_subscript);
             generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
             generator.emitGetByVal(finalDest.get(), superBase.get(), thisValue.get(), subscript.get());
         }
@@ -686,7 +686,7 @@ RegisterID* BracketAccessorNode::emitBytecode(BytecodeGenerator& generator, Regi
         ret = generator.emitGetById(finalDest.get(), base.get(), static_cast<StringNode*>(m_subscript)->value());
     } else {
         RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments, m_subscript->isPure(generator));
-        RegisterID* property = generator.emitNode(m_subscript);
+        RegisterID* property = generator.emitNodeForProperty(m_subscript);
         generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
         ret = generator.emitGetByVal(finalDest.get(), base.get(), property);
     }
@@ -929,7 +929,7 @@ RegisterID* BytecodeIntrinsicNode::emit_intrinsic_putByValDirect(BytecodeGenerat
     ArgumentListNode* node = m_args->m_listNode;
     RefPtr<RegisterID> base = generator.emitNode(node);
     node = node->m_next;
-    RefPtr<RegisterID> index = generator.emitNode(node);
+    RefPtr<RegisterID> index = generator.emitNodeForProperty(node);
     node = node->m_next;
     RefPtr<RegisterID> value = generator.emitNode(node);
 
@@ -1137,7 +1137,7 @@ RegisterID* FunctionCallBracketNode::emitBytecode(BytecodeGenerator& generator,
         else
             function = generator.emitGetById(generator.tempDestination(dst), base.get(), static_cast<StringNode*>(m_subscript)->value());
     } else {
-        RefPtr<RegisterID> property = generator.emitNode(m_subscript);
+        RefPtr<RegisterID> property = generator.emitNodeForProperty(m_subscript);
         generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd());
         if (baseIsSuper)
             function = generator.emitGetByVal(generator.tempDestination(dst), base.get(), thisRegister.get(), property.get());
@@ -1440,7 +1440,7 @@ RegisterID* PostfixNode::emitBracket(BytecodeGenerator& generator, RegisterID* d
     ExpressionNode* subscript = bracketAccessor->subscript();
 
     RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(baseNode, bracketAccessor->subscriptHasAssignments(), subscript->isPure(generator));
-    RefPtr<RegisterID> property = generator.emitNode(subscript);
+    RefPtr<RegisterID> property = generator.emitNodeForProperty(subscript);
 
     generator.emitExpressionInfo(bracketAccessor->divot(), bracketAccessor->divotStart(), bracketAccessor->divotEnd());
     RefPtr<RegisterID> value;
@@ -1657,7 +1657,7 @@ RegisterID* PrefixNode::emitBracket(BytecodeGenerator& generator, RegisterID* ds
     ExpressionNode* subscript = bracketAccessor->subscript();
 
     RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(baseNode, bracketAccessor->subscriptHasAssignments(), subscript->isPure(generator));
-    RefPtr<RegisterID> property = generator.emitNode(subscript);
+    RefPtr<RegisterID> property = generator.emitNodeForProperty(subscript);
     RefPtr<RegisterID> propDst = generator.tempDestination(dst);
 
     generator.emitExpressionInfo(bracketAccessor->divot(), bracketAccessor->divotStart(), bracketAccessor->divotEnd());
@@ -2384,7 +2384,7 @@ RegisterID* AssignErrorNode::emitBytecode(BytecodeGenerator& generator, Register
 RegisterID* AssignBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
     RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments || m_rightHasAssignments, m_subscript->isPure(generator) && m_right->isPure(generator));
-    RefPtr<RegisterID> property = generator.emitNodeForLeftHandSide(m_subscript, m_rightHasAssignments, m_right->isPure(generator));
+    RefPtr<RegisterID> property = generator.emitNodeForLeftHandSideForProperty(m_subscript, m_rightHasAssignments, m_right->isPure(generator));
     RefPtr<RegisterID> value = generator.destinationForAssignResult(dst);
     RefPtr<RegisterID> result = generator.emitNode(value.get(), m_right);
 
@@ -2414,7 +2414,7 @@ RegisterID* AssignBracketNode::emitBytecode(BytecodeGenerator& generator, Regist
 RegisterID* ReadModifyBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
     RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments || m_rightHasAssignments, m_subscript->isPure(generator) && m_right->isPure(generator));
-    RefPtr<RegisterID> property = generator.emitNodeForLeftHandSide(m_subscript, m_rightHasAssignments, m_right->isPure(generator));
+    RefPtr<RegisterID> property = generator.emitNodeForLeftHandSideForProperty(m_subscript, m_rightHasAssignments, m_right->isPure(generator));
 
     generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd());
     RefPtr<RegisterID> value;
@@ -2780,7 +2780,7 @@ void ForInNode::emitLoopHeader(BytecodeGenerator& generator, RegisterID* propert
     if (m_lexpr->isBracketAccessorNode()) {
         BracketAccessorNode* assignNode = static_cast<BracketAccessorNode*>(m_lexpr);
         RefPtr<RegisterID> base = generator.emitNode(assignNode->base());
-        RefPtr<RegisterID> subscript = generator.emitNode(assignNode->subscript());
+        RefPtr<RegisterID> subscript = generator.emitNodeForProperty(assignNode->subscript());
         generator.emitExpressionInfo(assignNode->divot(), assignNode->divotStart(), assignNode->divotEnd());
         if (assignNode->base()->isSuperNode()) {
             RefPtr<RegisterID> thisValue = generator.ensureThis();
@@ -3011,7 +3011,7 @@ void ForOfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
         } else if (m_lexpr->isBracketAccessorNode()) {
             BracketAccessorNode* assignNode = static_cast<BracketAccessorNode*>(m_lexpr);
             RefPtr<RegisterID> base = generator.emitNode(assignNode->base());
-            RegisterID* subscript = generator.emitNode(assignNode->subscript());
+            RegisterID* subscript = generator.emitNodeForProperty(assignNode->subscript());
             
             generator.emitExpressionInfo(assignNode->divot(), assignNode->divotStart(), assignNode->divotEnd());
             if (assignNode->base()->isSuperNode()) {
@@ -4071,7 +4071,7 @@ void ObjectPatternNode::bindValue(BytecodeGenerator& generator, RegisterID* rhs)
                 generator.emitGetByVal(temp.get(), rhs, index.get());
             }
         } else {
-            RefPtr<RegisterID> propertyName = generator.emitNode(target.propertyExpression);
+            RefPtr<RegisterID> propertyName = generator.emitNodeForProperty(target.propertyExpression);
             generator.emitGetByVal(temp.get(), rhs, propertyName.get());
         }
 
@@ -4180,7 +4180,7 @@ void AssignmentElementNode::bindValue(BytecodeGenerator& generator, RegisterID*
     } else if (m_assignmentTarget->isBracketAccessorNode()) {
         BracketAccessorNode* lhs = static_cast<BracketAccessorNode*>(m_assignmentTarget);
         RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(lhs->base(), true, false);
-        RefPtr<RegisterID> property = generator.emitNodeForLeftHandSide(lhs->subscript(), true, false);
+        RefPtr<RegisterID> property = generator.emitNodeForLeftHandSideForProperty(lhs->subscript(), true, false);
         generator.emitExpressionInfo(divotEnd(), divotStart(), divotEnd());
         if (lhs->base()->isSuperNode()) {
             RefPtr<RegisterID> thisValue = generator.ensureThis();