diff --git a/libredex/WellKnownTypes.h b/libredex/WellKnownTypes.h index 80cc29ee121..a5755a37e00 100644 --- a/libredex/WellKnownTypes.h +++ b/libredex/WellKnownTypes.h @@ -48,6 +48,7 @@ #define WELL_KNOWN_METHODS \ FOR_EACH(java_lang_Object_ctor, "Ljava/lang/Object;.:()V") \ + FOR_EACH(java_lang_Object_finalize, "Ljava/lang/Object;.finalize:()V") \ FOR_EACH(java_lang_Enum_ctor, \ "Ljava/lang/Enum;.:(Ljava/lang/String;I)V") \ FOR_EACH(java_lang_Enum_ordinal, "Ljava/lang/Enum;.ordinal:()I") \ diff --git a/opt/object-escape-analysis/ObjectEscapeAnalysis.cpp b/opt/object-escape-analysis/ObjectEscapeAnalysis.cpp index 2f2da393756..4eb4b5895d2 100644 --- a/opt/object-escape-analysis/ObjectEscapeAnalysis.cpp +++ b/opt/object-escape-analysis/ObjectEscapeAnalysis.cpp @@ -118,6 +118,19 @@ constexpr int64_t COST_NEW_INSTANCE = 20; using Locations = std::vector>; +std::unordered_set get_excluded_classes( + const mog::Graph& method_override_graph) { + std::unordered_set res; + for (auto* overriding_method : method_override_graph::get_overriding_methods( + method_override_graph, method::java_lang_Object_finalize())) { + auto* cls = type_class(overriding_method->get_class()); + if (cls && !cls->is_external()) { + res.insert(cls); + } + } + return res; +}; + // Collect all allocation and invoke instructions, as well as non-virtual // invocation dependencies. void analyze_scope( @@ -209,10 +222,12 @@ using MethodSummaries = std::unordered_map; // - which allocations return class Analyzer final : public BaseIRAnalyzer { public: - explicit Analyzer(const MethodSummaries& method_summaries, + explicit Analyzer(const std::unordered_set& excluded_classes, + const MethodSummaries& method_summaries, DexMethodRef* incomplete_marker_method, cfg::ControlFlowGraph& cfg) : BaseIRAnalyzer(cfg), + m_excluded_classes(excluded_classes), m_method_summaries(method_summaries), m_incomplete_marker_method(incomplete_marker_method) { MonotonicFixpointIterator::run(Environment::top()); @@ -245,7 +260,7 @@ class Analyzer final : public BaseIRAnalyzer { if (insn->opcode() == OPCODE_NEW_INSTANCE) { auto type = insn->get_type(); auto cls = type_class(type); - if (cls && !cls->is_external()) { + if (cls && !cls->is_external() && !m_excluded_classes.count(cls)) { m_escapes[insn]; current_state->set(RESULT_REGISTER, Domain(insn)); return; @@ -344,6 +359,7 @@ class Analyzer final : public BaseIRAnalyzer { } private: + const std::unordered_set& m_excluded_classes; const MethodSummaries& m_method_summaries; DexMethodRef* m_incomplete_marker_method; mutable Escapes m_escapes; @@ -360,7 +376,8 @@ MethodSummaries compute_method_summaries( const Scope& scope, const ConcurrentMap>& dependencies, - const mog::Graph& method_override_graph) { + const mog::Graph& method_override_graph, + const std::unordered_set& excluded_classes) { Timer t("compute_method_summaries"); std::unordered_set impacted_methods; @@ -382,7 +399,7 @@ MethodSummaries compute_method_summaries( workqueue_run( [&](DexMethod* method) { auto& cfg = method->get_code()->cfg(); - Analyzer analyzer(method_summaries, + Analyzer analyzer(excluded_classes, method_summaries, /* incomplete_marker_method */ nullptr, cfg); const auto& escapes = analyzer.get_escapes(); const auto& returns = analyzer.get_returns(); @@ -468,12 +485,14 @@ std::pair resolve_inlinable( using InlineAnchorsOfType = std::unordered_map>; ConcurrentMap compute_inline_anchors( - const Scope& scope, const MethodSummaries& method_summaries) { + const Scope& scope, + const MethodSummaries& method_summaries, + const std::unordered_set& excluded_classes) { Timer t("compute_inline_anchors"); ConcurrentMap inline_anchors; walk::parallel::code(scope, [&](DexMethod* method, IRCode& code) { - Analyzer analyzer(method_summaries, /* incomplete_marker_method */ nullptr, - code.cfg()); + Analyzer analyzer(excluded_classes, method_summaries, + /* incomplete_marker_method */ nullptr, code.cfg()); auto inlinables = analyzer.get_inlinables(); for (auto insn : inlinables) { auto [callee, type] = resolve_inlinable(method_summaries, insn); @@ -860,6 +879,7 @@ class RootMethodReducer { DexMethodRef* m_incomplete_marker_method; MultiMethodInliner& m_inliner; const MethodSummaries& m_method_summaries; + const std::unordered_set& m_excluded_classes; Stats* m_stats; bool m_is_init_or_clinit; DexMethod* m_method; @@ -873,6 +893,7 @@ class RootMethodReducer { DexMethodRef* incomplete_marker_method, MultiMethodInliner& inliner, const MethodSummaries& method_summaries, + const std::unordered_set& excluded_classes, Stats* stats, bool is_init_or_clinit, DexMethod* method, @@ -882,6 +903,7 @@ class RootMethodReducer { m_incomplete_marker_method(incomplete_marker_method), m_inliner(inliner), m_method_summaries(method_summaries), + m_excluded_classes(excluded_classes), m_stats(stats), m_is_init_or_clinit(is_init_or_clinit), m_method(method), @@ -1009,7 +1031,8 @@ class RootMethodReducer { bool inline_anchors() { auto& cfg = m_method->get_code()->cfg(); for (int iteration = 0; true; iteration++) { - Analyzer analyzer(m_method_summaries, m_incomplete_marker_method, cfg); + Analyzer analyzer(m_excluded_classes, m_method_summaries, + m_incomplete_marker_method, cfg); std::unordered_set invokes_to_inline; auto inlinables = analyzer.get_inlinables(); Lazy du_chains([&]() { @@ -1365,6 +1388,7 @@ compute_reduced_methods( ExpandableMethodParams& expandable_method_params, MultiMethodInliner& inliner, const MethodSummaries& method_summaries, + const std::unordered_set& excluded_classes, const std::unordered_map& root_methods, std::unordered_set* irreducible_types, Stats* stats, @@ -1448,6 +1472,7 @@ compute_reduced_methods( incomplete_marker_method, inliner, method_summaries, + excluded_classes, stats, method::is_init(method) || method::is_clinit(method), @@ -1632,6 +1657,7 @@ void reduce(DexStoresVector& stores, const init_classes::InitClassesWithSideEffects& init_classes_with_side_effects, const MethodSummaries& method_summaries, + const std::unordered_set& excluded_classes, const std::unordered_map& root_methods, Stats* stats, size_t max_inline_size) { @@ -1658,8 +1684,8 @@ void reduce(DexStoresVector& stores, ExpandableMethodParams expandable_method_params(scope); std::unordered_set irreducible_types; auto reduced_methods = compute_reduced_methods( - expandable_method_params, inliner, method_summaries, root_methods, - &irreducible_types, stats, max_inline_size); + expandable_method_params, inliner, method_summaries, excluded_classes, + root_methods, &irreducible_types, stats, max_inline_size); stats->reduced_methods = reduced_methods.size(); // Second, we select reduced methods that will result in net savings @@ -1705,23 +1731,26 @@ void ObjectEscapeAnalysisPass::run_pass(DexStoresVector& stores, init_classes::InitClassesWithSideEffects init_classes_with_side_effects( scope, conf.create_init_class_insns(), method_override_graph.get()); + auto excluded_classes = get_excluded_classes(*method_override_graph); + ConcurrentMap new_instances; ConcurrentMap invokes; ConcurrentMap> dependencies; analyze_scope(scope, *method_override_graph, &new_instances, &invokes, &dependencies); - auto method_summaries = compute_method_summaries(mgr, scope, dependencies, - *method_override_graph); + auto method_summaries = compute_method_summaries( + mgr, scope, dependencies, *method_override_graph, excluded_classes); - auto inline_anchors = compute_inline_anchors(scope, method_summaries); + auto inline_anchors = + compute_inline_anchors(scope, method_summaries, excluded_classes); auto root_methods = compute_root_methods(mgr, new_instances, invokes, method_summaries, inline_anchors); Stats stats; reduce(stores, scope, conf, init_classes_with_side_effects, method_summaries, - root_methods, &stats, m_max_inline_size); + excluded_classes, root_methods, &stats, m_max_inline_size); TRACE(OEA, 1, "[object escape analysis] total savings: %zu", (size_t)stats.total_savings); diff --git a/test/integ/ObjectEscapeAnalysisTest.cpp b/test/integ/ObjectEscapeAnalysisTest.cpp index 1e221e74f4e..234a4ff68c4 100644 --- a/test/integ/ObjectEscapeAnalysisTest.cpp +++ b/test/integ/ObjectEscapeAnalysisTest.cpp @@ -383,3 +383,23 @@ TEST_F(ObjectEscapeAnalysisTest, reduceTo42IncompleteInlinableTypeB) { )"); ASSERT_EQ(actual.str(), assembler::to_s_expr(expected.get()).str()); } + +TEST_F(ObjectEscapeAnalysisTest, doNotReduceTo42Finalize) { + run(); + + auto actual = get_s_expr( + "Lcom/facebook/redextest/" + "ObjectEscapeAnalysisTest;.doNotReduceTo42Finalize:()I"); + auto expected = assembler::ircode_from_string(R"( + ( + (new-instance "Lcom/facebook/redextest/ObjectEscapeAnalysisTest$P;") + (move-result-pseudo-object v0) + (const v1 42) + (invoke-direct (v0 v1) "Lcom/facebook/redextest/ObjectEscapeAnalysisTest$P;.:(I)V") + (invoke-virtual (v0) "Lcom/facebook/redextest/ObjectEscapeAnalysisTest$P;.getX:()I") + (move-result v1) + (return v1) + ) +)"); + ASSERT_EQ(actual.str(), assembler::to_s_expr(expected.get()).str()); +} diff --git a/test/integ/ObjectEscapeAnalysisTest.java b/test/integ/ObjectEscapeAnalysisTest.java index 5077b85f848..706b31d362d 100644 --- a/test/integ/ObjectEscapeAnalysisTest.java +++ b/test/integ/ObjectEscapeAnalysisTest.java @@ -337,4 +337,24 @@ public static int reduceTo42IncompleteInlinableTypeB() { O.instance = new O(16); return o.getX(); } + + public static class P { + int x; + + public P(int x) { + this.x = x; + } + + public int getX() { + return this.x; + } + + protected void finalize() {} + } + + public static int doNotReduceTo42Finalize() { + // This object creation can NOT be reduced + P p = new P(42); + return p.getX(); + } } diff --git a/test/unit/ScopeHelper.cpp b/test/unit/ScopeHelper.cpp index d17c7dc36c4..42bb61fef46 100644 --- a/test/unit/ScopeHelper.cpp +++ b/test/unit/ScopeHelper.cpp @@ -83,16 +83,12 @@ DexClass* create_java_lang_object() { method->set_external(); obj_cls->add_method(method); + // protected java.lang.Object.finalize()V method = static_cast( - DexMethod::get_method(obj_t, finalize, void_void)); - if (method == nullptr) { - // protected java.lang.Object.finalize()V - method = static_cast( - DexMethod::make_method(obj_t, finalize, void_void)); - method->set_access(ACC_PROTECTED); - method->set_virtual(true); - method->set_external(); - } + DexMethod::make_method(obj_t, finalize, void_void)); + method->set_access(ACC_PROTECTED); + method->set_virtual(true); + method->set_external(); obj_cls->add_method(method); method = static_cast(