Title: [241571] trunk
Revision
241571
Author
sbar...@apple.com
Date
2019-02-14 16:06:30 -0800 (Thu, 14 Feb 2019)

Log Message

Cache the results of BytecodeGenerator::getVariablesUnderTDZ
https://bugs.webkit.org/show_bug.cgi?id=194583
<rdar://problem/48028140>

Reviewed by Yusuke Suzuki.

JSTests:

* microbenchmarks/cache-get-variables-under-tdz-in-bytecode-generator.js: Added.

Source/_javascript_Core:

This patch makes it so that getVariablesUnderTDZ caches a result of
CompactVariableMap::Handle. getVariablesUnderTDZ is costly when
it's called in an environment where there are a lot of variables.
This patch makes it so we cache its results. This is profitable when
getVariablesUnderTDZ is called repeatedly with the same environment
state. This is common since we call this every time we encounter a
function definition/_expression_ node.

* builtins/BuiltinExecutables.cpp:
(JSC::BuiltinExecutables::createExecutable):
* bytecode/UnlinkedFunctionExecutable.cpp:
(JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
* bytecode/UnlinkedFunctionExecutable.h:
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::popLexicalScopeInternal):
(JSC::BytecodeGenerator::liftTDZCheckIfPossible):
(JSC::BytecodeGenerator::pushTDZVariables):
(JSC::BytecodeGenerator::getVariablesUnderTDZ):
(JSC::BytecodeGenerator::restoreTDZStack):
* bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::makeFunction):
* parser/VariableEnvironment.cpp:
(JSC::CompactVariableMap::Handle::Handle):
(JSC::CompactVariableMap::Handle::operator=):
* parser/VariableEnvironment.h:
(JSC::CompactVariableMap::Handle::operator bool const):
* runtime/CodeCache.cpp:
(JSC::CodeCache::getUnlinkedGlobalFunctionExecutable):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (241570 => 241571)


--- trunk/JSTests/ChangeLog	2019-02-15 00:01:53 UTC (rev 241570)
+++ trunk/JSTests/ChangeLog	2019-02-15 00:06:30 UTC (rev 241571)
@@ -1,3 +1,13 @@
+2019-02-14  Saam Barati  <sbar...@apple.com>
+
+        Cache the results of BytecodeGenerator::getVariablesUnderTDZ
+        https://bugs.webkit.org/show_bug.cgi?id=194583
+        <rdar://problem/48028140>
+
+        Reviewed by Yusuke Suzuki.
+
+        * microbenchmarks/cache-get-variables-under-tdz-in-bytecode-generator.js: Added.
+
 2019-02-08  Yusuke Suzuki  <ysuz...@apple.com>
 
         [JSC] String.fromCharCode's slow path always generates 16bit string

Added: trunk/JSTests/microbenchmarks/cache-get-variables-under-tdz-in-bytecode-generator.js (0 => 241571)


--- trunk/JSTests/microbenchmarks/cache-get-variables-under-tdz-in-bytecode-generator.js	                        (rev 0)
+++ trunk/JSTests/microbenchmarks/cache-get-variables-under-tdz-in-bytecode-generator.js	2019-02-15 00:06:30 UTC (rev 241571)
@@ -0,0 +1,20 @@
+//@ runDefault
+
+let script = "(() => {";
+for (let i = 0; i < 1000; ++i) {
+    script += `let x${i} = ${i};\n`;
+}
+
+for (let i = 0; i < 1000; ++i) {
+    script += `function f${i}() { return "foo"; }\n`;
+}
+
+script += "})();"
+
+let start = Date.now();
+for (let i = 0; i < 10; ++i) {
+    $.evalScript(`cacheBust = ${Math.random()}; ${script}`);
+}
+
+if (false)
+    print(Date.now() - start);

Modified: trunk/Source/_javascript_Core/ChangeLog (241570 => 241571)


--- trunk/Source/_javascript_Core/ChangeLog	2019-02-15 00:01:53 UTC (rev 241570)
+++ trunk/Source/_javascript_Core/ChangeLog	2019-02-15 00:06:30 UTC (rev 241571)
@@ -1,3 +1,40 @@
+2019-02-14  Saam Barati  <sbar...@apple.com>
+
+        Cache the results of BytecodeGenerator::getVariablesUnderTDZ
+        https://bugs.webkit.org/show_bug.cgi?id=194583
+        <rdar://problem/48028140>
+
+        Reviewed by Yusuke Suzuki.
+
+        This patch makes it so that getVariablesUnderTDZ caches a result of
+        CompactVariableMap::Handle. getVariablesUnderTDZ is costly when
+        it's called in an environment where there are a lot of variables.
+        This patch makes it so we cache its results. This is profitable when
+        getVariablesUnderTDZ is called repeatedly with the same environment
+        state. This is common since we call this every time we encounter a
+        function definition/_expression_ node.
+
+        * builtins/BuiltinExecutables.cpp:
+        (JSC::BuiltinExecutables::createExecutable):
+        * bytecode/UnlinkedFunctionExecutable.cpp:
+        (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
+        * bytecode/UnlinkedFunctionExecutable.h:
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::popLexicalScopeInternal):
+        (JSC::BytecodeGenerator::liftTDZCheckIfPossible):
+        (JSC::BytecodeGenerator::pushTDZVariables):
+        (JSC::BytecodeGenerator::getVariablesUnderTDZ):
+        (JSC::BytecodeGenerator::restoreTDZStack):
+        * bytecompiler/BytecodeGenerator.h:
+        (JSC::BytecodeGenerator::makeFunction):
+        * parser/VariableEnvironment.cpp:
+        (JSC::CompactVariableMap::Handle::Handle):
+        (JSC::CompactVariableMap::Handle::operator=):
+        * parser/VariableEnvironment.h:
+        (JSC::CompactVariableMap::Handle::operator bool const):
+        * runtime/CodeCache.cpp:
+        (JSC::CodeCache::getUnlinkedGlobalFunctionExecutable):
+
 2019-02-14  Yusuke Suzuki  <ysuz...@apple.com>
 
         [JSC] Non-JIT entrypoints should share NativeJITCode per entrypoint type

Modified: trunk/Source/_javascript_Core/builtins/BuiltinExecutables.cpp (241570 => 241571)


--- trunk/Source/_javascript_Core/builtins/BuiltinExecutables.cpp	2019-02-15 00:01:53 UTC (rev 241570)
+++ trunk/Source/_javascript_Core/builtins/BuiltinExecutables.cpp	2019-02-15 00:06:30 UTC (rev 241571)
@@ -258,7 +258,7 @@
     }
 
     VariableEnvironment dummyTDZVariables;
-    UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, &metadata, kind, constructAbility, JSParserScriptMode::Classic, dummyTDZVariables, DerivedContextType::None, isBuiltinDefaultClassConstructor);
+    UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, &metadata, kind, constructAbility, JSParserScriptMode::Classic, vm.m_compactVariableMap->get(dummyTDZVariables), DerivedContextType::None, isBuiltinDefaultClassConstructor);
     return functionExecutable;
 }
 

Modified: trunk/Source/_javascript_Core/bytecode/UnlinkedFunctionExecutable.cpp (241570 => 241571)


--- trunk/Source/_javascript_Core/bytecode/UnlinkedFunctionExecutable.cpp	2019-02-15 00:01:53 UTC (rev 241570)
+++ trunk/Source/_javascript_Core/bytecode/UnlinkedFunctionExecutable.cpp	2019-02-15 00:06:30 UTC (rev 241571)
@@ -78,7 +78,7 @@
     return result;
 }
 
-UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM* vm, Structure* structure, const SourceCode& parentSource, FunctionMetadataNode* node, UnlinkedFunctionKind kind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, VariableEnvironment& parentScopeTDZVariables, DerivedContextType derivedContextType, bool isBuiltinDefaultClassConstructor)
+UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM* vm, Structure* structure, const SourceCode& parentSource, FunctionMetadataNode* node, UnlinkedFunctionKind kind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, CompactVariableMap::Handle parentScopeTDZVariables, DerivedContextType derivedContextType, bool isBuiltinDefaultClassConstructor)
     : Base(*vm, structure)
     , m_firstLineOffset(node->firstLine() - parentSource.firstLine().oneBasedInt())
     , m_lineCount(node->lastLine() - node->firstLine())
@@ -107,7 +107,7 @@
     , m_ecmaName(node->ecmaName())
     , m_inferredName(node->inferredName())
     , m_classSource(node->classSource())
-    , m_parentScopeTDZVariables(vm->m_compactVariableMap->get(parentScopeTDZVariables))
+    , m_parentScopeTDZVariables(WTFMove(parentScopeTDZVariables))
 {
     // Make sure these bitfields are adequately wide.
     ASSERT(m_constructAbility == static_cast<unsigned>(constructAbility));

Modified: trunk/Source/_javascript_Core/bytecode/UnlinkedFunctionExecutable.h (241570 => 241571)


--- trunk/Source/_javascript_Core/bytecode/UnlinkedFunctionExecutable.h	2019-02-15 00:01:53 UTC (rev 241570)
+++ trunk/Source/_javascript_Core/bytecode/UnlinkedFunctionExecutable.h	2019-02-15 00:06:30 UTC (rev 241571)
@@ -67,7 +67,7 @@
         return &vm.unlinkedFunctionExecutableSpace.space;
     }
 
-    static UnlinkedFunctionExecutable* create(VM* vm, const SourceCode& source, FunctionMetadataNode* node, UnlinkedFunctionKind unlinkedFunctionKind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, VariableEnvironment& parentScopeTDZVariables, DerivedContextType derivedContextType, bool isBuiltinDefaultClassConstructor = false)
+    static UnlinkedFunctionExecutable* create(VM* vm, const SourceCode& source, FunctionMetadataNode* node, UnlinkedFunctionKind unlinkedFunctionKind, ConstructAbility constructAbility, JSParserScriptMode scriptMode, CompactVariableMap::Handle parentScopeTDZVariables, DerivedContextType derivedContextType, bool isBuiltinDefaultClassConstructor = false)
     {
         UnlinkedFunctionExecutable* instance = new (NotNull, allocateCell<UnlinkedFunctionExecutable>(vm->heap))
             UnlinkedFunctionExecutable(vm, vm->unlinkedFunctionExecutableStructure.get(), source, node, unlinkedFunctionKind, constructAbility, scriptMode, parentScopeTDZVariables, derivedContextType, isBuiltinDefaultClassConstructor);
@@ -152,7 +152,7 @@
     void setSourceMappingURLDirective(const String& sourceMappingURL) { m_sourceMappingURLDirective = sourceMappingURL; }
 
 private:
-    UnlinkedFunctionExecutable(VM*, Structure*, const SourceCode&, FunctionMetadataNode*, UnlinkedFunctionKind, ConstructAbility, JSParserScriptMode, VariableEnvironment&,  JSC::DerivedContextType, bool isBuiltinDefaultClassConstructor);
+    UnlinkedFunctionExecutable(VM*, Structure*, const SourceCode&, FunctionMetadataNode*, UnlinkedFunctionKind, ConstructAbility, JSParserScriptMode, CompactVariableMap::Handle,  JSC::DerivedContextType, bool isBuiltinDefaultClassConstructor);
     UnlinkedFunctionExecutable(Decoder&, VariableEnvironment&, const CachedFunctionExecutable&);
 
     unsigned m_firstLineOffset;

Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (241570 => 241571)


--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp	2019-02-15 00:01:53 UTC (rev 241570)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp	2019-02-15 00:06:30 UTC (rev 241571)
@@ -2195,6 +2195,7 @@
     }
 
     m_TDZStack.removeLast();
+    m_cachedVariablesUnderTDZ = { };
 }
 
 void BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode* node, RegisterID* loopSymbolTable)
@@ -2814,8 +2815,10 @@
     for (unsigned i = m_TDZStack.size(); i--;) {
         auto iter = m_TDZStack[i].find(identifier);
         if (iter != m_TDZStack[i].end()) {
-            if (iter->value == TDZNecessityLevel::Optimize)
+            if (iter->value == TDZNecessityLevel::Optimize) {
+                m_cachedVariablesUnderTDZ = { };
                 iter->value = TDZNecessityLevel::NotNeeded;
+            }
             break;
         }
     }
@@ -2840,10 +2843,14 @@
         map.add(entry.key, entry.value.isFunction() ? TDZNecessityLevel::NotNeeded : level);
 
     m_TDZStack.append(WTFMove(map));
+    m_cachedVariablesUnderTDZ = { };
 }
 
-void BytecodeGenerator::getVariablesUnderTDZ(VariableEnvironment& result)
+CompactVariableMap::Handle BytecodeGenerator::getVariablesUnderTDZ()
 {
+    if (m_cachedVariablesUnderTDZ)
+        return m_cachedVariablesUnderTDZ;
+
     // We keep track of variablesThatDontNeedTDZ in this algorithm to prevent
     // reporting that "x" is under TDZ if this function is called at "...".
     //
@@ -2854,18 +2861,21 @@
     //         }
     //         let x;
     //     }
-    //
     SmallPtrSet<UniquedStringImpl*, 16> variablesThatDontNeedTDZ;
+    VariableEnvironment environment;
     for (unsigned i = m_TDZStack.size(); i--; ) {
         auto& map = m_TDZStack[i];
         for (auto& entry : map)  {
             if (entry.value != TDZNecessityLevel::NotNeeded) {
                 if (!variablesThatDontNeedTDZ.contains(entry.key.get()))
-                    result.add(entry.key.get());
+                    environment.add(entry.key.get());
             } else
                 variablesThatDontNeedTDZ.add(entry.key.get());
         }
     }
+
+    m_cachedVariablesUnderTDZ = m_vm->m_compactVariableMap->get(environment);
+    return m_cachedVariablesUnderTDZ;
 }
 
 void BytecodeGenerator::preserveTDZStack(BytecodeGenerator::PreservedTDZStack& preservedStack)
@@ -2876,6 +2886,7 @@
 void BytecodeGenerator::restoreTDZStack(const BytecodeGenerator::PreservedTDZStack& preservedStack)
 {
     m_TDZStack = preservedStack.m_preservedTDZStack;
+    m_cachedVariablesUnderTDZ = { };
 }
 
 RegisterID* BytecodeGenerator::emitNewObject(RegisterID* dst)

Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h (241570 => 241571)


--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h	2019-02-15 00:01:53 UTC (rev 241570)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h	2019-02-15 00:06:30 UTC (rev 241571)
@@ -1137,8 +1137,7 @@
                     newDerivedContextType = DerivedContextType::DerivedMethodContext;
             }
 
-            VariableEnvironment variablesUnderTDZ;
-            getVariablesUnderTDZ(variablesUnderTDZ);
+            CompactVariableMap::Handle variablesUnderTDZ = getVariablesUnderTDZ();
 
             // FIXME: These flags, ParserModes and propagation to XXXCodeBlocks should be reorganized.
             // https://bugs.webkit.org/show_bug.cgi?id=151547
@@ -1147,10 +1146,10 @@
             if (parseMode == SourceParseMode::MethodMode && metadata->constructorKind() != ConstructorKind::None)
                 constructAbility = ConstructAbility::CanConstruct;
 
-            return UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), metadata, isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction, constructAbility, scriptMode(), variablesUnderTDZ, newDerivedContextType);
+            return UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), metadata, isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction, constructAbility, scriptMode(), WTFMove(variablesUnderTDZ), newDerivedContextType);
         }
 
-        void getVariablesUnderTDZ(VariableEnvironment&);
+        CompactVariableMap::Handle getVariablesUnderTDZ();
 
         RegisterID* emitConstructVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall);
         template<typename CallOp>
@@ -1313,6 +1312,8 @@
         bool m_needsToUpdateArrowFunctionContext;
         DerivedContextType m_derivedContextType { DerivedContextType::None };
 
+        CompactVariableMap::Handle m_cachedVariablesUnderTDZ;
+
         using CatchEntry = std::tuple<TryData*, VirtualRegister, VirtualRegister>;
         Vector<CatchEntry> m_catchesToEmit;
     };

Modified: trunk/Source/_javascript_Core/parser/VariableEnvironment.cpp (241570 => 241571)


--- trunk/Source/_javascript_Core/parser/VariableEnvironment.cpp	2019-02-15 00:01:53 UTC (rev 241570)
+++ trunk/Source/_javascript_Core/parser/VariableEnvironment.cpp	2019-02-15 00:06:30 UTC (rev 241571)
@@ -183,4 +183,27 @@
     }
 }
 
+CompactVariableMap::Handle::Handle(const CompactVariableMap::Handle& other)
+{
+    *this = other;
+}
+
+CompactVariableMap::Handle& CompactVariableMap::Handle::operator=(const Handle& other)
+{
+    m_map = other.m_map;
+    m_environment = other.m_environment;
+
+    if (!m_map) {
+        ASSERT(!m_environment);
+        // This happens if `other` were moved into a different handle.
+        return *this;
+    }
+
+    auto iter = m_map->m_map.find(CompactVariableMapKey { *m_environment });
+    RELEASE_ASSERT(iter != m_map->m_map.end());
+    ++iter->value;
+
+    return *this;
+}
+
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/parser/VariableEnvironment.h (241570 => 241571)


--- trunk/Source/_javascript_Core/parser/VariableEnvironment.h	2019-02-15 00:01:53 UTC (rev 241570)
+++ trunk/Source/_javascript_Core/parser/VariableEnvironment.h	2019-02-15 00:06:30 UTC (rev 241571)
@@ -204,8 +204,9 @@
 class CompactVariableMap : public RefCounted<CompactVariableMap> {
 public:
     class Handle {
-        WTF_MAKE_NONCOPYABLE(Handle); // If we wanted to make this copyable, we'd need to do a hashtable lookup and bump the reference count of the map entry.
     public:
+        Handle() = default;
+
         Handle(CompactVariableEnvironment& environment, CompactVariableMap& map)
             : m_environment(&environment)
             , m_map(&map)
@@ -218,8 +219,14 @@
             ASSERT(!other.m_map);
             other.m_environment = nullptr;
         }
+
+        Handle(const Handle&);
+        Handle& operator=(const Handle&);
+
         ~Handle();
 
+        explicit operator bool() const { return !!m_map; }
+
         const CompactVariableEnvironment& environment() const
         {
             return *m_environment;
@@ -226,7 +233,7 @@
         }
 
     private:
-        CompactVariableEnvironment* m_environment;
+        CompactVariableEnvironment* m_environment { nullptr };
         RefPtr<CompactVariableMap> m_map;
     };
 

Modified: trunk/Source/_javascript_Core/runtime/CodeCache.cpp (241570 => 241571)


--- trunk/Source/_javascript_Core/runtime/CodeCache.cpp	2019-02-15 00:01:53 UTC (rev 241570)
+++ trunk/Source/_javascript_Core/runtime/CodeCache.cpp	2019-02-15 00:06:30 UTC (rev 241571)
@@ -147,7 +147,7 @@
     // in the global lexical environment, which we always TDZ check accesses from.
     VariableEnvironment emptyTDZVariables;
     ConstructAbility constructAbility = constructAbilityForParseMode(metadata->parseMode());
-    UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, metadata, UnlinkedNormalFunction, constructAbility, JSParserScriptMode::Classic, emptyTDZVariables, DerivedContextType::None);
+    UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, metadata, UnlinkedNormalFunction, constructAbility, JSParserScriptMode::Classic, vm.m_compactVariableMap->get(emptyTDZVariables), DerivedContextType::None);
 
     functionExecutable->setSourceURLDirective(source.provider()->sourceURL());
     functionExecutable->setSourceMappingURLDirective(source.provider()->sourceMappingURL());
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to