ART: Add capability for a pass to have options

This patch adds capability to have pass options. These are needed when a pass
has multiple flags that can be tweaked. The user is now allowed to pass those
options via command line.

Since passes are treated as singletons and they are immutable, the overridden
options provided by user are set on the compilation unit. Doing this way also
allows a selectivity system to tweak the option per compilation instead of
doing it globally (due to the single pass existing).

The following command line flags have been added:
--print-pass-options - This prints all passes that have options along with
their defaults.
--pass-options= - This is used to pass the overridden options in format of
PassName:PassOption:PassOptionSetting

Change-Id: Ib5156f5d2ff51a0c64c4ea0fa050bd2170663417
Signed-off-by: Razvan A Lupusoru <razvan.a.lupusoru@intel.com>
Signed-off-by: Jean Christophe Beyler <jean.christophe.beyler@intel.com>
diff --git a/compiler/dex/compiler_ir.cc b/compiler/dex/compiler_ir.cc
index 7a5b114..e1d489a 100644
--- a/compiler/dex/compiler_ir.cc
+++ b/compiler/dex/compiler_ir.cc
@@ -54,6 +54,7 @@
 }
 
 CompilationUnit::~CompilationUnit() {
+  overridden_pass_options.clear();
 }
 
 void CompilationUnit::StartTimingSplit(const char* label) {
diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h
index 67bf88a..b20840ad 100644
--- a/compiler/dex/compiler_ir.h
+++ b/compiler/dex/compiler_ir.h
@@ -17,6 +17,7 @@
 #ifndef ART_COMPILER_DEX_COMPILER_IR_H_
 #define ART_COMPILER_DEX_COMPILER_IR_H_
 
+#include <string>
 #include <vector>
 
 #include "compiler_enums.h"
@@ -91,6 +92,15 @@
   std::unique_ptr<Backend> cg;           // Target-specific codegen.
   TimingLogger timings;
   bool print_pass;                 // Do we want to print a pass or not?
+
+  /**
+   * @brief Holds pass options for current pass being applied to compilation unit.
+   * @details This is updated for every pass to contain the overridden pass options
+   * that were specified by user. The pass itself will check this to see if the
+   * default settings have been changed. The key is simply the option string without
+   * the pass name.
+   */
+  SafeMap<const std::string, int> overridden_pass_options;
 };
 
 }  // namespace art
diff --git a/compiler/dex/pass_driver.h b/compiler/dex/pass_driver.h
index bd8f53c..8a3eae1 100644
--- a/compiler/dex/pass_driver.h
+++ b/compiler/dex/pass_driver.h
@@ -161,6 +161,17 @@
     print_pass_list_ = list;
   }
 
+  /**
+   * @brief Used to set a string that contains the overridden pass options.
+   * @details An overridden pass option means that the pass uses this option
+   * instead of using its default option.
+   * @param s The string passed by user with overridden options. The string is in format
+   * Pass1Name:Pass1Option:Pass1Setting,Pass2Name:Pass2Option::Pass2Setting
+   */
+  static void SetOverriddenPassOptions(const std::string& s) {
+    overridden_pass_options_list_ = s;
+  }
+
   void SetDefaultPasses() {
     pass_list_ = PassDriver<PassDriverType>::g_default_pass_list;
   }
@@ -206,6 +217,9 @@
 
   /** @brief What are the passes we want to be dumping the CFG? */
   static std::string dump_pass_list_;
+
+  /** @brief String of all options that should be overridden for selected passes */
+  static std::string overridden_pass_options_list_;
 };
 
 }  // namespace art
diff --git a/compiler/dex/pass_driver_me.h b/compiler/dex/pass_driver_me.h
index 133593c..537ceb6 100644
--- a/compiler/dex/pass_driver_me.h
+++ b/compiler/dex/pass_driver_me.h
@@ -17,6 +17,8 @@
 #ifndef ART_COMPILER_DEX_PASS_DRIVER_ME_H_
 #define ART_COMPILER_DEX_PASS_DRIVER_ME_H_
 
+#include <cstdlib>
+#include <cstring>
 #include "bb_optimizations.h"
 #include "dataflow_iterator.h"
 #include "dataflow_iterator-inl.h"
@@ -94,19 +96,27 @@
       c_unit->NewTimingSplit(pass->GetName());
     }
 
+    // First, work on determining pass verbosity.
+    bool old_print_pass = c_unit->print_pass;
+    c_unit->print_pass = PassDriver<PassDriverType>::default_print_passes_;
+    const char* print_pass_list = PassDriver<PassDriverType>::print_pass_list_.c_str();
+    if (print_pass_list != nullptr && strstr(print_pass_list, pass->GetName()) != nullptr) {
+      c_unit->print_pass = true;
+    }
+
+    // Next, check if there are any overridden settings for the pass that change default configuration.
+    c_unit->overridden_pass_options.clear();
+    FillOverriddenPassSettings(pass->GetName(), c_unit->overridden_pass_options);
+    if (c_unit->print_pass) {
+      for (auto setting_it : c_unit->overridden_pass_options) {
+        LOG(INFO) << "Overridden option \"" << setting_it.first << ":"
+          << setting_it.second << "\" for pass \"" << pass->GetName() << "\"";
+      }
+    }
+
     // Check the pass gate first.
     bool should_apply_pass = pass->Gate(&pass_me_data_holder_);
     if (should_apply_pass) {
-      bool old_print_pass = c_unit->print_pass;
-
-      c_unit->print_pass = PassDriver<PassDriverType>::default_print_passes_;
-
-      const char* print_pass_list = PassDriver<PassDriverType>::print_pass_list_.c_str();
-
-      if (print_pass_list != nullptr && strstr(print_pass_list, pass->GetName()) != nullptr) {
-        c_unit->print_pass = true;
-      }
-
       // Applying the pass: first start, doWork, and end calls.
       this->ApplyPass(&pass_me_data_holder_, pass);
 
@@ -137,10 +147,11 @@
           }
         }
       }
-
-      c_unit->print_pass = old_print_pass;
     }
 
+    // Before wrapping up with this pass, restore old pass verbosity flag.
+    c_unit->print_pass = old_print_pass;
+
     // If the pass gate passed, we can declare success.
     return should_apply_pass;
   }
@@ -149,6 +160,18 @@
     return dump_cfg_folder_;
   }
 
+  static void PrintPassOptions() {
+    for (auto pass : PassDriver<PassDriverType>::g_default_pass_list) {
+      const PassME* me_pass = down_cast<const PassME*>(pass);
+      if (me_pass->HasOptions()) {
+        LOG(INFO) << "Pass options for \"" << me_pass->GetName() << "\" are:";
+        SafeMap<const std::string, int> overridden_settings;
+        FillOverriddenPassSettings(me_pass->GetName(), overridden_settings);
+        me_pass->PrintPassOptions(overridden_settings);
+      }
+    }
+  }
+
  protected:
   /** @brief The data holder that contains data needed for the PassDriverME. */
   PassMEDataHolder pass_me_data_holder_;
@@ -175,6 +198,97 @@
       Iterator iterator(c_unit->mir_graph.get());
       DoWalkBasicBlocks(data, pass, &iterator);
     }
+
+  /**
+   * @brief Fills the settings_to_fill by finding all of the applicable options in the overridden_pass_options_list_.
+   * @param pass_name The pass name for which to fill settings.
+   * @param settings_to_fill Fills the options to contain the mapping of name of option to the new configuration.
+   */
+  static void FillOverriddenPassSettings(const char* pass_name, SafeMap<const std::string, int>& settings_to_fill) {
+    const std::string& settings = PassDriver<PassDriverType>::overridden_pass_options_list_;
+    const size_t settings_len = settings.size();
+
+    // Before anything, check if we care about anything right now.
+    if (settings_len == 0) {
+      return;
+    }
+
+    const size_t pass_name_len = strlen(pass_name);
+    const size_t min_setting_size = 4;  // 2 delimiters, 1 setting name, 1 setting
+    size_t search_pos = 0;
+
+    // If there is no room for pass options, exit early.
+    if (settings_len < pass_name_len + min_setting_size) {
+      return;
+    }
+
+    do {
+      search_pos = settings.find(pass_name, search_pos);
+
+      // Check if we found this pass name in rest of string.
+      if (search_pos == std::string::npos) {
+        // No more settings for this pass.
+        break;
+      }
+
+      // The string contains the pass name. Now check that there is
+      // room for the settings: at least one char for setting name,
+      // two chars for two delimiter, and at least one char for setting.
+      if (search_pos + pass_name_len + min_setting_size >= settings_len) {
+        // No more settings for this pass.
+        break;
+      }
+
+      // Update the current search position to not include the pass name.
+      search_pos += pass_name_len;
+
+      // The format must be "PassName:SettingName:#" where # is the setting.
+      // Thus look for the first ":" which must exist.
+      if (settings[search_pos] != ':') {
+        // Missing delimiter right after pass name.
+        continue;
+      } else {
+        search_pos += 1;
+      }
+
+      // Now look for the actual setting by finding the next ":" delimiter.
+      const size_t setting_name_pos = search_pos;
+      size_t setting_pos = settings.find(':', setting_name_pos);
+
+      if (setting_pos == std::string::npos) {
+        // Missing a delimiter that would capture where setting starts.
+        continue;
+      } else if (setting_pos == setting_name_pos) {
+        // Missing setting thus did not move from setting name
+        continue;
+      } else {
+        // Skip the delimiter.
+        setting_pos += 1;
+      }
+
+      // Look for the terminating delimiter which must be a comma.
+      size_t next_configuration_separator = settings.find(',', setting_pos);
+      if (next_configuration_separator == std::string::npos) {
+        next_configuration_separator = settings_len;
+      }
+
+      // Prevent end of string errors.
+      if (next_configuration_separator == setting_pos) {
+          continue;
+      }
+
+      // Get the actual setting itself. Strtol is being used to convert because it is
+      // exception safe. If the input is not sane, it will set a setting of 0.
+      std::string setting_string = settings.substr(setting_pos, next_configuration_separator - setting_pos);
+      int setting = std::strtol(setting_string.c_str(), 0, 0);
+
+      std::string setting_name = settings.substr(setting_name_pos, setting_pos - setting_name_pos - 1);
+
+      settings_to_fill.Put(setting_name, setting);
+
+      search_pos = next_configuration_separator;
+    } while (true);
+  }
 };
 }  // namespace art
 #endif  // ART_COMPILER_DEX_PASS_DRIVER_ME_H_
diff --git a/compiler/dex/pass_driver_me_opts.cc b/compiler/dex/pass_driver_me_opts.cc
index c72a4a6..aea7543 100644
--- a/compiler/dex/pass_driver_me_opts.cc
+++ b/compiler/dex/pass_driver_me_opts.cc
@@ -69,6 +69,10 @@
 template<>
 bool PassDriver<PassDriverMEOpts>::default_print_passes_ = false;
 
+// By default, there are no overridden pass settings.
+template<>
+std::string PassDriver<PassDriverMEOpts>::overridden_pass_options_list_ = std::string();
+
 void PassDriverMEOpts::ApplyPass(PassDataHolder* data, const Pass* pass) {
   // First call the base class' version.
   PassDriver::ApplyPass(data, pass);
diff --git a/compiler/dex/pass_driver_me_post_opt.cc b/compiler/dex/pass_driver_me_post_opt.cc
index 14108af..4acab6c 100644
--- a/compiler/dex/pass_driver_me_post_opt.cc
+++ b/compiler/dex/pass_driver_me_post_opt.cc
@@ -73,4 +73,8 @@
 template<>
 bool PassDriver<PassDriverMEPostOpt>::default_print_passes_ = false;
 
+// By default, there are no overridden pass settings.
+template<>
+std::string PassDriver<PassDriverMEPostOpt>::overridden_pass_options_list_ = std::string();
+
 }  // namespace art
diff --git a/compiler/dex/pass_me.h b/compiler/dex/pass_me.h
index c7276eb..0865afd 100644
--- a/compiler/dex/pass_me.h
+++ b/compiler/dex/pass_me.h
@@ -80,12 +80,53 @@
   }
 
   ~PassME() {
+    default_options_.clear();
   }
 
   virtual DataFlowAnalysisMode GetTraversal() const {
     return traversal_type_;
   }
 
+  /**
+   * @return Returns whether the pass has any configurable options.
+   */
+  bool HasOptions() const {
+    return default_options_.size() != 0;
+  }
+
+  /**
+   * @brief Prints the pass options along with default settings if there are any.
+   * @details The printing is done using LOG(INFO).
+   */
+  void PrintPassDefaultOptions() const {
+    for (auto option_it = default_options_.begin(); option_it != default_options_.end(); option_it++) {
+      LOG(INFO) << "\t" << option_it->first << ":" << std::dec << option_it->second;
+    }
+  }
+
+  /**
+   * @brief Prints the pass options along with either default or overridden setting.
+   * @param overridden_options The overridden settings for this pass.
+   */
+  void PrintPassOptions(SafeMap<const std::string, int>& overridden_options) const {
+    // We walk through the default options only to get the pass names. We use GetPassOption to
+    // also consider the overridden ones.
+    for (auto option_it = default_options_.begin(); option_it != default_options_.end(); option_it++) {
+      LOG(INFO) << "\t" << option_it->first << ":" << std::dec << GetPassOption(option_it->first, overridden_options);
+    }
+  }
+
+  /**
+   * @brief Used to obtain the option for a pass.
+   * @details Will return the overridden option if it exists or default one.
+   * @param option_name The name of option whose setting to look for.
+   * @param c_unit The compilation unit currently being handled.
+   * @return Returns the setting for the pass option.
+   */
+  int GetPassOption(const char* option_name, CompilationUnit* c_unit) const {
+    return GetPassOption(option_name, c_unit->overridden_pass_options);
+  }
+
   const char* GetDumpCFGFolder() const {
     return dump_cfg_folder_;
   }
@@ -95,6 +136,25 @@
   }
 
  protected:
+  int GetPassOption(const char* option_name, const SafeMap<const std::string, int>& overridden_options) const {
+    // First check if there are any overridden settings.
+    auto overridden_it = overridden_options.find(std::string(option_name));
+    if (overridden_it != overridden_options.end()) {
+      return overridden_it->second;
+    }
+
+    // Next check the default options.
+    auto default_it = default_options_.find(option_name);
+
+    if (default_it == default_options_.end()) {
+      // An invalid option is being requested.
+      DCHECK(false);
+      return 0;
+    }
+
+    return default_it->second;
+  }
+
   /** @brief Type of traversal: determines the order to execute the pass on the BasicBlocks. */
   const DataFlowAnalysisMode traversal_type_;
 
@@ -103,6 +163,13 @@
 
   /** @brief CFG Dump Folder: what sub-folder to use for dumping the CFGs post pass. */
   const char* const dump_cfg_folder_;
+
+  /**
+   * @brief Contains a map of options with the default settings.
+   * @details The constructor of the specific pass instance should fill this
+   * with default options.
+   * */
+  SafeMap<const char*, int> default_options_;
 };
 }  // namespace art
 #endif  // ART_COMPILER_DEX_PASS_ME_H_
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 19b37af..aef235b 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -226,6 +226,15 @@
   UsageError("  --disable-passes=<pass-names>:  disable one or more passes separated by comma.");
   UsageError("      Example: --disable-passes=UseCount,BBOptimizations");
   UsageError("");
+  UsageError("  --print-pass-options: print a list of passes that have configurable options along "
+             "with the setting.");
+  UsageError("      Will print default if no overridden setting exists.");
+  UsageError("");
+  UsageError("  --pass-options=Pass1Name:Pass1OptionName:Pass1Option#,"
+             "Pass2Name:Pass2OptionName:Pass2Option#");
+  UsageError("      Used to specify a pass specific option. The setting itself must be integer.");
+  UsageError("      Separator used between options is a comma.");
+  UsageError("");
   std::cerr << "See log for usage error information\n";
   exit(EXIT_FAILURE);
 }
@@ -847,6 +856,7 @@
   bool dump_stats = false;
   bool dump_timing = false;
   bool dump_passes = false;
+  bool print_pass_options = false;
   bool include_patch_information = CompilerOptions::kDefaultIncludePatchInformation;
   bool include_debug_symbols = kIsDebugBuild;
   bool dump_slow_timing = kIsDebugBuild;
@@ -1033,6 +1043,11 @@
     } else if (option.starts_with("--dump-cfg-passes=")) {
       std::string dump_passes = option.substr(strlen("--dump-cfg-passes=")).data();
       PassDriverMEOpts::SetDumpPassList(dump_passes);
+    } else if (option == "--print-pass-options") {
+      print_pass_options = true;
+    } else if (option.starts_with("--pass-options=")) {
+      std::string options = option.substr(strlen("--pass-options=")).data();
+      PassDriverMEOpts::SetOverriddenPassOptions(options);
     } else if (option == "--include-patch-information") {
       include_patch_information = true;
     } else if (option == "--no-include-patch-information") {
@@ -1179,6 +1194,10 @@
       break;
   }
 
+  if (print_pass_options) {
+    PassDriverMEOpts::PrintPassOptions();
+  }
+
   std::unique_ptr<CompilerOptions> compiler_options(new CompilerOptions(compiler_filter,
                                                                         huge_method_threshold,
                                                                         large_method_threshold,