ART: Use CmdlineParser in dex2oat

Refactor dex2oat and the compiler to use the cmdline parser. In
an effort to standardize command-line parsing and make it easier
to add new properties in a principled manner.

Test: m test-art-host
Change-Id: Ia077a56234dbf579d7ca3430fef325b57aa06333
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index 521156a..87bf1c4 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -290,26 +290,42 @@
   static const char* Name() { return "double"; }
 };
 
+template <typename T>
+static inline CmdlineParseResult<T> ParseNumeric(const std::string& str) {
+  static_assert(sizeof(T) < sizeof(long long int),  // NOLINT [runtime/int] [4]
+                "Current support is restricted.");
+
+  const char* begin = str.c_str();
+  char* end;
+
+  // Parse into a larger type (long long) because we can't use strtoul
+  // since it silently converts negative values into unsigned long and doesn't set errno.
+  errno = 0;
+  long long int result = strtoll(begin, &end, 10);  // NOLINT [runtime/int] [4]
+  if (begin == end || *end != '\0' || errno == EINVAL) {
+    return CmdlineParseResult<T>::Failure("Failed to parse integer from " + str);
+  } else if ((errno == ERANGE) ||  // NOLINT [runtime/int] [4]
+      result < std::numeric_limits<T>::min() || result > std::numeric_limits<T>::max()) {
+    return CmdlineParseResult<T>::OutOfRange(
+        "Failed to parse integer from " + str + "; out of range");
+  }
+
+  return CmdlineParseResult<T>::Success(static_cast<T>(result));
+}
+
 template <>
 struct CmdlineType<unsigned int> : CmdlineTypeParser<unsigned int> {
   Result Parse(const std::string& str) {
-    const char* begin = str.c_str();
-    char* end;
+    return ParseNumeric<unsigned int>(str);
+  }
 
-    // Parse into a larger type (long long) because we can't use strtoul
-    // since it silently converts negative values into unsigned long and doesn't set errno.
-    errno = 0;
-    long long int result = strtoll(begin, &end, 10);  // NOLINT [runtime/int] [4]
-    if (begin == end || *end != '\0' || errno == EINVAL) {
-      return Result::Failure("Failed to parse integer from " + str);
-    } else if ((errno == ERANGE) ||  // NOLINT [runtime/int] [4]
-        result < std::numeric_limits<int>::min()
-        || result > std::numeric_limits<unsigned int>::max() || result < 0) {
-      return Result::OutOfRange(
-          "Failed to parse integer from " + str + "; out of unsigned int range");
-    }
+  static const char* Name() { return "unsigned integer"; }
+};
 
-    return Result::Success(static_cast<unsigned int>(result));
+template <>
+struct CmdlineType<int> : CmdlineTypeParser<int> {
+  Result Parse(const std::string& str) {
+    return ParseNumeric<int>(str);
   }
 
   static const char* Name() { return "unsigned integer"; }
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 59ca4c7..1e4cdf2 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -193,6 +193,10 @@
         "liblzma",
     ],
     include_dirs: ["art/disassembler"],
+    header_libs: [
+        "art_cmdlineparser_headers",  // For compiler_options.
+    ],
+
     export_include_dirs: ["."],
 }
 
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 538845d..b6cedff 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -18,7 +18,13 @@
 
 #include <fstream>
 
+#include "android-base/stringprintf.h"
+
+#include "base/variant_map.h"
+#include "cmdline_parser.h"
+#include "compiler_options_map-inl.h"
 #include "runtime.h"
+#include "simple_compiler_options_map.h"
 
 namespace art {
 
@@ -71,115 +77,50 @@
       (kIsTargetBuild || IsCoreImage() || Runtime::Current()->UseJitCompilation());
 }
 
-void CompilerOptions::ParseHugeMethodMax(const StringPiece& option, UsageFn Usage) {
-  ParseUintOption(option, "--huge-method-max", &huge_method_threshold_, Usage);
-}
-
-void CompilerOptions::ParseLargeMethodMax(const StringPiece& option, UsageFn Usage) {
-  ParseUintOption(option, "--large-method-max", &large_method_threshold_, Usage);
-}
-
-void CompilerOptions::ParseSmallMethodMax(const StringPiece& option, UsageFn Usage) {
-  ParseUintOption(option, "--small-method-max", &small_method_threshold_, Usage);
-}
-
-void CompilerOptions::ParseTinyMethodMax(const StringPiece& option, UsageFn Usage) {
-  ParseUintOption(option, "--tiny-method-max", &tiny_method_threshold_, Usage);
-}
-
-void CompilerOptions::ParseNumDexMethods(const StringPiece& option, UsageFn Usage) {
-  ParseUintOption(option, "--num-dex-methods", &num_dex_methods_threshold_, Usage);
-}
-
-void CompilerOptions::ParseInlineMaxCodeUnits(const StringPiece& option, UsageFn Usage) {
-  ParseUintOption(option, "--inline-max-code-units", &inline_max_code_units_, Usage);
-}
-
-void CompilerOptions::ParseDumpInitFailures(const StringPiece& option,
-                                            UsageFn Usage ATTRIBUTE_UNUSED) {
-  DCHECK(option.starts_with("--dump-init-failures="));
-  std::string file_name = option.substr(strlen("--dump-init-failures=")).data();
-  init_failure_output_.reset(new std::ofstream(file_name));
+bool CompilerOptions::ParseDumpInitFailures(const std::string& option, std::string* error_msg) {
+  init_failure_output_.reset(new std::ofstream(option));
   if (init_failure_output_.get() == nullptr) {
-    LOG(ERROR) << "Failed to allocate ofstream";
+    *error_msg = "Failed to construct std::ofstream";
+    return false;
   } else if (init_failure_output_->fail()) {
-    LOG(ERROR) << "Failed to open " << file_name << " for writing the initialization "
-               << "failures.";
+    *error_msg = android::base::StringPrintf(
+        "Failed to open %s for writing the initialization failures.", option.c_str());
     init_failure_output_.reset();
-  }
-}
-
-void CompilerOptions::ParseRegisterAllocationStrategy(const StringPiece& option,
-                                                      UsageFn Usage) {
-  DCHECK(option.starts_with("--register-allocation-strategy="));
-  StringPiece choice = option.substr(strlen("--register-allocation-strategy=")).data();
-  if (choice == "linear-scan") {
-    register_allocation_strategy_ = RegisterAllocator::Strategy::kRegisterAllocatorLinearScan;
-  } else if (choice == "graph-color") {
-    register_allocation_strategy_ = RegisterAllocator::Strategy::kRegisterAllocatorGraphColor;
-  } else {
-    Usage("Unrecognized register allocation strategy. Try linear-scan, or graph-color.");
-  }
-}
-
-bool CompilerOptions::ParseCompilerOption(const StringPiece& option, UsageFn Usage) {
-  if (option.starts_with("--compiler-filter=")) {
-    const char* compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
-    if (!CompilerFilter::ParseCompilerFilter(compiler_filter_string, &compiler_filter_)) {
-      Usage("Unknown --compiler-filter value %s", compiler_filter_string);
-    }
-  } else if (option == "--compile-pic") {
-    compile_pic_ = true;
-  } else if (option.starts_with("--huge-method-max=")) {
-    ParseHugeMethodMax(option, Usage);
-  } else if (option.starts_with("--large-method-max=")) {
-    ParseLargeMethodMax(option, Usage);
-  } else if (option.starts_with("--small-method-max=")) {
-    ParseSmallMethodMax(option, Usage);
-  } else if (option.starts_with("--tiny-method-max=")) {
-    ParseTinyMethodMax(option, Usage);
-  } else if (option.starts_with("--num-dex-methods=")) {
-    ParseNumDexMethods(option, Usage);
-  } else if (option.starts_with("--inline-max-code-units=")) {
-    ParseInlineMaxCodeUnits(option, Usage);
-  } else if (option == "--generate-debug-info" || option == "-g") {
-    generate_debug_info_ = true;
-  } else if (option == "--no-generate-debug-info") {
-    generate_debug_info_ = false;
-  } else if (option == "--generate-mini-debug-info") {
-    generate_mini_debug_info_ = true;
-  } else if (option == "--no-generate-mini-debug-info") {
-    generate_mini_debug_info_ = false;
-  } else if (option == "--generate-build-id") {
-    generate_build_id_ = true;
-  } else if (option == "--no-generate-build-id") {
-    generate_build_id_ = false;
-  } else if (option == "--debuggable") {
-    debuggable_ = true;
-  } else if (option.starts_with("--top-k-profile-threshold=")) {
-    ParseDouble(option.data(), '=', 0.0, 100.0, &top_k_profile_threshold_, Usage);
-  } else if (option == "--abort-on-hard-verifier-error") {
-    abort_on_hard_verifier_failure_ = true;
-  } else if (option == "--no-abort-on-hard-verifier-error") {
-    abort_on_hard_verifier_failure_ = false;
-  } else if (option.starts_with("--dump-init-failures=")) {
-    ParseDumpInitFailures(option, Usage);
-  } else if (option.starts_with("--dump-cfg=")) {
-    dump_cfg_file_name_ = option.substr(strlen("--dump-cfg=")).data();
-  } else if (option == "--dump-cfg-append") {
-    dump_cfg_append_ = true;
-  } else if (option.starts_with("--register-allocation-strategy=")) {
-    ParseRegisterAllocationStrategy(option, Usage);
-  } else if (option.starts_with("--verbose-methods=")) {
-    // TODO: rather than switch off compiler logging, make all VLOG(compiler) messages
-    //       conditional on having verbose methods.
-    gLogVerbosity.compiler = false;
-    Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods_);
-  } else {
-    // Option not recognized.
     return false;
   }
   return true;
 }
 
+bool CompilerOptions::ParseRegisterAllocationStrategy(const std::string& option,
+                                                      std::string* error_msg) {
+  if (option == "linear-scan") {
+    register_allocation_strategy_ = RegisterAllocator::Strategy::kRegisterAllocatorLinearScan;
+  } else if (option == "graph-color") {
+    register_allocation_strategy_ = RegisterAllocator::Strategy::kRegisterAllocatorGraphColor;
+  } else {
+    *error_msg = "Unrecognized register allocation strategy. Try linear-scan, or graph-color.";
+    return false;
+  }
+  return true;
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+
+bool CompilerOptions::ParseCompilerOptions(const std::vector<std::string>& options,
+                                           bool ignore_unrecognized,
+                                           std::string* error_msg) {
+  auto parser = CreateSimpleParser(ignore_unrecognized);
+  CmdlineResult parse_result = parser.Parse(options);
+  if (!parse_result.IsSuccess()) {
+    *error_msg = parse_result.GetMessage();
+    return false;
+  }
+
+  SimpleParseArgumentMap args = parser.ReleaseArgumentsMap();
+  return ReadCompilerOptions(args, this, error_msg);
+}
+
+#pragma GCC diagnostic pop
+
 }  // namespace art
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index a9372c4..311dbd5 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -231,7 +231,9 @@
     return no_inline_from_;
   }
 
-  bool ParseCompilerOption(const StringPiece& option, UsageFn Usage);
+  bool ParseCompilerOptions(const std::vector<std::string>& options,
+                            bool ignore_unrecognized,
+                            std::string* error_msg);
 
   void SetNonPic() {
     compile_pic_ = false;
@@ -258,7 +260,7 @@
   }
 
  private:
-  void ParseDumpInitFailures(const StringPiece& option, UsageFn Usage);
+  bool ParseDumpInitFailures(const std::string& option, std::string* error_msg);
   void ParseDumpCfgPasses(const StringPiece& option, UsageFn Usage);
   void ParseInlineMaxCodeUnits(const StringPiece& option, UsageFn Usage);
   void ParseNumDexMethods(const StringPiece& option, UsageFn Usage);
@@ -266,7 +268,7 @@
   void ParseSmallMethodMax(const StringPiece& option, UsageFn Usage);
   void ParseLargeMethodMax(const StringPiece& option, UsageFn Usage);
   void ParseHugeMethodMax(const StringPiece& option, UsageFn Usage);
-  void ParseRegisterAllocationStrategy(const StringPiece& option, UsageFn Usage);
+  bool ParseRegisterAllocationStrategy(const std::string& option, std::string* error_msg);
 
   CompilerFilter::Filter compiler_filter_;
   size_t huge_method_threshold_;
@@ -327,6 +329,9 @@
   friend class CommonCompilerTest;
   friend class verifier::VerifierDepsTest;
 
+  template <class Base>
+  friend bool ReadCompilerOptions(Base& map, CompilerOptions* options, std::string* error_msg);
+
   DISALLOW_COPY_AND_ASSIGN(CompilerOptions);
 };
 
diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h
new file mode 100644
index 0000000..9cb818a
--- /dev/null
+++ b/compiler/driver/compiler_options_map-inl.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_INL_H_
+#define ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_INL_H_
+
+#include "compiler_options_map.h"
+
+#include <memory>
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
+
+#include "base/macros.h"
+#include "cmdline_parser.h"
+#include "compiler_options.h"
+
+namespace art {
+
+template <class Base>
+inline bool ReadCompilerOptions(Base& map, CompilerOptions* options, std::string* error_msg) {
+  if (map.Exists(Base::CompilerFilter)) {
+    CompilerFilter::Filter compiler_filter;
+    if (!CompilerFilter::ParseCompilerFilter(map.Get(Base::CompilerFilter)->c_str(),
+                                             &compiler_filter)) {
+      *error_msg = android::base::StringPrintf("Unknown --compiler-filter value %s",
+                                               map.Get(Base::CompilerFilter)->c_str());
+      return false;
+    }
+    options->SetCompilerFilter(compiler_filter);
+  }
+  if (map.Exists(Base::PIC)) {
+    options->compile_pic_ = true;
+  }
+  map.AssignIfExists(Base::HugeMethodMaxThreshold, &options->huge_method_threshold_);
+  map.AssignIfExists(Base::LargeMethodMaxThreshold, &options->large_method_threshold_);
+  map.AssignIfExists(Base::SmallMethodMaxThreshold, &options->small_method_threshold_);
+  map.AssignIfExists(Base::TinyMethodMaxThreshold, &options->tiny_method_threshold_);
+  map.AssignIfExists(Base::NumDexMethodsThreshold, &options->num_dex_methods_threshold_);
+  map.AssignIfExists(Base::InlineMaxCodeUnitsThreshold, &options->inline_max_code_units_);
+  map.AssignIfExists(Base::GenerateDebugInfo, &options->generate_debug_info_);
+  map.AssignIfExists(Base::GenerateMiniDebugInfo, &options->generate_mini_debug_info_);
+  map.AssignIfExists(Base::GenerateBuildID, &options->generate_build_id_);
+  if (map.Exists(Base::Debuggable)) {
+    options->debuggable_ = true;
+  }
+  map.AssignIfExists(Base::TopKProfileThreshold, &options->top_k_profile_threshold_);
+  map.AssignIfExists(Base::AbortOnHardVerifierFailure, &options->abort_on_hard_verifier_failure_);
+  if (map.Exists(Base::DumpInitFailures)) {
+    if (!options->ParseDumpInitFailures(*map.Get(Base::DumpInitFailures), error_msg)) {
+      return false;
+    }
+  }
+  map.AssignIfExists(Base::DumpCFG, &options->dump_cfg_file_name_);
+  if (map.Exists(Base::DumpCFGAppend)) {
+    options->dump_cfg_append_ = true;
+  }
+  if (map.Exists(Base::RegisterAllocationStrategy)) {
+    if (!options->ParseRegisterAllocationStrategy(*map.Get(Base::DumpInitFailures), error_msg)) {
+      return false;
+    }
+  }
+  map.AssignIfExists(Base::VerboseMethods, &options->verbose_methods_);
+
+  return true;
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+
+template <typename Map, typename Builder>
+inline void AddCompilerOptionsArgumentParserOptions(Builder& b) {
+  b.
+      Define("--compiler-filter=_")
+          .template WithType<std::string>()
+          .IntoKey(Map::CompilerFilter)
+
+      .Define("--compile-pic")
+          .IntoKey(Map::PIC)
+
+      .Define("--huge-method-max=_")
+          .template WithType<unsigned int>()
+          .IntoKey(Map::HugeMethodMaxThreshold)
+      .Define("--large-method-max=_")
+          .template WithType<unsigned int>()
+          .IntoKey(Map::LargeMethodMaxThreshold)
+      .Define("--small-method-max=_")
+          .template WithType<unsigned int>()
+          .IntoKey(Map::SmallMethodMaxThreshold)
+      .Define("--tiny-method-max=_")
+          .template WithType<unsigned int>()
+          .IntoKey(Map::TinyMethodMaxThreshold)
+      .Define("--num-dex-methods=_")
+          .template WithType<unsigned int>()
+          .IntoKey(Map::NumDexMethodsThreshold)
+      .Define("--inline-max-code-units=_")
+          .template WithType<unsigned int>()
+          .IntoKey(Map::InlineMaxCodeUnitsThreshold)
+
+      .Define({"--generate-debug-info", "-g", "--no-generate-debug-info"})
+          .WithValues({true, true, false})
+          .IntoKey(Map::GenerateDebugInfo)
+      .Define({"--generate-mini-debug-info", "--no-generate-mini-debug-info"})
+          .WithValues({true, false})
+          .IntoKey(Map::GenerateMiniDebugInfo)
+
+      .Define({"--generate-build-id", "--no-generate-build-id"})
+          .WithValues({true, false})
+          .IntoKey(Map::GenerateBuildID)
+
+      .Define("--debuggable")
+          .IntoKey(Map::Debuggable)
+
+      .Define("--top-k-profile-threshold=_")
+          .template WithType<double>().WithRange(0.0, 100.0)
+          .IntoKey(Map::TopKProfileThreshold)
+
+      .Define({"--abort-on-hard-verifier-error", "--no-abort-on-hard-verifier-error"})
+          .WithValues({true, false})
+          .IntoKey(Map::AbortOnHardVerifierFailure)
+
+      .Define("--dump-init-failures=_")
+          .template WithType<std::string>()
+          .IntoKey(Map::DumpInitFailures)
+
+      .Define("--dump-cfg=_")
+          .template WithType<std::string>()
+          .IntoKey(Map::DumpCFG)
+      .Define("--dump-cfg-append")
+          .IntoKey(Map::DumpCFGAppend)
+
+      .Define("--register-allocation-strategy=_")
+          .template WithType<std::string>()
+          .IntoKey(Map::RegisterAllocationStrategy)
+
+      .Define("--verbose-methods=_")
+          .template WithType<ParseStringList<','>>()
+          .IntoKey(Map::VerboseMethods);
+}
+
+#pragma GCC diagnostic pop
+
+}  // namespace art
+
+#endif  // ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_INL_H_
diff --git a/compiler/driver/compiler_options_map-storage.h b/compiler/driver/compiler_options_map-storage.h
new file mode 100644
index 0000000..756598d
--- /dev/null
+++ b/compiler/driver/compiler_options_map-storage.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_STORAGE_H_
+#define ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_STORAGE_H_
+
+// Assumes:
+// * #include "compiler_options_map.h"
+// * namespace art
+//
+// Usage:
+// #define COMPILER_OPTIONS_MAP_TYPE TheTypeOfTheMap
+// #define COMPILER_OPTIONS_MAP_KEY_TYPE TheTypeOfTheMapsKey
+// #include "driver/compiler_options_map-storage.h
+
+#ifndef COMPILER_OPTIONS_MAP_TYPE
+#error "Expected COMPILER_OPTIONS_MAP_TYPE"
+#endif
+
+#ifndef COMPILER_OPTIONS_MAP_KEY_TYPE
+#error "Expected COMPILER_OPTIONS_MAP_KEY_TYPE"
+#endif
+
+#define COMPILER_OPTIONS_KEY(Type, Name, ...) \
+  template <typename Base, template <typename TV> class KeyType> \
+  const KeyType<Type> CompilerOptionsMap<Base, KeyType>::Name {__VA_ARGS__};  // NOLINT [readability/braces] [4]
+#include <driver/compiler_options_map.def>
+
+template struct CompilerOptionsMap<COMPILER_OPTIONS_MAP_TYPE, COMPILER_OPTIONS_MAP_KEY_TYPE>;
+
+#undef COMPILER_OPTIONS_MAP_TYPE
+#undef COMPILER_OPTIONS_MAP_KEY_TYPE
+
+#endif  // ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_STORAGE_H_
+#undef ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_STORAGE_H_  // Guard is only for cpplint
diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def
new file mode 100644
index 0000000..570bc5a
--- /dev/null
+++ b/compiler/driver/compiler_options_map.def
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COMPILER_OPTIONS_KEY
+#error "Please #define COMPILER_OPTIONS_KEY before #including this file"
+#define COMPILER_OPTIONS_KEY(...)  // Don't display errors in this file in IDEs.
+#endif
+
+// This file defines the list of keys for CompilerOptionsMap.
+// These can be used with CompilerOptionsMap.Get/Set/etc, once that template class has been
+// instantiated.
+//
+// Column Descriptions:
+//                    <<Type>>                     <<Key Name>>                  (<<Default Value>>)
+//
+// Default values are only used by Map::GetOrDefault(K<T>).
+// If a default value is omitted here, T{} is used as the default value, which is
+// almost-always the value of the type as if it was memset to all 0.
+//
+// Please keep the columns aligned if possible when adding new rows.
+//
+
+// Parse-able keys from the command line.
+
+// TODO: Add type parser.
+COMPILER_OPTIONS_KEY (std::string,                 CompilerFilter)
+COMPILER_OPTIONS_KEY (Unit,                        PIC)
+COMPILER_OPTIONS_KEY (unsigned int,                HugeMethodMaxThreshold)
+COMPILER_OPTIONS_KEY (unsigned int,                LargeMethodMaxThreshold)
+COMPILER_OPTIONS_KEY (unsigned int,                SmallMethodMaxThreshold)
+COMPILER_OPTIONS_KEY (unsigned int,                TinyMethodMaxThreshold)
+COMPILER_OPTIONS_KEY (unsigned int,                NumDexMethodsThreshold)
+COMPILER_OPTIONS_KEY (unsigned int,                InlineMaxCodeUnitsThreshold)
+COMPILER_OPTIONS_KEY (bool,                        GenerateDebugInfo)
+COMPILER_OPTIONS_KEY (bool,                        GenerateMiniDebugInfo)
+COMPILER_OPTIONS_KEY (bool,                        GenerateBuildID)
+COMPILER_OPTIONS_KEY (Unit,                        Debuggable)
+COMPILER_OPTIONS_KEY (double,                      TopKProfileThreshold)
+COMPILER_OPTIONS_KEY (bool,                        AbortOnHardVerifierFailure)
+COMPILER_OPTIONS_KEY (std::string,                 DumpInitFailures)
+COMPILER_OPTIONS_KEY (std::string,                 DumpCFG)
+COMPILER_OPTIONS_KEY (Unit,                        DumpCFGAppend)
+// TODO: Add type parser.
+COMPILER_OPTIONS_KEY (std::string,                 RegisterAllocationStrategy)
+COMPILER_OPTIONS_KEY (ParseStringList<','>,        VerboseMethods)
+
+#undef COMPILER_OPTIONS_KEY
diff --git a/compiler/driver/compiler_options_map.h b/compiler/driver/compiler_options_map.h
new file mode 100644
index 0000000..b9bc8b6
--- /dev/null
+++ b/compiler/driver/compiler_options_map.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_H_
+#define ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_H_
+
+#include <string>
+#include <vector>
+
+#include "base/variant_map.h"
+#include "cmdline_types.h"
+
+namespace art {
+
+// Defines a type-safe heterogeneous key->value map. This is to be used as the base for
+// an extended map.
+template <typename Base, template <typename TV> class KeyType>
+struct CompilerOptionsMap : VariantMap<Base, KeyType> {
+  // Make the next many usages of Key slightly shorter to type.
+  template <typename TValue>
+  using Key = KeyType<TValue>;
+
+  // List of key declarations, shorthand for 'static const Key<T> Name'
+#define COMPILER_OPTIONS_KEY(Type, Name, ...) static const Key<Type> (Name);
+#include "compiler_options_map.def"
+};
+
+#undef DECLARE_KEY
+
+}  // namespace art
+
+#endif  // ART_COMPILER_DRIVER_COMPILER_OPTIONS_MAP_H_
diff --git a/compiler/driver/simple_compiler_options_map.h b/compiler/driver/simple_compiler_options_map.h
new file mode 100644
index 0000000..3860da9
--- /dev/null
+++ b/compiler/driver/simple_compiler_options_map.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This file declares a completion of the CompilerOptionsMap and should be included into a
+// .cc file, only.
+
+#ifndef ART_COMPILER_DRIVER_SIMPLE_COMPILER_OPTIONS_MAP_H_
+#define ART_COMPILER_DRIVER_SIMPLE_COMPILER_OPTIONS_MAP_H_
+
+#include <memory>
+
+#include "compiler_options_map-inl.h"
+#include "base/variant_map.h"
+
+namespace art {
+
+template <typename TValue>
+struct SimpleParseArgumentMapKey : VariantMapKey<TValue> {
+  SimpleParseArgumentMapKey() {}
+  explicit SimpleParseArgumentMapKey(TValue default_value)
+      : VariantMapKey<TValue>(std::move(default_value)) {}
+  // Don't ODR-use constexpr default values, which means that Struct::Fields
+  // that are declared 'static constexpr T Name = Value' don't need to have a matching definition.
+};
+
+struct SimpleParseArgumentMap : CompilerOptionsMap<SimpleParseArgumentMap,
+                                                   SimpleParseArgumentMapKey> {
+  // This 'using' line is necessary to inherit the variadic constructor.
+  using CompilerOptionsMap<SimpleParseArgumentMap, SimpleParseArgumentMapKey>::CompilerOptionsMap;
+};
+
+#define COMPILER_OPTIONS_MAP_TYPE SimpleParseArgumentMap
+#define COMPILER_OPTIONS_MAP_KEY_TYPE SimpleParseArgumentMapKey
+#include "compiler_options_map-storage.h"
+
+using Parser = CmdlineParser<SimpleParseArgumentMap, SimpleParseArgumentMapKey>;
+
+static inline Parser CreateSimpleParser(bool ignore_unrecognized) {
+  std::unique_ptr<Parser::Builder> parser_builder =
+      std::unique_ptr<Parser::Builder>(new Parser::Builder());
+
+  AddCompilerOptionsArgumentParserOptions<SimpleParseArgumentMap>(*parser_builder);
+
+  parser_builder->IgnoreUnrecognized(ignore_unrecognized);
+
+  return parser_builder->Build();
+}
+
+}  // namespace art
+
+#endif  // ART_COMPILER_DRIVER_SIMPLE_COMPILER_OPTIONS_MAP_H_
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 511a44a..5c89869 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -78,21 +78,16 @@
   }
 }
 
-// Callers of this method assume it has NO_RETURN.
-NO_RETURN static void Usage(const char* fmt, ...) {
-  va_list ap;
-  va_start(ap, fmt);
-  std::string error;
-  android::base::StringAppendV(&error, fmt, ap);
-  LOG(FATAL) << error;
-  va_end(ap);
-  exit(EXIT_FAILURE);
-}
-
 JitCompiler::JitCompiler() {
   compiler_options_.reset(new CompilerOptions());
-  for (const std::string& argument : Runtime::Current()->GetCompilerOptions()) {
-    compiler_options_->ParseCompilerOption(argument, Usage);
+  {
+    std::string error_msg;
+    if (!compiler_options_->ParseCompilerOptions(Runtime::Current()->GetCompilerOptions(),
+                                                 true /* ignore_unrecognized */,
+                                                 &error_msg)) {
+      LOG(FATAL) << error_msg;
+      UNREACHABLE();
+    }
   }
   // JIT is never PIC, no matter what the runtime compiler options specify.
   compiler_options_->SetNonPic();
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index c9125df..a93b0e7 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -91,7 +91,10 @@
     name: "dex2oat-defaults",
     host_supported: true,
     defaults: ["art_defaults"],
-    srcs: ["dex2oat.cc"],
+    srcs: [
+        "dex2oat_options.cc",
+        "dex2oat.cc",
+    ],
 
     target: {
         android: {
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 7b46531..45d0f17 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -20,11 +20,13 @@
 #include <sys/stat.h>
 #include "base/memory_tool.h"
 
+#include <forward_list>
 #include <fstream>
 #include <iostream>
 #include <limits>
 #include <sstream>
 #include <string>
+#include <type_traits>
 #include <unordered_set>
 #include <vector>
 
@@ -50,16 +52,19 @@
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
 #include "class_loader_context.h"
+#include "cmdline_parser.h"
 #include "compiler.h"
 #include "compiler_callbacks.h"
 #include "debug/elf_debug_writer.h"
 #include "debug/method_debug_info.h"
 #include "dex/quick_compiler_callbacks.h"
 #include "dex/verification_results.h"
+#include "dex2oat_options.h"
 #include "dex2oat_return_codes.h"
 #include "dex_file-inl.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
+#include "driver/compiler_options_map-inl.h"
 #include "elf_file.h"
 #include "gc/space/image_space.h"
 #include "gc/space/space-inl.h"
@@ -659,76 +664,27 @@
     std::string error_msg;
   };
 
-  void ParseZipFd(const StringPiece& option) {
-    ParseUintOption(option, "--zip-fd", &zip_fd_, Usage);
-  }
-
-  void ParseInputVdexFd(const StringPiece& option) {
-    // Note that the input vdex fd might be -1.
-    ParseIntOption(option, "--input-vdex-fd", &input_vdex_fd_, Usage);
-  }
-
-  void ParseOutputVdexFd(const StringPiece& option) {
-    ParseUintOption(option, "--output-vdex-fd", &output_vdex_fd_, Usage);
-  }
-
-  void ParseOatFd(const StringPiece& option) {
-    ParseUintOption(option, "--oat-fd", &oat_fd_, Usage);
-  }
-
-  void ParseFdForCollection(const StringPiece& option,
-                            const char* arg_name,
-                            std::vector<uint32_t>* fds) {
-    uint32_t fd;
-    ParseUintOption(option, arg_name, &fd, Usage);
-    fds->push_back(fd);
-  }
-
-  void ParseJ(const StringPiece& option) {
-    ParseUintOption(option, "-j", &thread_count_, Usage, /* is_long_option */ false);
-  }
-
-  void ParseBase(const StringPiece& option) {
-    DCHECK(option.starts_with("--base="));
-    const char* image_base_str = option.substr(strlen("--base=")).data();
+  void ParseBase(const std::string& option) {
     char* end;
-    image_base_ = strtoul(image_base_str, &end, 16);
-    if (end == image_base_str || *end != '\0') {
+    image_base_ = strtoul(option.c_str(), &end, 16);
+    if (end == option.c_str() || *end != '\0') {
       Usage("Failed to parse hexadecimal value for option %s", option.data());
     }
   }
 
-  void ParseInstructionSet(const StringPiece& option) {
-    DCHECK(option.starts_with("--instruction-set="));
-    StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
-    // StringPiece is not necessarily zero-terminated, so need to make a copy and ensure it.
-    std::unique_ptr<char[]> buf(new char[instruction_set_str.length() + 1]);
-    strncpy(buf.get(), instruction_set_str.data(), instruction_set_str.length());
-    buf.get()[instruction_set_str.length()] = 0;
-    instruction_set_ = GetInstructionSetFromString(buf.get());
-    // arm actually means thumb2.
-    if (instruction_set_ == InstructionSet::kArm) {
-      instruction_set_ = InstructionSet::kThumb2;
-    }
-  }
-
   bool VerifyProfileData() {
     return profile_compilation_info_->VerifyProfileData(dex_files_);
   }
 
-  void ParseInstructionSetVariant(const StringPiece& option, ParserOptions* parser_options) {
-    DCHECK(option.starts_with("--instruction-set-variant="));
-    StringPiece str = option.substr(strlen("--instruction-set-variant=")).data();
+  void ParseInstructionSetVariant(const std::string& option, ParserOptions* parser_options) {
     instruction_set_features_ = InstructionSetFeatures::FromVariant(
-        instruction_set_, str.as_string(), &parser_options->error_msg);
+        instruction_set_, option, &parser_options->error_msg);
     if (instruction_set_features_.get() == nullptr) {
       Usage("%s", parser_options->error_msg.c_str());
     }
   }
 
-  void ParseInstructionSetFeatures(const StringPiece& option, ParserOptions* parser_options) {
-    DCHECK(option.starts_with("--instruction-set-features="));
-    StringPiece str = option.substr(strlen("--instruction-set-features=")).data();
+  void ParseInstructionSetFeatures(const std::string& option, ParserOptions* parser_options) {
     if (instruction_set_features_ == nullptr) {
       instruction_set_features_ = InstructionSetFeatures::FromVariant(
           instruction_set_, "default", &parser_options->error_msg);
@@ -738,38 +694,9 @@
       }
     }
     instruction_set_features_ =
-        instruction_set_features_->AddFeaturesFromString(str.as_string(),
-                                                         &parser_options->error_msg);
+        instruction_set_features_->AddFeaturesFromString(option, &parser_options->error_msg);
     if (instruction_set_features_ == nullptr) {
-      Usage("Error parsing '%s': %s", option.data(), parser_options->error_msg.c_str());
-    }
-  }
-
-  void ParseCompilerBackend(const StringPiece& option, ParserOptions* parser_options) {
-    DCHECK(option.starts_with("--compiler-backend="));
-    parser_options->requested_specific_compiler = true;
-    StringPiece backend_str = option.substr(strlen("--compiler-backend=")).data();
-    if (backend_str == "Quick") {
-      compiler_kind_ = Compiler::kQuick;
-    } else if (backend_str == "Optimizing") {
-      compiler_kind_ = Compiler::kOptimizing;
-    } else {
-      Usage("Unknown compiler backend: %s", backend_str.data());
-    }
-  }
-
-  void ParseImageFormat(const StringPiece& option) {
-    const StringPiece substr("--image-format=");
-    DCHECK(option.starts_with(substr));
-    const StringPiece format_str = option.substr(substr.length());
-    if (format_str == "lz4") {
-      image_storage_mode_ = ImageHeader::kStorageModeLZ4;
-    } else if (format_str == "lz4hc") {
-      image_storage_mode_ = ImageHeader::kStorageModeLZ4HC;
-    } else if (format_str == "uncompressed") {
-      image_storage_mode_ = ImageHeader::kStorageModeUncompressed;
-    } else {
-      Usage("Unknown image format: %s", format_str.data());
+      Usage("Error parsing '%s': %s", option.c_str(), parser_options->error_msg.c_str());
     }
   }
 
@@ -1092,23 +1019,20 @@
       base_symbol_oat = base_symbol_oat.substr(0, last_symbol_oat_slash + 1);
     }
 
-    const size_t num_expanded_files = 2 + (base_symbol_oat.empty() ? 0 : 1);
-    char_backing_storage_.reserve((dex_locations_.size() - 1) * num_expanded_files);
-
     // Now create the other names. Use a counted loop to skip the first one.
     for (size_t i = 1; i < dex_locations_.size(); ++i) {
       // TODO: Make everything properly std::string.
       std::string image_name = CreateMultiImageName(dex_locations_[i], prefix, infix, ".art");
-      char_backing_storage_.push_back(base_img + image_name);
-      image_filenames_.push_back((char_backing_storage_.end() - 1)->c_str());
+      char_backing_storage_.push_front(base_img + image_name);
+      image_filenames_.push_back(char_backing_storage_.front().c_str());
 
       std::string oat_name = CreateMultiImageName(dex_locations_[i], prefix, infix, ".oat");
-      char_backing_storage_.push_back(base_oat + oat_name);
-      oat_filenames_.push_back((char_backing_storage_.end() - 1)->c_str());
+      char_backing_storage_.push_front(base_oat + oat_name);
+      oat_filenames_.push_back(char_backing_storage_.front().c_str());
 
       if (!base_symbol_oat.empty()) {
-        char_backing_storage_.push_back(base_symbol_oat + oat_name);
-        oat_unstripped_.push_back((char_backing_storage_.end() - 1)->c_str());
+        char_backing_storage_.push_front(base_symbol_oat + oat_name);
+        oat_unstripped_.push_back(char_backing_storage_.front().c_str());
       }
     }
   }
@@ -1173,6 +1097,43 @@
                           kUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue);
   }
 
+  // This simple forward is here so the string specializations below don't look out of place.
+  template <typename T, typename U>
+  void AssignIfExists(Dex2oatArgumentMap& map,
+                      const Dex2oatArgumentMap::Key<T>& key,
+                      U* out) {
+    map.AssignIfExists(key, out);
+  }
+
+  // Specializations to handle const char* vs std::string.
+  void AssignIfExists(Dex2oatArgumentMap& map,
+                      const Dex2oatArgumentMap::Key<std::string>& key,
+                      const char** out) {
+    if (map.Exists(key)) {
+      char_backing_storage_.push_front(std::move(*map.Get(key)));
+      *out = char_backing_storage_.front().c_str();
+    }
+  }
+  void AssignIfExists(Dex2oatArgumentMap& map,
+                      const Dex2oatArgumentMap::Key<std::vector<std::string>>& key,
+                      std::vector<const char*>* out) {
+    if (map.Exists(key)) {
+      for (auto& val : *map.Get(key)) {
+        char_backing_storage_.push_front(std::move(val));
+        out->push_back(char_backing_storage_.front().c_str());
+      }
+    }
+  }
+
+  template <typename T>
+  void AssignTrueIfExists(Dex2oatArgumentMap& map,
+                          const Dex2oatArgumentMap::Key<T>& key,
+                          bool* out) {
+    if (map.Exists(key)) {
+      *out = true;
+    }
+  }
+
   // Parse the arguments from the command line. In case of an unrecognized option or impossible
   // values/combinations, a usage error will be displayed and exit() is called. Thus, if the method
   // returns, arguments have been successfully parsed.
@@ -1182,159 +1143,104 @@
 
     InitLogging(argv, Runtime::Abort);
 
-    // Skip over argv[0].
-    argv++;
-    argc--;
-
-    if (argc == 0) {
-      Usage("No arguments specified");
-    }
-
-    std::unique_ptr<ParserOptions> parser_options(new ParserOptions());
     compiler_options_.reset(new CompilerOptions());
 
-    for (int i = 0; i < argc; i++) {
-      const StringPiece option(argv[i]);
-      const bool log_options = false;
-      if (log_options) {
-        LOG(INFO) << "dex2oat: option[" << i << "]=" << argv[i];
+    using M = Dex2oatArgumentMap;
+    std::string error_msg;
+    std::unique_ptr<M> args_uptr = M::Parse(argc, const_cast<const char**>(argv), &error_msg);
+    if (args_uptr == nullptr) {
+      Usage("Failed to parse command line: %s", error_msg.c_str());
+      UNREACHABLE();
+    }
+
+    M& args = *args_uptr;
+
+    std::unique_ptr<ParserOptions> parser_options(new ParserOptions());
+
+    AssignIfExists(args, M::DexFiles, &dex_filenames_);
+    AssignIfExists(args, M::DexLocations, &dex_locations_);
+    AssignIfExists(args, M::OatFiles, &oat_filenames_);
+    AssignIfExists(args, M::OatSymbols, &parser_options->oat_symbols);
+    AssignIfExists(args, M::ImageFilenames, &image_filenames_);
+    AssignIfExists(args, M::ZipFd, &zip_fd_);
+    AssignIfExists(args, M::ZipLocation, &zip_location_);
+    AssignIfExists(args, M::InputVdexFd, &input_vdex_fd_);
+    AssignIfExists(args, M::OutputVdexFd, &output_vdex_fd_);
+    AssignIfExists(args, M::InputVdex, &input_vdex_);
+    AssignIfExists(args, M::OutputVdex, &output_vdex_);
+    AssignIfExists(args, M::OatFd, &oat_fd_);
+    AssignIfExists(args, M::OatLocation, &oat_location_);
+    AssignIfExists(args, M::Watchdog, &parser_options->watch_dog_enabled);
+    AssignIfExists(args, M::WatchdogTimeout, &parser_options->watch_dog_timeout_in_ms);
+    AssignIfExists(args, M::Threads, &thread_count_);
+    AssignIfExists(args, M::ImageClasses, &image_classes_filename_);
+    AssignIfExists(args, M::ImageClassesZip, &image_classes_zip_filename_);
+    AssignIfExists(args, M::CompiledClasses, &compiled_classes_filename_);
+    AssignIfExists(args, M::CompiledClassesZip, &compiled_classes_zip_filename_);
+    AssignIfExists(args, M::CompiledMethods, &compiled_methods_filename_);
+    AssignIfExists(args, M::CompiledMethodsZip, &compiled_methods_zip_filename_);
+    AssignIfExists(args, M::Passes, &passes_to_run_filename_);
+    AssignIfExists(args, M::BootImage, &parser_options->boot_image_filename);
+    AssignIfExists(args, M::AndroidRoot, &android_root_);
+    AssignIfExists(args, M::Profile, &profile_file_);
+    AssignIfExists(args, M::ProfileFd, &profile_file_fd_);
+    AssignIfExists(args, M::RuntimeOptions, &runtime_args_);
+    AssignIfExists(args, M::SwapFile, &swap_file_name_);
+    AssignIfExists(args, M::SwapFileFd, &swap_fd_);
+    AssignIfExists(args, M::SwapDexSizeThreshold, &min_dex_file_cumulative_size_for_swap_);
+    AssignIfExists(args, M::SwapDexCountThreshold, &min_dex_files_for_swap_);
+    AssignIfExists(args, M::VeryLargeAppThreshold, &very_large_threshold_);
+    AssignIfExists(args, M::AppImageFile, &app_image_file_name_);
+    AssignIfExists(args, M::AppImageFileFd, &app_image_fd_);
+    AssignIfExists(args, M::NoInlineFrom, &no_inline_from_string_);
+    AssignIfExists(args, M::ClasspathDir, &classpath_dir_);
+    AssignIfExists(args, M::DirtyImageObjects, &dirty_image_objects_filename_);
+    AssignIfExists(args, M::ImageFormat, &image_storage_mode_);
+
+    AssignIfExists(args, M::Backend, &compiler_kind_);
+    parser_options->requested_specific_compiler = args.Exists(M::Backend);
+
+    AssignIfExists(args, M::TargetInstructionSet, &instruction_set_);
+    // arm actually means thumb2.
+    if (instruction_set_ == InstructionSet::kArm) {
+      instruction_set_ = InstructionSet::kThumb2;
+    }
+
+    AssignTrueIfExists(args, M::Host, &is_host_);
+    AssignTrueIfExists(args, M::DumpTiming, &dump_timing_);
+    AssignTrueIfExists(args, M::DumpPasses, &dump_passes_);
+    AssignTrueIfExists(args, M::DumpStats, &dump_stats_);
+    AssignTrueIfExists(args, M::AvoidStoringInvocation, &avoid_storing_invocation_);
+    AssignTrueIfExists(args, M::MultiImage, &multi_image_);
+
+    if (args.Exists(M::ForceDeterminism)) {
+      if (!SupportsDeterministicCompilation()) {
+        Usage("Option --force-determinism requires read barriers or a CMS/MS garbage collector");
       }
-      if (option.starts_with("--dex-file=")) {
-        dex_filenames_.push_back(option.substr(strlen("--dex-file=")).data());
-      } else if (option.starts_with("--dex-location=")) {
-        dex_locations_.push_back(option.substr(strlen("--dex-location=")).data());
-      } else if (option.starts_with("--zip-fd=")) {
-        ParseZipFd(option);
-      } else if (option.starts_with("--zip-location=")) {
-        zip_location_ = option.substr(strlen("--zip-location=")).data();
-      } else if (option.starts_with("--input-vdex-fd=")) {
-        ParseInputVdexFd(option);
-      } else if (option.starts_with("--input-vdex=")) {
-        input_vdex_ = option.substr(strlen("--input-vdex=")).data();
-      } else if (option.starts_with("--output-vdex=")) {
-        output_vdex_ = option.substr(strlen("--output-vdex=")).data();
-      } else if (option.starts_with("--output-vdex-fd=")) {
-        ParseOutputVdexFd(option);
-      } else if (option.starts_with("--oat-file=")) {
-        oat_filenames_.push_back(option.substr(strlen("--oat-file=")).data());
-      } else if (option.starts_with("--oat-symbols=")) {
-        parser_options->oat_symbols.push_back(option.substr(strlen("--oat-symbols=")).data());
-      } else if (option.starts_with("--oat-fd=")) {
-        ParseOatFd(option);
-      } else if (option.starts_with("--oat-location=")) {
-        oat_location_ = option.substr(strlen("--oat-location=")).data();
-      } else if (option == "--watch-dog") {
-        parser_options->watch_dog_enabled = true;
-      } else if (option == "--no-watch-dog") {
-        parser_options->watch_dog_enabled = false;
-      } else if (option.starts_with("--watchdog-timeout=")) {
-        ParseIntOption(option,
-                       "--watchdog-timeout",
-                       &parser_options->watch_dog_timeout_in_ms,
-                       Usage);
-      } else if (option.starts_with("-j")) {
-        ParseJ(option);
-      } else if (option.starts_with("--image=")) {
-        image_filenames_.push_back(option.substr(strlen("--image=")).data());
-      } else if (option.starts_with("--image-classes=")) {
-        image_classes_filename_ = option.substr(strlen("--image-classes=")).data();
-      } else if (option.starts_with("--image-classes-zip=")) {
-        image_classes_zip_filename_ = option.substr(strlen("--image-classes-zip=")).data();
-      } else if (option.starts_with("--image-format=")) {
-        ParseImageFormat(option);
-      } else if (option.starts_with("--compiled-classes=")) {
-        compiled_classes_filename_ = option.substr(strlen("--compiled-classes=")).data();
-      } else if (option.starts_with("--compiled-classes-zip=")) {
-        compiled_classes_zip_filename_ = option.substr(strlen("--compiled-classes-zip=")).data();
-      } else if (option.starts_with("--compiled-methods=")) {
-        compiled_methods_filename_ = option.substr(strlen("--compiled-methods=")).data();
-      } else if (option.starts_with("--compiled-methods-zip=")) {
-        compiled_methods_zip_filename_ = option.substr(strlen("--compiled-methods-zip=")).data();
-      } else if (option.starts_with("--run-passes=")) {
-        passes_to_run_filename_ = option.substr(strlen("--run-passes=")).data();
-      } else if (option.starts_with("--base=")) {
-        ParseBase(option);
-      } else if (option.starts_with("--boot-image=")) {
-        parser_options->boot_image_filename = option.substr(strlen("--boot-image=")).data();
-      } else if (option.starts_with("--android-root=")) {
-        android_root_ = option.substr(strlen("--android-root=")).data();
-      } else if (option.starts_with("--instruction-set=")) {
-        ParseInstructionSet(option);
-      } else if (option.starts_with("--instruction-set-variant=")) {
-        ParseInstructionSetVariant(option, parser_options.get());
-      } else if (option.starts_with("--instruction-set-features=")) {
-        ParseInstructionSetFeatures(option, parser_options.get());
-      } else if (option.starts_with("--compiler-backend=")) {
-        ParseCompilerBackend(option, parser_options.get());
-      } else if (option.starts_with("--profile-file=")) {
-        profile_file_ = option.substr(strlen("--profile-file=")).ToString();
-      } else if (option.starts_with("--profile-file-fd=")) {
-        ParseUintOption(option, "--profile-file-fd", &profile_file_fd_, Usage);
-      } else if (option == "--host") {
-        is_host_ = true;
-      } else if (option == "--runtime-arg") {
-        if (++i >= argc) {
-          Usage("Missing required argument for --runtime-arg");
-        }
-        if (log_options) {
-          LOG(INFO) << "dex2oat: option[" << i << "]=" << argv[i];
-        }
-        runtime_args_.push_back(argv[i]);
-      } else if (option == "--dump-timing") {
-        dump_timing_ = true;
-      } else if (option == "--dump-passes") {
-        dump_passes_ = true;
-      } else if (option == "--dump-stats") {
-        dump_stats_ = true;
-      } else if (option == "--avoid-storing-invocation") {
-        avoid_storing_invocation_ = true;
-      } else if (option.starts_with("--swap-file=")) {
-        swap_file_name_ = option.substr(strlen("--swap-file=")).data();
-      } else if (option.starts_with("--swap-fd=")) {
-        ParseUintOption(option, "--swap-fd", &swap_fd_, Usage);
-      } else if (option.starts_with("--swap-dex-size-threshold=")) {
-        ParseUintOption(option,
-                        "--swap-dex-size-threshold",
-                        &min_dex_file_cumulative_size_for_swap_,
-                        Usage);
-      } else if (option.starts_with("--swap-dex-count-threshold=")) {
-        ParseUintOption(option,
-                        "--swap-dex-count-threshold",
-                        &min_dex_files_for_swap_,
-                        Usage);
-      } else if (option.starts_with("--very-large-app-threshold=")) {
-        ParseUintOption(option,
-                        "--very-large-app-threshold",
-                        &very_large_threshold_,
-                        Usage);
-      } else if (option.starts_with("--app-image-file=")) {
-        app_image_file_name_ = option.substr(strlen("--app-image-file=")).data();
-      } else if (option.starts_with("--app-image-fd=")) {
-        ParseUintOption(option, "--app-image-fd", &app_image_fd_, Usage);
-      } else if (option == "--multi-image") {
-        multi_image_ = true;
-      } else if (option.starts_with("--no-inline-from=")) {
-        no_inline_from_string_ = option.substr(strlen("--no-inline-from=")).data();
-      } else if (option == "--force-determinism") {
-        if (!SupportsDeterministicCompilation()) {
-          Usage("Option --force-determinism requires read barriers or a CMS/MS garbage collector");
-        }
-        force_determinism_ = true;
-      } else if (option.starts_with("--classpath-dir=")) {
-        classpath_dir_ = option.substr(strlen("--classpath-dir=")).data();
-      } else if (option.starts_with("--class-loader-context=")) {
-        class_loader_context_ = ClassLoaderContext::Create(
-            option.substr(strlen("--class-loader-context=")).data());
-        if (class_loader_context_ == nullptr) {
-          Usage("Option --class-loader-context has an incorrect format: %s", option.data());
-        }
-      } else if (option.starts_with("--dirty-image-objects=")) {
-        dirty_image_objects_filename_ = option.substr(strlen("--dirty-image-objects=")).data();
-      } else if (!compiler_options_->ParseCompilerOption(option, Usage)) {
-        Usage("Unknown argument %s", option.data());
+      force_determinism_ = true;
+    }
+
+    if (args.Exists(M::Base)) {
+      ParseBase(*args.Get(M::Base));
+    }
+    if (args.Exists(M::TargetInstructionSetVariant)) {
+      ParseInstructionSetVariant(*args.Get(M::TargetInstructionSetVariant), parser_options.get());
+    }
+    if (args.Exists(M::TargetInstructionSetFeatures)) {
+      ParseInstructionSetFeatures(*args.Get(M::TargetInstructionSetFeatures), parser_options.get());
+    }
+    if (args.Exists(M::ClassLoaderContext)) {
+      class_loader_context_ = ClassLoaderContext::Create(*args.Get(M::ClassLoaderContext));
+      if (class_loader_context_ == nullptr) {
+        Usage("Option --class-loader-context has an incorrect format: %s",
+              args.Get(M::ClassLoaderContext)->c_str());
       }
     }
 
+    if (!ReadCompilerOptions(args, compiler_options_.get(), &error_msg)) {
+      Usage(error_msg.c_str());
+    }
+
     ProcessOptions(parser_options.get());
 
     // Insert some compiler things.
@@ -2931,7 +2837,7 @@
   std::unordered_map<const DexFile*, size_t> dex_file_oat_index_map_;
 
   // Backing storage.
-  std::vector<std::string> char_backing_storage_;
+  std::forward_list<std::string> char_backing_storage_;
 
   // See CompilerOptions.force_determinism_.
   bool force_determinism_;
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
new file mode 100644
index 0000000..43e6c4d
--- /dev/null
+++ b/dex2oat/dex2oat_options.cc
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dex2oat_options.h"
+
+#include <memory>
+
+#include "cmdline_parser.h"
+#include "driver/compiler_options_map-inl.h"
+
+namespace art {
+
+template<>
+struct CmdlineType<InstructionSet> : CmdlineTypeParser<InstructionSet> {
+  Result Parse(const std::string& option) {
+    InstructionSet set = GetInstructionSetFromString(option.c_str());
+    if (set == kNone) {
+      return Result::Failure(std::string("Not a valid instruction set: '") + option + "'");
+    }
+    return Result::Success(set);
+  }
+
+  static const char* Name() { return "InstructionSet"; }
+};
+
+#define COMPILER_OPTIONS_MAP_TYPE Dex2oatArgumentMap
+#define COMPILER_OPTIONS_MAP_KEY_TYPE Dex2oatArgumentMapKey
+#include "driver/compiler_options_map-storage.h"
+
+// Specify storage for the Dex2oatOptions keys.
+
+#define DEX2OAT_OPTIONS_KEY(Type, Name, ...) \
+  const Dex2oatArgumentMap::Key<Type> Dex2oatArgumentMap::Name {__VA_ARGS__};  // NOLINT [readability/braces] [4]
+#include "dex2oat_options.def"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+
+using M = Dex2oatArgumentMap;
+using Parser = CmdlineParser<Dex2oatArgumentMap, Dex2oatArgumentMap::Key>;
+using Builder = Parser::Builder;
+
+static void AddInputMappings(Builder& builder) {
+  builder.
+      Define("--dex-file=_")
+          .WithType<std::vector<std::string>>().AppendValues()
+          .IntoKey(M::DexFiles)
+      .Define("--dex-location=_")
+          .WithType<std::vector<std::string>>().AppendValues()
+          .IntoKey(M::DexLocations)
+      .Define("--zip-fd=_")
+          .WithType<int>()
+          .IntoKey(M::ZipFd)
+      .Define("--zip-location=_")
+          .WithType<std::string>()
+          .IntoKey(M::ZipLocation)
+      .Define("--boot-image=_")
+          .WithType<std::string>()
+          .IntoKey(M::BootImage);
+}
+
+static void AddGeneratedArtifactMappings(Builder& builder) {
+  builder.
+      Define("--input-vdex-fd=_")
+          .WithType<int>()
+          .IntoKey(M::InputVdexFd)
+      .Define("--input-vdex=_")
+          .WithType<std::string>()
+          .IntoKey(M::InputVdex)
+      .Define("--output-vdex-fd=_")
+          .WithType<int>()
+          .IntoKey(M::OutputVdexFd)
+      .Define("--output-vdex=_")
+          .WithType<std::string>()
+          .IntoKey(M::OutputVdex)
+      .Define("--oat-file=_")
+          .WithType<std::vector<std::string>>().AppendValues()
+          .IntoKey(M::OatFiles)
+      .Define("--oat-symbols=_")
+          .WithType<std::vector<std::string>>().AppendValues()
+          .IntoKey(M::OatSymbols)
+      .Define("--oat-fd=_")
+          .WithType<int>()
+          .IntoKey(M::OatFd)
+      .Define("--oat-location=_")
+          .WithType<std::string>()
+          .IntoKey(M::OatLocation);
+}
+
+static void AddImageMappings(Builder& builder) {
+  builder.
+      Define("--image=_")
+          .WithType<std::vector<std::string>>().AppendValues()
+          .IntoKey(M::ImageFilenames)
+      .Define("--image-classes=_")
+          .WithType<std::string>()
+          .IntoKey(M::ImageClasses)
+      .Define("--image-classes-zip=_")
+          .WithType<std::string>()
+          .IntoKey(M::ImageClassesZip)
+      .Define("--base=_")
+          .WithType<std::string>()
+          .IntoKey(M::Base)
+      .Define("--app-image-file=_")
+          .WithType<std::string>()
+          .IntoKey(M::AppImageFile)
+      .Define("--app-image-fd=_")
+          .WithType<int>()
+          .IntoKey(M::AppImageFileFd)
+      .Define("--multi-image")
+          .IntoKey(M::MultiImage)
+      .Define("--dirty-image-objects=_")
+          .WithType<std::string>()
+          .IntoKey(M::DirtyImageObjects)
+      .Define("--image-format=_")
+          .WithType<ImageHeader::StorageMode>()
+          .WithValueMap({{"lz4", ImageHeader::kStorageModeLZ4},
+                         {"lz4hc", ImageHeader::kStorageModeLZ4HC},
+                         {"uncompressed", ImageHeader::kStorageModeUncompressed}})
+          .IntoKey(M::ImageFormat);
+}
+
+static void AddSwapMappings(Builder& builder) {
+  builder.
+      Define("--swap-file=_")
+          .WithType<std::string>()
+          .IntoKey(M::SwapFile)
+      .Define("--swap-fd=_")
+          .WithType<int>()
+          .IntoKey(M::SwapFileFd)
+      .Define("--swap-dex-size-threshold=_")
+          .WithType<unsigned int>()
+          .IntoKey(M::SwapDexSizeThreshold)
+      .Define("--swap-dex-count-threshold=_")
+          .WithType<unsigned int>()
+          .IntoKey(M::SwapDexCountThreshold);
+}
+
+static void AddCompilerMappings(Builder& builder) {
+  builder.
+      Define("--compiled-classes=_")
+          .WithType<std::string>()
+          .IntoKey(M::CompiledClasses)
+      .Define("--compiled-classes-zip=_")
+          .WithType<std::string>()
+          .IntoKey(M::CompiledClassesZip)
+      .Define("--compiled-methods=_")
+          .WithType<std::string>()
+          .IntoKey(M::CompiledMethods)
+      .Define("--compiled-methods-zip=_")
+          .WithType<std::string>()
+          .IntoKey(M::CompiledMethodsZip)
+      .Define("--run-passes=_")
+          .WithType<std::string>()
+          .IntoKey(M::Passes)
+      .Define("--profile-file=_")
+          .WithType<std::string>()
+          .IntoKey(M::Profile)
+      .Define("--profile-file-fd=_")
+          .WithType<int>()
+          .IntoKey(M::ProfileFd)
+      .Define("--no-inline-from=_")
+          .WithType<std::string>()
+          .IntoKey(M::NoInlineFrom);
+}
+
+static void AddTargetMappings(Builder& builder) {
+  builder.
+      Define("--instruction-set=_")
+          .WithType<InstructionSet>()
+          .IntoKey(M::TargetInstructionSet)
+      .Define("--instruction-set-variant=_")
+          .WithType<std::string>()
+          .IntoKey(M::TargetInstructionSetVariant)
+      .Define("--instruction-set-features=_")
+          .WithType<std::string>()
+          .IntoKey(M::TargetInstructionSetFeatures);
+}
+
+static Parser CreateArgumentParser() {
+  std::unique_ptr<Builder> parser_builder = std::unique_ptr<Builder>(new Builder());
+
+  AddInputMappings(*parser_builder);
+  AddGeneratedArtifactMappings(*parser_builder);
+  AddImageMappings(*parser_builder);
+  AddSwapMappings(*parser_builder);
+  AddCompilerMappings(*parser_builder);
+  AddTargetMappings(*parser_builder);
+
+  parser_builder->
+      Define({"--watch-dog", "--no-watch-dog"})
+          .WithValues({true, false})
+          .IntoKey(M::Watchdog)
+      .Define("--watchdog-timeout=_")
+          .WithType<int>()
+          .IntoKey(M::WatchdogTimeout)
+      .Define("-j_")
+          .WithType<unsigned int>()
+          .IntoKey(M::Threads)
+      .Define("--android-root=_")
+          .WithType<std::string>()
+          .IntoKey(M::AndroidRoot)
+      .Define("--compiler-backend=_")
+          .WithType<Compiler::Kind>()
+          .WithValueMap({{"Quick", Compiler::Kind::kQuick},
+                         {"Optimizing", Compiler::Kind::kOptimizing}})
+          .IntoKey(M::Backend)
+      .Define("--host")
+          .IntoKey(M::Host)
+      .Define("--dump-timing")
+          .IntoKey(M::DumpTiming)
+      .Define("--dump-passes")
+          .IntoKey(M::DumpPasses)
+      .Define("--dump-stats")
+          .IntoKey(M::DumpStats)
+      .Define("--avoid-storing-invocation")
+          .IntoKey(M::AvoidStoringInvocation)
+      .Define("--very-large-app-threshold=_")
+          .WithType<unsigned int>()
+          .IntoKey(M::VeryLargeAppThreshold)
+      .Define("--force-determinism")
+          .IntoKey(M::ForceDeterminism)
+      .Define("--classpath-dir=_")
+          .WithType<std::string>()
+          .IntoKey(M::ClasspathDir)
+      .Define("--class-loader-context=_")
+          .WithType<std::string>()
+          .IntoKey(M::ClassLoaderContext)
+      .Define("--runtime-arg _")
+          .WithType<std::vector<std::string>>().AppendValues()
+          .IntoKey(M::RuntimeOptions);
+
+  AddCompilerOptionsArgumentParserOptions<Dex2oatArgumentMap>(*parser_builder);
+
+  parser_builder->IgnoreUnrecognized(false);
+
+  return parser_builder->Build();
+}
+
+#pragma GCC diagnostic pop
+
+std::unique_ptr<Dex2oatArgumentMap> Dex2oatArgumentMap::Parse(int argc,
+                                                              const char** argv,
+                                                              std::string* error_msg) {
+  Parser parser = CreateArgumentParser();
+  CmdlineResult parse_result = parser.Parse(argv, argc);
+  if (!parse_result.IsSuccess()) {
+    *error_msg = parse_result.GetMessage();
+    return nullptr;
+  }
+
+  return std::unique_ptr<Dex2oatArgumentMap>(new Dex2oatArgumentMap(parser.ReleaseArgumentsMap()));
+}
+
+}  // namespace art
diff --git a/dex2oat/dex2oat_options.def b/dex2oat/dex2oat_options.def
new file mode 100644
index 0000000..83a3035
--- /dev/null
+++ b/dex2oat/dex2oat_options.def
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DEX2OAT_OPTIONS_KEY
+#error "Please #define DEX2OAT_OPTIONS_KEY before #including this file"
+#define DEX2OAT_OPTIONS_KEY(...)  // Don't display errors in this file in IDEs.
+#endif
+
+// This file defines the list of keys for Dex2oatOptions.
+// These can be used with Dex2oatOptions.Get/Set/etc, for example:
+//         Dex2oatOptions opt; bool* dex2oat_enabled = opt.Get(Dex2oatOptions::Dex2Oat);
+//
+// Column Descriptions:
+//                   <<Type>>             <<Key Name>>                  <<Default Value>>
+//
+// Default values are only used by Map::GetOrDefault(K<T>).
+// If a default value is omitted here, T{} is used as the default value, which is
+// almost-always the value of the type as if it was memset to all 0.
+//
+// Please keep the columns aligned if possible when adding new rows.
+//
+
+// Parse-able keys from the command line.
+DEX2OAT_OPTIONS_KEY (std::vector<std::string>,       DexFiles)
+DEX2OAT_OPTIONS_KEY (std::vector<std::string>,       DexLocations)
+DEX2OAT_OPTIONS_KEY (int,                            ZipFd)
+DEX2OAT_OPTIONS_KEY (std::string,                    ZipLocation)
+DEX2OAT_OPTIONS_KEY (int,                            InputVdexFd)
+DEX2OAT_OPTIONS_KEY (std::string,                    InputVdex)
+DEX2OAT_OPTIONS_KEY (int,                            OutputVdexFd)
+DEX2OAT_OPTIONS_KEY (std::string,                    OutputVdex)
+DEX2OAT_OPTIONS_KEY (std::vector<std::string>,       OatFiles)
+DEX2OAT_OPTIONS_KEY (std::vector<std::string>,       OatSymbols)
+DEX2OAT_OPTIONS_KEY (int,                            OatFd)
+DEX2OAT_OPTIONS_KEY (std::string,                    OatLocation)
+DEX2OAT_OPTIONS_KEY (bool,                           Watchdog)
+DEX2OAT_OPTIONS_KEY (int,                            WatchdogTimeout)
+DEX2OAT_OPTIONS_KEY (unsigned int,                   Threads)
+DEX2OAT_OPTIONS_KEY (std::vector<std::string>,       ImageFilenames)
+DEX2OAT_OPTIONS_KEY (std::string,                    ImageClasses)
+DEX2OAT_OPTIONS_KEY (std::string,                    ImageClassesZip)
+DEX2OAT_OPTIONS_KEY (ImageHeader::StorageMode,       ImageFormat)
+DEX2OAT_OPTIONS_KEY (std::string,                    CompiledClasses)
+DEX2OAT_OPTIONS_KEY (std::string,                    CompiledClassesZip)
+DEX2OAT_OPTIONS_KEY (std::string,                    CompiledMethods)
+DEX2OAT_OPTIONS_KEY (std::string,                    CompiledMethodsZip)
+DEX2OAT_OPTIONS_KEY (std::string,                    Passes)
+DEX2OAT_OPTIONS_KEY (std::string,                    Base)  // TODO: Hex string parsing.
+DEX2OAT_OPTIONS_KEY (std::string,                    BootImage)
+DEX2OAT_OPTIONS_KEY (std::string,                    AndroidRoot)
+DEX2OAT_OPTIONS_KEY (InstructionSet,                 TargetInstructionSet)
+DEX2OAT_OPTIONS_KEY (std::string,                    TargetInstructionSetVariant)
+DEX2OAT_OPTIONS_KEY (std::string,                    TargetInstructionSetFeatures)
+DEX2OAT_OPTIONS_KEY (Compiler::Kind,                 Backend)
+DEX2OAT_OPTIONS_KEY (std::string,                    Profile)
+DEX2OAT_OPTIONS_KEY (int,                            ProfileFd)
+DEX2OAT_OPTIONS_KEY (Unit,                           Host)
+DEX2OAT_OPTIONS_KEY (Unit,                           DumpTiming)
+DEX2OAT_OPTIONS_KEY (Unit,                           DumpPasses)
+DEX2OAT_OPTIONS_KEY (Unit,                           DumpStats)
+DEX2OAT_OPTIONS_KEY (Unit,                           AvoidStoringInvocation)
+DEX2OAT_OPTIONS_KEY (std::string,                    SwapFile)
+DEX2OAT_OPTIONS_KEY (int,                            SwapFileFd)
+DEX2OAT_OPTIONS_KEY (unsigned int,                   SwapDexSizeThreshold)
+DEX2OAT_OPTIONS_KEY (unsigned int,                   SwapDexCountThreshold)
+DEX2OAT_OPTIONS_KEY (unsigned int,                   VeryLargeAppThreshold)
+DEX2OAT_OPTIONS_KEY (std::string,                    AppImageFile)
+DEX2OAT_OPTIONS_KEY (int,                            AppImageFileFd)
+DEX2OAT_OPTIONS_KEY (Unit,                           MultiImage)
+DEX2OAT_OPTIONS_KEY (std::string,                    NoInlineFrom)
+DEX2OAT_OPTIONS_KEY (Unit,                           ForceDeterminism)
+DEX2OAT_OPTIONS_KEY (std::string,                    ClasspathDir)
+DEX2OAT_OPTIONS_KEY (std::string,                    ClassLoaderContext)
+DEX2OAT_OPTIONS_KEY (std::string,                    DirtyImageObjects)
+DEX2OAT_OPTIONS_KEY (std::vector<std::string>,       RuntimeOptions)
+
+#undef DEX2OAT_OPTIONS_KEY
diff --git a/dex2oat/dex2oat_options.h b/dex2oat/dex2oat_options.h
new file mode 100644
index 0000000..a4c7186
--- /dev/null
+++ b/dex2oat/dex2oat_options.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_DEX2OAT_DEX2OAT_OPTIONS_H_
+#define ART_DEX2OAT_DEX2OAT_OPTIONS_H_
+
+#include <cstdio>
+#include <string>
+#include <vector>
+
+#include "base/variant_map.h"
+#include "cmdline_types.h"  // TODO: don't need to include this file here
+#include "compiler.h"
+#include "driver/compiler_options_map.h"
+#include "image.h"
+
+namespace art {
+
+template <typename TVariantMap,
+          template <typename TKeyValue> class TVariantMapKey>
+struct CmdlineParser;
+
+// Define a key that is usable with a Dex2oatArgumentMap.
+// This key will *not* work with other subtypes of VariantMap.
+template <typename TValue>
+struct Dex2oatArgumentMapKey : VariantMapKey<TValue> {
+  Dex2oatArgumentMapKey() {}
+  explicit Dex2oatArgumentMapKey(TValue default_value)
+      : VariantMapKey<TValue>(std::move(default_value)) {}
+  // Don't ODR-use constexpr default values, which means that Struct::Fields
+  // that are declared 'static constexpr T Name = Value' don't need to have a matching definition.
+};
+
+// Defines a type-safe heterogeneous key->value map.
+// Use the VariantMap interface to look up or to store a Dex2oatArgumentMapKey,Value pair.
+//
+// Example:
+//    auto map = Dex2oatArgumentMap();
+//    map.Set(Dex2oatArgumentMap::ZipFd, -1);
+//    int *target_utilization = map.Get(Dex2oatArgumentMap::ZipFd);
+//
+struct Dex2oatArgumentMap : CompilerOptionsMap<Dex2oatArgumentMap, Dex2oatArgumentMapKey> {
+  // This 'using' line is necessary to inherit the variadic constructor.
+  using CompilerOptionsMap<Dex2oatArgumentMap, Dex2oatArgumentMapKey>::CompilerOptionsMap;
+
+  static std::unique_ptr<Dex2oatArgumentMap> Parse(int argc,
+                                                   const char** argv,
+                                                   std::string* error_msg);
+
+  // Make the next many usages of Key slightly shorter to type.
+  template <typename TValue>
+  using Key = Dex2oatArgumentMapKey<TValue>;
+
+  // List of key declarations, shorthand for 'static const Key<T> Name'
+#define DEX2OAT_OPTIONS_KEY(Type, Name, ...) static const Key<Type> (Name);
+#include "dex2oat_options.def"
+};
+
+extern template struct CompilerOptionsMap<Dex2oatArgumentMap, Dex2oatArgumentMapKey>;
+
+}  // namespace art
+
+#endif  // ART_DEX2OAT_DEX2OAT_OPTIONS_H_
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index d89d9f0..50434ef 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -48,16 +48,6 @@
 namespace art {
 namespace linker {
 
-NO_RETURN static void Usage(const char* fmt, ...) {
-  va_list ap;
-  va_start(ap, fmt);
-  std::string error;
-  android::base::StringAppendV(&error, fmt, ap);
-  LOG(FATAL) << error;
-  va_end(ap);
-  UNREACHABLE();
-}
-
 class OatTest : public CommonCompilerTest {
  protected:
   static const bool kCompile = false;  // DISABLED_ due to the time to compile libcore
@@ -101,8 +91,11 @@
     insn_features_ = InstructionSetFeatures::FromVariant(insn_set, "default", error_msg);
     ASSERT_TRUE(insn_features_ != nullptr) << *error_msg;
     compiler_options_.reset(new CompilerOptions);
-    for (const std::string& option : compiler_options) {
-      compiler_options_->ParseCompilerOption(option, Usage);
+    if (!compiler_options_->ParseCompilerOptions(compiler_options,
+                                                 false /* ignore_unrecognized */,
+                                                 error_msg)) {
+      LOG(FATAL) << *error_msg;
+      UNREACHABLE();
     }
     verification_results_.reset(new VerificationResults(compiler_options_.get()));
     callbacks_.reset(new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileApp));
diff --git a/runtime/base/variant_map.h b/runtime/base/variant_map.h
index d87df87..71a1018 100644
--- a/runtime/base/variant_map.h
+++ b/runtime/base/variant_map.h
@@ -237,6 +237,14 @@
     return (ptr == nullptr) ? key.CreateDefaultValue() : *ptr;
   }
 
+  template <typename T, typename U>
+  void AssignIfExists(const TKey<T>& key, U* out) {
+    DCHECK(out != nullptr);
+    if (Exists(key)) {
+      *out = std::move(*Get(key));
+    }
+  }
+
  private:
   // TODO: move to detail, or make it more generic like a ScopeGuard(function)
   template <typename TValue>