Add DexInstructionIterator and use it a few places

Motivation:
Want to start abstracting away dex specific functionality to enable
CompactDex. Adding an iterator will enable CompactDex iteration to
work differently than normal dex iteration.

Will eventually replace CodeItemIterator.

Bug: 63756964
Test: test-art-host

Change-Id: I90e67c1a994b7698aaac0523a82816b0a003fbdc
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index e49f83f..7581962 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -374,8 +374,8 @@
       // Dex pc is not serialized, only used for checking the instructions. Since we access the
       // array based on the index of the quickened instruction, the indexes must line up perfectly.
       // The reader side uses the NeedsIndexForInstruction function too.
-      const Instruction* inst = Instruction::At(code_item->insns_ + info.dex_pc);
-      CHECK(QuickenInfoTable::NeedsIndexForInstruction(inst)) << inst->Opcode();
+      const Instruction& inst = code_item->InstructionAt(info.dex_pc);
+      CHECK(QuickenInfoTable::NeedsIndexForInstruction(&inst)) << inst.Opcode();
       // Add the index.
       quicken_data.push_back(static_cast<uint8_t>(info.dex_member_index >> 0));
       quicken_data.push_back(static_cast<uint8_t>(info.dex_member_index >> 8));
diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc
index c8e3d5e..925863e 100644
--- a/compiler/dex/inline_method_analyser.cc
+++ b/compiler/dex/inline_method_analyser.cc
@@ -64,14 +64,14 @@
  private:
   explicit Matcher(const DexFile::CodeItem* code_item)
       : code_item_(code_item),
-        instruction_(Instruction::At(code_item->insns_)),
+        instruction_(code_item->Instructions().begin()),
         pos_(0u),
         mark_(0u) { }
 
   static bool DoMatch(const DexFile::CodeItem* code_item, MatchFn* const* pattern, size_t size);
 
   const DexFile::CodeItem* const code_item_;
-  const Instruction* instruction_;
+  DexInstructionIterator instruction_;
   size_t pos_;
   size_t mark_;
 };
@@ -93,7 +93,7 @@
     return false;
   }
   matcher->pos_ += 1u;
-  matcher->instruction_ = matcher->instruction_->Next();
+  ++matcher->instruction_;
   return true;
 }
 
@@ -105,7 +105,7 @@
     return true;
   }
   matcher->pos_ = matcher->mark_;
-  matcher->instruction_ = matcher->instruction_->Next();
+  ++matcher->instruction_;
   return true;
 }
 
@@ -301,26 +301,27 @@
   // Verify the invoke, prevent a few odd cases and collect IPUTs.
   uint16_t this_vreg = code_item->registers_size_ - code_item->ins_size_;
   uint16_t zero_vreg_mask = 0u;
-  for (const Instruction* instruction = Instruction::At(code_item->insns_);
-      instruction->Opcode() != Instruction::RETURN_VOID;
-      instruction = instruction->Next()) {
-    if (instruction->Opcode() == Instruction::INVOKE_DIRECT) {
-      ArtMethod* target_method = GetTargetConstructor(method, instruction);
+
+  for (const Instruction& instruction : code_item->Instructions()) {
+    if (instruction.Opcode() == Instruction::RETURN_VOID) {
+      break;
+    } else if (instruction.Opcode() == Instruction::INVOKE_DIRECT) {
+      ArtMethod* target_method = GetTargetConstructor(method, &instruction);
       if (target_method == nullptr) {
         return false;
       }
       // We allow forwarding constructors only if they pass more arguments
       // to prevent infinite recursion.
       if (target_method->GetDeclaringClass() == method->GetDeclaringClass() &&
-          instruction->VRegA_35c() <= code_item->ins_size_) {
+          instruction.VRegA_35c() <= code_item->ins_size_) {
         return false;
       }
-      size_t forwarded = CountForwardedConstructorArguments(code_item, instruction, zero_vreg_mask);
+      size_t forwarded = CountForwardedConstructorArguments(code_item, &instruction, zero_vreg_mask);
       if (forwarded == static_cast<size_t>(-1)) {
         return false;
       }
       if (target_method->GetDeclaringClass()->IsObjectClass()) {
-        DCHECK_EQ(Instruction::At(target_method->GetCodeItem()->insns_)->Opcode(),
+        DCHECK_EQ(target_method->GetCodeItem()->Instructions().begin()->Opcode(),
                   Instruction::RETURN_VOID);
       } else {
         const DexFile::CodeItem* target_code_item = target_method->GetCodeItem();
@@ -345,15 +346,15 @@
           return false;
         }
       }
-    } else if (IsInstructionDirectConst(instruction->Opcode())) {
-      zero_vreg_mask |= GetZeroVRegMask(instruction);
+    } else if (IsInstructionDirectConst(instruction.Opcode())) {
+      zero_vreg_mask |= GetZeroVRegMask(&instruction);
       if ((zero_vreg_mask & (1u << this_vreg)) != 0u) {
         return false;  // Overwriting `this` is unsupported.
       }
     } else {
-      DCHECK(IsInstructionIPut(instruction->Opcode()));
-      DCHECK_EQ(instruction->VRegB_22c(), this_vreg);
-      if (!RecordConstructorIPut(method, instruction, this_vreg, zero_vreg_mask, iputs)) {
+      DCHECK(IsInstructionIPut(instruction.Opcode()));
+      DCHECK_EQ(instruction.VRegB_22c(), this_vreg);
+      if (!RecordConstructorIPut(method, &instruction, this_vreg, zero_vreg_mask, iputs)) {
         return false;
       }
     }
@@ -447,8 +448,7 @@
   // We currently support only plain return or 2-instruction methods.
 
   DCHECK_NE(code_item->insns_size_in_code_units_, 0u);
-  const Instruction* instruction = Instruction::At(code_item->insns_);
-  Instruction::Code opcode = instruction->Opcode();
+  Instruction::Code opcode = code_item->Instructions().begin()->Opcode();
 
   switch (opcode) {
     case Instruction::RETURN_VOID:
@@ -519,7 +519,7 @@
 
 bool InlineMethodAnalyser::AnalyseReturnMethod(const DexFile::CodeItem* code_item,
                                                InlineMethod* result) {
-  const Instruction* return_instruction = Instruction::At(code_item->insns_);
+  DexInstructionIterator return_instruction = code_item->Instructions().begin();
   Instruction::Code return_opcode = return_instruction->Opcode();
   uint32_t reg = return_instruction->VRegA_11x();
   uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
@@ -541,7 +541,7 @@
 
 bool InlineMethodAnalyser::AnalyseConstMethod(const DexFile::CodeItem* code_item,
                                               InlineMethod* result) {
-  const Instruction* instruction = Instruction::At(code_item->insns_);
+  DexInstructionIterator instruction = code_item->Instructions().begin();
   const Instruction* return_instruction = instruction->Next();
   Instruction::Code return_opcode = return_instruction->Opcode();
   if (return_opcode != Instruction::RETURN &&
@@ -575,7 +575,7 @@
                                              bool is_static,
                                              ArtMethod* method,
                                              InlineMethod* result) {
-  const Instruction* instruction = Instruction::At(code_item->insns_);
+  DexInstructionIterator instruction = code_item->Instructions().begin();
   Instruction::Code opcode = instruction->Opcode();
   DCHECK(IsInstructionIGet(opcode));
 
@@ -639,7 +639,7 @@
                                              bool is_static,
                                              ArtMethod* method,
                                              InlineMethod* result) {
-  const Instruction* instruction = Instruction::At(code_item->insns_);
+  DexInstructionIterator instruction = code_item->Instructions().begin();
   Instruction::Code opcode = instruction->Opcode();
   DCHECK(IsInstructionIPut(opcode));
 
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index e46dc59..9c5b632 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -64,24 +64,21 @@
   if (method_verifier->HasFailures()) {
     return;
   }
-  const DexFile::CodeItem* code_item = method_verifier->CodeItem();
-  const Instruction* inst = Instruction::At(code_item->insns_);
-  const Instruction* end = Instruction::At(code_item->insns_ +
-                                           code_item->insns_size_in_code_units_);
-
-  for (; inst < end; inst = inst->Next()) {
-    Instruction::Code code = inst->Opcode();
+  IterationRange<DexInstructionIterator> instructions = method_verifier->CodeItem()->Instructions();
+  for (auto it = instructions.begin(); it != instructions.end(); ++it) {
+    const Instruction& inst = *it;
+    const Instruction::Code code = inst.Opcode();
     if (code == Instruction::CHECK_CAST) {
-      uint32_t dex_pc = inst->GetDexPc(code_item->insns_);
+      const uint32_t dex_pc = it.GetDexPC(instructions.begin());
       if (!method_verifier->GetInstructionFlags(dex_pc).IsVisited()) {
         // Do not attempt to quicken this instruction, it's unreachable anyway.
         continue;
       }
       const verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
       const verifier::RegType& reg_type(line->GetRegisterType(method_verifier,
-                                                              inst->VRegA_21c()));
+                                                              inst.VRegA_21c()));
       const verifier::RegType& cast_type =
-          method_verifier->ResolveCheckedClass(dex::TypeIndex(inst->VRegB_21c()));
+          method_verifier->ResolveCheckedClass(dex::TypeIndex(inst.VRegB_21c()));
       // Pass null for the method verifier to not record the VerifierDeps dependency
       // if the types are not assignable.
       if (cast_type.IsStrictlyAssignableFrom(reg_type, /* method_verifier */ nullptr)) {
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 03d8ef5..7573367 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -762,18 +762,14 @@
     return;
   }
 
-  const uint16_t* code_ptr = code_item->insns_;
-  const uint16_t* code_end = code_item->insns_ + code_item->insns_size_in_code_units_;
   ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
-
-  while (code_ptr < code_end) {
-    const Instruction* inst = Instruction::At(code_ptr);
-    switch (inst->Opcode()) {
+  for (const Instruction& inst : code_item->Instructions()) {
+    switch (inst.Opcode()) {
       case Instruction::CONST_STRING:
       case Instruction::CONST_STRING_JUMBO: {
-        dex::StringIndex string_index((inst->Opcode() == Instruction::CONST_STRING)
-            ? inst->VRegB_21c()
-            : inst->VRegB_31c());
+        dex::StringIndex string_index((inst.Opcode() == Instruction::CONST_STRING)
+            ? inst.VRegB_21c()
+            : inst.VRegB_31c());
         mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache);
         CHECK(string != nullptr) << "Could not allocate a string when forcing determinism";
         break;
@@ -782,8 +778,6 @@
       default:
         break;
     }
-
-    code_ptr += inst->SizeInCodeUnits();
   }
 }
 
@@ -2439,21 +2433,16 @@
     if (clinit != nullptr) {
       const DexFile::CodeItem* code_item = clinit->GetCodeItem();
       DCHECK(code_item != nullptr);
-      const Instruction* inst = Instruction::At(code_item->insns_);
-
-      const uint32_t insns_size = code_item->insns_size_in_code_units_;
-      for (uint32_t dex_pc = 0; dex_pc < insns_size;) {
-        if (inst->Opcode() == Instruction::CONST_STRING) {
+      for (const Instruction& inst : code_item->Instructions()) {
+        if (inst.Opcode() == Instruction::CONST_STRING) {
           ObjPtr<mirror::String> s = class_linker->ResolveString(
-              *dex_file, dex::StringIndex(inst->VRegB_21c()), h_dex_cache);
+              *dex_file, dex::StringIndex(inst.VRegB_21c()), h_dex_cache);
           CHECK(s != nullptr);
-        } else if (inst->Opcode() == Instruction::CONST_STRING_JUMBO) {
+        } else if (inst.Opcode() == Instruction::CONST_STRING_JUMBO) {
           ObjPtr<mirror::String> s = class_linker->ResolveString(
-              *dex_file, dex::StringIndex(inst->VRegB_31c()), h_dex_cache);
+              *dex_file, dex::StringIndex(inst.VRegB_31c()), h_dex_cache);
           CHECK(s != nullptr);
         }
-        dex_pc += inst->SizeInCodeUnits();
-        inst = inst->Next();
       }
     }
   }
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index b8e4f32..3cb3792 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -722,12 +722,10 @@
     }
   }
   ArenaVector<size_t> covered(loop_headers.size(), 0, graph.GetArena()->Adapter(kArenaAllocMisc));
-  const uint16_t* code_ptr = code_item.insns_;
-  const uint16_t* code_end = code_item.insns_ + code_item.insns_size_in_code_units_;
-
-  size_t dex_pc = 0;
-  while (code_ptr < code_end) {
-    const Instruction& instruction = *Instruction::At(code_ptr);
+  IterationRange<DexInstructionIterator> instructions = code_item.Instructions();
+  for (auto it = instructions.begin(); it != instructions.end(); ++it) {
+    const uint32_t dex_pc = it.GetDexPC(instructions.begin());
+    const Instruction& instruction = *it;
     if (instruction.IsBranch()) {
       uint32_t target = dex_pc + instruction.GetTargetOffset();
       CheckCovers(target, graph, code_info, loop_headers, &covered);
@@ -743,8 +741,6 @@
         CheckCovers(target, graph, code_info, loop_headers, &covered);
       }
     }
-    dex_pc += instruction.SizeInCodeUnits();
-    code_ptr += instruction.SizeInCodeUnits();
   }
 
   for (size_t i = 0; i < covered.size(); ++i) {
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index e832b10..6ad8036 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -368,17 +368,16 @@
   };
   dex_file_->DecodeDebugPositionInfo(&code_item_, Callback::Position, locations);
   // Instruction-specific tweaks.
-  const Instruction* const begin = Instruction::At(code_item_.insns_);
-  const Instruction* const end = begin->RelativeAt(code_item_.insns_size_in_code_units_);
-  for (const Instruction* inst = begin; inst < end; inst = inst->Next()) {
-    switch (inst->Opcode()) {
+  IterationRange<DexInstructionIterator> instructions = code_item_.Instructions();
+  for (const Instruction& inst : instructions) {
+    switch (inst.Opcode()) {
       case Instruction::MOVE_EXCEPTION: {
         // Stop in native debugger after the exception has been moved.
         // The compiler also expects the move at the start of basic block so
         // we do not want to interfere by inserting native-debug-info before it.
-        locations->ClearBit(inst->GetDexPc(code_item_.insns_));
-        const Instruction* next = inst->Next();
-        if (next < end) {
+        locations->ClearBit(inst.GetDexPc(code_item_.insns_));
+        const Instruction* next = inst.Next();
+        if (DexInstructionIterator(next) != instructions.end()) {
           locations->SetBit(next->GetDexPc(code_item_.insns_));
         }
         break;
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index 5913832..0c944ce 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -185,21 +185,14 @@
                                std::vector<MethodId*>* method_ids,
                                std::vector<FieldId*>* field_ids) {
   bool has_id = false;
-  // Iterate over all instructions.
-  const uint16_t* insns = code->Insns();
-  for (uint32_t insn_idx = 0; insn_idx < code->InsnsSize();) {
-    const Instruction* instruction = Instruction::At(&insns[insn_idx]);
-    const uint32_t insn_width = instruction->SizeInCodeUnits();
-    if (insn_width == 0) {
-      break;
-    }
+  for (const Instruction& instruction : code->Instructions()) {
+    CHECK_GT(instruction.SizeInCodeUnits(), 0u);
     has_id |= GetIdFromInstruction(collections,
-                                   instruction,
+                                   &instruction,
                                    type_ids,
                                    string_ids,
                                    method_ids,
                                    field_ids);
-    insn_idx += insn_width;
   }  // for
   return has_id;
 }
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index 362c08b..5dcc87d 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -947,6 +947,11 @@
 
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
+  IterationRange<DexInstructionIterator> Instructions() const {
+    return MakeIterationRange(DexInstructionIterator(Insns()),
+                              DexInstructionIterator(Insns() + InsnsSize()));
+  }
+
  private:
   uint16_t registers_size_;
   uint16_t ins_size_;
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 92a1366..095c960 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -1079,16 +1079,15 @@
           code_offset, code_offset, dot.c_str(), name, type_descriptor.c_str());
 
   // Iterate over all instructions.
-  const uint16_t* insns = code->Insns();
-  for (uint32_t insn_idx = 0; insn_idx < code->InsnsSize();) {
-    const Instruction* instruction = Instruction::At(&insns[insn_idx]);
-    const uint32_t insn_width = instruction->SizeInCodeUnits();
+  IterationRange<DexInstructionIterator> instructions = code->Instructions();
+  for (auto inst = instructions.begin(); inst != instructions.end(); ++inst) {
+    const uint32_t dex_pc = inst.GetDexPC(instructions.begin());
+    const uint32_t insn_width = inst->SizeInCodeUnits();
     if (insn_width == 0) {
-      fprintf(stderr, "GLITCH: zero-width instruction at idx=0x%04x\n", insn_idx);
+      fprintf(stderr, "GLITCH: zero-width instruction at idx=0x%04x\n", dex_pc);
       break;
     }
-    DumpInstruction(code, code_offset, insn_idx, insn_width, instruction);
-    insn_idx += insn_width;
+    DumpInstruction(code, code_offset, dex_pc, insn_width, &*inst);
   }  // for
 }
 
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index be78136..bcf007b 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -905,26 +905,23 @@
       if (code_item == nullptr) {
         return;
       }
-      const size_t code_item_size = code_item->insns_size_in_code_units_;
-      const uint16_t* code_ptr = code_item->insns_;
-      const uint16_t* code_end = code_item->insns_ + code_item_size;
 
+      const uint16_t* code_ptr = code_item->insns_;
       // If we inserted a new dex code item pointer, add to total code bytes.
       if (dex_code_item_ptrs_.insert(code_ptr).second) {
-        dex_code_bytes_ += code_item_size * sizeof(code_ptr[0]);
+        dex_code_bytes_ += code_item->insns_size_in_code_units_ * sizeof(code_ptr[0]);
       }
 
-      while (code_ptr < code_end) {
-        const Instruction* inst = Instruction::At(code_ptr);
-        switch (inst->Opcode()) {
+      for (const Instruction& inst : code_item->Instructions()) {
+        switch (inst.Opcode()) {
           case Instruction::CONST_STRING: {
-            const dex::StringIndex string_index(inst->VRegB_21c());
+            const dex::StringIndex string_index(inst.VRegB_21c());
             unique_string_ids_from_code_.insert(StringReference(&dex_file, string_index));
             ++num_string_ids_from_code_;
             break;
           }
           case Instruction::CONST_STRING_JUMBO: {
-            const dex::StringIndex string_index(inst->VRegB_31c());
+            const dex::StringIndex string_index(inst.VRegB_31c());
             unique_string_ids_from_code_.insert(StringReference(&dex_file, string_index));
             ++num_string_ids_from_code_;
             break;
@@ -932,8 +929,6 @@
           default:
             break;
         }
-
-        code_ptr += inst->SizeInCodeUnits();
       }
     }
 
@@ -1520,12 +1515,11 @@
 
   void DumpDexCode(std::ostream& os, const DexFile& dex_file, const DexFile::CodeItem* code_item) {
     if (code_item != nullptr) {
-      size_t i = 0;
-      while (i < code_item->insns_size_in_code_units_) {
-        const Instruction* instruction = Instruction::At(&code_item->insns_[i]);
-        os << StringPrintf("0x%04zx: ", i) << instruction->DumpHexLE(5)
-           << StringPrintf("\t| %s\n", instruction->DumpString(&dex_file).c_str());
-        i += instruction->SizeInCodeUnits();
+      IterationRange<DexInstructionIterator> instructions = code_item->Instructions();
+      for (auto it = instructions.begin(); it != instructions.end(); ++it) {
+        const size_t dex_pc = it.GetDexPC(instructions.begin());
+        os << StringPrintf("0x%04zx: ", dex_pc) << it->DumpHexLE(5)
+           << StringPrintf("\t| %s\n", it->DumpString(&dex_file).c_str());
       }
     }
   }
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 4807427..ac91d52 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -21,9 +21,11 @@
 #include <string>
 #include <vector>
 
+#include "base/iteration_range.h"
 #include "base/logging.h"
 #include "base/value_object.h"
 #include "dex_file_types.h"
+#include "dex_instruction_iterator.h"
 #include "globals.h"
 #include "jni.h"
 #include "modifiers.h"
@@ -293,6 +295,15 @@
 
   // Raw code_item.
   struct CodeItem {
+    IterationRange<DexInstructionIterator> Instructions() const {
+      return { DexInstructionIterator(insns_),
+               DexInstructionIterator(insns_ + insns_size_in_code_units_)};
+    }
+
+    const Instruction& InstructionAt(uint32_t dex_pc) const {
+      return *Instruction::At(insns_ + dex_pc);
+    }
+
     uint16_t registers_size_;            // the number of registers used by this code
                                          //   (locals + parameters)
     uint16_t ins_size_;                  // the number of words of incoming arguments to the method
diff --git a/runtime/dex_instruction_iterator.h b/runtime/dex_instruction_iterator.h
new file mode 100644
index 0000000..6585875
--- /dev/null
+++ b/runtime/dex_instruction_iterator.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_DEX_INSTRUCTION_ITERATOR_H_
+#define ART_RUNTIME_DEX_INSTRUCTION_ITERATOR_H_
+
+#include <iterator>
+
+#include "dex_instruction.h"
+#include "base/logging.h"
+
+namespace art {
+
+class DexInstructionIterator : public std::iterator<std::forward_iterator_tag, Instruction> {
+ public:
+  using Type = std::iterator<std::forward_iterator_tag, Instruction>::value_type;
+  using difference_type = typename std::iterator<std::forward_iterator_tag, Type>::difference_type;
+
+  DexInstructionIterator() = default;
+  DexInstructionIterator(const DexInstructionIterator&) = default;
+  DexInstructionIterator(DexInstructionIterator&&) = default;
+  DexInstructionIterator& operator=(const DexInstructionIterator&) = default;
+  DexInstructionIterator& operator=(DexInstructionIterator&&) = default;
+
+  explicit DexInstructionIterator(const Type* inst) : inst_(inst) {}
+  explicit DexInstructionIterator(const uint16_t* inst) : inst_(Type::At(inst)) {}
+
+  ALWAYS_INLINE bool operator==(const DexInstructionIterator& other) const {
+    return inst_ == other.inst_ || inst_ == nullptr || other.inst_ == nullptr;
+  }
+
+  // Value after modification.
+  DexInstructionIterator& operator++() {
+    inst_ = inst_->Next();
+    return *this;
+  }
+
+  // Value before modification.
+  DexInstructionIterator operator++(int) {
+    DexInstructionIterator temp = *this;
+    ++*this;
+    return temp;
+  }
+
+  const Type& operator*() const {
+    return *inst_;
+  }
+
+  const Type* operator->() const {
+    return &**this;
+  }
+
+  // Return the dex pc for an iterator compared to the code item begin.
+  uint32_t GetDexPC(const DexInstructionIterator& code_item_begin) {
+    return reinterpret_cast<const uint16_t*>(inst_) -
+        reinterpret_cast<const uint16_t*>(code_item_begin.inst_);
+  }
+
+  const Type* Inst() const {
+    return inst_;
+  }
+
+ private:
+  const Type* inst_ = nullptr;
+};
+
+static inline bool operator!=(const DexInstructionIterator& lhs,
+                              const DexInstructionIterator& rhs) {
+  return !(lhs == rhs);
+}
+
+static inline bool operator<(const DexInstructionIterator& lhs,
+                             const DexInstructionIterator& rhs) {
+  return lhs.Inst() < rhs.Inst();
+}
+
+static inline bool operator>(const DexInstructionIterator& lhs,
+                             const DexInstructionIterator& rhs) {
+  return rhs < lhs;
+}
+
+static inline bool operator<=(const DexInstructionIterator& lhs,
+                              const DexInstructionIterator& rhs) {
+  return !(rhs < lhs);
+}
+
+static inline bool operator>=(const DexInstructionIterator& lhs,
+                              const DexInstructionIterator& rhs) {
+  return !(lhs < rhs);
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_DEX_INSTRUCTION_ITERATOR_H_
diff --git a/runtime/dex_instruction_test.cc b/runtime/dex_instruction_test.cc
index 3f7ac57..48ed027 100644
--- a/runtime/dex_instruction_test.cc
+++ b/runtime/dex_instruction_test.cc
@@ -15,6 +15,7 @@
  */
 
 #include "dex_instruction-inl.h"
+#include "dex_instruction_iterator.h"
 #include "gtest/gtest.h"
 
 namespace art {
@@ -73,7 +74,7 @@
   Build45cc(4u /* num_vregs */, 16u /* method_idx */, 32u /* proto_idx */,
             0xcafe /* arg_regs */, instruction);
 
-  const Instruction* ins = Instruction::At(instruction);
+  DexInstructionIterator ins(instruction);
   ASSERT_EQ(4u, ins->SizeInCodeUnits());
 
   ASSERT_TRUE(ins->HasVRegA());
@@ -108,7 +109,7 @@
   Build4rcc(4u /* num_vregs */, 16u /* method_idx */, 32u /* proto_idx */,
             0xcafe /* arg_regs */, instruction);
 
-  const Instruction* ins = Instruction::At(instruction);
+  DexInstructionIterator ins(instruction);
   ASSERT_EQ(4u, ins->SizeInCodeUnits());
 
   ASSERT_TRUE(ins->HasVRegA());
@@ -154,7 +155,7 @@
                                std::vector<uint16_t> args) {
   uint16_t inst[6] = {};
   Build35c(inst, code, method_idx, args);
-  return Instruction::At(inst)->DumpString(nullptr);
+  return DexInstructionIterator(inst)->DumpString(nullptr);
 }
 
 TEST(Instruction, DumpString) {
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 211381c..eef2773 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1119,7 +1119,7 @@
       const DexFile::CodeItem* code;
       code = caller->GetCodeItem();
       CHECK_LT(dex_pc, code->insns_size_in_code_units_);
-      const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
+      const Instruction* instr = &code->InstructionAt(dex_pc);
       Instruction::Code instr_code = instr->Opcode();
       bool is_range;
       switch (instr_code) {
@@ -2484,7 +2484,7 @@
     uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
     const DexFile::CodeItem* code_item = caller_method->GetCodeItem();
     DCHECK_LT(dex_pc, code_item->insns_size_in_code_units_);
-    const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]);
+    const Instruction* instr = &code_item->InstructionAt(dex_pc);
     Instruction::Code instr_code = instr->Opcode();
     DCHECK(instr_code == Instruction::INVOKE_INTERFACE ||
            instr_code == Instruction::INVOKE_INTERFACE_RANGE)
@@ -2600,7 +2600,7 @@
   ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
   uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
   const DexFile::CodeItem* code = caller_method->GetCodeItem();
-  const Instruction* inst = Instruction::At(&code->insns_[dex_pc]);
+  const Instruction* inst = &code->InstructionAt(dex_pc);
   DCHECK(inst->Opcode() == Instruction::INVOKE_POLYMORPHIC ||
          inst->Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE);
   const DexFile* dex_file = caller_method->GetDexFile();
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index 71ab01e..fb37825 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -393,7 +393,7 @@
 
   // create instruction data for invoke-direct {v0, v1} of method with fake index
   uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 };
-  const Instruction* inst = Instruction::At(inst_data);
+  DexInstructionIterator inst(inst_data);
 
   JValue result;
   ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, method, 0);
@@ -403,7 +403,7 @@
   shadow_frame->SetVRegReference(0, reference_empty_string);
   shadow_frame->SetVRegReference(1, string_arg);
 
-  interpreter::DoCall<false, false>(method, self, *shadow_frame, inst, inst_data[0], &result);
+  interpreter::DoCall<false, false>(method, self, *shadow_frame, &*inst, inst_data[0], &result);
   mirror::String* string_result = reinterpret_cast<mirror::String*>(result.GetL());
   EXPECT_EQ(string_arg->GetLength(), string_result->GetLength());
 
@@ -1027,12 +1027,12 @@
 
   // create instruction data for invoke-direct {v0, v1} of method with fake index
   uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 };
-  const Instruction* inst = Instruction::At(inst_data);
+  DexInstructionIterator inst(inst_data);
 
   JValue result;
   ShadowFrame* shadow_frame = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, method, 0);
   shadow_frame->SetVRegDouble(0, 1.23);
-  interpreter::DoCall<false, false>(method, self, *shadow_frame, inst, inst_data[0], &result);
+  interpreter::DoCall<false, false>(method, self, *shadow_frame, &*inst, inst_data[0], &result);
   ObjPtr<mirror::String> string_result = reinterpret_cast<mirror::String*>(result.GetL());
   ASSERT_TRUE(string_result != nullptr);
 
@@ -1187,12 +1187,12 @@
 
       // create instruction data for invoke-direct {v0} of method with fake index
       uint16_t inst_data[3] = { 0x1070, 0x0000, 0x0010 };
-      const Instruction* inst = Instruction::At(inst_data);
+      DexInstructionIterator inst(inst_data);
 
       interpreter::DoCall<false, false>(boot_cp_init,
                                         self,
                                         *shadow_frame,
-                                        inst,
+                                        &*inst,
                                         inst_data[0],
                                         &result);
       CHECK(!self->IsExceptionPending());
diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc
index 1bd095a..ad01324 100644
--- a/runtime/jit/profiling_info.cc
+++ b/runtime/jit/profiling_info.cc
@@ -43,15 +43,12 @@
   // instructions we are interested in profiling.
   DCHECK(!method->IsNative());
 
-  const DexFile::CodeItem& code_item = *method->GetCodeItem();
-  const uint16_t* code_ptr = code_item.insns_;
-  const uint16_t* code_end = code_item.insns_ + code_item.insns_size_in_code_units_;
-
-  uint32_t dex_pc = 0;
   std::vector<uint32_t> entries;
-  while (code_ptr < code_end) {
-    const Instruction& instruction = *Instruction::At(code_ptr);
-    switch (instruction.Opcode()) {
+
+  IterationRange<DexInstructionIterator> instructions = method->GetCodeItem()->Instructions();
+  for (auto inst = instructions.begin(); inst != instructions.end(); ++inst) {
+    const uint32_t dex_pc = inst.GetDexPC(instructions.begin());
+    switch (inst->Opcode()) {
       case Instruction::INVOKE_VIRTUAL:
       case Instruction::INVOKE_VIRTUAL_RANGE:
       case Instruction::INVOKE_VIRTUAL_QUICK:
@@ -64,8 +61,6 @@
       default:
         break;
     }
-    dex_pc += instruction.SizeInCodeUnits();
-    code_ptr += instruction.SizeInCodeUnits();
   }
 
   // We always create a `ProfilingInfo` object, even if there is no instruction we are
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index e4ad55d..b789ac6 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -616,18 +616,11 @@
 }
 
 static bool HasMonitorEnterInstructions(const DexFile::CodeItem* const code_item) {
-  const Instruction* inst = Instruction::At(code_item->insns_);
-
-  uint32_t insns_size = code_item->insns_size_in_code_units_;
-  for (uint32_t dex_pc = 0; dex_pc < insns_size;) {
-    if (inst->Opcode() == Instruction::MONITOR_ENTER) {
+  for (const Instruction& inst : code_item->Instructions()) {
+    if (inst.Opcode() == Instruction::MONITOR_ENTER) {
       return true;
     }
-
-    dex_pc += inst->SizeInCodeUnits();
-    inst = inst->Next();
   }
-
   return false;
 }
 
@@ -683,7 +676,7 @@
   if (register_line == nullptr) {
     return nullptr;
   }
-  const Instruction* inst = Instruction::At(code_item_->insns_ + dex_pc);
+  const Instruction* inst = &code_item_->InstructionAt(dex_pc);
   return GetQuickFieldAccess(inst, register_line);
 }
 
@@ -723,7 +716,7 @@
   if (register_line == nullptr) {
     return nullptr;
   }
-  const Instruction* inst = Instruction::At(code_item_->insns_ + dex_pc);
+  const Instruction* inst = &code_item_->InstructionAt(dex_pc);
   const bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK);
   return GetQuickInvokedMethod(inst, register_line, is_range, false);
 }
@@ -929,9 +922,8 @@
         // Note: this can fail before we touch any instruction, for the signature of a method. So
         //       add a check.
         if (work_insn_idx_ < dex::kDexNoIndex) {
-          const uint16_t* insns = code_item_->insns_ + work_insn_idx_;
-          const Instruction* inst = Instruction::At(insns);
-          int opcode_flags = Instruction::FlagsOf(inst->Opcode());
+          const Instruction& inst = code_item_->InstructionAt(work_insn_idx_);
+          int opcode_flags = Instruction::FlagsOf(inst.Opcode());
 
           if ((opcode_flags & Instruction::kThrow) == 0 && CurrentInsnFlags()->IsInTry()) {
             saved_line_->CopyFromLine(work_line_.get());
@@ -990,14 +982,12 @@
 }
 
 bool MethodVerifier::ComputeWidthsAndCountOps() {
-  const uint16_t* insns = code_item_->insns_;
-  size_t insns_size = code_item_->insns_size_in_code_units_;
-  const Instruction* inst = Instruction::At(insns);
   size_t new_instance_count = 0;
   size_t monitor_enter_count = 0;
-  size_t dex_pc = 0;
 
-  while (dex_pc < insns_size) {
+  IterationRange<DexInstructionIterator> instructions = code_item_->Instructions();
+  DexInstructionIterator inst = instructions.begin();
+  for ( ; inst < instructions.end(); ++inst) {
     Instruction::Code opcode = inst->Opcode();
     switch (opcode) {
       case Instruction::APUT_OBJECT:
@@ -1019,15 +1009,14 @@
       default:
         break;
     }
-    size_t inst_size = inst->SizeInCodeUnits();
-    GetInstructionFlags(dex_pc).SetIsOpcode();
-    dex_pc += inst_size;
-    inst = inst->RelativeAt(inst_size);
+    GetInstructionFlags(inst.GetDexPC(instructions.begin())).SetIsOpcode();
   }
 
-  if (dex_pc != insns_size) {
+  if (inst != instructions.end()) {
+    const size_t insns_size = code_item_->insns_size_in_code_units_;
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "code did not end where expected ("
-                                      << dex_pc << " vs. " << insns_size << ")";
+                                      << inst.GetDexPC(instructions.begin()) << " vs. "
+                                      << insns_size << ")";
     return false;
   }
 
@@ -1105,15 +1094,13 @@
 
 template <bool kAllowRuntimeOnlyInstructions>
 bool MethodVerifier::VerifyInstructions() {
-  const Instruction* inst = Instruction::At(code_item_->insns_);
-
   /* Flag the start of the method as a branch target, and a GC point due to stack overflow errors */
   GetInstructionFlags(0).SetBranchTarget();
   GetInstructionFlags(0).SetCompileTimeInfoPoint();
-
-  uint32_t insns_size = code_item_->insns_size_in_code_units_;
-  for (uint32_t dex_pc = 0; dex_pc < insns_size;) {
-    if (!VerifyInstruction<kAllowRuntimeOnlyInstructions>(inst, dex_pc)) {
+  IterationRange<DexInstructionIterator> instructions = code_item_->Instructions();
+  for (auto inst = instructions.begin(); inst != instructions.end(); ++inst) {
+    const uint32_t dex_pc = inst.GetDexPC(instructions.begin());
+    if (!VerifyInstruction<kAllowRuntimeOnlyInstructions>(&*inst, dex_pc)) {
       DCHECK_NE(failures_.size(), 0U);
       return false;
     }
@@ -1134,8 +1121,6 @@
     } else if (inst->IsReturn()) {
       GetInstructionFlags(dex_pc).SetCompileTimeInfoPointAndReturn();
     }
-    dex_pc += inst->SizeInCodeUnits();
-    inst = inst->Next();
   }
   return true;
 }
@@ -1671,9 +1656,10 @@
   }
   vios->Stream() << "Dumping instructions and register lines:\n";
   ScopedIndentation indent1(vios);
-  const Instruction* inst = Instruction::At(code_item_->insns_);
-  for (size_t dex_pc = 0; dex_pc < code_item_->insns_size_in_code_units_;
-      dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) {
+
+  IterationRange<DexInstructionIterator> instructions = code_item_->Instructions();
+  for (auto inst = instructions.begin(); inst != instructions.end(); ++inst) {
+    const size_t dex_pc = inst.GetDexPC(instructions.begin());
     RegisterLine* reg_line = reg_table_.GetLine(dex_pc);
     if (reg_line != nullptr) {
       vios->Stream() << reg_line->Dump(this) << "\n";
@@ -1938,9 +1924,10 @@
      * we are almost certainly going to have some dead code.
      */
     int dead_start = -1;
-    uint32_t insn_idx = 0;
-    for (; insn_idx < insns_size;
-         insn_idx += Instruction::At(code_item_->insns_ + insn_idx)->SizeInCodeUnits()) {
+
+    IterationRange<DexInstructionIterator> instructions = code_item_->Instructions();
+    for (auto inst = instructions.begin(); inst != instructions.end(); ++inst) {
+      const uint32_t insn_idx = inst.GetDexPC(instructions.begin());
       /*
        * Switch-statement data doesn't get "visited" by scanner. It
        * may or may not be preceded by a padding NOP (for alignment).
@@ -1956,8 +1943,9 @@
       }
 
       if (!GetInstructionFlags(insn_idx).IsVisited()) {
-        if (dead_start < 0)
+        if (dead_start < 0) {
           dead_start = insn_idx;
+        }
       } else if (dead_start >= 0) {
         LogVerifyInfo() << "dead code " << reinterpret_cast<void*>(dead_start)
                         << "-" << reinterpret_cast<void*>(insn_idx - 1);
@@ -1965,8 +1953,9 @@
       }
     }
     if (dead_start >= 0) {
-      LogVerifyInfo() << "dead code " << reinterpret_cast<void*>(dead_start)
-                      << "-" << reinterpret_cast<void*>(insn_idx - 1);
+      LogVerifyInfo()
+          << "dead code " << reinterpret_cast<void*>(dead_start)
+          << "-" << reinterpret_cast<void*>(instructions.end().GetDexPC(instructions.begin()));
     }
     // To dump the state of the verify after a method, do something like:
     // if (dex_file_->PrettyMethod(dex_method_idx_) ==
@@ -2340,17 +2329,17 @@
         while (0 != prev_idx && !GetInstructionFlags(prev_idx).IsOpcode()) {
           prev_idx--;
         }
-        const Instruction* prev_inst = Instruction::At(code_item_->insns_ + prev_idx);
-        switch (prev_inst->Opcode()) {
+        const Instruction& prev_inst = code_item_->InstructionAt(prev_idx);
+        switch (prev_inst.Opcode()) {
           case Instruction::MOVE_OBJECT:
           case Instruction::MOVE_OBJECT_16:
           case Instruction::MOVE_OBJECT_FROM16:
-            if (prev_inst->VRegB() == inst->VRegA_11x()) {
+            if (prev_inst.VRegB() == inst->VRegA_11x()) {
               // Redo the copy. This won't change the register types, but update the lock status
               // for the aliased register.
               work_line_->CopyRegister1(this,
-                                        prev_inst->VRegA(),
-                                        prev_inst->VRegB(),
+                                        prev_inst.VRegA(),
+                                        prev_inst.VRegB(),
                                         kTypeCategoryRef);
             }
             break;
@@ -2648,7 +2637,7 @@
         break;
       }
 
-      const Instruction* instance_of_inst = Instruction::At(code_item_->insns_ + instance_of_idx);
+      const Instruction* instance_of_inst = &code_item_->InstructionAt(instance_of_idx);
 
       /* Check for peep-hole pattern of:
        *    ...;
@@ -2710,7 +2699,7 @@
                             work_insn_idx_)) {
               break;
             }
-            const Instruction* move_inst = Instruction::At(code_item_->insns_ + move_idx);
+            const Instruction* move_inst = &code_item_->InstructionAt(move_idx);
             switch (move_inst->Opcode()) {
               case Instruction::MOVE_OBJECT:
                 if (move_inst->VRegA_12x() == instance_of_inst->VRegB_22c()) {
@@ -3648,7 +3637,7 @@
    *        and this change should not be used in those cases.
    */
   if ((opcode_flags & Instruction::kContinue) != 0) {
-    DCHECK_EQ(Instruction::At(code_item_->insns_ + work_insn_idx_), inst);
+    DCHECK_EQ(&code_item_->InstructionAt(work_insn_idx_), inst);
     uint32_t next_insn_idx = work_insn_idx_ + inst->SizeInCodeUnits();
     if (next_insn_idx >= code_item_->insns_size_in_code_units_) {
       Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Execution can walk off end of code area";