Exemple #1
0
size_t delete_methods(
    std::vector<DexClass*>& scope, std::unordered_set<DexMethod*>& removable,
    std::function<DexMethod*(DexMethod*, MethodSearch search)> resolver) {

  // if a removable candidate is invoked do not delete
  walk_opcodes(scope, [](DexMethod* meth) { return true; },
      [&](DexMethod* meth, DexInstruction* insn) {
        if (is_invoke(insn->opcode())) {
          const auto mop = static_cast<DexOpcodeMethod*>(insn);
          auto callee = resolver(mop->get_method(), opcode_to_search(insn));
          if (callee != nullptr) {
            removable.erase(callee);
          }
        }
      });

  size_t deleted = 0;
  for (auto callee : removable) {
    if (!callee->is_concrete()) continue;
    if (do_not_strip(callee)) continue;
    auto cls = type_class(callee->get_class());
    always_assert_log(cls != nullptr,
        "%s is concrete but does not have a DexClass\n",
        SHOW(callee));
    if (callee->is_virtual()) {
      cls->get_vmethods().remove(callee);
    } else {
      cls->get_dmethods().remove(callee);
    }
    deleted++;
    TRACE(DELMET, 4, "removing %s\n", SHOW(callee));
  }
  return deleted;
}
Exemple #2
0
void change_visibility(DexMethod* method) {
  auto code = method->get_code();
  always_assert(code != nullptr);

  editable_cfg_adapter::iterate(code, [](MethodItemEntry& mie) {
    auto insn = mie.insn;

    if (insn->has_field()) {
      auto cls = type_class(insn->get_field()->get_class());
      if (cls != nullptr && !cls->is_external()) {
        set_public(cls);
      }
      auto field =
          resolve_field(insn->get_field(), is_sfield_op(insn->opcode())
              ? FieldSearch::Static : FieldSearch::Instance);
      if (field != nullptr && field->is_concrete()) {
        set_public(field);
        set_public(type_class(field->get_class()));
        // FIXME no point in rewriting opcodes in the method
        insn->set_field(field);
      }
    } else if (insn->has_method()) {
      auto cls = type_class(insn->get_method()->get_class());
      if (cls != nullptr && !cls->is_external()) {
        set_public(cls);
      }
      auto current_method = resolve_method(
          insn->get_method(), opcode_to_search(insn));
      if (current_method != nullptr && current_method->is_concrete()) {
        set_public(current_method);
        set_public(type_class(current_method->get_class()));
        // FIXME no point in rewriting opcodes in the method
        insn->set_method(current_method);
      }
    } else if (insn->has_type()) {
      auto type = insn->get_type();
      auto cls = type_class(type);
      if (cls != nullptr && !cls->is_external()) {
        set_public(cls);
      }
    }
    return editable_cfg_adapter::LOOP_CONTINUE;
  });

  std::vector<DexType*> types;
  if (code->editable_cfg_built()) {
    code->cfg().gather_catch_types(types);
  } else {
    code->gather_catch_types(types);
  }
  for (auto type : types) {
    auto cls = type_class(type);
    if (cls != nullptr && !cls->is_external()) {
      set_public(cls);
    }
  }
}
Exemple #3
0
void MultiMethodInliner::inline_callees(
    InlineContext& inline_context, std::vector<DexMethod*>& callees) {
  size_t found = 0;
  auto caller = inline_context.caller;
  auto insns = caller->get_code()->get_instructions();

  // walk the caller opcodes collecting all candidates to inline
  // Build a callee to opcode map
  std::vector<std::pair<DexMethod*, DexOpcodeMethod*>> inlinables;
  for (auto insn = insns.begin(); insn != insns.end(); ++insn) {
    if (!is_invoke((*insn)->opcode())) continue;
    auto mop = static_cast<DexOpcodeMethod*>(*insn);
    auto callee = resolver(mop->get_method(), opcode_to_search(*insn));
    if (callee == nullptr) continue;
    if (std::find(callees.begin(), callees.end(), callee) == callees.end()) {
      continue;
    }
    always_assert(callee->is_concrete());
    found++;
    inlinables.push_back(std::make_pair(callee, mop));
    if (found == callees.size()) break;
  }
  if (found != callees.size()) {
    always_assert(found <= callees.size());
    info.not_found += callees.size() - found;
  }

  // attempt to inline all inlinable candidates
  for (auto inlinable : inlinables) {
    auto callee = inlinable.first;
    auto mop = inlinable.second;

    if (!is_inlinable(callee, caller)) continue;

    auto op = mop->opcode();
    if (is_invoke_range(op)) {
      info.invoke_range++;
      continue;
    }

    TRACE(MMINL, 4, "inline %s (%d) in %s (%d)\n",
        SHOW(callee), caller->get_code()->get_registers_size(),
        SHOW(caller),
        callee->get_code()->get_registers_size() -
        callee->get_code()->get_ins_size());
    change_visibility(callee);
    MethodTransform::inline_16regs(inline_context, callee, mop);
    info.calls_inlined++;
    inlined.insert(callee);
  }
}
Exemple #4
0
/**
 * Change the visibility of members accessed in a callee as they are moved
 * to the caller context.
 * We make everything public but we could be more precise and only
 * relax visibility as needed.
 */
void MultiMethodInliner::change_visibility(DexMethod* callee) {
  TRACE(MMINL, 6, "checking visibility usage of members in %s\n",
      SHOW(callee));
  for (auto insn : callee->get_code()->get_instructions()) {
    if (insn->has_fields()) {
      auto fop = static_cast<DexOpcodeField*>(insn);
      auto field = fop->field();
      field = resolve_field(field, is_sfield_op(insn->opcode())
          ? FieldSearch::Static : FieldSearch::Instance);
      if (field != nullptr && field->is_concrete()) {
        TRACE(MMINL, 6, "changing visibility of %s.%s %s\n",
            SHOW(field->get_class()), SHOW(field->get_name()),
            SHOW(field->get_type()));
        set_public(field);
        set_public(type_class(field->get_class()));
        fop->rewrite_field(field);
      }
      continue;
    }
    if (insn->has_methods()) {
      auto mop = static_cast<DexOpcodeMethod*>(insn);
      auto method = mop->get_method();
      method = resolver(method, opcode_to_search(insn));
      if (method != nullptr && method->is_concrete()) {
        TRACE(MMINL, 6, "changing visibility of %s.%s: %s\n",
            SHOW(method->get_class()), SHOW(method->get_name()),
            SHOW(method->get_proto()));
        set_public(method);
        set_public(type_class(method->get_class()));
        mop->rewrite_method(method);
      }
      continue;
    }
    if (insn->has_types()) {
      auto type = static_cast<DexOpcodeType*>(insn)->get_type();
      auto cls = type_class(type);
      if (cls != nullptr && !cls->is_external()) {
        TRACE(MMINL, 6, "changing visibility of %s\n", SHOW(type));
        set_public(cls);
      }
      continue;
    }
  }
}
Exemple #5
0
// Check that visibility / accessibility changes to the current method
// won't need to change a referenced method into a virtual or static one.
bool gather_invoked_methods_that_prevent_relocation(
    const DexMethod* method,
    std::unordered_set<DexMethodRef*>* methods_preventing_relocation) {
  auto code = method->get_code();
  always_assert(code);

  bool can_relocate = true;
  for (const auto& mie : InstructionIterable(code)) {
    auto insn = mie.insn;
    auto opcode = insn->opcode();
    if (is_invoke(opcode)) {
      auto meth = resolve_method(insn->get_method(), opcode_to_search(insn));
      if (!meth && opcode == OPCODE_INVOKE_VIRTUAL &&
          unknown_virtuals::is_method_known_to_be_public(insn->get_method())) {
        continue;
      }
      if (meth) {
        always_assert(meth->is_def());
        if (meth->is_external() && !is_public(meth)) {
          meth = nullptr;
        } else if (opcode == OPCODE_INVOKE_DIRECT && !is_init(meth)) {
          meth = nullptr;
        }
      }
      if (!meth) {
        can_relocate = false;
        if (!methods_preventing_relocation) {
          break;
        }
        methods_preventing_relocation->emplace(insn->get_method());
      }
    }
  }

  return can_relocate;
}
Exemple #6
0
/**
 * Add to the list the single called.
 */
void SimpleInlinePass::select_single_called(
    Scope& scope, std::unordered_set<DexMethod*>& methods) {
  std::unordered_map<DexMethod*, int> calls;
  for (const auto& method : methods) {
    calls[method] = 0;
  }
  // count call sites for each method
  walk_opcodes(scope, [](DexMethod* meth) { return true; },
      [&](DexMethod* meth, DexInstruction* insn) {
        if (is_invoke(insn->opcode())) {
          auto mop = static_cast<DexOpcodeMethod*>(insn);
          auto callee = resolve_method(
              mop->get_method(), opcode_to_search(insn), resolved_refs);
          if (callee != nullptr && callee->is_concrete()
              && methods.count(callee) > 0) {
            calls[callee]++;
          }
        }
      });

  // pick methods with a single call site and add to candidates.
  // This vector usage is only because of logging we should remove it
  // once the optimization is "closed"
  std::vector<std::vector<DexMethod*>> calls_group(MAX_COUNT);
  for (auto call_it : calls) {
    if (call_it.second >= MAX_COUNT) {
      calls_group[MAX_COUNT - 1].push_back(call_it.first);
      continue;
    }
    calls_group[call_it.second].push_back(call_it.first);
  }
  assert(method_breakup(calls_group));
  for (auto callee : calls_group[1]) {
    inlinable.insert(callee);
  }
}
Exemple #7
0
MultiMethodInliner::MultiMethodInliner(
    std::vector<DexClass*>& scope,
    DexClasses& primary_dex,
    std::unordered_set<DexMethod*>& candidates,
    std::function<DexMethod*(DexMethod*, MethodSearch)> resolver)
        : resolver(resolver) {
  for (const auto& cls : primary_dex) {
    primary.insert(cls->get_type());
  }
  // walk every opcode in scope looking for calls to inlinable candidates
  // and build a map of callers to callees and the reverse callees to callers
  walk_opcodes(scope, [](DexMethod* meth) { return true; },
      [&](DexMethod* meth, DexInstruction* opcode) {
        if (is_invoke(opcode->opcode())) {
          auto mop = static_cast<DexOpcodeMethod*>(opcode);
          auto callee = resolver(mop->get_method(), opcode_to_search(opcode));
          if (callee != nullptr && callee->is_concrete() &&
              candidates.find(callee) != candidates.end()) {
            callee_caller[callee].push_back(meth);
            caller_callee[meth].push_back(callee);
          }
        }
      });
}
Exemple #8
0
bool UsedVarsFixpointIterator::is_required(const IRInstruction* insn,
                                           const UsedVarsSet& used_vars) const {
  auto op = insn->opcode();
  switch (op) {
  case IOPCODE_LOAD_PARAM:
  case IOPCODE_LOAD_PARAM_OBJECT:
  case IOPCODE_LOAD_PARAM_WIDE:
  // Control-flow opcodes are always required.
  case OPCODE_RETURN_VOID:
  case OPCODE_RETURN:
  case OPCODE_RETURN_WIDE:
  case OPCODE_RETURN_OBJECT:
  case OPCODE_MONITOR_ENTER:
  case OPCODE_MONITOR_EXIT:
  case OPCODE_CHECK_CAST:
  case OPCODE_THROW:
  case OPCODE_GOTO:
  case OPCODE_PACKED_SWITCH:
  case OPCODE_SPARSE_SWITCH:
  case OPCODE_IF_EQ:
  case OPCODE_IF_NE:
  case OPCODE_IF_LT:
  case OPCODE_IF_GE:
  case OPCODE_IF_GT:
  case OPCODE_IF_LE:
  case OPCODE_IF_EQZ:
  case OPCODE_IF_NEZ:
  case OPCODE_IF_LTZ:
  case OPCODE_IF_GEZ:
  case OPCODE_IF_GTZ:
  case OPCODE_IF_LEZ: {
    return true;
  }
  case OPCODE_APUT:
  case OPCODE_APUT_WIDE:
  case OPCODE_APUT_OBJECT:
  case OPCODE_APUT_BOOLEAN:
  case OPCODE_APUT_BYTE:
  case OPCODE_APUT_CHAR:
  case OPCODE_APUT_SHORT:
  case OPCODE_IPUT:
  case OPCODE_IPUT_WIDE:
  case OPCODE_IPUT_OBJECT:
  case OPCODE_IPUT_BOOLEAN:
  case OPCODE_IPUT_BYTE:
  case OPCODE_IPUT_CHAR:
  case OPCODE_IPUT_SHORT: {
    const auto& env = m_insn_env_map.at(insn);
    return is_used_or_escaping_write(env, used_vars, insn->src(1));
  }
  case OPCODE_FILL_ARRAY_DATA: {
    const auto& env = m_insn_env_map.at(insn);
    return is_used_or_escaping_write(env, used_vars, insn->src(0));
  }
  case OPCODE_SPUT:
  case OPCODE_SPUT_WIDE:
  case OPCODE_SPUT_OBJECT:
  case OPCODE_SPUT_BOOLEAN:
  case OPCODE_SPUT_BYTE:
  case OPCODE_SPUT_CHAR:
  case OPCODE_SPUT_SHORT: {
    return true;
  }
  case OPCODE_INVOKE_DIRECT:
  case OPCODE_INVOKE_STATIC:
  case OPCODE_INVOKE_VIRTUAL: {
    auto method = resolve_method(insn->get_method(), opcode_to_search(insn));
    if (method == nullptr) {
      return true;
    }
    if (assumenosideeffects(method)) {
      return used_vars.contains(RESULT_REGISTER);
    }
    // We could be smarter about virtual calls that may resolve to one of many
    // callees, but right now we just bail out.
    if (op == OPCODE_INVOKE_VIRTUAL &&
        !m_non_overridden_virtuals.count(method)) {
      return true;
    }
    if (!m_effect_summaries.count(method)) {
      return true;
    }
    // A call is required if it has a side-effect, if its return value is used,
    // or if it mutates an argument that may later be read somewhere up the
    // callstack.
    auto& summary = m_effect_summaries.at(method);
    if (summary.effects != EFF_NONE || used_vars.contains(RESULT_REGISTER)) {
      return true;
    }
    const auto& env = m_insn_env_map.at(insn);
    const auto& mod_params = summary.modified_params;
    return std::any_of(
        mod_params.begin(), mod_params.end(), [&](param_idx_t idx) {
          return is_used_or_escaping_write(env, used_vars, insn->src(idx));
        });
  }
  case OPCODE_INVOKE_SUPER:
  case OPCODE_INVOKE_INTERFACE: {
    return true;
  }
  default: {
    if (insn->dests_size()) {
      return used_vars.contains(insn->dest());
    } else if (insn->has_move_result()) {
      return used_vars.contains(RESULT_REGISTER);
    }
    return true;
  }
  }
}