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); } } }
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(); }
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); }
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; } } }