inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlock, JITCode& jitCode, MacroAssemblerCodePtr* jitCodeWithArityCheck, unsigned osrEntryBytecodeIndex)
{
    SamplingRegion samplingRegion("DFG Compilation (Driver)");
    
    numCompilations++;
    
    ASSERT(codeBlock);
    ASSERT(codeBlock->alternative());
    ASSERT(codeBlock->alternative()->getJITType() == JITCode::BaselineJIT);

    ASSERT(osrEntryBytecodeIndex != UINT_MAX);

    if (!Options::useDFGJIT())
        return false;

    if (!Options::bytecodeRangeToDFGCompile().isInRange(codeBlock->instructionCount()))
        return false;
    
    if (logCompilationChanges())
        dataLog("DFG compiling ", *codeBlock, ", number of instructions = ", codeBlock->instructionCount(), "\n");
    
    // Derive our set of must-handle values. The compilation must be at least conservative
    // enough to allow for OSR entry with these values.
    unsigned numVarsWithValues;
    if (osrEntryBytecodeIndex)
        numVarsWithValues = codeBlock->m_numVars;
    else
        numVarsWithValues = 0;
    Operands<JSValue> mustHandleValues(codeBlock->numParameters(), numVarsWithValues);
    for (size_t i = 0; i < mustHandleValues.size(); ++i) {
        int operand = mustHandleValues.operandForIndex(i);
        if (operandIsArgument(operand)
            && !operandToArgument(operand)
            && compileMode == CompileFunction
            && codeBlock->specializationKind() == CodeForConstruct) {
            // Ugh. If we're in a constructor, the 'this' argument may hold garbage. It will
            // also never be used. It doesn't matter what we put into the value for this,
            // but it has to be an actual value that can be grokked by subsequent DFG passes,
            // so we sanitize it here by turning it into Undefined.
            mustHandleValues[i] = jsUndefined();
        } else
            mustHandleValues[i] = exec->uncheckedR(operand).jsValue();
    }
    
    Graph dfg(exec->vm(), codeBlock, osrEntryBytecodeIndex, mustHandleValues);
    if (!parse(exec, dfg))
        return false;
    
    // By this point the DFG bytecode parser will have potentially mutated various tables
    // in the CodeBlock. This is a good time to perform an early shrink, which is more
    // powerful than a late one. It's safe to do so because we haven't generated any code
    // that references any of the tables directly, yet.
    codeBlock->shrinkToFit(CodeBlock::EarlyShrink);

    if (validationEnabled())
    validate(dfg);
    
    performCPSRethreading(dfg);
    performUnification(dfg);
    performPredictionInjection(dfg);
    
    if (validationEnabled())
        validate(dfg);
    
    performBackwardsPropagation(dfg);
    performPredictionPropagation(dfg);
    performFixup(dfg);
    performTypeCheckHoisting(dfg);
    
    dfg.m_fixpointState = FixpointNotConverged;

    performCSE(dfg);
    performArgumentsSimplification(dfg);
    performCPSRethreading(dfg); // This should usually be a no-op since CSE rarely dethreads, and arguments simplification rarely does anything.
        performCFA(dfg);
    performConstantFolding(dfg);
    performCFGSimplification(dfg);

    dfg.m_fixpointState = FixpointConverged;

    performStoreElimination(dfg);
    performCPSRethreading(dfg);
    performDCE(dfg);
    performVirtualRegisterAllocation(dfg);

    GraphDumpMode modeForFinalValidate = DumpGraph;
    if (verboseCompilationEnabled()) {
        dataLogF("Graph after optimization:\n");
    dfg.dump();
    modeForFinalValidate = DontDumpGraph;
    }
    if (validationEnabled())
    validate(dfg, modeForFinalValidate);
    
    JITCompiler dataFlowJIT(dfg);
    bool result;
    if (compileMode == CompileFunction) {
        ASSERT(jitCodeWithArityCheck);
        
        result = dataFlowJIT.compileFunction(jitCode, *jitCodeWithArityCheck);
    } else {
        ASSERT(compileMode == CompileOther);
        ASSERT(!jitCodeWithArityCheck);
        
        result = dataFlowJIT.compile(jitCode);
    }
    
    return result;
}
Beispiel #2
0
void Graph::dump(const char* prefix, NodeIndex nodeIndex)
{
    Node& node = at(nodeIndex);
    NodeType op = node.op();

    unsigned refCount = node.refCount();
    bool skipped = !refCount;
    bool mustGenerate = node.mustGenerate();
    if (mustGenerate)
        --refCount;
    
    dataLog("%s", prefix);
    printNodeWhiteSpace(node);

    // Example/explanation of dataflow dump output
    //
    //   14:   <!2:7>  GetByVal(@3, @13)
    //   ^1     ^2 ^3     ^4       ^5
    //
    // (1) The nodeIndex of this operation.
    // (2) The reference count. The number printed is the 'real' count,
    //     not including the 'mustGenerate' ref. If the node is
    //     'mustGenerate' then the count it prefixed with '!'.
    // (3) The virtual register slot assigned to this node.
    // (4) The name of the operation.
    // (5) The arguments to the operation. The may be of the form:
    //         @#   - a NodeIndex referencing a prior node in the graph.
    //         arg# - an argument number.
    //         $#   - the index in the CodeBlock of a constant { for numeric constants the value is displayed | for integers, in both decimal and hex }.
    //         id#  - the index in the CodeBlock of an identifier { if codeBlock is passed to dump(), the string representation is displayed }.
    //         var# - the index of a var on the global object, used by GetGlobalVar/PutGlobalVar operations.
    dataLog("% 4d:%s<%c%u:", (int)nodeIndex, skipped ? "  skipped  " : "           ", mustGenerate ? '!' : ' ', refCount);
    if (node.hasResult() && !skipped && node.hasVirtualRegister())
        dataLog("%u", node.virtualRegister());
    else
        dataLog("-");
    dataLog(">\t%s(", opName(op));
    bool hasPrinted = false;
    if (node.flags() & NodeHasVarArgs) {
        for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++) {
            if (hasPrinted)
                dataLog(", ");
            else
                hasPrinted = true;
            dataLog("%s@%u%s",
                    useKindToString(m_varArgChildren[childIdx].useKind()),
                    m_varArgChildren[childIdx].index(),
                    speculationToAbbreviatedString(
                        at(m_varArgChildren[childIdx]).prediction()));
        }
    } else {
        if (!!node.child1()) {
            dataLog("%s@%u%s",
                    useKindToString(node.child1().useKind()),
                    node.child1().index(),
                    speculationToAbbreviatedString(at(node.child1()).prediction()));
        }
        if (!!node.child2()) {
            dataLog(", %s@%u%s",
                    useKindToString(node.child2().useKind()),
                    node.child2().index(),
                    speculationToAbbreviatedString(at(node.child2()).prediction()));
        }
        if (!!node.child3()) {
            dataLog(", %s@%u%s",
                    useKindToString(node.child3().useKind()),
                    node.child3().index(),
                    speculationToAbbreviatedString(at(node.child3()).prediction()));
        }
        hasPrinted = !!node.child1();
    }

    if (strlen(nodeFlagsAsString(node.flags()))) {
        dataLog("%s%s", hasPrinted ? ", " : "", nodeFlagsAsString(node.flags()));
        hasPrinted = true;
    }
    if (node.hasArrayMode()) {
        dataLog("%s%s", hasPrinted ? ", " : "", modeToString(node.arrayMode()));
        hasPrinted = true;
    }
    if (node.hasVarNumber()) {
        dataLog("%svar%u", hasPrinted ? ", " : "", node.varNumber());
        hasPrinted = true;
    }
    if (node.hasRegisterPointer()) {
        dataLog(
            "%sglobal%u(%p)", hasPrinted ? ", " : "",
            globalObjectFor(node.codeOrigin)->findRegisterIndex(node.registerPointer()),
            node.registerPointer());
        hasPrinted = true;
    }
    if (node.hasIdentifier()) {
        dataLog("%sid%u{%s}", hasPrinted ? ", " : "", node.identifierNumber(), m_codeBlock->identifier(node.identifierNumber()).ustring().utf8().data());
        hasPrinted = true;
    }
    if (node.hasStructureSet()) {
        for (size_t i = 0; i < node.structureSet().size(); ++i) {
            dataLog("%sstruct(%p)", hasPrinted ? ", " : "", node.structureSet()[i]);
            hasPrinted = true;
        }
    }
    if (node.hasStructure()) {
        dataLog("%sstruct(%p)", hasPrinted ? ", " : "", node.structure());
        hasPrinted = true;
    }
    if (node.hasStructureTransitionData()) {
        dataLog("%sstruct(%p -> %p)", hasPrinted ? ", " : "", node.structureTransitionData().previousStructure, node.structureTransitionData().newStructure);
        hasPrinted = true;
    }
    if (node.hasStorageAccessData()) {
        StorageAccessData& storageAccessData = m_storageAccessData[node.storageAccessDataIndex()];
        dataLog("%sid%u{%s}", hasPrinted ? ", " : "", storageAccessData.identifierNumber, m_codeBlock->identifier(storageAccessData.identifierNumber).ustring().utf8().data());
        
        dataLog(", %lu", static_cast<unsigned long>(storageAccessData.offset));
        hasPrinted = true;
    }
    ASSERT(node.hasVariableAccessData() == node.hasLocal());
    if (node.hasVariableAccessData()) {
        VariableAccessData* variableAccessData = node.variableAccessData();
        int operand = variableAccessData->operand();
        if (operandIsArgument(operand))
            dataLog("%sarg%u(%s)", hasPrinted ? ", " : "", operandToArgument(operand), nameOfVariableAccessData(variableAccessData));
        else
            dataLog("%sr%u(%s)", hasPrinted ? ", " : "", operand, nameOfVariableAccessData(variableAccessData));
        hasPrinted = true;
    }
    if (node.hasConstantBuffer()) {
        if (hasPrinted)
            dataLog(", ");
        dataLog("%u:[", node.startConstant());
        for (unsigned i = 0; i < node.numConstants(); ++i) {
            if (i)
                dataLog(", ");
            dataLog("%s", m_codeBlock->constantBuffer(node.startConstant())[i].description());
        }
        dataLog("]");
        hasPrinted = true;
    }
    if (op == JSConstant) {
        dataLog("%s$%u", hasPrinted ? ", " : "", node.constantNumber());
        JSValue value = valueOfJSConstant(nodeIndex);
        dataLog(" = %s", value.description());
        hasPrinted = true;
    }
    if (op == WeakJSConstant) {
        dataLog("%s%p", hasPrinted ? ", " : "", node.weakConstant());
        hasPrinted = true;
    }
    if  (node.isBranch() || node.isJump()) {
        dataLog("%sT:#%u", hasPrinted ? ", " : "", node.takenBlockIndex());
        hasPrinted = true;
    }
    if  (node.isBranch()) {
        dataLog("%sF:#%u", hasPrinted ? ", " : "", node.notTakenBlockIndex());
        hasPrinted = true;
    }
    dataLog("%sbc#%u", hasPrinted ? ", " : "", node.codeOrigin.bytecodeIndex);
    hasPrinted = true;
    (void)hasPrinted;
    
    dataLog(")");

    if (!skipped) {
        if (node.hasVariableAccessData())
            dataLog("  predicting %s%s", speculationToString(node.variableAccessData()->prediction()), node.variableAccessData()->shouldUseDoubleFormat() ? ", forcing double" : "");
        else if (node.hasHeapPrediction())
            dataLog("  predicting %s", speculationToString(node.getHeapPrediction()));
    }
    
    dataLog("\n");
}
Beispiel #3
0
static CompilationResult compileImpl(
    ExecState* exec, CodeBlock* codeBlock, CompilationMode mode,
    unsigned osrEntryBytecodeIndex, PassRefPtr<DeferredCompilationCallback> callback,
    Worklist* worklist)
{
    SamplingRegion samplingRegion("DFG Compilation (Driver)");
    
    numCompilations++;
    
    ASSERT(codeBlock);
    ASSERT(codeBlock->alternative());
    ASSERT(codeBlock->alternative()->jitType() == JITCode::BaselineJIT);
    
    ASSERT(osrEntryBytecodeIndex != UINT_MAX);

    if (!Options::useDFGJIT() || !MacroAssembler::supportsFloatingPoint())
        return CompilationFailed;

    if (!Options::bytecodeRangeToDFGCompile().isInRange(codeBlock->instructionCount()))
        return CompilationFailed;
    
    if (logCompilationChanges())
        dataLog("DFG(Driver) compiling ", *codeBlock, ", number of instructions = ", codeBlock->instructionCount(), "\n");
    
    VM& vm = exec->vm();
    
    // Make sure that any stubs that the DFG is going to use are initialized. We want to
    // make sure that al JIT code generation does finalization on the main thread.
    vm.getCTIStub(osrExitGenerationThunkGenerator);
    vm.getCTIStub(throwExceptionFromCallSlowPathGenerator);
    vm.getCTIStub(linkCallThunkGenerator);
    vm.getCTIStub(linkConstructThunkGenerator);
    vm.getCTIStub(linkClosureCallThunkGenerator);
    vm.getCTIStub(virtualCallThunkGenerator);
    vm.getCTIStub(virtualConstructThunkGenerator);
#if ENABLE(FTL_JIT)
    vm.getCTIStub(FTL::osrExitGenerationThunkGenerator);
#endif
    
    // Derive our set of must-handle values. The compilation must be at least conservative
    // enough to allow for OSR entry with these values.
    unsigned numVarsWithValues;
    if (osrEntryBytecodeIndex)
        numVarsWithValues = codeBlock->m_numVars;
    else
        numVarsWithValues = 0;
    RefPtr<Plan> plan = adoptRef(
        new Plan(codeBlock, mode, osrEntryBytecodeIndex, numVarsWithValues));
    for (size_t i = 0; i < plan->mustHandleValues.size(); ++i) {
        int operand = plan->mustHandleValues.operandForIndex(i);
        if (operandIsArgument(operand)
            && !operandToArgument(operand)
            && codeBlock->codeType() == FunctionCode
            && codeBlock->specializationKind() == CodeForConstruct) {
            // Ugh. If we're in a constructor, the 'this' argument may hold garbage. It will
            // also never be used. It doesn't matter what we put into the value for this,
            // but it has to be an actual value that can be grokked by subsequent DFG passes,
            // so we sanitize it here by turning it into Undefined.
            plan->mustHandleValues[i] = jsUndefined();
        } else
            plan->mustHandleValues[i] = exec->uncheckedR(operand).jsValue();
    }
    
    if (worklist) {
        plan->callback = callback;
        if (logCompilationChanges())
            dataLog("Deferring DFG compilation of ", *codeBlock, " with queue length ", worklist->queueLength(), ".\n");
        worklist->enqueue(plan);
        return CompilationDeferred;
    }
    
    plan->compileInThread(*vm.dfgState);
    return plan->finalizeWithoutNotifyingCallback();
}