/* * Build the interference graph by adding edges between nodes that are * simultaneously live. * * check-cast instructions have to be handled specially. They are represented * with both a dest (via a move-result-pseudo) and a src in our IR. However, in * actual Dex bytecode, it only takes a single operand which acts as both src * and dest. So when converting IR to Dex bytecode, we need to insert a move * instruction if the src and dest operands differ. We must insert the move * before, not after, the check-cast. Suppose we did not: * * IR | Dex * sget-object v0 LFoo; | sget-object v0 LFoo; * check-cast v0 LBar; | check-cast v0 LBar; * move-result-pseudo v1 | move-object v1 v0 * invoke-static v0 LFoo.a; | invoke-static v0 LFoo.a; // v0 is of type Bar! * * However, inserting before the check-cast is tricky to get right. If the * check-cast is in a try region, we must be careful to not clobber other * live registers. For example, if we had some IRCode like * * B0: * load-param v1 Ljava/lang/Object; * TRY_START * const v0 123 * check-cast v1 LFoo; * B1: * move-result-pseudo v0 * return v0 * TRY_END * B2: * CATCH * // handle failure of check-cast * // Note that v0 has the value of 123 here because the check-cast failed * add-int v0, v0, v0 * * Inserting the move before the check-cast would cause v0 to have an object * (instead of integer) type inside the exception handler. * * The solution is to have the interference graph make check-cast's dest * register interfere with the live registers in both B0 and B1, so that when * the move gets inserted, it does not clobber any live registers. */ Graph GraphBuilder::build(const LivenessFixpointIterator& fixpoint_iter, IRCode* code, reg_t initial_regs, const RangeSet& range_set) { Graph graph; auto ii = InstructionIterable(code); for (auto it = ii.begin(); it != ii.end(); ++it) { GraphBuilder::update_node_constraints(it.unwrap(), range_set, &graph); } auto& cfg = code->cfg(); for (cfg::Block* block : cfg.blocks()) { LivenessDomain live_out = fixpoint_iter.get_live_out_vars_at(block); for (auto it = block->rbegin(); it != block->rend(); ++it) { if (it->type != MFLOW_OPCODE) { continue; } auto insn = it->insn; auto op = insn->opcode(); if (opcode::has_range_form(op)) { graph.m_range_liveness.emplace(insn, live_out); } if (insn->dests_size()) { for (auto reg : live_out.elements()) { if (is_move(op) && reg == insn->src(0)) { continue; } graph.add_edge(insn->dest(), reg); } // We add interference edges between the wide src and dest operands of // an instruction even if the srcs are not live-out. This avoids // allocations like `xor-long v1, v0, v9`, where v1 and v0 overlap -- // even though this is not a verification error, we have observed bugs // in the ART interpreter when handling these sorts of instructions. // However, we still want to be able to coalesce these symregs if they // don't actually interfere based on liveness information, so that we // can remove move-wide opcodes and/or use /2addr encodings. As such, // we insert a specially marked edge that coalescing ignores but // coloring respects. if (insn->dest_is_wide()) { for (size_t i = 0; i < insn->srcs_size(); ++i) { if (insn->src_is_wide(i)) { graph.add_coalesceable_edge(insn->dest(), insn->src(i)); } } } } if (op == OPCODE_CHECK_CAST) { auto move_result_pseudo = std::prev(it)->insn; for (auto reg : live_out.elements()) { graph.add_edge(move_result_pseudo->dest(), reg); } } // adding containment edge between liverange defined in insn and elements // in live-out set of insn if (insn->dests_size()) { for (auto reg : live_out.elements()) { graph.add_containment_edge(insn->dest(), reg); } } fixpoint_iter.analyze_instruction(it->insn, &live_out); // adding containment edge between liverange used in insn and elements // in live-in set of insn for (size_t i = 0; i < insn->srcs_size(); ++i) { for (auto reg : live_out.elements()) { graph.add_containment_edge(insn->src(i), reg); } } } } for (auto& pair : graph.nodes()) { auto reg = pair.first; auto& node = pair.second; if (reg >= initial_regs) { node.m_props.set(Node::SPILL); } assert_log(!node.m_type_domain.is_bottom(), "Type violation of v%u in code:\n%s\n", reg, SHOW(code)); } return graph; }
bool DexInstruction::is_wide() const { return src_is_wide(0) || src_is_wide(1) || dest_is_wide(); }