[JSC] Make StructureChain less-tricky by using Auxiliary Buffer
authorysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 31 Jul 2019 01:22:20 +0000 (01:22 +0000)
committerysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 31 Jul 2019 01:22:20 +0000 (01:22 +0000)
https://bugs.webkit.org/show_bug.cgi?id=200192

Reviewed by Saam Barati.

JSTests:

* stress/structure-chain-stress.js: Added.
(keys):

Source/JavaScriptCore:

StructureChain has a bit tricky write barrier / mutator fence to use UniqueArray for its underlying storage.
But, since the size of StructureChain is fixed at initialization, we should allocate an underlying storage from auxiliary memory and
set it in its constructor instead of finishCreation. We can store values in the finishCreation so that we do not need to have
a hacky write-barrier and mutator fence. Furthermore, we can make StructureChain non-destructible.

This patch leverages auxiliary buffer for the implementation of StructureChain. And it also adds a test that stresses StructureChain creation.

* runtime/StructureChain.cpp:
(JSC::StructureChain::StructureChain):
(JSC::StructureChain::create):
(JSC::StructureChain::finishCreation):
(JSC::StructureChain::visitChildren):
(JSC::StructureChain::destroy): Deleted.
* runtime/StructureChain.h:

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

JSTests/ChangeLog
JSTests/stress/structure-chain-stress.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/StructureChain.cpp
Source/JavaScriptCore/runtime/StructureChain.h

index 93c8a06..de133a1 100644 (file)
@@ -1,3 +1,13 @@
+2019-07-30  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] Make StructureChain less-tricky by using Auxiliary Buffer
+        https://bugs.webkit.org/show_bug.cgi?id=200192
+
+        Reviewed by Saam Barati.
+
+        * stress/structure-chain-stress.js: Added.
+        (keys):
+
 2019-07-29  Yusuke Suzuki  <ysuzuki@apple.com>
 
         [JSC] Increment bytecode age only when SlotVisitor is first-visit
diff --git a/JSTests/stress/structure-chain-stress.js b/JSTests/stress/structure-chain-stress.js
new file mode 100644 (file)
index 0000000..5a9dad2
--- /dev/null
@@ -0,0 +1,30 @@
+var counter = 0;
+function keys(a, b, c) {
+    for (var i in a) {
+        for (var j in b) {
+            for (var k in c) {
+            }
+        }
+    }
+    if ((++counter) % 1000 === 0)
+        gc();
+}
+noInline(keys);
+
+var dictionary = {
+    0: 2,
+    "Hey": "Hello",
+    "World": 32.4,
+    "deleted": 20,
+};
+delete dictionary["deleted"];
+for (var i = 0; i < 1e4; ++i) {
+    keys([], [20], ["Hey"]);
+    keys(["OK", 30], { Hello: 0, World: 2 }, []);
+    keys(["OK", 30], { Hello: 0, World: 2 }, [42]);
+    keys(["OK", 30], [], { Hello: 0, World: 2 });
+    keys(["OK", 30], [2.5, 3.7], dictionary);
+    keys(dictionary, { Hello: 0, World: 2 }, dictionary);
+    keys({ Hello: 0, World: 2 }, dictionary, { Hello: 0, World: 2, 3:42 });
+    keys({ [`hello${i}`]: i }, { [`hello${i}`]: i, [`world${i}`]: i  }, [ 42 ]);
+}
index c5fd318..2ae0b99 100644 (file)
@@ -1,3 +1,25 @@
+2019-07-30  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] Make StructureChain less-tricky by using Auxiliary Buffer
+        https://bugs.webkit.org/show_bug.cgi?id=200192
+
+        Reviewed by Saam Barati.
+
+        StructureChain has a bit tricky write barrier / mutator fence to use UniqueArray for its underlying storage.
+        But, since the size of StructureChain is fixed at initialization, we should allocate an underlying storage from auxiliary memory and
+        set it in its constructor instead of finishCreation. We can store values in the finishCreation so that we do not need to have
+        a hacky write-barrier and mutator fence. Furthermore, we can make StructureChain non-destructible.
+
+        This patch leverages auxiliary buffer for the implementation of StructureChain. And it also adds a test that stresses StructureChain creation.
+
+        * runtime/StructureChain.cpp:
+        (JSC::StructureChain::StructureChain):
+        (JSC::StructureChain::create):
+        (JSC::StructureChain::finishCreation):
+        (JSC::StructureChain::visitChildren):
+        (JSC::StructureChain::destroy): Deleted.
+        * runtime/StructureChain.h:
+
 2019-07-29  Yusuke Suzuki  <ysuzuki@apple.com>
 
         [JSC] Increment bytecode age only when SlotVisitor is first-visit
index 41d2038..d469a3b 100644 (file)
@@ -34,14 +34,35 @@ namespace JSC {
     
 const ClassInfo StructureChain::s_info = { "StructureChain", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(StructureChain) };
 
-StructureChain::StructureChain(VM& vm, Structure* structure)
-    : JSCell(vm, structure)
+StructureChain::StructureChain(VM& vm, Structure* structure, WriteBarrier<Structure>* vector)
+    : Base(vm, structure)
+    , m_vector(vm, this, vector)
 {
 }
 
-void StructureChain::destroy(JSCell* cell)
+StructureChain* StructureChain::create(VM& vm, JSObject* head)
 {
-    static_cast<StructureChain*>(cell)->StructureChain::~StructureChain();
+    // FIXME: Make StructureChain::create fail for large chain. Caching large chain is not so profitable.
+    // By making the size <= UINT16_MAX, we can store length in a high bits of auxiliary pointer.
+    // https://bugs.webkit.org/show_bug.cgi?id=200290
+    size_t size = 0;
+    for (JSObject* current = head; current; current = current->structure(vm)->storedPrototypeObject(current))
+        ++size;
+    ++size; // Sentinel nullptr.
+    WriteBarrier<Structure>* vector = static_cast<WriteBarrier<Structure>*>(vm.jsValueGigacageAuxiliarySpace.allocateNonVirtual(vm, (Checked<size_t>(size) * sizeof(WriteBarrier<Structure>)).unsafeGet(), nullptr, AllocationFailureMode::Assert));
+    for (size_t i = 0; i < size; ++i)
+        vector[i].clear();
+    StructureChain* chain = new (NotNull, allocateCell<StructureChain>(vm.heap)) StructureChain(vm, vm.structureChainStructure.get(), vector);
+    chain->finishCreation(vm, head);
+    return chain;
+}
+
+void StructureChain::finishCreation(VM& vm, JSObject* head)
+{
+    Base::finishCreation(vm);
+    size_t i = 0;
+    for (JSObject* current = head; current; current = current->structure(vm)->storedPrototypeObject(current))
+        m_vector.get()[i++].set(vm, this, current->structure(vm));
 }
 
 void StructureChain::visitChildren(JSCell* cell, SlotVisitor& visitor)
@@ -49,11 +70,9 @@ void StructureChain::visitChildren(JSCell* cell, SlotVisitor& visitor)
     StructureChain* thisObject = jsCast<StructureChain*>(cell);
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
     Base::visitChildren(thisObject, visitor);
-    if (WriteBarrier<Structure>* vector = thisObject->m_vector.get()) {
-        size_t i = 0;
-        while (vector[i])
-            visitor.append(vector[i++]);
-    }
+    visitor.markAuxiliary(thisObject->m_vector.get());
+    for (auto* current = thisObject->m_vector.get(); *current; ++current)
+        visitor.append(*current);
 }
 
 } // namespace JSC
index d62d106..0ed40d2 100644 (file)
@@ -40,15 +40,10 @@ class StructureChain final : public JSCell {
     friend class JIT;
 
 public:
-    typedef JSCell Base;
+    using Base = JSCell;
     static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
 
-    static StructureChain* create(VM& vm, JSObject* head)
-    { 
-        StructureChain* chain = new (NotNull, allocateCell<StructureChain>(vm.heap)) StructureChain(vm, vm.structureChainStructure.get());
-        chain->finishCreation(vm, head);
-        return chain;
-    }
+    static StructureChain* create(VM&, JSObject*);
     WriteBarrier<Structure>* head() { return m_vector.get(); }
     static void visitChildren(JSCell*, SlotVisitor&);
 
@@ -59,34 +54,13 @@ public:
 
     DECLARE_INFO;
 
-    static const bool needsDestruction = true;
-    static void destroy(JSCell*);
-
-protected:
-    void finishCreation(VM& vm, JSObject* head)
-    {
-        Base::finishCreation(vm);
-
-        size_t size = 0;
-        for (JSObject* current = head; current; current = current->structure(vm)->storedPrototypeObject(current))
-            ++size;
-
-        auto vector = makeUniqueArray<WriteBarrier<Structure>>(size + 1);
-
-        size_t i = 0;
-        for (JSObject* current = head; current; current = current->structure(vm)->storedPrototypeObject(current))
-            vector[i++].set(vm, this, current->structure(vm));
-        
-        vm.heap.mutatorFence();
-        m_vector = WTFMove(vector);
-        vm.heap.writeBarrier(this);
-    }
-
 private:
     friend class LLIntOffsetsExtractor;
 
-    StructureChain(VM&, Structure*);
-    UniqueArray<WriteBarrier<Structure>> m_vector;
+    void finishCreation(VM&, JSObject* head);
+
+    StructureChain(VM&, Structure*, WriteBarrier<Structure>*);
+    AuxiliaryBarrier<WriteBarrier<Structure>*> m_vector;
 };
 
 } // namespace JSC