diff --git a/libredex/Reachability.cpp b/libredex/Reachability.cpp index 71cfb02e132..ed5acc0cc8e 100644 --- a/libredex/Reachability.cpp +++ b/libredex/Reachability.cpp @@ -1907,7 +1907,8 @@ void ReachableAspects::finish(const ConditionallyMarked& cond_marked, } std::unique_ptr compute_reachable_objects( - const DexStoresVector& stores, + const Scope& scope, + const method_override_graph::Graph& method_override_graph, const IgnoreSets& ignore_sets, int* num_ignore_check_strings, ReachableAspects* reachable_aspects, @@ -1918,17 +1919,14 @@ std::unique_ptr compute_reachable_objects( bool cfg_gathering_check_instance_callable, bool cfg_gathering_check_returning, bool should_mark_all_as_seed, - std::unique_ptr* out_method_override_graph, bool remove_no_argument_constructors) { Timer t("Marking"); - auto scope = build_class_scope(stores); std::unordered_set scope_set(scope.begin(), scope.end()); auto reachable_objects = std::make_unique(); ConditionallyMarked cond_marked; - auto method_override_graph = mog::build_graph(scope); ConcurrentSet root_set; - RootSetMarker root_set_marker(*method_override_graph, record_reachability, + RootSetMarker root_set_marker(method_override_graph, record_reachability, relaxed_keep_class_members, remove_no_argument_constructors, &cond_marked, reachable_objects.get(), &root_set); @@ -1944,7 +1942,7 @@ std::unique_ptr compute_reachable_objects( TransitiveClosureMarkerSharedState shared_state{ std::move(scope_set), &ignore_sets, - method_override_graph.get(), + &method_override_graph, record_reachability, relaxed_keep_class_members, relaxed_keep_interfaces, @@ -1965,17 +1963,13 @@ std::unique_ptr compute_reachable_objects( }, root_set, num_threads, /*push_tasks_while_running=*/true); - compute_zombie_methods(*method_override_graph, *reachable_objects, + compute_zombie_methods(method_override_graph, *reachable_objects, *reachable_aspects); if (num_ignore_check_strings != nullptr) { *num_ignore_check_strings = (int)stats.num_ignore_check_strings; } - if (out_method_override_graph) { - *out_method_override_graph = std::move(method_override_graph); - } - reachable_aspects->finish(cond_marked, *reachable_objects); return reachable_objects; diff --git a/libredex/Reachability.h b/libredex/Reachability.h index e6f0f989c54..741073359fe 100644 --- a/libredex/Reachability.h +++ b/libredex/Reachability.h @@ -691,7 +691,8 @@ class TransitiveClosureMarkerWorker { * (e.g. proguard rules). */ std::unique_ptr compute_reachable_objects( - const DexStoresVector& stores, + const Scope& scope, + const method_override_graph::Graph& method_override_graph, const IgnoreSets& ignore_sets, int* num_ignore_check_strings, ReachableAspects* reachable_aspects, @@ -702,8 +703,6 @@ std::unique_ptr compute_reachable_objects( bool cfg_gathering_check_instance_callable = false, bool cfg_gathering_check_returning = false, bool should_mark_all_as_seed = false, - std::unique_ptr* - out_method_override_graph = nullptr, bool remove_no_argument_constructors = false); void compute_zombie_methods( diff --git a/opt/remove-unreachable/RemoveUnreachable.cpp b/opt/remove-unreachable/RemoveUnreachable.cpp index 1d695689dc0..375ca457bc7 100644 --- a/opt/remove-unreachable/RemoveUnreachable.cpp +++ b/opt/remove-unreachable/RemoveUnreachable.cpp @@ -14,6 +14,8 @@ #include "ConfigFiles.h" #include "DexUtil.h" #include "IOUtil.h" +#include "InitClassesWithSideEffects.h" +#include "LocalDce.h" #include "MethodOverrideGraph.h" #include "PassManager.h" #include "Show.h" @@ -176,6 +178,18 @@ void RemoveUnreachablePassBase::run_pass(DexStoresVector& stores, // Store names of removed classes and methods ConcurrentSet removed_symbols; + auto sweep_code = m_prune_uninstantiable_insns || m_throw_propagation; + auto scope = build_class_scope(stores); + always_assert(!pm.unreliable_virtual_scopes()); + auto method_override_graph = mog::build_graph(scope); + std::unique_ptr + init_classes_with_side_effects; + if (sweep_code && !pm.init_class_lowering_has_run()) { + init_classes_with_side_effects = + std::make_unique( + scope, conf.create_init_class_insns(), method_override_graph.get()); + } + root_metrics(stores, pm); bool emit_graph_this_run = @@ -193,8 +207,8 @@ void RemoveUnreachablePassBase::run_pass(DexStoresVector& stores, int num_ignore_check_strings = 0; reachability::ReachableAspects reachable_aspects; auto reachables = this->compute_reachable_objects( - stores, pm, &num_ignore_check_strings, &reachable_aspects, - emit_graph_this_run, m_relaxed_keep_class_members, + scope, *method_override_graph, pm, &num_ignore_check_strings, + &reachable_aspects, emit_graph_this_run, m_relaxed_keep_class_members, m_prune_unreferenced_interfaces, m_prune_uninstantiable_insns, m_prune_uncallable_instance_method_bodies, m_throw_propagation, m_remove_no_argument_constructors); @@ -219,7 +233,7 @@ void RemoveUnreachablePassBase::run_pass(DexStoresVector& stores, auto abstracted_classes = reachability::mark_classes_abstract( stores, *reachables, reachable_aspects); pm.incr_metric("abstracted_classes", abstracted_classes.size()); - if (m_prune_uninstantiable_insns || m_throw_propagation) { + if (sweep_code) { remove_uninstantiables_impl::Stats remove_uninstantiables_stats; std::atomic throws_inserted{0}; InsertOnlyConcurrentSet affected_methods; @@ -230,6 +244,23 @@ void RemoveUnreachablePassBase::run_pass(DexStoresVector& stores, remove_uninstantiables_stats.report(pm); pm.incr_metric("throws_inserted", (size_t)throws_inserted); pm.incr_metric("methods_with_code_changes", affected_methods.size()); + std::unordered_set pure_methods; + LocalDce::Stats dce_stats; + std::mutex dce_stats_mutex; + workqueue_run( + [&](DexMethod* method) { + LocalDce dce(init_classes_with_side_effects.get(), pure_methods); + dce.dce(method->get_code()->cfg(), /* normalize_new_instances */ true, + method->get_class()); + auto local_stats = dce.get_stats(); + std::lock_guard lock(dce_stats_mutex); + dce_stats += local_stats; + }, + affected_methods); + pm.incr_metric("instructions_eliminated_localdce_dead", + dce_stats.dead_instruction_count); + pm.incr_metric("instructions_eliminated_localdce_unreachable", + dce_stats.unreachable_instruction_count); } reachability::sweep(stores, *reachables, output_unreachable_symbols ? &removed_symbols : nullptr, @@ -268,7 +299,7 @@ void RemoveUnreachablePassBase::run_pass(DexStoresVector& stores, { std::ofstream os; open_or_die(conf.metafile("method-override-graph"), &os); - auto method_override_graph = mog::build_graph(build_class_scope(stores)); + method_override_graph = mog::build_graph(build_class_scope(stores)); method_override_graph->dump(os); } } @@ -301,7 +332,8 @@ void RemoveUnreachablePassBase::write_out_removed_symbols( std::unique_ptr RemoveUnreachablePass::compute_reachable_objects( - const DexStoresVector& stores, + const Scope& scope, + const method_override_graph::Graph& method_override_graph, PassManager& /* pm */, int* num_ignore_check_strings, reachability::ReachableAspects* reachable_aspects, @@ -313,11 +345,11 @@ RemoveUnreachablePass::compute_reachable_objects( bool cfg_gathering_check_returning, bool remove_no_argument_constructors) { return reachability::compute_reachable_objects( - stores, m_ignore_sets, num_ignore_check_strings, reachable_aspects, - emit_graph_this_run, relaxed_keep_class_members, relaxed_keep_interfaces, - cfg_gathering_check_instantiable, cfg_gathering_check_instance_callable, - cfg_gathering_check_returning, false, nullptr, - remove_no_argument_constructors); + scope, method_override_graph, m_ignore_sets, num_ignore_check_strings, + reachable_aspects, emit_graph_this_run, relaxed_keep_class_members, + relaxed_keep_interfaces, cfg_gathering_check_instantiable, + cfg_gathering_check_instance_callable, cfg_gathering_check_returning, + false, remove_no_argument_constructors); } static RemoveUnreachablePass s_pass; diff --git a/opt/remove-unreachable/RemoveUnreachable.h b/opt/remove-unreachable/RemoveUnreachable.h index ddf6b750afb..7f6bfd4de6c 100644 --- a/opt/remove-unreachable/RemoveUnreachable.h +++ b/opt/remove-unreachable/RemoveUnreachable.h @@ -64,17 +64,19 @@ class RemoveUnreachablePassBase : public Pass { void run_pass(DexStoresVector&, ConfigFiles&, PassManager&) override; virtual std::unique_ptr - compute_reachable_objects(const DexStoresVector& stores, - PassManager& pm, - int* num_ignore_check_strings, - reachability::ReachableAspects* reachable_aspects, - bool emit_graph_this_run, - bool relaxed_keep_class_members, - bool relaxed_keep_interfaces, - bool cfg_gathering_check_instantiable, - bool cfg_gathering_check_instance_callable, - bool cfg_gathering_check_returning, - bool remove_no_argument_constructors) = 0; + compute_reachable_objects( + const Scope& scope, + const method_override_graph::Graph& method_override_graph, + PassManager& pm, + int* num_ignore_check_strings, + reachability::ReachableAspects* reachable_aspects, + bool emit_graph_this_run, + bool relaxed_keep_class_members, + bool relaxed_keep_interfaces, + bool cfg_gathering_check_instantiable, + bool cfg_gathering_check_instance_callable, + bool cfg_gathering_check_returning, + bool remove_no_argument_constructors) = 0; void write_out_removed_symbols( const std::string& filepath, @@ -101,7 +103,8 @@ class RemoveUnreachablePass : public RemoveUnreachablePassBase { : RemoveUnreachablePassBase("RemoveUnreachablePass") {} std::unique_ptr compute_reachable_objects( - const DexStoresVector& stores, + const Scope& scope, + const method_override_graph::Graph& method_override_graph, PassManager& pm, int* num_ignore_check_strings, reachability::ReachableAspects* reachable_aspects, diff --git a/opt/remove-unreachable/TypeAnalysisAwareRemoveUnreachable.cpp b/opt/remove-unreachable/TypeAnalysisAwareRemoveUnreachable.cpp index 480a83793d2..2bf38092ae9 100644 --- a/opt/remove-unreachable/TypeAnalysisAwareRemoveUnreachable.cpp +++ b/opt/remove-unreachable/TypeAnalysisAwareRemoveUnreachable.cpp @@ -339,7 +339,8 @@ class TypeAnalysisAwareClosureMarkerWorker final }; std::unique_ptr compute_reachable_objects_with_type_anaysis( - const DexStoresVector& stores, + const Scope& scope, + const method_override_graph::Graph& method_override_graph, const IgnoreSets& ignore_sets, int* num_ignore_check_strings, ReachableAspects* reachable_aspects, @@ -355,18 +356,16 @@ std::unique_ptr compute_reachable_objects_with_type_anaysis( int* num_unreachable_invokes, int* num_null_invokes) { Timer t("Marking"); - auto scope = build_class_scope(stores); std::unordered_set scope_set(scope.begin(), scope.end()); walk::parallel::code(scope, [](DexMethod*, IRCode& code) { code.cfg().calculate_exit_block(); }); auto reachable_objects = std::make_unique(); ConditionallyMarked cond_marked; - auto method_override_graph = mog::build_graph(scope); ConcurrentSet root_set; bool remove_no_argument_constructors = false; - RootSetMarker root_set_marker(*method_override_graph, record_reachability, + RootSetMarker root_set_marker(method_override_graph, record_reachability, relaxed_keep_class_members, remove_no_argument_constructors, &cond_marked, reachable_objects.get(), &root_set); @@ -375,7 +374,7 @@ std::unique_ptr compute_reachable_objects_with_type_anaysis( size_t num_threads = redex_parallel::default_num_threads(); Stats stats; TypeAnalysisAwareClosureMarkerSharedState shared_state{ - {std::move(scope_set), &ignore_sets, method_override_graph.get(), + {std::move(scope_set), &ignore_sets, &method_override_graph, record_reachability, relaxed_keep_class_members, relaxed_keep_interfaces, cfg_gathering_check_instantiable, cfg_gathering_check_instance_callable, cfg_gathering_check_returning, &cond_marked, reachable_objects.get(), @@ -392,7 +391,7 @@ std::unique_ptr compute_reachable_objects_with_type_anaysis( }, root_set, num_threads, /* push_tasks_while_running*/ true); - compute_zombie_methods(*method_override_graph, *reachable_objects, + compute_zombie_methods(method_override_graph, *reachable_objects, *reachable_aspects); if (num_ignore_check_strings != nullptr) { @@ -417,7 +416,8 @@ std::unique_ptr compute_reachable_objects_with_type_anaysis( std::unique_ptr TypeAnalysisAwareRemoveUnreachablePass::compute_reachable_objects( - const DexStoresVector& stores, + const Scope& scope, + const method_override_graph::Graph& method_override_graph, PassManager& pm, int* num_ignore_check_strings, reachability::ReachableAspects* reachable_aspects, @@ -438,11 +438,12 @@ TypeAnalysisAwareRemoveUnreachablePass::compute_reachable_objects( int num_unreachable_invokes; int num_null_invokes; auto res = compute_reachable_objects_with_type_anaysis( - stores, m_ignore_sets, num_ignore_check_strings, reachable_aspects, - emit_graph_this_run, relaxed_keep_class_members, relaxed_keep_interfaces, - cfg_gathering_check_instantiable, cfg_gathering_check_instance_callable, - cfg_gathering_check_returning, gta.get(), remove_no_argument_constructors, - &num_exact_resolved_callees, &num_unreachable_invokes, &num_null_invokes); + scope, method_override_graph, m_ignore_sets, num_ignore_check_strings, + reachable_aspects, emit_graph_this_run, relaxed_keep_class_members, + relaxed_keep_interfaces, cfg_gathering_check_instantiable, + cfg_gathering_check_instance_callable, cfg_gathering_check_returning, + gta.get(), remove_no_argument_constructors, &num_exact_resolved_callees, + &num_unreachable_invokes, &num_null_invokes); pm.incr_metric("num_exact_resolved_callees", num_exact_resolved_callees); pm.incr_metric("num_unreachable_invokes", num_unreachable_invokes); pm.incr_metric("num_null_invokes", num_null_invokes); diff --git a/opt/remove-unreachable/TypeAnalysisAwareRemoveUnreachable.h b/opt/remove-unreachable/TypeAnalysisAwareRemoveUnreachable.h index 9c4e59d8072..c2a77e0140b 100644 --- a/opt/remove-unreachable/TypeAnalysisAwareRemoveUnreachable.h +++ b/opt/remove-unreachable/TypeAnalysisAwareRemoveUnreachable.h @@ -23,7 +23,8 @@ class TypeAnalysisAwareRemoveUnreachablePass } std::unique_ptr compute_reachable_objects( - const DexStoresVector& stores, + const Scope& scope, + const method_override_graph::Graph& method_override_graph, PassManager& pm, int* num_ignore_check_strings, reachability::ReachableAspects* reachable_aspects, diff --git a/test/integ/FlowSensitiveReachabilityTest.cpp b/test/integ/FlowSensitiveReachabilityTest.cpp index cb8062250f9..3b2b80f10d0 100644 --- a/test/integ/FlowSensitiveReachabilityTest.cpp +++ b/test/integ/FlowSensitiveReachabilityTest.cpp @@ -31,9 +31,11 @@ TEST_F(FlowSensitiveReachabilityTest, reachability::ReachableAspects reachable_aspects; auto scope = build_class_scope(stores); walk::parallel::code(scope, [&](auto*, auto& code) { code.build_cfg(); }); + auto method_override_graph = method_override_graph::build_graph(scope); auto reachable_objects = reachability::compute_reachable_objects( - stores, ig_sets, &num_ignore_check_strings, &reachable_aspects, false, + scope, *method_override_graph, ig_sets, &num_ignore_check_strings, + &reachable_aspects, false, /* relaxed_keep_class_members */ true, /* relaxed_keep_interfaces */ false, /* cfg_gathering_check_instantiable */ true); @@ -112,9 +114,11 @@ TEST_F(FlowSensitiveReachabilityTest, cfg_gathering_check_instance_callable) { reachability::ReachableAspects reachable_aspects; auto scope = build_class_scope(stores); walk::parallel::code(scope, [&](auto*, auto& code) { code.build_cfg(); }); + auto method_override_graph = method_override_graph::build_graph(scope); auto reachable_objects = reachability::compute_reachable_objects( - stores, ig_sets, &num_ignore_check_strings, &reachable_aspects, false, + scope, *method_override_graph, ig_sets, &num_ignore_check_strings, + &reachable_aspects, false, /* relaxed_keep_class_members */ true, /* relaxed_keep_interfaces */ false, /* cfg_gathering_check_instantiable */ true, @@ -195,9 +199,11 @@ TEST_F(FlowSensitiveReachabilityTest, sweep_uncallable_virtual_methods) { reachability::ReachableAspects reachable_aspects; auto scope = build_class_scope(stores); walk::parallel::code(scope, [&](auto*, auto& code) { code.build_cfg(); }); + auto method_override_graph = method_override_graph::build_graph(scope); auto reachable_objects = reachability::compute_reachable_objects( - stores, ig_sets, &num_ignore_check_strings, &reachable_aspects, false, + scope, *method_override_graph, ig_sets, &num_ignore_check_strings, + &reachable_aspects, false, /* relaxed_keep_class_members */ true, /* relaxed_keep_interfaces */ false, /* cfg_gathering_check_instantiable */ true, @@ -288,9 +294,11 @@ TEST_F(FlowSensitiveReachabilityTest, abstract_overrides_non_abstract) { reachability::ReachableAspects reachable_aspects; auto scope = build_class_scope(stores); walk::parallel::code(scope, [&](auto*, auto& code) { code.build_cfg(); }); + auto method_override_graph = method_override_graph::build_graph(scope); auto reachable_objects = reachability::compute_reachable_objects( - stores, ig_sets, &num_ignore_check_strings, &reachable_aspects, false, + scope, *method_override_graph, ig_sets, &num_ignore_check_strings, + &reachable_aspects, false, /* relaxed_keep_class_members */ true, /* relaxed_keep_interfaces */ false, /* cfg_gathering_check_instantiable */ true); @@ -333,9 +341,11 @@ TEST_F(FlowSensitiveReachabilityTest, throw_propagation) { reachability::ReachableAspects reachable_aspects; auto scope = build_class_scope(stores); walk::parallel::code(scope, [&](auto*, auto& code) { code.build_cfg(); }); + auto method_override_graph = method_override_graph::build_graph(scope); auto reachable_objects = reachability::compute_reachable_objects( - stores, ig_sets, &num_ignore_check_strings, &reachable_aspects, false, + scope, *method_override_graph, ig_sets, &num_ignore_check_strings, + &reachable_aspects, false, /* relaxed_keep_class_members */ true, /* relaxed_keep_interfaces */ true, /* cfg_gathering_check_instantiable */ true, diff --git a/test/integ/ReachabilityTest.cpp b/test/integ/ReachabilityTest.cpp index 949f78f272c..97ac3ca6ac2 100644 --- a/test/integ/ReachabilityTest.cpp +++ b/test/integ/ReachabilityTest.cpp @@ -40,8 +40,11 @@ TEST_F(ReachabilityTest, ReachabilityFromProguardTest) { reachability::ReachableAspects reachable_aspects; auto scope = build_class_scope(stores); walk::parallel::code(scope, [&](auto*, auto& code) { code.build_cfg(); }); + auto method_override_graph = method_override_graph::build_graph(scope); + auto reachable_objects = reachability::compute_reachable_objects( - stores, ig_sets, &num_ignore_check_strings, &reachable_aspects); + scope, *method_override_graph, ig_sets, &num_ignore_check_strings, + &reachable_aspects); walk::parallel::code(scope, [&](auto*, auto& code) { code.clear_cfg(); }); reachability::mark_classes_abstract(stores, *reachable_objects, @@ -82,14 +85,17 @@ TEST_F(ReachabilityTest, ReachabilityMarkAllTest) { reachability::ReachableAspects reachable_aspects; auto scope = build_class_scope(stores); walk::parallel::code(scope, [&](auto*, auto& code) { code.build_cfg(); }); + auto method_override_graph = method_override_graph::build_graph(scope); + auto reachable_objects = reachability::compute_reachable_objects( - stores, ig_sets, &num_ignore_check_strings, &reachable_aspects, + scope, *method_override_graph, ig_sets, &num_ignore_check_strings, + &reachable_aspects, /* record_reachability */ false, /* relaxed_keep_class_members */ false, /* relaxed_keep_interfaces */ false, /* cfg_gathering_check_instantiable */ false, /* cfg_gathering_check_instance_callable */ false, /* cfg_gathering_check_returning */ false, - /* should_mark_all_as_seed */ true, nullptr); + /* should_mark_all_as_seed */ true); walk::parallel::code(scope, [&](auto*, auto& code) { code.clear_cfg(); }); reachability::mark_classes_abstract(stores, *reachable_objects, @@ -121,8 +127,11 @@ TEST_F(ReachabilityTest, NotDireclyInstantiatedClassesBecomeAbstract) { reachability::ReachableAspects reachable_aspects; auto scope = build_class_scope(stores); walk::parallel::code(scope, [&](auto*, auto& code) { code.build_cfg(); }); + auto method_override_graph = method_override_graph::build_graph(scope); + auto reachable_objects = reachability::compute_reachable_objects( - stores, ig_sets, &num_ignore_check_strings, &reachable_aspects); + scope, *method_override_graph, ig_sets, &num_ignore_check_strings, + &reachable_aspects); walk::parallel::code(scope, [&](auto*, auto& code) { code.clear_cfg(); }); auto abstracted_classes = reachability::mark_classes_abstract( stores, *reachable_objects, reachable_aspects); @@ -173,8 +182,11 @@ TEST_F(ReachabilityTest, SharpeningCreatesMoreZombies) { reachability::ReachableAspects reachable_aspects; auto scope = build_class_scope(stores); walk::parallel::code(scope, [&](auto*, auto& code) { code.build_cfg(); }); + auto method_override_graph = method_override_graph::build_graph(scope); + auto reachable_objects = reachability::compute_reachable_objects( - stores, ig_sets, &num_ignore_check_strings, &reachable_aspects); + scope, *method_override_graph, ig_sets, &num_ignore_check_strings, + &reachable_aspects); walk::parallel::code(scope, [&](auto*, auto& code) { code.clear_cfg(); }); auto is_callable_instance_method = [&](const auto& s) { diff --git a/test/integ/VirtualTargetsReachabilityTest.cpp b/test/integ/VirtualTargetsReachabilityTest.cpp index 1c83bf0e7cd..abdc828e3d0 100644 --- a/test/integ/VirtualTargetsReachabilityTest.cpp +++ b/test/integ/VirtualTargetsReachabilityTest.cpp @@ -32,9 +32,11 @@ TEST_F(VirtualTargetsReachabilityTest, invoke_super_subtlety) { reachability::ReachableAspects reachable_aspects; auto scope = build_class_scope(stores); walk::parallel::code(scope, [&](auto*, auto& code) { code.build_cfg(); }); + auto method_override_graph = method_override_graph::build_graph(scope); auto reachable_objects = reachability::compute_reachable_objects( - stores, ig_sets, &num_ignore_check_strings, &reachable_aspects, false, + scope, *method_override_graph, ig_sets, &num_ignore_check_strings, + &reachable_aspects, false, /* relaxed_keep_class_members */ true, /* relaxed_keep_interfaces */ false, /* cfg_gathering_check_instantiable */ true);