[JSC] Promise resolve/reject functions should be created more efficiently
authorysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 9 Sep 2019 17:52:07 +0000 (17:52 +0000)
committerysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 9 Sep 2019 17:52:07 +0000 (17:52 +0000)
https://bugs.webkit.org/show_bug.cgi?id=201488

Reviewed by Mark Lam.

JSTests:

* microbenchmarks/promise-creation-many.js: Added.
(executor):

Source/JavaScriptCore:

While r246553 fixed an important issue, it makes anonymous-builtin-function creation costly since it enforces FunctionRareData allocations.
Unfortunately, anonymous-builtin-function function can be created frequently since this type of function is used
for `resolve` and `reject` arguments of Promise's executor (e.g. `new Promise((resolve, reject) => ...)`'s resolve and reject).
Since we are now always creating FunctionRareData for these functions, this additional allocation makes promise creation slower.

In this patch, we use `isAnonymousBuiltinFunction` information for `hasReifiedName` correctly. And we propagate `isAnonymousBuiltinFunction` information
to FunctionRareData to initialize `m_hasReifiedName` correctly. Then we can avoid unnecessary FunctionRareData allocation, which makes
anonymous-builtin-function creation faster.

We can ensure that this patch does not revert r246553's fix by running JSTests/stress/builtin-private-function-name.js test.
The simple microbenchmark shows 1.7x improvement.

                                      ToT                     Patched

    promise-creation-many       45.6701+-0.1488     ^     26.8663+-1.8336        ^ definitely 1.6999x faster

* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileNewFunctionCommon):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNewFunction):
* runtime/FunctionRareData.cpp:
(JSC::FunctionRareData::create):
(JSC::FunctionRareData::FunctionRareData):
* runtime/FunctionRareData.h:
* runtime/JSFunction.cpp:
(JSC::JSFunction::finishCreation):
(JSC::JSFunction::allocateRareData):
(JSC::JSFunction::allocateAndInitializeRareData):
* runtime/JSFunctionInlines.h:
(JSC::JSFunction::hasReifiedName const):

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

JSTests/ChangeLog
JSTests/microbenchmarks/promise-creation-many.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/runtime/FunctionRareData.cpp
Source/JavaScriptCore/runtime/FunctionRareData.h
Source/JavaScriptCore/runtime/JSFunction.cpp
Source/JavaScriptCore/runtime/JSFunctionInlines.h

index 977afdd..9701b6f 100644 (file)
@@ -1,3 +1,13 @@
+2019-09-09  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] Promise resolve/reject functions should be created more efficiently
+        https://bugs.webkit.org/show_bug.cgi?id=201488
+
+        Reviewed by Mark Lam.
+
+        * microbenchmarks/promise-creation-many.js: Added.
+        (executor):
+
 2019-09-09  Zan Dobersek  <zdobersek@igalia.com>
 
         Unreviewed JSC test gardening.
diff --git a/JSTests/microbenchmarks/promise-creation-many.js b/JSTests/microbenchmarks/promise-creation-many.js
new file mode 100644 (file)
index 0000000..11aeee8
--- /dev/null
@@ -0,0 +1,6 @@
+function executor(resolve, reject) {
+}
+noInline(executor); // Ensure `resolve` and `reject`'s materialization.
+
+for (var i = 0; i < 1e6; ++i)
+    new Promise(executor);
index f832d18..eec9153 100644 (file)
@@ -1,3 +1,41 @@
+2019-09-09  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        [JSC] Promise resolve/reject functions should be created more efficiently
+        https://bugs.webkit.org/show_bug.cgi?id=201488
+
+        Reviewed by Mark Lam.
+
+        While r246553 fixed an important issue, it makes anonymous-builtin-function creation costly since it enforces FunctionRareData allocations.
+        Unfortunately, anonymous-builtin-function function can be created frequently since this type of function is used
+        for `resolve` and `reject` arguments of Promise's executor (e.g. `new Promise((resolve, reject) => ...)`'s resolve and reject).
+        Since we are now always creating FunctionRareData for these functions, this additional allocation makes promise creation slower.
+
+        In this patch, we use `isAnonymousBuiltinFunction` information for `hasReifiedName` correctly. And we propagate `isAnonymousBuiltinFunction` information
+        to FunctionRareData to initialize `m_hasReifiedName` correctly. Then we can avoid unnecessary FunctionRareData allocation, which makes
+        anonymous-builtin-function creation faster.
+
+        We can ensure that this patch does not revert r246553's fix by running JSTests/stress/builtin-private-function-name.js test.
+        The simple microbenchmark shows 1.7x improvement.
+
+                                              ToT                     Patched
+
+            promise-creation-many       45.6701+-0.1488     ^     26.8663+-1.8336        ^ definitely 1.6999x faster
+
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileNewFunctionCommon):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNewFunction):
+        * runtime/FunctionRareData.cpp:
+        (JSC::FunctionRareData::create):
+        (JSC::FunctionRareData::FunctionRareData):
+        * runtime/FunctionRareData.h:
+        * runtime/JSFunction.cpp:
+        (JSC::JSFunction::finishCreation):
+        (JSC::JSFunction::allocateRareData):
+        (JSC::JSFunction::allocateAndInitializeRareData):
+        * runtime/JSFunctionInlines.h:
+        (JSC::JSFunction::hasReifiedName const):
+
 2019-09-07  Mark Lam  <mark.lam@apple.com>
 
         performJITMemcpy() source buffer should not be in the Gigacage.
index 27cc1b4..528e113 100644 (file)
@@ -7212,29 +7212,7 @@ void SpeculativeJIT::compileNewFunctionCommon(GPRReg resultGPR, RegisteredStruct
     m_jit.storePtr(scopeGPR, JITCompiler::Address(resultGPR, JSFunction::offsetOfScopeChain()));
     m_jit.storePtr(TrustedImmPtr::weakPointer(m_jit.graph(), executable), JITCompiler::Address(resultGPR, JSFunction::offsetOfExecutable()));
     m_jit.storePtr(TrustedImmPtr(nullptr), JITCompiler::Address(resultGPR, JSFunction::offsetOfRareData()));
-    
-    if (executable->isAnonymousBuiltinFunction()) {
-        VM& vm = this->vm();
-        m_jit.mutatorFence(vm);
-        GPRTemporary allocator(this);
-        Allocator allocatorValue = allocatorForNonVirtualConcurrently<FunctionRareData>(vm, sizeof(FunctionRareData), AllocatorForMode::AllocatorIfExists);
-        emitAllocateJSCell(scratch1GPR, JITAllocator::constant(allocatorValue), allocator.gpr(), TrustedImmPtr(m_jit.graph().registerStructure(vm.functionRareDataStructure.get())), scratch2GPR, slowPath);
-
-        ptrdiff_t objectAllocationProfileOffset = FunctionRareData::offsetOfObjectAllocationProfile();
-        m_jit.storePtr(TrustedImmPtr(nullptr), JITCompiler::Address(scratch1GPR, objectAllocationProfileOffset + ObjectAllocationProfileWithPrototype::offsetOfAllocator()));
-        m_jit.storePtr(TrustedImmPtr(nullptr), JITCompiler::Address(scratch1GPR, objectAllocationProfileOffset + ObjectAllocationProfileWithPrototype::offsetOfStructure()));
-        m_jit.storePtr(TrustedImmPtr(nullptr), JITCompiler::Address(scratch1GPR, objectAllocationProfileOffset + ObjectAllocationProfileWithPrototype::offsetOfPrototype()));
-        m_jit.storePtr(TrustedImmPtr(0x1), JITCompiler::Address(scratch1GPR, FunctionRareData::offsetOfAllocationProfileWatchpointSet()));
-        m_jit.storePtr(TrustedImmPtr(nullptr), JITCompiler::Address(scratch1GPR, FunctionRareData::offsetOfInternalFunctionAllocationProfile() + InternalFunctionAllocationProfile::offsetOfStructure()));
-        m_jit.storePtr(TrustedImmPtr(nullptr), JITCompiler::Address(scratch1GPR, FunctionRareData::offsetOfBoundFunctionStructure()));
-        m_jit.storePtr(TrustedImmPtr(nullptr), JITCompiler::Address(scratch1GPR, FunctionRareData::offsetOfAllocationProfileClearingWatchpoint()));
-        m_jit.store8(TrustedImm32(0), JITCompiler::Address(scratch1GPR, FunctionRareData::offsetOfHasReifiedLength()));
-        m_jit.store8(TrustedImm32(1), JITCompiler::Address(scratch1GPR, FunctionRareData::offsetOfHasReifiedName()));
-        m_jit.mutatorFence(vm);
-        m_jit.storePtr(scratch1GPR, JITCompiler::Address(resultGPR, JSFunction::offsetOfRareData()));
-    } else
-        m_jit.mutatorFence(vm());
-
+    m_jit.mutatorFence(vm());
 }
 
 void SpeculativeJIT::compileNewFunction(Node* node)
index 8c091c6..3c1f288 100644 (file)
@@ -5575,25 +5575,7 @@ private:
         m_out.storePtr(scope, fastObject, m_heaps.JSFunction_scope);
         m_out.storePtr(weakPointer(executable), fastObject, m_heaps.JSFunction_executable);
         m_out.storePtr(m_out.intPtrZero, fastObject, m_heaps.JSFunction_rareData);
-        
-        VM& vm = this->vm();
-        if (executable->isAnonymousBuiltinFunction()) {
-            mutatorFence();
-            Allocator allocator = allocatorForNonVirtualConcurrently<FunctionRareData>(vm, sizeof(FunctionRareData), AllocatorForMode::AllocatorIfExists);
-            LValue rareData = allocateCell(m_out.constIntPtr(allocator.localAllocator()), vm.functionRareDataStructure.get(), slowPath);
-            m_out.storePtr(m_out.intPtrZero, rareData, m_heaps.FunctionRareData_allocator);
-            m_out.storePtr(m_out.intPtrZero, rareData, m_heaps.FunctionRareData_structure);
-            m_out.storePtr(m_out.intPtrZero, rareData, m_heaps.FunctionRareData_prototype);
-            m_out.storePtr(m_out.intPtrOne, rareData, m_heaps.FunctionRareData_allocationProfileWatchpointSet);
-            m_out.storePtr(m_out.intPtrZero, rareData, m_heaps.FunctionRareData_internalFunctionAllocationProfile_structure);
-            m_out.storePtr(m_out.intPtrZero, rareData, m_heaps.FunctionRareData_boundFunctionStructure);
-            m_out.storePtr(m_out.intPtrZero, rareData, m_heaps.FunctionRareData_allocationProfileClearingWatchpoint);
-            m_out.store32As8(m_out.int32One, rareData, m_heaps.FunctionRareData_hasReifiedName);
-            m_out.store32As8(m_out.int32Zero, rareData, m_heaps.FunctionRareData_hasReifiedLength);
-            mutatorFence();
-            m_out.storePtr(rareData, fastObject, m_heaps.JSFunction_rareData);
-        } else
-            mutatorFence();
+        mutatorFence();
 
         ValueFromBlock fastResult = m_out.anchor(fastObject);
         m_out.jump(continuation);
@@ -5602,6 +5584,7 @@ private:
 
         Vector<LValue> slowPathArguments;
         slowPathArguments.append(scope);
+        VM& vm = this->vm();
         LValue callResult = lazySlowPath(
             [=, &vm] (const Vector<Location>& locations) -> RefPtr<LazySlowPath::Generator> {
                 auto* operation = operationNewFunctionWithInvalidatedReallocationWatchpoint;
index 1306257..9db7358 100644 (file)
@@ -33,9 +33,9 @@ namespace JSC {
 
 const ClassInfo FunctionRareData::s_info = { "FunctionRareData", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(FunctionRareData) };
 
-FunctionRareData* FunctionRareData::create(VM& vm)
+FunctionRareData* FunctionRareData::create(VM& vm, JSFunction* function)
 {
-    FunctionRareData* rareData = new (NotNull, allocateCell<FunctionRareData>(vm.heap)) FunctionRareData(vm);
+    FunctionRareData* rareData = new (NotNull, allocateCell<FunctionRareData>(vm.heap)) FunctionRareData(vm, function);
     rareData->finishCreation(vm);
     return rareData;
 }
@@ -62,13 +62,14 @@ void FunctionRareData::visitChildren(JSCell* cell, SlotVisitor& visitor)
     visitor.append(rareData->m_boundFunctionStructure);
 }
 
-FunctionRareData::FunctionRareData(VM& vm)
+FunctionRareData::FunctionRareData(VM& vm, JSFunction* function)
     : Base(vm, vm.functionRareDataStructure.get())
     , m_objectAllocationProfile()
     // We initialize blind so that changes to the prototype after function creation but before
     // the first allocation don't disable optimizations. This isn't super important, since the
     // function is unlikely to allocate a rare data until the first allocation anyway.
     , m_allocationProfileWatchpointSet(ClearWatchpoint)
+    , m_hasReifiedName(function->isAnonymousBuiltinFunction())
 {
 }
 
index b01606f..4ee2d48 100644 (file)
@@ -50,7 +50,7 @@ public:
     typedef JSCell Base;
     static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
 
-    static FunctionRareData* create(VM&);
+    static FunctionRareData* create(VM&, JSFunction*);
 
     static const bool needsDestruction = true;
     static void destroy(JSCell*);
@@ -119,7 +119,7 @@ public:
     class AllocationProfileClearingWatchpoint;
 
 protected:
-    FunctionRareData(VM&);
+    FunctionRareData(VM&, JSFunction*);
     ~FunctionRareData();
 
 private:
index 92bd1b8..2cae44d 100644 (file)
@@ -125,10 +125,6 @@ void JSFunction::finishCreation(VM& vm)
     Base::finishCreation(vm);
     ASSERT(jsDynamicCast<JSFunction*>(vm, this));
     ASSERT(type() == JSFunctionType);
-    if (isAnonymousBuiltinFunction()) {
-        // This is anonymous builtin function.
-        rareData(vm)->setHasReifiedName();
-    }
 }
 
 void JSFunction::finishCreation(VM& vm, NativeExecutable* executable, int length, const String& name)
@@ -146,7 +142,7 @@ void JSFunction::finishCreation(VM& vm, NativeExecutable* executable, int length
 FunctionRareData* JSFunction::allocateRareData(VM& vm)
 {
     ASSERT(!m_rareData);
-    FunctionRareData* rareData = FunctionRareData::create(vm);
+    FunctionRareData* rareData = FunctionRareData::create(vm, this);
 
     // A DFG compilation thread may be trying to read the rare data
     // We want to ensure that it sees it properly allocated
@@ -186,7 +182,7 @@ FunctionRareData* JSFunction::allocateAndInitializeRareData(ExecState* exec, siz
     ASSERT(canUseAllocationProfile());
     VM& vm = exec->vm();
     JSObject* prototype = prototypeForConstruction(vm, exec);
-    FunctionRareData* rareData = FunctionRareData::create(vm);
+    FunctionRareData* rareData = FunctionRareData::create(vm, this);
     rareData->initializeObjectAllocationProfile(vm, globalObject(vm), prototype, inlineCapacity, this);
 
     // A DFG compilation thread may be trying to read the rare data
index 71e8bd1..1872684 100644 (file)
@@ -110,7 +110,11 @@ inline bool JSFunction::hasReifiedLength() const
 
 inline bool JSFunction::hasReifiedName() const
 {
-    return m_rareData ? m_rareData->hasReifiedName() : false;
+    if (m_rareData)
+        return m_rareData->hasReifiedName();
+    if (isAnonymousBuiltinFunction())
+        return true;
+    return false;
 }
 
 inline bool JSFunction::canUseAllocationProfile()