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; }
void inline_field_values(Scope& fullscope) { std::unordered_set<DexField*> inline_field; std::unordered_set<DexField*> cheap_inline_field; std::vector<DexClass*> scope; uint32_t aflags = ACC_STATIC | ACC_FINAL; for (auto clazz : fullscope) { std::unordered_map<DexField*, bool> blank_statics; get_sput_in_clinit(clazz, blank_statics); auto sfields = clazz->get_sfields(); for (auto sfield : sfields) { if ((sfield->get_access() & aflags) != aflags) continue; if (blank_statics[sfield]) continue; auto value = sfield->get_static_value(); if (value == nullptr && !is_primitive(sfield->get_type())) { continue; } if (value != nullptr && !value->is_evtype_primitive()) { continue; } uint64_t v = value != nullptr ? value->value() : 0; if ((v & 0xffff) == v || (v & 0xffff0000) == v) { cheap_inline_field.insert(sfield); } inline_field.insert(sfield); scope.push_back(clazz); } } std::vector<std::pair<DexMethod*, DexOpcodeField*>> cheap_rewrites; std::vector<std::pair<DexMethod*, DexOpcodeField*>> simple_rewrites; walk_opcodes( fullscope, [](DexMethod* method) { return true; }, [&](DexMethod* method, DexInstruction* insn) { if (insn->has_fields() && is_sfield_op(insn->opcode())) { auto fieldop = static_cast<DexOpcodeField*>(insn); auto field = resolve_field(fieldop->field(), FieldSearch::Static); if (field == nullptr || !field->is_concrete()) return; if (inline_field.count(field) == 0) return; if (cheap_inline_field.count(field) > 0) { cheap_rewrites.push_back(std::make_pair(method, fieldop)); return; } simple_rewrites.push_back(std::make_pair(method, fieldop)); } }); TRACE(FINALINLINE, 1, "Method Re-writes Cheap %lu Simple %lu\n", cheap_rewrites.size(), simple_rewrites.size()); for (auto cheapcase : cheap_rewrites) { inline_cheap_sget(cheapcase.first, cheapcase.second); } for (auto simplecase : simple_rewrites) { inline_sget(simplecase.first, simplecase.second); } MethodTransform::sync_all(); }
/** * 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); } }
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); } } }); }