uint32_t
OptimizationInfo::compilerWarmUpThreshold(JSScript *script, jsbytecode *pc) const
{
    JS_ASSERT(pc == nullptr || pc == script->code() || JSOp(*pc) == JSOP_LOOPENTRY);

    if (pc == script->code())
        pc = nullptr;

    uint32_t warmUpThreshold = compilerWarmUpThreshold_;
    if (js_JitOptions.forceDefaultIonWarmUpThreshold)
        warmUpThreshold = js_JitOptions.forcedDefaultIonWarmUpThreshold;

    // If the script is too large to compile on the main thread, we can still
    // compile it off thread. In these cases, increase the warm-up counter
    // threshold to improve the compilation's type information and hopefully
    // avoid later recompilation.

    if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE)
        warmUpThreshold *= (script->length() / (double) MAX_MAIN_THREAD_SCRIPT_SIZE);

    uint32_t numLocalsAndArgs = NumLocalsAndArgs(script);
    if (numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
        warmUpThreshold *= (numLocalsAndArgs / (double) MAX_MAIN_THREAD_LOCALS_AND_ARGS);

    if (!pc || js_JitOptions.eagerCompilation)
        return warmUpThreshold;

    // It's more efficient to enter outer loops, rather than inner loops, via OSR.
    // To accomplish this, we use a slightly higher threshold for inner loops.
    // Note that the loop depth is always > 0 so we will prefer non-OSR over OSR.
    uint32_t loopDepth = LoopEntryDepthHint(pc);
    JS_ASSERT(loopDepth > 0);
    return warmUpThreshold + loopDepth * 100;
}
Example #2
0
bool
LCovSource::writeScript(JSScript* script)
{
    numFunctionsFound_++;
    outFN_.printf("FN:%d,", script->lineno());
    if (!writeScriptName(outFN_, script))
        return false;
    outFN_.put("\n", 1);

    uint64_t hits = 0;
    ScriptCounts* sc = nullptr;
    if (script->hasScriptCounts()) {
        sc = &script->getScriptCounts();
        numFunctionsHit_++;
        const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(script->main()));
        outFNDA_.printf("FNDA:%" PRIu64 ",", counts->numExec());
        if (!writeScriptName(outFNDA_, script))
            return false;
        outFNDA_.put("\n", 1);

        // Set the hit count of the pre-main code to 1, if the function ever got
        // visited.
        hits = 1;
    }

    jsbytecode* snpc = script->code();
    jssrcnote* sn = script->notes();
    if (!SN_IS_TERMINATOR(sn))
        snpc += SN_DELTA(sn);

    size_t lineno = script->lineno();
    jsbytecode* end = script->codeEnd();
    size_t branchId = 0;
    size_t tableswitchExitOffset = 0;
    for (jsbytecode* pc = script->code(); pc != end; pc = GetNextPc(pc)) {
        JSOp op = JSOp(*pc);
        bool jump = IsJumpOpcode(op) || op == JSOP_TABLESWITCH;
        bool fallsthrough = BytecodeFallsThrough(op) && op != JSOP_GOSUB;

        // If the current script & pc has a hit-count report, then update the
        // current number of hits.
        if (sc) {
            const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(pc));
            if (counts)
                hits = counts->numExec();
        }

        // If we have additional source notes, walk all the source notes of the
        // current pc.
        if (snpc <= pc) {
            size_t oldLine = lineno;
            while (!SN_IS_TERMINATOR(sn) && snpc <= pc) {
                SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
                if (type == SRC_SETLINE)
                    lineno = size_t(GetSrcNoteOffset(sn, 0));
                else if (type == SRC_NEWLINE)
                    lineno++;
                else if (type == SRC_TABLESWITCH)
                    tableswitchExitOffset = GetSrcNoteOffset(sn, 0);

                sn = SN_NEXT(sn);
                snpc += SN_DELTA(sn);
            }

            if (oldLine != lineno && fallsthrough) {
                outDA_.printf("DA:%d,%" PRIu64 "\n", lineno, hits);

                // Count the number of lines instrumented & hit.
                numLinesInstrumented_++;
                if (hits)
                    numLinesHit_++;
            }
        }

        // If the current instruction has thrown, then decrement the hit counts
        // with the number of throws.
        if (sc) {
            const PCCounts* counts = sc->maybeGetThrowCounts(script->pcToOffset(pc));
            if (counts)
                hits -= counts->numExec();
        }

        // If the current pc corresponds to a conditional jump instruction, then reports
        // branch hits.
        if (jump && fallsthrough) {
            jsbytecode* fallthroughTarget = GetNextPc(pc);
            uint64_t fallthroughHits = 0;
            if (sc) {
                const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(fallthroughTarget));
                if (counts)
                    fallthroughHits = counts->numExec();
            }

            uint64_t taken = hits - fallthroughHits;
            outBRDA_.printf("BRDA:%d,%d,0,", lineno, branchId);
            if (taken)
                outBRDA_.printf("%d\n", taken);
            else
                outBRDA_.put("-\n", 2);

            outBRDA_.printf("BRDA:%d,%d,1,", lineno, branchId);
            if (fallthroughHits)
                outBRDA_.printf("%d\n", fallthroughHits);
            else
                outBRDA_.put("-\n", 2);

            // Count the number of branches, and the number of branches hit.
            numBranchesFound_ += 2;
            if (hits)
                numBranchesHit_ += !!taken + !!fallthroughHits;
            branchId++;
        }

        // If the current pc corresponds to a pre-computed switch case, then
        // reports branch hits for each case statement.
        if (jump && op == JSOP_TABLESWITCH) {
            MOZ_ASSERT(tableswitchExitOffset != 0);

            // Get the default and exit pc
            jsbytecode* exitpc = pc + tableswitchExitOffset;
            jsbytecode* defaultpc = pc + GET_JUMP_OFFSET(pc);
            MOZ_ASSERT(defaultpc > pc && defaultpc <= exitpc);

            // Get the low and high from the tableswitch
            int32_t low = GET_JUMP_OFFSET(pc + JUMP_OFFSET_LEN * 1);
            int32_t high = GET_JUMP_OFFSET(pc + JUMP_OFFSET_LEN * 2);
            MOZ_ASSERT(high - low + 1 >= 0);
            size_t numCases = high - low + 1;
            jsbytecode* jumpTable = pc + JUMP_OFFSET_LEN * 3;

            jsbytecode* firstcasepc = exitpc;
            for (size_t j = 0; j < numCases; j++) {
                jsbytecode* testpc = pc + GET_JUMP_OFFSET(jumpTable + JUMP_OFFSET_LEN * j);
                if (testpc < firstcasepc)
                    firstcasepc = testpc;
            }

            // Count the number of hits of the default branch, by subtracting
            // the number of hits of each cases.
            uint64_t defaultHits = hits;

            // Count the number of hits of the previous case entry.
            uint64_t fallsThroughHits = 0;

            // Record branches for each cases.
            size_t caseId = 0;
            for (size_t i = 0; i < numCases; i++) {
                jsbytecode* casepc = pc + GET_JUMP_OFFSET(jumpTable + JUMP_OFFSET_LEN * i);
                // The case is not present, and jumps to the default pc if used.
                if (casepc == pc)
                    continue;

                // PCs might not be in increasing order of case indexes.
                jsbytecode* lastcasepc = firstcasepc - 1;
                for (size_t j = 0; j < numCases; j++) {
                    jsbytecode* testpc = pc + GET_JUMP_OFFSET(jumpTable + JUMP_OFFSET_LEN * j);
                    if (lastcasepc < testpc && (testpc < casepc || (j < i && testpc == casepc)))
                        lastcasepc = testpc;
                }

                if (casepc != lastcasepc) {
                    // Case (i + low)
                    uint64_t caseHits = 0;
                    if (sc) {
                        const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(casepc));
                        if (counts)
                            caseHits = counts->numExec();

                        // Remove fallthrough.
                        fallsThroughHits = 0;
                        if (casepc != firstcasepc) {
                            jsbytecode* endpc = lastcasepc;
                            while (GetNextPc(endpc) < casepc)
                                endpc = GetNextPc(endpc);

                            if (BytecodeFallsThrough(JSOp(*endpc)))
                                fallsThroughHits = script->getHitCount(endpc);
                        }

                        caseHits -= fallsThroughHits;
                    }

                    outBRDA_.printf("BRDA:%d,%d,%d,", lineno, branchId, caseId);
                    if (caseHits)
                        outBRDA_.printf("%d\n", caseHits);
                    else
                        outBRDA_.put("-\n", 2);

                    numBranchesFound_++;
                    numBranchesHit_ += !!caseHits;
                    defaultHits -= caseHits;
                    caseId++;
                }
            }

            // Compute the number of hits of the default branch, if it has its
            // own case clause.
            bool defaultHasOwnClause = true;
            if (defaultpc != exitpc) {
                defaultHits = 0;

                // Look for the last case entry before the default pc.
                jsbytecode* lastcasepc = firstcasepc - 1;
                for (size_t j = 0; j < numCases; j++) {
                    jsbytecode* testpc = pc + GET_JUMP_OFFSET(jumpTable + JUMP_OFFSET_LEN * j);
                    if (lastcasepc < testpc && testpc <= defaultpc)
                        lastcasepc = testpc;
                }

                if (lastcasepc == defaultpc)
                    defaultHasOwnClause = false;

                // Look if the last case entry fallthrough to the default case,
                // in which case we have to remove the number of fallthrough
                // hits out of the default case hits.
                if (sc && lastcasepc != pc) {
                    jsbytecode* endpc = lastcasepc;
                    while (GetNextPc(endpc) < defaultpc)
                        endpc = GetNextPc(endpc);

                    if (BytecodeFallsThrough(JSOp(*endpc)))
                        fallsThroughHits = script->getHitCount(endpc);
                }

                if (sc) {
                    const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(defaultpc));
                    if (counts)
                        defaultHits = counts->numExec();
                }
                defaultHits -= fallsThroughHits;
            }

            if (defaultHasOwnClause) {
                outBRDA_.printf("BRDA:%d,%d,%d,", lineno, branchId, caseId);
                if (defaultHits)
                    outBRDA_.printf("%d\n", defaultHits);
                else
                    outBRDA_.put("-\n", 2);
                numBranchesFound_++;
                numBranchesHit_ += !!defaultHits;
            }

            // Increment the branch identifier, and go to the next instruction.
            branchId++;
            tableswitchExitOffset = 0;
        }
    }

    // Report any new OOM.
    if (outFN_.hadOutOfMemory() ||
        outFNDA_.hadOutOfMemory() ||
        outBRDA_.hadOutOfMemory() ||
        outDA_.hadOutOfMemory())
    {
        return false;
    }

    // If this script is the top-level script, then record it such that we can
    // assume that the code coverage report is complete, as this script has
    // references on all inner scripts.
    if (script->isTopLevel())
        hasTopLevelScript_ = true;

    return true;
}