Пример #1
0
void renumber_registers(IRCode* code, bool width_aware) {
  auto chains = calculate_ud_chains(code);

  Rank rank;
  Parent parent;
  DefSets def_sets((RankPMap(rank)), (ParentPMap(parent)));
  for (const auto& mie : InstructionIterable(code)) {
    if (mie.insn->dests_size()) {
      def_sets.make_set(mie.insn);
    }
  }
  unify_defs(chains, &def_sets);
  SymRegMapper sym_reg_mapper(width_aware);
  for (auto& mie : InstructionIterable(code)) {
    auto insn = mie.insn;
    if (insn->dests_size()) {
      auto sym_reg = sym_reg_mapper.make(def_sets.find_set(insn));
      insn->set_dest(sym_reg);
    }
  }
  for (auto& mie : InstructionIterable(code)) {
    auto insn = mie.insn;
    for (size_t i = 0; i < insn->srcs_size(); ++i) {
      auto& defs = chains.at(Use{insn, insn->src(i)});
      insn->set_src(i, sym_reg_mapper.at(def_sets.find_set(*defs.begin())));
    }
  }
  code->set_registers_size(sym_reg_mapper.regs_size());
}
Пример #2
0
void DexInstruction::verify_encoding() const {
  auto test = m_count ? new DexInstruction(opcode()) : new DexInstruction(opcode(), 0);
  if (dests_size()) {
    test->set_dest(dest());
  }
  for (unsigned i = 0; i < srcs_size(); i++) {
    test->set_src(i, src(i));
  }
  if (has_range_base()) test->set_range_base(range_base());
  if (has_range_size()) test->set_range_size(range_size());
  if (has_arg_word_count()) test->set_arg_word_count(arg_word_count());
  if (has_literal()) test->set_literal(literal());
  if (has_offset()) test->set_offset(offset());

  assert_log(m_opcode == test->m_opcode, "%x %x\n", m_opcode, test->m_opcode);
  for (unsigned i = 0; i < m_count; i++) {
    assert_log(m_arg[i] == test->m_arg[i],
               "(%x %x) (%x %x)",
               m_opcode,
               m_arg[i],
               test->m_opcode,
               test->m_arg[i]);
  }

  delete test;
}
Пример #3
0
bool IRInstruction::src_is_wide(size_t i) const {
  always_assert(i < srcs_size());

  if (is_invoke(m_opcode)) {
    return invoke_src_is_wide(i);
  }

  switch (opcode()) {
  case OPCODE_MOVE_WIDE:
  case OPCODE_RETURN_WIDE:
    return i == 0;

  case OPCODE_CMPL_DOUBLE:
  case OPCODE_CMPG_DOUBLE:
  case OPCODE_CMP_LONG:
    return i == 0 || i == 1;

  case OPCODE_APUT_WIDE:
  case OPCODE_IPUT_WIDE:
  case OPCODE_SPUT_WIDE:
    return i == 0;

  case OPCODE_NEG_LONG:
  case OPCODE_NOT_LONG:
  case OPCODE_NEG_DOUBLE:
  case OPCODE_LONG_TO_INT:
  case OPCODE_LONG_TO_FLOAT:
  case OPCODE_LONG_TO_DOUBLE:
  case OPCODE_DOUBLE_TO_INT:
  case OPCODE_DOUBLE_TO_LONG:
  case OPCODE_DOUBLE_TO_FLOAT:
    return i == 0;

  case OPCODE_ADD_LONG:
  case OPCODE_SUB_LONG:
  case OPCODE_MUL_LONG:
  case OPCODE_DIV_LONG:
  case OPCODE_REM_LONG:
  case OPCODE_AND_LONG:
  case OPCODE_OR_LONG:
  case OPCODE_XOR_LONG:
  case OPCODE_ADD_DOUBLE:
  case OPCODE_SUB_DOUBLE:
  case OPCODE_MUL_DOUBLE:
  case OPCODE_DIV_DOUBLE:
  case OPCODE_REM_DOUBLE:
    return i == 0 || i == 1;

  case OPCODE_SHL_LONG:
  case OPCODE_SHR_LONG:
  case OPCODE_USHR_LONG:
    return i == 0;

  default:
    return false;
  }
}
Пример #4
0
void IRInstruction::normalize_registers() {
  if (is_invoke(opcode())) {
    auto& args = get_method()->get_proto()->get_args()->get_type_list();
    size_t old_srcs_idx{0};
    size_t srcs_idx{0};
    if (m_opcode != OPCODE_INVOKE_STATIC) {
      ++srcs_idx;
      ++old_srcs_idx;
    }
    for (size_t args_idx = 0; args_idx < args.size(); ++args_idx) {
      always_assert_log(
          old_srcs_idx < srcs_size(),
          "Invalid arg indices in %s args_idx %d old_srcs_idx %d\n",
          SHOW(this),
          args_idx,
          old_srcs_idx);
      set_src(srcs_idx++, src(old_srcs_idx));
      old_srcs_idx += is_wide_type(args.at(args_idx)) ? 2 : 1;
    }
    always_assert(old_srcs_idx == srcs_size());
    set_arg_word_count(srcs_idx);
  }
}
Пример #5
0
uint64_t IRInstruction::hash() const {
  std::vector<uint64_t> bits;
  bits.push_back(opcode());

  for (size_t i = 0; i < srcs_size(); i++) {
    bits.push_back(src(i));
  }

  if (dests_size() > 0) {
    bits.push_back(dest());
  }

  if (has_data()) {
    size_t size = get_data()->data_size();
    const auto& data = get_data()->data();
    for (size_t i = 0; i < size; i++) {
      bits.push_back(data[i]);
    }
  }

  if (has_type()) {
    bits.push_back(reinterpret_cast<uint64_t>(get_type()));
  }
  if (has_field()) {
    bits.push_back(reinterpret_cast<uint64_t>(get_field()));
  }
  if (has_method()) {
    bits.push_back(reinterpret_cast<uint64_t>(get_method()));
  }
  if (has_string()) {
    bits.push_back(reinterpret_cast<uint64_t>(get_string()));
  }
  if (has_literal()) {
    bits.push_back(get_literal());
  }

  uint64_t result = 0;
  for (uint64_t elem : bits) {
    result ^= elem;
  }
  return result;
}
Пример #6
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;
    }
  }
}
Пример #7
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;
}