Example #1
0
/*
 * 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;
}
Example #2
0
bool DexInstruction::is_wide() const {
  return src_is_wide(0) || src_is_wide(1) || dest_is_wide();
}