Use LLInt profiling to rule out generating an IC for get_by_val
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 23 Nov 2019 04:45:14 +0000 (04:45 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 23 Nov 2019 04:45:14 +0000 (04:45 +0000)
https://bugs.webkit.org/show_bug.cgi?id=204536

Reviewed by Yusuke Suzuki.

When I landed the get_by_val polymorphic inline caching patch, the prepack
benchmark in JetStream2 slowed down by 10%. Through some analysis, I found
out that we were slowing down because of the time we spent in the JITs
actually generating inline caches. This patch skips generating an inline
cache when it seems like it won't be profitable. The heuristic for doing this
is simple:
- If we see more than 4 identifiers in the LLInt, we won't generate an IC
in the upper tiers.
- If we see a non-identifier JSString in the LLInt, we won't generate an IC
in the upper tiers.

This patch recovers the regression on prepack.

* bytecode/BytecodeList.rb:
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* generator/main.rb:
* heap/TinyBloomFilter.h:
(JSC::TinyBloomFilter::bits const):
(JSC::TinyBloomFilter::TinyBloomFilter):
* jit/JIT.h:
* jit/JITOperations.cpp:
* jit/JITOperations.h:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emit_op_get_by_val):
(JSC::JIT::emitSlow_op_get_by_val):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* runtime/Operations.h:
(JSC::getByValWithIndex):
* runtime/OptionsList.h:

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

17 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/bytecode/BytecodeList.rb
Source/JavaScriptCore/bytecode/GetByValHistory.h [new file with mode: 0644]
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/generator/DSL.rb
Source/JavaScriptCore/heap/TinyBloomFilter.h
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITInlines.h
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/jit/JITPropertyAccess.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
Source/JavaScriptCore/runtime/Operations.h
Source/JavaScriptCore/runtime/OptionsList.h

index 4809d3c..644ecfe 100644 (file)
@@ -1,3 +1,44 @@
+2019-11-22  Saam Barati  <sbarati@apple.com>
+
+        Use LLInt profiling to rule out generating an IC for get_by_val
+        https://bugs.webkit.org/show_bug.cgi?id=204536
+
+        Reviewed by Yusuke Suzuki.
+
+        When I landed the get_by_val polymorphic inline caching patch, the prepack
+        benchmark in JetStream2 slowed down by 10%. Through some analysis, I found
+        out that we were slowing down because of the time we spent in the JITs
+        actually generating inline caches. This patch skips generating an inline
+        cache when it seems like it won't be profitable. The heuristic for doing this
+        is simple:
+        - If we see more than 4 identifiers in the LLInt, we won't generate an IC
+        in the upper tiers.
+        - If we see a non-identifier JSString in the LLInt, we won't generate an IC
+        in the upper tiers.
+        
+        This patch recovers the regression on prepack.
+
+        * bytecode/BytecodeList.rb:
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * generator/main.rb:
+        * heap/TinyBloomFilter.h:
+        (JSC::TinyBloomFilter::bits const):
+        (JSC::TinyBloomFilter::TinyBloomFilter):
+        * jit/JIT.h:
+        * jit/JITOperations.cpp:
+        * jit/JITOperations.h:
+        * jit/JITPropertyAccess.cpp:
+        (JSC::JIT::emit_op_get_by_val):
+        (JSC::JIT::emitSlow_op_get_by_val):
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        * runtime/Operations.h:
+        (JSC::getByValWithIndex):
+        * runtime/OptionsList.h:
+
 2019-11-22  Per Arne Vollan  <pvollan@apple.com>
 
         Fix compile error in release mode
index 465ad85..a41668b 100644 (file)
                451539B912DC994500EF7AC4 /* Yarr.h in Headers */ = {isa = PBXBuildFile; fileRef = 451539B812DC994500EF7AC4 /* Yarr.h */; settings = {ATTRIBUTES = (Private, ); }; };
                473DA4A4764C45FE871B0485 /* DefinePropertyAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = 169948EDE68D4054B01EF797 /* DefinePropertyAttributes.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4BAA07CEB81F49A296E02203 /* WasmSignatureInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 30A5F403F11C4F599CD596D5 /* WasmSignatureInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               520D99F12388CC81000509A3 /* GetByValHistory.h in Headers */ = {isa = PBXBuildFile; fileRef = 520D99F02388CC78000509A3 /* GetByValHistory.h */; };
                521131F71F82BF14007CCEEE /* PolyProtoAccessChain.h in Headers */ = {isa = PBXBuildFile; fileRef = 521131F61F82BF11007CCEEE /* PolyProtoAccessChain.h */; settings = {ATTRIBUTES = (Private, ); }; };
                521322461ECBCE8200F65615 /* WebAssemblyFunctionBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 521322441ECBCE8200F65615 /* WebAssemblyFunctionBase.h */; };
                522927D5235FD0B9005CB169 /* GCMemoryOperations.h in Headers */ = {isa = PBXBuildFile; fileRef = 5272987B235FC8BA005C982C /* GCMemoryOperations.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4CE978E385A8498199052153 /* ModuleNamespaceAccessCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModuleNamespaceAccessCase.h; sourceTree = "<group>"; };
                51F0EB6105C86C6B00E6DF1B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
                51F0EC0705C86C9A00E6DF1B /* libobjc.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libobjc.dylib; path = /usr/lib/libobjc.dylib; sourceTree = "<absolute>"; };
+               520D99F02388CC78000509A3 /* GetByValHistory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GetByValHistory.h; sourceTree = "<group>"; };
                521131F51F82BF11007CCEEE /* PolyProtoAccessChain.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PolyProtoAccessChain.cpp; sourceTree = "<group>"; };
                521131F61F82BF11007CCEEE /* PolyProtoAccessChain.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PolyProtoAccessChain.h; sourceTree = "<group>"; };
                521322431ECBCE8200F65615 /* WebAssemblyFunctionBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebAssemblyFunctionBase.cpp; path = js/WebAssemblyFunctionBase.cpp; sourceTree = "<group>"; };
                                0F93329614CA7DC10085F3C6 /* GetByStatus.h */,
                                0F0332C118B01763005F979A /* GetByIdVariant.cpp */,
                                0F0332C218B01763005F979A /* GetByIdVariant.h */,
+                               520D99F02388CC78000509A3 /* GetByValHistory.h */,
                                14AD91081DCA92940014F9FE /* GlobalCodeBlock.h */,
                                0F0B83A814BCF55E00885B4F /* HandlerInfo.h */,
                                0F44A7AB20BF685E0022B171 /* ICStatusMap.cpp */,
                                65B8392E1BACAD360044E824 /* CachedRecovery.h in Headers */,
                                14F09C2A2231923100CF88EB /* CachedTypes.h in Headers */,
                                1409ECC1225E178C00BEDD54 /* CachePayload.h in Headers */,
+                               520D99F12388CC81000509A3 /* GetByValHistory.h in Headers */,
                                1409ECC0225E178100BEDD54 /* CacheUpdate.h in Headers */,
                                0FEC3C601F379F5300F59B6C /* CagedBarrierPtr.h in Headers */,
                                BC18C3ED0E16F5CD00B34460 /* CallData.h in Headers */,
index 65e648f..7954c14 100644 (file)
@@ -30,6 +30,7 @@ types [
     :ErrorType,
     :GetByIdMode,
     :GetByIdModeMetadata,
+    :GetByValHistory,
     :GetPutInfo,
     :IndexingType,
     :JSCell,
@@ -534,6 +535,7 @@ op :get_by_val,
     metadata: {
         profile: ValueProfile,
         arrayProfile: ArrayProfile,
+        seenIdentifiers: GetByValHistory,
     }
 
 op :put_by_val,
diff --git a/Source/JavaScriptCore/bytecode/GetByValHistory.h b/Source/JavaScriptCore/bytecode/GetByValHistory.h
new file mode 100644 (file)
index 0000000..83cbae7
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "TinyBloomFilter.h"
+#include <wtf/text/UniquedStringImpl.h>
+
+namespace JSC {
+
+struct GetByValHistory {
+    void observeNonUID()
+    {
+        uint64_t count = Options::getByValICMaxNumberOfIdentifiers() + 1;
+        update(count, filter());
+    }
+
+    void observe(const UniquedStringImpl* impl)
+    {
+        if (!impl) {
+            observeNonUID();
+            return;
+        }
+
+        uint64_t count = this->count();
+        uint64_t filter = this->filter();
+
+        TinyBloomFilter bloomFilter(filter);
+        uint64_t implBits = static_cast<uint64_t>(bitwise_cast<uintptr_t>(impl));
+        ASSERT(((implBits << 8) >> 8) == implBits);
+        if (bloomFilter.ruleOut(implBits)) {
+            bloomFilter.add(implBits);
+            ++count;
+        }
+
+        update(count, bloomFilter.bits());
+    }
+
+    uint64_t count() const { return m_payload >> 56; }
+
+private:
+    uint64_t filter() const { return (m_payload << 8) >> 8; }
+
+    void update(uint64_t count, uint64_t filter)
+    {
+        ASSERT(((filter << 8) >> 8) == filter);
+        m_payload = (count << 56) | filter;
+    }
+
+    uint64_t m_payload { 0 };
+};
+
+} // namespace JSC
index 0ba20f1..be62275 100644 (file)
@@ -5627,21 +5627,18 @@ void ByteCodeParser::parseBlock(unsigned limit)
             bool shouldCompileAsGetById = false;
             GetByStatus getByStatus = GetByStatus::computeFor(m_inlineStackTop->m_profiledBlock, m_inlineStackTop->m_baselineMap, m_icContextStack, currentCodeOrigin(), GetByStatus::TrackIdentifiers::Yes);
             unsigned identifierNumber = 0;
-            {
-                // FIXME: When the bytecode is not compiled in the baseline JIT, byValInfo becomes null.
-                // At that time, there is no information.
-                if (!m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadIdent)
-                    && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadType)
-                    && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCell)) {
 
-                    // FIXME: In the future, we should be able to do something like MultiGetByOffset in a multi identifier mode.
-                    // That way, we could both switch on multiple structures and multiple identifiers (or int 32 properties).
-                    // https://bugs.webkit.org/show_bug.cgi?id=204216
-                    if (Box<Identifier> impl = getByStatus.singleIdentifier()) {
-                        identifierNumber = m_graph.identifiers().ensure(impl);
-                        shouldCompileAsGetById = true;
-                        addToGraph(CheckIdent, OpInfo(impl->impl()), property);
-                    }
+            if (!m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadIdent)
+                && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadType)
+                && !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCell)) {
+
+                // FIXME: In the future, we should be able to do something like MultiGetByOffset in a multi identifier mode.
+                // That way, we could both switch on multiple structures and multiple identifiers (or int 32 properties).
+                // https://bugs.webkit.org/show_bug.cgi?id=204216
+                if (Box<Identifier> impl = getByStatus.singleIdentifier()) {
+                    identifierNumber = m_graph.identifiers().ensure(impl);
+                    shouldCompileAsGetById = true;
+                    addToGraph(CheckIdent, OpInfo(impl->impl()), property);
                 }
             }
 
@@ -5658,7 +5655,7 @@ void ByteCodeParser::parseBlock(unsigned limit)
                 Node* getByVal = addToGraph(Node::VarArg, GetByVal, OpInfo(arrayMode.asWord()), OpInfo(prediction));
                 m_exitOK = false; // GetByVal must be treated as if it clobbers exit state, since FixupPhase may make it generic.
                 set(bytecode.m_dst, getByVal);
-                if (getByStatus.observedStructureStubInfoSlowPath())
+                if (getByStatus.observedStructureStubInfoSlowPath() || bytecode.metadata(codeBlock).m_seenIdentifiers.count() > Options::getByValICMaxNumberOfIdentifiers())
                     m_graph.m_slowGetByVal.add(getByVal);
             }
 
index e1a5bf0..ea27c40 100644 (file)
@@ -804,62 +804,6 @@ EncodedJSValue JIT_OPERATION operationArithTrunc(JSGlobalObject* globalObject, E
     return JSValue::encode(jsNumber(truncatedValueOfArgument));
 }
 
-static ALWAYS_INLINE EncodedJSValue getByVal(JSGlobalObject* globalObject, JSCell* base, uint32_t index)
-{
-    if (base->isObject()) {
-        JSObject* object = asObject(base);
-        if (object->canGetIndexQuickly(index))
-            return JSValue::encode(object->getIndexQuickly(index));
-    }
-
-    if (isJSString(base) && asString(base)->canGetIndex(index))
-        return JSValue::encode(asString(base)->getIndex(globalObject, index));
-
-    return JSValue::encode(JSValue(base).get(globalObject, index));
-}
-
-EncodedJSValue JIT_OPERATION operationGetByVal(JSGlobalObject* globalObject, EncodedJSValue encodedBase, EncodedJSValue encodedProperty)
-{
-    VM& vm = globalObject->vm();
-    CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
-    JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
-    JSValue baseValue = JSValue::decode(encodedBase);
-    JSValue property = JSValue::decode(encodedProperty);
-
-    if (LIKELY(baseValue.isCell())) {
-        JSCell* base = baseValue.asCell();
-
-        if (property.isUInt32())
-            RELEASE_AND_RETURN(scope, getByVal(globalObject, base, property.asUInt32()));
-
-        if (property.isDouble()) {
-            double propertyAsDouble = property.asDouble();
-            uint32_t propertyAsUInt32 = static_cast<uint32_t>(propertyAsDouble);
-            if (propertyAsUInt32 == propertyAsDouble && isIndex(propertyAsUInt32))
-                RELEASE_AND_RETURN(scope, getByVal(globalObject, base, propertyAsUInt32));
-
-        } else if (property.isString()) {
-            Structure& structure = *base->structure(vm);
-            if (JSCell::canUseFastGetOwnProperty(structure)) {
-                RefPtr<AtomStringImpl> existingAtomString = asString(property)->toExistingAtomString(globalObject);
-                RETURN_IF_EXCEPTION(scope, encodedJSValue());
-                if (existingAtomString) {
-                    if (JSValue result = base->fastGetOwnProperty(vm, structure, existingAtomString.get()))
-                        return JSValue::encode(result);
-                }
-            }
-        }
-    }
-
-    baseValue.requireObjectCoercible(globalObject);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    auto propertyName = property.toPropertyKey(globalObject);
-    RETURN_IF_EXCEPTION(scope, encodedJSValue());
-    RELEASE_AND_RETURN(scope, JSValue::encode(baseValue.get(globalObject, propertyName)));
-}
-
 EncodedJSValue JIT_OPERATION operationGetByValCell(JSGlobalObject* globalObject, JSCell* base, EncodedJSValue encodedProperty)
 {
     VM& vm = globalObject->vm();
@@ -870,13 +814,13 @@ EncodedJSValue JIT_OPERATION operationGetByValCell(JSGlobalObject* globalObject,
     JSValue property = JSValue::decode(encodedProperty);
 
     if (property.isUInt32())
-        RELEASE_AND_RETURN(scope, getByVal(globalObject, base, property.asUInt32()));
+        RELEASE_AND_RETURN(scope, getByValWithIndex(globalObject, base, property.asUInt32()));
 
     if (property.isDouble()) {
         double propertyAsDouble = property.asDouble();
         uint32_t propertyAsUInt32 = static_cast<uint32_t>(propertyAsDouble);
         if (propertyAsUInt32 == propertyAsDouble)
-            RELEASE_AND_RETURN(scope, getByVal(globalObject, base, propertyAsUInt32));
+            RELEASE_AND_RETURN(scope, getByValWithIndex(globalObject, base, propertyAsUInt32));
 
     } else if (property.isString()) {
         Structure& structure = *base->structure(vm);
index 7090cfe..93d97bd 100644 (file)
@@ -85,7 +85,6 @@ EncodedJSValue JIT_OPERATION operationArithRound(JSGlobalObject*, EncodedJSValue
 EncodedJSValue JIT_OPERATION operationArithFloor(JSGlobalObject*, EncodedJSValue) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationArithCeil(JSGlobalObject*, EncodedJSValue) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationArithTrunc(JSGlobalObject*, EncodedJSValue) WTF_INTERNAL;
-EncodedJSValue JIT_OPERATION operationGetByVal(JSGlobalObject*, EncodedJSValue encodedBase, EncodedJSValue encodedProperty) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValCell(JSGlobalObject*, JSCell*, EncodedJSValue encodedProperty) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValObjectInt(JSGlobalObject*, JSObject*, int32_t) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValStringInt(JSGlobalObject*, JSString*, int32_t) WTF_INTERNAL;
index fcaa792..a78b19d 100644 (file)
@@ -136,6 +136,7 @@ module DSL
 #include "BytecodeDumper.h"
 #include "Fits.h"
 #include "GetByIdMetadata.h"
+#include "GetByValHistory.h"
 #include "Instruction.h"
 #include "Opcode.h"
 #include "PutByIdStatus.h"
index 3ad7715..38a7bcf 100644 (file)
@@ -31,19 +31,21 @@ typedef uintptr_t Bits;
 
 class TinyBloomFilter {
 public:
-    TinyBloomFilter();
+    TinyBloomFilter() = default;
+    TinyBloomFilter(Bits);
 
     void add(Bits);
     void add(TinyBloomFilter&);
     bool ruleOut(Bits) const; // True for 0.
     void reset();
+    Bits bits() const { return m_bits; }
 
 private:
-    Bits m_bits;
+    Bits m_bits { 0 };
 };
 
-inline TinyBloomFilter::TinyBloomFilter()
-    : m_bits(0)
+inline TinyBloomFilter::TinyBloomFilter(Bits bits)
+    : m_bits(bits)
 {
 }
 
index 0ab73e7..017bc59 100644 (file)
@@ -739,6 +739,12 @@ namespace JSC {
             linkAllSlowCasesForBytecodeIndex(m_slowCases, iter, m_bytecodeIndex);
         }
 
+        bool hasAnySlowCases(Vector<SlowCaseEntry>& slowCases, Vector<SlowCaseEntry>::iterator&, BytecodeIndex bytecodeOffset);
+        bool hasAnySlowCases(Vector<SlowCaseEntry>::iterator& iter)
+        {
+            return hasAnySlowCases(m_slowCases, iter, m_bytecodeIndex);
+        }
+
         MacroAssembler::Call appendCallWithExceptionCheck(const FunctionPtr<CFunctionPtrTag>);
 #if OS(WINDOWS) && CPU(X86_64)
         MacroAssembler::Call appendCallWithExceptionCheckAndSlowPathReturnType(const FunctionPtr<CFunctionPtrTag>);
index fd2f6c5..22149c9 100644 (file)
@@ -179,6 +179,13 @@ ALWAYS_INLINE void JIT::linkAllSlowCasesForBytecodeIndex(Vector<SlowCaseEntry>&
         linkSlowCase(iter);
 }
 
+ALWAYS_INLINE bool JIT::hasAnySlowCases(Vector<SlowCaseEntry>& slowCases, Vector<SlowCaseEntry>::iterator& iter, BytecodeIndex bytecodeIndex)
+{
+    if (iter != slowCases.end() && iter->to == bytecodeIndex)
+        return true;
+    return false;
+}
+
 ALWAYS_INLINE void JIT::addSlowCase(Jump jump)
 {
     ASSERT(m_bytecodeIndex); // This method should only be called during hot/cold path generation, so that m_bytecodeIndex is set.
index b358b1a..3b4850f 100644 (file)
@@ -2046,6 +2046,48 @@ EncodedJSValue JIT_OPERATION operationGetByValOptimize(JSGlobalObject* globalObj
     RELEASE_AND_RETURN(scope, JSValue::encode(getByVal(globalObject, callFrame, profile, baseValue, subscript)));
 }
 
+EncodedJSValue JIT_OPERATION operationGetByVal(JSGlobalObject* globalObject, EncodedJSValue encodedBase, EncodedJSValue encodedProperty)
+{
+    VM& vm = globalObject->vm();
+    CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
+    JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSValue baseValue = JSValue::decode(encodedBase);
+    JSValue property = JSValue::decode(encodedProperty);
+
+    if (LIKELY(baseValue.isCell())) {
+        JSCell* base = baseValue.asCell();
+
+        if (property.isUInt32())
+            RELEASE_AND_RETURN(scope, getByValWithIndex(globalObject, base, property.asUInt32()));
+
+        if (property.isDouble()) {
+            double propertyAsDouble = property.asDouble();
+            uint32_t propertyAsUInt32 = static_cast<uint32_t>(propertyAsDouble);
+            if (propertyAsUInt32 == propertyAsDouble && isIndex(propertyAsUInt32))
+                RELEASE_AND_RETURN(scope, getByValWithIndex(globalObject, base, propertyAsUInt32));
+        } else if (property.isString()) {
+            Structure& structure = *base->structure(vm);
+            if (JSCell::canUseFastGetOwnProperty(structure)) {
+                RefPtr<AtomStringImpl> existingAtomString = asString(property)->toExistingAtomString(globalObject);
+                RETURN_IF_EXCEPTION(scope, encodedJSValue());
+                if (existingAtomString) {
+                    if (JSValue result = base->fastGetOwnProperty(vm, structure, existingAtomString.get()))
+                        return JSValue::encode(result);
+                }
+            }
+        }
+    }
+
+    baseValue.requireObjectCoercible(globalObject);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    auto propertyName = property.toPropertyKey(globalObject);
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+    RELEASE_AND_RETURN(scope, JSValue::encode(baseValue.get(globalObject, propertyName)));
+}
+
+
 EncodedJSValue JIT_OPERATION operationHasIndexedPropertyDefault(JSGlobalObject* globalObject, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo* byValInfo)
 {
     VM& vm = globalObject->vm();
index 92e2b19..6210c5a 100644 (file)
@@ -248,6 +248,7 @@ void JIT_OPERATION operationPopScope(JSGlobalObject*, int32_t) WTF_INTERNAL;
 
 EncodedJSValue JIT_OPERATION operationGetByValOptimize(JSGlobalObject*, StructureStubInfo*, ArrayProfile*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValGeneric(JSGlobalObject*, StructureStubInfo*, ArrayProfile*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationGetByVal(JSGlobalObject*, EncodedJSValue encodedBase, EncodedJSValue encodedProperty) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationHasIndexedPropertyDefault(JSGlobalObject*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationHasIndexedPropertyGeneric(JSGlobalObject*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ByValInfo*) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationDeleteByIdJSResult(JSGlobalObject*, EncodedJSValue base, UniquedStringImpl*) WTF_INTERNAL;
index 80a6827..ab99fd9 100644 (file)
@@ -64,37 +64,47 @@ void JIT::emit_op_get_by_val(const Instruction* currentInstruction)
 
     emitGetVirtualRegister(base, regT0);
     emitGetVirtualRegister(property, regT1);
-    emitJumpSlowCaseIfNotJSCell(regT0, base);
-    emitArrayProfilingSiteWithCell(regT0, regT2, profile);
 
-    JITGetByValGenerator gen(
-        m_codeBlock, CodeOrigin(m_bytecodeIndex), CallSiteIndex(m_bytecodeIndex), RegisterSet::stubUnavailableRegisters(),
-        JSValueRegs(regT0), JSValueRegs(regT1), JSValueRegs(regT0));
-    if (isOperandConstantInt(property))
-        gen.stubInfo()->propertyIsInt32 = true;
-    gen.generateFastPath(*this);
-    addSlowCase(gen.slowPathJump());
-    m_getByVals.append(gen);
+    if (metadata.m_seenIdentifiers.count() > Options::getByValICMaxNumberOfIdentifiers()) {
+        auto notCell = branchIfNotCell(regT0);
+        emitArrayProfilingSiteWithCell(regT0, regT2, profile);
+        notCell.link(this);
+        callOperationWithProfile(bytecode.metadata(m_codeBlock), operationGetByVal, dst, TrustedImmPtr(m_codeBlock->globalObject()), regT0, regT1);
+    } else {
+        emitJumpSlowCaseIfNotJSCell(regT0, base);
+        emitArrayProfilingSiteWithCell(regT0, regT2, profile);
+
+        JITGetByValGenerator gen(
+            m_codeBlock, CodeOrigin(m_bytecodeIndex), CallSiteIndex(m_bytecodeIndex), RegisterSet::stubUnavailableRegisters(),
+            JSValueRegs(regT0), JSValueRegs(regT1), JSValueRegs(regT0));
+        if (isOperandConstantInt(property))
+            gen.stubInfo()->propertyIsInt32 = true;
+        gen.generateFastPath(*this);
+        addSlowCase(gen.slowPathJump());
+        m_getByVals.append(gen);
+
+        emitValueProfilingSite(bytecode.metadata(m_codeBlock));
+        emitPutVirtualRegister(dst);
+    }
 
-    emitValueProfilingSite(bytecode.metadata(m_codeBlock));
-    emitPutVirtualRegister(dst);
 }
 
 void JIT::emitSlow_op_get_by_val(const Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
 {
-    auto bytecode = currentInstruction->as<OpGetByVal>();
-    int dst = bytecode.m_dst.offset();
-    auto& metadata = bytecode.metadata(m_codeBlock);
-    ArrayProfile* profile = &metadata.m_arrayProfile;
-
-    JITGetByValGenerator& gen = m_getByVals[m_getByValIndex];
-    ++m_getByValIndex;
-
-    linkAllSlowCases(iter);
-
-    Label coldPathBegin = label();
-    Call call = callOperationWithProfile(bytecode.metadata(m_codeBlock), operationGetByValOptimize, dst, TrustedImmPtr(m_codeBlock->globalObject()), gen.stubInfo(), profile, regT0, regT1);
-    gen.reportSlowPathCall(coldPathBegin, call);
+    if (hasAnySlowCases(iter)) {
+        auto bytecode = currentInstruction->as<OpGetByVal>();
+        int dst = bytecode.m_dst.offset();
+        auto& metadata = bytecode.metadata(m_codeBlock);
+        ArrayProfile* profile = &metadata.m_arrayProfile;
+
+        linkAllSlowCases(iter);
+
+        JITGetByValGenerator& gen = m_getByVals[m_getByValIndex];
+        ++m_getByValIndex;
+        Label coldPathBegin = label();
+        Call call = callOperationWithProfile(bytecode.metadata(m_codeBlock), operationGetByValOptimize, dst, TrustedImmPtr(m_codeBlock->globalObject()), gen.stubInfo(), profile, regT0, regT1);
+        gen.reportSlowPathCall(coldPathBegin, call);
+    }
 }
 
 void JIT::emit_op_put_by_val_direct(const Instruction* currentInstruction)
index 7bd7e1f..7316e95 100644 (file)
@@ -989,6 +989,25 @@ LLINT_SLOW_PATH_DECL(slow_path_get_by_val)
     auto bytecode = pc->as<OpGetByVal>();
     JSValue baseValue = getOperand(callFrame, bytecode.m_base);
     JSValue subscript = getOperand(callFrame, bytecode.m_property);
+
+    if (subscript.isString() || subscript.isSymbol()) {
+        auto& metadata = bytecode.metadata(codeBlock);
+        if (metadata.m_seenIdentifiers.count() <= Options::getByValICMaxNumberOfIdentifiers()) {
+            const UniquedStringImpl* impl = nullptr;
+            if (subscript.isSymbol())
+                impl = &jsCast<Symbol*>(subscript)->privateName().uid();
+            else {
+                JSString* string = asString(subscript);
+                if (auto* maybeUID = string->tryGetValueImpl()) {
+                    if (maybeUID->isAtom())
+                        impl = static_cast<const UniquedStringImpl*>(maybeUID);
+                }
+            }
+
+            metadata.m_seenIdentifiers.observe(impl);
+        }
+    }
+    
     LLINT_RETURN_PROFILED(getByVal(vm, globalObject, codeBlock, baseValue, subscript, bytecode));
 }
 
index 98c2c6e..2c365c4 100644 (file)
@@ -519,4 +519,18 @@ inline void scribble(void* base, size_t size)
     }
 }
 
+ALWAYS_INLINE EncodedJSValue getByValWithIndex(JSGlobalObject* globalObject, JSCell* base, uint32_t index)
+{
+    if (base->isObject()) {
+        JSObject* object = asObject(base);
+        if (object->canGetIndexQuickly(index))
+            return JSValue::encode(object->getIndexQuickly(index));
+    }
+
+    if (isJSString(base) && asString(base)->canGetIndex(index))
+        return JSValue::encode(asString(base)->getIndex(globalObject, index));
+
+    return JSValue::encode(JSValue(base).get(globalObject, index));
+}
+
 } // namespace JSC
index 9d6b561..4714c91 100644 (file)
@@ -489,6 +489,7 @@ constexpr bool enableWebAssemblyStreamingApi = false;
     v(Double, dumpJITMemoryFlushInterval, 10, Restricted, "Maximum time in between flushes of the JIT memory dump in seconds.") \
     v(Bool, useUnlinkedCodeBlockJettisoning, false, Normal, "If true, UnlinkedCodeBlock can be jettisoned.") \
     v(Bool, forceOSRExitToLLInt, false, Normal, "If true, we always exit to the LLInt. If false, we exit to whatever is most convenient.") \
+    v(Unsigned, getByValICMaxNumberOfIdentifiers, 4, Normal, "Number of identifiers we see in the LLInt that could cause us to bail on generating an IC for get_by_val.") \
 
 enum OptionEquivalence {
     SameOption,