Ignore:
Timestamp:
Dec 4, 2014, 9:58:07 PM (10 years ago)
Author:
[email protected]
Message:

JavaScript Control Flow Profiler
https://bugs.webkit.org/show_bug.cgi?id=137785

Reviewed by Filip Pizlo.

This patch introduces a mechanism for JavaScriptCore to profile
which basic blocks have executed. This mechanism will then be
used by the Web Inspector to indicate which basic blocks
have and have not executed.

The profiling works by compiling in an op_profile_control_flow
at the start of every basic block. Then, whenever this op code
executes, we know that a particular basic block has executed.

When we tier up a CodeBlock that contains an op_profile_control_flow
that corresponds to an already executed basic block, we don't
have to emit code for that particular op_profile_control_flow
because the internal data structures used to keep track of
basic block locations has already recorded that the corresponding
op_profile_control_flow has executed.

  • CMakeLists.txt:
  • JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
  • JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
  • JavaScriptCore.xcodeproj/project.pbxproj:
  • bytecode/BytecodeList.json:
  • bytecode/BytecodeUseDef.h:

(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::dumpBytecode):
(JSC::CodeBlock::CodeBlock):

  • bytecode/Instruction.h:
  • bytecode/UnlinkedCodeBlock.cpp:

(JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):

  • bytecode/UnlinkedCodeBlock.h:

(JSC::UnlinkedCodeBlock::addOpProfileControlFlowBytecodeOffset):
(JSC::UnlinkedCodeBlock::opProfileControlFlowBytecodeOffsets):

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::emitProfileControlFlow):

  • bytecompiler/BytecodeGenerator.h:
  • bytecompiler/NodesCodegen.cpp:

(JSC::ConditionalNode::emitBytecode):
(JSC::IfElseNode::emitBytecode):
(JSC::WhileNode::emitBytecode):
(JSC::ForNode::emitBytecode):
(JSC::ContinueNode::emitBytecode):
(JSC::BreakNode::emitBytecode):
(JSC::ReturnNode::emitBytecode):
(JSC::CaseClauseNode::emitBytecode):
(JSC::SwitchNode::emitBytecode):
(JSC::ThrowNode::emitBytecode):
(JSC::TryNode::emitBytecode):
(JSC::ProgramNode::emitBytecode):
(JSC::FunctionNode::emitBytecode):

  • dfg/DFGAbstractInterpreterInlines.h:

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

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::parseBlock):

  • dfg/DFGCapabilities.cpp:

(JSC::DFG::capabilityLevel):

  • dfg/DFGClobberize.h:

(JSC::DFG::clobberize):

  • dfg/DFGDoesGC.cpp:

(JSC::DFG::doesGC):

  • dfg/DFGFixupPhase.cpp:

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

  • dfg/DFGNode.h:

(JSC::DFG::Node::basicBlockLocation):

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

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

  • dfg/DFGSafeToExecute.h:

(JSC::DFG::safeToExecute):

  • dfg/DFGSpeculativeJIT32_64.cpp:

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

  • dfg/DFGSpeculativeJIT64.cpp:

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

  • inspector/agents/InspectorRuntimeAgent.cpp:

(Inspector::InspectorRuntimeAgent::getRuntimeTypesForVariablesAtOffsets):

  • jit/JIT.cpp:

(JSC::JIT::privateCompileMainPass):

  • jit/JIT.h:
  • jit/JITOpcodes.cpp:

(JSC::JIT::emit_op_profile_control_flow):

  • jit/JITOpcodes32_64.cpp:

(JSC::JIT::emit_op_profile_control_flow):

  • jsc.cpp:

(GlobalObject::finishCreation):
(functionFindTypeForExpression):
(functionReturnTypeFor):
(functionDumpBasicBlockExecutionRanges):

  • llint/LowLevelInterpreter.asm:
  • parser/ASTBuilder.h:

(JSC::ASTBuilder::createFunctionExpr):
(JSC::ASTBuilder::createGetterOrSetterProperty):
(JSC::ASTBuilder::createFuncDeclStatement):
(JSC::ASTBuilder::endOffset):
(JSC::ASTBuilder::setStartOffset):

  • parser/NodeConstructors.h:

(JSC::Node::Node):

  • parser/Nodes.h:

(JSC::CaseClauseNode::setStartOffset):

  • parser/Parser.cpp:

(JSC::Parser<LexerType>::parseSwitchClauses):
(JSC::Parser<LexerType>::parseSwitchDefaultClause):
(JSC::Parser<LexerType>::parseBlockStatement):
(JSC::Parser<LexerType>::parseStatement):
(JSC::Parser<LexerType>::parseFunctionDeclaration):
(JSC::Parser<LexerType>::parseIfStatement):
(JSC::Parser<LexerType>::parseExpression):
(JSC::Parser<LexerType>::parseConditionalExpression):
(JSC::Parser<LexerType>::parseProperty):
(JSC::Parser<LexerType>::parseMemberExpression):

  • parser/SyntaxChecker.h:

(JSC::SyntaxChecker::createFunctionExpr):
(JSC::SyntaxChecker::createFuncDeclStatement):
(JSC::SyntaxChecker::createGetterOrSetterProperty):
(JSC::SyntaxChecker::operatorStackPop):

  • runtime/BasicBlockLocation.cpp: Added.

(JSC::BasicBlockLocation::BasicBlockLocation):
(JSC::BasicBlockLocation::insertGap):
(JSC::BasicBlockLocation::getExecutedRanges):
(JSC::BasicBlockLocation::dumpData):
(JSC::BasicBlockLocation::emitExecuteCode):

  • runtime/BasicBlockLocation.h: Added.

(JSC::BasicBlockLocation::startOffset):
(JSC::BasicBlockLocation::endOffset):
(JSC::BasicBlockLocation::setStartOffset):
(JSC::BasicBlockLocation::setEndOffset):
(JSC::BasicBlockLocation::hasExecuted):

  • runtime/CodeCache.cpp:

(JSC::CodeCache::getGlobalCodeBlock):

  • runtime/ControlFlowProfiler.cpp: Added.

(JSC::ControlFlowProfiler::~ControlFlowProfiler):
(JSC::ControlFlowProfiler::getBasicBlockLocation):
(JSC::ControlFlowProfiler::dumpData):
(JSC::ControlFlowProfiler::getBasicBlocksForSourceID):

  • runtime/ControlFlowProfiler.h: Added. This class is in

charge of generating BasicBlockLocations and also
providing an interface that the Web Inspector can use to ping
which basic blocks have executed based on the source id of a script.

(JSC::BasicBlockKey::BasicBlockKey):
(JSC::BasicBlockKey::isHashTableDeletedValue):
(JSC::BasicBlockKey::operator==):
(JSC::BasicBlockKey::hash):
(JSC::BasicBlockKeyHash::hash):
(JSC::BasicBlockKeyHash::equal):

  • runtime/Executable.cpp:

(JSC::ProgramExecutable::ProgramExecutable):
(JSC::ProgramExecutable::initializeGlobalProperties):

  • runtime/FunctionHasExecutedCache.cpp:

(JSC::FunctionHasExecutedCache::getUnexecutedFunctionRanges):

  • runtime/FunctionHasExecutedCache.h:
  • runtime/Options.h:
  • runtime/TypeProfiler.cpp:

(JSC::TypeProfiler::logTypesForTypeLocation):
(JSC::TypeProfiler::typeInformationForExpressionAtOffset):
(JSC::TypeProfiler::findLocation):
(JSC::TypeProfiler::dumpTypeProfilerData):

  • runtime/TypeProfiler.h:

(JSC::TypeProfiler::functionHasExecutedCache): Deleted.

  • runtime/VM.cpp:

(JSC::VM::VM):
(JSC::enableProfilerWithRespectToCount):
(JSC::disableProfilerWithRespectToCount):
(JSC::VM::enableTypeProfiler):
(JSC::VM::disableTypeProfiler):
(JSC::VM::enableControlFlowProfiler):
(JSC::VM::disableControlFlowProfiler):
(JSC::VM::dumpTypeProfilerData):

  • runtime/VM.h:

(JSC::VM::functionHasExecutedCache):
(JSC::VM::controlFlowProfiler):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp

    r176479 r176836  
    3131#include "CodeBlock.h"
    3232
     33#include "BasicBlockLocation.h"
    3334#include "BytecodeGenerator.h"
    3435#include "BytecodeUseDef.h"
     
    851852            break;
    852853        }
     854        case op_profile_control_flow: {
     855            BasicBlockLocation* basicBlockLocation = (++it)->u.basicBlockLocation;
     856            printLocationAndOp(out, exec, location, it, "profile_control_flow");
     857            out.printf("[%d, %d]", basicBlockLocation->startOffset(), basicBlockLocation->endOffset());
     858            break;
     859        }
    853860        case op_not: {
    854861            printUnaryOp(out, exec, location, it, "not");
     
    17291736    setNumParameters(unlinkedCodeBlock->numParameters());
    17301737
    1731     if (vm()->typeProfiler())
    1732         vm()->typeProfiler()->functionHasExecutedCache()->removeUnexecutedRange(m_ownerExecutable->sourceID(), m_ownerExecutable->typeProfilingStartOffset(), m_ownerExecutable->typeProfilingEndOffset());
     1738    if (vm()->typeProfiler() || vm()->controlFlowProfiler())
     1739        vm()->functionHasExecutedCache()->removeUnexecutedRange(m_ownerExecutable->sourceID(), m_ownerExecutable->typeProfilingStartOffset(), m_ownerExecutable->typeProfilingEndOffset());
    17331740
    17341741    setConstantRegisters(unlinkedCodeBlock->constantRegisters());
     
    17381745    for (size_t count = unlinkedCodeBlock->numberOfFunctionDecls(), i = 0; i < count; ++i) {
    17391746        UnlinkedFunctionExecutable* unlinkedExecutable = unlinkedCodeBlock->functionDecl(i);
    1740         if (vm()->typeProfiler())
    1741             vm()->typeProfiler()->functionHasExecutedCache()->insertUnexecutedRange(m_ownerExecutable->sourceID(), unlinkedExecutable->typeProfilingStartOffset(), unlinkedExecutable->typeProfilingEndOffset());
     1747        if (vm()->typeProfiler() || vm()->controlFlowProfiler())
     1748            vm()->functionHasExecutedCache()->insertUnexecutedRange(m_ownerExecutable->sourceID(), unlinkedExecutable->typeProfilingStartOffset(), unlinkedExecutable->typeProfilingEndOffset());
    17421749        unsigned lineCount = unlinkedExecutable->lineCount();
    17431750        unsigned firstLine = ownerExecutable->lineNo() + unlinkedExecutable->firstLineOffset();
     
    17561763    for (size_t count = unlinkedCodeBlock->numberOfFunctionExprs(), i = 0; i < count; ++i) {
    17571764        UnlinkedFunctionExecutable* unlinkedExecutable = unlinkedCodeBlock->functionExpr(i);
    1758         if (vm()->typeProfiler())
    1759             vm()->typeProfiler()->functionHasExecutedCache()->insertUnexecutedRange(m_ownerExecutable->sourceID(), unlinkedExecutable->typeProfilingStartOffset(), unlinkedExecutable->typeProfilingEndOffset());
     1765        if (vm()->typeProfiler() || vm()->controlFlowProfiler())
     1766            vm()->functionHasExecutedCache()->insertUnexecutedRange(m_ownerExecutable->sourceID(), unlinkedExecutable->typeProfilingStartOffset(), unlinkedExecutable->typeProfilingEndOffset());
    17601767        unsigned lineCount = unlinkedExecutable->lineCount();
    17611768        unsigned firstLine = ownerExecutable->lineNo() + unlinkedExecutable->firstLineOffset();
     
    21262133        i += opLength;
    21272134    }
     2135
     2136    if (vm()->controlFlowProfiler()) {
     2137        const Vector<size_t>& bytecodeOffsets = unlinkedCodeBlock->opProfileControlFlowBytecodeOffsets();
     2138        for (size_t i = 0, offsetsLength = bytecodeOffsets.size(); i < offsetsLength; i++) {
     2139            // Because op_profile_control_flow is emitted at the beginning of every basic block, finding
     2140            // the next op_profile_control_flow will give us the text range of a single basic block.
     2141            size_t startIdx = bytecodeOffsets[i];
     2142            RELEASE_ASSERT(vm()->interpreter->getOpcodeID(instructions[startIdx].u.opcode) == op_profile_control_flow);
     2143            int basicBlockStartOffset = instructions[startIdx + 1].u.operand;
     2144            int basicBlockEndOffset;
     2145            if (i + 1 < offsetsLength) {
     2146                size_t endIdx = bytecodeOffsets[i + 1];
     2147                RELEASE_ASSERT(vm()->interpreter->getOpcodeID(instructions[endIdx].u.opcode) == op_profile_control_flow);
     2148                basicBlockEndOffset = instructions[endIdx + 1].u.operand;
     2149            } else
     2150                basicBlockEndOffset = m_sourceOffset + m_ownerExecutable->source().length() - 1;
     2151
     2152            BasicBlockLocation* basicBlockLocation = vm()->controlFlowProfiler()->getBasicBlockLocation(m_ownerExecutable->sourceID(), basicBlockStartOffset, basicBlockEndOffset);
     2153
     2154            // Find all functions that are enclosed within the range: [basicBlockStartOffset, basicBlockEndOffset]
     2155            // and insert these functions' start/end offsets as gaps in the current BasicBlockLocation.
     2156            // This is necessary because in the original source text of a JavaScript program,
     2157            // function literals form new basic blocks boundaries, but they aren't represented
     2158            // inside the CodeBlock's instruction stream.
     2159            auto insertFunctionGaps = [basicBlockLocation, basicBlockStartOffset, basicBlockEndOffset] (const WriteBarrier<FunctionExecutable>& functionExecutable) {
     2160                const UnlinkedFunctionExecutable* executable = functionExecutable->unlinkedExecutable();
     2161                int functionStart = executable->typeProfilingStartOffset();
     2162                int functionEnd = executable->typeProfilingEndOffset();
     2163                if (functionStart >= basicBlockStartOffset && functionEnd <= basicBlockEndOffset)
     2164                    basicBlockLocation->insertGap(functionStart, functionEnd);
     2165            };
     2166
     2167            for (const WriteBarrier<FunctionExecutable>& executable : m_functionDecls)
     2168                insertFunctionGaps(executable);
     2169            for (const WriteBarrier<FunctionExecutable>& executable : m_functionExprs)
     2170                insertFunctionGaps(executable);
     2171
     2172            instructions[startIdx + 1].u.basicBlockLocation = basicBlockLocation;
     2173        }
     2174    }
     2175
    21282176    m_instructions = WTF::RefCountedArray<Instruction>(instructions);
    21292177
Note: See TracChangeset for help on using the changeset viewer.