Ejemplo n.º 1
0
//
// Helps determine whether a function should be speculatively jitted.
// This function is only used once and is used in a time-critical area, so
// be careful with it (moving it around actually caused around a 5% perf
// regression on a test).
//
bool CodeGenWorkItem::ShouldSpeculativelyJit(uint byteCodeSizeGenerated) const
{
    if(PHASE_OFF(Js::FullJitPhase, this->functionBody))
    {
        return false;
    }

    byteCodeSizeGenerated += this->GetByteCodeCount();
    if(CONFIG_FLAG(ProfileBasedSpeculativeJit))
    {
        Assert(!CONFIG_ISENABLED(Js::NoDynamicProfileInMemoryCacheFlag));

        // JIT this now if we are under the speculation cap.
        return
            byteCodeSizeGenerated < (uint)CONFIG_FLAG(SpeculationCap) ||
            (
                byteCodeSizeGenerated < (uint)CONFIG_FLAG(ProfileBasedSpeculationCap) &&
                this->ShouldSpeculativelyJitBasedOnProfile()
            );
    }
    else
    {
        return byteCodeSizeGenerated < (uint)CONFIG_FLAG(SpeculationCap);
    }
}
Ejemplo n.º 2
0
bool InliningDecider::CanRecursivelyInline(Js::FunctionBody * inlinee, Js::FunctionBody *inliner, bool allowRecursiveInlining, uint recursiveInlineDepth)
{
#if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
    char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
    char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
#endif


    if (!PHASE_OFF(Js::InlineRecursivePhase, inliner)
        && allowRecursiveInlining
        &&  inlinee == inliner
        &&  inlinee->CanInlineRecursively(recursiveInlineDepth))
    {
        INLINE_TESTTRACE(_u("INLINING: Inlined recursively\tInlinee: %s (%s)\tCaller: %s (%s)\tDepth: %d\n"),
            inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
            inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2), recursiveInlineDepth);
        return true;
    }

    if (!inlinee->CanInlineAgain())
    {
        INLINE_TESTTRACE(_u("INLINING: Skip Inline: Do not inline recursive functions\tInlinee: %s (%s)\tCaller: %s (%s)\n"),
            inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
            inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2));
        return false;
    }

    return true;
}
Ejemplo n.º 3
0
uint InliningDecider::InlinePolymorphicCallSite(Js::FunctionBody *const inliner, const Js::ProfileId profiledCallSiteId,
    Js::FunctionBody** functionBodyArray, uint functionBodyArrayLength, bool* canInlineArray, uint recursiveInlineDepth)
{
    Assert(inliner);
    Assert(profiledCallSiteId < inliner->GetProfiledCallSiteCount());
    Assert(functionBodyArray);

    const auto profileData = inliner->GetAnyDynamicProfileInfo();
    Assert(profileData);

    bool isConstructorCall;
    if (!profileData->GetPolymorphicCallSiteInfo(inliner, profiledCallSiteId, &isConstructorCall, functionBodyArray, functionBodyArrayLength))
    {
        return false;
    }

    uint inlineeCount = 0;
    uint actualInlineeCount  = 0;

    for (inlineeCount = 0; inlineeCount < functionBodyArrayLength; inlineeCount++)
    {
        if (!functionBodyArray[inlineeCount])
        {
            AssertMsg(inlineeCount >= 2, "There are at least two polymorphic call site");
            break;
        }
        if (Inline(inliner, functionBodyArray[inlineeCount], isConstructorCall, true /*isPolymorphicCall*/, 0, profiledCallSiteId, recursiveInlineDepth, false))
        {
            canInlineArray[inlineeCount] = true;
            actualInlineeCount++;
        }
    }
    if (inlineeCount != actualInlineeCount)
    {
        // We generate polymorphic dispatch and call even if there are no inlinees as it's seen to provide a perf boost
        // Skip loop bodies for now as we do not handle re-jit scenarios for the bailouts from them
        if (!PHASE_OFF(Js::PartialPolymorphicInlinePhase, inliner) && !this->isLoopBody)
        {
#if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
            char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
            char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
#endif
            INLINE_TESTTRACE(_u("Partial inlining of polymorphic call: %s (%s)\tCaller: %s (%s)\n"),
                functionBodyArray[inlineeCount - 1]->GetDisplayName(), functionBodyArray[inlineeCount - 1]->GetDebugNumberSet(debugStringBuffer),
                inliner->GetDisplayName(),
                inliner->GetDebugNumberSet(debugStringBuffer2));
        }
        else
        {
            return 0;
        }
    }
    return inlineeCount;
}
Ejemplo n.º 4
0
void Symbol::SetHasNonLocalReference(bool b, ByteCodeGenerator *byteCodeGenerator)
{
    this->hasNonLocalReference = b;

    // The symbol's home function will tell us which child function we're currently processing.
    // This is the one that captures the symbol, from the declaring function's perspective.
    // So based on that information, note either that, (a.) the symbol is committed to the heap from its
    // inception, (b.) the symbol must be committed when the capturing function is instantiated.

    FuncInfo *funcHome = this->scope->GetFunc();
    FuncInfo *funcChild = funcHome->GetCurrentChildFunction();

    // If this is not a local property, or not all its references can be tracked, or
    // it's not scoped to the function, or we're in debug mode, disable the delayed capture optimization.
    if (funcHome->IsGlobalFunction() ||
        funcHome->GetCallsEval() ||
        funcHome->GetChildCallsEval() ||
        funcChild == nullptr ||
        this->GetScope() != funcHome->GetBodyScope() ||
        byteCodeGenerator->IsInDebugMode() ||
        PHASE_OFF(Js::DelayCapturePhase, funcHome->byteCodeFunction))
    {
        this->SetIsCommittedToSlot();
    }

    if (this->isCommittedToSlot)
    {
        return;
    }

    AnalysisAssert(funcChild);
    ParseNode *pnodeChild = funcChild->root;

    Assert(pnodeChild && pnodeChild->nop == knopFncDecl);

    if (pnodeChild->sxFnc.IsDeclaration())
    {
        // The capturing function is a declaration but may still be limited to an inner scope.
        Scope *scopeChild = funcHome->GetCurrentChildScope();
        if (scopeChild == this->scope || scopeChild->GetScopeType() == ScopeType_FunctionBody)
        {
            // The symbol is captured on entry to the scope in which it's declared.
            // (Check the scope type separately so that we get the special parameter list and
            // named function expression cases as well.)
            this->SetIsCommittedToSlot();
            return;
        }
    }

    // There is a chance we can limit the region in which the symbol lives on the heap.
    // Note which function captures the symbol.
    funcChild->AddCapturedSym(this);
}
Ejemplo n.º 5
0
bool InliningDecider::InlineIntoInliner(Js::FunctionBody *const inliner) const
{
    Assert(inliner);
    Assert(this->jitMode == ExecutionMode::FullJit);

#if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
    char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
#endif

    if (PHASE_OFF(Js::InlinePhase, inliner) ||
        PHASE_OFF(Js::GlobOptPhase, inliner))
    {
        return false;
    }

    if (!inliner->HasDynamicProfileInfo())
    {
        INLINE_TESTTRACE(_u("INLINING: Skip Inline: No dynamic profile info\tCaller: %s (%s)\n"), inliner->GetDisplayName(),
            inliner->GetDebugNumberSet(debugStringBuffer));
        return false;
    }

    if (inliner->GetProfiledCallSiteCount() == 0 && !inliner->GetAnyDynamicProfileInfo()->HasLdFldCallSiteInfo())
    {
        INLINE_TESTTRACE_VERBOSE(_u("INLINING: Skip Inline: Leaf function\tCaller: %s (%s)\n"), inliner->GetDisplayName(),
            inliner->GetDebugNumberSet(debugStringBuffer));
        // Nothing to do
        return false;
    }

    if (!inliner->GetAnyDynamicProfileInfo()->HasCallSiteInfo(inliner))
    {
        INLINE_TESTTRACE(_u("INLINING: Skip Inline: No call site info\tCaller: %s (#%d)\n"), inliner->GetDisplayName(),
            inliner->GetDebugNumberSet(debugStringBuffer));
        return false;
    }

    return true;
}
Ejemplo n.º 6
0
bool InliningDecider::InlineIntoTopFunc() const
{
    if (this->jitMode == ExecutionMode::SimpleJit ||
        PHASE_OFF(Js::InlinePhase, this->topFunc) ||
        PHASE_OFF(Js::GlobOptPhase, this->topFunc))
    {
        return false;
    }

    if (this->topFunc->GetHasTry())
    {
#if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
        char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
#endif
        INLINE_TESTTRACE(_u("INLINING: Skip Inline: Has try\tCaller: %s (%s)\n"), this->topFunc->GetDisplayName(),
            this->topFunc->GetDebugNumberSet(debugStringBuffer));
        // Glob opt doesn't run on function with try, so we can't generate bailout for it
        return false;
    }

    return InlineIntoInliner(topFunc);
}
Ejemplo n.º 7
0
// This only enables collection of the inlinee data, we are much more aggressive here.
// Actual decision of whether something is inlined or not is taken in CommitInlineIntoInliner
bool InliningDecider::DeciderInlineIntoInliner(Js::FunctionBody * inlinee, Js::FunctionBody * inliner, bool isConstructorCall, bool isPolymorphicCall, uint16 constantArgInfo, uint recursiveInlineDepth, bool allowRecursiveInlining)
{

    if (!CanRecursivelyInline(inlinee, inliner, allowRecursiveInlining, recursiveInlineDepth))
    {
        return false;
    }

    if (inlinee->GetIsAsmjsMode() || inliner->GetIsAsmjsMode())
    {
        return false;
    }

    if (PHASE_FORCE(Js::InlinePhase, this->topFunc) ||
        PHASE_FORCE(Js::InlinePhase, inliner) ||
        PHASE_FORCE(Js::InlinePhase, inlinee))
    {
        return true;
    }

    if (PHASE_OFF(Js::InlinePhase, this->topFunc) ||
        PHASE_OFF(Js::InlinePhase, inliner) ||
        PHASE_OFF(Js::InlinePhase, inlinee))
    {
        return false;
    }

    if (PHASE_FORCE(Js::InlineTreePhase, this->topFunc) ||
        PHASE_FORCE(Js::InlineTreePhase, inliner))
    {
        return true;
    }

    if (PHASE_FORCE(Js::InlineAtEveryCallerPhase, inlinee))
    {
        return true;
    }

    uint inlineeByteCodeCount = inlinee->GetByteCodeWithoutLDACount();

    // Heuristics are hit in the following order (Note *order* is important)
    // 1. Leaf function:  If the inlinee is a leaf (but not a constructor or a polymorphic call) inline threshold is LeafInlineThreshold (60). Also it can have max 1 loop
    // 2. Constant Function Argument: If the inlinee candidate has a constant argument and that argument is used for branching, then the inline threshold is ConstantArgumentInlineThreshold (157)
    // 3. InlineThreshold: If an inlinee candidate exceeds InlineThreshold just don't inline no matter what.

    // Following are additional constraint for an inlinee which meets InlineThreshold (Rule no 3)
    // 4. Rule for inlinee with loops:
    //      4a. Only single loop in inlinee is permitted.
    //      4b. Should not have polymorphic field access.
    //      4c. Should not be a constructor.
    //      4d. Should meet LoopInlineThreshold (25)
    // 5. Rule for polymorphic inlinee:
    //      4a. Should meet PolymorphicInlineThreshold (32)
    // 6. Rule for constructors:
    //       5a. Always inline if inlinee has polymorphic field access (as we have cloned runtime data).
    //       5b. If inlinee is monomorphic, inline only small constructors. They are governed by ConstructorInlineThreshold (21)
    // 7. Rule for inlinee which is not interpreted enough (as we might not have all the profile data):
    //       7a. As of now it is still governed by the InlineThreshold. Plan to play with this in future.
    // 8. Rest should be inlined.

    uint16 mask = constantArgInfo &  inlinee->m_argUsedForBranch;
    if (mask && inlineeByteCodeCount <  (uint)CONFIG_FLAG(ConstantArgumentInlineThreshold))
    {
        return true;
    }

    int inlineThreshold = threshold.inlineThreshold;
    if (!isPolymorphicCall && !isConstructorCall && IsInlineeLeaf(inlinee) && (inlinee->GetLoopCount() <= 2))
    {
        // Inlinee is a leaf function
        if (inlinee->GetLoopCount() == 0 || GetNumberOfInlineesWithLoop() <= (uint)threshold.maxNumberOfInlineesWithLoop) // Don't inlinee too many inlinees with loops.
        {
            // Negative LeafInlineThreshold disable the threshold
            if (threshold.leafInlineThreshold >= 0)
            {
                inlineThreshold += threshold.leafInlineThreshold - threshold.inlineThreshold;
            }
        }
    }

#if ENABLE_DEBUG_CONFIG_OPTIONS
    char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
    char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
    char16 debugStringBuffer3[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
#endif

    if (inlinee->GetHasLoops())
    {
        if (threshold.loopInlineThreshold < 0 ||                                     // Negative LoopInlineThreshold disable inlining with loop
            GetNumberOfInlineesWithLoop()  >(uint)threshold.maxNumberOfInlineesWithLoop || // See if we are inlining too many inlinees with loops.
            (inlinee->GetLoopCount() > 2) ||                                         // Allow at most 2 loops.
            inlinee->GetHasNestedLoop() ||                                           // Nested loops are not a good inlinee candidate
            isConstructorCall ||                                                     // If the function is constructor with loops, don't inline.
            PHASE_OFF(Js::InlineFunctionsWithLoopsPhase, this->topFunc))
        {
            INLINE_TESTTRACE(_u("INLINING: Skip Inline: Has loops \tBytecode size: %d \tgetNumberOfInlineesWithLoop: %d\tloopCount: %d\thasNestedLoop: %B\tisConstructorCall:%B\tInlinee: %s (%s)\tCaller: %s (%s) \tRoot: %s (%s)\n"),
                inlinee->GetByteCodeCount(),
                GetNumberOfInlineesWithLoop(),
                inlinee->GetLoopCount(),
                inlinee->GetHasNestedLoop(),
                isConstructorCall,
                inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
                inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
                topFunc->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3));
            // Don't inline function with loops
            return false;
        }
        else
        {
            inlineThreshold -= (threshold.inlineThreshold > threshold.loopInlineThreshold) ? threshold.inlineThreshold - threshold.loopInlineThreshold : 0;
        }
    }

    if (isPolymorphicCall)
    {
        if (threshold.polymorphicInlineThreshold < 0 ||                              // Negative PolymorphicInlineThreshold disable inlining
            isConstructorCall)
        {
            INLINE_TESTTRACE(_u("INLINING: Skip Inline: Polymorphic call under PolymorphicInlineThreshold: %d \tBytecode size: %d\tInlinee: %s (%s)\tCaller: %s (%s) \tRoot: %s (%s)\n"),
                threshold.polymorphicInlineThreshold,
                inlinee->GetByteCodeCount(),
                inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
                inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
                topFunc->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3));
            return false;
        }
        else
        {
            inlineThreshold -= (threshold.inlineThreshold > threshold.polymorphicInlineThreshold) ? threshold.inlineThreshold - threshold.polymorphicInlineThreshold : 0;
        }
    }

    if (isConstructorCall)
    {
#pragma prefast(suppress: 6285, "logical-or of constants is by design")
        if (PHASE_OFF(Js::InlineConstructorsPhase, this->topFunc) ||
            PHASE_OFF(Js::InlineConstructorsPhase, inliner) ||
            PHASE_OFF(Js::InlineConstructorsPhase, inlinee) ||
            !CONFIG_FLAG(CloneInlinedPolymorphicCaches))
        {
            return false;
        }

        if (PHASE_FORCE(Js::InlineConstructorsPhase, this->topFunc) ||
            PHASE_FORCE(Js::InlineConstructorsPhase, inliner) ||
            PHASE_FORCE(Js::InlineConstructorsPhase, inlinee))
        {
            return true;
        }

        if (inlinee->HasDynamicProfileInfo() && inlinee->GetAnyDynamicProfileInfo()->HasPolymorphicFldAccess())
        {
            // As of now this is not dependent on bytecodeInlinedThreshold.
            return true;
        }

        // Negative ConstructorInlineThreshold always disable constructor inlining
        if (threshold.constructorInlineThreshold < 0)
        {
            INLINE_TESTTRACE(_u("INLINING: Skip Inline: Constructor with no polymorphic field access \tBytecode size: %d\tInlinee: %s (%s)\tCaller: %s (%s) \tRoot: %s (%s)\n"),
                inlinee->GetByteCodeCount(),
                inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
                inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
                topFunc->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3));
            // Don't inline constructor that does not have a polymorphic field access, or if cloning polymorphic inline
            // caches is disabled
            return false;
        }
        else
        {
            inlineThreshold -= (threshold.inlineThreshold > threshold.constructorInlineThreshold) ? threshold.inlineThreshold - threshold.constructorInlineThreshold : 0;
        }
    }

    if (threshold.forLoopBody)
    {
        inlineThreshold /= CONFIG_FLAG(InlineInLoopBodyScaleDownFactor);
    }

    if (inlineThreshold > 0 && inlineeByteCodeCount <= (uint)inlineThreshold)
    {
        if (inlinee->GetLoopCount())
        {
            IncrementNumberOfInlineesWithLoop();
        }
        return true;
    }
    else
    {
        return false;
    }
}
Ejemplo n.º 8
0
Js::FunctionInfo *InliningDecider::Inline(Js::FunctionBody *const inliner, Js::FunctionInfo* functionInfo,
    bool isConstructorCall, bool isPolymorphicCall, uint16 constantArgInfo, Js::ProfileId callSiteId, uint recursiveInlineDepth, bool allowRecursiveInlining)
{
#if defined(DBG_DUMP) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
    char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
    char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
#endif
    Js::FunctionProxy * proxy = functionInfo->GetFunctionProxy();
    if (proxy && proxy->IsFunctionBody())
    {
        if (isLoopBody && PHASE_OFF(Js::InlineInJitLoopBodyPhase, this->topFunc))
        {
            INLINE_TESTTRACE_VERBOSE(_u("INLINING: Skip Inline: Jit loop body: %s (%s)\n"), this->topFunc->GetDisplayName(),
                this->topFunc->GetDebugNumberSet(debugStringBuffer));
            return nullptr;
        }

        // Note: disable inline for debugger, as we can't bailout at return from function.
        // Alternative can be generate this bailout as part of inline, which can be done later as perf improvement.
        const auto inlinee = proxy->GetFunctionBody();
        Assert(this->jitMode == ExecutionMode::FullJit);
        if (PHASE_OFF(Js::InlinePhase, inlinee) ||
            PHASE_OFF(Js::GlobOptPhase, inlinee) ||
            !ContinueInliningUserDefinedFunctions(this->bytecodeInlinedCount) ||
            this->isInDebugMode)
        {
            return nullptr;
        }

        if (functionInfo->IsDeferred() || inlinee->GetByteCode() == nullptr)
        {
            // DeferredParse...
            INLINE_TESTTRACE(_u("INLINING: Skip Inline: No bytecode\tInlinee: %s (%s)\tCaller: %s (%s)\n"),
                inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(),
                inliner->GetDebugNumberSet(debugStringBuffer2));
            return nullptr;
        }

        if (inlinee->GetHasTry())
        {
            INLINE_TESTTRACE(_u("INLINING: Skip Inline: Has try\tInlinee: %s (%s)\tCaller: %s (%s)\n"),
                inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(),
                inliner->GetDebugNumberSet(debugStringBuffer2));
            return nullptr;
        }

        // This is a hard limit as the argOuts array is statically sized.
        if (inlinee->GetInParamsCount() > Js::InlineeCallInfo::MaxInlineeArgoutCount)
        {
            INLINE_TESTTRACE(_u("INLINING: Skip Inline: Params count greater then MaxInlineeArgoutCount\tInlinee: %s (%s)\tParamcount: %d\tMaxInlineeArgoutCount: %d\tCaller: %s (%s)\n"),
                inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inlinee->GetInParamsCount(), Js::InlineeCallInfo::MaxInlineeArgoutCount,
                inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2));
            return nullptr;
        }

        if (inlinee->GetInParamsCount() == 0)
        {
            // Inline candidate has no params, not even a this pointer.  This can only be the global function,
            // which we shouldn't inline.
            INLINE_TESTTRACE(_u("INLINING: Skip Inline: Params count is zero!\tInlinee: %s (%s)\tParamcount: %d\tCaller: %s (%s)\n"),
                inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inlinee->GetInParamsCount(),
                inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2));
            return nullptr;
        }

        if (inlinee->GetDontInline())
        {
            INLINE_TESTTRACE(_u("INLINING: Skip Inline: Do not inline\tInlinee: %s (%s)\tCaller: %s (%s)\n"),
                inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(),
                inliner->GetDebugNumberSet(debugStringBuffer2));
            return nullptr;
        }

        // Do not inline a call to a class constructor if it isn't part of a new expression since the call will throw a TypeError anyway.
        if (inlinee->IsClassConstructor() && !isConstructorCall)
        {
            INLINE_TESTTRACE(_u("INLINING: Skip Inline: Class constructor without new keyword\tInlinee: %s (%s)\tCaller: %s (%s)\n"),
                inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inliner->GetDisplayName(),
                inliner->GetDebugNumberSet(debugStringBuffer2));
            return nullptr;
        }

        if (!DeciderInlineIntoInliner(inlinee, inliner, isConstructorCall, isPolymorphicCall, constantArgInfo, recursiveInlineDepth, allowRecursiveInlining))
        {
            return nullptr;
        }

#if defined(ENABLE_DEBUG_CONFIG_OPTIONS)
        TraceInlining(inliner, inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer), inlinee->GetByteCodeCount(), this->topFunc, this->bytecodeInlinedCount, inlinee, callSiteId, this->isLoopBody);
#endif

        this->bytecodeInlinedCount += inlinee->GetByteCodeCount();
        return inlinee;
    }

    Js::OpCode builtInInlineCandidateOpCode;
    ValueType builtInReturnType;
    GetBuiltInInfo(functionInfo, &builtInInlineCandidateOpCode, &builtInReturnType);

    if(builtInInlineCandidateOpCode == 0 && builtInReturnType.IsUninitialized())
    {
        return nullptr;
    }

    Assert(this->jitMode == ExecutionMode::FullJit);
    if (builtInInlineCandidateOpCode != 0 &&
        (
            PHASE_OFF(Js::InlinePhase, inliner) ||
            PHASE_OFF(Js::GlobOptPhase, inliner) ||
            isConstructorCall
        ))
    {
        return nullptr;
    }

    // Note: for built-ins at this time we don't have enough data (the instr) to decide whether it's going to be inlined.
    return functionInfo;
}
Ejemplo n.º 9
0
// Called from background thread to commit inlining.
bool InliningHeuristics::BackendInlineIntoInliner(Js::FunctionBody* inlinee,
                                Js::FunctionBody *inliner,
                                Func *topFunction,
                                Js::ProfileId callSiteId,
                                bool isConstructorCall,
                                bool isFixedMethodCall,                     // Reserved
                                bool isCallOutsideLoopInTopFunc,            // There is a loop for sure and this call is outside loop
                                bool isCallInsideLoop,
                                uint recursiveInlineDepth,
                                uint16 constantArguments
                                )
{
    // We have one piece of additional data in backend, whether  we are outside loop or inside
    // This function decides to inline or not based on that additional data. Most of the filtering is already done by DeciderInlineIntoInliner which is called
    // during work item creation.
    // This is additional filtering during actual inlining phase.

    // Note *order* is important
    // Following are
    // 1. Constructor is always inlined (irrespective of inside or outside)
    // 2. If the inlinee candidate has constant argument and that argument is used for a branch and the inlinee size is within ConstantArgumentInlineThreshold(157) we inline
    // 3. Inside loops:
    //     3a. Leaf function will always get inlined (irrespective of leaf has loop or not)
    //     3b. If the inlinee has loops, don't inline it (Basically avoiding inlining a loop within another loop unless its leaf).
    // 4. Outside loop (inliner has loops):
    //     4a. Only inline small inlinees. Governed by OutsideLoopInlineThreshold (16)
    // 5. Rest are inlined.
#if ENABLE_DEBUG_CONFIG_OPTIONS
    wchar_t debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
    wchar_t debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
    wchar_t debugStringBuffer3[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
#endif

    bool doBackEndAggressiveInline = (constantArguments & inlinee->m_argUsedForBranch) != 0;

    if (!PHASE_OFF(Js::InlineRecursivePhase, inliner)
        && inlinee == inliner
        && (!inlinee->CanInlineRecursively(recursiveInlineDepth, doBackEndAggressiveInline)))
    {
        INLINE_TESTTRACE(L"INLINING: Skip Inline (backend): Recursive inlining\tInlinee: %s (#%s)\tCaller: %s (#%s) \tRoot: %s (#%s) Depth: %d\n",
            inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
            inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
            topFunc->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3),
            recursiveInlineDepth);
        return false;
    }

    if(PHASE_FORCE(Js::InlinePhase, this->topFunc) ||
        PHASE_FORCE(Js::InlinePhase, inliner) ||
        PHASE_FORCE(Js::InlinePhase, inlinee))
    {
        return true;
    }

    if (PHASE_FORCE(Js::InlineTreePhase, this->topFunc) ||
        PHASE_FORCE(Js::InlineTreePhase, inliner))
    {
        return true;
    }

    if (PHASE_FORCE(Js::InlineAtEveryCallerPhase, inlinee))
    {
        return true;
    }

    Js::DynamicProfileInfo *dynamicProfile = inliner->GetAnyDynamicProfileInfo();

    bool doConstantArgumentInlining = (dynamicProfile->GetConstantArgInfo(callSiteId) & inlinee->m_argUsedForBranch) != 0;
    if (doConstantArgumentInlining && inlinee->GetByteCodeWithoutLDACount() <  (uint)threshold.constantArgumentInlineThreshold)
    {
        return true;
    }


    if (topFunction->m_workItem->RecyclableData()->JitTimeData()->GetIsAggressiveInliningEnabled())
    {
        return true;
    }

    if (isConstructorCall)
    {
        return true;
    }


    if (isCallInsideLoop && IsInlineeLeaf(inlinee))
    {
        return true;
    }

    if (isCallInsideLoop && inlinee->GetHasLoops() )                            // Don't inline function with loops inside another loop unless it is a leaf
    {
        INLINE_TESTTRACE(L"INLINING: Skip Inline (backend): Recursive loop inlining\tInlinee: %s (#%s)\tCaller: %s (#%s) \tRoot: %s (#%s)\n",
            inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
            inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
            topFunc->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3));
        return false;
    }
    byte scale = 1;

    if (doBackEndAggressiveInline)
    {
        scale = 2;
    }

    if (isCallOutsideLoopInTopFunc &&
        (threshold.outsideLoopInlineThreshold < 0 ||
        inlinee->GetByteCodeWithoutLDACount() > (uint)threshold.outsideLoopInlineThreshold * scale))
    {
        Assert(!isCallInsideLoop);
        INLINE_TESTTRACE(L"INLINING: Skip Inline (backend): Inlining outside loop doesn't meet OutsideLoopInlineThreshold: %d \tBytecode size: %d\tInlinee: %s (#%s)\tCaller: %s (#%s) \tRoot: %s (#%s)\n",
            threshold.outsideLoopInlineThreshold,
            inlinee->GetByteCodeCount(),
            inlinee->GetDisplayName(), inlinee->GetDebugNumberSet(debugStringBuffer),
            inliner->GetDisplayName(), inliner->GetDebugNumberSet(debugStringBuffer2),
            topFunc->GetDisplayName(), topFunc->GetDebugNumberSet(debugStringBuffer3));
        return false;
    }
    return true;
}