Merge "ART: Compile libctstiagent with the NDK"
diff --git a/Android.mk b/Android.mk
index 2489308..558986e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -25,18 +25,6 @@
 include $(art_path)/build/Android.common_path.mk
 include $(art_path)/build/Android.oat.mk
 
-# Following the example of build's dont_bother for clean targets.
-art_dont_bother := false
-ifneq (,$(filter clean-oat%,$(MAKECMDGOALS)))
-  art_dont_bother := true
-endif
-
-# Don't bother with tests unless there is a test-art*, build-art*, or related target.
-art_test_bother := false
-ifneq (,$(filter tests test-art% valgrind-test-art% build-art% checkbuild,$(MAKECMDGOALS)))
-  art_test_bother := true
-endif
-
 .PHONY: clean-oat
 clean-oat: clean-oat-host clean-oat-target
 
@@ -66,8 +54,6 @@
 endif
 	adb shell rm -rf data/run-test/test-*/dalvik-cache/*
 
-ifneq ($(art_dont_bother),true)
-
 ########################################################################
 # cpplint rules to style check art source files
 
@@ -104,8 +90,6 @@
 ########################################################################
 # test rules
 
-ifeq ($(art_test_bother),true)
-
 # All the dependencies that must be built ahead of sync-ing them onto the target device.
 TEST_ART_TARGET_SYNC_DEPS :=
 
@@ -349,7 +333,6 @@
 valgrind-test-art-target64: valgrind-test-art-target-gtest64
 	$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
 
-endif  # art_test_bother
 
 #######################
 # Fake packages for ART
@@ -606,11 +589,7 @@
 
 ########################################################################
 
-endif # !art_dont_bother
-
 # Clear locally used variables.
-art_dont_bother :=
-art_test_bother :=
 TEST_ART_TARGET_SYNC_DEPS :=
 
 # Helper target that depends on boot image creation.
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index df5bb37..59a080f 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -156,14 +156,25 @@
   // No program headers since the ELF file is not linked and has no allocated sections.
   builder->Start(false /* write_program_headers */);
   if (mini_debug_info) {
-    std::vector<uint8_t> mdi = MakeMiniDebugInfo(isa,
-                                                 features,
-                                                 min_address,
-                                                 max_address - min_address,
-                                                 /* dex_section_address */ 0,
-                                                 /* dex_section_size */ 0,
-                                                 debug_info);
-    builder->WriteSection(".gnu_debugdata", &mdi);
+    if (method_infos.size() > 1) {
+      std::vector<uint8_t> mdi = MakeMiniDebugInfo(isa,
+                                                   features,
+                                                   min_address,
+                                                   max_address - min_address,
+                                                   /* dex_section_address */ 0,
+                                                   /* dex_section_size */ 0,
+                                                   debug_info);
+      builder->WriteSection(".gnu_debugdata", &mdi);
+    } else {
+      // The compression is great help for multiple methods but it is not worth it for a
+      // single method due to the overheads so skip the compression here for performance.
+      builder->GetText()->AllocateVirtualMemory(min_address, max_address - min_address);
+      WriteDebugSymbols(builder.get(), true /* mini-debug-info */, debug_info);
+      WriteCFISection(builder.get(),
+                      debug_info.compiled_methods,
+                      dwarf::DW_DEBUG_FRAME_FORMAT,
+                      false /* write_oat_paches */);
+    }
   } else {
     builder->GetText()->AllocateVirtualMemory(min_address, max_address - min_address);
     WriteDebugInfo(builder.get(),
diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h
index 4b19547..1310e8d 100644
--- a/compiler/debug/elf_symtab_writer.h
+++ b/compiler/debug/elf_symtab_writer.h
@@ -37,7 +37,8 @@
 // exist, but it will still work well without them.
 // However, these extra symbols take space, so let's just generate
 // one symbol which marks the whole .text section as code.
-constexpr bool kGenerateSingleArmMappingSymbol = true;
+// Note that ARM's Streamline requires it to match function symbol.
+constexpr bool kGenerateArmMappingSymbol = true;
 
 // Magic name for .symtab symbols which enumerate dex files used
 // by this ELF file (currently mmapped inside the .dex section).
@@ -48,6 +49,7 @@
                               bool mini_debug_info,
                               const DebugInfo& debug_info) {
   uint64_t mapping_symbol_address = std::numeric_limits<uint64_t>::max();
+  const auto* text = builder->GetText();
   auto* strtab = builder->GetStrTab();
   auto* symtab = builder->GetSymTab();
 
@@ -62,10 +64,19 @@
     if (info.deduped) {
       deduped_addresses.insert(info.code_address);
     }
+    if (kGenerateArmMappingSymbol && info.isa == InstructionSet::kThumb2) {
+      uint64_t address = info.code_address;
+      address += info.is_code_address_text_relative ? text->GetAddress() : 0;
+      mapping_symbol_address = std::min(mapping_symbol_address, address);
+    }
   }
 
   strtab->Start();
   strtab->Write("");  // strtab should start with empty string.
+  // Generate ARM mapping symbols. ELF local symbols must be added first.
+  if (mapping_symbol_address != std::numeric_limits<uint64_t>::max()) {
+    symtab->Add(strtab->Write("$t"), text, mapping_symbol_address, 0, STB_LOCAL, STT_NOTYPE);
+  }
   // Add symbols for compiled methods.
   for (const MethodDebugInfo& info : debug_info.compiled_methods) {
     if (info.deduped) {
@@ -83,23 +94,11 @@
       name_offset = strtab->Write(name);
     }
 
-    const auto* text = builder->GetText();
     uint64_t address = info.code_address;
     address += info.is_code_address_text_relative ? text->GetAddress() : 0;
     // Add in code delta, e.g., thumb bit 0 for Thumb2 code.
     address += CompiledMethod::CodeDelta(info.isa);
     symtab->Add(name_offset, text, address, info.code_size, STB_GLOBAL, STT_FUNC);
-
-    // Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2
-    // instructions, so that disassembler tools can correctly disassemble.
-    // Note that even if we generate just a single mapping symbol, ARM's Streamline
-    // requires it to match function symbol.  Just address 0 does not work.
-    if (info.isa == InstructionSet::kThumb2) {
-      if (address < mapping_symbol_address || !kGenerateSingleArmMappingSymbol) {
-        symtab->Add(strtab->Write("$t"), text, address & ~1, 0, STB_LOCAL, STT_NOTYPE);
-        mapping_symbol_address = address;
-      }
-    }
   }
   // Add symbols for dex files.
   if (!debug_info.dex_files.empty() && builder->GetDex()->Exists()) {
diff --git a/compiler/linker/elf_builder.h b/compiler/linker/elf_builder.h
index 3145497..9fb4b18 100644
--- a/compiler/linker/elf_builder.h
+++ b/compiler/linker/elf_builder.h
@@ -201,7 +201,7 @@
       return section_index_ != 0;
     }
 
-   private:
+   protected:
     // Add this section to the list of generated ELF sections (if not there already).
     // It also ensures the alignment is sufficient to generate valid program headers,
     // since that depends on the previous section. It returns the required alignment.
@@ -345,7 +345,7 @@
                   type,
                   flags,
                   strtab,
-                  /* info */ 0,
+                  /* info */ 1,
                   sizeof(Elf_Off),
                   sizeof(Elf_Sym)) {
       syms_.push_back(Elf_Sym());  // The symbol table always has to start with NULL symbol.
@@ -386,6 +386,11 @@
       sym.st_shndx = section_index;
       sym.st_info = (binding << 4) + (type & 0xf);
       syms_.push_back(sym);
+
+      // The sh_info file must be set to index one-past the last local symbol.
+      if (binding == STB_LOCAL) {
+        this->header_.sh_info = syms_.size();
+      }
     }
 
     Elf_Word GetCacheSize() { return syms_.size() * sizeof(Elf_Sym); }
@@ -717,8 +722,10 @@
     Elf_Word oatdata = dynstr_.Add("oatdata");
     dynsym_.Add(oatdata, &rodata_, rodata_.GetAddress(), rodata_size, STB_GLOBAL, STT_OBJECT);
     if (text_size != 0u) {
+      // The runtime does not care about the size of this symbol (it uses the "lastword" symbol).
+      // We use size 0 (meaning "unknown size" in ELF) to prevent overlap with the debug symbols.
       Elf_Word oatexec = dynstr_.Add("oatexec");
-      dynsym_.Add(oatexec, &text_, text_.GetAddress(), text_size, STB_GLOBAL, STT_OBJECT);
+      dynsym_.Add(oatexec, &text_, text_.GetAddress(), /* size */ 0, STB_GLOBAL, STT_OBJECT);
       Elf_Word oatlastword = dynstr_.Add("oatlastword");
       Elf_Word oatlastword_address = text_.GetAddress() + text_size - 4;
       dynsym_.Add(oatlastword, &text_, oatlastword_address, 4, STB_GLOBAL, STT_OBJECT);
@@ -756,7 +763,7 @@
     }
     if (dex_size != 0u) {
       Elf_Word oatdex = dynstr_.Add("oatdex");
-      dynsym_.Add(oatdex, &dex_, dex_.GetAddress(), dex_size, STB_GLOBAL, STT_OBJECT);
+      dynsym_.Add(oatdex, &dex_, dex_.GetAddress(), /* size */ 0, STB_GLOBAL, STT_OBJECT);
       Elf_Word oatdexlastword = dynstr_.Add("oatdexlastword");
       Elf_Word oatdexlastword_address = dex_.GetAddress() + dex_size - 4;
       dynsym_.Add(oatdexlastword, &dex_, oatdexlastword_address, 4, STB_GLOBAL, STT_OBJECT);
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 91e475d..f6ba19f 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1121,10 +1121,6 @@
   user->FixUpUserRecordsAfterEnvUseRemoval(before_env_use_node);
 }
 
-HInstruction::InstructionKind HInstruction::GetKind() const {
-  return GetKindInternal();
-}
-
 HInstruction* HInstruction::GetNextDisregardingMoves() const {
   HInstruction* next = GetNext();
   while (next != nullptr && next->IsParallelMove()) {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index f91d37b..0534685 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1518,7 +1518,6 @@
   private:                                                                \
   H##type& operator=(const H##type&) = delete;                            \
   public:                                                                 \
-  InstructionKind GetKindInternal() const OVERRIDE { return k##type; }    \
   const char* DebugName() const OVERRIDE { return #type; }                \
   bool InstructionTypeEquals(const HInstruction* other) const OVERRIDE {  \
     return other->Is##type();                                             \
@@ -1952,7 +1951,14 @@
 
 class HInstruction : public ArenaObject<kArenaAllocInstruction> {
  public:
-  HInstruction(SideEffects side_effects, uint32_t dex_pc)
+#define DECLARE_KIND(type, super) k##type,
+  enum InstructionKind {
+    FOR_EACH_INSTRUCTION(DECLARE_KIND)
+    kLastInstructionKind
+  };
+#undef DECLARE_KIND
+
+  HInstruction(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc)
       : previous_(nullptr),
         next_(nullptr),
         block_(nullptr),
@@ -1966,16 +1972,12 @@
         lifetime_position_(kNoLifetime),
         side_effects_(side_effects),
         reference_type_handle_(ReferenceTypeInfo::CreateInvalid().GetTypeHandle()) {
+    SetPackedField<InstructionKindField>(kind);
     SetPackedFlag<kFlagReferenceTypeIsExact>(ReferenceTypeInfo::CreateInvalid().IsExact());
   }
 
   virtual ~HInstruction() {}
 
-#define DECLARE_KIND(type, super) k##type,
-  enum InstructionKind {
-    FOR_EACH_INSTRUCTION(DECLARE_KIND)
-  };
-#undef DECLARE_KIND
 
   HInstruction* GetNext() const { return next_; }
   HInstruction* GetPrevious() const { return previous_; }
@@ -2279,8 +2281,7 @@
   // is adopted and implemented by our C++ compiler(s). Fow now, we need to hide
   // the virtual function because the __attribute__((__pure__)) doesn't really
   // apply the strong requirement for virtual functions, preventing optimizations.
-  InstructionKind GetKind() const PURE;
-  virtual InstructionKind GetKindInternal() const = 0;
+  InstructionKind GetKind() const { return GetPackedField<InstructionKindField>(); }
 
   virtual size_t ComputeHashCode() const {
     size_t result = GetKind();
@@ -2332,9 +2333,16 @@
   // its users. Used by liveness analysis to compute use positions accordingly.
   static constexpr size_t kFlagEmittedAtUseSite = 0u;
   static constexpr size_t kFlagReferenceTypeIsExact = kFlagEmittedAtUseSite + 1;
-  static constexpr size_t kNumberOfGenericPackedBits = kFlagReferenceTypeIsExact + 1;
+  static constexpr size_t kFieldInstructionKind = kFlagReferenceTypeIsExact + 1;
+  static constexpr size_t kFieldInstructionKindSize =
+      MinimumBitsToStore(static_cast<size_t>(InstructionKind::kLastInstructionKind - 1));
+  static constexpr size_t kNumberOfGenericPackedBits =
+      kFieldInstructionKind + kFieldInstructionKindSize;
   static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte;
 
+  static_assert(kNumberOfGenericPackedBits <= kMaxNumberOfPackedBits,
+                "Too many generic packed fields");
+
   const HUserRecord<HInstruction*> InputRecordAt(size_t i) const {
     return GetInputRecords()[i];
   }
@@ -2391,9 +2399,13 @@
         live_interval_(nullptr),
         lifetime_position_(kNoLifetime),
         side_effects_(other.side_effects_),
-        reference_type_handle_(other.reference_type_handle_) {}
+        reference_type_handle_(other.reference_type_handle_) {
+  }
 
  private:
+  using InstructionKindField =
+     BitField<InstructionKind, kFieldInstructionKind, kFieldInstructionKindSize>;
+
   void FixUpUserRecordsAfterUseInsertion(HUseList<HInstruction*>::iterator fixup_end) {
     auto before_use_node = uses_.before_begin();
     for (auto use_node = uses_.begin(); use_node != fixup_end; ++use_node) {
@@ -2569,12 +2581,13 @@
   void RemoveAllInputs();
 
  protected:
-  HVariableInputSizeInstruction(SideEffects side_effects,
+  HVariableInputSizeInstruction(InstructionKind inst_kind,
+                                SideEffects side_effects,
                                 uint32_t dex_pc,
                                 ArenaAllocator* allocator,
                                 size_t number_of_inputs,
                                 ArenaAllocKind kind)
-      : HInstruction(side_effects, dex_pc),
+      : HInstruction(inst_kind, side_effects, dex_pc),
         inputs_(number_of_inputs, allocator->Adapter(kind)) {}
 
   DEFAULT_COPY_CONSTRUCTOR(VariableInputSizeInstruction);
@@ -2585,8 +2598,8 @@
 template<size_t N>
 class HTemplateInstruction: public HInstruction {
  public:
-  HTemplateInstruction<N>(SideEffects side_effects, uint32_t dex_pc)
-      : HInstruction(side_effects, dex_pc), inputs_() {}
+  HTemplateInstruction<N>(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc)
+      : HInstruction(kind, side_effects, dex_pc), inputs_() {}
   virtual ~HTemplateInstruction() {}
 
   using HInstruction::GetInputRecords;  // Keep the const version visible.
@@ -2607,8 +2620,8 @@
 template<>
 class HTemplateInstruction<0>: public HInstruction {
  public:
-  explicit HTemplateInstruction<0>(SideEffects side_effects, uint32_t dex_pc)
-      : HInstruction(side_effects, dex_pc) {}
+  explicit HTemplateInstruction<0>(InstructionKind kind, SideEffects side_effects, uint32_t dex_pc)
+      : HInstruction(kind, side_effects, dex_pc) {}
 
   virtual ~HTemplateInstruction() {}
 
@@ -2627,8 +2640,12 @@
 template<intptr_t N>
 class HExpression : public HTemplateInstruction<N> {
  public:
-  HExpression<N>(DataType::Type type, SideEffects side_effects, uint32_t dex_pc)
-      : HTemplateInstruction<N>(side_effects, dex_pc) {
+  using HInstruction::InstructionKind;
+  HExpression<N>(InstructionKind kind,
+                 DataType::Type type,
+                 SideEffects side_effects,
+                 uint32_t dex_pc)
+      : HTemplateInstruction<N>(kind, side_effects, dex_pc) {
     this->template SetPackedField<TypeField>(type);
   }
   virtual ~HExpression() {}
@@ -2653,7 +2670,8 @@
 class HReturnVoid FINAL : public HTemplateInstruction<0> {
  public:
   explicit HReturnVoid(uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(SideEffects::None(), dex_pc) {}
+      : HTemplateInstruction(kReturnVoid, SideEffects::None(), dex_pc) {
+  }
 
   bool IsControlFlow() const OVERRIDE { return true; }
 
@@ -2668,7 +2686,7 @@
 class HReturn FINAL : public HTemplateInstruction<1> {
  public:
   explicit HReturn(HInstruction* value, uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(SideEffects::None(), dex_pc) {
+      : HTemplateInstruction(kReturn, SideEffects::None(), dex_pc) {
     SetRawInputAt(0, value);
   }
 
@@ -2688,6 +2706,7 @@
        DataType::Type type,
        uint32_t dex_pc = kNoDexPc)
       : HVariableInputSizeInstruction(
+            kPhi,
             SideEffects::None(),
             dex_pc,
             allocator,
@@ -2788,7 +2807,9 @@
 // exit block.
 class HExit FINAL : public HTemplateInstruction<0> {
  public:
-  explicit HExit(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {}
+  explicit HExit(uint32_t dex_pc = kNoDexPc)
+      : HTemplateInstruction(kExit, SideEffects::None(), dex_pc) {
+  }
 
   bool IsControlFlow() const OVERRIDE { return true; }
 
@@ -2801,7 +2822,9 @@
 // Jumps from one block to another.
 class HGoto FINAL : public HTemplateInstruction<0> {
  public:
-  explicit HGoto(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {}
+  explicit HGoto(uint32_t dex_pc = kNoDexPc)
+      : HTemplateInstruction(kGoto, SideEffects::None(), dex_pc) {
+  }
 
   bool IsClonable() const OVERRIDE { return true; }
   bool IsControlFlow() const OVERRIDE { return true; }
@@ -2818,8 +2841,9 @@
 
 class HConstant : public HExpression<0> {
  public:
-  explicit HConstant(DataType::Type type, uint32_t dex_pc = kNoDexPc)
-      : HExpression(type, SideEffects::None(), dex_pc) {}
+  explicit HConstant(InstructionKind kind, DataType::Type type, uint32_t dex_pc = kNoDexPc)
+      : HExpression(kind, type, SideEffects::None(), dex_pc) {
+  }
 
   bool CanBeMoved() const OVERRIDE { return true; }
 
@@ -2860,7 +2884,8 @@
 
  private:
   explicit HNullConstant(uint32_t dex_pc = kNoDexPc)
-      : HConstant(DataType::Type::kReference, dex_pc) {}
+      : HConstant(kNullConstant, DataType::Type::kReference, dex_pc) {
+  }
 
   friend class HGraph;
 };
@@ -2899,9 +2924,12 @@
 
  private:
   explicit HIntConstant(int32_t value, uint32_t dex_pc = kNoDexPc)
-      : HConstant(DataType::Type::kInt32, dex_pc), value_(value) {}
+      : HConstant(kIntConstant, DataType::Type::kInt32, dex_pc), value_(value) {
+  }
   explicit HIntConstant(bool value, uint32_t dex_pc = kNoDexPc)
-      : HConstant(DataType::Type::kInt32, dex_pc), value_(value ? 1 : 0) {}
+      : HConstant(kIntConstant, DataType::Type::kInt32, dex_pc),
+        value_(value ? 1 : 0) {
+  }
 
   const int32_t value_;
 
@@ -2935,7 +2963,9 @@
 
  private:
   explicit HLongConstant(int64_t value, uint32_t dex_pc = kNoDexPc)
-      : HConstant(DataType::Type::kInt64, dex_pc), value_(value) {}
+      : HConstant(kLongConstant, DataType::Type::kInt64, dex_pc),
+        value_(value) {
+  }
 
   const int64_t value_;
 
@@ -2986,9 +3016,13 @@
 
  private:
   explicit HFloatConstant(float value, uint32_t dex_pc = kNoDexPc)
-      : HConstant(DataType::Type::kFloat32, dex_pc), value_(value) {}
+      : HConstant(kFloatConstant, DataType::Type::kFloat32, dex_pc),
+        value_(value) {
+  }
   explicit HFloatConstant(int32_t value, uint32_t dex_pc = kNoDexPc)
-      : HConstant(DataType::Type::kFloat32, dex_pc), value_(bit_cast<float, int32_t>(value)) {}
+      : HConstant(kFloatConstant, DataType::Type::kFloat32, dex_pc),
+        value_(bit_cast<float, int32_t>(value)) {
+  }
 
   const float value_;
 
@@ -3039,9 +3073,13 @@
 
  private:
   explicit HDoubleConstant(double value, uint32_t dex_pc = kNoDexPc)
-      : HConstant(DataType::Type::kFloat64, dex_pc), value_(value) {}
+      : HConstant(kDoubleConstant, DataType::Type::kFloat64, dex_pc),
+        value_(value) {
+  }
   explicit HDoubleConstant(int64_t value, uint32_t dex_pc = kNoDexPc)
-      : HConstant(DataType::Type::kFloat64, dex_pc), value_(bit_cast<double, int64_t>(value)) {}
+      : HConstant(kDoubleConstant, DataType::Type::kFloat64, dex_pc),
+        value_(bit_cast<double, int64_t>(value)) {
+  }
 
   const double value_;
 
@@ -3055,7 +3093,7 @@
 class HIf FINAL : public HTemplateInstruction<1> {
  public:
   explicit HIf(HInstruction* input, uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(SideEffects::None(), dex_pc) {
+      : HTemplateInstruction(kIf, SideEffects::None(), dex_pc) {
     SetRawInputAt(0, input);
   }
 
@@ -3091,7 +3129,7 @@
   };
 
   explicit HTryBoundary(BoundaryKind kind, uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(SideEffects::None(), dex_pc) {
+      : HTemplateInstruction(kTryBoundary, SideEffects::None(), dex_pc) {
     SetPackedField<BoundaryKindField>(kind);
   }
 
@@ -3150,6 +3188,7 @@
               DeoptimizationKind kind,
               uint32_t dex_pc)
       : HVariableInputSizeInstruction(
+            kDeoptimize,
             SideEffects::All(),
             dex_pc,
             allocator,
@@ -3173,6 +3212,7 @@
               DeoptimizationKind kind,
               uint32_t dex_pc)
       : HVariableInputSizeInstruction(
+            kDeoptimize,
             SideEffects::CanTriggerGC(),
             dex_pc,
             allocator,
@@ -3241,7 +3281,12 @@
   // CHA guards are only optimized in a separate pass and it has no side effects
   // with regard to other passes.
   HShouldDeoptimizeFlag(ArenaAllocator* allocator, uint32_t dex_pc)
-      : HVariableInputSizeInstruction(SideEffects::None(), dex_pc, allocator, 0, kArenaAllocCHA) {
+      : HVariableInputSizeInstruction(kShouldDeoptimizeFlag,
+                                      SideEffects::None(),
+                                      dex_pc,
+                                      allocator,
+                                      0,
+                                      kArenaAllocCHA) {
   }
 
   DataType::Type GetType() const OVERRIDE { return DataType::Type::kInt32; }
@@ -3264,7 +3309,8 @@
 class HCurrentMethod FINAL : public HExpression<0> {
  public:
   explicit HCurrentMethod(DataType::Type type, uint32_t dex_pc = kNoDexPc)
-      : HExpression(type, SideEffects::None(), dex_pc) {}
+      : HExpression(kCurrentMethod, type, SideEffects::None(), dex_pc) {
+  }
 
   DECLARE_INSTRUCTION(CurrentMethod);
 
@@ -3286,7 +3332,7 @@
                  TableKind kind,
                  size_t index,
                  uint32_t dex_pc)
-      : HExpression(type, SideEffects::None(), dex_pc),
+      : HExpression(kClassTableGet, type, SideEffects::None(), dex_pc),
         index_(index) {
     SetPackedField<TableKindField>(kind);
     SetRawInputAt(0, cls);
@@ -3329,7 +3375,7 @@
                 uint32_t num_entries,
                 HInstruction* input,
                 uint32_t dex_pc = kNoDexPc)
-    : HTemplateInstruction(SideEffects::None(), dex_pc),
+    : HTemplateInstruction(kPackedSwitch, SideEffects::None(), dex_pc),
       start_value_(start_value),
       num_entries_(num_entries) {
     SetRawInputAt(0, input);
@@ -3359,8 +3405,11 @@
 
 class HUnaryOperation : public HExpression<1> {
  public:
-  HUnaryOperation(DataType::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
-      : HExpression(result_type, SideEffects::None(), dex_pc) {
+  HUnaryOperation(InstructionKind kind,
+                  DataType::Type result_type,
+                  HInstruction* input,
+                  uint32_t dex_pc = kNoDexPc)
+      : HExpression(kind, result_type, SideEffects::None(), dex_pc) {
     SetRawInputAt(0, input);
   }
 
@@ -3394,12 +3443,13 @@
 
 class HBinaryOperation : public HExpression<2> {
  public:
-  HBinaryOperation(DataType::Type result_type,
+  HBinaryOperation(InstructionKind kind,
+                   DataType::Type result_type,
                    HInstruction* left,
                    HInstruction* right,
                    SideEffects side_effects = SideEffects::None(),
                    uint32_t dex_pc = kNoDexPc)
-      : HExpression(result_type, side_effects, dex_pc) {
+      : HExpression(kind, result_type, side_effects, dex_pc) {
     SetRawInputAt(0, left);
     SetRawInputAt(1, right);
   }
@@ -3498,8 +3548,16 @@
 
 class HCondition : public HBinaryOperation {
  public:
-  HCondition(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
-      : HBinaryOperation(DataType::Type::kBool, first, second, SideEffects::None(), dex_pc) {
+  HCondition(InstructionKind kind,
+             HInstruction* first,
+             HInstruction* second,
+             uint32_t dex_pc = kNoDexPc)
+      : HBinaryOperation(kind,
+                         DataType::Type::kBool,
+                         first,
+                         second,
+                         SideEffects::None(),
+                         dex_pc) {
     SetPackedField<ComparisonBiasField>(ComparisonBias::kNoBias);
   }
 
@@ -3579,7 +3637,8 @@
 class HEqual FINAL : public HCondition {
  public:
   HEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
-      : HCondition(first, second, dex_pc) {}
+      : HCondition(kEqual, first, second, dex_pc) {
+  }
 
   bool IsCommutative() const OVERRIDE { return true; }
 
@@ -3623,8 +3682,10 @@
 
 class HNotEqual FINAL : public HCondition {
  public:
-  HNotEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
-      : HCondition(first, second, dex_pc) {}
+  HNotEqual(HInstruction* first, HInstruction* second,
+            uint32_t dex_pc = kNoDexPc)
+      : HCondition(kNotEqual, first, second, dex_pc) {
+  }
 
   bool IsCommutative() const OVERRIDE { return true; }
 
@@ -3667,8 +3728,10 @@
 
 class HLessThan FINAL : public HCondition {
  public:
-  HLessThan(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
-      : HCondition(first, second, dex_pc) {}
+  HLessThan(HInstruction* first, HInstruction* second,
+            uint32_t dex_pc = kNoDexPc)
+      : HCondition(kLessThan, first, second, dex_pc) {
+  }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
     return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -3705,8 +3768,10 @@
 
 class HLessThanOrEqual FINAL : public HCondition {
  public:
-  HLessThanOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
-      : HCondition(first, second, dex_pc) {}
+  HLessThanOrEqual(HInstruction* first, HInstruction* second,
+                   uint32_t dex_pc = kNoDexPc)
+      : HCondition(kLessThanOrEqual, first, second, dex_pc) {
+  }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
     return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -3744,7 +3809,8 @@
 class HGreaterThan FINAL : public HCondition {
  public:
   HGreaterThan(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
-      : HCondition(first, second, dex_pc) {}
+      : HCondition(kGreaterThan, first, second, dex_pc) {
+  }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
     return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -3782,7 +3848,8 @@
 class HGreaterThanOrEqual FINAL : public HCondition {
  public:
   HGreaterThanOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
-      : HCondition(first, second, dex_pc) {}
+      : HCondition(kGreaterThanOrEqual, first, second, dex_pc) {
+  }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
     return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -3820,7 +3887,8 @@
 class HBelow FINAL : public HCondition {
  public:
   HBelow(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
-      : HCondition(first, second, dex_pc) {}
+      : HCondition(kBelow, first, second, dex_pc) {
+  }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
     return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -3861,7 +3929,8 @@
 class HBelowOrEqual FINAL : public HCondition {
  public:
   HBelowOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
-      : HCondition(first, second, dex_pc) {}
+      : HCondition(kBelowOrEqual, first, second, dex_pc) {
+  }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
     return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -3902,7 +3971,8 @@
 class HAbove FINAL : public HCondition {
  public:
   HAbove(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
-      : HCondition(first, second, dex_pc) {}
+      : HCondition(kAbove, first, second, dex_pc) {
+  }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
     return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -3943,7 +4013,8 @@
 class HAboveOrEqual FINAL : public HCondition {
  public:
   HAboveOrEqual(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
-      : HCondition(first, second, dex_pc) {}
+      : HCondition(kAboveOrEqual, first, second, dex_pc) {
+  }
 
   HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
     return MakeConstantCondition(Compute(x->GetValue(), y->GetValue()), GetDexPc());
@@ -3993,7 +4064,8 @@
            HInstruction* second,
            ComparisonBias bias,
            uint32_t dex_pc)
-      : HBinaryOperation(DataType::Type::kInt32,
+      : HBinaryOperation(kCompare,
+                         DataType::Type::kInt32,
                          first,
                          second,
                          SideEffectsForArchRuntimeCalls(comparison_type),
@@ -4079,7 +4151,10 @@
                const DexFile& dex_file,
                bool finalizable,
                QuickEntrypointEnum entrypoint)
-      : HExpression(DataType::Type::kReference, SideEffects::CanTriggerGC(), dex_pc),
+      : HExpression(kNewInstance,
+                    DataType::Type::kReference,
+                    SideEffects::CanTriggerGC(),
+                    dex_pc),
         type_index_(type_index),
         dex_file_(dex_file),
         entrypoint_(entrypoint) {
@@ -4235,7 +4310,8 @@
   using InvokeTypeField = BitField<InvokeType, kFieldInvokeType, kFieldInvokeTypeSize>;
   using ReturnTypeField = BitField<DataType::Type, kFieldReturnType, kFieldReturnTypeSize>;
 
-  HInvoke(ArenaAllocator* allocator,
+  HInvoke(InstructionKind kind,
+          ArenaAllocator* allocator,
           uint32_t number_of_arguments,
           uint32_t number_of_other_inputs,
           DataType::Type return_type,
@@ -4244,6 +4320,7 @@
           ArtMethod* resolved_method,
           InvokeType invoke_type)
     : HVariableInputSizeInstruction(
+          kind,
           SideEffects::AllExceptGCDependency(),  // Assume write/read on all fields/arrays.
           dex_pc,
           allocator,
@@ -4278,7 +4355,8 @@
                     uint32_t dex_pc,
                     uint32_t dex_method_index,
                     InvokeType invoke_type)
-      : HInvoke(allocator,
+      : HInvoke(kInvokeUnresolved,
+                allocator,
                 number_of_arguments,
                 0u /* number_of_other_inputs */,
                 return_type,
@@ -4303,14 +4381,16 @@
                      DataType::Type return_type,
                      uint32_t dex_pc,
                      uint32_t dex_method_index)
-      : HInvoke(allocator,
+      : HInvoke(kInvokePolymorphic,
+                allocator,
                 number_of_arguments,
                 0u /* number_of_other_inputs */,
                 return_type,
                 dex_pc,
                 dex_method_index,
                 nullptr,
-                kVirtual) {}
+                kVirtual) {
+  }
 
   bool IsClonable() const OVERRIDE { return true; }
 
@@ -4387,7 +4467,8 @@
                         InvokeType invoke_type,
                         MethodReference target_method,
                         ClinitCheckRequirement clinit_check_requirement)
-      : HInvoke(allocator,
+      : HInvoke(kInvokeStaticOrDirect,
+                allocator,
                 number_of_arguments,
                 // There is potentially one extra argument for the HCurrentMethod node, and
                 // potentially one other if the clinit check is explicit, and potentially
@@ -4582,7 +4663,8 @@
                  uint32_t dex_method_index,
                  ArtMethod* resolved_method,
                  uint32_t vtable_index)
-      : HInvoke(allocator,
+      : HInvoke(kInvokeVirtual,
+                allocator,
                 number_of_arguments,
                 0u,
                 return_type,
@@ -4590,7 +4672,8 @@
                 dex_method_index,
                 resolved_method,
                 kVirtual),
-        vtable_index_(vtable_index) {}
+        vtable_index_(vtable_index) {
+  }
 
   bool IsClonable() const OVERRIDE { return true; }
 
@@ -4633,7 +4716,8 @@
                    uint32_t dex_method_index,
                    ArtMethod* resolved_method,
                    uint32_t imt_index)
-      : HInvoke(allocator,
+      : HInvoke(kInvokeInterface,
+                allocator,
                 number_of_arguments,
                 0u,
                 return_type,
@@ -4641,7 +4725,8 @@
                 dex_method_index,
                 resolved_method,
                 kInterface),
-        imt_index_(imt_index) {}
+        imt_index_(imt_index) {
+  }
 
   bool IsClonable() const OVERRIDE { return true; }
 
@@ -4670,7 +4755,7 @@
 class HNeg FINAL : public HUnaryOperation {
  public:
   HNeg(DataType::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
-      : HUnaryOperation(result_type, input, dex_pc) {
+      : HUnaryOperation(kNeg, result_type, input, dex_pc) {
     DCHECK_EQ(result_type, DataType::Kind(input->GetType()));
   }
 
@@ -4698,7 +4783,7 @@
 class HNewArray FINAL : public HExpression<2> {
  public:
   HNewArray(HInstruction* cls, HInstruction* length, uint32_t dex_pc)
-      : HExpression(DataType::Type::kReference, SideEffects::CanTriggerGC(), dex_pc) {
+      : HExpression(kNewArray, DataType::Type::kReference, SideEffects::CanTriggerGC(), dex_pc) {
     SetRawInputAt(0, cls);
     SetRawInputAt(1, length);
   }
@@ -4734,7 +4819,8 @@
        HInstruction* left,
        HInstruction* right,
        uint32_t dex_pc = kNoDexPc)
-      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+      : HBinaryOperation(kAdd, result_type, left, right, SideEffects::None(), dex_pc) {
+  }
 
   bool IsCommutative() const OVERRIDE { return true; }
 
@@ -4769,7 +4855,8 @@
        HInstruction* left,
        HInstruction* right,
        uint32_t dex_pc = kNoDexPc)
-      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+      : HBinaryOperation(kSub, result_type, left, right, SideEffects::None(), dex_pc) {
+  }
 
   template <typename T> static T Compute(T x, T y) { return x - y; }
 
@@ -4802,7 +4889,8 @@
        HInstruction* left,
        HInstruction* right,
        uint32_t dex_pc = kNoDexPc)
-      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+      : HBinaryOperation(kMul, result_type, left, right, SideEffects::None(), dex_pc) {
+  }
 
   bool IsCommutative() const OVERRIDE { return true; }
 
@@ -4837,7 +4925,8 @@
        HInstruction* left,
        HInstruction* right,
        uint32_t dex_pc)
-      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+      : HBinaryOperation(kDiv, result_type, left, right, SideEffects::None(), dex_pc) {
+  }
 
   template <typename T>
   T ComputeIntegral(T x, T y) const {
@@ -4884,7 +4973,8 @@
        HInstruction* left,
        HInstruction* right,
        uint32_t dex_pc)
-      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+      : HBinaryOperation(kRem, result_type, left, right, SideEffects::None(), dex_pc) {
+  }
 
   template <typename T>
   T ComputeIntegral(T x, T y) const {
@@ -4930,7 +5020,7 @@
   // `HDivZeroCheck` can trigger GC, as it may call the `ArithmeticException`
   // constructor.
   HDivZeroCheck(HInstruction* value, uint32_t dex_pc)
-      : HExpression(value->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
+      : HExpression(kDivZeroCheck, value->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
     SetRawInputAt(0, value);
   }
 
@@ -4957,7 +5047,7 @@
        HInstruction* value,
        HInstruction* distance,
        uint32_t dex_pc = kNoDexPc)
-      : HBinaryOperation(result_type, value, distance, SideEffects::None(), dex_pc) {
+      : HBinaryOperation(kShl, result_type, value, distance, SideEffects::None(), dex_pc) {
     DCHECK_EQ(result_type, DataType::Kind(value->GetType()));
     DCHECK_EQ(DataType::Type::kInt32, DataType::Kind(distance->GetType()));
   }
@@ -5003,7 +5093,7 @@
        HInstruction* value,
        HInstruction* distance,
        uint32_t dex_pc = kNoDexPc)
-      : HBinaryOperation(result_type, value, distance, SideEffects::None(), dex_pc) {
+      : HBinaryOperation(kShr, result_type, value, distance, SideEffects::None(), dex_pc) {
     DCHECK_EQ(result_type, DataType::Kind(value->GetType()));
     DCHECK_EQ(DataType::Type::kInt32, DataType::Kind(distance->GetType()));
   }
@@ -5049,7 +5139,7 @@
         HInstruction* value,
         HInstruction* distance,
         uint32_t dex_pc = kNoDexPc)
-      : HBinaryOperation(result_type, value, distance, SideEffects::None(), dex_pc) {
+      : HBinaryOperation(kUShr, result_type, value, distance, SideEffects::None(), dex_pc) {
     DCHECK_EQ(result_type, DataType::Kind(value->GetType()));
     DCHECK_EQ(DataType::Type::kInt32, DataType::Kind(distance->GetType()));
   }
@@ -5097,7 +5187,8 @@
        HInstruction* left,
        HInstruction* right,
        uint32_t dex_pc = kNoDexPc)
-      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+      : HBinaryOperation(kAnd, result_type, left, right, SideEffects::None(), dex_pc) {
+  }
 
   bool IsCommutative() const OVERRIDE { return true; }
 
@@ -5134,7 +5225,8 @@
       HInstruction* left,
       HInstruction* right,
       uint32_t dex_pc = kNoDexPc)
-      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+      : HBinaryOperation(kOr, result_type, left, right, SideEffects::None(), dex_pc) {
+  }
 
   bool IsCommutative() const OVERRIDE { return true; }
 
@@ -5171,7 +5263,8 @@
        HInstruction* left,
        HInstruction* right,
        uint32_t dex_pc = kNoDexPc)
-      : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {}
+      : HBinaryOperation(kXor, result_type, left, right, SideEffects::None(), dex_pc) {
+  }
 
   bool IsCommutative() const OVERRIDE { return true; }
 
@@ -5205,7 +5298,7 @@
 class HRor FINAL : public HBinaryOperation {
  public:
   HRor(DataType::Type result_type, HInstruction* value, HInstruction* distance)
-    : HBinaryOperation(result_type, value, distance) {
+      : HBinaryOperation(kRor, result_type, value, distance) {
     DCHECK_EQ(result_type, DataType::Kind(value->GetType()));
     DCHECK_EQ(DataType::Type::kInt32, DataType::Kind(distance->GetType()));
   }
@@ -5262,7 +5355,7 @@
                   uint8_t index,
                   DataType::Type parameter_type,
                   bool is_this = false)
-      : HExpression(parameter_type, SideEffects::None(), kNoDexPc),
+      : HExpression(kParameterValue, parameter_type, SideEffects::None(), kNoDexPc),
         dex_file_(dex_file),
         type_index_(type_index),
         index_(index) {
@@ -5301,7 +5394,8 @@
 class HNot FINAL : public HUnaryOperation {
  public:
   HNot(DataType::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
-      : HUnaryOperation(result_type, input, dex_pc) {}
+      : HUnaryOperation(kNot, result_type, input, dex_pc) {
+  }
 
   bool CanBeMoved() const OVERRIDE { return true; }
   bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
@@ -5334,7 +5428,8 @@
 class HBooleanNot FINAL : public HUnaryOperation {
  public:
   explicit HBooleanNot(HInstruction* input, uint32_t dex_pc = kNoDexPc)
-      : HUnaryOperation(DataType::Type::kBool, input, dex_pc) {}
+      : HUnaryOperation(kBooleanNot, DataType::Type::kBool, input, dex_pc) {
+  }
 
   bool CanBeMoved() const OVERRIDE { return true; }
   bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
@@ -5372,7 +5467,7 @@
  public:
   // Instantiate a type conversion of `input` to `result_type`.
   HTypeConversion(DataType::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
-      : HExpression(result_type, SideEffects::None(), dex_pc) {
+      : HExpression(kTypeConversion, result_type, SideEffects::None(), dex_pc) {
     SetRawInputAt(0, input);
     // Invariant: We should never generate a conversion to a Boolean value.
     DCHECK_NE(DataType::Type::kBool, result_type);
@@ -5404,7 +5499,7 @@
   // `HNullCheck` can trigger GC, as it may call the `NullPointerException`
   // constructor.
   HNullCheck(HInstruction* value, uint32_t dex_pc)
-      : HExpression(value->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
+      : HExpression(kNullCheck, value->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
     SetRawInputAt(0, value);
   }
 
@@ -5474,7 +5569,10 @@
                     uint16_t declaring_class_def_index,
                     const DexFile& dex_file,
                     uint32_t dex_pc)
-      : HExpression(field_type, SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
+      : HExpression(kInstanceFieldGet,
+                    field_type,
+                    SideEffects::FieldReadOfType(field_type, is_volatile),
+                    dex_pc),
         field_info_(field,
                     field_offset,
                     field_type,
@@ -5534,7 +5632,9 @@
                     uint16_t declaring_class_def_index,
                     const DexFile& dex_file,
                     uint32_t dex_pc)
-      : HTemplateInstruction(SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
+      : HTemplateInstruction(kInstanceFieldSet,
+                             SideEffects::FieldWriteOfType(field_type, is_volatile),
+                             dex_pc),
         field_info_(field,
                     field_offset,
                     field_type,
@@ -5586,7 +5686,8 @@
                  type,
                  SideEffects::ArrayReadOfType(type),
                  dex_pc,
-                 /* is_string_char_at */ false) {}
+                 /* is_string_char_at */ false) {
+  }
 
   HArrayGet(HInstruction* array,
             HInstruction* index,
@@ -5594,7 +5695,7 @@
             SideEffects side_effects,
             uint32_t dex_pc,
             bool is_string_char_at)
-      : HExpression(type, side_effects, dex_pc) {
+      : HExpression(kArrayGet, type, side_effects, dex_pc) {
     SetPackedFlag<kFlagIsStringCharAt>(is_string_char_at);
     SetRawInputAt(0, array);
     SetRawInputAt(1, index);
@@ -5678,7 +5779,8 @@
                   expected_component_type,
                   // Make a best guess for side effects now, may be refined during SSA building.
                   ComputeSideEffects(GetComponentType(value->GetType(), expected_component_type)),
-                  dex_pc) {}
+                  dex_pc) {
+  }
 
   HArraySet(HInstruction* array,
             HInstruction* index,
@@ -5686,7 +5788,7 @@
             DataType::Type expected_component_type,
             SideEffects side_effects,
             uint32_t dex_pc)
-      : HTemplateInstruction(side_effects, dex_pc) {
+      : HTemplateInstruction(kArraySet, side_effects, dex_pc) {
     SetPackedField<ExpectedComponentTypeField>(expected_component_type);
     SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == DataType::Type::kReference);
     SetPackedFlag<kFlagValueCanBeNull>(true);
@@ -5786,7 +5888,7 @@
 class HArrayLength FINAL : public HExpression<1> {
  public:
   HArrayLength(HInstruction* array, uint32_t dex_pc, bool is_string_length = false)
-      : HExpression(DataType::Type::kInt32, SideEffects::None(), dex_pc) {
+      : HExpression(kArrayLength, DataType::Type::kInt32, SideEffects::None(), dex_pc) {
     SetPackedFlag<kFlagIsStringLength>(is_string_length);
     // Note that arrays do not change length, so the instruction does not
     // depend on any write.
@@ -5829,7 +5931,7 @@
                HInstruction* length,
                uint32_t dex_pc,
                bool is_string_char_at = false)
-      : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
+      : HExpression(kBoundsCheck, index->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
     DCHECK_EQ(DataType::Type::kInt32, DataType::Kind(index->GetType()));
     SetPackedFlag<kFlagIsStringCharAt>(is_string_char_at);
     SetRawInputAt(0, index);
@@ -5862,7 +5964,9 @@
 class HSuspendCheck FINAL : public HTemplateInstruction<0> {
  public:
   explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc), slow_path_(nullptr) {}
+      : HTemplateInstruction(kSuspendCheck, SideEffects::CanTriggerGC(), dex_pc),
+        slow_path_(nullptr) {
+  }
 
   bool IsClonable() const OVERRIDE { return true; }
 
@@ -5889,7 +5993,8 @@
 class HNativeDebugInfo : public HTemplateInstruction<0> {
  public:
   explicit HNativeDebugInfo(uint32_t dex_pc)
-      : HTemplateInstruction<0>(SideEffects::None(), dex_pc) {}
+      : HTemplateInstruction<0>(kNativeDebugInfo, SideEffects::None(), dex_pc) {
+  }
 
   bool NeedsEnvironment() const OVERRIDE {
     return true;
@@ -5947,7 +6052,7 @@
              bool is_referrers_class,
              uint32_t dex_pc,
              bool needs_access_check)
-      : HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc),
+      : HInstruction(kLoadClass, SideEffectsForArchRuntimeCalls(), dex_pc),
         special_input_(HUserRecord<HInstruction*>(current_method)),
         type_index_(type_index),
         dex_file_(dex_file),
@@ -6163,7 +6268,7 @@
               dex::StringIndex string_index,
               const DexFile& dex_file,
               uint32_t dex_pc)
-      : HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc),
+      : HInstruction(kLoadString, SideEffectsForArchRuntimeCalls(), dex_pc),
         special_input_(HUserRecord<HInstruction*>(current_method)),
         string_index_(string_index),
         dex_file_(dex_file) {
@@ -6300,6 +6405,7 @@
  public:
   HClinitCheck(HLoadClass* constant, uint32_t dex_pc)
       : HExpression(
+            kClinitCheck,
             DataType::Type::kReference,
             SideEffects::AllExceptGCDependency(),  // Assume write/read on all fields/arrays.
             dex_pc) {
@@ -6342,7 +6448,10 @@
                   uint16_t declaring_class_def_index,
                   const DexFile& dex_file,
                   uint32_t dex_pc)
-      : HExpression(field_type, SideEffects::FieldReadOfType(field_type, is_volatile), dex_pc),
+      : HExpression(kStaticFieldGet,
+                    field_type,
+                    SideEffects::FieldReadOfType(field_type, is_volatile),
+                    dex_pc),
         field_info_(field,
                     field_offset,
                     field_type,
@@ -6399,7 +6508,9 @@
                   uint16_t declaring_class_def_index,
                   const DexFile& dex_file,
                   uint32_t dex_pc)
-      : HTemplateInstruction(SideEffects::FieldWriteOfType(field_type, is_volatile), dex_pc),
+      : HTemplateInstruction(kStaticFieldSet,
+                             SideEffects::FieldWriteOfType(field_type, is_volatile),
+                             dex_pc),
         field_info_(field,
                     field_offset,
                     field_type,
@@ -6442,7 +6553,10 @@
                               DataType::Type field_type,
                               uint32_t field_index,
                               uint32_t dex_pc)
-      : HExpression(field_type, SideEffects::AllExceptGCDependency(), dex_pc),
+      : HExpression(kUnresolvedInstanceFieldGet,
+                    field_type,
+                    SideEffects::AllExceptGCDependency(),
+                    dex_pc),
         field_index_(field_index) {
     SetRawInputAt(0, obj);
   }
@@ -6470,7 +6584,9 @@
                               DataType::Type field_type,
                               uint32_t field_index,
                               uint32_t dex_pc)
-      : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
+      : HTemplateInstruction(kUnresolvedInstanceFieldSet,
+                             SideEffects::AllExceptGCDependency(),
+                             dex_pc),
         field_index_(field_index) {
     SetPackedField<FieldTypeField>(field_type);
     DCHECK_EQ(DataType::Kind(field_type), DataType::Kind(value->GetType()));
@@ -6508,7 +6624,10 @@
   HUnresolvedStaticFieldGet(DataType::Type field_type,
                             uint32_t field_index,
                             uint32_t dex_pc)
-      : HExpression(field_type, SideEffects::AllExceptGCDependency(), dex_pc),
+      : HExpression(kUnresolvedStaticFieldGet,
+                    field_type,
+                    SideEffects::AllExceptGCDependency(),
+                    dex_pc),
         field_index_(field_index) {
   }
 
@@ -6534,7 +6653,9 @@
                             DataType::Type field_type,
                             uint32_t field_index,
                             uint32_t dex_pc)
-      : HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
+      : HTemplateInstruction(kUnresolvedStaticFieldSet,
+                             SideEffects::AllExceptGCDependency(),
+                             dex_pc),
         field_index_(field_index) {
     SetPackedField<FieldTypeField>(field_type);
     DCHECK_EQ(DataType::Kind(field_type), DataType::Kind(value->GetType()));
@@ -6570,7 +6691,8 @@
 class HLoadException FINAL : public HExpression<0> {
  public:
   explicit HLoadException(uint32_t dex_pc = kNoDexPc)
-      : HExpression(DataType::Type::kReference, SideEffects::None(), dex_pc) {}
+      : HExpression(kLoadException, DataType::Type::kReference, SideEffects::None(), dex_pc) {
+  }
 
   bool CanBeNull() const OVERRIDE { return false; }
 
@@ -6585,7 +6707,8 @@
 class HClearException FINAL : public HTemplateInstruction<0> {
  public:
   explicit HClearException(uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(SideEffects::AllWrites(), dex_pc) {}
+      : HTemplateInstruction(kClearException, SideEffects::AllWrites(), dex_pc) {
+  }
 
   DECLARE_INSTRUCTION(ClearException);
 
@@ -6596,7 +6719,7 @@
 class HThrow FINAL : public HTemplateInstruction<1> {
  public:
   HThrow(HInstruction* exception, uint32_t dex_pc)
-      : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) {
+      : HTemplateInstruction(kThrow, SideEffects::CanTriggerGC(), dex_pc) {
     SetRawInputAt(0, exception);
   }
 
@@ -6637,7 +6760,8 @@
               HLoadClass* target_class,
               TypeCheckKind check_kind,
               uint32_t dex_pc)
-      : HExpression(DataType::Type::kBool,
+      : HExpression(kInstanceOf,
+                    DataType::Type::kBool,
                     SideEffectsForArchRuntimeCalls(check_kind),
                     dex_pc) {
     SetPackedField<TypeCheckKindField>(check_kind);
@@ -6696,7 +6820,7 @@
 class HBoundType FINAL : public HExpression<1> {
  public:
   explicit HBoundType(HInstruction* input, uint32_t dex_pc = kNoDexPc)
-      : HExpression(DataType::Type::kReference, SideEffects::None(), dex_pc),
+      : HExpression(kBoundType, DataType::Type::kReference, SideEffects::None(), dex_pc),
         upper_bound_(ReferenceTypeInfo::CreateInvalid()) {
     SetPackedFlag<kFlagUpperCanBeNull>(true);
     SetPackedFlag<kFlagCanBeNull>(true);
@@ -6746,7 +6870,7 @@
              HLoadClass* target_class,
              TypeCheckKind check_kind,
              uint32_t dex_pc)
-      : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) {
+      : HTemplateInstruction(kCheckCast, SideEffects::CanTriggerGC(), dex_pc) {
     SetPackedField<TypeCheckKindField>(check_kind);
     SetPackedFlag<kFlagMustDoNullCheck>(true);
     SetRawInputAt(0, object);
@@ -6823,7 +6947,9 @@
  public:
   explicit HMemoryBarrier(MemBarrierKind barrier_kind, uint32_t dex_pc = kNoDexPc)
       : HTemplateInstruction(
-            SideEffects::AllWritesAndReads(), dex_pc) {  // Assume write/read on all fields/arrays.
+            kMemoryBarrier,
+            SideEffects::AllWritesAndReads(),  // Assume write/read on all fields/arrays.
+            dex_pc) {
     SetPackedField<BarrierKindField>(barrier_kind);
   }
 
@@ -6942,7 +7068,8 @@
     //
     // If in a later phase we discover that there are no writes to reference final fields,
     // we can refine the side effect to a smaller set of type reads (see above constraints).
-      : HVariableInputSizeInstruction(SideEffects::AllReads(),
+      : HVariableInputSizeInstruction(kConstructorFence,
+                                      SideEffects::AllReads(),
                                       dex_pc,
                                       allocator,
                                       /* number_of_inputs */ 1,
@@ -7009,6 +7136,7 @@
 
   HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc)
     : HTemplateInstruction(
+          kMonitorOperation,
           SideEffects::AllExceptGCDependency(),  // Assume write/read on all fields/arrays.
           dex_pc) {
     SetPackedField<OperationKindField>(kind);
@@ -7050,7 +7178,7 @@
           HInstruction* true_value,
           HInstruction* false_value,
           uint32_t dex_pc)
-      : HExpression(HPhi::ToPhiType(true_value->GetType()), SideEffects::None(), dex_pc) {
+      : HExpression(kSelect, HPhi::ToPhiType(true_value->GetType()), SideEffects::None(), dex_pc) {
     DCHECK_EQ(HPhi::ToPhiType(true_value->GetType()), HPhi::ToPhiType(false_value->GetType()));
 
     // First input must be `true_value` or `false_value` to allow codegens to
@@ -7163,7 +7291,7 @@
 class HParallelMove FINAL : public HTemplateInstruction<0> {
  public:
   explicit HParallelMove(ArenaAllocator* allocator, uint32_t dex_pc = kNoDexPc)
-      : HTemplateInstruction(SideEffects::None(), dex_pc),
+      : HTemplateInstruction(kParallelMove, SideEffects::None(), dex_pc),
         moves_(allocator->Adapter(kArenaAllocMoveOperands)) {
     moves_.reserve(kDefaultNumberOfMoves);
   }
@@ -7225,7 +7353,10 @@
 class HIntermediateAddress FINAL : public HExpression<2> {
  public:
   HIntermediateAddress(HInstruction* base_address, HInstruction* offset, uint32_t dex_pc)
-      : HExpression(DataType::Type::kInt32, SideEffects::DependsOnGC(), dex_pc) {
+      : HExpression(kIntermediateAddress,
+                    DataType::Type::kInt32,
+                    SideEffects::DependsOnGC(),
+                    dex_pc) {
         DCHECK_EQ(DataType::Size(DataType::Type::kInt32),
                   DataType::Size(DataType::Type::kReference))
             << "kPrimInt and kPrimNot have different sizes.";
diff --git a/compiler/optimizing/nodes_mips.h b/compiler/optimizing/nodes_mips.h
index 2c0595e..d0e0fef 100644
--- a/compiler/optimizing/nodes_mips.h
+++ b/compiler/optimizing/nodes_mips.h
@@ -24,7 +24,11 @@
  public:
   // Treat the value as an int32_t, but it is really a 32 bit native pointer.
   HMipsComputeBaseMethodAddress()
-      : HExpression(DataType::Type::kInt32, SideEffects::None(), kNoDexPc) {}
+      : HExpression(kMipsComputeBaseMethodAddress,
+                    DataType::Type::kInt32,
+                    SideEffects::None(),
+                    kNoDexPc) {
+  }
 
   bool CanBeMoved() const OVERRIDE { return true; }
 
@@ -42,7 +46,7 @@
                     HInstruction* input,
                     HMipsComputeBaseMethodAddress* method_base,
                     uint32_t dex_pc)
-    : HTemplateInstruction(SideEffects::None(), dex_pc),
+    : HTemplateInstruction(kMipsPackedSwitch, SideEffects::None(), dex_pc),
       start_value_(start_value),
       num_entries_(num_entries) {
     SetRawInputAt(0, input);
@@ -90,7 +94,10 @@
 class HIntermediateArrayAddressIndex FINAL : public HExpression<2> {
  public:
   HIntermediateArrayAddressIndex(HInstruction* index, HInstruction* shift, uint32_t dex_pc)
-      : HExpression(DataType::Type::kInt32, SideEffects::None(), dex_pc) {
+      : HExpression(kIntermediateArrayAddressIndex,
+                    DataType::Type::kInt32,
+                    SideEffects::None(),
+                    dex_pc) {
     SetRawInputAt(0, index);
     SetRawInputAt(1, shift);
   }
diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h
index e837f1e..29358e1 100644
--- a/compiler/optimizing/nodes_shared.h
+++ b/compiler/optimizing/nodes_shared.h
@@ -32,7 +32,8 @@
                       HInstruction* mul_left,
                       HInstruction* mul_right,
                       uint32_t dex_pc = kNoDexPc)
-      : HExpression(type, SideEffects::None(), dex_pc), op_kind_(op) {
+      : HExpression(kMultiplyAccumulate, type, SideEffects::None(), dex_pc),
+        op_kind_(op) {
     SetRawInputAt(kInputAccumulatorIndex, accumulator);
     SetRawInputAt(kInputMulLeftIndex, mul_left);
     SetRawInputAt(kInputMulRightIndex, mul_right);
@@ -68,7 +69,12 @@
                        HInstruction* left,
                        HInstruction* right,
                        uint32_t dex_pc = kNoDexPc)
-    : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc),
+    : HBinaryOperation(kBitwiseNegatedRight,
+                       result_type,
+                       left,
+                       right,
+                       SideEffects::None(),
+                       dex_pc),
       op_kind_(op) {
     DCHECK(op == HInstruction::kAnd || op == HInstruction::kOr || op == HInstruction::kXor) << op;
   }
@@ -143,7 +149,10 @@
  public:
   HIntermediateAddressIndex(
       HInstruction* index, HInstruction* offset, HInstruction* shift, uint32_t dex_pc)
-      : HExpression(DataType::Type::kInt32, SideEffects::None(), dex_pc) {
+      : HExpression(kIntermediateAddressIndex,
+                    DataType::Type::kInt32,
+                    SideEffects::None(),
+                    dex_pc) {
     SetRawInputAt(0, index);
     SetRawInputAt(1, offset);
     SetRawInputAt(2, shift);
@@ -193,7 +202,7 @@
                          // is an extension.
                          int shift = 0,
                          uint32_t dex_pc = kNoDexPc)
-      : HExpression(instr->GetType(), SideEffects::None(), dex_pc),
+      : HExpression(kDataProcWithShifterOp, instr->GetType(), SideEffects::None(), dex_pc),
         instr_kind_(instr->GetKind()), op_kind_(op),
         shift_amount_(shift & (instr->GetType() == DataType::Type::kInt32
             ? kMaxIntShiftDistance
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index ecabdf3..0d38d57 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -71,13 +71,15 @@
   // TODO: we could introduce SIMD types in HIR.
   static constexpr DataType::Type kSIMDType = DataType::Type::kFloat64;
 
-  HVecOperation(ArenaAllocator* allocator,
+  HVecOperation(InstructionKind kind,
+                ArenaAllocator* allocator,
                 DataType::Type packed_type,
                 SideEffects side_effects,
                 size_t number_of_inputs,
                 size_t vector_length,
                 uint32_t dex_pc)
-      : HVariableInputSizeInstruction(side_effects,
+      : HVariableInputSizeInstruction(kind,
+                                      side_effects,
                                       dex_pc,
                                       allocator,
                                       number_of_inputs,
@@ -196,12 +198,14 @@
 // Abstraction of a unary vector operation.
 class HVecUnaryOperation : public HVecOperation {
  public:
-  HVecUnaryOperation(ArenaAllocator* allocator,
+  HVecUnaryOperation(InstructionKind kind,
+                     ArenaAllocator* allocator,
                      HInstruction* input,
                      DataType::Type packed_type,
                      size_t vector_length,
                      uint32_t dex_pc)
-      : HVecOperation(allocator,
+      : HVecOperation(kind,
+                      allocator,
                       packed_type,
                       SideEffects::None(),
                       /* number_of_inputs */ 1,
@@ -221,13 +225,15 @@
 // Abstraction of a binary vector operation.
 class HVecBinaryOperation : public HVecOperation {
  public:
-  HVecBinaryOperation(ArenaAllocator* allocator,
+  HVecBinaryOperation(InstructionKind kind,
+                      ArenaAllocator* allocator,
                       HInstruction* left,
                       HInstruction* right,
                       DataType::Type packed_type,
                       size_t vector_length,
                       uint32_t dex_pc)
-      : HVecOperation(allocator,
+      : HVecOperation(kind,
+                      allocator,
                       packed_type,
                       SideEffects::None(),
                       /* number_of_inputs */ 2,
@@ -250,13 +256,15 @@
 // The Android runtime guarantees elements have at least natural alignment.
 class HVecMemoryOperation : public HVecOperation {
  public:
-  HVecMemoryOperation(ArenaAllocator* allocator,
+  HVecMemoryOperation(InstructionKind kind,
+                      ArenaAllocator* allocator,
                       DataType::Type packed_type,
                       SideEffects side_effects,
                       size_t number_of_inputs,
                       size_t vector_length,
                       uint32_t dex_pc)
-      : HVecOperation(allocator,
+      : HVecOperation(kind,
+                      allocator,
                       packed_type,
                       side_effects,
                       number_of_inputs,
@@ -315,7 +323,8 @@
                       DataType::Type packed_type,
                       size_t vector_length,
                       uint32_t dex_pc)
-      : HVecUnaryOperation(allocator, scalar, packed_type, vector_length, dex_pc) {
+      : HVecUnaryOperation(
+            kVecReplicateScalar, allocator, scalar, packed_type, vector_length, dex_pc) {
     DCHECK(!scalar->IsVecOperation());
   }
 
@@ -341,7 +350,8 @@
                     size_t vector_length,
                     size_t index,
                     uint32_t dex_pc)
-      : HVecUnaryOperation(allocator, input, packed_type, vector_length, dex_pc) {
+      : HVecUnaryOperation(
+            kVecExtractScalar, allocator, input, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(input, packed_type));
     DCHECK_LT(index, vector_length);
     DCHECK_EQ(index, 0u);
@@ -379,7 +389,7 @@
              size_t vector_length,
              ReductionKind kind,
              uint32_t dex_pc)
-      : HVecUnaryOperation(allocator, input, packed_type, vector_length, dex_pc),
+      : HVecUnaryOperation(kVecReduce, allocator, input, packed_type, vector_length, dex_pc),
         kind_(kind) {
     DCHECK(HasConsistentPackedTypes(input, packed_type));
   }
@@ -412,7 +422,7 @@
           DataType::Type packed_type,
           size_t vector_length,
           uint32_t dex_pc)
-      : HVecUnaryOperation(allocator, input, packed_type, vector_length, dex_pc) {
+      : HVecUnaryOperation(kVecCnv, allocator, input, packed_type, vector_length, dex_pc) {
     DCHECK(input->IsVecOperation());
     DCHECK_NE(GetInputType(), GetResultType());  // actual convert
   }
@@ -437,7 +447,7 @@
           DataType::Type packed_type,
           size_t vector_length,
           uint32_t dex_pc)
-      : HVecUnaryOperation(allocator, input, packed_type, vector_length, dex_pc) {
+      : HVecUnaryOperation(kVecNeg, allocator, input, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(input, packed_type));
   }
 
@@ -459,7 +469,7 @@
           DataType::Type packed_type,
           size_t vector_length,
           uint32_t dex_pc)
-      : HVecUnaryOperation(allocator, input, packed_type, vector_length, dex_pc) {
+      : HVecUnaryOperation(kVecAbs, allocator, input, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(input, packed_type));
   }
 
@@ -481,7 +491,7 @@
           DataType::Type packed_type,
           size_t vector_length,
           uint32_t dex_pc)
-      : HVecUnaryOperation(allocator, input, packed_type, vector_length, dex_pc) {
+      : HVecUnaryOperation(kVecNot, allocator, input, packed_type, vector_length, dex_pc) {
     DCHECK(input->IsVecOperation());
   }
 
@@ -507,7 +517,7 @@
           DataType::Type packed_type,
           size_t vector_length,
           uint32_t dex_pc)
-      : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+      : HVecBinaryOperation(kVecAdd, allocator, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(left, packed_type));
     DCHECK(HasConsistentPackedTypes(right, packed_type));
   }
@@ -533,7 +543,8 @@
                  size_t vector_length,
                  bool is_rounded,
                  uint32_t dex_pc)
-      : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+      : HVecBinaryOperation(
+            kVecHalvingAdd, allocator, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(left, packed_type));
     DCHECK(HasConsistentPackedTypes(right, packed_type));
     SetPackedFlag<kFieldHAddIsRounded>(is_rounded);
@@ -571,7 +582,7 @@
           DataType::Type packed_type,
           size_t vector_length,
           uint32_t dex_pc)
-      : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+      : HVecBinaryOperation(kVecSub, allocator, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(left, packed_type));
     DCHECK(HasConsistentPackedTypes(right, packed_type));
   }
@@ -594,7 +605,7 @@
           DataType::Type packed_type,
           size_t vector_length,
           uint32_t dex_pc)
-      : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+      : HVecBinaryOperation(kVecMul, allocator, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(left, packed_type));
     DCHECK(HasConsistentPackedTypes(right, packed_type));
   }
@@ -617,7 +628,7 @@
           DataType::Type packed_type,
           size_t vector_length,
           uint32_t dex_pc)
-      : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+      : HVecBinaryOperation(kVecDiv, allocator, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(left, packed_type));
     DCHECK(HasConsistentPackedTypes(right, packed_type));
   }
@@ -641,7 +652,7 @@
           DataType::Type packed_type,
           size_t vector_length,
           uint32_t dex_pc)
-      : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+      : HVecBinaryOperation(kVecMin, allocator, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(left, packed_type));
     DCHECK(HasConsistentPackedTypes(right, packed_type));
   }
@@ -665,7 +676,7 @@
           DataType::Type packed_type,
           size_t vector_length,
           uint32_t dex_pc)
-      : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+      : HVecBinaryOperation(kVecMax, allocator, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(left, packed_type));
     DCHECK(HasConsistentPackedTypes(right, packed_type));
   }
@@ -688,7 +699,7 @@
           DataType::Type packed_type,
           size_t vector_length,
           uint32_t dex_pc)
-      : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+      : HVecBinaryOperation(kVecAnd, allocator, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(left->IsVecOperation() && right->IsVecOperation());
   }
 
@@ -710,7 +721,8 @@
              DataType::Type packed_type,
              size_t vector_length,
              uint32_t dex_pc)
-         : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+         : HVecBinaryOperation(
+               kVecAndNot, allocator, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(left->IsVecOperation() && right->IsVecOperation());
   }
 
@@ -732,7 +744,7 @@
          DataType::Type packed_type,
          size_t vector_length,
          uint32_t dex_pc)
-      : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+      : HVecBinaryOperation(kVecOr, allocator, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(left->IsVecOperation() && right->IsVecOperation());
   }
 
@@ -754,7 +766,7 @@
           DataType::Type packed_type,
           size_t vector_length,
           uint32_t dex_pc)
-      : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+      : HVecBinaryOperation(kVecXor, allocator, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(left->IsVecOperation() && right->IsVecOperation());
   }
 
@@ -776,7 +788,7 @@
           DataType::Type packed_type,
           size_t vector_length,
           uint32_t dex_pc)
-      : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+      : HVecBinaryOperation(kVecShl, allocator, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(left, packed_type));
   }
 
@@ -798,7 +810,7 @@
           DataType::Type packed_type,
           size_t vector_length,
           uint32_t dex_pc)
-      : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+      : HVecBinaryOperation(kVecShr, allocator, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(left, packed_type));
   }
 
@@ -820,7 +832,7 @@
            DataType::Type packed_type,
            size_t vector_length,
            uint32_t dex_pc)
-      : HVecBinaryOperation(allocator, left, right, packed_type, vector_length, dex_pc) {
+      : HVecBinaryOperation(kVecUShr, allocator, left, right, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(left, packed_type));
   }
 
@@ -847,7 +859,8 @@
                  size_t vector_length,
                  size_t number_of_scalars,
                  uint32_t dex_pc)
-      : HVecOperation(allocator,
+      : HVecOperation(kVecSetScalars,
+                      allocator,
                       packed_type,
                       SideEffects::None(),
                       number_of_scalars,
@@ -881,7 +894,8 @@
                          DataType::Type packed_type,
                          size_t vector_length,
                          uint32_t dex_pc)
-      : HVecOperation(allocator,
+      : HVecOperation(kVecMultiplyAccumulate,
+                      allocator,
                       packed_type,
                       SideEffects::None(),
                       /* number_of_inputs */ 3,
@@ -931,7 +945,8 @@
                     DataType::Type packed_type,
                     size_t vector_length,
                     uint32_t dex_pc)
-      : HVecOperation(allocator,
+      : HVecOperation(kVecSADAccumulate,
+                      allocator,
                       packed_type,
                       SideEffects::None(),
                       /* number_of_inputs */ 3,
@@ -965,7 +980,8 @@
            size_t vector_length,
            bool is_string_char_at,
            uint32_t dex_pc)
-      : HVecMemoryOperation(allocator,
+      : HVecMemoryOperation(kVecLoad,
+                            allocator,
                             packed_type,
                             side_effects,
                             /* number_of_inputs */ 2,
@@ -1010,7 +1026,8 @@
             SideEffects side_effects,
             size_t vector_length,
             uint32_t dex_pc)
-      : HVecMemoryOperation(allocator,
+      : HVecMemoryOperation(kVecStore,
+                            allocator,
                             packed_type,
                             side_effects,
                             /* number_of_inputs */ 3,
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
index 6326065..4c32be7 100644
--- a/compiler/optimizing/nodes_x86.h
+++ b/compiler/optimizing/nodes_x86.h
@@ -24,7 +24,11 @@
  public:
   // Treat the value as an int32_t, but it is really a 32 bit native pointer.
   HX86ComputeBaseMethodAddress()
-      : HExpression(DataType::Type::kInt32, SideEffects::None(), kNoDexPc) {}
+      : HExpression(kX86ComputeBaseMethodAddress,
+                    DataType::Type::kInt32,
+                    SideEffects::None(),
+                    kNoDexPc) {
+  }
 
   bool CanBeMoved() const OVERRIDE { return true; }
 
@@ -39,7 +43,10 @@
  public:
   HX86LoadFromConstantTable(HX86ComputeBaseMethodAddress* method_base,
                             HConstant* constant)
-      : HExpression(constant->GetType(), SideEffects::None(), kNoDexPc) {
+      : HExpression(kX86LoadFromConstantTable,
+                    constant->GetType(),
+                    SideEffects::None(),
+                    kNoDexPc) {
     SetRawInputAt(0, method_base);
     SetRawInputAt(1, constant);
   }
@@ -65,7 +72,7 @@
             HInstruction* input,
             HX86ComputeBaseMethodAddress* method_base,
             uint32_t dex_pc)
-      : HExpression(result_type, SideEffects::None(), dex_pc) {
+      : HExpression(kX86FPNeg, result_type, SideEffects::None(), dex_pc) {
     DCHECK(DataType::IsFloatingPointType(result_type));
     SetRawInputAt(0, input);
     SetRawInputAt(1, method_base);
@@ -89,7 +96,7 @@
                    HInstruction* input,
                    HX86ComputeBaseMethodAddress* method_base,
                    uint32_t dex_pc)
-    : HTemplateInstruction(SideEffects::None(), dex_pc),
+    : HTemplateInstruction(kX86PackedSwitch, SideEffects::None(), dex_pc),
       start_value_(start_value),
       num_entries_(num_entries) {
     SetRawInputAt(0, input);
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 12cfd24..6d3658f 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -1850,23 +1850,25 @@
   GenerateOdexForTest(dex_location,
                       odex_location,
                       CompilerFilter::Filter::kQuicken,
-                      { "--dump-timings", "--dm-file=" + dm_file.GetFilename() },
+                      { "--dump-timings",
+                        "--dm-file=" + dm_file.GetFilename(),
+                        // Pass -Xuse-stderr-logger have dex2oat output in output_ on target.
+                        "--runtime-arg",
+                        "-Xuse-stderr-logger" },
                       true,  // expect_success
                       false,  // use_fd
                       [](const OatFile& o) {
                         CHECK(o.ContainsDexCode());
                       });
-  if (!kIsTargetBuild) {
-    // The output_ variable is not correctly set for target, TODO: investigate.
-    std::istringstream iss(output_);
-    std::string line;
-    bool found_fast_verify = false;
-    const std::string kFastVerifyString = "Fast Verify";
-    while (std::getline(iss, line) && !found_fast_verify) {
-      found_fast_verify = found_fast_verify || line.find(kFastVerifyString) != std::string::npos;
-    }
-    EXPECT_TRUE(found_fast_verify) << "Expected to find " << kFastVerifyString << "\n" << output_;
+  // Check the output for "Fast verify", this is printed from --dump-timings.
+  std::istringstream iss(output_);
+  std::string line;
+  bool found_fast_verify = false;
+  const std::string kFastVerifyString = "Fast Verify";
+  while (std::getline(iss, line) && !found_fast_verify) {
+    found_fast_verify = found_fast_verify || line.find(kFastVerifyString) != std::string::npos;
   }
+  EXPECT_TRUE(found_fast_verify) << "Expected to find " << kFastVerifyString << "\n" << output_;
 }
 
 }  // namespace art
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 09f9010..ee872db 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -3348,9 +3348,8 @@
 
   vdex_dex_files_offset_ = vdex_size_;
 
-  extract_dex_files_into_vdex_ = copy_dex_files;
   // If extraction is enabled, only do it if not all the dex files are aligned and uncompressed.
-  if (extract_dex_files_into_vdex_) {
+  if (copy_dex_files) {
     extract_dex_files_into_vdex_ = false;
     for (OatDexFile& oat_dex_file : oat_dex_files_) {
       if (!oat_dex_file.source_.IsZipEntry()) {
@@ -3363,6 +3362,8 @@
         break;
       }
     }
+  } else {
+    extract_dex_files_into_vdex_ = false;
   }
 
   if (extract_dex_files_into_vdex_) {
diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc
index 39cf86d..bd76bf1 100644
--- a/dexlayout/compact_dex_writer.cc
+++ b/dexlayout/compact_dex_writer.cc
@@ -19,8 +19,8 @@
 #include "android-base/stringprintf.h"
 #include "base/logging.h"
 #include "base/time_utils.h"
-#include "dex/compact_dex_debug_info.h"
 #include "dex/compact_dex_file.h"
+#include "dex/compact_offset_table.h"
 #include "dexlayout.h"
 
 namespace art {
@@ -76,12 +76,12 @@
   std::vector<uint8_t> data;
   debug_info_base_ = 0u;
   debug_info_offsets_table_offset_ = 0u;
-  CompactDexDebugInfoOffsetTable::Build(debug_info_offsets,
-                                        &data,
-                                        &debug_info_base_,
-                                        &debug_info_offsets_table_offset_);
+  CompactOffsetTable::Build(debug_info_offsets,
+                            &data,
+                            &debug_info_base_,
+                            &debug_info_offsets_table_offset_);
   // Align the table and write it out.
-  stream->AlignTo(CompactDexDebugInfoOffsetTable::kAlignment);
+  stream->AlignTo(CompactOffsetTable::kAlignment);
   debug_info_offsets_pos_ = stream->Tell();
   stream->Write(data.data(), data.size());
 
@@ -90,12 +90,12 @@
   if (kMeasureAndTestOutput && !debug_info_offsets.empty()) {
     uint64_t start_time = NanoTime();
     stream->Begin();
-    CompactDexDebugInfoOffsetTable::Accessor accessor(stream->Begin() + debug_info_offsets_pos_,
-                                                      debug_info_base_,
-                                                      debug_info_offsets_table_offset_);
+    CompactOffsetTable::Accessor accessor(stream->Begin() + debug_info_offsets_pos_,
+                                          debug_info_base_,
+                                          debug_info_offsets_table_offset_);
 
     for (size_t i = 0; i < debug_info_offsets.size(); ++i) {
-      CHECK_EQ(accessor.GetDebugInfoOffset(i), debug_info_offsets[i]);
+      CHECK_EQ(accessor.GetOffset(i), debug_info_offsets[i]);
     }
     uint64_t end_time = NanoTime();
     VLOG(dex) << "Average lookup time (ns) for debug info offsets: "
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp
index 90c603f..382a46a 100644
--- a/libdexfile/Android.bp
+++ b/libdexfile/Android.bp
@@ -19,8 +19,8 @@
     defaults: ["art_defaults"],
     host_supported: true,
     srcs: [
-        "dex/compact_dex_debug_info.cc",
         "dex/compact_dex_file.cc",
+        "dex/compact_offset_table.cc",
         "dex/descriptors_names.cc",
         "dex/dex_file.cc",
         "dex/dex_file_exception_helpers.cc",
@@ -108,8 +108,8 @@
     ],
     srcs: [
         "dex/code_item_accessors_test.cc",
-        "dex/compact_dex_debug_info_test.cc",
         "dex/compact_dex_file_test.cc",
+        "dex/compact_offset_table_test.cc",
         "dex/dex_file_loader_test.cc",
         "dex/dex_file_verifier_test.cc",
         "dex/dex_instruction_test.cc",
diff --git a/libdexfile/dex/compact_dex_file.h b/libdexfile/dex/compact_dex_file.h
index 47b170c..2bea071 100644
--- a/libdexfile/dex/compact_dex_file.h
+++ b/libdexfile/dex/compact_dex_file.h
@@ -19,7 +19,7 @@
 
 #include "base/casts.h"
 #include "dex_file.h"
-#include "dex/compact_dex_debug_info.h"
+#include "dex/compact_offset_table.h"
 
 namespace art {
 
@@ -260,7 +260,7 @@
   uint32_t GetCodeItemSize(const DexFile::CodeItem& item) const OVERRIDE;
 
   uint32_t GetDebugInfoOffset(uint32_t dex_method_index) const {
-    return debug_info_offsets_.GetDebugInfoOffset(dex_method_index);
+    return debug_info_offsets_.GetOffset(dex_method_index);
   }
 
   static uint32_t CalculateChecksum(const uint8_t* base_begin,
@@ -279,7 +279,7 @@
                  const OatDexFile* oat_dex_file,
                  DexFileContainer* container);
 
-  CompactDexDebugInfoOffsetTable::Accessor debug_info_offsets_;
+  CompactOffsetTable::Accessor debug_info_offsets_;
 
   friend class DexFile;
   friend class DexFileLoader;
diff --git a/libdexfile/dex/compact_dex_debug_info.cc b/libdexfile/dex/compact_offset_table.cc
similarity index 61%
rename from libdexfile/dex/compact_dex_debug_info.cc
rename to libdexfile/dex/compact_offset_table.cc
index 19495ca..8cee0e3 100644
--- a/libdexfile/dex/compact_dex_debug_info.cc
+++ b/libdexfile/dex/compact_offset_table.cc
@@ -14,25 +14,25 @@
  * limitations under the License.
  */
 
-#include "compact_dex_debug_info.h"
+#include "compact_offset_table.h"
 
 #include "compact_dex_utils.h"
 #include "leb128.h"
 
 namespace art {
 
-constexpr size_t CompactDexDebugInfoOffsetTable::kElementsPerIndex;
+constexpr size_t CompactOffsetTable::kElementsPerIndex;
 
-CompactDexDebugInfoOffsetTable::Accessor::Accessor(const uint8_t* data_begin,
-                                                   uint32_t debug_info_base,
-                                                   uint32_t debug_info_table_offset)
-    : table_(reinterpret_cast<const uint32_t*>(data_begin + debug_info_table_offset)),
-      debug_info_base_(debug_info_base),
+CompactOffsetTable::Accessor::Accessor(const uint8_t* data_begin,
+                                       uint32_t minimum_offset,
+                                       uint32_t table_offset)
+    : table_(reinterpret_cast<const uint32_t*>(data_begin + table_offset)),
+      minimum_offset_(minimum_offset),
       data_begin_(data_begin) {}
 
-uint32_t CompactDexDebugInfoOffsetTable::Accessor::GetDebugInfoOffset(uint32_t method_idx) const {
-  const uint32_t offset = table_[method_idx / kElementsPerIndex];
-  const size_t bit_index = method_idx % kElementsPerIndex;
+uint32_t CompactOffsetTable::Accessor::GetOffset(uint32_t index) const {
+  const uint32_t offset = table_[index / kElementsPerIndex];
+  const size_t bit_index = index % kElementsPerIndex;
 
   const uint8_t* block = data_begin_ + offset;
   uint16_t bit_mask = *block;
@@ -40,14 +40,14 @@
   bit_mask = (bit_mask << kBitsPerByte) | *block;
   ++block;
   if ((bit_mask & (1 << bit_index)) == 0) {
-    // Bit is not set means the offset is 0 for the debug info.
+    // Bit is not set means the offset is 0.
     return 0u;
   }
   // Trim off the bits above the index we want and count how many bits are set. This is how many
   // lebs we need to decode.
   size_t count = POPCOUNT(static_cast<uintptr_t>(bit_mask) << (kBitsPerIntPtrT - 1 - bit_index));
   DCHECK_GT(count, 0u);
-  uint32_t current_offset = debug_info_base_;
+  uint32_t current_offset = minimum_offset_;
   do {
     current_offset += DecodeUnsignedLeb128(&block);
     --count;
@@ -55,15 +55,15 @@
   return current_offset;
 }
 
-void CompactDexDebugInfoOffsetTable::Build(const std::vector<uint32_t>& debug_info_offsets,
-                                           std::vector<uint8_t>* out_data,
-                                           uint32_t* out_min_offset,
-                                           uint32_t* out_table_offset) {
+void CompactOffsetTable::Build(const std::vector<uint32_t>& offsets,
+                               std::vector<uint8_t>* out_data,
+                               uint32_t* out_min_offset,
+                               uint32_t* out_table_offset) {
   DCHECK(out_data != nullptr);
   DCHECK(out_data->empty());
   // Calculate the base offset and return it.
   *out_min_offset = std::numeric_limits<uint32_t>::max();
-  for (const uint32_t offset : debug_info_offsets) {
+  for (const uint32_t offset : offsets) {
     if (offset != 0u) {
       *out_min_offset = std::min(*out_min_offset, offset);
     }
@@ -74,17 +74,17 @@
   std::vector<uint32_t> offset_table;
 
   // Write data first then the table.
-  while (block_start < debug_info_offsets.size()) {
+  while (block_start < offsets.size()) {
     // Write the offset of the block for each block.
     offset_table.push_back(out_data->size());
 
     // Block size of up to kElementsPerIndex
-    const size_t block_size = std::min(debug_info_offsets.size() - block_start, kElementsPerIndex);
+    const size_t block_size = std::min(offsets.size() - block_start, kElementsPerIndex);
 
     // Calculate bit mask since need to write that first.
     uint16_t bit_mask = 0u;
     for (size_t i = 0; i < block_size; ++i) {
-      if (debug_info_offsets[block_start + i] != 0u) {
+      if (offsets[block_start + i] != 0u) {
         bit_mask |= 1 << i;
       }
     }
@@ -92,14 +92,14 @@
     out_data->push_back(static_cast<uint8_t>(bit_mask >> kBitsPerByte));
     out_data->push_back(static_cast<uint8_t>(bit_mask));
 
-    // Write debug info offsets relative to the current offset.
-    uint32_t current_offset = *out_min_offset;
+    // Write offsets relative to the previous offset.
+    uint32_t prev_offset = *out_min_offset;
     for (size_t i = 0; i < block_size; ++i) {
-      const uint32_t debug_info_offset = debug_info_offsets[block_start + i];
-      if (debug_info_offset != 0u) {
-        uint32_t delta = debug_info_offset - current_offset;
+      const uint32_t offset = offsets[block_start + i];
+      if (offset != 0u) {
+        uint32_t delta = offset - prev_offset;
         EncodeUnsignedLeb128(out_data, delta);
-        current_offset = debug_info_offset;
+        prev_offset = offset;
       }
     }
 
diff --git a/libdexfile/dex/compact_dex_debug_info.h b/libdexfile/dex/compact_offset_table.h
similarity index 63%
rename from libdexfile/dex/compact_dex_debug_info.h
rename to libdexfile/dex/compact_offset_table.h
index bfd0bbe..17e6bb4 100644
--- a/libdexfile/dex/compact_dex_debug_info.h
+++ b/libdexfile/dex/compact_offset_table.h
@@ -14,44 +14,44 @@
  * limitations under the License.
  */
 
-#ifndef ART_LIBDEXFILE_DEX_COMPACT_DEX_DEBUG_INFO_H_
-#define ART_LIBDEXFILE_DEX_COMPACT_DEX_DEBUG_INFO_H_
+#ifndef ART_LIBDEXFILE_DEX_COMPACT_OFFSET_TABLE_H_
+#define ART_LIBDEXFILE_DEX_COMPACT_OFFSET_TABLE_H_
 
 #include <cstdint>
 #include <vector>
 
 namespace art {
 
-// Debug offset table for compact dex, aims to minimize size while still providing reasonable
-// speed (10-20ns average time per lookup on host).
-class CompactDexDebugInfoOffsetTable {
+// Compact offset table that aims to minimize size while still providing reasonable speed (10-20ns
+// average time per lookup on host).
+class CompactOffsetTable {
  public:
   // This value is coupled with the leb chunk bitmask. That logic must also be adjusted when the
   // integer is modified.
   static constexpr size_t kElementsPerIndex = 16;
 
   // Leb block format:
-  // [uint16_t] 16 bit mask for what method ids actually have a debug info offset for the chunk.
+  // [uint16_t] 16 bit mask for what indexes actually have a non zero offset for the chunk.
   // [lebs] Up to 16 lebs encoded using leb128, one leb bit. The leb specifies how the offset
   // changes compared to the previous index.
 
   class Accessor {
    public:
     Accessor(const uint8_t* data_begin,
-             uint32_t debug_info_base,
-             uint32_t debug_info_table_offset);
+             uint32_t minimum_offset,
+             uint32_t table_offset);
 
-    // Return the debug info for a method index (or 0 if it doesn't have one).
-    uint32_t GetDebugInfoOffset(uint32_t method_idx) const;
+    // Return the offset for the index.
+    uint32_t GetOffset(uint32_t index) const;
 
    private:
     const uint32_t* const table_;
-    const uint32_t debug_info_base_;
+    const uint32_t minimum_offset_;
     const uint8_t* const data_begin_;
   };
 
-  // Returned offsets are all relative to debug_info_offsets.
-  static void Build(const std::vector<uint32_t>& debug_info_offsets,
+  // Returned offsets are all relative to out_min_offset.
+  static void Build(const std::vector<uint32_t>& offsets,
                     std::vector<uint8_t>* out_data,
                     uint32_t* out_min_offset,
                     uint32_t* out_table_offset);
@@ -62,4 +62,4 @@
 
 }  // namespace art
 
-#endif  // ART_LIBDEXFILE_DEX_COMPACT_DEX_DEBUG_INFO_H_
+#endif  // ART_LIBDEXFILE_DEX_COMPACT_OFFSET_TABLE_H_
diff --git a/libdexfile/dex/compact_dex_debug_info_test.cc b/libdexfile/dex/compact_offset_table_test.cc
similarity index 72%
rename from libdexfile/dex/compact_dex_debug_info_test.cc
rename to libdexfile/dex/compact_offset_table_test.cc
index 7911a86..7eb0156 100644
--- a/libdexfile/dex/compact_dex_debug_info_test.cc
+++ b/libdexfile/dex/compact_offset_table_test.cc
@@ -17,12 +17,12 @@
 #include <vector>
 
 #include <android-base/logging.h>
-#include "dex/compact_dex_debug_info.h"
+#include "dex/compact_offset_table.h"
 #include "gtest/gtest.h"
 
 namespace art {
 
-TEST(CompactDexDebugInfoTest, TestBuildAndAccess) {
+TEST(CompactOffsetTableTest, TestBuildAndAccess) {
   const size_t kDebugInfoMinOffset = 1234567;
   std::vector<uint32_t> offsets = {
       0, 17, 2, 3, 11, 0, 0, 0, 0, 1, 0, 1552, 100, 122, 44, 1234567, 0, 0,
@@ -38,13 +38,10 @@
   }
 
   std::vector<uint8_t> data;
-  uint32_t base_offset = 0;
+  uint32_t min_offset = 0;
   uint32_t table_offset = 0;
-  CompactDexDebugInfoOffsetTable::Build(offsets,
-                                        /*out*/ &data,
-                                        /*out*/ &base_offset,
-                                        /*out*/ &table_offset);
-  EXPECT_GE(base_offset, kDebugInfoMinOffset);
+  CompactOffsetTable::Build(offsets, /*out*/ &data, /*out*/ &min_offset, /*out*/ &table_offset);
+  EXPECT_GE(min_offset, kDebugInfoMinOffset);
   EXPECT_LT(table_offset, data.size());
   ASSERT_GT(data.size(), 0u);
   const size_t before_size = offsets.size() * sizeof(offsets.front());
@@ -57,21 +54,19 @@
   std::vector<uint8_t> fake_dex(data.size() + kExtraOffset);
   std::copy(data.begin(), data.end(), fake_dex.data() + kExtraOffset);
 
-  CompactDexDebugInfoOffsetTable::Accessor accessor(fake_dex.data() + kExtraOffset,
-                                                    base_offset,
-                                                    table_offset);
+  CompactOffsetTable::Accessor accessor(fake_dex.data() + kExtraOffset, min_offset, table_offset);
   for (size_t i = 0; i < offsets.size(); ++i) {
-    EXPECT_EQ(offsets[i], accessor.GetDebugInfoOffset(i));
+    EXPECT_EQ(offsets[i], accessor.GetOffset(i));
   }
 
   // Sort to produce a try and produce a smaller table. This happens because the leb diff is smaller
   // for sorted increasing order.
   std::sort(offsets.begin(), offsets.end());
   std::vector<uint8_t> sorted_data;
-  CompactDexDebugInfoOffsetTable::Build(offsets,
-                                        /*out*/ &sorted_data,
-                                        /*out*/ &base_offset,
-                                        /*out*/ &table_offset);
+  CompactOffsetTable::Build(offsets,
+                            /*out*/ &sorted_data,
+                            /*out*/ &min_offset,
+                            /*out*/ &table_offset);
   EXPECT_LT(sorted_data.size(), data.size());
   {
     android::base::ScopedLogSeverity sls(android::base::LogSeverity::INFO);
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index 4d54d75..d510ae5 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -69,7 +69,7 @@
 #include "scoped_thread_state_change-inl.h"
 #include "thread-current-inl.h"
 #include "thread_list.h"
-#include "ti_class_loader.h"
+#include "ti_class_loader-inl.h"
 #include "ti_phase.h"
 #include "ti_redefine.h"
 #include "utils.h"
@@ -862,6 +862,108 @@
   return ERR(NONE);
 }
 
+// Copies unique class descriptors into the classes list from dex_files.
+static jvmtiError CopyClassDescriptors(jvmtiEnv* env,
+                                       const std::vector<const art::DexFile*>& dex_files,
+                                       /*out*/jint* count_ptr,
+                                       /*out*/char*** classes) {
+  jvmtiError res = OK;
+  std::set<art::StringPiece> unique_descriptors;
+  std::vector<const char*> descriptors;
+  auto add_descriptor = [&](const char* desc) {
+    // Don't add duplicates.
+    if (res == OK && unique_descriptors.find(desc) == unique_descriptors.end()) {
+      // The desc will remain valid since we hold a ref to the class_loader.
+      unique_descriptors.insert(desc);
+      descriptors.push_back(CopyString(env, desc, &res).release());
+    }
+  };
+  for (const art::DexFile* dex_file : dex_files) {
+    uint32_t num_defs = dex_file->NumClassDefs();
+    for (uint32_t i = 0; i < num_defs; i++) {
+      add_descriptor(dex_file->GetClassDescriptor(dex_file->GetClassDef(i)));
+    }
+  }
+  char** out_data = nullptr;
+  if (res == OK) {
+    res = env->Allocate(sizeof(char*) * descriptors.size(),
+                        reinterpret_cast<unsigned char**>(&out_data));
+  }
+  if (res != OK) {
+    env->Deallocate(reinterpret_cast<unsigned char*>(out_data));
+    // Failed to allocate. Cleanup everything.
+    for (const char* data : descriptors) {
+      env->Deallocate(reinterpret_cast<unsigned char*>(const_cast<char*>(data)));
+    }
+    descriptors.clear();
+    return res;
+  }
+  // Everything is good.
+  memcpy(out_data, descriptors.data(), sizeof(char*) * descriptors.size());
+  *count_ptr = static_cast<jint>(descriptors.size());
+  *classes = out_data;
+  return OK;
+}
+
+jvmtiError ClassUtil::GetClassLoaderClassDescriptors(jvmtiEnv* env,
+                                                     jobject loader,
+                                                     /*out*/jint* count_ptr,
+                                                     /*out*/char*** classes) {
+  art::Thread* self = art::Thread::Current();
+  if (env == nullptr) {
+    return ERR(INVALID_ENVIRONMENT);
+  } else if (self == nullptr) {
+    return ERR(UNATTACHED_THREAD);
+  } else if (count_ptr == nullptr || classes == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+  art::JNIEnvExt* jnienv = self->GetJniEnv();
+  if (loader == nullptr ||
+      jnienv->IsInstanceOf(loader, art::WellKnownClasses::java_lang_BootClassLoader)) {
+    // We can just get the dex files directly for the boot class path.
+    return CopyClassDescriptors(env,
+                                art::Runtime::Current()->GetClassLinker()->GetBootClassPath(),
+                                count_ptr,
+                                classes);
+  }
+  if (!jnienv->IsInstanceOf(loader, art::WellKnownClasses::java_lang_ClassLoader)) {
+    return ERR(ILLEGAL_ARGUMENT);
+  } else if (!jnienv->IsInstanceOf(loader,
+                                   art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
+    LOG(ERROR) << "GetClassLoaderClassDescriptors is only implemented for BootClassPath and "
+               << "dalvik.system.BaseDexClassLoader class loaders";
+    // TODO Possibly return OK With no classes would  be better since these ones cannot have any
+    // real classes associated with them.
+    return ERR(NOT_IMPLEMENTED);
+  }
+
+  art::ScopedObjectAccess soa(self);
+  art::StackHandleScope<1> hs(self);
+  art::Handle<art::mirror::ClassLoader> class_loader(
+      hs.NewHandle(soa.Decode<art::mirror::ClassLoader>(loader)));
+  std::vector<const art::DexFile*> dex_files;
+  ClassLoaderHelper::VisitDexFileObjects(
+      self,
+      class_loader,
+      [&] (art::ObjPtr<art::mirror::Object> dex_file) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+        art::StackHandleScope<2> hs(self);
+        art::Handle<art::mirror::Object> h_dex_file(hs.NewHandle(dex_file));
+        art::Handle<art::mirror::LongArray> cookie(
+            hs.NewHandle(ClassLoaderHelper::GetDexFileCookie(h_dex_file)));
+        size_t num_elements = cookie->GetLength();
+        // We need to skip over the oat_file that's the first element. The other elements are all
+        // dex files.
+        for (size_t i = 1; i < num_elements; i++) {
+          dex_files.push_back(
+              reinterpret_cast<const art::DexFile*>(static_cast<uintptr_t>(cookie->Get(i))));
+        }
+        // Iterate over all dex files.
+        return true;
+      });
+  // We hold the loader so the dex files won't go away until after this call at worst.
+  return CopyClassDescriptors(env, dex_files, count_ptr, classes);
+}
+
 jvmtiError ClassUtil::GetClassLoaderClasses(jvmtiEnv* env,
                                             jobject initiating_loader,
                                             jint* class_count_ptr,
diff --git a/openjdkjvmti/ti_class.h b/openjdkjvmti/ti_class.h
index dd99e36..7e427a0 100644
--- a/openjdkjvmti/ti_class.h
+++ b/openjdkjvmti/ti_class.h
@@ -75,6 +75,11 @@
                                           jint* class_count_ptr,
                                           jclass** classes_ptr);
 
+  static jvmtiError GetClassLoaderClassDescriptors(jvmtiEnv* env,
+                                                   jobject loader,
+                                                   jint* count_ptr,
+                                                   char*** classes);
+
   static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr);
   static jvmtiError IsArrayClass(jvmtiEnv* env, jclass klass, jboolean* is_array_class_ptr);
 
diff --git a/openjdkjvmti/ti_class_loader-inl.h b/openjdkjvmti/ti_class_loader-inl.h
new file mode 100644
index 0000000..95278f4
--- /dev/null
+++ b/openjdkjvmti/ti_class_loader-inl.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_OPENJDKJVMTI_TI_CLASS_LOADER_INL_H_
+#define ART_OPENJDKJVMTI_TI_CLASS_LOADER_INL_H_
+
+#include "ti_class_loader.h"
+#include "art_field-inl.h"
+#include "handle.h"
+#include "handle_scope.h"
+#include "jni_internal.h"
+#include "mirror/object.h"
+#include "mirror/object_array-inl.h"
+#include "well_known_classes.h"
+
+namespace openjdkjvmti {
+
+template<typename Visitor>
+inline void ClassLoaderHelper::VisitDexFileObjects(art::Thread* self,
+                                                   art::Handle<art::mirror::ClassLoader> loader,
+                                                   const Visitor& visitor) {
+  art::StackHandleScope<1> hs(self);
+  art::ArtField* element_dex_file_field = art::jni::DecodeArtField(
+      art::WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+
+  art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(
+      hs.NewHandle(GetDexElementList(self, loader)));
+  if (dex_elements_list == nullptr) {
+    return;
+  }
+
+  size_t num_elements = dex_elements_list->GetLength();
+  // Iterate over the DexPathList$Element to find the right one
+  for (size_t i = 0; i < num_elements; i++) {
+    art::ObjPtr<art::mirror::Object> current_element = dex_elements_list->Get(i);
+    CHECK(!current_element.IsNull());
+    art::ObjPtr<art::mirror::Object> dex_file(element_dex_file_field->GetObject(current_element));
+    if (!dex_file.IsNull()) {
+      if (!visitor(dex_file)) {
+        return;
+      }
+    }
+  }
+}
+
+}  // namespace openjdkjvmti
+
+#endif  // ART_OPENJDKJVMTI_TI_CLASS_LOADER_INL_H_
diff --git a/openjdkjvmti/ti_class_loader.cc b/openjdkjvmti/ti_class_loader.cc
index d594d6e..3df5de9 100644
--- a/openjdkjvmti/ti_class_loader.cc
+++ b/openjdkjvmti/ti_class_loader.cc
@@ -29,7 +29,7 @@
  * questions.
  */
 
-#include "ti_class_loader.h"
+#include "ti_class_loader-inl.h"
 
 #include <limits>
 
@@ -134,45 +134,28 @@
   return new_cookie.Get();
 }
 
-// TODO This should return the actual source java.lang.DexFile object for the klass being loaded.
-art::ObjPtr<art::mirror::Object> ClassLoaderHelper::FindSourceDexFileObject(
-    art::Thread* self, art::Handle<art::mirror::ClassLoader> loader) {
-  const char* dex_path_list_element_array_name = "[Ldalvik/system/DexPathList$Element;";
-  const char* dex_path_list_element_name = "Ldalvik/system/DexPathList$Element;";
-  const char* dex_file_name = "Ldalvik/system/DexFile;";
-  const char* dex_path_list_name = "Ldalvik/system/DexPathList;";
-  const char* dex_class_loader_name = "Ldalvik/system/BaseDexClassLoader;";
+art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> ClassLoaderHelper::GetDexElementList(
+    art::Thread* self,
+    art::Handle<art::mirror::ClassLoader> loader) {
+  art::StackHandleScope<4> hs(self);
 
-  CHECK(!self->IsExceptionPending());
-  art::StackHandleScope<5> hs(self);
-  art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
-
-  art::Handle<art::mirror::ClassLoader> null_loader(hs.NewHandle<art::mirror::ClassLoader>(
-      nullptr));
-  art::Handle<art::mirror::Class> base_dex_loader_class(hs.NewHandle(class_linker->FindClass(
-      self, dex_class_loader_name, null_loader)));
+  art::Handle<art::mirror::Class>
+      base_dex_loader_class(hs.NewHandle(self->DecodeJObject(
+          art::WellKnownClasses::dalvik_system_BaseDexClassLoader)->AsClass()));
 
   // Get all the ArtFields so we can look in the BaseDexClassLoader
-  art::ArtField* path_list_field = base_dex_loader_class->FindDeclaredInstanceField(
-      "pathList", dex_path_list_name);
-  CHECK(path_list_field != nullptr);
-
+  art::ArtField* path_list_field = art::jni::DecodeArtField(
+      art::WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
   art::ArtField* dex_path_list_element_field =
-      class_linker->FindClass(self, dex_path_list_name, null_loader)
-        ->FindDeclaredInstanceField("dexElements", dex_path_list_element_array_name);
-  CHECK(dex_path_list_element_field != nullptr);
-
-  art::ArtField* element_dex_file_field =
-      class_linker->FindClass(self, dex_path_list_element_name, null_loader)
-        ->FindDeclaredInstanceField("dexFile", dex_file_name);
-  CHECK(element_dex_file_field != nullptr);
+      art::jni::DecodeArtField(art::WellKnownClasses::dalvik_system_DexPathList_dexElements);
 
   // Check if loader is a BaseDexClassLoader
   art::Handle<art::mirror::Class> loader_class(hs.NewHandle(loader->GetClass()));
   // Currently only base_dex_loader is allowed to actually define classes but if this changes in the
   // future we should make sure to support all class loader types.
   if (!loader_class->IsSubClass(base_dex_loader_class.Get())) {
-    LOG(ERROR) << "The classloader is not a BaseDexClassLoader which is currently the only "
+    LOG(ERROR) << "The classloader " << loader_class->PrettyClass() << " is not a "
+               << base_dex_loader_class->PrettyClass() << " which is currently the only "
                << "supported class loader type!";
     return nullptr;
   }
@@ -180,28 +163,28 @@
   art::Handle<art::mirror::Object> path_list(
       hs.NewHandle(path_list_field->GetObject(loader.Get())));
   CHECK(path_list != nullptr);
-  CHECK(!self->IsExceptionPending());
-  art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(hs.NewHandle(
-      dex_path_list_element_field->GetObject(path_list.Get())->
-      AsObjectArray<art::mirror::Object>()));
-  CHECK(!self->IsExceptionPending());
-  CHECK(dex_elements_list != nullptr);
-  size_t num_elements = dex_elements_list->GetLength();
-  // Iterate over the DexPathList$Element to find the right one
-  for (size_t i = 0; i < num_elements; i++) {
-    art::ObjPtr<art::mirror::Object> current_element = dex_elements_list->Get(i);
-    CHECK(!current_element.IsNull());
-    // TODO It would be cleaner to put the art::DexFile into the dalvik.system.DexFile the class
-    // comes from but it is more annoying because we would need to find this class. It is not
-    // necessary for proper function since we just need to be in front of the classes old dex file
-    // in the path.
-    art::ObjPtr<art::mirror::Object> first_dex_file(
-        element_dex_file_field->GetObject(current_element));
-    if (!first_dex_file.IsNull()) {
-      return first_dex_file;
-    }
-  }
-  return nullptr;
+  art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list =
+      dex_path_list_element_field->GetObject(path_list.Get())->AsObjectArray<art::mirror::Object>();
+  return dex_elements_list;
+}
+
+// TODO This should return the actual source java.lang.DexFile object for the klass being loaded.
+art::ObjPtr<art::mirror::Object> ClassLoaderHelper::FindSourceDexFileObject(
+    art::Thread* self, art::Handle<art::mirror::ClassLoader> loader) {
+  art::ObjPtr<art::mirror::Object> res = nullptr;
+  VisitDexFileObjects(self,
+                      loader,
+                      [&] (art::ObjPtr<art::mirror::Object> dex_file) {
+                        res = dex_file;
+                        // Just stop at the first one.
+                        // TODO It would be cleaner to put the art::DexFile into the
+                        // dalvik.system.DexFile the class comes from but it is more annoying
+                        // because we would need to find this class. It is not necessary for proper
+                        // function since we just need to be in front of the classes old dex file in
+                        // the path.
+                        return false;
+                      });
+  return res;
 }
 
 }  // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_class_loader.h b/openjdkjvmti/ti_class_loader.h
index ceb7b33..5c9497b 100644
--- a/openjdkjvmti/ti_class_loader.h
+++ b/openjdkjvmti/ti_class_loader.h
@@ -82,6 +82,14 @@
       art::Thread* self, art::Handle<art::mirror::ClassLoader> loader)
       REQUIRES_SHARED(art::Locks::mutator_lock_);
 
+  // Calls visitor on each java.lang.DexFile associated with the given loader. The visitor should
+  // return true to continue on to the next DexFile or false to stop iterating.
+  template<typename Visitor>
+  static inline void VisitDexFileObjects(art::Thread* self,
+                                         art::Handle<art::mirror::ClassLoader> loader,
+                                         const Visitor& visitor)
+      REQUIRES_SHARED(art::Locks::mutator_lock_);
+
   static art::ObjPtr<art::mirror::LongArray> GetDexFileCookie(
       art::Handle<art::mirror::Object> java_dex_file) REQUIRES_SHARED(art::Locks::mutator_lock_);
 
@@ -93,6 +101,11 @@
   static void UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
                                 art::ObjPtr<art::mirror::LongArray> new_cookie)
       REQUIRES(art::Roles::uninterruptible_) REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ private:
+  static art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> GetDexElementList(
+      art::Thread* self, art::Handle<art::mirror::ClassLoader> loader)
+        REQUIRES_SHARED(art::Locks::mutator_lock_);
 };
 
 }  // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc
index 79a8cd6..5b1a16c 100644
--- a/openjdkjvmti/ti_extension.cc
+++ b/openjdkjvmti/ti_extension.cc
@@ -36,6 +36,7 @@
 #include "art_jvmti.h"
 #include "events.h"
 #include "ti_allocator.h"
+#include "ti_class.h"
 #include "ti_ddms.h"
 #include "ti_heap.h"
 #include "thread-inl.h"
@@ -226,6 +227,31 @@
     return error;
   }
 
+  // GetClassLoaderClassDescriptors extension
+  error = add_extension(
+      reinterpret_cast<jvmtiExtensionFunction>(ClassUtil::GetClassLoaderClassDescriptors),
+      "com.android.art.class.get_class_loader_class_descriptors",
+      "Retrieves a list of all the classes (as class descriptors) that the given class loader is"
+      " capable of being the defining class loader for. The return format is a list of"
+      " null-terminated descriptor strings of the form \"L/java/lang/Object;\". Each descriptor"
+      " will be in the list at most once. If the class_loader is null the bootclassloader will be"
+      " used. If the class_loader is not null it must either be a java.lang.BootClassLoader, a"
+      " dalvik.system.BaseDexClassLoader or a derived type. The data_out list and all elements"
+      " must be deallocated by the caller.",
+      {
+        { "class_loader", JVMTI_KIND_IN, JVMTI_TYPE_JOBJECT, true },
+        { "class_descriptor_count_out", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false },
+        { "data_out", JVMTI_KIND_ALLOC_ALLOC_BUF, JVMTI_TYPE_CCHAR, false },
+      },
+      {
+        ERR(NULL_POINTER),
+        ERR(ILLEGAL_ARGUMENT),
+        ERR(OUT_OF_MEMORY),
+        ERR(NOT_IMPLEMENTED),
+      });
+  if (error != ERR(NONE)) {
+    return error;
+  }
   // Copy into output buffer.
 
   *extension_count_ptr = ext_vector.size();
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 4900f17..9267b18 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -1093,10 +1093,10 @@
   UsageError("  --input-image-location=<file.art>: Specifies the 'location' of the image file to");
   UsageError("      be patched.");
   UsageError("");
-  UsageError("  --output-image-file=<file.art>: Specifies the exact file to write the patched");
-  UsageError("      image file to.");
+  UsageError("  --output-image-directory=<dir>: Specifies the directory to write the patched");
+  UsageError("      image file(s) to.");
   UsageError("");
-  UsageError("  --output-image-relocation-file=<file.art.rel>: Specifies the exact file to write");
+  UsageError("  --output-image-relocation-directory=<dir>: Specifies the directory to write");
   UsageError("      the image relocation information to.");
   UsageError("");
   UsageError("  --base-offset-delta=<delta>: Specify the amount to change the old base-offset by.");
@@ -1116,13 +1116,13 @@
                                 InstructionSet isa,
                                 const std::string& input_image_location,
                                 const std::string& output_image_directory,
-                                const std::string& output_image_relocation_filename,
+                                const std::string& output_image_relocation_directory,
                                 off_t base_delta,
                                 bool base_delta_set,
                                 bool debug) {
   CHECK(!input_image_location.empty());
-  if ((output_image_directory.empty()) && (output_image_relocation_filename.empty())) {
-    Usage("Image patching requires --output-image-file or --output-image-relocation-file");
+  if ((output_image_directory.empty()) && (output_image_relocation_directory.empty())) {
+    Usage("Image patching requires --output-image-directory or --output-image-relocation-directory");
   }
 
   if (!base_delta_set) {
@@ -1141,9 +1141,6 @@
 
   TimingLogger::ScopedTiming pt("patch image and oat", &timings);
 
-  std::string output_image_relocation_directory =
-      output_image_relocation_filename.substr(
-          0, output_image_relocation_filename.find_last_of('/'));
   bool ret =
       PatchOat::Patch(
           input_image_location,
@@ -1201,8 +1198,8 @@
   bool isa_set = false;
   InstructionSet isa = InstructionSet::kNone;
   std::string input_image_location;
-  std::string output_image_filename;
-  std::string output_image_relocation_filename;
+  std::string output_image_directory;
+  std::string output_image_relocation_directory;
   off_t base_delta = 0;
   bool base_delta_set = false;
   bool dump_timings = kIsDebugBuild;
@@ -1223,11 +1220,11 @@
       }
     } else if (option.starts_with("--input-image-location=")) {
       input_image_location = option.substr(strlen("--input-image-location=")).data();
-    } else if (option.starts_with("--output-image-file=")) {
-      output_image_filename = option.substr(strlen("--output-image-file=")).data();
-    } else if (option.starts_with("--output-image-relocation-file=")) {
-      output_image_relocation_filename =
-          option.substr(strlen("--output-image-relocation-file=")).data();
+    } else if (option.starts_with("--output-image-directory=")) {
+      output_image_directory = option.substr(strlen("--output-image-directory=")).data();
+    } else if (option.starts_with("--output-image-relocation-directory=")) {
+      output_image_relocation_directory =
+          option.substr(strlen("--output-image-relocation-directory=")).data();
     } else if (option.starts_with("--base-offset-delta=")) {
       const char* base_delta_str = option.substr(strlen("--base-offset-delta=")).data();
       base_delta_set = true;
@@ -1245,12 +1242,6 @@
     }
   }
 
-  // TODO: Have calls to patchoat pass in the output_image directory instead of
-  // the output_image_filename.
-  std::string output_image_directory;
-  if (!output_image_filename.empty())
-    output_image_directory = android::base::Dirname(output_image_filename);
-
   // The instruction set is mandatory. This simplifies things...
   if (!isa_set) {
     Usage("Instruction set must be set.");
@@ -1267,7 +1258,7 @@
                                isa,
                                input_image_location,
                                output_image_directory,
-                               output_image_relocation_filename,
+                               output_image_relocation_directory,
                                base_delta,
                                base_delta_set,
                                debug);
diff --git a/patchoat/patchoat_test.cc b/patchoat/patchoat_test.cc
index 69c6bfa..ac3abd0 100644
--- a/patchoat/patchoat_test.cc
+++ b/patchoat/patchoat_test.cc
@@ -137,32 +137,32 @@
   }
 
   bool RelocateBootImage(const std::string& input_image_location,
-                         const std::string& output_image_filename,
+                         const std::string& output_image_directory,
                          off_t base_offset_delta,
                          std::string* error_msg) {
     std::vector<std::string> argv = BasePatchoatCommand(input_image_location, base_offset_delta);
-    argv.push_back("--output-image-file=" + output_image_filename);
+    argv.push_back("--output-image-directory=" + output_image_directory);
 
     return RunDex2OatOrPatchoat(argv, error_msg);
   }
 
   bool VerifyBootImage(const std::string& input_image_location,
-                       const std::string& output_image_filename,
+                       const std::string& output_image_directory,
                        off_t base_offset_delta,
                        std::string* error_msg) {
     std::vector<std::string> argv = BasePatchoatCommand(input_image_location, base_offset_delta);
-    argv.push_back("--output-image-file=" + output_image_filename);
+    argv.push_back("--output-image-directory=" + output_image_directory);
     argv.push_back("--verify");
 
     return RunDex2OatOrPatchoat(argv, error_msg);
   }
 
   bool GenerateBootImageRelFile(const std::string& input_image_location,
-                                const std::string& output_rel_filename,
+                                const std::string& output_rel_directory,
                                 off_t base_offset_delta,
                                 std::string* error_msg) {
     std::vector<std::string> argv = BasePatchoatCommand(input_image_location, base_offset_delta);
-    argv.push_back("--output-image-relocation-file=" + output_rel_filename);
+    argv.push_back("--output-image-relocation-directory=" + output_rel_directory);
 
     return RunDex2OatOrPatchoat(argv, error_msg);
   }
@@ -375,7 +375,7 @@
   ASSERT_EQ(0, symlink(dex2oat_orig_dir.c_str(), dex2oat_orig_with_arch_dir.c_str()));
   if (!RelocateBootImage(
       dex2oat_orig_dir + "/boot.art",
-      patchoat_dir + "/boot.art",
+      patchoat_dir,
       base_addr_delta,
       &error_msg)) {
     FAIL() << "RelocateBootImage failed: " << error_msg;
@@ -467,7 +467,7 @@
   off_t base_addr_delta = 0x100000;
   if (!GenerateBootImageRelFile(
       dex2oat_orig_dir + "/boot.art",
-      dex2oat_orig_dir + "/boot.art.rel",
+      dex2oat_orig_dir,
       base_addr_delta,
       &error_msg)) {
     FAIL() << "RelocateBootImage failed: " << error_msg;
@@ -483,7 +483,7 @@
   base_addr_delta -= 0x10000;
   if (!RelocateBootImage(
       dex2oat_orig_dir + "/boot.art",
-      relocated_dir + "/boot.art",
+      relocated_dir,
       base_addr_delta,
       &error_msg)) {
     FAIL() << "RelocateBootImage failed: " << error_msg;
@@ -524,7 +524,7 @@
   // Assert that verification works with the .rel files.
   if (!VerifyBootImage(
       dex2oat_orig_dir + "/boot.art",
-      relocated_dir + "/boot.art",
+      relocated_dir,
       base_addr_delta,
       &error_msg)) {
     FAIL() << "VerifyBootImage failed: " << error_msg;
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 05f099f..02580cc 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -61,6 +61,10 @@
 static constexpr uint64_t kCriticalWarnTimeUs = MsToUs(16);
 static_assert(kCriticalWarnTimeUs > 0, "No JNI critical warn time set");
 
+// True if primitives within specific ranges cause a fatal error,
+// otherwise just warn.
+static constexpr bool kBrokenPrimitivesAreFatal = kIsDebugBuild;
+
 // Flags passed into ScopedCheck.
 static constexpr uint16_t kFlag_Default = 0x0000;
 
@@ -206,10 +210,12 @@
     JniValueType o;
     if (type_ == kTypeVaList) {
       switch (fmt) {
-        case 'Z': o.Z = static_cast<jboolean>(va_arg(vargs_, jint)); break;
-        case 'B': o.B = static_cast<jbyte>(va_arg(vargs_, jint)); break;
-        case 'C': o.C = static_cast<jchar>(va_arg(vargs_, jint)); break;
-        case 'S': o.S = static_cast<jshort>(va_arg(vargs_, jint)); break;
+        // Assign a full int for va_list values as this is what is done in reflection.cc.
+        // TODO(b/73656264): avoid undefined behavior.
+        case 'Z': FALLTHROUGH_INTENDED;
+        case 'B': FALLTHROUGH_INTENDED;
+        case 'C': FALLTHROUGH_INTENDED;
+        case 'S': FALLTHROUGH_INTENDED;
         case 'I': o.I = va_arg(vargs_, jint); break;
         case 'J': o.J = va_arg(vargs_, jlong); break;
         case 'F': o.F = static_cast<jfloat>(va_arg(vargs_, jdouble)); break;
@@ -224,10 +230,14 @@
       jvalue v = ptr_[cnt_];
       cnt_++;
       switch (fmt) {
-        case 'Z': o.Z = v.z; break;
-        case 'B': o.B = v.b; break;
-        case 'C': o.C = v.c; break;
-        case 'S': o.S = v.s; break;
+        // Copy just the amount of the jvalue necessary, as done in
+        // reflection.cc, but extend to an int to be consistent with
+        // var args in CheckNonHeapValue.
+        // TODO(b/73656264): avoid undefined behavior.
+        case 'Z': o.I = v.z; break;
+        case 'B': o.I = v.b; break;
+        case 'C': o.I = v.c; break;
+        case 'S': o.I = v.s; break;
         case 'I': o.I = v.i; break;
         case 'J': o.J = v.j; break;
         case 'F': o.F = v.f; break;
@@ -911,17 +921,20 @@
     switch (fmt) {
       case 'p':  // TODO: pointer - null or readable?
       case 'v':  // JavaVM*
-      case 'B':  // jbyte
-      case 'C':  // jchar
       case 'D':  // jdouble
       case 'F':  // jfloat
-      case 'I':  // jint
       case 'J':  // jlong
-      case 'S':  // jshort
+      case 'I':  // jint
         break;  // Ignored.
       case 'b':  // jboolean, why two? Fall-through.
       case 'Z':
-        return CheckBoolean(arg.Z);
+        return CheckBoolean(arg.I);
+      case 'B':  // jbyte
+        return CheckByte(arg.I);
+      case 'C':  // jchar
+        return CheckChar(arg.I);
+      case 'S':  // jshort
+        return CheckShort(arg.I);
       case 'u':  // utf8
         if ((flags_ & kFlag_Release) != 0) {
           return CheckNonNull(arg.u);
@@ -1152,14 +1165,54 @@
     return true;
   }
 
-  bool CheckBoolean(jboolean z) {
+  bool CheckBoolean(jint z) {
     if (z != JNI_TRUE && z != JNI_FALSE) {
+      // Note, broken booleans are always fatal.
       AbortF("unexpected jboolean value: %d", z);
       return false;
     }
     return true;
   }
 
+  bool CheckByte(jint b) {
+    if (b < std::numeric_limits<jbyte>::min() ||
+        b > std::numeric_limits<jbyte>::max()) {
+      if (kBrokenPrimitivesAreFatal) {
+        AbortF("unexpected jbyte value: %d", b);
+        return false;
+      } else {
+        LOG(WARNING) << "Unexpected jbyte value: " << b;
+      }
+    }
+    return true;
+  }
+
+  bool CheckShort(jint s) {
+    if (s < std::numeric_limits<jshort>::min() ||
+        s > std::numeric_limits<jshort>::max()) {
+      if (kBrokenPrimitivesAreFatal) {
+        AbortF("unexpected jshort value: %d", s);
+        return false;
+      } else {
+        LOG(WARNING) << "Unexpected jshort value: " << s;
+      }
+    }
+    return true;
+  }
+
+  bool CheckChar(jint c) {
+    if (c < std::numeric_limits<jchar>::min() ||
+        c > std::numeric_limits<jchar>::max()) {
+      if (kBrokenPrimitivesAreFatal) {
+        AbortF("unexpected jchar value: %d", c);
+        return false;
+      } else {
+        LOG(WARNING) << "Unexpected jchar value: " << c;
+      }
+    }
+    return true;
+  }
+
   bool CheckLengthPositive(jsize length) {
     if (length < 0) {
       AbortF("negative jsize: %d", length);
@@ -1813,7 +1866,7 @@
   static jobject ToReflectedMethod(JNIEnv* env, jclass cls, jmethodID mid, jboolean isStatic) {
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
-    JniValueType args[4] = {{.E = env}, {.c = cls}, {.m = mid}, {.b = isStatic}};
+    JniValueType args[4] = {{.E = env}, {.c = cls}, {.m = mid}, {.I = isStatic}};
     if (sc.Check(soa, true, "Ecmb", args)) {
       JniValueType result;
       result.L = baseEnv(env)->ToReflectedMethod(env, cls, mid, isStatic);
@@ -1828,7 +1881,7 @@
   static jobject ToReflectedField(JNIEnv* env, jclass cls, jfieldID fid, jboolean isStatic) {
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
-    JniValueType args[4] = {{.E = env}, {.c = cls}, {.f = fid}, {.b = isStatic}};
+    JniValueType args[4] = {{.E = env}, {.c = cls}, {.f = fid}, {.I = isStatic}};
     if (sc.Check(soa, true, "Ecfb", args)) {
       JniValueType result;
       result.L = baseEnv(env)->ToReflectedField(env, cls, fid, isStatic);
@@ -2113,7 +2166,7 @@
     return GetFieldIDInternal(__FUNCTION__, env, c, name, sig, true);
   }
 
-#define FIELD_ACCESSORS(jtype, name, ptype, shorty) \
+#define FIELD_ACCESSORS(jtype, name, ptype, shorty, slot_sized_shorty)  \
   static jtype GetStatic##name##Field(JNIEnv* env, jclass c, jfieldID fid) { \
     return GetField(__FUNCTION__, env, c, fid, true, ptype).shorty; \
   } \
@@ -2124,25 +2177,25 @@
   \
   static void SetStatic##name##Field(JNIEnv* env, jclass c, jfieldID fid, jtype v) { \
     JniValueType value; \
-    value.shorty = v; \
+    value.slot_sized_shorty = v; \
     SetField(__FUNCTION__, env, c, fid, true, ptype, value); \
   } \
   \
   static void Set##name##Field(JNIEnv* env, jobject obj, jfieldID fid, jtype v) { \
     JniValueType value; \
-    value.shorty = v; \
+    value.slot_sized_shorty = v; \
     SetField(__FUNCTION__, env, obj, fid, false, ptype, value); \
   }
 
-  FIELD_ACCESSORS(jobject, Object, Primitive::kPrimNot, L)
-  FIELD_ACCESSORS(jboolean, Boolean, Primitive::kPrimBoolean, Z)
-  FIELD_ACCESSORS(jbyte, Byte, Primitive::kPrimByte, B)
-  FIELD_ACCESSORS(jchar, Char, Primitive::kPrimChar, C)
-  FIELD_ACCESSORS(jshort, Short, Primitive::kPrimShort, S)
-  FIELD_ACCESSORS(jint, Int, Primitive::kPrimInt, I)
-  FIELD_ACCESSORS(jlong, Long, Primitive::kPrimLong, J)
-  FIELD_ACCESSORS(jfloat, Float, Primitive::kPrimFloat, F)
-  FIELD_ACCESSORS(jdouble, Double, Primitive::kPrimDouble, D)
+  FIELD_ACCESSORS(jobject, Object, Primitive::kPrimNot, L, L)
+  FIELD_ACCESSORS(jboolean, Boolean, Primitive::kPrimBoolean, Z, I)
+  FIELD_ACCESSORS(jbyte, Byte, Primitive::kPrimByte, B, I)
+  FIELD_ACCESSORS(jchar, Char, Primitive::kPrimChar, C, I)
+  FIELD_ACCESSORS(jshort, Short, Primitive::kPrimShort, S, I)
+  FIELD_ACCESSORS(jint, Int, Primitive::kPrimInt, I, I)
+  FIELD_ACCESSORS(jlong, Long, Primitive::kPrimLong, J, J)
+  FIELD_ACCESSORS(jfloat, Float, Primitive::kPrimFloat, F, F)
+  FIELD_ACCESSORS(jdouble, Double, Primitive::kPrimDouble, D, D)
 #undef FIELD_ACCESSORS
 
   static void CallVoidMethodA(JNIEnv* env, jobject obj, jmethodID mid, jvalue* vargs) {
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index 20cde53..75642fc 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -160,26 +160,6 @@
       + "/core.art";
   }
 
-  bool GetCachedImageFile(const std::string& image_location,
-                          /*out*/std::string* image,
-                          /*out*/std::string* error_msg) const {
-    std::string cache;
-    bool have_android_data;
-    bool dalvik_cache_exists;
-    bool is_global_cache;
-    GetDalvikCache(GetInstructionSetString(kRuntimeISA),
-                   true,
-                   &cache,
-                   &have_android_data,
-                   &dalvik_cache_exists,
-                   &is_global_cache);
-    if (!dalvik_cache_exists) {
-      *error_msg = "Failed to create dalvik cache";
-      return false;
-    }
-    return GetDalvikCacheFilename(image_location.c_str(), cache.c_str(), image, error_msg);
-  }
-
   // Returns the path to an image location whose contents differ from the
   // image at GetImageLocation(). This is used for testing mismatched
   // image checksums in the oat_file_assistant_tests.
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index 8ce7921..c19fa82 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -20,6 +20,7 @@
 #include <backtrace/BacktraceMap.h>
 #include <gtest/gtest.h>
 
+#include "base/file_utils.h"
 #include "common_runtime_test.h"
 #include "compiler_callbacks.h"
 #include "dex2oat_environment_test.h"
@@ -197,8 +198,18 @@
 }
 
 bool DexoptTest::PreRelocateImage(const std::string& image_location, std::string* error_msg) {
-  std::string image;
-  if (!GetCachedImageFile(image_location, &image, error_msg)) {
+  std::string dalvik_cache;
+  bool have_android_data;
+  bool dalvik_cache_exists;
+  bool is_global_cache;
+  GetDalvikCache(GetInstructionSetString(kRuntimeISA),
+                 true,
+                 &dalvik_cache,
+                 &have_android_data,
+                 &dalvik_cache_exists,
+                 &is_global_cache);
+  if (!dalvik_cache_exists) {
+    *error_msg = "Failed to create dalvik cache";
     return false;
   }
 
@@ -208,7 +219,7 @@
   std::vector<std::string> argv;
   argv.push_back(patchoat);
   argv.push_back("--input-image-location=" + image_location);
-  argv.push_back("--output-image-file=" + image);
+  argv.push_back("--output-image-directory=" + dalvik_cache);
   argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)));
   argv.push_back("--base-offset-delta=0x00008000");
   return Exec(argv, error_msg);
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 17913fc..cb43601 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -3453,7 +3453,8 @@
   if (gc_type != collector::kGcTypeSticky) {
     // Grow the heap for non sticky GC.
     ssize_t delta = bytes_allocated / GetTargetHeapUtilization() - bytes_allocated;
-    CHECK_GE(delta, 0);
+    CHECK_GE(delta, 0) << "bytes_allocated=" << bytes_allocated
+                       << " target_utilization_=" << target_utilization_;
     target_size = bytes_allocated + delta * multiplier;
     target_size = std::min(target_size, bytes_allocated + adjusted_max_free);
     target_size = std::max(target_size, bytes_allocated + adjusted_min_free);
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 7fb634f..9d2d2ed 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -1331,7 +1331,7 @@
   // The ideal maximum free size, when we grow the heap for utilization.
   size_t max_free_;
 
-  // Target ideal heap utilization ratio
+  // Target ideal heap utilization ratio.
   double target_utilization_;
 
   // How much more we grow the heap when we are a foreground app instead of background.
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 2b06e83..9ae1f45 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -240,7 +240,7 @@
 
 // Relocate the image at image_location to dest_filename and relocate it by a random amount.
 static bool RelocateImage(const char* image_location,
-                          const char* dest_filename,
+                          const char* dest_directory,
                           InstructionSet isa,
                           std::string* error_msg) {
   // We should clean up so we are more likely to have room for the image.
@@ -254,8 +254,8 @@
   std::string input_image_location_arg("--input-image-location=");
   input_image_location_arg += image_location;
 
-  std::string output_image_filename_arg("--output-image-file=");
-  output_image_filename_arg += dest_filename;
+  std::string output_image_directory_arg("--output-image-directory=");
+  output_image_directory_arg += dest_directory;
 
   std::string instruction_set_arg("--instruction-set=");
   instruction_set_arg += GetInstructionSetString(isa);
@@ -267,7 +267,7 @@
   argv.push_back(patchoat);
 
   argv.push_back(input_image_location_arg);
-  argv.push_back(output_image_filename_arg);
+  argv.push_back(output_image_directory_arg);
 
   argv.push_back(instruction_set_arg);
   argv.push_back(base_offset_arg);
@@ -278,7 +278,7 @@
 }
 
 static bool VerifyImage(const char* image_location,
-                        const char* dest_filename,
+                        const char* dest_directory,
                         InstructionSet isa,
                         std::string* error_msg) {
   std::string patchoat(Runtime::Current()->GetPatchoatExecutable());
@@ -286,8 +286,8 @@
   std::string input_image_location_arg("--input-image-location=");
   input_image_location_arg += image_location;
 
-  std::string output_image_filename_arg("--output-image-file=");
-  output_image_filename_arg += dest_filename;
+  std::string output_image_directory_arg("--output-image-directory=");
+  output_image_directory_arg += dest_directory;
 
   std::string instruction_set_arg("--instruction-set=");
   instruction_set_arg += GetInstructionSetString(isa);
@@ -296,7 +296,7 @@
   argv.push_back(patchoat);
 
   argv.push_back(input_image_location_arg);
-  argv.push_back(output_image_filename_arg);
+  argv.push_back(output_image_directory_arg);
 
   argv.push_back(instruction_set_arg);
 
@@ -1536,7 +1536,7 @@
     std::string local_error_msg;
     // All secondary images are verified when the primary image is verified.
     bool verified = secondary_image || VerifyImage(image_location,
-                                                   cache_filename.c_str(),
+                                                   dalvik_cache.c_str(),
                                                    image_isa,
                                                    &local_error_msg);
     if (!(verified && CheckSpace(dalvik_cache, &local_error_msg))) {
@@ -1628,7 +1628,7 @@
       _exit(1);
     } else if (ImageCreationAllowed(is_global_cache, image_isa, &local_error_msg)) {
       bool patch_success =
-          RelocateImage(image_location, cache_filename.c_str(), image_isa, &local_error_msg);
+          RelocateImage(image_location, dalvik_cache.c_str(), image_isa, &local_error_msg);
       if (patch_success) {
         std::unique_ptr<ImageSpace> patched_space =
             ImageSpaceLoader::Load(image_location,
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index ad24c94..6300038 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -835,6 +835,73 @@
   check_jni_abort_catcher.Check("null");
 }
 
+TEST_F(JniInternalTest, CallVarArgMethodBadPrimitive) {
+  // Check that bad primitive values cause check JNI to abort when
+  // passed out-of-range primitive value var args. As var args can't
+  // differentiate type sizes less than an int, and this isn't
+  // corrected by JNI, this helps ensure JNI code is valid.
+#define DoCall(boxed_type, shorty, c_type, bad_value)                   \
+  {                                                                     \
+    jclass prim_class = env_->FindClass("java/lang/" #boxed_type);      \
+    jmethodID method = env_->GetStaticMethodID(prim_class, "valueOf",   \
+                                               "(" #shorty ")Ljava/lang/" #boxed_type ";"); \
+    EXPECT_NE(nullptr, method);                                         \
+    EXPECT_FALSE(env_->ExceptionCheck());                               \
+    CheckJniAbortCatcher check_jni_abort_catcher;                       \
+    env_->CallStaticObjectMethod(prim_class, method, bad_value);        \
+    check_jni_abort_catcher.Check("unexpected " #c_type " value: " #bad_value); \
+  }
+
+  DoCall(Boolean, Z, jboolean, 2);
+  DoCall(Byte, B, jbyte, 128);
+  DoCall(Byte, B, jbyte, -129);
+  DoCall(Short, S, jshort, 32768);
+  DoCall(Short, S, jshort, -32769);
+  DoCall(Character, C, jchar, 65536);
+  DoCall(Character, C, jchar, -1);
+#undef DoCall
+}
+
+TEST_F(JniInternalTest, CallJValueMethodBadPrimitive) {
+  // Check that bad primitive values, passed as jvalues, cause check
+  // JNI to abort. Unlike with var args, sizes less than an int should
+  // be truncated or sign extended and not cause an abort except for
+  // jbooleans that are passed as bytes.
+#define DoFailCall(boxed_type, shorty, c_type, bad_value)               \
+  {                                                                     \
+    jclass prim_class = env_->FindClass("java/lang/" #boxed_type);      \
+    jmethodID method = env_->GetStaticMethodID(prim_class, "valueOf",   \
+                                               "(" #shorty ")Ljava/lang/" #boxed_type ";"); \
+    EXPECT_NE(nullptr, method);                                         \
+    EXPECT_FALSE(env_->ExceptionCheck());                               \
+    CheckJniAbortCatcher check_jni_abort_catcher;                       \
+    jvalue jval;                                                        \
+    jval.i = bad_value;                                                 \
+    env_->CallStaticObjectMethodA(prim_class, method, &jval);           \
+    check_jni_abort_catcher.Check("unexpected " #c_type " value: " #bad_value); \
+  }
+#define DoGoodCall(boxed_type, shorty, c_type, bad_value)               \
+  {                                                                     \
+    jclass prim_class = env_->FindClass("java/lang/" #boxed_type);      \
+    jmethodID method = env_->GetStaticMethodID(prim_class, "valueOf",   \
+                                               "(" #shorty ")Ljava/lang/" #boxed_type ";"); \
+    EXPECT_NE(nullptr, method);                                         \
+    EXPECT_FALSE(env_->ExceptionCheck());                               \
+    jvalue jval;                                                        \
+    jval.i = bad_value;                                                 \
+    env_->CallStaticObjectMethodA(prim_class, method, &jval);           \
+  }
+
+  DoFailCall(Boolean, Z, jboolean, 2);
+  DoGoodCall(Byte, B, jbyte, 128);
+  DoGoodCall(Byte, B, jbyte, -129);
+  DoGoodCall(Short, S, jshort, 32768);
+  DoGoodCall(Short, S, jshort, -32769);
+  DoGoodCall(Character, C, jchar, 65536);
+  DoGoodCall(Character, C, jchar, -1);
+#undef DoCall
+}
+
 TEST_F(JniInternalTest, GetStaticMethodID) {
   jclass jlobject = env_->FindClass("java/lang/Object");
   jclass jlnsme = env_->FindClass("java/lang/NoSuchMethodError");
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 635a03a..068bc28 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -35,6 +35,7 @@
 #include "well_known_classes.h"
 
 namespace art {
+namespace {
 
 using android::base::StringPrintf;
 
@@ -157,6 +158,7 @@
           Append(args[args_offset].s);
           break;
         case 'I':
+          FALLTHROUGH_INTENDED;
         case 'F':
           Append(args[args_offset].i);
           break;
@@ -164,6 +166,7 @@
           Append(soa.Decode<mirror::Object>(args[args_offset].l));
           break;
         case 'D':
+          FALLTHROUGH_INTENDED;
         case 'J':
           AppendWide(args[args_offset].j);
           break;
@@ -361,7 +364,7 @@
   std::unique_ptr<uint32_t[]> large_arg_array_;
 };
 
-static void CheckMethodArguments(JavaVMExt* vm, ArtMethod* m, uint32_t* args)
+void CheckMethodArguments(JavaVMExt* vm, ArtMethod* m, uint32_t* args)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   const DexFile::TypeList* params = m->GetParameterTypeList();
   if (params == nullptr) {
@@ -436,13 +439,13 @@
   }
 }
 
-static ArtMethod* FindVirtualMethod(ObjPtr<mirror::Object> receiver, ArtMethod* method)
+ArtMethod* FindVirtualMethod(ObjPtr<mirror::Object> receiver, ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   return receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(method, kRuntimePointerSize);
 }
 
 
-static void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa,
+void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa,
                                ArtMethod* method, ArgArray* arg_array, JValue* result,
                                const char* shorty)
     REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -453,6 +456,8 @@
   method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty);
 }
 
+}  // anonymous namespace
+
 JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid,
                          va_list args)
     REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index d0aec11..2074f1e 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -844,6 +844,18 @@
                  0);
   }
 
+  // In case we have a profile path passed as a command line argument,
+  // register the current class path for profiling now. Note that we cannot do
+  // this before we create the JIT and having it here is the most convenient way.
+  // This is used when testing profiles with dalvikvm command as there is no
+  // framework to register the dex files for profiling.
+  if (jit_.get() != nullptr && jit_options_->GetSaveProfilingInfo() &&
+      !jit_options_->GetProfileSaverOptions().GetProfilePath().empty()) {
+    std::vector<std::string> dex_filenames;
+    Split(class_path_string_, ':', &dex_filenames);
+    RegisterAppInfo(dex_filenames, jit_options_->GetProfileSaverOptions().GetProfilePath());
+  }
+
   return true;
 }
 
@@ -2410,18 +2422,6 @@
     LOG(WARNING) << "Failed to create JIT " << error_msg;
     return;
   }
-
-  // In case we have a profile path passed as a command line argument,
-  // register the current class path for profiling now. Note that we cannot do
-  // this before we create the JIT and having it here is the most convenient way.
-  // This is used when testing profiles with dalvikvm command as there is no
-  // framework to register the dex files for profiling.
-  if (jit_options_->GetSaveProfilingInfo() &&
-      !jit_options_->GetProfileSaverOptions().GetProfilePath().empty()) {
-    std::vector<std::string> dex_filenames;
-    Split(class_path_string_, ':', &dex_filenames);
-    RegisterAppInfo(dex_filenames, jit_options_->GetProfileSaverOptions().GetProfilePath());
-  }
 }
 
 bool Runtime::CanRelocate() const {
diff --git a/test/1946-list-descriptors/descriptors.cc b/test/1946-list-descriptors/descriptors.cc
new file mode 100644
index 0000000..01b306d
--- /dev/null
+++ b/test/1946-list-descriptors/descriptors.cc
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2013 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 "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1946Descriptors {
+
+typedef jvmtiError (*GetDescriptorList)(jvmtiEnv* env, jobject loader, jint* cnt, char*** descs);
+
+struct DescriptorData {
+  GetDescriptorList get_descriptor_list;
+};
+
+template <typename T>
+static void Dealloc(T* t) {
+  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(t));
+}
+
+template <typename T, typename ...Rest>
+static void Dealloc(T* t, Rest... rs) {
+  Dealloc(t);
+  Dealloc(rs...);
+}
+
+static void Cleanup(char** data, jint cnt) {
+  for (jint i = 0; i < cnt; i++) {
+    Dealloc(data[i]);
+  }
+  Dealloc(data);
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test1946_getClassloaderDescriptors(
+    JNIEnv* env, jclass, jobject loader) {
+  DescriptorData* data = nullptr;
+  if (JvmtiErrorToException(
+      env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+    return nullptr;
+  }
+  if (data == nullptr || data->get_descriptor_list == nullptr) {
+    ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+    env->ThrowNew(rt_exception.get(), "Alloc tracking data not initialized.");
+    return nullptr;
+  }
+  char** classes = nullptr;
+  jint cnt = -1;
+  if (JvmtiErrorToException(env, jvmti_env, data->get_descriptor_list(jvmti_env,
+                                                                      loader,
+                                                                      &cnt,
+                                                                      &classes))) {
+    return nullptr;
+  }
+  ScopedLocalRef<jobjectArray> arr(env, env->NewObjectArray(cnt,
+                                                            env->FindClass("java/lang/String"),
+                                                            nullptr));
+  if (env->ExceptionCheck()) {
+    Cleanup(classes, cnt);
+    return nullptr;
+  }
+
+  for (jint i = 0; i < cnt; i++) {
+    env->SetObjectArrayElement(arr.get(), i, env->NewStringUTF(classes[i]));
+    if (env->ExceptionCheck()) {
+      Cleanup(classes, cnt);
+      return nullptr;
+    }
+  }
+  Cleanup(classes, cnt);
+  return arr.release();
+}
+
+static void DeallocParams(jvmtiParamInfo* params, jint n_params) {
+  for (jint i = 0; i < n_params; i++) {
+    Dealloc(params[i].name);
+  }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1946_initializeTest(JNIEnv* env, jclass) {
+  void* old_data = nullptr;
+  if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
+    return;
+  } else if (old_data != nullptr) {
+    ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+    env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
+    return;
+  }
+  DescriptorData* data = nullptr;
+  if (JvmtiErrorToException(env,
+                            jvmti_env,
+                            jvmti_env->Allocate(sizeof(DescriptorData),
+                                                reinterpret_cast<unsigned char**>(&data)))) {
+    return;
+  }
+  memset(data, 0, sizeof(DescriptorData));
+  // Get the extensions.
+  jint n_ext = 0;
+  jvmtiExtensionFunctionInfo* infos = nullptr;
+  if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionFunctions(&n_ext, &infos))) {
+    return;
+  }
+  for (jint i = 0; i < n_ext; i++) {
+    jvmtiExtensionFunctionInfo* cur_info = &infos[i];
+    if (strcmp("com.android.art.class.get_class_loader_class_descriptors", cur_info->id) == 0) {
+      data->get_descriptor_list = reinterpret_cast<GetDescriptorList>(cur_info->func);
+    }
+    // Cleanup the cur_info
+    DeallocParams(cur_info->params, cur_info->param_count);
+    Dealloc(cur_info->id, cur_info->short_description, cur_info->params, cur_info->errors);
+  }
+  // Cleanup the array.
+  Dealloc(infos);
+  if (data->get_descriptor_list == nullptr) {
+    ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+    env->ThrowNew(rt_exception.get(), "Unable to find memory tracking extensions.");
+    return;
+  }
+  JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data));
+  return;
+}
+
+}  // namespace Test1946Descriptors
+}  // namespace art
diff --git a/test/1946-list-descriptors/expected.txt b/test/1946-list-descriptors/expected.txt
new file mode 100644
index 0000000..53e0935
--- /dev/null
+++ b/test/1946-list-descriptors/expected.txt
@@ -0,0 +1 @@
+Passed!
diff --git a/test/1946-list-descriptors/info.txt b/test/1946-list-descriptors/info.txt
new file mode 100644
index 0000000..924e0b3
--- /dev/null
+++ b/test/1946-list-descriptors/info.txt
@@ -0,0 +1 @@
+Tests the jvmti-extension to get the classes contained in class-loaders.
diff --git a/test/1946-list-descriptors/run b/test/1946-list-descriptors/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/1946-list-descriptors/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --jvmti
diff --git a/test/1946-list-descriptors/src-art/art/Test1946.java b/test/1946-list-descriptors/src-art/art/Test1946.java
new file mode 100644
index 0000000..3e5ec65
--- /dev/null
+++ b/test/1946-list-descriptors/src-art/art/Test1946.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+import java.util.*;
+import java.lang.reflect.*;
+import java.nio.ByteBuffer;
+import dalvik.system.InMemoryDexClassLoader;
+
+public class Test1946 {
+  // Base64 encoded dex file containing the following classes. Note the class E cannot be loaded.
+  // public class A {}
+  // public class B {}
+  // public class C {}
+  // public class D {}
+  // public class E extends ClassNotThere {}
+  private static final byte[] TEST_CLASSES = Base64.getDecoder().decode(
+      "ZGV4CjAzNQDzTO8rVDlKlz80vQF4NLYV5MjMMjHlOtRoAwAAcAAAAHhWNBIAAAAAAAAAAOACAAAO" +
+      "AAAAcAAAAAgAAACoAAAAAQAAAMgAAAAAAAAAAAAAAAcAAADUAAAABQAAAAwBAAC8AQAArAEAACQC" +
+      "AAAsAgAANAIAADwCAABEAgAATAIAAFQCAABZAgAAXgIAAGMCAAB0AgAAeQIAAH4CAACSAgAABgAA" +
+      "AAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAANAAAABwAAAAAAAAAAAAAAAAAAAAEAAAAAAAAA" +
+      "AgAAAAAAAAADAAAAAAAAAAQAAAAAAAAABQAAAAAAAAAGAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAB" +
+      "AAAAAAAAAK4CAAAAAAAAAQAAAAAAAAAGAAAAAAAAAAIAAAAAAAAAuAIAAAAAAAACAAAAAAAAAAYA" +
+      "AAAAAAAAAwAAAAAAAADCAgAAAAAAAAQAAAAAAAAABgAAAAAAAAAEAAAAAAAAAMwCAAAAAAAABQAA" +
+      "AAAAAAADAAAAAAAAAAUAAAAAAAAA1gIAAAAAAAABAAEAAQAAAJUCAAAEAAAAcBAGAAAADgABAAEA" +
+      "AQAAAJoCAAAEAAAAcBAGAAAADgABAAEAAQAAAJ8CAAAEAAAAcBAGAAAADgABAAEAAQAAAKQCAAAE" +
+      "AAAAcBAGAAAADgABAAEAAQAAAKkCAAAEAAAAcBADAAAADgAGPGluaXQ+AAZBLmphdmEABkIuamF2" +
+      "YQAGQy5qYXZhAAZELmphdmEABkUuamF2YQADTEE7AANMQjsAA0xDOwAPTENsYXNzTm90VGhlcmU7" +
+      "AANMRDsAA0xFOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAEABw4AAQAHDgABAAcOAAEABw4AAQAH" +
+      "DgAAAAEAAICABKwDAAABAAGAgATEAwAAAQACgIAE3AMAAAEABICABPQDAAABAAWAgASMBAsAAAAA" +
+      "AAAAAQAAAAAAAAABAAAADgAAAHAAAAACAAAACAAAAKgAAAADAAAAAQAAAMgAAAAFAAAABwAAANQA" +
+      "AAAGAAAABQAAAAwBAAABIAAABQAAAKwBAAACIAAADgAAACQCAAADIAAABQAAAJUCAAAAIAAABQAA" +
+      "AK4CAAAAEAAAAQAAAOACAAA=");
+  public class TMP1 {}
+  public class TMP2 {}
+  public class TMP3 extends ArrayList {}
+
+  private static void check(boolean b, String msg) {
+    if (!b) {
+      throw new Error("Test failed! " + msg);
+    }
+  }
+
+  private static <T> void checkEq(T[] full, T[] sub, String msg) {
+    List<T> f = Arrays.asList(full);
+    check(full.length == sub.length, "not equal length");
+    msg = Arrays.toString(full) + " is not same as " + Arrays.toString(sub) + ": " + msg;
+    check(Arrays.asList(full).containsAll(Arrays.asList(sub)), msg);
+  }
+
+  private static <T> void checkSubset(T[] full, T[] sub, String msg) {
+    msg = Arrays.toString(full) + " does not contain all of " + Arrays.toString(sub) + ": " + msg;
+    check(Arrays.asList(full).containsAll(Arrays.asList(sub)), msg);
+  }
+
+  public static void run() throws Exception {
+    initializeTest();
+    // Check a few random classes in BCP.
+    checkSubset(getClassloaderDescriptors(null),
+        new String[] { "Ljava/lang/String;", "Ljava/util/TreeSet;" },
+        "Missing entries for null classloader.");
+    // Make sure that null is the same as BootClassLoader
+    checkEq(getClassloaderDescriptors(null),
+        getClassloaderDescriptors(Object.class.getClassLoader()), "Object not in bcp!");
+    // Check the current class loader gets expected classes.
+    checkSubset(getClassloaderDescriptors(Test1946.class.getClassLoader()),
+        new String[] {
+          "Lart/Test1946;",
+          "Lart/Test1946$TMP1;",
+          "Lart/Test1946$TMP2;",
+          "Lart/Test1946$TMP3;"
+        },
+        "Missing entries for current class classloader.");
+    // Check that the result is exactly what we expect and includes classes that fail verification.
+    checkEq(getClassloaderDescriptors(makeClassLoaderFrom(TEST_CLASSES,
+            ClassLoader.getSystemClassLoader())),
+        new String[] { "LA;", "LB;", "LC;", "LD;", "LE;" },
+        "Unexpected classes in custom classloader");
+    checkEq(getClassloaderDescriptors(makeClassLoaderFrom(TEST_CLASSES,
+            Object.class.getClassLoader())),
+        new String[] { "LA;", "LB;", "LC;", "LD;", "LE;" },
+        "Unexpected classes in custom classloader");
+    checkEq(getClassloaderDescriptors(makeClassLoaderFrom(TEST_CLASSES,
+            Test1946.class.getClassLoader())),
+        new String[] { "LA;", "LB;", "LC;", "LD;", "LE;" },
+        "Unexpected classes in custom classloader");
+    // Check we only get 1 copy of each descriptor.
+    checkEq(getClassloaderDescriptors(makeClassLoaderFrom(Arrays.asList(TEST_CLASSES, TEST_CLASSES),
+            Test1946.class.getClassLoader())),
+        new String[] { "LA;", "LB;", "LC;", "LD;", "LE;" },
+        "Unexpected classes in custom classloader");
+    System.out.println("Passed!");
+  }
+
+  private static ClassLoader makeClassLoaderFrom(byte[] data, ClassLoader parent) throws Exception {
+    return new InMemoryDexClassLoader(ByteBuffer.wrap(data), parent);
+  }
+
+  private static ClassLoader makeClassLoaderFrom(List<byte[]> data, ClassLoader parent)
+      throws Exception {
+    ArrayList<ByteBuffer> bufs = new ArrayList<>();
+    for (byte[] d : data) {
+      bufs.add(ByteBuffer.wrap(d));
+    }
+    return new InMemoryDexClassLoader(bufs.toArray(new ByteBuffer[0]), parent);
+  }
+
+  private static native void initializeTest();
+  private static native String[] getClassloaderDescriptors(ClassLoader loader);
+}
diff --git a/test/1946-list-descriptors/src/Main.java b/test/1946-list-descriptors/src/Main.java
new file mode 100644
index 0000000..7d6f7ce
--- /dev/null
+++ b/test/1946-list-descriptors/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1946.run();
+  }
+}
diff --git a/test/1946-list-descriptors/src/art/Test1946.java b/test/1946-list-descriptors/src/art/Test1946.java
new file mode 100644
index 0000000..9636957
--- /dev/null
+++ b/test/1946-list-descriptors/src/art/Test1946.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package art;
+
+public class Test1946 {
+  public static void run() {
+    System.out.println("Failed! This should not be run. The test is in src-art/art/Test1946.java");
+  }
+}
diff --git a/test/Android.bp b/test/Android.bp
index 4e79b75..72cdd41 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -276,6 +276,7 @@
         "1941-dispose-stress/dispose_stress.cc",
         "1942-suspend-raw-monitor-exit/native_suspend_monitor.cc",
         "1943-suspend-raw-monitor-wait/native_suspend_monitor.cc",
+        "1946-list-descriptors/descriptors.cc",
     ],
     // Use NDK-compatible headers for ctstiagent.
     header_libs: [