bool canHandleOpcodes(CodeBlock* codeBlock)
{
    Interpreter* interpreter = codeBlock->globalData()->interpreter;
    Instruction* instructionsBegin = codeBlock->instructions().begin();
    unsigned instructionCount = codeBlock->instructions().size();
    
    for (unsigned bytecodeOffset = 0; bytecodeOffset < instructionCount; ) {
        switch (interpreter->getOpcodeID(instructionsBegin[bytecodeOffset].u.opcode)) {
#define DEFINE_OP(opcode, length)               \
        case opcode:                            \
            if (!canHandleOpcode(opcode)) {     \
                debugFail(codeBlock, opcode);   \
                return false;                   \
            }                                   \
            bytecodeOffset += length;           \
            break;
            FOR_EACH_OPCODE_ID(DEFINE_OP)
#undef DEFINE_OP
        default:
            ASSERT_NOT_REACHED();
            break;
        }
    }
    
    return true;
}
ReturnType canHandleOpcodes(CodeBlock* codeBlock, ReturnType initialValue)
{
    Interpreter* interpreter = codeBlock->vm()->interpreter;
    Instruction* instructionsBegin = codeBlock->instructions().begin();
    unsigned instructionCount = codeBlock->instructions().size();
    ReturnType result = initialValue;
    
    for (unsigned bytecodeOffset = 0; bytecodeOffset < instructionCount; ) {
        switch (interpreter->getOpcodeID(instructionsBegin[bytecodeOffset].u.opcode)) {
#define DEFINE_OP(opcode, length) \
        case opcode: { \
            ReturnType current = canHandleOpcode( \
                opcode, codeBlock, instructionsBegin + bytecodeOffset); \
            if (current < result) { \
                result = current; \
                debugFail(codeBlock, opcode, current); \
            } \
            bytecodeOffset += length; \
            break; \
        }
            FOR_EACH_OPCODE_ID(DEFINE_OP)
#undef DEFINE_OP
        default:
            RELEASE_ASSERT_NOT_REACHED();
            break;
        }
    }
    
    return result;
}
void BytecodeLivenessAnalysis::dumpResults()
{
    Interpreter* interpreter = m_codeBlock->vm()->interpreter;
    Instruction* instructionsBegin = m_codeBlock->instructions().begin();
    for (unsigned i = 0; i < m_basicBlocks.size(); i++) {
        BytecodeBasicBlock* block = m_basicBlocks[i].get();
        dataLogF("\nBytecode basic block %u: %p (offset: %u, length: %u)\n", i, block, block->leaderBytecodeOffset(), block->totalBytecodeLength());
        dataLogF("Predecessors: ");
        for (unsigned j = 0; j < block->predecessors().size(); j++) {
            BytecodeBasicBlock* predecessor = block->predecessors()[j];
            dataLogF("%p ", predecessor);
        }
        dataLogF("\n");
        dataLogF("Successors: ");
        for (unsigned j = 0; j < block->successors().size(); j++) {
            BytecodeBasicBlock* successor = block->successors()[j];
            dataLogF("%p ", successor);
        }
        dataLogF("\n");
        if (block->isEntryBlock()) {
            dataLogF("Entry block %p\n", block);
            continue;
        }
        if (block->isExitBlock()) {
            dataLogF("Exit block: %p\n", block);
            continue;
        }
        for (unsigned bytecodeOffset = block->leaderBytecodeOffset(); bytecodeOffset < block->leaderBytecodeOffset() + block->totalBytecodeLength();) {
            const Instruction* currentInstruction = &instructionsBegin[bytecodeOffset];

            dataLogF("Live variables: ");
            FastBitVector liveBefore = getLivenessInfoAtBytecodeOffset(bytecodeOffset);
            for (unsigned j = 0; j < liveBefore.numBits(); j++) {
                if (liveBefore.get(j))
                    dataLogF("%u ", j);
            }
            dataLogF("\n");
            m_codeBlock->dumpBytecode(WTF::dataFile(), m_codeBlock->globalObject()->globalExec(), instructionsBegin, currentInstruction);

            OpcodeID opcodeID = interpreter->getOpcodeID(instructionsBegin[bytecodeOffset].u.opcode);
            unsigned opcodeLength = opcodeLengths[opcodeID];
            bytecodeOffset += opcodeLength;
        }

        dataLogF("Live variables: ");
        FastBitVector liveAfter = block->out();
        for (unsigned j = 0; j < liveAfter.numBits(); j++) {
            if (liveAfter.get(j))
                dataLogF("%u ", j);
        }
        dataLogF("\n");
    }
}
static void stepOverInstruction(CodeBlock* codeBlock, BytecodeBasicBlock* block, Vector<std::unique_ptr<BytecodeBasicBlock>>& basicBlocks, unsigned bytecodeOffset, const UseFunctor& use, const DefFunctor& def)
{
    // This abstractly execute the instruction in reverse. Instructions logically first use operands and
    // then define operands. This logical ordering is necessary for operations that use and def the same
    // operand, like:
    //
    //     op_add loc1, loc1, loc2
    //
    // The use of loc1 happens before the def of loc1. That's a semantic requirement since the add
    // operation cannot travel forward in time to read the value that it will produce after reading that
    // value. Since we are executing in reverse, this means that we must do defs before uses (reverse of
    // uses before defs).
    //
    // Since this is a liveness analysis, this ordering ends up being particularly important: if we did
    // uses before defs, then the add operation above would appear to not have loc1 live, since we'd
    // first add it to the out set (the use), and then we'd remove it (the def).
    
    computeDefsForBytecodeOffset(
        codeBlock, block, bytecodeOffset,
        [&] (CodeBlock* codeBlock, Instruction*, OpcodeID, int operand) {
            if (isValidRegisterForLiveness(codeBlock, operand))
                def(VirtualRegister(operand).toLocal());
        });

    computeUsesForBytecodeOffset(
        codeBlock, block, bytecodeOffset,
        [&] (CodeBlock* codeBlock, Instruction*, OpcodeID, int operand) {
            if (isValidRegisterForLiveness(codeBlock, operand))
                use(VirtualRegister(operand).toLocal());
        });

    // If we have an exception handler, we want the live-in variables of the 
    // exception handler block to be included in the live-in of this particular bytecode.
    if (HandlerInfo* handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset)) {
        // FIXME: This resume check should not be needed.
        // https://bugs.webkit.org/show_bug.cgi?id=159281
        Interpreter* interpreter = codeBlock->vm()->interpreter;
        Instruction* instructionsBegin = codeBlock->instructions().begin();
        Instruction* instruction = &instructionsBegin[bytecodeOffset];
        OpcodeID opcodeID = interpreter->getOpcodeID(instruction->u.opcode);
        if (opcodeID != op_resume) {
            BytecodeBasicBlock* handlerBlock = findBasicBlockWithLeaderOffset(basicBlocks, handler->target);
            ASSERT(handlerBlock);
            handlerBlock->in().forEachSetBit(use);
        }
    }
}
Example #5
0
void computePreciseJumpTargetsInternal(Block* codeBlock, Instruction* instructionsBegin, unsigned instructionCount, Vector<unsigned, vectorSize>& out)
{
    ASSERT(out.isEmpty());
    
    // We will derive a superset of the jump targets that the code block thinks it has.
    // So, if the code block claims there are none, then we are done.
    if (Mode == ComputePreciseJumpTargetsMode::FollowCodeBlockClaim && !codeBlock->numberOfJumpTargets())
        return;
    
    for (unsigned i = codeBlock->numberOfExceptionHandlers(); i--;) {
        out.append(codeBlock->exceptionHandler(i).target);
        out.append(codeBlock->exceptionHandler(i).start);
        out.append(codeBlock->exceptionHandler(i).end);
    }

    Interpreter* interpreter = codeBlock->vm()->interpreter;
    for (unsigned bytecodeOffset = 0; bytecodeOffset < instructionCount;) {
        OpcodeID opcodeID = interpreter->getOpcodeID(instructionsBegin[bytecodeOffset]);
        getJumpTargetsForBytecodeOffset(codeBlock, interpreter, instructionsBegin, bytecodeOffset, out);
        bytecodeOffset += opcodeLengths[opcodeID];
    }
    
    std::sort(out.begin(), out.end());
    
    // We will have duplicates, and we must remove them.
    unsigned toIndex = 0;
    unsigned fromIndex = 0;
    unsigned lastValue = UINT_MAX;
    while (fromIndex < out.size()) {
        unsigned value = out[fromIndex++];
        if (value == lastValue)
            continue;
        out[toIndex++] = value;
        lastValue = value;
    }
    out.resize(toIndex);
    out.shrinkToFit();
}
Example #6
0
void computeBytecodeBasicBlocks(CodeBlock* codeBlock, Vector<RefPtr<BytecodeBasicBlock> >& basicBlocks)
{
    Vector<unsigned, 32> jumpTargets;
    computePreciseJumpTargets(codeBlock, jumpTargets);

    // Create the entry and exit basic blocks.
    BytecodeBasicBlock* entry = new BytecodeBasicBlock(BytecodeBasicBlock::EntryBlock);
    basicBlocks.append(adoptRef(entry));
    BytecodeBasicBlock* exit = new BytecodeBasicBlock(BytecodeBasicBlock::ExitBlock);

    // Find basic block boundaries.
    BytecodeBasicBlock* current = new BytecodeBasicBlock(0, 0);
    linkBlocks(entry, current);
    basicBlocks.append(adoptRef(current));

    bool nextInstructionIsLeader = false;

    Interpreter* interpreter = codeBlock->vm()->interpreter;
    Instruction* instructionsBegin = codeBlock->instructions().begin();
    unsigned instructionCount = codeBlock->instructions().size();
    for (unsigned bytecodeOffset = 0; bytecodeOffset < instructionCount;) {
        OpcodeID opcodeID = interpreter->getOpcodeID(instructionsBegin[bytecodeOffset].u.opcode);
        unsigned opcodeLength = opcodeLengths[opcodeID];

        bool createdBlock = false;
        // If the current bytecode is a jump target, then it's the leader of its own basic block.
        if (isJumpTarget(opcodeID, jumpTargets, bytecodeOffset) || nextInstructionIsLeader) {
            BytecodeBasicBlock* block = new BytecodeBasicBlock(bytecodeOffset, opcodeLength);
            basicBlocks.append(adoptRef(block));
            current = block;
            createdBlock = true;
            nextInstructionIsLeader = false;
            bytecodeOffset += opcodeLength;
        }

        // If the current bytecode is a branch or a return, then the next instruction is the leader of its own basic block.
        if (isBranch(opcodeID) || isTerminal(opcodeID) || isThrow(opcodeID))
            nextInstructionIsLeader = true;

        if (createdBlock)
            continue;

        // Otherwise, just add to the length of the current block.
        current->addBytecodeLength(opcodeLength);
        bytecodeOffset += opcodeLength;
    }

    // Link basic blocks together.
    for (unsigned i = 0; i < basicBlocks.size(); i++) {
        BytecodeBasicBlock* block = basicBlocks[i].get();

        if (block->isEntryBlock() || block->isExitBlock())
            continue;

        bool fallsThrough = true; 
        for (unsigned bytecodeOffset = block->leaderBytecodeOffset(); bytecodeOffset < block->leaderBytecodeOffset() + block->totalBytecodeLength();) {
            const Instruction& currentInstruction = instructionsBegin[bytecodeOffset];
            OpcodeID opcodeID = interpreter->getOpcodeID(currentInstruction.u.opcode);
            unsigned opcodeLength = opcodeLengths[opcodeID];
            // If we found a terminal bytecode, link to the exit block.
            if (isTerminal(opcodeID)) {
                ASSERT(bytecodeOffset + opcodeLength == block->leaderBytecodeOffset() + block->totalBytecodeLength());
                linkBlocks(block, exit);
                fallsThrough = false;
                break;
            }

            // If we found a throw, get the HandlerInfo for this instruction to see where we will jump. 
            // If there isn't one, treat this throw as a terminal. This is true even if we have a finally
            // block because the finally block will create its own catch, which will generate a HandlerInfo.
            if (isThrow(opcodeID)) {
                ASSERT(bytecodeOffset + opcodeLength == block->leaderBytecodeOffset() + block->totalBytecodeLength());
                HandlerInfo* handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset);
                fallsThrough = false;
                if (!handler) {
                    linkBlocks(block, exit);
                    break;
                }
                for (unsigned i = 0; i < basicBlocks.size(); i++) {
                    BytecodeBasicBlock* otherBlock = basicBlocks[i].get();
                    if (handler->target == otherBlock->leaderBytecodeOffset()) {
                        linkBlocks(block, otherBlock);
                        break;
                    }
                }
                break;
            }

            // If we found a branch, link to the block(s) that we jump to.
            if (isBranch(opcodeID)) {
                ASSERT(bytecodeOffset + opcodeLength == block->leaderBytecodeOffset() + block->totalBytecodeLength());
                Vector<unsigned, 1> bytecodeOffsetsJumpedTo;
                findJumpTargetsForBytecodeOffset(codeBlock, bytecodeOffset, bytecodeOffsetsJumpedTo);

                for (unsigned i = 0; i < basicBlocks.size(); i++) {
                    BytecodeBasicBlock* otherBlock = basicBlocks[i].get();
                    if (bytecodeOffsetsJumpedTo.contains(otherBlock->leaderBytecodeOffset()))
                        linkBlocks(block, otherBlock);
                }

                if (isUnconditionalBranch(opcodeID))
                    fallsThrough = false;

                break;
            }
            bytecodeOffset += opcodeLength;
        }

        // If we fall through then link to the next block in program order.
        if (fallsThrough) {
            ASSERT(i + 1 < basicBlocks.size());
            BytecodeBasicBlock* nextBlock = basicBlocks[i + 1].get();
            linkBlocks(block, nextBlock);
        }
    }

    basicBlocks.append(adoptRef(exit));
}
void computePreciseJumpTargets(CodeBlock* codeBlock, Vector<unsigned, 32>& out)
{
    ASSERT(out.isEmpty());
    
    // We will derive a superset of the jump targets that the code block thinks it has.
    // So, if the code block claims there are none, then we are done.
    if (!codeBlock->numberOfJumpTargets())
        return;
    
    for (unsigned i = codeBlock->numberOfExceptionHandlers(); i--;)
        out.append(codeBlock->exceptionHandler(i).target);
    
    Interpreter* interpreter = codeBlock->vm()->interpreter;
    Instruction* instructionsBegin = codeBlock->instructions().begin();
    unsigned instructionCount = codeBlock->instructions().size();
    for (unsigned bytecodeOffset = 0; bytecodeOffset < instructionCount;) {
        OpcodeID opcodeID = interpreter->getOpcodeID(instructionsBegin[bytecodeOffset].u.opcode);
        Instruction* current = instructionsBegin + bytecodeOffset;
        switch (opcodeID) {
        case op_jmp:
            out.append(bytecodeOffset + current[1].u.operand);
            break;
        case op_jtrue:
        case op_jfalse:
        case op_jeq_null:
        case op_jneq_null:
            out.append(bytecodeOffset + current[2].u.operand);
            break;
        case op_jneq_ptr:
        case op_jless:
        case op_jlesseq:
        case op_jgreater:
        case op_jgreatereq:
        case op_jnless:
        case op_jnlesseq:
        case op_jngreater:
        case op_jngreatereq:
            out.append(bytecodeOffset + current[3].u.operand);
            break;
        case op_switch_imm:
            addSimpleSwitchTargets(codeBlock->immediateSwitchJumpTable(current[1].u.operand), bytecodeOffset, out);
            out.append(bytecodeOffset + current[2].u.operand);
            break;
        case op_switch_char:
            addSimpleSwitchTargets(codeBlock->characterSwitchJumpTable(current[1].u.operand), bytecodeOffset, out);
            out.append(bytecodeOffset + current[2].u.operand);
            break;
        case op_switch_string: {
            StringJumpTable& table = codeBlock->stringSwitchJumpTable(current[1].u.operand);
            StringJumpTable::StringOffsetTable::iterator iter = table.offsetTable.begin();
            StringJumpTable::StringOffsetTable::iterator end = table.offsetTable.end();
            for (; iter != end; ++iter)
                out.append(bytecodeOffset + iter->value.branchOffset);
            out.append(bytecodeOffset + current[2].u.operand);
            break;
        }
        case op_get_pnames:
            out.append(bytecodeOffset + current[5].u.operand);
            break;
        case op_next_pname:
            out.append(bytecodeOffset + current[6].u.operand);
            break;
        case op_check_has_instance:
            out.append(bytecodeOffset + current[4].u.operand);
            break;
        case op_loop_hint:
            out.append(bytecodeOffset);
            break;
        default:
            break;
        }
        bytecodeOffset += opcodeLengths[opcodeID];
    }
    
    std::sort(out.begin(), out.end());
    
    // We will have duplicates, and we must remove them.
    unsigned toIndex = 0;
    unsigned fromIndex = 0;
    unsigned lastValue = UINT_MAX;
    while (fromIndex < out.size()) {
        unsigned value = out[fromIndex++];
        if (value == lastValue)
            continue;
        out[toIndex++] = value;
        lastValue = value;
    }
    out.resize(toIndex);
}
Example #8
0
void BytecodeBasicBlock::computeImpl(Block* codeBlock, Instruction* instructionsBegin, unsigned instructionCount, Vector<std::unique_ptr<BytecodeBasicBlock>>& basicBlocks)
{
    Vector<unsigned, 32> jumpTargets;
    computePreciseJumpTargets(codeBlock, instructionsBegin, instructionCount, jumpTargets);

    auto appendBlock = [&] (std::unique_ptr<BytecodeBasicBlock>&& block) {
        block->m_index = basicBlocks.size();
        basicBlocks.append(WTFMove(block));
    };

    auto linkBlocks = [&] (BytecodeBasicBlock* from, BytecodeBasicBlock* to) {
        from->addSuccessor(to);
    };

    // Create the entry and exit basic blocks.
    basicBlocks.reserveCapacity(jumpTargets.size() + 2);

    auto entry = std::make_unique<BytecodeBasicBlock>(BytecodeBasicBlock::EntryBlock);
    auto firstBlock = std::make_unique<BytecodeBasicBlock>(0, 0);
    linkBlocks(entry.get(), firstBlock.get());

    appendBlock(WTFMove(entry));
    BytecodeBasicBlock* current = firstBlock.get();
    appendBlock(WTFMove(firstBlock));

    auto exit = std::make_unique<BytecodeBasicBlock>(BytecodeBasicBlock::ExitBlock);

    bool nextInstructionIsLeader = false;

    Interpreter* interpreter = codeBlock->vm()->interpreter;
    for (unsigned bytecodeOffset = 0; bytecodeOffset < instructionCount;) {
        OpcodeID opcodeID = interpreter->getOpcodeID(instructionsBegin[bytecodeOffset]);
        unsigned opcodeLength = opcodeLengths[opcodeID];

        bool createdBlock = false;
        // If the current bytecode is a jump target, then it's the leader of its own basic block.
        if (isJumpTarget(opcodeID, jumpTargets, bytecodeOffset) || nextInstructionIsLeader) {
            auto newBlock = std::make_unique<BytecodeBasicBlock>(bytecodeOffset, opcodeLength);
            current = newBlock.get();
            appendBlock(WTFMove(newBlock));
            createdBlock = true;
            nextInstructionIsLeader = false;
            bytecodeOffset += opcodeLength;
        }

        // If the current bytecode is a branch or a return, then the next instruction is the leader of its own basic block.
        if (isBranch(opcodeID) || isTerminal(opcodeID) || isThrow(opcodeID))
            nextInstructionIsLeader = true;

        if (createdBlock)
            continue;

        // Otherwise, just add to the length of the current block.
        current->addLength(opcodeLength);
        bytecodeOffset += opcodeLength;
    }

    // Link basic blocks together.
    for (unsigned i = 0; i < basicBlocks.size(); i++) {
        BytecodeBasicBlock* block = basicBlocks[i].get();

        if (block->isEntryBlock() || block->isExitBlock())
            continue;

        bool fallsThrough = true; 
        for (unsigned bytecodeOffset = block->leaderOffset(); bytecodeOffset < block->leaderOffset() + block->totalLength();) {
            OpcodeID opcodeID = interpreter->getOpcodeID(instructionsBegin[bytecodeOffset]);
            unsigned opcodeLength = opcodeLengths[opcodeID];
            // If we found a terminal bytecode, link to the exit block.
            if (isTerminal(opcodeID)) {
                ASSERT(bytecodeOffset + opcodeLength == block->leaderOffset() + block->totalLength());
                linkBlocks(block, exit.get());
                fallsThrough = false;
                break;
            }

            // If we found a throw, get the HandlerInfo for this instruction to see where we will jump. 
            // If there isn't one, treat this throw as a terminal. This is true even if we have a finally
            // block because the finally block will create its own catch, which will generate a HandlerInfo.
            if (isThrow(opcodeID)) {
                ASSERT(bytecodeOffset + opcodeLength == block->leaderOffset() + block->totalLength());
                auto* handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset);
                fallsThrough = false;
                if (!handler) {
                    linkBlocks(block, exit.get());
                    break;
                }
                for (unsigned i = 0; i < basicBlocks.size(); i++) {
                    BytecodeBasicBlock* otherBlock = basicBlocks[i].get();
                    if (handler->target == otherBlock->leaderOffset()) {
                        linkBlocks(block, otherBlock);
                        break;
                    }
                }
                break;
            }

            // If we found a branch, link to the block(s) that we jump to.
            if (isBranch(opcodeID)) {
                ASSERT(bytecodeOffset + opcodeLength == block->leaderOffset() + block->totalLength());
                Vector<unsigned, 1> bytecodeOffsetsJumpedTo;
                findJumpTargetsForBytecodeOffset(codeBlock, instructionsBegin, bytecodeOffset, bytecodeOffsetsJumpedTo);

                for (unsigned i = 0; i < basicBlocks.size(); i++) {
                    BytecodeBasicBlock* otherBlock = basicBlocks[i].get();
                    if (bytecodeOffsetsJumpedTo.contains(otherBlock->leaderOffset()))
                        linkBlocks(block, otherBlock);
                }

                if (isUnconditionalBranch(opcodeID))
                    fallsThrough = false;

                break;
            }
            bytecodeOffset += opcodeLength;
        }

        // If we fall through then link to the next block in program order.
        if (fallsThrough) {
            ASSERT(i + 1 < basicBlocks.size());
            BytecodeBasicBlock* nextBlock = basicBlocks[i + 1].get();
            linkBlocks(block, nextBlock);
        }
    }

    appendBlock(WTFMove(exit));
    
    for (auto& basicBlock : basicBlocks)
        basicBlock->shrinkToFit();
}
static void computeUsesForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset, FastBitVector& uses)
{
    Interpreter* interpreter = codeBlock->vm()->interpreter;
    Instruction* instructionsBegin = codeBlock->instructions().begin();
    Instruction* instruction = &instructionsBegin[bytecodeOffset];
    OpcodeID opcodeID = interpreter->getOpcodeID(instruction->u.opcode);
    switch (opcodeID) {
    // No uses.
    case op_new_regexp:
    case op_new_array_buffer:
    case op_throw_static_error:
    case op_debug:
    case op_resolve_scope:
    case op_pop_scope:
    case op_jneq_ptr:
    case op_new_func_exp:
    case op_loop_hint:
    case op_jmp:
    case op_new_object:
    case op_init_lazy_reg:
    case op_get_callee:
    case op_enter:
    case op_catch:
        return;
    // First argument.
    case op_new_func:
    case op_create_activation: 
    case op_create_arguments:
    case op_to_this:
    case op_tear_off_activation:
    case op_profile_will_call:
    case op_profile_did_call:
    case op_throw:
    case op_push_with_scope:
    case op_end:
    case op_ret:
    case op_jtrue:
    case op_jfalse:
    case op_jeq_null:
    case op_jneq_null:
    case op_dec:
    case op_inc: {
        if (isValidRegisterForLiveness(codeBlock, instruction[1].u.operand))
            setForOperand(codeBlock, uses, instruction[1].u.operand);
        return;
    }
    // First and second arguments.
    case op_del_by_id:
    case op_ret_object_or_this:
    case op_jlesseq:
    case op_jgreater:
    case op_jgreatereq:
    case op_jnless:
    case op_jnlesseq:
    case op_jngreater:
    case op_jngreatereq:
    case op_jless: {
        if (isValidRegisterForLiveness(codeBlock, instruction[1].u.operand))
            setForOperand(codeBlock, uses, instruction[1].u.operand);
        if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
            setForOperand(codeBlock, uses, instruction[2].u.operand);
        return;
    }
    // First, second, and third arguments.
    case op_del_by_val:
    case op_put_by_val_direct:
    case op_put_by_val: {
        if (isValidRegisterForLiveness(codeBlock, instruction[1].u.operand))
            setForOperand(codeBlock, uses, instruction[1].u.operand);
        if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
            setForOperand(codeBlock, uses, instruction[2].u.operand);
        if (isValidRegisterForLiveness(codeBlock, instruction[3].u.operand))
            setForOperand(codeBlock, uses, instruction[3].u.operand);
        return;
    }
    // First and third arguments.
    case op_put_by_index:
    case op_put_by_id_replace:
    case op_put_by_id_transition:
    case op_put_by_id_transition_direct:
    case op_put_by_id_transition_direct_out_of_line:
    case op_put_by_id_transition_normal:
    case op_put_by_id_transition_normal_out_of_line:
    case op_put_by_id_generic:
    case op_put_by_id_out_of_line:
    case op_put_by_id:
    case op_put_to_scope: {
        if (isValidRegisterForLiveness(codeBlock, instruction[1].u.operand))
            setForOperand(codeBlock, uses, instruction[1].u.operand);
        if (isValidRegisterForLiveness(codeBlock, instruction[3].u.operand))
            setForOperand(codeBlock, uses, instruction[3].u.operand);
        return;
    }
    // First, third, and fourth arguments.
    case op_put_getter_setter: {
        if (isValidRegisterForLiveness(codeBlock, instruction[1].u.operand))
            setForOperand(codeBlock, uses, instruction[1].u.operand);
        if (isValidRegisterForLiveness(codeBlock, instruction[3].u.operand))
            setForOperand(codeBlock, uses, instruction[3].u.operand);
        if (isValidRegisterForLiveness(codeBlock, instruction[4].u.operand))
            setForOperand(codeBlock, uses, instruction[4].u.operand);
        return;
    }
    // Second argument.
    case op_init_global_const_nop:
    case op_init_global_const:
    case op_push_name_scope:
    case op_get_from_scope:
    case op_to_primitive:
    case op_get_by_id:
    case op_get_by_id_out_of_line:
    case op_get_by_id_self:
    case op_get_by_id_proto:
    case op_get_by_id_chain:
    case op_get_by_id_getter_self:
    case op_get_by_id_getter_proto:
    case op_get_by_id_getter_chain:
    case op_get_by_id_custom_self:
    case op_get_by_id_custom_proto:
    case op_get_by_id_custom_chain:
    case op_get_by_id_generic:
    case op_get_array_length:
    case op_get_string_length:
    case op_get_arguments_length:
    case op_typeof:
    case op_is_undefined:
    case op_is_boolean:
    case op_is_number:
    case op_is_string:
    case op_is_object:
    case op_is_function:
    case op_to_number:
    case op_negate:
    case op_neq_null:
    case op_eq_null:
    case op_not:
    case op_mov:
    case op_new_array_with_size:
    case op_create_this: {
        if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
            setForOperand(codeBlock, uses, instruction[2].u.operand);
        return;
    }
    // Second and third arguments.
    case op_get_by_val:
    case op_get_argument_by_val:
    case op_in:
    case op_instanceof:
    case op_check_has_instance:
    case op_add:
    case op_mul:
    case op_div:
    case op_mod:
    case op_sub:
    case op_lshift:
    case op_rshift:
    case op_urshift:
    case op_bitand:
    case op_bitxor:
    case op_bitor:
    case op_less:
    case op_lesseq:
    case op_greater:
    case op_greatereq:
    case op_nstricteq:
    case op_stricteq:
    case op_neq:
    case op_eq: {
        if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
            setForOperand(codeBlock, uses, instruction[2].u.operand);
        if (isValidRegisterForLiveness(codeBlock, instruction[3].u.operand))
            setForOperand(codeBlock, uses, instruction[3].u.operand);
        return;
    }
    // Second, third, and fourth arguments.
    case op_call_varargs:
    case op_get_pnames: {
        if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
            setForOperand(codeBlock, uses, instruction[2].u.operand);
        if (isValidRegisterForLiveness(codeBlock, instruction[3].u.operand))
            setForOperand(codeBlock, uses, instruction[3].u.operand);
        if (isValidRegisterForLiveness(codeBlock, instruction[4].u.operand))
            setForOperand(codeBlock, uses, instruction[4].u.operand);
        return;
    }
    // Second, third, fourth, and fifth arguments.
    case op_next_pname: {
        if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
            setForOperand(codeBlock, uses, instruction[2].u.operand);
        if (isValidRegisterForLiveness(codeBlock, instruction[3].u.operand))
            setForOperand(codeBlock, uses, instruction[3].u.operand);
        if (isValidRegisterForLiveness(codeBlock, instruction[4].u.operand))
            setForOperand(codeBlock, uses, instruction[4].u.operand);
        if (isValidRegisterForLiveness(codeBlock, instruction[5].u.operand))
            setForOperand(codeBlock, uses, instruction[5].u.operand);
        return;
    }
    // Second, third, fourth, fifth, and sixth arguments. 
    case op_get_by_pname: {
        if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
            setForOperand(codeBlock, uses, instruction[2].u.operand);
        if (isValidRegisterForLiveness(codeBlock, instruction[3].u.operand))
            setForOperand(codeBlock, uses, instruction[3].u.operand);
        if (isValidRegisterForLiveness(codeBlock, instruction[4].u.operand))
            setForOperand(codeBlock, uses, instruction[4].u.operand);
        if (isValidRegisterForLiveness(codeBlock, instruction[5].u.operand))
            setForOperand(codeBlock, uses, instruction[5].u.operand);
        if (isValidRegisterForLiveness(codeBlock, instruction[6].u.operand))
            setForOperand(codeBlock, uses, instruction[6].u.operand);
        return;
    }
    // Third argument.
    case op_switch_string:
    case op_switch_char:
    case op_switch_imm: {
        if (isValidRegisterForLiveness(codeBlock, instruction[3].u.operand))
            setForOperand(codeBlock, uses, instruction[3].u.operand);
        return;
    }
    // Variable number of arguments.
    case op_new_array:
    case op_strcat: {
        int base = instruction[2].u.operand;
        int count = instruction[3].u.operand;
        for (int i = 0; i < count; i++) {
            if (isValidRegisterForLiveness(codeBlock, base + i))
                setForOperand(codeBlock, uses, base + i);
        }
        return;
    }
    // Crazy stuff for calls.
    case op_construct:
    case op_call_eval:
    case op_call: {
        if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
            setForOperand(codeBlock, uses, instruction[2].u.operand);
        int argCount = instruction[3].u.operand;
        int registerOffset = instruction[4].u.operand;
        int lastArg = registerOffset + CallFrame::thisArgumentOffset();
        for (int i = 0; i < argCount; i++) {
            if (isValidRegisterForLiveness(codeBlock, lastArg - i))
                setForOperand(codeBlock, uses, lastArg - i);
        }
        return;
    }
    // Special stuff for tear off arguments.
    case op_tear_off_arguments: {
        if (isValidRegisterForLiveness(codeBlock, instruction[1].u.operand))
            setForOperand(codeBlock, uses, instruction[1].u.operand);
        if (isValidRegisterForLiveness(codeBlock, unmodifiedArgumentsRegister(VirtualRegister(instruction[1].u.operand)).offset()))
            setForOperand(codeBlock, uses, unmodifiedArgumentsRegister(VirtualRegister(instruction[1].u.operand)).offset());
        if (isValidRegisterForLiveness(codeBlock, instruction[2].u.operand))
            setForOperand(codeBlock, uses, instruction[2].u.operand);
        return;
    }
#define LLINT_HELPER_OPCODES(opcode, length) case opcode:
    FOR_EACH_LLINT_OPCODE_EXTENSION(LLINT_HELPER_OPCODES)
        return;
#undef LLINT_HELPER_OPCODES
    }
}
static void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset, FastBitVector& defs)
{
    Interpreter* interpreter = codeBlock->vm()->interpreter;
    Instruction* instructionsBegin = codeBlock->instructions().begin();
    Instruction* instruction = &instructionsBegin[bytecodeOffset];
    OpcodeID opcodeID = interpreter->getOpcodeID(instruction->u.opcode);
    switch (opcodeID) {
    // These don't define anything.
    case op_init_global_const:
    case op_init_global_const_nop:
    case op_push_name_scope:
    case op_push_with_scope:
    case op_put_to_scope:
    case op_pop_scope:
    case op_end:
    case op_profile_will_call:
    case op_profile_did_call:
    case op_throw:
    case op_throw_static_error:
    case op_debug:
    case op_ret:
    case op_ret_object_or_this:
    case op_jmp:
    case op_jtrue:
    case op_jfalse:
    case op_jeq_null:
    case op_jneq_null:
    case op_jneq_ptr:
    case op_jless:
    case op_jlesseq:
    case op_jgreater:
    case op_jgreatereq:
    case op_jnless:
    case op_jnlesseq:
    case op_jngreater:
    case op_jngreatereq:
    case op_loop_hint:
    case op_switch_imm:
    case op_switch_char:
    case op_switch_string:
    case op_put_by_id:
    case op_put_by_id_out_of_line:
    case op_put_by_id_replace:
    case op_put_by_id_transition:
    case op_put_by_id_transition_direct:
    case op_put_by_id_transition_direct_out_of_line:
    case op_put_by_id_transition_normal:
    case op_put_by_id_transition_normal_out_of_line:
    case op_put_by_id_generic:
    case op_put_getter_setter:
    case op_put_by_val:
    case op_put_by_val_direct:
    case op_put_by_index:
    case op_del_by_id:
    case op_del_by_val:
#define LLINT_HELPER_OPCODES(opcode, length) case opcode:
        FOR_EACH_LLINT_OPCODE_EXTENSION(LLINT_HELPER_OPCODES);
#undef LLINT_HELPER_OPCODES
        return;
    // These all have a single destination for the first argument.
    case op_next_pname:
    case op_get_pnames:
    case op_resolve_scope:
    case op_strcat:
    case op_tear_off_activation:
    case op_to_primitive:
    case op_catch:
    case op_create_this:
    case op_new_array:
    case op_new_array_buffer:
    case op_new_array_with_size:
    case op_new_regexp:
    case op_new_func:
    case op_new_func_exp:
    case op_call_varargs:
    case op_get_from_scope:
    case op_call:
    case op_call_eval:
    case op_construct:
    case op_get_by_id:
    case op_get_by_id_out_of_line:
    case op_get_by_id_self:
    case op_get_by_id_proto:
    case op_get_by_id_chain:
    case op_get_by_id_getter_self:
    case op_get_by_id_getter_proto:
    case op_get_by_id_getter_chain:
    case op_get_by_id_custom_self:
    case op_get_by_id_custom_proto:
    case op_get_by_id_custom_chain:
    case op_get_by_id_generic:
    case op_get_array_length:
    case op_get_string_length:
    case op_check_has_instance:
    case op_instanceof:
    case op_get_by_val:
    case op_get_argument_by_val:
    case op_get_by_pname:
    case op_get_arguments_length:
    case op_typeof:
    case op_is_undefined:
    case op_is_boolean:
    case op_is_number:
    case op_is_string:
    case op_is_object:
    case op_is_function:
    case op_in:
    case op_to_number:
    case op_negate:
    case op_add:
    case op_mul:
    case op_div:
    case op_mod:
    case op_sub:
    case op_lshift:
    case op_rshift:
    case op_urshift:
    case op_bitand:
    case op_bitxor:
    case op_bitor:
    case op_inc:
    case op_dec:
    case op_eq:
    case op_neq:
    case op_stricteq:
    case op_nstricteq:
    case op_less:
    case op_lesseq:
    case op_greater:
    case op_greatereq:
    case op_neq_null:
    case op_eq_null:
    case op_not:
    case op_mov:
    case op_new_object:
    case op_to_this:
    case op_get_callee:
    case op_init_lazy_reg:
    case op_create_activation:
    case op_create_arguments: {
        if (isValidRegisterForLiveness(codeBlock, instruction[1].u.operand))
            setForOperand(codeBlock, defs, instruction[1].u.operand);
        return;
    }
    case op_tear_off_arguments: {
        if (isValidRegisterForLiveness(codeBlock, instruction[1].u.operand - 1))
            setForOperand(codeBlock, defs, instruction[1].u.operand - 1);
        return;
    }
    case op_enter: {
        defs.setAll();
        return;
    }
    }
}