ART: Add compiler option for code deduplication

Add --deduplicate-code and --no-deduplicate-code to ease in
experiments with deduplication, e.g., profiling.

Add dex2oat test.

Test: m test-art-host
Change-Id: Ib6c7fe082f43c5f76c8463cc563e2503c9a50480
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index b2d214e..7769aad 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -100,7 +100,7 @@
 	 $(HOST_OUT_EXECUTABLES)/smali assemble --output $@ $(filter %.smali,$^)
 
 # Dex file dependencies for each gtest.
-ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested VerifierDeps VerifierDepsMulti
+ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary MyClassNatives Nested VerifierDeps VerifierDepsMulti
 
 ART_GTEST_atomic_dex_ref_map_test_DEX_DEPS := Interfaces
 ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit ForClassLoaderA ForClassLoaderB ForClassLoaderC ForClassLoaderD Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index bd78af4..a9d27ef 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -320,6 +320,8 @@
   if (GetCompilerOptions().IsBootImage()) {
     CHECK(image_classes_.get() != nullptr) << "Expected image classes for boot image";
   }
+
+  compiled_method_storage_.SetDedupeEnabled(compiler_options_->DeduplicateCode());
 }
 
 CompilerDriver::~CompilerDriver() {
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index f789314..032763c 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -56,6 +56,7 @@
       dump_cfg_file_name_(""),
       dump_cfg_append_(false),
       force_determinism_(false),
+      deduplicate_code_(true),
       register_allocation_strategy_(RegisterAllocator::kRegisterAllocatorDefault),
       passes_to_run_(nullptr) {
 }
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 12de9be..ab2a681 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -254,6 +254,10 @@
     return force_determinism_;
   }
 
+  bool DeduplicateCode() const {
+    return deduplicate_code_;
+  }
+
   RegisterAllocator::Strategy GetRegisterAllocationStrategy() const {
     return register_allocation_strategy_;
   }
@@ -319,6 +323,9 @@
   // outcomes.
   bool force_determinism_;
 
+  // Whether code should be deduplicated.
+  bool deduplicate_code_;
+
   RegisterAllocator::Strategy register_allocation_strategy_;
 
   // If not null, specifies optimization passes which will be run instead of defaults.
diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h
index 772d1b4..e28d499 100644
--- a/compiler/driver/compiler_options_map-inl.h
+++ b/compiler/driver/compiler_options_map-inl.h
@@ -76,6 +76,7 @@
     }
   }
   map.AssignIfExists(Base::VerboseMethods, &options->verbose_methods_);
+  options->deduplicate_code_ = map.GetOrDefault(Base::DeduplicateCode);
 
   return true;
 }
@@ -123,6 +124,11 @@
           .WithValues({true, false})
           .IntoKey(Map::GenerateBuildID)
 
+      .Define({"--deduplicate-code=_"})
+          .template WithType<bool>()
+          .WithValueMap({{"false", false}, {"true", true}})
+          .IntoKey(Map::DeduplicateCode)
+
       .Define("--debuggable")
           .IntoKey(Map::Debuggable)
 
diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def
index cc75634..cccd618 100644
--- a/compiler/driver/compiler_options_map.def
+++ b/compiler/driver/compiler_options_map.def
@@ -57,5 +57,6 @@
 // TODO: Add type parser.
 COMPILER_OPTIONS_KEY (std::string,                 RegisterAllocationStrategy)
 COMPILER_OPTIONS_KEY (ParseStringList<','>,        VerboseMethods)
+COMPILER_OPTIONS_KEY (bool,                        DeduplicateCode,        true)
 
 #undef COMPILER_OPTIONS_KEY
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 46474d2..29228a3 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -452,6 +452,9 @@
   UsageError("  --compact-dex-level=none|fast: None avoids generating compact dex, fast");
   UsageError("      generates compact dex with low compile time.");
   UsageError("");
+  UsageError("  --deduplicate-code|--no-deduplicate-code: enable|disable code deduplication.");
+  UsageError("      Deduplicated code will have an arbitrary symbol tagged with [DEDUPED].");
+  UsageError("");
   std::cerr << "See log for usage error information\n";
   exit(EXIT_FAILURE);
 }
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index cb91978..99be111 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -1481,4 +1481,36 @@
   EXPECT_EQ(0, res_no_fail);
 }
 
+class Dex2oatDedupeCode : public Dex2oatTest {};
+
+TEST_F(Dex2oatDedupeCode, DedupeTest) {
+  // Use MyClassNatives. It has lots of native methods that will produce deduplicate-able code.
+  std::unique_ptr<const DexFile> dex(OpenTestDexFile("MyClassNatives"));
+  std::string out_dir = GetScratchDir();
+  const std::string base_oat_name = out_dir + "/base.oat";
+  size_t no_dedupe_size = 0;
+  GenerateOdexForTest(dex->GetLocation(),
+                      base_oat_name,
+                      CompilerFilter::Filter::kSpeed,
+                      { "--deduplicate-code=false" },
+                      true,  // expect_success
+                      false,  // use_fd
+                      [&no_dedupe_size](const OatFile& o) {
+                        no_dedupe_size = o.Size();
+                      });
+
+  size_t dedupe_size = 0;
+  GenerateOdexForTest(dex->GetLocation(),
+                      base_oat_name,
+                      CompilerFilter::Filter::kSpeed,
+                      { "--deduplicate-code=true" },
+                      true,  // expect_success
+                      false,  // use_fd
+                      [&dedupe_size](const OatFile& o) {
+                        dedupe_size = o.Size();
+                      });
+
+  EXPECT_LT(dedupe_size, no_dedupe_size);
+}
+
 }  // namespace art