Parcourir la source

fix two bailout issues with callback.call inlining

First, there was no check to ensure .call/.apply were the real .call/.apply. There is now a CheckFixedFld for loading .call/.apply.

Second, when bailing out on the callback function not equal to what was inlined, the register for the .call/.apply method was not being restored.
Matt Gardner il y a 7 ans
Parent
commit
c6278e25a9

+ 28 - 16
lib/Backend/Inline.cpp

@@ -2925,11 +2925,7 @@ bool Inline::InlineApplyScriptTarget(IR::Instr *callInstr, const FunctionJITTime
     bool originalCallTargetOpndIsJITOpt = callInstr->GetSrc1()->GetIsJITOptimizedReg();
     bool safeThis = false;
 
-    if (targetIsCallback)
-    {
-        callInstr->ReplaceSrc1(GetCallbackFunctionOpnd(callInstr));
-    }
-    else if (!TryGetFixedMethodsForBuiltInAndTarget(callInstr, inlinerData, inlineeData, applyFuncInfo, applyLdInstr, applyTargetLdInstr, safeThis, /*isApplyTarget*/ true))
+    if (!TryGetFixedMethodsForBuiltInAndTarget(callInstr, inlinerData, inlineeData, applyFuncInfo, applyLdInstr, applyTargetLdInstr, safeThis, /*isApplyTarget*/ true, targetIsCallback))
     {
         return false;
     }
@@ -3256,14 +3252,7 @@ Inline::InlineCallTarget(IR::Instr *callInstr, const FunctionJITTimeInfo* inline
     bool originalCallTargetOpndIsJITOpt = callInstr->GetSrc1()->GetIsJITOptimizedReg();
     bool safeThis = false;
 
-    if (targetIsCallback)
-    {
-        if (!isCallInstanceFunction)
-        {
-            callInstr->ReplaceSrc1(GetCallbackFunctionOpnd(callInstr));
-        }
-    }
-    else if (!TryGetFixedMethodsForBuiltInAndTarget(callInstr, inlinerData, inlineeData, callFuncInfo, callLdInstr, callTargetLdInstr, safeThis, /*isApplyTarget*/ false))
+    if (!TryGetFixedMethodsForBuiltInAndTarget(callInstr, inlinerData, inlineeData, callFuncInfo, callLdInstr, callTargetLdInstr, safeThis, /*isApplyTarget*/ false, targetIsCallback))
     {
         return false;
     }
@@ -3394,7 +3383,7 @@ Inline::SkipCallApplyScriptTargetInlining_Shared(IR::Instr *callInstr, const Fun
 
 bool
 Inline::TryGetFixedMethodsForBuiltInAndTarget(IR::Instr *callInstr, const FunctionJITTimeInfo* inlinerData, const FunctionJITTimeInfo* inlineeData, const FunctionJITTimeInfo *builtInFuncInfo,
-                                              IR::Instr* builtInLdInstr, IR::Instr* targetLdInstr, bool& safeThis, bool isApplyTarget)
+                                              IR::Instr* builtInLdInstr, IR::Instr* targetLdInstr, bool& safeThis, bool isApplyTarget, bool isCallback)
 {
 #if ENABLE_DEBUG_CONFIG_OPTIONS
     char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
@@ -3410,6 +3399,29 @@ Inline::TryGetFixedMethodsForBuiltInAndTarget(IR::Instr *callInstr, const Functi
 
     IR::ByteCodeUsesInstr * useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr);
 
+    if (isCallback)
+    {
+        IR::Opnd * functionOpnd = GetCallbackFunctionOpnd(callInstr);
+
+        // Emit Fixed Method check for apply/call
+        safeThis = false;
+        if (!TryOptimizeCallInstrWithFixedMethod(callInstr, builtInFuncInfo/*funcinfo for apply/call */, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/, safeThis /*unused here*/))
+        {
+            callInstr->ReplaceSrc1(builtInLdInstr->GetDst());
+            INLINE_CALLBACKS_TRACE(_u("INLINING: Skip Inline: Skipping callback.%s target inlining, did not get fixed method for %s \tInlinee: %s (%s)\tCaller: %s\t(%s) \tTop Func:%s\t(%s)\n"), isApplyTarget ? _u("apply") : _u("call"), isApplyTarget ? _u("apply") : _u("call"),
+                inlineeData->GetBody()->GetDisplayName(), inlineeData->GetDebugNumberSet(debugStringBuffer),
+                inlinerData->GetBody()->GetDisplayName(), inlinerData->GetDebugNumberSet(debugStringBuffer2),
+                this->topFunc->GetJITFunctionBody()->GetDisplayName(), this->topFunc->GetDebugNumberSet(debugStringBuffer3));
+            return false;
+        }
+        callInstr->m_opcode = originalCallOpCode;
+        callInstr->ReplaceSrc1(functionOpnd);
+
+        useCallTargetInstr->SetRemovedOpndSymbol(originalCallTargetOpndJITOpt, originalCallTargetStackSym->m_id);
+        callInstr->InsertBefore(useCallTargetInstr);
+        return true;
+    }
+
     safeThis = false;
     // Check if we can get fixed method for call
     if (TryOptimizeCallInstrWithFixedMethod(callInstr, builtInFuncInfo/*funcinfo for call*/, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/,
@@ -3423,7 +3435,7 @@ Inline::TryGetFixedMethodsForBuiltInAndTarget(IR::Instr *callInstr, const Functi
             safeThis /*unused here*/, true /*dontOptimizeJustCheck*/))
         {
             callInstr->ReplaceSrc1(builtInLdInstr->GetDst());
-            INLINE_TESTTRACE(_u("INLINING: Skip Inline: Skipping %s target inlining, did not get fixed method for %s target \tInlinee: %s (#%d)\tCaller: %s\t(#%d) \tTop Func:%s\t(#%d)\n"), isApplyTarget ? _u("apply") : _u("call"), isApplyTarget ? _u("apply") : _u("call"),
+            INLINE_TESTTRACE(_u("INLINING: Skip Inline: Skipping %s target inlining, did not get fixed method for %s target \tInlinee: %s (%s)\tCaller: %s\t(%s) \tTop Func:%s\t(%s)\n"), isApplyTarget ? _u("apply") : _u("call"), isApplyTarget ? _u("apply") : _u("call"),
                 inlineeData->GetBody()->GetDisplayName(), inlineeData->GetDebugNumberSet(debugStringBuffer),
                 inlinerData->GetBody()->GetDisplayName(), inlinerData->GetDebugNumberSet(debugStringBuffer2),
                 this->topFunc->GetJITFunctionBody()->GetDisplayName(), this->topFunc->GetDebugNumberSet(debugStringBuffer3));
@@ -3432,7 +3444,7 @@ Inline::TryGetFixedMethodsForBuiltInAndTarget(IR::Instr *callInstr, const Functi
     }
     else
     {
-        INLINE_TESTTRACE(_u("INLINING: Skip Inline: Skipping %s target inlining, did not get fixed method for %s \tInlinee: %s (#%d)\tCaller: %s\t(#%d) \tTop Func:%s\t(#%d)\n"), isApplyTarget ? _u("apply") : _u("call"), isApplyTarget ? _u("apply") : _u("call"),
+        INLINE_TESTTRACE(_u("INLINING: Skip Inline: Skipping %s target inlining, did not get fixed method for %s \tInlinee: %s (%s)\tCaller: %s\t(%s) \tTop Func:%s\t(%s)\n"), isApplyTarget ? _u("apply") : _u("call"), isApplyTarget ? _u("apply") : _u("call"),
             inlineeData->GetBody()->GetDisplayName(), inlineeData->GetDebugNumberSet(debugStringBuffer),
             inlinerData->GetBody()->GetDisplayName(), inlinerData->GetDebugNumberSet(debugStringBuffer2),
             this->topFunc->GetJITFunctionBody()->GetDisplayName(), this->topFunc->GetDebugNumberSet(debugStringBuffer3));

+ 1 - 1
lib/Backend/Inline.h

@@ -87,7 +87,7 @@ private:
                     uint inlineCacheIndex, bool safeThis, bool isApplyTarget, bool isCallTarget, IR::Instr * inlineeDefInstr, uint recursiveInlineDepth);
     bool        SkipCallApplyScriptTargetInlining_Shared(IR::Instr *callInstr, const FunctionJITTimeInfo* inlinerData, const FunctionJITTimeInfo* inlineeData, bool isApplyTarget, bool isCallTarget);
     bool        TryGetFixedMethodsForBuiltInAndTarget(IR::Instr *callInstr, const FunctionJITTimeInfo* inlinerData, const FunctionJITTimeInfo* inlineeData, const FunctionJITTimeInfo *builtInFuncInfo,
-                                IR::Instr* builtInLdInstr, IR::Instr* targetLdInstr, bool& safeThis, bool isApplyTarget);
+                                IR::Instr* builtInLdInstr, IR::Instr* targetLdInstr, bool& safeThis, bool isApplyTarget, bool isCallback);
 
     IR::Instr * InlineBuiltInFunction(IR::Instr *callInstr, const FunctionJITTimeInfo * inlineeData, Js::OpCode inlineCallOpCode, const FunctionJITTimeInfo * inlinerData, const StackSym *symCallerThis, bool* pIsInlined, uint profileId, uint recursiveInlineDepth);
     IR::Instr * InlineFunc(IR::Instr *callInstr, const FunctionJITTimeInfo *const inlineeData, const uint profileId);

+ 18 - 0
test/inlining/InlineCallbackCallBailout.baseline

@@ -3,3 +3,21 @@ INLINING CALLBACK : Inlining callback for call/apply target : 	BailOut ( (#1.1),
 BailOut: function BailOut, Opcode: BoundCheck Kind: BailOutOnNotNativeArray
 BailOut: function DispatchCallWithThis, Opcode: InlineeEnd Kind: BailOutOnNotNativeArray
 BailOut: function DispatchBailout, Opcode: InlineeEnd Kind: BailOutOnNotNativeArray
+foo
+foo
+INLINING : Found callback def instr for call/apply target callback at	CallSite: 0	Caller: Dispatch ( (#1.5), #6)
+INLINING CALLBACK : Inlining callback for call/apply target : 	foo ( (#1.4), #5)
+foo
+BailOut: function Dispatch, Opcode: BailOnNotEqual Kind: BailOutOnInlineFunction
+bar
+BailOut: function CallDispatch, Opcode: InlineeEnd Kind: BailOutOnInlineFunction
+foo
+BailOut: function Dispatch, Opcode: CheckFixedFld Kind: BailOutFailedEquivalentFixedFieldTypeCheck
+foo
+BailOut: function CallDispatch, Opcode: InlineeEnd Kind: BailOutFailedEquivalentFixedFieldTypeCheck
+BailOut: function Dispatch, Opcode: CheckFixedFld Kind: BailOutFailedEquivalentFixedFieldTypeCheck
+foo
+BailOut: function CallDispatch, Opcode: InlineeEnd Kind: BailOutFailedEquivalentFixedFieldTypeCheck
+INLINING : Found callback def instr for call/apply target callback at	CallSite: 0	Caller: Dispatch ( (#1.5), #6)
+INLINING: Skip Inline: Skipping callback.call target inlining, did not get fixed method for call 	Inlinee: foo ( (#1.4), #5)	Caller: Dispatch	( (#1.5), #6) 	Top Func:CallDispatch	( (#1.6), #7)
+foo

+ 33 - 0
test/inlining/InlineCallbackCallBailout.js

@@ -22,3 +22,36 @@ function DispatchBailout(arg)
 DispatchBailout([1]);
 DispatchBailout([1]);
 DispatchBailout([1.1]);
+
+// test bail out from having a different callback function
+function foo()
+{
+    WScript.Echo("foo");
+};
+
+function Dispatch(callback)
+{
+    callback.call(undefined);
+}
+
+function CallDispatch(callback)
+{
+    Dispatch(callback);
+}
+
+CallDispatch(foo);
+CallDispatch(foo);
+CallDispatch(foo);
+
+// BailOutOnInlineFunction
+CallDispatch(function() { WScript.Echo("bar"); });
+
+CallDispatch(foo);
+
+// tautological statement makes function.call a non-fixed method. Test CheckFixedFld bailout.
+Function.prototype.call = Function.prototype.call;
+CallDispatch(foo)
+CallDispatch(foo)
+
+// rejit, not inlining callback.call
+CallDispatch(foo)