[DFG] AI should convert CreateThis to NewObject if the prototype object is proved
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 12 Mar 2018 03:25:39 +0000 (03:25 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 12 Mar 2018 03:25:39 +0000 (03:25 +0000)
https://bugs.webkit.org/show_bug.cgi?id=183310

Reviewed by Filip Pizlo.

JSTests:

* stress/ai-create-this-to-new-object-fire.js: Added.
(assert):
(test):
(func):
(check):
(test.body.A):
(test.body.B):
(test.body):
* stress/ai-create-this-to-new-object.js: Added.
(assert):
(test):
(func):
(check):
(test.body.A):
(test.body.B):
(test.body):

Source/JavaScriptCore:

This patch implements CreateThis -> NewObject conversion in AI if the given function is constant.
This contributes to 6% win in Octane/raytrace.

                                baseline                  patched

    raytrace       x2       1.19915+-0.01862    ^     1.13156+-0.01589       ^ definitely 1.0597x faster

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):

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

JSTests/ChangeLog
JSTests/stress/ai-create-this-to-new-object-fire.js [new file with mode: 0644]
JSTests/stress/ai-create-this-to-new-object.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp

index 9843b3cb3c1a2d584ba7d77b4cba042788e2bc9c..6c4877b96fd5d48255eb6a5c4b675ee86e55efde 100644 (file)
@@ -1,3 +1,27 @@
+2018-03-11  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DFG] AI should convert CreateThis to NewObject if the prototype object is proved
+        https://bugs.webkit.org/show_bug.cgi?id=183310
+
+        Reviewed by Filip Pizlo.
+
+        * stress/ai-create-this-to-new-object-fire.js: Added.
+        (assert):
+        (test):
+        (func):
+        (check):
+        (test.body.A):
+        (test.body.B):
+        (test.body):
+        * stress/ai-create-this-to-new-object.js: Added.
+        (assert):
+        (test):
+        (func):
+        (check):
+        (test.body.A):
+        (test.body.B):
+        (test.body):
+
 2018-03-10  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [FTL] Drop NewRegexp for String.prototype.match with RegExp + global flag
diff --git a/JSTests/stress/ai-create-this-to-new-object-fire.js b/JSTests/stress/ai-create-this-to-new-object-fire.js
new file mode 100644 (file)
index 0000000..5adf008
--- /dev/null
@@ -0,0 +1,61 @@
+function assert(b, m = "Bad!") {
+    if (!b) {
+        throw new Error(m);
+    }
+}
+
+function test(f, iters = 1000) {
+    for (let i = 0; i < iters; i++)
+        f(i);
+}
+
+function func(x) {
+    return x;
+}
+noInline(func);
+
+var n = 2;
+var prototype = {};
+function prep(index, i, A, B)
+{
+    if (index === (n - 1) && i === 5000) {
+        // Fire watchpoint!
+        A.prototype = prototype;
+    }
+}
+
+function check(index, arr, A, B, originalPrototype)
+{
+    if (index === (n - 1)) {
+        assert(originalPrototype !== prototype);
+        for (let i = 0; i < 5000; i++)
+            assert(arr[i].__proto__ === originalPrototype);
+        for (let i = 5000; i < 10000; i++)
+            assert(arr[i].__proto__ === prototype);
+    } else {
+        for (let i = 0; i < 10000; i++)
+            assert(arr[i].__proto__ === originalPrototype);
+    }
+}
+noInline(check);
+
+test(function body(index) {
+    function A(x, f = func) {
+        this._value = x;
+        this._func = f;
+    }
+
+    function B(n)
+    {
+        return new A(n);
+    }
+
+    var originalPrototype = A.prototype;
+    let arr = [];
+    for (let i = 0; i < 10000; i++) {
+        prep(index, i, A, B);
+        arr.push(B(20));
+    }
+
+    check(index, arr, A, B, originalPrototype);
+}, n);
diff --git a/JSTests/stress/ai-create-this-to-new-object.js b/JSTests/stress/ai-create-this-to-new-object.js
new file mode 100644 (file)
index 0000000..c89fb18
--- /dev/null
@@ -0,0 +1,41 @@
+function assert(b, m = "Bad!") {
+    if (!b) {
+        throw new Error(m);
+    }
+}
+
+function test(f, iters = 1000) {
+    for (let i = 0; i < iters; i++)
+        f(i);
+}
+
+function func(x) {
+    return x;
+}
+noInline(func);
+
+function check(index, arr, B)
+{
+    for (let i = 0; i < 1000; i++)
+        assert(arr[i] instanceof B);
+}
+noInline(check);
+
+test(function body(index) {
+    class A {
+        constructor(x, f = func)
+        {
+            this._value = x;
+            this._func = f;
+        }
+    }
+
+    class B extends A {
+    }
+
+    let arr = [];
+    for (let i = 0; i < 1000; i++)
+        arr.push(new B(20));
+
+    check(index, arr, B);
+}, 8);
index 02e308d25531f582ab9d3ba018c920eb793371c7..16f67a2ee3ca7fe86c027c686b439f235d1b1744 100644 (file)
@@ -1,3 +1,22 @@
+2018-03-11  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DFG] AI should convert CreateThis to NewObject if the prototype object is proved
+        https://bugs.webkit.org/show_bug.cgi?id=183310
+
+        Reviewed by Filip Pizlo.
+
+        This patch implements CreateThis -> NewObject conversion in AI if the given function is constant.
+        This contributes to 6% win in Octane/raytrace.
+
+                                        baseline                  patched
+
+            raytrace       x2       1.19915+-0.01862    ^     1.13156+-0.01589       ^ definitely 1.0597x faster
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+
 2018-03-11  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Disable Sigill crash analyzer on watchOS
index de6a8c6753a3632e970326f79731c70725cdeca6..e7f1585c658342700c53b18ed1c85ef1f50128b7 100644 (file)
@@ -2257,7 +2257,23 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     }
 
     case CreateThis: {
-        // FIXME: We can fold this to NewObject if the incoming callee is a constant.
+        if (JSValue base = forNode(node->child1()).m_value) {
+            if (auto* function = jsDynamicCast<JSFunction*>(m_vm, base)) {
+                if (FunctionRareData* rareData = function->rareData()) {
+                    if (Structure* structure = rareData->objectAllocationStructure()) {
+                        // FIXME: we should be able to allocate a poly proto object here:
+                        // https://bugs.webkit.org/show_bug.cgi?id=177517
+                        if (structure->hasMonoProto()) {
+                            m_graph.freeze(rareData);
+                            m_graph.watchpoints().addLazily(rareData->allocationProfileWatchpointSet());
+                            m_state.setFoundConstants(true);
+                            forNode(node).set(m_graph, structure);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
         forNode(node).setType(m_graph, SpecFinalObject);
         break;
     }
index b6df73186a7edf07629e277967161fd5e5a7d85c..bd8748acdc331a00e05c77e4ecbd54753f15571a 100644 (file)
@@ -636,6 +636,27 @@ private:
                 break;
             }
 
+            case CreateThis: {
+                if (JSValue base = m_state.forNode(node->child1()).m_value) {
+                    if (auto* function = jsDynamicCast<JSFunction*>(m_graph.m_vm, base)) {
+                        if (FunctionRareData* rareData = function->rareData()) {
+                            if (Structure* structure = rareData->objectAllocationStructure()) {
+                                // FIXME: we should be able to allocate a poly proto object here:
+                                // https://bugs.webkit.org/show_bug.cgi?id=177517
+                                if (structure->hasMonoProto()) {
+                                    m_graph.freeze(rareData);
+                                    m_graph.watchpoints().addLazily(rareData->allocationProfileWatchpointSet());
+                                    node->convertToNewObject(m_graph.registerStructure(structure));
+                                    changed = true;
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+
             case ToNumber: {
                 if (m_state.forNode(node->child1()).m_type & ~SpecBytecodeNumber)
                     break;