Przeglądaj źródła

[MERGE #2991 @pleath] Fix perf regression in typed array tests.

Merge pull request #2991 from pleath:2945

If we bail out on array access because we needed a helper call to, for instance, convert the value stored to a typed array, then when we interpret the array access the profile info needs to be updated to indicate that the helper call was needed. Otherwise, on re-jit, we'll generate the same code again, and we'll keep bailing out. Try to be proactive in the case of non-number value stored to a typed array, but also make use of the fact that we had BailOutOnArrayAccessHelperCall, since helper call cases can't be perfectly predicted in the interpreter.
Paul Leathers 8 lat temu
rodzic
commit
a81d47bebb

+ 4 - 0
lib/Backend/BailOut.cpp

@@ -1547,6 +1547,10 @@ BailOutRecord::BailOutHelper(Js::JavascriptCallStackLayout * layout, Js::ScriptF
 
     newInstance->ehBailoutData = bailOutRecord->ehBailoutData;
     newInstance->OrFlags(Js::InterpreterStackFrameFlags_FromBailOut);
+    if (bailOutKind == IR::BailOutOnArrayAccessHelperCall)
+    {
+        newInstance->OrFlags(Js::InterpreterStackFrameFlags_ProcessingBailOutOnArrayAccessHelperCall);
+    }
 
     ThreadContext *threadContext = newInstance->GetScriptContext()->GetThreadContext();
 

+ 6 - 2
lib/Backend/Lower.cpp

@@ -8464,11 +8464,13 @@ void Lowerer::LowerProfiledLdElemI(IR::JitProfilingInstr *const instr)
             const Var base,
             const Var varIndex,
             FunctionBody *const functionBody,
-            const ProfileId profileId)
+            const ProfileId profileId,
+            bool didArrayAccessHelperCall)
     */
 
     Func *const func = instr->m_func;
 
+    m_lowererMD.LoadHelperArgument(instr, IR::IntConstOpnd::New(false, TyInt8, func));
     m_lowererMD.LoadHelperArgument(instr, IR::Opnd::CreateProfileIdOpnd(instr->profileId, func));
     m_lowererMD.LoadHelperArgument(instr, CreateFunctionBodyOpnd(func));
     IR::IndirOpnd *const indir = instr->UnlinkSrc1()->AsIndirOpnd();
@@ -8497,7 +8499,8 @@ void Lowerer::LowerProfiledStElemI(IR::JitProfilingInstr *const instr, const Js:
             const Var value,
             FunctionBody *const functionBody,
             const ProfileId profileId,
-            const PropertyOperationFlags flags)
+            const PropertyOperationFlags flags,
+            bool didArrayAccessHelperCall)
     */
 
     Func *const func = instr->m_func;
@@ -8510,6 +8513,7 @@ void Lowerer::LowerProfiledStElemI(IR::JitProfilingInstr *const instr, const Js:
     else
     {
         helper = IR::HelperProfiledStElem;
+        m_lowererMD.LoadHelperArgument(instr, IR::IntConstOpnd::New(false, TyInt8, func));
         m_lowererMD.LoadHelperArgument(instr, IR::IntConstOpnd::New(flags, TyInt32, func, true));
     }
     m_lowererMD.LoadHelperArgument(instr, IR::Opnd::CreateProfileIdOpnd(instr->profileId, func));

+ 12 - 2
lib/Runtime/Language/InterpreterStackFrame.cpp

@@ -5060,7 +5060,10 @@ namespace Js
                 GetReg(playout->Instance),
                 GetReg(playout->Element),
                 m_functionBody,
-                playout->profileId));
+                playout->profileId,
+                (m_flags & InterpreterStackFrameFlags_ProcessingBailOutOnArrayAccessHelperCall) != 0));
+
+        m_flags &= ~InterpreterStackFrameFlags_ProcessingBailOutOnArrayAccessHelperCall;
 
         threadContext->CheckAndResetImplicitCallAccessorFlag();
         threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
@@ -5095,6 +5098,8 @@ namespace Js
             element = JavascriptOperators::OP_GetElementI(instance, GetReg(playout->Element), GetScriptContext());
         }
 
+        m_flags &= ~InterpreterStackFrameFlags_ProcessingBailOutOnArrayAccessHelperCall;
+
         threadContext->CheckAndResetImplicitCallAccessorFlag();
         threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
 
@@ -5133,6 +5138,8 @@ namespace Js
             JavascriptOperators::OP_SetElementI(instance, varIndex, value, GetScriptContext(), flags);
         }
 
+        m_flags &= ~InterpreterStackFrameFlags_ProcessingBailOutOnArrayAccessHelperCall;
+
         threadContext->CheckAndResetImplicitCallAccessorFlag();
         threadContext->AddImplicitCallFlags(savedImplicitCallFlags);
     }
@@ -5153,7 +5160,10 @@ namespace Js
             GetReg(playout->Value),
             m_functionBody,
             playout->profileId,
-            flags);
+            flags,
+            (m_flags & InterpreterStackFrameFlags_ProcessingBailOutOnArrayAccessHelperCall) != 0);
+
+        m_flags &= ~InterpreterStackFrameFlags_ProcessingBailOutOnArrayAccessHelperCall;
 
         threadContext->CheckAndResetImplicitCallAccessorFlag();
         threadContext->AddImplicitCallFlags(savedImplicitCallFlags);

+ 2 - 1
lib/Runtime/Language/InterpreterStackFrame.h

@@ -25,7 +25,8 @@ namespace Js
         InterpreterStackFrameFlags_WithinCatchBlock = 2,
         InterpreterStackFrameFlags_WithinFinallyBlock = 4,
         InterpreterStackFrameFlags_FromBailOut = 8,
-        InterpreterStackFrameFlags_ProcessingBailOutFromEHCode = 0x10,
+        InterpreterStackFrameFlags_ProcessingBailOutOnArrayAccessHelperCall = 0x10,
+        InterpreterStackFrameFlags_ProcessingBailOutFromEHCode = 0x20,
         InterpreterStackFrameFlags_All = 0xFFFF,
     };
 

+ 21 - 4
lib/Runtime/Language/ProfilingHelpers.cpp

@@ -11,7 +11,8 @@ namespace Js
         const Var base,
         const Var varIndex,
         FunctionBody *const functionBody,
-        const ProfileId profileId)
+        const ProfileId profileId,
+        bool didArrayAccessHelperCall)
     {
         Assert(base);
         Assert(varIndex);
@@ -44,6 +45,11 @@ namespace Js
         JavascriptArray *const array =
             JavascriptArray::GetArrayForArrayOrObjectWithArray(base, &isObjectWithArray, &arrayTypeId);
 
+        if (didArrayAccessHelperCall)
+        {
+            ldElemInfo.neededHelperCall = true;
+        }
+
         do // while(false)
         {
             // The fast path is only for JavascriptArray and doesn't cover native arrays, objects with internal arrays, or typed
@@ -191,7 +197,7 @@ namespace Js
         FunctionBody *const functionBody,
         const ProfileId profileId)
     {
-        ProfiledStElem(base, varIndex, value, functionBody, profileId, PropertyOperation_None);
+        ProfiledStElem(base, varIndex, value, functionBody, profileId, PropertyOperation_None, false);
     }
 
     void ProfilingHelpers::ProfiledStElem(
@@ -200,7 +206,8 @@ namespace Js
         const Var value,
         FunctionBody *const functionBody,
         const ProfileId profileId,
-        const PropertyOperationFlags flags)
+        const PropertyOperationFlags flags,
+        bool didArrayAccessHelperCall)
     {
         Assert(base);
         Assert(varIndex);
@@ -271,13 +278,23 @@ namespace Js
             {
                 length = headSegmentLength;
                 bool isVirtual = (VirtualTableInfoBase::GetVirtualTable(base) == ValueType::GetVirtualTypedArrayVtable(arrayTypeId));
-                stElemInfo.arrayType = ValueType::FromTypeId(arrayTypeId, isVirtual).ToLikely();
+                stElemInfo.arrayType = ValueType::FromTypeId(arrayTypeId, isVirtual).ToLikely();        
+                if (!TaggedNumber::Is(value) && !JavascriptNumber::Is_NoTaggedIntCheck(value))
+                {
+                    // Non-number stored to a typed array. A helper call will be needed to convert the value.
+                    stElemInfo.neededHelperCall = true;
+                }
             }
             else
             {
                 break;
             }
 
+            if (didArrayAccessHelperCall)
+            {
+                stElemInfo.neededHelperCall = true;
+            }
+
             if(!TaggedInt::Is(varIndex))
             {
                 stElemInfo.neededHelperCall = true;

+ 2 - 2
lib/Runtime/Language/ProfilingHelpers.h

@@ -10,12 +10,12 @@ namespace Js
     class ProfilingHelpers
     {
     public:
-        static Var ProfiledLdElem(const Var base, const Var varIndex, FunctionBody *const functionBody, const ProfileId profileId);
+        static Var ProfiledLdElem(const Var base, const Var varIndex, FunctionBody *const functionBody, const ProfileId profileId, bool didArrayAccessHelperCall);
         static Var ProfiledLdElem_FastPath(JavascriptArray *const array, const Var varIndex, ScriptContext *const scriptContext, LdElemInfo *const ldElemInfo = nullptr);
 
     public:
         static void ProfiledStElem_DefaultFlags(const Var base, const Var varIndex, const Var value, FunctionBody *const functionBody, const ProfileId profileId);
-        static void ProfiledStElem(const Var base, const Var varIndex, const Var value, FunctionBody *const functionBody, const ProfileId profileId, const PropertyOperationFlags flags);
+        static void ProfiledStElem(const Var base, const Var varIndex, const Var value, FunctionBody *const functionBody, const ProfileId profileId, const PropertyOperationFlags flags, bool didArrayAccessHelperCall);
         static void ProfiledStElem_FastPath(JavascriptArray *const array, const Var varIndex, const Var value, ScriptContext *const scriptContext, const PropertyOperationFlags flags, StElemInfo *const stElemInfo = nullptr);
 
     public: