Ignore:
Timestamp:
Feb 2, 2015, 10:38:08 AM (11 years ago)
Author:
[email protected]
Message:

Polymorphic call inlining should be based on polymorphic call inline caching rather than logging
https://bugs.webkit.org/show_bug.cgi?id=140660

Reviewed by Geoffrey Garen.

When we first implemented polymorphic call inlining, we did the profiling based on a call
edge log. The idea was to store each call edge (a tuple of call site and callee) into a
global log that was processed lazily. Processing the log would give precise counts of call
edges, and could be used to drive well-informed inlining decisions - polymorphic or not.
This was a speed-up on throughput tests but a slow-down for latency tests. It was a net win
nonetheless.

Experience with this code shows three things. First, the call edge profiler is buggy and
complex. It would take work to fix the bugs. Second, the call edge profiler incurs lots of
overhead for latency code that we care deeply about. Third, it's not at all clear that
having call edge counts for every possible callee is any better than just having call edge
counts for the limited number of callees that an inline cache would catch.

So, this patch removes the call edge profiler and replaces it with a polymorphic call inline
cache. If we miss the basic call inline cache, we inflate the cache to be a jump to an
out-of-line stub that cases on the previously known callees. If that misses again, then we
rewrite that stub to include the new callee. We do this up to some number of callees. If we
hit the limit then we switch to using a plain virtual call.

Substantial speed-up on V8Spider; undoes the slow-down that the original call edge profiler
caused. Might be a SunSpider speed-up (below 1%), depending on hardware.

Rolling this back in after fixing https://bugs.webkit.org/show_bug.cgi?id=141107.

(JSC::CallEdge::count):
(JSC::CallEdge::CallEdge):

  • bytecode/CallEdgeProfile.cpp: Removed.
  • bytecode/CallEdgeProfile.h: Removed.
  • bytecode/CallEdgeProfileInlines.h: Removed.
  • bytecode/CallLinkInfo.cpp:

(JSC::CallLinkInfo::unlink):
(JSC::CallLinkInfo::visitWeak):

  • bytecode/CallLinkInfo.h:
  • bytecode/CallLinkStatus.cpp:

(JSC::CallLinkStatus::CallLinkStatus):
(JSC::CallLinkStatus::computeFor):
(JSC::CallLinkStatus::computeFromCallLinkInfo):
(JSC::CallLinkStatus::isClosureCall):
(JSC::CallLinkStatus::makeClosureCall):
(JSC::CallLinkStatus::dump):
(JSC::CallLinkStatus::computeFromCallEdgeProfile): Deleted.

  • bytecode/CallLinkStatus.h:

(JSC::CallLinkStatus::CallLinkStatus):
(JSC::CallLinkStatus::isSet):
(JSC::CallLinkStatus::variants):
(JSC::CallLinkStatus::size):
(JSC::CallLinkStatus::at):
(JSC::CallLinkStatus::operator[]):
(JSC::CallLinkStatus::canOptimize):
(JSC::CallLinkStatus::edges): Deleted.
(JSC::CallLinkStatus::canTrustCounts): Deleted.

  • bytecode/CallVariant.cpp:

(JSC::variantListWithVariant):
(JSC::despecifiedVariantList):

  • bytecode/CallVariant.h:
  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::~CodeBlock):
(JSC::CodeBlock::linkIncomingPolymorphicCall):
(JSC::CodeBlock::unlinkIncomingCalls):
(JSC::CodeBlock::noticeIncomingCall):

  • bytecode/CodeBlock.h:

(JSC::CodeBlock::isIncomingCallAlreadyLinked): Deleted.

  • dfg/DFGAbstractInterpreterInlines.h:

(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::addCallWithoutSettingResult):
(JSC::DFG::ByteCodeParser::handleCall):
(JSC::DFG::ByteCodeParser::handleInlining):

  • dfg/DFGClobberize.h:

(JSC::DFG::clobberize):

  • dfg/DFGConstantFoldingPhase.cpp:

(JSC::DFG::ConstantFoldingPhase::foldConstants):

  • dfg/DFGDoesGC.cpp:

(JSC::DFG::doesGC):

  • dfg/DFGDriver.cpp:

(JSC::DFG::compileImpl):

  • dfg/DFGFixupPhase.cpp:

(JSC::DFG::FixupPhase::fixupNode):

  • dfg/DFGNode.h:

(JSC::DFG::Node::hasHeapPrediction):

  • dfg/DFGNodeType.h:
  • dfg/DFGOperations.cpp:
  • dfg/DFGPredictionPropagationPhase.cpp:

(JSC::DFG::PredictionPropagationPhase::propagate):

  • dfg/DFGSafeToExecute.h:

(JSC::DFG::safeToExecute):

  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::emitCall):
(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::emitCall):
(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGTierUpCheckInjectionPhase.cpp:

(JSC::DFG::TierUpCheckInjectionPhase::run):
(JSC::DFG::TierUpCheckInjectionPhase::removeFTLProfiling): Deleted.

  • ftl/FTLCapabilities.cpp:

(JSC::FTL::canCompile):

  • heap/Heap.cpp:

(JSC::Heap::collect):

  • jit/BinarySwitch.h:
  • jit/ClosureCallStubRoutine.cpp: Removed.
  • jit/ClosureCallStubRoutine.h: Removed.
  • jit/JITCall.cpp:

(JSC::JIT::compileOpCall):

  • jit/JITCall32_64.cpp:

(JSC::JIT::compileOpCall):

  • jit/JITOperations.cpp:
  • jit/JITOperations.h:

(JSC::operationLinkPolymorphicCallFor):
(JSC::operationLinkClosureCallFor): Deleted.

  • jit/JITStubRoutine.h:
  • jit/JITWriteBarrier.h:
  • jit/PolymorphicCallStubRoutine.cpp: Added.

(JSC::PolymorphicCallNode::~PolymorphicCallNode):
(JSC::PolymorphicCallNode::unlink):
(JSC::PolymorphicCallCase::dump):
(JSC::PolymorphicCallStubRoutine::PolymorphicCallStubRoutine):
(JSC::PolymorphicCallStubRoutine::~PolymorphicCallStubRoutine):
(JSC::PolymorphicCallStubRoutine::variants):
(JSC::PolymorphicCallStubRoutine::edges):
(JSC::PolymorphicCallStubRoutine::visitWeak):
(JSC::PolymorphicCallStubRoutine::markRequiredObjectsInternal):

  • jit/PolymorphicCallStubRoutine.h: Added.

(JSC::PolymorphicCallNode::PolymorphicCallNode):
(JSC::PolymorphicCallCase::PolymorphicCallCase):
(JSC::PolymorphicCallCase::variant):
(JSC::PolymorphicCallCase::codeBlock):

  • jit/Repatch.cpp:

(JSC::linkSlowFor):
(JSC::linkFor):
(JSC::revertCall):
(JSC::unlinkFor):
(JSC::linkVirtualFor):
(JSC::linkPolymorphicCall):
(JSC::linkClosureCall): Deleted.

  • jit/Repatch.h:
  • jit/ThunkGenerators.cpp:

(JSC::linkPolymorphicCallForThunkGenerator):
(JSC::linkPolymorphicCallThunkGenerator):
(JSC::linkPolymorphicCallThatPreservesRegsThunkGenerator):
(JSC::linkClosureCallForThunkGenerator): Deleted.
(JSC::linkClosureCallThunkGenerator): Deleted.
(JSC::linkClosureCallThatPreservesRegsThunkGenerator): Deleted.

  • jit/ThunkGenerators.h:

(JSC::linkPolymorphicCallThunkGeneratorFor):
(JSC::linkClosureCallThunkGeneratorFor): Deleted.

  • llint/LLIntSlowPaths.cpp:

(JSC::LLInt::jitCompileAndSetHeuristics):

  • runtime/Options.h:
  • runtime/VM.cpp:

(JSC::VM::prepareToDiscardCode):
(JSC::VM::ensureCallEdgeLog): Deleted.

  • runtime/VM.h:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

    r179392 r179478  
    11 /*
    2  * Copyright (C) 2011, 2012, 2013, 2014 Apple Inc. All rights reserved.
     2 * Copyright (C) 2011-2015 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    676676            m_parameterSlots = parameterSlots;
    677677
    678         int dummyThisArgument = op == Call || op == NativeCall || op == ProfiledCall ? 0 : 1;
     678        int dummyThisArgument = op == Call || op == NativeCall ? 0 : 1;
    679679        for (int i = 0 + dummyThisArgument; i < argCount; ++i)
    680680            addVarArgChild(get(virtualRegisterForArgument(i, registerOffset)));
     
    10451045        callLinkStatus = CallLinkStatus(callTarget->asJSValue()).setIsProved(true);
    10461046   
    1047     if ((!callLinkStatus.canOptimize() || callLinkStatus.size() != 1)
    1048         && !isFTL(m_graph.m_plan.mode) && Options::useFTLJIT()
    1049         && InlineCallFrame::isNormalCall(kind)
    1050         && CallEdgeLog::isEnabled()
    1051         && Options::dfgDoesCallEdgeProfiling()) {
    1052         ASSERT(op == Call || op == Construct);
    1053         if (op == Call)
    1054             op = ProfiledCall;
    1055         else
    1056             op = ProfiledConstruct;
    1057     }
     1047    if (Options::verboseDFGByteCodeParsing())
     1048        dataLog("    Handling call at ", currentCodeOrigin(), ": ", callLinkStatus, "\n");
    10581049   
    10591050    if (!callLinkStatus.canOptimize()) {
     
    10771068#if ENABLE(FTL_NATIVE_CALL_INLINING)
    10781069    if (isFTL(m_graph.m_plan.mode) && Options::optimizeNativeCalls() && callLinkStatus.size() == 1 && !callLinkStatus.couldTakeSlowPath()) {
    1079         CallVariant callee = callLinkStatus[0].callee();
     1070        CallVariant callee = callLinkStatus[0];
    10801071        JSFunction* function = callee.function();
    10811072        CodeSpecializationKind specializationKind = InlineCallFrame::specializationKindFor(kind);
     
    10841075            callOpInfo = OpInfo(m_graph.freeze(function));
    10851076
    1086             if (op == Call || op == ProfiledCall)
     1077            if (op == Call)
    10871078                op = NativeCall;
    10881079            else {
    1089                 ASSERT(op == Construct || op == ProfiledConstruct);
     1080                ASSERT(op == Construct);
    10901081                op = NativeConstruct;
    10911082            }
     
    14271418    if (!callLinkStatus.couldTakeSlowPath() && callLinkStatus.size() == 1) {
    14281419        emitFunctionChecks(
    1429             callLinkStatus[0].callee(), callTargetNode, registerOffset, specializationKind);
     1420            callLinkStatus[0], callTargetNode, registerOffset, specializationKind);
    14301421        bool result = attemptToInlineCall(
    1431             callTargetNode, resultOperand, callLinkStatus[0].callee(), registerOffset,
     1422            callTargetNode, resultOperand, callLinkStatus[0], registerOffset,
    14321423            argumentCountIncludingThis, nextOffset, kind, CallerDoesNormalLinking, prediction,
    14331424            inliningBalance);
    14341425        if (!result && !callLinkStatus.isProved())
    1435             undoFunctionChecks(callLinkStatus[0].callee());
     1426            undoFunctionChecks(callLinkStatus[0]);
    14361427        if (verbose) {
    14371428            dataLog("Done inlining (simple).\n");
     
    14631454    bool allAreDirectCalls = true;
    14641455    for (unsigned i = callLinkStatus.size(); i--;) {
    1465         if (callLinkStatus[i].callee().isClosureCall())
     1456        if (callLinkStatus[i].isClosureCall())
    14661457            allAreDirectCalls = false;
    14671458        else
     
    14761467    else {
    14771468        // FIXME: We should be able to handle this case, but it's tricky and we don't know of cases
    1478         // where it would be beneficial. Also, CallLinkStatus would make all callees appear like
    1479         // closure calls if any calls were closure calls - except for calls to internal functions.
    1480         // So this will only arise if some callees are internal functions and others are closures.
     1469        // where it would be beneficial. It might be best to handle these cases as if all calls were
     1470        // closure calls.
    14811471        // https://bugs.webkit.org/show_bug.cgi?id=136020
    14821472        if (verbose) {
     
    15181508    Vector<BasicBlock*> landingBlocks;
    15191509   
    1520     // We make force this true if we give up on inlining any of the edges.
     1510    // We may force this true if we give up on inlining any of the edges.
    15211511    bool couldTakeSlowPath = callLinkStatus.couldTakeSlowPath();
    15221512   
     
    15341524       
    15351525        bool inliningResult = attemptToInlineCall(
    1536             myCallTargetNode, resultOperand, callLinkStatus[i].callee(), registerOffset,
     1526            myCallTargetNode, resultOperand, callLinkStatus[i], registerOffset,
    15371527            argumentCountIncludingThis, nextOffset, kind, CallerLinksManually, prediction,
    15381528            inliningBalance);
     
    15531543        JSCell* thingToCaseOn;
    15541544        if (allAreDirectCalls)
    1555             thingToCaseOn = callLinkStatus[i].callee().nonExecutableCallee();
     1545            thingToCaseOn = callLinkStatus[i].nonExecutableCallee();
    15561546        else {
    15571547            ASSERT(allAreClosureCalls);
    1558             thingToCaseOn = callLinkStatus[i].callee().executable();
     1548            thingToCaseOn = callLinkStatus[i].executable();
    15591549        }
    15601550        data.cases.append(SwitchCase(m_graph.freeze(thingToCaseOn), block.get()));
     
    15681558
    15691559        if (verbose)
    1570             dataLog("Finished inlining ", callLinkStatus[i].callee(), " at ", currentCodeOrigin(), ".\n");
     1560            dataLog("Finished inlining ", callLinkStatus[i], " at ", currentCodeOrigin(), ".\n");
    15711561    }
    15721562   
Note: See TracChangeset for help on using the changeset viewer.