diff --git a/Makefile.am b/Makefile.am index 03a33819e59..e9d39381e11 100644 --- a/Makefile.am +++ b/Makefile.am @@ -415,6 +415,7 @@ libopt_la_SOURCES = \ opt/interdex/InterDexPass.cpp \ opt/interdex/InterDexReshuffleImpl.cpp \ opt/interdex/InterDexReshufflePass.cpp \ + opt/interdex/MAInterDexReshufflePass.cpp \ opt/interdex/SortRemainingClassesPass.cpp \ opt/kotlin-lambda/RewriteKotlinSingletonInstance.cpp \ opt/kotlin-lambda/KotlinObjectInliner.cpp \ diff --git a/opt/class-merging/IntraDexClassMergingPass.h b/opt/class-merging/IntraDexClassMergingPass.h index 7e89a1aa5ec..2ba3e5fc28a 100644 --- a/opt/class-merging/IntraDexClassMergingPass.h +++ b/opt/class-merging/IntraDexClassMergingPass.h @@ -28,7 +28,7 @@ class IntraDexClassMergingPass : public Pass { using namespace redex_properties::interactions; using namespace redex_properties::names; return { - {DexLimitsObeyed, Preserves}, + {DexLimitsObeyed, Establishes}, {NoResolvablePureRefs, Preserves}, {InitialRenameClass, Preserves}, }; diff --git a/opt/interdex/InterDexReshufflePass.cpp b/opt/interdex/InterDexReshufflePass.cpp index 97ce733488a..2672d11af00 100644 --- a/opt/interdex/InterDexReshufflePass.cpp +++ b/opt/interdex/InterDexReshufflePass.cpp @@ -6,14 +6,12 @@ */ #include "InterDexReshufflePass.h" -#include "ClassMerging.h" #include "ConfigFiles.h" #include "DedupStrings.h" #include "DexClass.h" #include "DexStructure.h" #include "DexUtil.h" #include "InterDexPass.h" -#include "ModelSpecGenerator.h" #include "PassManager.h" #include "Show.h" #include "StlUtil.h" @@ -51,29 +49,9 @@ void InterDexReshufflePass::run_pass(DexStoresVector& stores, return; } - auto has_IDCM_pass = mgr.find_pass("IntraDexClassMergingPass"); - // The mergeability_aware reshuffle algorithm is only enabled when 1) there - // will be a IDCM pass; 2) m_allow_mergeability_aware is set true; and 3) This - // is the first run of InterDexReshufflePass. - bool enable_mergeability_aware_reshuffle = - has_IDCM_pass && m_allow_mergeability_aware && m_run == 0; - if (enable_mergeability_aware_reshuffle) { - TRACE(PM, 1, "Run regular mergeability-aware InterDexReshuffle"); - mgr.incr_metric("Mergeability_aware", 1); - class_merging::Model merging_model = class_merging::construct_global_model( - original_scope, mgr, conf, stores); - - InterDexReshuffleImpl impl(conf, mgr, m_config, original_scope, root_dexen, - merging_model); - impl.compute_plan(); - impl.apply_plan(); - } else { - TRACE(PM, 1, "Run regular InterDexReshuffle"); - mgr.incr_metric("Mergeability_aware", 0); - InterDexReshuffleImpl impl(conf, mgr, m_config, original_scope, root_dexen); - impl.compute_plan(); - impl.apply_plan(); - } + InterDexReshuffleImpl impl(conf, mgr, m_config, original_scope, root_dexen); + impl.compute_plan(); + impl.apply_plan(); // Sanity check std::unordered_set original_scope_set(original_scope.begin(), @@ -85,8 +63,6 @@ void InterDexReshufflePass::run_pass(DexStoresVector& stores, for (auto cls : original_scope_set) { always_assert(new_scope_set.count(cls)); } - - ++m_run; } static InterDexReshufflePass s_pass; diff --git a/opt/interdex/InterDexReshufflePass.h b/opt/interdex/InterDexReshufflePass.h index 3dc30913824..35af0668cc9 100644 --- a/opt/interdex/InterDexReshufflePass.h +++ b/opt/interdex/InterDexReshufflePass.h @@ -89,30 +89,14 @@ class InterDexReshufflePass : public Pass { m_config.max_batch_size, "How many class to move per batch. More might yield better results, " "but might take longer."); - bind("other_weight", - m_config.other_weight, - m_config.other_weight, - "Weight for non-deduped method in mergeability-aware reshuffle cost " - "function."); - bind("deduped_weight", - m_config.deduped_weight, - m_config.deduped_weight, - "Weight for deduped method in mergeability-aware reshuffle cost " - "function."); bind("exclude_below20pct_coldstart_classes", false, m_config.exclude_below20pct_coldstart_classes, "Whether to exclude coldstart classes in between 1pctColdStart and " "20pctColdStart marker" "from the reshuffle."); - bind("allow_mergeability_aware", true, m_allow_mergeability_aware); } private: ReshuffleConfig m_config; - - // Which iteration of `run_pass`. - size_t m_run{0}; - - bool m_allow_mergeability_aware; }; diff --git a/opt/interdex/MAInterDexReshufflePass.cpp b/opt/interdex/MAInterDexReshufflePass.cpp new file mode 100644 index 00000000000..df7c3528582 --- /dev/null +++ b/opt/interdex/MAInterDexReshufflePass.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "MAInterDexReshufflePass.h" +#include "ClassMerging.h" +#include "ConfigFiles.h" +#include "DedupStrings.h" +#include "DexClass.h" +#include "DexStructure.h" +#include "DexUtil.h" +#include "InterDexPass.h" +#include "ModelSpecGenerator.h" +#include "PassManager.h" +#include "Show.h" +#include "StlUtil.h" +#include "Trace.h" +#include "Walkers.h" + +namespace { +const interdex::InterDexPass* get_interdex_pass(const PassManager& mgr) { + const auto* pass = + static_cast(mgr.find_pass("InterDexPass")); + always_assert_log(pass, "InterDexPass missing"); + return pass; +} +} // namespace + +void MergeabilityAwareInterDexReshufflePass::run_pass(DexStoresVector& stores, + ConfigFiles& conf, + PassManager& mgr) { + const auto* interdex_pass = get_interdex_pass(mgr); + if (!interdex_pass->minimize_cross_dex_refs()) { + mgr.incr_metric("no minimize_cross_dex_refs", 1); + TRACE(IDEXR, 1, + "MergeabilityAwareInterDexReshufflePass not run because InterDexPass " + "is not configured " + "for minimize_cross_dex_refs."); + return; + } + + auto has_IDCM_pass = mgr.find_pass("IntraDexClassMergingPass"); + if (!has_IDCM_pass) { + mgr.incr_metric("no IntraDexClassMergingPass", 1); + TRACE(IDEXR, 1, + "MergeabilityAwareInterDexReshufflePass not run because there is no " + "IntraDexClassMergingPass."); + return; + } + auto original_scope = build_class_scope(stores); + + auto& root_store = stores.at(0); + auto& root_dexen = root_store.get_dexen(); + if (root_dexen.size() == 1) { + // only a primary dex? Nothing to do + return; + } + + class_merging::Model merging_model = + class_merging::construct_global_model(original_scope, mgr, conf, stores); + + InterDexReshuffleImpl impl(conf, mgr, m_config, original_scope, root_dexen, + merging_model); + impl.compute_plan(); + impl.apply_plan(); + + // Sanity check + std::unordered_set original_scope_set(original_scope.begin(), + original_scope.end()); + auto new_scope = build_class_scope(stores); + std::unordered_set new_scope_set(new_scope.begin(), + new_scope.end()); + always_assert(original_scope_set.size() == new_scope_set.size()); + for (auto cls : original_scope_set) { + always_assert(new_scope_set.count(cls)); + } +} + +static MergeabilityAwareInterDexReshufflePass s_pass; diff --git a/opt/interdex/MAInterDexReshufflePass.h b/opt/interdex/MAInterDexReshufflePass.h new file mode 100644 index 00000000000..dbe4f29b870 --- /dev/null +++ b/opt/interdex/MAInterDexReshufflePass.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include "DexClass.h" +#include "InterDex.h" +#include "InterDexReshuffleImpl.h" +#include "Pass.h" + +/* Similar to InterDexReshufflePass, this pass impls Local Search Algotithm to + * minize cross-dex refs by reshuffling classes among dex files. Different from + * InterDexReshufflePass, when reshuffling classes, this pass considers the + * classes mergeability. That is, if two classes may be merged in later IDCM, + * they have the high possibility to moved to the same dex. + */ +class MergeabilityAwareInterDexReshufflePass : public Pass { + public: + explicit MergeabilityAwareInterDexReshufflePass() + : Pass("MergeabilityAwareInterDexReshufflePass") {} + + redex_properties::PropertyInteractions get_property_interactions() + const override { + using namespace redex_properties::interactions; + using namespace redex_properties::names; + return { + {NoResolvablePureRefs, Preserves}, + {InitialRenameClass, Preserves}, + }; + } + + void run_pass(DexStoresVector&, ConfigFiles&, PassManager&) override; + + void bind_config() override { + bind("reserved_extra_frefs", + m_config.reserved_extra_frefs, + m_config.reserved_extra_frefs, + "How many extra frefs to be reserved for the dexes this pass " + "processes."); + bind("reserved_extra_trefs", + m_config.reserved_extra_trefs, + m_config.reserved_extra_trefs, + "How many extra trefs to be reserved for the dexes this pass " + "processes."); + bind("reserved_extra_mrefs", + m_config.reserved_extra_mrefs, + m_config.reserved_extra_mrefs, + "How many extra mrefs to be reserved for the dexes this pass " + "processes."); + bind("extra_linear_alloc_limit", + m_config.extra_linear_alloc_limit, + m_config.extra_linear_alloc_limit, + "How many extra linear_alloc_limit to be reserved for the dexes " + "this pass rocesses."); + bind("max_batches", + m_config.max_batches, + m_config.max_batches, + "How many batches to execute. More might yield better results, but " + "might take longer."); + bind("max_batch_size", + m_config.max_batch_size, + m_config.max_batch_size, + "How many class to move per batch. More might yield better results, " + "but might take longer."); + bind("other_weight", + m_config.other_weight, + m_config.other_weight, + "Weight for non-deduped method in mergeability-aware reshuffle cost " + "function."); + bind("deduped_weight", + m_config.deduped_weight, + m_config.deduped_weight, + "Weight for deduped method in mergeability-aware reshuffle cost " + "function."); + bind("exclude_below20pct_coldstart_classes", + false, + m_config.exclude_below20pct_coldstart_classes, + "Whether to exclude coldstart classes in between 1pctColdStart and " + "20pctColdStart marker" + "from the reshuffle."); + } + + private: + ReshuffleConfig m_config; +};