示例#1
0
DexMethod* MethodCreator::create() {
  auto param_insns = meth_code->get_param_instructions();
  auto param_reg = meth_code->get_registers_size();
  transform::RegMap reg_map;
  // allocate all the params at the end of the register frame
  for (const auto& mie : boost::adaptors::reverse(param_insns)) {
    auto insn = mie.insn;
    param_reg -= insn->dest_is_wide() ? 2 : 1;
    reg_map[insn->dest()] = param_reg;
    if (insn->dest_is_wide()) {
      reg_map[insn->dest() + 1] = param_reg + 1;
    }
  }
  // now allocate the rest at the start
  size_t temp_reg{0};
  for (size_t i = 0; i < meth_code->get_registers_size(); ++i) {
    if (reg_map.find(i) != reg_map.end()) {
      continue;
    }
    reg_map[i] = temp_reg++;
  }
  always_assert(temp_reg == param_reg);
  transform::remap_registers(meth_code, reg_map);
  return method;
}
示例#2
0
void analyze_move_result(cfg::InstructionIterator it, Environment* env) {
  auto insn = it->insn;
  if (!insn->dest_is_wide()) {
    env->set(insn->dest(), env->get(RESULT_REGISTER));
  } else {
    analyze_default(it, env);
  }
}
示例#3
0
void analyze_default(cfg::InstructionIterator it, Environment* env) {
  auto insn = it->insn;
  if (insn->dests_size()) {
    env->set(insn->dest(), Domain::top());
    if (insn->dest_is_wide()) {
      env->set(insn->dest() + 1, Domain::top());
    }
  }
  if (insn->has_move_result()) {
    env->set(RESULT_REGISTER, Domain::top());
  }
}
示例#4
0
void GraphBuilder::update_node_constraints(IRList::iterator it,
                                           const RangeSet& range_set,
                                           Graph* graph) {
  auto insn = it->insn;
  auto op = insn->opcode();
  if (insn->dests_size()) {
    auto dest = insn->dest();
    auto& node = graph->m_nodes[dest];
    if (opcode::is_load_param(op)) {
      node.m_props.set(Node::PARAM);
    }
    node.m_type_domain.meet_with(RegisterTypeDomain(dest_reg_type(insn)));
    auto max_vreg = max_unsigned_value(dest_bit_width(it));
    node.m_max_vreg = std::min(node.m_max_vreg, max_vreg);
    node.m_width = insn->dest_is_wide() ? 2 : 1;
    if (max_vreg < max_unsigned_value(16)) {
      ++node.m_spill_cost;
    }
  }

  for (size_t i = 0; i < insn->srcs_size(); ++i) {
    auto src = insn->src(i);
    auto& node = graph->m_nodes[src];
    auto type = src_reg_type(insn, i);
    node.m_type_domain.meet_with(RegisterTypeDomain(type));
    reg_t max_vreg;
    if (range_set.contains(insn)) {
      max_vreg = max_unsigned_value(16);
      node.m_props.set(Node::RANGE);
    } else {
      max_vreg = max_value_for_src(insn, i, type == RegisterType::WIDE);
    }
    node.m_max_vreg = std::min(node.m_max_vreg, max_vreg);
    if (max_vreg < max_unsigned_value(16)) {
      ++node.m_spill_cost;
    }
  }
}
示例#5
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;
}
示例#6
0
bool DexInstruction::is_wide() const {
  return src_is_wide(0) || src_is_wide(1) || dest_is_wide();
}