Merge "ART: Fix .bss index lookup in wrong dex file."
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 19abcd1..3699d66 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -322,6 +322,7 @@
         "linker/linker_patch_test.cc",
         "linker/output_stream_test.cc",
         "optimizing/bounds_check_elimination_test.cc",
+        "optimizing/cloner_test.cc",
         "optimizing/data_type_test.cc",
         "optimizing/dominator_test.cc",
         "optimizing/find_loops_test.cc",
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
index 37c2d32..d599994 100644
--- a/compiler/debug/elf_debug_info_writer.h
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -35,6 +35,7 @@
 #include "mirror/array.h"
 #include "mirror/class-inl.h"
 #include "mirror/class.h"
+#include "oat_file.h"
 
 namespace art {
 namespace debug {
@@ -49,7 +50,8 @@
   std::vector<const char*> names;
   if (mi->code_item != nullptr) {
     DCHECK(mi->dex_file != nullptr);
-    const uint8_t* stream = mi->dex_file->GetDebugInfoStream(mi->code_item);
+    uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*mi->dex_file, mi->code_item);
+    const uint8_t* stream = mi->dex_file->GetDebugInfoStream(debug_info_offset);
     if (stream != nullptr) {
       DecodeUnsignedLeb128(&stream);  // line.
       uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
@@ -257,7 +259,9 @@
 
       // Write local variables.
       LocalInfos local_infos;
+      uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex, dex_code);
       if (dex->DecodeDebugLocalInfo(dex_code,
+                                    debug_info_offset,
                                     is_static,
                                     mi->dex_method_index,
                                     LocalInfoCallback,
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index 6e72b46..943e03a 100644
--- a/compiler/debug/elf_debug_line_writer.h
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -26,6 +26,7 @@
 #include "debug/src_map_elem.h"
 #include "dex_file-inl.h"
 #include "linker/elf_builder.h"
+#include "oat_file.h"
 #include "stack_map.h"
 
 namespace art {
@@ -158,7 +159,9 @@
       PositionInfos dex2line_map;
       DCHECK(mi->dex_file != nullptr);
       const DexFile* dex = mi->dex_file;
-      if (!dex->DecodeDebugPositionInfo(mi->code_item, PositionInfoCallback, &dex2line_map)) {
+      uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex, mi->code_item);
+      if (!dex->DecodeDebugPositionInfo(
+              mi->code_item, debug_info_offset, PositionInfoCallback, &dex2line_map)) {
         continue;
       }
 
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 726401d..e4dd544 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2025,28 +2025,19 @@
       ClassReference ref(manager_->GetDexFile(), class_def_index);
       manager_->GetCompiler()->RecordClassStatus(ref, klass->GetStatus());
 
-      // It is *very* problematic if there are verification errors in the boot classpath.
-      // For example, we rely on things working OK without verification when the decryption dialog
-      // is brought up. So abort in a debug build if we find this violated.
+      // It is *very* problematic if there are resolution errors in the boot classpath.
+      //
+      // It is also bad if classes fail verification. For example, we rely on things working
+      // OK without verification when the decryption dialog is brought up. It is thus highly
+      // recommended to compile the boot classpath with
+      //   --abort-on-hard-verifier-error --abort-on-soft-verifier-error
+      // which is the default build system configuration.
       if (kIsDebugBuild) {
         if (manager_->GetCompiler()->GetCompilerOptions().IsBootImage()) {
-          if (!klass->IsVerified()) {
-            // Re-run verification to get all failure messages if it soft-failed.
-            if (!klass->IsErroneous()) {
-              gLogVerbosity.verifier = true;
-              // Note: We can't call ClassLinker::VerifyClass, as it will elide the second
-              //       verification.
-              Runtime* runtime = Runtime::Current();
-              std::string v_error;
-              verifier::MethodVerifier::VerifyClass(soa.Self(),
-                                                    klass.Get(),
-                                                    runtime->GetCompilerCallbacks(),
-                                                    runtime->IsAotCompiler(),
-                                                    verifier::HardFailLogMode::kLogInternalFatal,
-                                                    &v_error);
-            }
+          if (!klass->IsResolved() || klass->IsErroneous()) {
             LOG(FATAL) << "Boot classpath class " << klass->PrettyClass()
-                       << " failed to fully verify: state= " << klass->GetStatus();
+                       << " failed to resolve/is erroneous: state= " << klass->GetStatus();
+            UNREACHABLE();
           }
         }
         if (klass->IsVerified()) {
diff --git a/compiler/optimizing/cloner_test.cc b/compiler/optimizing/cloner_test.cc
new file mode 100644
index 0000000..d34dd81
--- /dev/null
+++ b/compiler/optimizing/cloner_test.cc
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "graph_checker.h"
+#include "nodes.h"
+#include "optimizing_unit_test.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+// This class provides methods and helpers for testing various cloning and copying routines:
+// individual instruction cloning and cloning of the more coarse-grain structures.
+class ClonerTest : public OptimizingUnitTest {
+ public:
+  ClonerTest()
+      : graph_(CreateGraph()), entry_block_(nullptr), exit_block_(nullptr), parameter_(nullptr) {}
+
+  void CreateBasicLoopControlFlow(/* out */ HBasicBlock** header_p,
+                                  /* out */ HBasicBlock** body_p) {
+    entry_block_ = new (GetAllocator()) HBasicBlock(graph_);
+    graph_->AddBlock(entry_block_);
+    graph_->SetEntryBlock(entry_block_);
+
+    HBasicBlock* loop_preheader = new (GetAllocator()) HBasicBlock(graph_);
+    HBasicBlock* loop_header = new (GetAllocator()) HBasicBlock(graph_);
+    HBasicBlock* loop_body = new (GetAllocator()) HBasicBlock(graph_);
+    HBasicBlock* loop_exit = new (GetAllocator()) HBasicBlock(graph_);
+
+    graph_->AddBlock(loop_preheader);
+    graph_->AddBlock(loop_header);
+    graph_->AddBlock(loop_body);
+    graph_->AddBlock(loop_exit);
+
+    exit_block_ = new (GetAllocator()) HBasicBlock(graph_);
+    graph_->AddBlock(exit_block_);
+    graph_->SetExitBlock(exit_block_);
+
+    entry_block_->AddSuccessor(loop_preheader);
+    loop_preheader->AddSuccessor(loop_header);
+    // Loop exit first to have a proper exit condition/target for HIf.
+    loop_header->AddSuccessor(loop_exit);
+    loop_header->AddSuccessor(loop_body);
+    loop_body->AddSuccessor(loop_header);
+    loop_exit->AddSuccessor(exit_block_);
+
+    *header_p = loop_header;
+    *body_p = loop_body;
+
+    parameter_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
+                                                      dex::TypeIndex(0),
+                                                      0,
+                                                      DataType::Type::kInt32);
+    entry_block_->AddInstruction(parameter_);
+    loop_exit->AddInstruction(new (GetAllocator()) HReturnVoid());
+    exit_block_->AddInstruction(new (GetAllocator()) HExit());
+  }
+
+  void CreateBasicLoopDataFlow(HBasicBlock* loop_header, HBasicBlock* loop_body) {
+    uint32_t dex_pc = 0;
+
+    // Entry block.
+    HIntConstant* const_0 = graph_->GetIntConstant(0);
+    HIntConstant* const_1 = graph_->GetIntConstant(1);
+    HIntConstant* const_128 = graph_->GetIntConstant(128);
+
+    // Header block.
+    HPhi* phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
+    HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck();
+
+    loop_header->AddPhi(phi);
+    loop_header->AddInstruction(suspend_check);
+    loop_header->AddInstruction(new (GetAllocator()) HGreaterThanOrEqual(phi, const_128));
+    loop_header->AddInstruction(new (GetAllocator()) HIf(parameter_));
+
+    // Loop body block.
+    HInstruction* null_check = new (GetAllocator()) HNullCheck(parameter_, dex_pc);
+    HInstruction* array_length = new (GetAllocator()) HArrayLength(null_check, dex_pc);
+    HInstruction* bounds_check = new (GetAllocator()) HBoundsCheck(phi, array_length, dex_pc);
+    HInstruction* array_get =
+        new (GetAllocator()) HArrayGet(null_check, bounds_check, DataType::Type::kInt32, dex_pc);
+    HInstruction* add =  new (GetAllocator()) HAdd(DataType::Type::kInt32, array_get, const_1);
+    HInstruction* array_set =
+        new (GetAllocator()) HArraySet(null_check, bounds_check, add, DataType::Type::kInt32, dex_pc);
+    HInstruction* induction_inc = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi, const_1);
+
+    loop_body->AddInstruction(null_check);
+    loop_body->AddInstruction(array_length);
+    loop_body->AddInstruction(bounds_check);
+    loop_body->AddInstruction(array_get);
+    loop_body->AddInstruction(add);
+    loop_body->AddInstruction(array_set);
+    loop_body->AddInstruction(induction_inc);
+    loop_body->AddInstruction(new (GetAllocator()) HGoto());
+
+    phi->AddInput(const_0);
+    phi->AddInput(induction_inc);
+
+    graph_->SetHasBoundsChecks(true);
+
+    // Adjust HEnvironment for each instruction which require that.
+    ArenaVector<HInstruction*> current_locals({phi, const_128, parameter_},
+                                              GetAllocator()->Adapter(kArenaAllocInstruction));
+
+    HEnvironment* env = ManuallyBuildEnvFor(suspend_check, &current_locals);
+    null_check->CopyEnvironmentFrom(env);
+    bounds_check->CopyEnvironmentFrom(env);
+  }
+
+  HEnvironment* ManuallyBuildEnvFor(HInstruction* instruction,
+                                    ArenaVector<HInstruction*>* current_locals) {
+    HEnvironment* environment = new (GetAllocator()) HEnvironment(
+        (GetAllocator()),
+        current_locals->size(),
+        graph_->GetArtMethod(),
+        instruction->GetDexPc(),
+        instruction);
+
+    environment->CopyFrom(ArrayRef<HInstruction* const>(*current_locals));
+    instruction->SetRawEnvironment(environment);
+    return environment;
+  }
+
+  bool CheckGraph() {
+    GraphChecker checker(graph_);
+    checker.Run();
+    if (!checker.IsValid()) {
+      for (const std::string& error : checker.GetErrors()) {
+        std::cout << error << std::endl;
+      }
+      return false;
+    }
+    return true;
+  }
+
+  HGraph* graph_;
+
+  HBasicBlock* entry_block_;
+  HBasicBlock* exit_block_;
+
+  HInstruction* parameter_;
+};
+
+TEST_F(ClonerTest, IndividualInstrCloner) {
+  HBasicBlock* header = nullptr;
+  HBasicBlock* loop_body = nullptr;
+
+  CreateBasicLoopControlFlow(&header, &loop_body);
+  CreateBasicLoopDataFlow(header, loop_body);
+  graph_->BuildDominatorTree();
+  ASSERT_TRUE(CheckGraph());
+
+  HSuspendCheck* old_suspend_check = header->GetLoopInformation()->GetSuspendCheck();
+  CloneAndReplaceInstructionVisitor visitor(graph_);
+  // Do instruction cloning and replacement twice with different visiting order.
+
+  visitor.VisitInsertionOrder();
+  size_t instr_replaced_by_clones_count = visitor.GetInstrReplacedByClonesCount();
+  EXPECT_EQ(instr_replaced_by_clones_count, 12u);
+  EXPECT_TRUE(CheckGraph());
+
+  visitor.VisitReversePostOrder();
+  instr_replaced_by_clones_count = visitor.GetInstrReplacedByClonesCount();
+  EXPECT_EQ(instr_replaced_by_clones_count, 24u);
+  EXPECT_TRUE(CheckGraph());
+
+  HSuspendCheck* new_suspend_check = header->GetLoopInformation()->GetSuspendCheck();
+  EXPECT_NE(new_suspend_check, old_suspend_check);
+  EXPECT_NE(new_suspend_check, nullptr);
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index ad29ba5..d270c6a 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -93,6 +93,136 @@
   }
 }
 
+/**
+ * Returns true if loop is guarded by "a cmp b" on entry.
+ */
+static bool IsGuardedBy(HLoopInformation* loop,
+                        IfCondition cmp,
+                        HInstruction* a,
+                        HInstruction* b) {
+  // Chase back through straightline code to the first potential
+  // block that has a control dependence.
+  // guard:   if (x) bypass
+  //              |
+  // entry: straightline code
+  //              |
+  //           preheader
+  //              |
+  //            header
+  HBasicBlock* guard = loop->GetPreHeader();
+  HBasicBlock* entry = loop->GetHeader();
+  while (guard->GetPredecessors().size() == 1 &&
+         guard->GetSuccessors().size() == 1) {
+    entry = guard;
+    guard = guard->GetSinglePredecessor();
+  }
+  // Find guard.
+  HInstruction* control = guard->GetLastInstruction();
+  if (!control->IsIf()) {
+    return false;
+  }
+  HIf* ifs = control->AsIf();
+  HInstruction* if_expr = ifs->InputAt(0);
+  if (if_expr->IsCondition()) {
+    IfCondition other_cmp = ifs->IfTrueSuccessor() == entry
+        ? if_expr->AsCondition()->GetCondition()
+        : if_expr->AsCondition()->GetOppositeCondition();
+    if (if_expr->InputAt(0) == a && if_expr->InputAt(1) == b) {
+      return cmp == other_cmp;
+    } else if (if_expr->InputAt(1) == a && if_expr->InputAt(0) == b) {
+      switch (cmp) {
+        case kCondLT: return other_cmp == kCondGT;
+        case kCondLE: return other_cmp == kCondGE;
+        case kCondGT: return other_cmp == kCondLT;
+        case kCondGE: return other_cmp == kCondLE;
+        default: LOG(FATAL) << "unexpected cmp: " << cmp;
+      }
+    }
+  }
+  return false;
+}
+
+/* Finds first loop header phi use. */
+HInstruction* FindFirstLoopHeaderPhiUse(HLoopInformation* loop, HInstruction* instruction) {
+  for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
+    if (use.GetUser()->GetBlock() == loop->GetHeader() &&
+        use.GetUser()->IsPhi() &&
+        use.GetUser()->InputAt(1) == instruction) {
+      return use.GetUser();
+    }
+  }
+  return nullptr;
+}
+
+/**
+ * Relinks the Phi structure after break-loop rewriting.
+ */
+bool FixOutsideUse(HLoopInformation* loop,
+                   HInstruction* instruction,
+                   HInstruction* replacement,
+                   bool rewrite) {
+  // Deal with regular uses.
+  const HUseList<HInstruction*>& uses = instruction->GetUses();
+  for (auto it = uses.begin(), end = uses.end(); it != end; ) {
+    HInstruction* user = it->GetUser();
+    size_t index = it->GetIndex();
+    ++it;  // increment prior to potential removal
+    if (user->GetBlock()->GetLoopInformation() != loop) {
+      if (replacement == nullptr) {
+        return false;
+      } else if (rewrite) {
+        user->ReplaceInput(replacement, index);
+      }
+    }
+  }
+  // Deal with environment uses.
+  const HUseList<HEnvironment*>& env_uses = instruction->GetEnvUses();
+  for (auto it = env_uses.begin(), end = env_uses.end(); it != end;) {
+    HEnvironment* user = it->GetUser();
+    size_t index = it->GetIndex();
+    ++it;  // increment prior to potential removal
+    if (user->GetHolder()->GetBlock()->GetLoopInformation() != loop) {
+      if (replacement == nullptr) {
+        return false;
+      } else if (rewrite) {
+        user->RemoveAsUserOfInput(index);
+        user->SetRawEnvAt(index, replacement);
+        replacement->AddEnvUseAt(user, index);
+      }
+    }
+  }
+  return true;
+}
+
+/**
+ * Test and rewrite the loop body of a break-loop. Returns true on success.
+ */
+bool RewriteBreakLoopBody(HLoopInformation* loop,
+                          HBasicBlock* body,
+                          HInstruction* cond,
+                          HInstruction* index,
+                          HInstruction* upper,
+                          bool rewrite) {
+  // Deal with Phis. Outside use prohibited, except for index (which gets exit value).
+  for (HInstructionIterator it(loop->GetHeader()->GetPhis()); !it.Done(); it.Advance()) {
+    HInstruction* exit_value = it.Current() == index ? upper : nullptr;
+    if (!FixOutsideUse(loop, it.Current(), exit_value, rewrite)) {
+      return false;
+    }
+  }
+  // Deal with other statements in header.
+  for (HInstruction* m = cond->GetPrevious(), *p = nullptr; m && !m->IsSuspendCheck(); m = p) {
+    p = m->GetPrevious();
+    if (rewrite) {
+      m->MoveBefore(body->GetFirstInstruction(), false);
+    }
+    if (!FixOutsideUse(loop, m, FindFirstLoopHeaderPhiUse(loop, m), rewrite)) {
+      return false;
+    }
+  }
+  return true;
+}
+
 //
 // Class methods.
 //
@@ -754,6 +884,10 @@
   return nullptr;
 }
 
+//
+// Loop trip count analysis methods.
+//
+
 void HInductionVarAnalysis::VisitControl(HLoopInformation* loop) {
   HInstruction* control = loop->GetHeader()->GetLastInstruction();
   if (control->IsIf()) {
@@ -774,15 +908,16 @@
       if (a == nullptr || b == nullptr) {
         return;  // Loop control is not a sequence.
       } else if (if_true->GetLoopInformation() != loop && if_false->GetLoopInformation() == loop) {
-        VisitCondition(loop, a, b, type, condition->GetOppositeCondition());
+        VisitCondition(loop, if_false, a, b, type, condition->GetOppositeCondition());
       } else if (if_true->GetLoopInformation() == loop && if_false->GetLoopInformation() != loop) {
-        VisitCondition(loop, a, b, type, condition->GetCondition());
+        VisitCondition(loop, if_true, a, b, type, condition->GetCondition());
       }
     }
   }
 }
 
 void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop,
+                                           HBasicBlock* body,
                                            InductionInfo* a,
                                            InductionInfo* b,
                                            DataType::Type type,
@@ -790,11 +925,11 @@
   if (a->induction_class == kInvariant && b->induction_class == kLinear) {
     // Swap condition if induction is at right-hand-side (e.g. U > i is same as i < U).
     switch (cmp) {
-      case kCondLT: VisitCondition(loop, b, a, type, kCondGT); break;
-      case kCondLE: VisitCondition(loop, b, a, type, kCondGE); break;
-      case kCondGT: VisitCondition(loop, b, a, type, kCondLT); break;
-      case kCondGE: VisitCondition(loop, b, a, type, kCondLE); break;
-      case kCondNE: VisitCondition(loop, b, a, type, kCondNE); break;
+      case kCondLT: VisitCondition(loop, body, b, a, type, kCondGT); break;
+      case kCondLE: VisitCondition(loop, body, b, a, type, kCondGE); break;
+      case kCondGT: VisitCondition(loop, body, b, a, type, kCondLT); break;
+      case kCondGE: VisitCondition(loop, body, b, a, type, kCondLE); break;
+      case kCondNE: VisitCondition(loop, body, b, a, type, kCondNE); break;
       default: break;
     }
   } else if (a->induction_class == kLinear && b->induction_class == kInvariant) {
@@ -802,24 +937,30 @@
     InductionInfo* lower_expr = a->op_b;
     InductionInfo* upper_expr = b;
     InductionInfo* stride_expr = a->op_a;
-    // Constant stride?
+    // Test for constant stride and integral condition.
     int64_t stride_value = 0;
     if (!IsExact(stride_expr, &stride_value)) {
-      return;
+      return;  // unknown stride
+    } else if (type != DataType::Type::kInt32 && type != DataType::Type::kInt64) {
+      return;  // not integral
     }
-    // Rewrite condition i != U into strict end condition i < U or i > U if this end condition
-    // is reached exactly (tested by verifying if the loop has a unit stride and the non-strict
-    // condition would be always taken).
+    // Since loops with a i != U condition will not be normalized by the method below, first
+    // try to rewrite a break-loop with terminating condition i != U into an equivalent loop
+    // with non-strict end condition i <= U or i >= U if such a rewriting is possible and safe.
+    if (cmp == kCondNE && RewriteBreakLoop(loop, body, stride_value, type)) {
+      cmp = stride_value > 0 ? kCondLE : kCondGE;
+    }
+    // If this rewriting failed, try to rewrite condition i != U into strict end condition i < U
+    // or i > U if this end condition is reached exactly (tested by verifying if the loop has a
+    // unit stride and the non-strict condition would be always taken).
     if (cmp == kCondNE && ((stride_value == +1 && IsTaken(lower_expr, upper_expr, kCondLE)) ||
                            (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGE)))) {
       cmp = stride_value > 0 ? kCondLT : kCondGT;
     }
-    // Only accept integral condition. A mismatch between the type of condition and the induction
-    // is only allowed if the, necessarily narrower, induction range fits the narrower control.
-    if (type != DataType::Type::kInt32 && type != DataType::Type::kInt64) {
-      return;  // not integral
-    } else if (type != a->type &&
-               !FitsNarrowerControl(lower_expr, upper_expr, stride_value, a->type, cmp)) {
+    // A mismatch between the type of condition and the induction is only allowed if the,
+    // necessarily narrower, induction range fits the narrower control.
+    if (type != a->type &&
+        !FitsNarrowerControl(lower_expr, upper_expr, stride_value, a->type, cmp)) {
       return;  // mismatched type
     }
     // Normalize a linear loop control with a nonzero stride:
@@ -984,6 +1125,69 @@
          IsAtMost(upper_expr, &value)  && value <= max;
 }
 
+bool HInductionVarAnalysis::RewriteBreakLoop(HLoopInformation* loop,
+                                             HBasicBlock* body,
+                                             int64_t stride_value,
+                                             DataType::Type type) {
+  // Only accept unit stride.
+  if (std::abs(stride_value) != 1) {
+    return false;
+  }
+  // Simple terminating i != U condition, used nowhere else.
+  HIf* ifs = loop->GetHeader()->GetLastInstruction()->AsIf();
+  HInstruction* cond = ifs->InputAt(0);
+  if (ifs->GetPrevious() != cond || !cond->HasOnlyOneNonEnvironmentUse()) {
+    return false;
+  }
+  int c = LookupInfo(loop, cond->InputAt(0))->induction_class == kLinear ? 0 : 1;
+  HInstruction* index = cond->InputAt(c);
+  HInstruction* upper = cond->InputAt(1 - c);
+  // Safe to rewrite into i <= U?
+  IfCondition cmp = stride_value > 0 ? kCondLE : kCondGE;
+  if (!index->IsPhi() || !IsFinite(LookupInfo(loop, upper), stride_value, type, cmp)) {
+    return false;
+  }
+  // Body consists of update to index i only, used nowhere else.
+  if (body->GetSuccessors().size() != 1 ||
+      body->GetSingleSuccessor() != loop->GetHeader() ||
+      !body->GetPhis().IsEmpty() ||
+      body->GetInstructions().IsEmpty() ||
+      body->GetFirstInstruction() != index->InputAt(1) ||
+      !body->GetFirstInstruction()->HasOnlyOneNonEnvironmentUse() ||
+      !body->GetFirstInstruction()->GetNext()->IsGoto()) {
+    return false;
+  }
+  // Always taken or guarded by enclosing condition.
+  if (!IsTaken(LookupInfo(loop, index)->op_b, LookupInfo(loop, upper), cmp) &&
+      !IsGuardedBy(loop, cmp, index->InputAt(0), upper)) {
+    return false;
+  }
+  // Test if break-loop body can be written, and do so on success.
+  if (RewriteBreakLoopBody(loop, body, cond, index, upper, /*rewrite*/ false)) {
+    RewriteBreakLoopBody(loop, body, cond, index, upper, /*rewrite*/ true);
+  } else {
+    return false;
+  }
+  // Rewrite condition in HIR.
+  if (ifs->IfTrueSuccessor() != body) {
+    cmp = (cmp == kCondLE) ? kCondGT : kCondLT;
+  }
+  HInstruction* rep = nullptr;
+  switch (cmp) {
+    case kCondLT: rep = new (graph_->GetAllocator()) HLessThan(index, upper); break;
+    case kCondGT: rep = new (graph_->GetAllocator()) HGreaterThan(index, upper); break;
+    case kCondLE: rep = new (graph_->GetAllocator()) HLessThanOrEqual(index, upper); break;
+    case kCondGE: rep = new (graph_->GetAllocator()) HGreaterThanOrEqual(index, upper); break;
+    default: LOG(FATAL) << cmp; UNREACHABLE();
+  }
+  loop->GetHeader()->ReplaceAndRemoveInstructionWith(cond, rep);
+  return true;
+}
+
+//
+// Helper methods.
+//
+
 void HInductionVarAnalysis::AssignInfo(HLoopInformation* loop,
                                        HInstruction* instruction,
                                        InductionInfo* info) {
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 8737b89..acad77d 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -195,9 +195,14 @@
                                  HInstruction* entry_phi,
                                  HTypeConversion* conversion);
 
+  //
+  // Loop trip count analysis methods.
+  //
+
   // Trip count information.
   void VisitControl(HLoopInformation* loop);
   void VisitCondition(HLoopInformation* loop,
+                      HBasicBlock* body,
                       InductionInfo* a,
                       InductionInfo* b,
                       DataType::Type type,
@@ -219,6 +224,14 @@
                            int64_t stride_value,
                            DataType::Type type,
                            IfCondition cmp);
+  bool RewriteBreakLoop(HLoopInformation* loop,
+                        HBasicBlock* body,
+                        int64_t stride_value,
+                        DataType::Type type);
+
+  //
+  // Helper methods.
+  //
 
   // Assign and lookup.
   void AssignInfo(HLoopInformation* loop, HInstruction* instruction, InductionInfo* info);
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 61840cc..978d0c2 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -29,6 +29,7 @@
 #include "driver/compiler_options.h"
 #include "imtable-inl.h"
 #include "mirror/dex_cache.h"
+#include "oat_file.h"
 #include "optimizing_compiler_stats.h"
 #include "quicken_info.h"
 #include "scoped_thread_state_change-inl.h"
@@ -447,7 +448,8 @@
                                                      /* expandable */ false,
                                                      kArenaAllocGraphBuilder);
   locations->ClearAllBits();
-  dex_file_->DecodeDebugPositionInfo(code_item_, Callback::Position, locations);
+  uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file_, code_item_);
+  dex_file_->DecodeDebugPositionInfo(code_item_, debug_info_offset, Callback::Position, locations);
   // Instruction-specific tweaks.
   IterationRange<DexInstructionIterator> instructions = code_item_->Instructions();
   for (const DexInstructionPcPair& inst : instructions) {
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index fbfee12..4c18e16 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -27,6 +27,10 @@
 
 namespace art {
 
+// Whether to run an exhaustive test of individual HInstructions cloning when each instruction
+// is replaced with its copy if it is clonable.
+static constexpr bool kTestInstructionClonerExhaustively = false;
+
 class InstructionSimplifierVisitor : public HGraphDelegateVisitor {
  public:
   InstructionSimplifierVisitor(HGraph* graph,
@@ -130,6 +134,11 @@
 };
 
 void InstructionSimplifier::Run() {
+  if (kTestInstructionClonerExhaustively) {
+    CloneAndReplaceInstructionVisitor visitor(graph_);
+    visitor.VisitReversePostOrder();
+  }
+
   InstructionSimplifierVisitor visitor(graph_, codegen_, compiler_driver_, stats_);
   visitor.Run();
 }
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index fff61f5..fa580d9 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -845,6 +845,13 @@
   DCHECK(!instruction->HasEnvironment());
 }
 
+void HBasicBlock::ReplaceAndRemovePhiWith(HPhi* initial, HPhi* replacement) {
+  DCHECK(initial->GetBlock() == this);
+  InsertPhiAfter(replacement, initial);
+  initial->ReplaceWith(replacement);
+  RemovePhi(initial);
+}
+
 void HBasicBlock::ReplaceAndRemoveInstructionWith(HInstruction* initial,
                                                   HInstruction* replacement) {
   DCHECK(initial->GetBlock() == this);
@@ -2907,6 +2914,28 @@
   env_uses_.clear();
 }
 
+HInstruction* ReplaceInstrOrPhiByClone(HInstruction* instr) {
+  HInstruction* clone = instr->Clone(instr->GetBlock()->GetGraph()->GetAllocator());
+  HBasicBlock* block = instr->GetBlock();
+
+  if (instr->IsPhi()) {
+    HPhi* phi = instr->AsPhi();
+    DCHECK(!phi->HasEnvironment());
+    HPhi* phi_clone = clone->AsPhi();
+    block->ReplaceAndRemovePhiWith(phi, phi_clone);
+  } else {
+    block->ReplaceAndRemoveInstructionWith(instr, clone);
+    if (instr->HasEnvironment()) {
+      clone->CopyEnvironmentFrom(instr->GetEnvironment());
+      HLoopInformation* loop_info = block->GetLoopInformation();
+      if (instr->IsSuspendCheck() && loop_info != nullptr) {
+        loop_info->SetSuspendCheck(clone->AsSuspendCheck());
+      }
+    }
+  }
+  return clone;
+}
+
 // Returns an instruction with the opposite Boolean value from 'cond'.
 HInstruction* HGraph::InsertOppositeCondition(HInstruction* cond, HInstruction* cursor) {
   ArenaAllocator* allocator = GetAllocator();
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 6672901..66d5bfe 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1160,6 +1160,8 @@
   // Insert `instruction` before/after an existing instruction `cursor`.
   void InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor);
   void InsertInstructionAfter(HInstruction* instruction, HInstruction* cursor);
+  // Replace phi `initial` with `replacement` within this block.
+  void ReplaceAndRemovePhiWith(HPhi* initial, HPhi* replacement);
   // Replace instruction `initial` with `replacement` within this block.
   void ReplaceAndRemoveInstructionWith(HInstruction* initial,
                                        HInstruction* replacement);
@@ -1480,18 +1482,31 @@
 #undef FORWARD_DECLARATION
 
 #define DECLARE_INSTRUCTION(type)                                         \
+  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();                                             \
   }                                                                       \
+  HInstruction* Clone(ArenaAllocator* arena) const OVERRIDE {             \
+    DCHECK(IsClonable());                                                 \
+    return new (arena) H##type(*this->As##type());                        \
+  }                                                                       \
   void Accept(HGraphVisitor* visitor) OVERRIDE
 
 #define DECLARE_ABSTRACT_INSTRUCTION(type)                              \
+  private:                                                              \
+  H##type& operator=(const H##type&) = delete;                          \
+  public:                                                               \
   bool Is##type() const { return As##type() != nullptr; }               \
   const H##type* As##type() const { return this; }                      \
   H##type* As##type() { return this; }
 
+#define DEFAULT_COPY_CONSTRUCTOR(type)                                  \
+  explicit H##type(const H##type& other) = default;
+
 template <typename T>
 class HUseListNode : public ArenaObject<kArenaAllocUseListNode>,
                      public IntrusiveForwardListNode<HUseListNode<T>> {
@@ -2182,6 +2197,25 @@
   FOR_EACH_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
 #undef INSTRUCTION_TYPE_CHECK
 
+  // Return a clone of the instruction if it is clonable (shallow copy by default, custom copy
+  // if a custom copy-constructor is provided for a particular type). If IsClonable() is false for
+  // the instruction then the behaviour of this function is undefined.
+  //
+  // Note: It is semantically valid to create a clone of the instruction only until
+  // prepare_for_register_allocator phase as lifetime, intervals and codegen info are not
+  // copied.
+  //
+  // Note: HEnvironment and some other fields are not copied and are set to default values, see
+  // 'explicit HInstruction(const HInstruction& other)' for details.
+  virtual HInstruction* Clone(ArenaAllocator* arena ATTRIBUTE_UNUSED) const {
+    LOG(FATAL) << "Cloning is not implemented for the instruction " <<
+                  DebugName() << " " << GetId();
+    UNREACHABLE();
+  }
+
+  // Return whether instruction can be cloned (copied).
+  virtual bool IsClonable() const { return false; }
+
   // Returns whether the instruction can be moved within the graph.
   // TODO: this method is used by LICM and GVN with possibly different
   //       meanings? split and rename?
@@ -2298,6 +2332,30 @@
     packed_fields_ = BitFieldType::Update(value, packed_fields_);
   }
 
+  // Copy construction for the instruction (used for Clone function).
+  //
+  // Fields (e.g. lifetime, intervals and codegen info) associated with phases starting from
+  // prepare_for_register_allocator are not copied (set to default values).
+  //
+  // Copy constructors must be provided for every HInstruction type; default copy constructor is
+  // fine for most of them. However for some of the instructions a custom copy constructor must be
+  // specified (when instruction has non-trivially copyable fields and must have a special behaviour
+  // for copying them).
+  explicit HInstruction(const HInstruction& other)
+      : previous_(nullptr),
+        next_(nullptr),
+        block_(nullptr),
+        dex_pc_(other.dex_pc_),
+        id_(-1),
+        ssa_index_(-1),
+        packed_fields_(other.packed_fields_),
+        environment_(nullptr),
+        locations_(nullptr),
+        live_interval_(nullptr),
+        lifetime_position_(kNoLifetime),
+        side_effects_(other.side_effects_),
+        reference_type_handle_(other.reference_type_handle_) {}
+
  private:
   void FixUpUserRecordsAfterUseInsertion(HUseList<HInstruction*>::iterator fixup_end) {
     auto before_use_node = uses_.before_begin();
@@ -2387,8 +2445,6 @@
   friend class HEnvironment;
   friend class HGraph;
   friend class HInstructionList;
-
-  DISALLOW_COPY_AND_ASSIGN(HInstruction);
 };
 std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& rhs);
 
@@ -2484,10 +2540,9 @@
       : HInstruction(side_effects, dex_pc),
         inputs_(number_of_inputs, allocator->Adapter(kind)) {}
 
-  ArenaVector<HUserRecord<HInstruction*>> inputs_;
+  DEFAULT_COPY_CONSTRUCTOR(VariableInputSizeInstruction);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVariableInputSizeInstruction);
+  ArenaVector<HUserRecord<HInstruction*>> inputs_;
 };
 
 template<size_t N>
@@ -2502,6 +2557,9 @@
     return ArrayRef<HUserRecord<HInstruction*>>(inputs_);
   }
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(TemplateInstruction<N>);
+
  private:
   std::array<HUserRecord<HInstruction*>, N> inputs_;
 
@@ -2522,6 +2580,9 @@
     return ArrayRef<HUserRecord<HInstruction*>>();
   }
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(TemplateInstruction<0>);
+
  private:
   friend class SsaBuilder;
 };
@@ -2547,6 +2608,7 @@
   static_assert(kNumberOfExpressionPackedBits <= HInstruction::kMaxNumberOfPackedBits,
                 "Too many packed fields.");
   using TypeField = BitField<DataType::Type, kFieldType, kFieldTypeSize>;
+  DEFAULT_COPY_CONSTRUCTOR(Expression<N>);
 };
 
 // Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow
@@ -2560,8 +2622,8 @@
 
   DECLARE_INSTRUCTION(ReturnVoid);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HReturnVoid);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(ReturnVoid);
 };
 
 // Represents dex's RETURN opcodes. A HReturn is a control flow
@@ -2577,8 +2639,8 @@
 
   DECLARE_INSTRUCTION(Return);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HReturn);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Return);
 };
 
 class HPhi FINAL : public HVariableInputSizeInstruction {
@@ -2604,6 +2666,8 @@
     SetPackedFlag<kFlagCanBeNull>(true);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   // Returns a type equivalent to the given `type`, but that a `HPhi` can hold.
   static DataType::Type ToPhiType(DataType::Type type) {
     return DataType::Kind(type);
@@ -2666,6 +2730,9 @@
 
   DECLARE_INSTRUCTION(Phi);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Phi);
+
  private:
   static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
   static constexpr size_t kFieldTypeSize =
@@ -2677,8 +2744,6 @@
   using TypeField = BitField<DataType::Type, kFieldType, kFieldTypeSize>;
 
   const uint32_t reg_number_;
-
-  DISALLOW_COPY_AND_ASSIGN(HPhi);
 };
 
 // The exit instruction is the only instruction of the exit block.
@@ -2692,8 +2757,8 @@
 
   DECLARE_INSTRUCTION(Exit);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HExit);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Exit);
 };
 
 // Jumps from one block to another.
@@ -2701,6 +2766,7 @@
  public:
   explicit HGoto(uint32_t dex_pc = kNoDexPc) : HTemplateInstruction(SideEffects::None(), dex_pc) {}
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool IsControlFlow() const OVERRIDE { return true; }
 
   HBasicBlock* GetSuccessor() const {
@@ -2709,8 +2775,8 @@
 
   DECLARE_INSTRUCTION(Goto);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HGoto);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Goto);
 };
 
 class HConstant : public HExpression<0> {
@@ -2733,8 +2799,8 @@
 
   DECLARE_ABSTRACT_INSTRUCTION(Constant);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HConstant);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Constant);
 };
 
 class HNullConstant FINAL : public HConstant {
@@ -2752,12 +2818,14 @@
 
   DECLARE_INSTRUCTION(NullConstant);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(NullConstant);
+
  private:
   explicit HNullConstant(uint32_t dex_pc = kNoDexPc)
       : HConstant(DataType::Type::kReference, dex_pc) {}
 
   friend class HGraph;
-  DISALLOW_COPY_AND_ASSIGN(HNullConstant);
 };
 
 // Constants of the type int. Those can be from Dex instructions, or
@@ -2789,6 +2857,9 @@
 
   DECLARE_INSTRUCTION(IntConstant);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(IntConstant);
+
  private:
   explicit HIntConstant(int32_t value, uint32_t dex_pc = kNoDexPc)
       : HConstant(DataType::Type::kInt32, dex_pc), value_(value) {}
@@ -2800,7 +2871,6 @@
   friend class HGraph;
   ART_FRIEND_TEST(GraphTest, InsertInstructionBefore);
   ART_FRIEND_TYPED_TEST(ParallelMoveTest, ConstantLast);
-  DISALLOW_COPY_AND_ASSIGN(HIntConstant);
 };
 
 class HLongConstant FINAL : public HConstant {
@@ -2823,6 +2893,9 @@
 
   DECLARE_INSTRUCTION(LongConstant);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(LongConstant);
+
  private:
   explicit HLongConstant(int64_t value, uint32_t dex_pc = kNoDexPc)
       : HConstant(DataType::Type::kInt64, dex_pc), value_(value) {}
@@ -2830,7 +2903,6 @@
   const int64_t value_;
 
   friend class HGraph;
-  DISALLOW_COPY_AND_ASSIGN(HLongConstant);
 };
 
 class HFloatConstant FINAL : public HConstant {
@@ -2872,6 +2944,9 @@
 
   DECLARE_INSTRUCTION(FloatConstant);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(FloatConstant);
+
  private:
   explicit HFloatConstant(float value, uint32_t dex_pc = kNoDexPc)
       : HConstant(DataType::Type::kFloat32, dex_pc), value_(value) {}
@@ -2883,7 +2958,6 @@
   // Only the SsaBuilder and HGraph can create floating-point constants.
   friend class SsaBuilder;
   friend class HGraph;
-  DISALLOW_COPY_AND_ASSIGN(HFloatConstant);
 };
 
 class HDoubleConstant FINAL : public HConstant {
@@ -2923,6 +2997,9 @@
 
   DECLARE_INSTRUCTION(DoubleConstant);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(DoubleConstant);
+
  private:
   explicit HDoubleConstant(double value, uint32_t dex_pc = kNoDexPc)
       : HConstant(DataType::Type::kFloat64, dex_pc), value_(value) {}
@@ -2934,7 +3011,6 @@
   // Only the SsaBuilder and HGraph can create floating-point constants.
   friend class SsaBuilder;
   friend class HGraph;
-  DISALLOW_COPY_AND_ASSIGN(HDoubleConstant);
 };
 
 // Conditional branch. A block ending with an HIf instruction must have
@@ -2946,6 +3022,7 @@
     SetRawInputAt(0, input);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool IsControlFlow() const OVERRIDE { return true; }
 
   HBasicBlock* IfTrueSuccessor() const {
@@ -2958,8 +3035,8 @@
 
   DECLARE_INSTRUCTION(If);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HIf);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(If);
 };
 
 
@@ -3012,6 +3089,9 @@
 
   DECLARE_INSTRUCTION(TryBoundary);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(TryBoundary);
+
  private:
   static constexpr size_t kFieldBoundaryKind = kNumberOfGenericPackedBits;
   static constexpr size_t kFieldBoundaryKindSize =
@@ -3021,8 +3101,6 @@
   static_assert(kNumberOfTryBoundaryPackedBits <= kMaxNumberOfPackedBits,
                 "Too many packed fields.");
   using BoundaryKindField = BitField<BoundaryKind, kFieldBoundaryKind, kFieldBoundaryKindSize>;
-
-  DISALLOW_COPY_AND_ASSIGN(HTryBoundary);
 };
 
 // Deoptimize to interpreter, upon checking a condition.
@@ -3045,6 +3123,8 @@
     SetRawInputAt(0, cond);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   // Use this constructor when the `HDeoptimize` guards an instruction, and any user
   // that relies on the deoptimization to pass should have its input be the `HDeoptimize`
   // instead of `guard`.
@@ -3098,6 +3178,9 @@
 
   DECLARE_INSTRUCTION(Deoptimize);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Deoptimize);
+
  private:
   static constexpr size_t kFieldCanBeMoved = kNumberOfGenericPackedBits;
   static constexpr size_t kFieldDeoptimizeKind = kNumberOfGenericPackedBits + 1;
@@ -3109,8 +3192,6 @@
                 "Too many packed fields.");
   using DeoptimizeKindField =
       BitField<DeoptimizationKind, kFieldDeoptimizeKind, kFieldDeoptimizeKindSize>;
-
-  DISALLOW_COPY_AND_ASSIGN(HDeoptimize);
 };
 
 // Represents a should_deoptimize flag. Currently used for CHA-based devirtualization.
@@ -3136,8 +3217,8 @@
 
   DECLARE_INSTRUCTION(ShouldDeoptimizeFlag);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HShouldDeoptimizeFlag);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(ShouldDeoptimizeFlag);
 };
 
 // Represents the ArtMethod that was passed as a first argument to
@@ -3150,8 +3231,8 @@
 
   DECLARE_INSTRUCTION(CurrentMethod);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HCurrentMethod);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(CurrentMethod);
 };
 
 // Fetches an ArtMethod from the virtual table or the interface method table
@@ -3174,6 +3255,7 @@
     SetRawInputAt(0, cls);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool CanBeMoved() const OVERRIDE { return true; }
   bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
     return other->AsClassTableGet()->GetIndex() == index_ &&
@@ -3185,6 +3267,9 @@
 
   DECLARE_INSTRUCTION(ClassTableGet);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(ClassTableGet);
+
  private:
   static constexpr size_t kFieldTableKind = kNumberOfExpressionPackedBits;
   static constexpr size_t kFieldTableKindSize =
@@ -3196,8 +3281,6 @@
 
   // The index of the ArtMethod in the table.
   const size_t index_;
-
-  DISALLOW_COPY_AND_ASSIGN(HClassTableGet);
 };
 
 // PackedSwitch (jump table). A block ending with a PackedSwitch instruction will
@@ -3215,6 +3298,8 @@
     SetRawInputAt(0, input);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   bool IsControlFlow() const OVERRIDE { return true; }
 
   int32_t GetStartValue() const { return start_value_; }
@@ -3227,11 +3312,12 @@
   }
   DECLARE_INSTRUCTION(PackedSwitch);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(PackedSwitch);
+
  private:
   const int32_t start_value_;
   const uint32_t num_entries_;
-
-  DISALLOW_COPY_AND_ASSIGN(HPackedSwitch);
 };
 
 class HUnaryOperation : public HExpression<1> {
@@ -3241,6 +3327,9 @@
     SetRawInputAt(0, input);
   }
 
+  // All of the UnaryOperation instructions are clonable.
+  bool IsClonable() const OVERRIDE { return true; }
+
   HInstruction* GetInput() const { return InputAt(0); }
   DataType::Type GetResultType() const { return GetType(); }
 
@@ -3262,8 +3351,8 @@
 
   DECLARE_ABSTRACT_INSTRUCTION(UnaryOperation);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HUnaryOperation);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(UnaryOperation);
 };
 
 class HBinaryOperation : public HExpression<2> {
@@ -3278,6 +3367,9 @@
     SetRawInputAt(1, right);
   }
 
+  // All of the BinaryOperation instructions are clonable.
+  bool IsClonable() const OVERRIDE { return true; }
+
   HInstruction* GetLeft() const { return InputAt(0); }
   HInstruction* GetRight() const { return InputAt(1); }
   DataType::Type GetResultType() const { return GetType(); }
@@ -3352,8 +3444,8 @@
 
   DECLARE_ABSTRACT_INSTRUCTION(BinaryOperation);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HBinaryOperation);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(BinaryOperation);
 };
 
 // The comparison bias applies for floating point operations and indicates how NaN
@@ -3443,8 +3535,7 @@
     return GetBlock()->GetGraph()->GetIntConstant(value, dex_pc);
   }
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HCondition);
+  DEFAULT_COPY_CONSTRUCTOR(Condition);
 };
 
 // Instruction to check if two inputs are equal to each other.
@@ -3486,10 +3577,11 @@
     return kCondNE;
   }
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Equal);
+
  private:
   template <typename T> static bool Compute(T x, T y) { return x == y; }
-
-  DISALLOW_COPY_AND_ASSIGN(HEqual);
 };
 
 class HNotEqual FINAL : public HCondition {
@@ -3529,10 +3621,11 @@
     return kCondEQ;
   }
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(NotEqual);
+
  private:
   template <typename T> static bool Compute(T x, T y) { return x != y; }
-
-  DISALLOW_COPY_AND_ASSIGN(HNotEqual);
 };
 
 class HLessThan FINAL : public HCondition {
@@ -3566,10 +3659,11 @@
     return kCondGE;
   }
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(LessThan);
+
  private:
   template <typename T> static bool Compute(T x, T y) { return x < y; }
-
-  DISALLOW_COPY_AND_ASSIGN(HLessThan);
 };
 
 class HLessThanOrEqual FINAL : public HCondition {
@@ -3603,10 +3697,11 @@
     return kCondGT;
   }
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(LessThanOrEqual);
+
  private:
   template <typename T> static bool Compute(T x, T y) { return x <= y; }
-
-  DISALLOW_COPY_AND_ASSIGN(HLessThanOrEqual);
 };
 
 class HGreaterThan FINAL : public HCondition {
@@ -3640,10 +3735,11 @@
     return kCondLE;
   }
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(GreaterThan);
+
  private:
   template <typename T> static bool Compute(T x, T y) { return x > y; }
-
-  DISALLOW_COPY_AND_ASSIGN(HGreaterThan);
 };
 
 class HGreaterThanOrEqual FINAL : public HCondition {
@@ -3677,10 +3773,11 @@
     return kCondLT;
   }
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(GreaterThanOrEqual);
+
  private:
   template <typename T> static bool Compute(T x, T y) { return x >= y; }
-
-  DISALLOW_COPY_AND_ASSIGN(HGreaterThanOrEqual);
 };
 
 class HBelow FINAL : public HCondition {
@@ -3715,12 +3812,13 @@
     return kCondAE;
   }
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Below);
+
  private:
   template <typename T> static bool Compute(T x, T y) {
     return MakeUnsigned(x) < MakeUnsigned(y);
   }
-
-  DISALLOW_COPY_AND_ASSIGN(HBelow);
 };
 
 class HBelowOrEqual FINAL : public HCondition {
@@ -3755,12 +3853,13 @@
     return kCondA;
   }
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(BelowOrEqual);
+
  private:
   template <typename T> static bool Compute(T x, T y) {
     return MakeUnsigned(x) <= MakeUnsigned(y);
   }
-
-  DISALLOW_COPY_AND_ASSIGN(HBelowOrEqual);
 };
 
 class HAbove FINAL : public HCondition {
@@ -3795,12 +3894,13 @@
     return kCondBE;
   }
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Above);
+
  private:
   template <typename T> static bool Compute(T x, T y) {
     return MakeUnsigned(x) > MakeUnsigned(y);
   }
-
-  DISALLOW_COPY_AND_ASSIGN(HAbove);
 };
 
 class HAboveOrEqual FINAL : public HCondition {
@@ -3835,12 +3935,13 @@
     return kCondB;
   }
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(AboveOrEqual);
+
  private:
   template <typename T> static bool Compute(T x, T y) {
     return MakeUnsigned(x) >= MakeUnsigned(y);
   }
-
-  DISALLOW_COPY_AND_ASSIGN(HAboveOrEqual);
 };
 
 // Instruction to check how two inputs compare to each other.
@@ -3930,8 +4031,7 @@
     return GetBlock()->GetGraph()->GetIntConstant(value, dex_pc);
   }
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HCompare);
+  DEFAULT_COPY_CONSTRUCTOR(Compare);
 };
 
 class HNewInstance FINAL : public HExpression<1> {
@@ -3950,6 +4050,8 @@
     SetRawInputAt(0, cls);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   dex::TypeIndex GetTypeIndex() const { return type_index_; }
   const DexFile& GetDexFile() const { return dex_file_; }
 
@@ -3986,6 +4088,9 @@
 
   DECLARE_INSTRUCTION(NewInstance);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(NewInstance);
+
  private:
   static constexpr size_t kFlagFinalizable = kNumberOfExpressionPackedBits;
   static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1;
@@ -3995,8 +4100,6 @@
   const dex::TypeIndex type_index_;
   const DexFile& dex_file_;
   QuickEntrypointEnum entrypoint_;
-
-  DISALLOW_COPY_AND_ASSIGN(HNewInstance);
 };
 
 enum IntrinsicNeedsEnvironmentOrCache {
@@ -4114,6 +4217,8 @@
     SetPackedFlag<kFlagCanThrow>(true);
   }
 
+  DEFAULT_COPY_CONSTRUCTOR(Invoke);
+
   uint32_t number_of_arguments_;
   ArtMethod* resolved_method_;
   const uint32_t dex_method_index_;
@@ -4121,9 +4226,6 @@
 
   // A magic word holding optimizations for intrinsics. See intrinsics.h.
   uint32_t intrinsic_optimizations_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(HInvoke);
 };
 
 class HInvokeUnresolved FINAL : public HInvoke {
@@ -4144,10 +4246,12 @@
                 invoke_type) {
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(InvokeUnresolved);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HInvokeUnresolved);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(InvokeUnresolved);
 };
 
 class HInvokePolymorphic FINAL : public HInvoke {
@@ -4166,10 +4270,12 @@
                 nullptr,
                 kVirtual) {}
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   DECLARE_INSTRUCTION(InvokePolymorphic);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HInvokePolymorphic);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(InvokePolymorphic);
 };
 
 class HInvokeStaticOrDirect FINAL : public HInvoke {
@@ -4256,6 +4362,8 @@
     SetPackedField<ClinitCheckRequirementField>(clinit_check_requirement);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   void SetDispatchInfo(const DispatchInfo& dispatch_info) {
     bool had_current_method_input = HasCurrentMethodInput();
     bool needs_current_method_input = NeedsCurrentMethodInput(dispatch_info.method_load_kind);
@@ -4401,6 +4509,9 @@
 
   DECLARE_INSTRUCTION(InvokeStaticOrDirect);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(InvokeStaticOrDirect);
+
  private:
   static constexpr size_t kFieldClinitCheckRequirement = kNumberOfInvokePackedBits;
   static constexpr size_t kFieldClinitCheckRequirementSize =
@@ -4416,8 +4527,6 @@
   // Cached values of the resolved method, to avoid needing the mutator lock.
   MethodReference target_method_;
   DispatchInfo dispatch_info_;
-
-  DISALLOW_COPY_AND_ASSIGN(HInvokeStaticOrDirect);
 };
 std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::MethodLoadKind rhs);
 std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::ClinitCheckRequirement rhs);
@@ -4441,6 +4550,8 @@
                 kVirtual),
         vtable_index_(vtable_index) {}
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   bool CanBeNull() const OVERRIDE {
     switch (GetIntrinsic()) {
       case Intrinsics::kThreadCurrentThread:
@@ -4463,11 +4574,12 @@
 
   DECLARE_INSTRUCTION(InvokeVirtual);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(InvokeVirtual);
+
  private:
   // Cached value of the resolved method, to avoid needing the mutator lock.
   const uint32_t vtable_index_;
-
-  DISALLOW_COPY_AND_ASSIGN(HInvokeVirtual);
 };
 
 class HInvokeInterface FINAL : public HInvoke {
@@ -4489,6 +4601,8 @@
                 kInterface),
         imt_index_(imt_index) {}
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE {
     // TODO: Add implicit null checks in intrinsics.
     return (obj == InputAt(0)) && !GetLocations()->Intrinsified();
@@ -4504,11 +4618,12 @@
 
   DECLARE_INSTRUCTION(InvokeInterface);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(InvokeInterface);
+
  private:
   // Cached value of the resolved method, to avoid needing the mutator lock.
   const uint32_t imt_index_;
-
-  DISALLOW_COPY_AND_ASSIGN(HInvokeInterface);
 };
 
 class HNeg FINAL : public HUnaryOperation {
@@ -4535,8 +4650,8 @@
 
   DECLARE_INSTRUCTION(Neg);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HNeg);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Neg);
 };
 
 class HNewArray FINAL : public HExpression<2> {
@@ -4547,6 +4662,8 @@
     SetRawInputAt(1, length);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   // Calls runtime so needs an environment.
   bool NeedsEnvironment() const OVERRIDE { return true; }
 
@@ -4566,8 +4683,8 @@
 
   DECLARE_INSTRUCTION(NewArray);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HNewArray);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(NewArray);
 };
 
 class HAdd FINAL : public HBinaryOperation {
@@ -4601,8 +4718,8 @@
 
   DECLARE_INSTRUCTION(Add);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HAdd);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Add);
 };
 
 class HSub FINAL : public HBinaryOperation {
@@ -4634,8 +4751,8 @@
 
   DECLARE_INSTRUCTION(Sub);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HSub);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Sub);
 };
 
 class HMul FINAL : public HBinaryOperation {
@@ -4669,8 +4786,8 @@
 
   DECLARE_INSTRUCTION(Mul);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HMul);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Mul);
 };
 
 class HDiv FINAL : public HBinaryOperation {
@@ -4716,8 +4833,8 @@
 
   DECLARE_INSTRUCTION(Div);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HDiv);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Div);
 };
 
 class HRem FINAL : public HBinaryOperation {
@@ -4763,8 +4880,8 @@
 
   DECLARE_INSTRUCTION(Rem);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HRem);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Rem);
 };
 
 class HDivZeroCheck FINAL : public HExpression<1> {
@@ -4789,8 +4906,8 @@
 
   DECLARE_INSTRUCTION(DivZeroCheck);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HDivZeroCheck);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(DivZeroCheck);
 };
 
 class HShl FINAL : public HBinaryOperation {
@@ -4835,8 +4952,8 @@
 
   DECLARE_INSTRUCTION(Shl);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HShl);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Shl);
 };
 
 class HShr FINAL : public HBinaryOperation {
@@ -4881,8 +4998,8 @@
 
   DECLARE_INSTRUCTION(Shr);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HShr);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Shr);
 };
 
 class HUShr FINAL : public HBinaryOperation {
@@ -4929,8 +5046,8 @@
 
   DECLARE_INSTRUCTION(UShr);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HUShr);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(UShr);
 };
 
 class HAnd FINAL : public HBinaryOperation {
@@ -4966,8 +5083,8 @@
 
   DECLARE_INSTRUCTION(And);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HAnd);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(And);
 };
 
 class HOr FINAL : public HBinaryOperation {
@@ -5003,8 +5120,8 @@
 
   DECLARE_INSTRUCTION(Or);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HOr);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Or);
 };
 
 class HXor FINAL : public HBinaryOperation {
@@ -5040,8 +5157,8 @@
 
   DECLARE_INSTRUCTION(Xor);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HXor);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Xor);
 };
 
 class HRor FINAL : public HBinaryOperation {
@@ -5091,8 +5208,8 @@
 
   DECLARE_INSTRUCTION(Ror);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HRor);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Ror);
 };
 
 // The value of a parameter in this method. Its location depends on
@@ -5122,6 +5239,9 @@
 
   DECLARE_INSTRUCTION(ParameterValue);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(ParameterValue);
+
  private:
   // Whether or not the parameter value corresponds to 'this' argument.
   static constexpr size_t kFlagIsThis = kNumberOfExpressionPackedBits;
@@ -5135,8 +5255,6 @@
   // The index of this parameter in the parameters list. Must be less
   // than HGraph::number_of_in_vregs_.
   const uint8_t index_;
-
-  DISALLOW_COPY_AND_ASSIGN(HParameterValue);
 };
 
 class HNot FINAL : public HUnaryOperation {
@@ -5168,8 +5286,8 @@
 
   DECLARE_INSTRUCTION(Not);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HNot);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Not);
 };
 
 class HBooleanNot FINAL : public HUnaryOperation {
@@ -5205,8 +5323,8 @@
 
   DECLARE_INSTRUCTION(BooleanNot);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HBooleanNot);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(BooleanNot);
 };
 
 class HTypeConversion FINAL : public HExpression<1> {
@@ -5234,8 +5352,8 @@
 
   DECLARE_INSTRUCTION(TypeConversion);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HTypeConversion);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(TypeConversion);
 };
 
 static constexpr uint32_t kNoRegNumber = -1;
@@ -5249,6 +5367,7 @@
     SetRawInputAt(0, value);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool CanBeMoved() const OVERRIDE { return true; }
   bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
     return true;
@@ -5260,11 +5379,10 @@
 
   bool CanBeNull() const OVERRIDE { return false; }
 
-
   DECLARE_INSTRUCTION(NullCheck);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HNullCheck);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(NullCheck);
 };
 
 // Embeds an ArtField and all the information required by the compiler. We cache
@@ -5326,6 +5444,7 @@
     SetRawInputAt(0, value);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool CanBeMoved() const OVERRIDE { return !IsVolatile(); }
 
   bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
@@ -5355,10 +5474,11 @@
 
   DECLARE_INSTRUCTION(InstanceFieldGet);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(InstanceFieldGet);
+
  private:
   const FieldInfo field_info_;
-
-  DISALLOW_COPY_AND_ASSIGN(HInstanceFieldGet);
 };
 
 class HInstanceFieldSet FINAL : public HTemplateInstruction<2> {
@@ -5386,6 +5506,8 @@
     SetRawInputAt(1, value);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   bool CanDoImplicitNullCheckOn(HInstruction* obj) const OVERRIDE {
     return (obj == InputAt(0)) && art::CanDoImplicitNullCheckOn(GetFieldOffset().Uint32Value());
   }
@@ -5400,6 +5522,9 @@
 
   DECLARE_INSTRUCTION(InstanceFieldSet);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(InstanceFieldSet);
+
  private:
   static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits;
   static constexpr size_t kNumberOfInstanceFieldSetPackedBits = kFlagValueCanBeNull + 1;
@@ -5407,8 +5532,6 @@
                 "Too many packed fields.");
 
   const FieldInfo field_info_;
-
-  DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet);
 };
 
 class HArrayGet FINAL : public HExpression<2> {
@@ -5436,6 +5559,7 @@
     SetRawInputAt(1, index);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool CanBeMoved() const OVERRIDE { return true; }
   bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
     return true;
@@ -5485,6 +5609,9 @@
 
   DECLARE_INSTRUCTION(ArrayGet);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(ArrayGet);
+
  private:
   // We treat a String as an array, creating the HArrayGet from String.charAt()
   // intrinsic in the instruction simplifier. We can always determine whether
@@ -5495,8 +5622,6 @@
   static constexpr size_t kNumberOfArrayGetPackedBits = kFlagIsStringCharAt + 1;
   static_assert(kNumberOfArrayGetPackedBits <= HInstruction::kMaxNumberOfPackedBits,
                 "Too many packed fields.");
-
-  DISALLOW_COPY_AND_ASSIGN(HArrayGet);
 };
 
 class HArraySet FINAL : public HTemplateInstruction<3> {
@@ -5530,6 +5655,8 @@
     SetRawInputAt(2, value);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   bool NeedsEnvironment() const OVERRIDE {
     // We call a runtime method to throw ArrayStoreException.
     return NeedsTypeCheck();
@@ -5595,6 +5722,9 @@
 
   DECLARE_INSTRUCTION(ArraySet);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(ArraySet);
+
  private:
   static constexpr size_t kFieldExpectedComponentType = kNumberOfGenericPackedBits;
   static constexpr size_t kFieldExpectedComponentTypeSize =
@@ -5610,8 +5740,6 @@
   static_assert(kNumberOfArraySetPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
   using ExpectedComponentTypeField =
       BitField<DataType::Type, kFieldExpectedComponentType, kFieldExpectedComponentTypeSize>;
-
-  DISALLOW_COPY_AND_ASSIGN(HArraySet);
 };
 
 class HArrayLength FINAL : public HExpression<1> {
@@ -5624,6 +5752,7 @@
     SetRawInputAt(0, array);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool CanBeMoved() const OVERRIDE { return true; }
   bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
     return true;
@@ -5636,6 +5765,9 @@
 
   DECLARE_INSTRUCTION(ArrayLength);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(ArrayLength);
+
  private:
   // We treat a String as an array, creating the HArrayLength from String.length()
   // or String.isEmpty() intrinsic in the instruction simplifier. We can always
@@ -5646,8 +5778,6 @@
   static constexpr size_t kNumberOfArrayLengthPackedBits = kFlagIsStringLength + 1;
   static_assert(kNumberOfArrayLengthPackedBits <= HInstruction::kMaxNumberOfPackedBits,
                 "Too many packed fields.");
-
-  DISALLOW_COPY_AND_ASSIGN(HArrayLength);
 };
 
 class HBoundsCheck FINAL : public HExpression<2> {
@@ -5665,6 +5795,7 @@
     SetRawInputAt(1, length);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool CanBeMoved() const OVERRIDE { return true; }
   bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
     return true;
@@ -5680,10 +5811,11 @@
 
   DECLARE_INSTRUCTION(BoundsCheck);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(BoundsCheck);
+
  private:
   static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits;
-
-  DISALLOW_COPY_AND_ASSIGN(HBoundsCheck);
 };
 
 class HSuspendCheck FINAL : public HTemplateInstruction<0> {
@@ -5691,6 +5823,8 @@
   explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc)
       : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc), slow_path_(nullptr) {}
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   bool NeedsEnvironment() const OVERRIDE {
     return true;
   }
@@ -5700,12 +5834,13 @@
 
   DECLARE_INSTRUCTION(SuspendCheck);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(SuspendCheck);
+
  private:
   // Only used for code generation, in order to share the same slow path between back edges
   // of a same loop.
   SlowPathCode* slow_path_;
-
-  DISALLOW_COPY_AND_ASSIGN(HSuspendCheck);
 };
 
 // Pseudo-instruction which provides the native debugger with mapping information.
@@ -5721,8 +5856,8 @@
 
   DECLARE_INSTRUCTION(NativeDebugInfo);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HNativeDebugInfo);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(NativeDebugInfo);
 };
 
 /**
@@ -5788,6 +5923,8 @@
     SetPackedFlag<kFlagGenerateClInitCheck>(false);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   void SetLoadKind(LoadKind load_kind);
 
   LoadKind GetLoadKind() const {
@@ -5879,6 +6016,9 @@
 
   DECLARE_INSTRUCTION(LoadClass);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(LoadClass);
+
  private:
   static constexpr size_t kFlagNeedsAccessCheck    = kNumberOfGenericPackedBits;
   static constexpr size_t kFlagIsInBootImage       = kFlagNeedsAccessCheck + 1;
@@ -5918,8 +6058,6 @@
   Handle<mirror::Class> klass_;
 
   ReferenceTypeInfo loaded_class_rti_;
-
-  DISALLOW_COPY_AND_ASSIGN(HLoadClass);
 };
 std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs);
 
@@ -5977,6 +6115,8 @@
     SetPackedField<LoadKindField>(LoadKind::kRuntimeCall);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   void SetLoadKind(LoadKind load_kind);
 
   LoadKind GetLoadKind() const {
@@ -6043,6 +6183,9 @@
 
   DECLARE_INSTRUCTION(LoadString);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(LoadString);
+
  private:
   static constexpr size_t kFieldLoadKind = kNumberOfGenericPackedBits;
   static constexpr size_t kFieldLoadKindSize =
@@ -6062,8 +6205,6 @@
   const DexFile& dex_file_;
 
   Handle<mirror::String> string_;
-
-  DISALLOW_COPY_AND_ASSIGN(HLoadString);
 };
 std::ostream& operator<<(std::ostream& os, HLoadString::LoadKind rhs);
 
@@ -6095,6 +6236,7 @@
     SetRawInputAt(0, constant);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool CanBeMoved() const OVERRIDE { return true; }
   bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
     return true;
@@ -6114,8 +6256,9 @@
 
   DECLARE_INSTRUCTION(ClinitCheck);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HClinitCheck);
+
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(ClinitCheck);
 };
 
 class HStaticFieldGet FINAL : public HExpression<1> {
@@ -6141,6 +6284,7 @@
   }
 
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool CanBeMoved() const OVERRIDE { return !IsVolatile(); }
 
   bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
@@ -6166,10 +6310,11 @@
 
   DECLARE_INSTRUCTION(StaticFieldGet);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(StaticFieldGet);
+
  private:
   const FieldInfo field_info_;
-
-  DISALLOW_COPY_AND_ASSIGN(HStaticFieldGet);
 };
 
 class HStaticFieldSet FINAL : public HTemplateInstruction<2> {
@@ -6197,6 +6342,7 @@
     SetRawInputAt(1, value);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   const FieldInfo& GetFieldInfo() const { return field_info_; }
   MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
   DataType::Type GetFieldType() const { return field_info_.GetFieldType(); }
@@ -6208,6 +6354,9 @@
 
   DECLARE_INSTRUCTION(StaticFieldSet);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(StaticFieldSet);
+
  private:
   static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits;
   static constexpr size_t kNumberOfStaticFieldSetPackedBits = kFlagValueCanBeNull + 1;
@@ -6215,8 +6364,6 @@
                 "Too many packed fields.");
 
   const FieldInfo field_info_;
-
-  DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet);
 };
 
 class HUnresolvedInstanceFieldGet FINAL : public HExpression<1> {
@@ -6230,6 +6377,7 @@
     SetRawInputAt(0, obj);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool NeedsEnvironment() const OVERRIDE { return true; }
   bool CanThrow() const OVERRIDE { return true; }
 
@@ -6238,10 +6386,11 @@
 
   DECLARE_INSTRUCTION(UnresolvedInstanceFieldGet);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(UnresolvedInstanceFieldGet);
+
  private:
   const uint32_t field_index_;
-
-  DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldGet);
 };
 
 class HUnresolvedInstanceFieldSet FINAL : public HTemplateInstruction<2> {
@@ -6259,6 +6408,7 @@
     SetRawInputAt(1, value);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool NeedsEnvironment() const OVERRIDE { return true; }
   bool CanThrow() const OVERRIDE { return true; }
 
@@ -6267,6 +6417,9 @@
 
   DECLARE_INSTRUCTION(UnresolvedInstanceFieldSet);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(UnresolvedInstanceFieldSet);
+
  private:
   static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits;
   static constexpr size_t kFieldFieldTypeSize =
@@ -6278,8 +6431,6 @@
   using FieldTypeField = BitField<DataType::Type, kFieldFieldType, kFieldFieldTypeSize>;
 
   const uint32_t field_index_;
-
-  DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldSet);
 };
 
 class HUnresolvedStaticFieldGet FINAL : public HExpression<0> {
@@ -6291,6 +6442,7 @@
         field_index_(field_index) {
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool NeedsEnvironment() const OVERRIDE { return true; }
   bool CanThrow() const OVERRIDE { return true; }
 
@@ -6299,10 +6451,11 @@
 
   DECLARE_INSTRUCTION(UnresolvedStaticFieldGet);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(UnresolvedStaticFieldGet);
+
  private:
   const uint32_t field_index_;
-
-  DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldGet);
 };
 
 class HUnresolvedStaticFieldSet FINAL : public HTemplateInstruction<1> {
@@ -6318,6 +6471,7 @@
     SetRawInputAt(0, value);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool NeedsEnvironment() const OVERRIDE { return true; }
   bool CanThrow() const OVERRIDE { return true; }
 
@@ -6326,6 +6480,9 @@
 
   DECLARE_INSTRUCTION(UnresolvedStaticFieldSet);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(UnresolvedStaticFieldSet);
+
  private:
   static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits;
   static constexpr size_t kFieldFieldTypeSize =
@@ -6337,8 +6494,6 @@
   using FieldTypeField = BitField<DataType::Type, kFieldFieldType, kFieldFieldTypeSize>;
 
   const uint32_t field_index_;
-
-  DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldSet);
 };
 
 // Implement the move-exception DEX instruction.
@@ -6351,8 +6506,8 @@
 
   DECLARE_INSTRUCTION(LoadException);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HLoadException);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(LoadException);
 };
 
 // Implicit part of move-exception which clears thread-local exception storage.
@@ -6364,8 +6519,8 @@
 
   DECLARE_INSTRUCTION(ClearException);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HClearException);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(ClearException);
 };
 
 class HThrow FINAL : public HTemplateInstruction<1> {
@@ -6381,11 +6536,10 @@
 
   bool CanThrow() const OVERRIDE { return true; }
 
-
   DECLARE_INSTRUCTION(Throw);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HThrow);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Throw);
 };
 
 /**
@@ -6420,6 +6574,7 @@
     SetRawInputAt(1, constant);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool CanBeMoved() const OVERRIDE { return true; }
 
   bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
@@ -6447,6 +6602,9 @@
 
   DECLARE_INSTRUCTION(InstanceOf);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(InstanceOf);
+
  private:
   static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits;
   static constexpr size_t kFieldTypeCheckKindSize =
@@ -6455,8 +6613,6 @@
   static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1;
   static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
   using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;
-
-  DISALLOW_COPY_AND_ASSIGN(HInstanceOf);
 };
 
 class HBoundType FINAL : public HExpression<1> {
@@ -6470,6 +6626,8 @@
     SetRawInputAt(0, input);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   // {Get,Set}Upper* should only be used in reference type propagation.
   const ReferenceTypeInfo& GetUpperBound() const { return upper_bound_; }
   bool GetUpperCanBeNull() const { return GetPackedFlag<kFlagUpperCanBeNull>(); }
@@ -6484,6 +6642,9 @@
 
   DECLARE_INSTRUCTION(BoundType);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(BoundType);
+
  private:
   // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this
   // is false then CanBeNull() cannot be true).
@@ -6499,8 +6660,6 @@
   //     // uper_bound_ will be ClassX
   //   }
   ReferenceTypeInfo upper_bound_;
-
-  DISALLOW_COPY_AND_ASSIGN(HBoundType);
 };
 
 class HCheckCast FINAL : public HTemplateInstruction<2> {
@@ -6516,6 +6675,7 @@
     SetRawInputAt(1, constant);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool CanBeMoved() const OVERRIDE { return true; }
 
   bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
@@ -6536,6 +6696,9 @@
 
   DECLARE_INSTRUCTION(CheckCast);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(CheckCast);
+
  private:
   static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits;
   static constexpr size_t kFieldTypeCheckKindSize =
@@ -6544,8 +6707,6 @@
   static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1;
   static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
   using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;
-
-  DISALLOW_COPY_AND_ASSIGN(HCheckCast);
 };
 
 /**
@@ -6582,10 +6743,15 @@
     SetPackedField<BarrierKindField>(barrier_kind);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   MemBarrierKind GetBarrierKind() { return GetPackedField<BarrierKindField>(); }
 
   DECLARE_INSTRUCTION(MemoryBarrier);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(MemoryBarrier);
+
  private:
   static constexpr size_t kFieldBarrierKind = HInstruction::kNumberOfGenericPackedBits;
   static constexpr size_t kFieldBarrierKindSize =
@@ -6595,8 +6761,6 @@
   static_assert(kNumberOfMemoryBarrierPackedBits <= kMaxNumberOfPackedBits,
                 "Too many packed fields.");
   using BarrierKindField = BitField<MemBarrierKind, kFieldBarrierKind, kFieldBarrierKindSize>;
-
-  DISALLOW_COPY_AND_ASSIGN(HMemoryBarrier);
 };
 
 // A constructor fence orders all prior stores to fields that could be accessed via a final field of
@@ -6747,8 +6911,8 @@
 
   DECLARE_INSTRUCTION(ConstructorFence);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HConstructorFence);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(ConstructorFence);
 };
 
 class HMonitorOperation FINAL : public HTemplateInstruction<1> {
@@ -6782,6 +6946,9 @@
 
   DECLARE_INSTRUCTION(MonitorOperation);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(MonitorOperation);
+
  private:
   static constexpr size_t kFieldOperationKind = HInstruction::kNumberOfGenericPackedBits;
   static constexpr size_t kFieldOperationKindSize =
@@ -6791,9 +6958,6 @@
   static_assert(kNumberOfMonitorOperationPackedBits <= HInstruction::kMaxNumberOfPackedBits,
                 "Too many packed fields.");
   using OperationKindField = BitField<OperationKind, kFieldOperationKind, kFieldOperationKindSize>;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(HMonitorOperation);
 };
 
 class HSelect FINAL : public HExpression<3> {
@@ -6814,6 +6978,7 @@
     SetRawInputAt(2, condition);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   HInstruction* GetFalseValue() const { return InputAt(0); }
   HInstruction* GetTrueValue() const { return InputAt(1); }
   HInstruction* GetCondition() const { return InputAt(2); }
@@ -6829,8 +6994,8 @@
 
   DECLARE_INSTRUCTION(Select);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HSelect);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(Select);
 };
 
 class MoveOperands : public ArenaObject<kArenaAllocMoveOperands> {
@@ -6961,10 +7126,11 @@
 
   DECLARE_INSTRUCTION(ParallelMove);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(ParallelMove);
+
  private:
   ArenaVector<MoveOperands> moves_;
-
-  DISALLOW_COPY_AND_ASSIGN(HParallelMove);
 };
 
 // This instruction computes an intermediate address pointing in the 'middle' of an object. The
@@ -6983,6 +7149,7 @@
     SetRawInputAt(1, offset);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool CanBeMoved() const OVERRIDE { return true; }
   bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
     return true;
@@ -6994,8 +7161,8 @@
 
   DECLARE_INSTRUCTION(IntermediateAddress);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HIntermediateAddress);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(IntermediateAddress);
 };
 
 
@@ -7070,6 +7237,33 @@
   DISALLOW_COPY_AND_ASSIGN(HGraphDelegateVisitor);
 };
 
+// Create a clone of the instruction, insert it into the graph; replace the old one with a new
+// and remove the old instruction.
+HInstruction* ReplaceInstrOrPhiByClone(HInstruction* instr);
+
+// Create a clone for each clonable instructions/phis and replace the original with the clone.
+//
+// Used for testing individual instruction cloner.
+class CloneAndReplaceInstructionVisitor : public HGraphDelegateVisitor {
+ public:
+  explicit CloneAndReplaceInstructionVisitor(HGraph* graph)
+      : HGraphDelegateVisitor(graph), instr_replaced_by_clones_count(0) {}
+
+  void VisitInstruction(HInstruction* instruction) OVERRIDE {
+    if (instruction->IsClonable()) {
+      ReplaceInstrOrPhiByClone(instruction);
+      instr_replaced_by_clones_count++;
+    }
+  }
+
+  size_t GetInstrReplacedByClonesCount() const { return instr_replaced_by_clones_count; }
+
+ private:
+  size_t instr_replaced_by_clones_count;
+
+  DISALLOW_COPY_AND_ASSIGN(CloneAndReplaceInstructionVisitor);
+};
+
 // Iterator over the blocks that art part of the loop. Includes blocks part
 // of an inner loop. The order in which the blocks are iterated is on their
 // block id.
diff --git a/compiler/optimizing/nodes_mips.h b/compiler/optimizing/nodes_mips.h
index ef388c3..2c0595e 100644
--- a/compiler/optimizing/nodes_mips.h
+++ b/compiler/optimizing/nodes_mips.h
@@ -30,8 +30,8 @@
 
   DECLARE_INSTRUCTION(MipsComputeBaseMethodAddress);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HMipsComputeBaseMethodAddress);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(MipsComputeBaseMethodAddress);
 };
 
 // Mips version of HPackedSwitch that holds a pointer to the base method address.
@@ -62,11 +62,12 @@
 
   DECLARE_INSTRUCTION(MipsPackedSwitch);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(MipsPackedSwitch);
+
  private:
   const int32_t start_value_;
   const int32_t num_entries_;
-
-  DISALLOW_COPY_AND_ASSIGN(HMipsPackedSwitch);
 };
 
 // This instruction computes part of the array access offset (index offset).
@@ -105,8 +106,8 @@
 
   DECLARE_INSTRUCTION(IntermediateArrayAddressIndex);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HIntermediateArrayAddressIndex);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(IntermediateArrayAddressIndex);
 };
 
 }  // namespace art
diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h
index 7b4f5f7..e837f1e 100644
--- a/compiler/optimizing/nodes_shared.h
+++ b/compiler/optimizing/nodes_shared.h
@@ -38,6 +38,8 @@
     SetRawInputAt(kInputMulRightIndex, mul_right);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
+
   static constexpr int kInputAccumulatorIndex = 0;
   static constexpr int kInputMulLeftIndex = 1;
   static constexpr int kInputMulRightIndex = 2;
@@ -51,11 +53,12 @@
 
   DECLARE_INSTRUCTION(MultiplyAccumulate);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(MultiplyAccumulate);
+
  private:
   // Indicates if this is a MADD or MSUB.
   const InstructionKind op_kind_;
-
-  DISALLOW_COPY_AND_ASSIGN(HMultiplyAccumulate);
 };
 
 class HBitwiseNegatedRight FINAL : public HBinaryOperation {
@@ -111,11 +114,12 @@
 
   DECLARE_INSTRUCTION(BitwiseNegatedRight);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(BitwiseNegatedRight);
+
  private:
   // Specifies the bitwise operation, which will be then negated.
   const InstructionKind op_kind_;
-
-  DISALLOW_COPY_AND_ASSIGN(HBitwiseNegatedRight);
 };
 
 // This instruction computes part of the array access offset (data and index offset).
@@ -145,6 +149,7 @@
     SetRawInputAt(2, shift);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool CanBeMoved() const OVERRIDE { return true; }
   bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE {
     return true;
@@ -157,8 +162,8 @@
 
   DECLARE_INSTRUCTION(IntermediateAddressIndex);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HIntermediateAddressIndex);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(IntermediateAddressIndex);
 };
 
 class HDataProcWithShifterOp FINAL : public HExpression<2> {
@@ -198,6 +203,7 @@
     SetRawInputAt(1, right);
   }
 
+  bool IsClonable() const OVERRIDE { return true; }
   bool CanBeMoved() const OVERRIDE { return true; }
   bool InstructionDataEquals(const HInstruction* other_instr) const OVERRIDE {
     const HDataProcWithShifterOp* other = other_instr->AsDataProcWithShifterOp();
@@ -225,14 +231,15 @@
 
   DECLARE_INSTRUCTION(DataProcWithShifterOp);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(DataProcWithShifterOp);
+
  private:
   InstructionKind instr_kind_;
   OpKind op_kind_;
   int shift_amount_;
 
   friend std::ostream& operator<<(std::ostream& os, OpKind op);
-
-  DISALLOW_COPY_AND_ASSIGN(HDataProcWithShifterOp);
 };
 
 std::ostream& operator<<(std::ostream& os, const HDataProcWithShifterOp::OpKind op);
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index 17540b9..59d5b9f 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -161,10 +161,10 @@
   static_assert(kNumberOfVectorOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
   using TypeField = BitField<DataType::Type, kFieldType, kFieldTypeSize>;
 
+  DEFAULT_COPY_CONSTRUCTOR(VecOperation);
+
  private:
   const size_t vector_length_;
-
-  DISALLOW_COPY_AND_ASSIGN(HVecOperation);
 };
 
 // Abstraction of a unary vector operation.
@@ -188,8 +188,8 @@
 
   DECLARE_ABSTRACT_INSTRUCTION(VecUnaryOperation);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecUnaryOperation);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecUnaryOperation);
 };
 
 // Abstraction of a binary vector operation.
@@ -216,8 +216,8 @@
 
   DECLARE_ABSTRACT_INSTRUCTION(VecBinaryOperation);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecBinaryOperation);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecBinaryOperation);
 };
 
 // Abstraction of a vector operation that references memory, with an alignment.
@@ -255,10 +255,11 @@
 
   DECLARE_ABSTRACT_INSTRUCTION(VecMemoryOperation);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecMemoryOperation);
+
  private:
   Alignment alignment_;
-
-  DISALLOW_COPY_AND_ASSIGN(HVecMemoryOperation);
 };
 
 // Packed type consistency checker ("same vector length" integral types may mix freely).
@@ -296,8 +297,8 @@
 
   DECLARE_INSTRUCTION(VecReplicateScalar);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecReplicateScalar);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecReplicateScalar);
 };
 
 // Extracts a particular scalar from the given vector,
@@ -329,8 +330,8 @@
 
   DECLARE_INSTRUCTION(VecExtractScalar);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecExtractScalar);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecExtractScalar);
 };
 
 // Reduces the given vector into the first element as sum/min/max,
@@ -367,10 +368,11 @@
 
   DECLARE_INSTRUCTION(VecReduce);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecReduce);
+
  private:
   const ReductionKind kind_;
-
-  DISALLOW_COPY_AND_ASSIGN(HVecReduce);
 };
 
 // Converts every component in the vector,
@@ -394,8 +396,8 @@
 
   DECLARE_INSTRUCTION(VecCnv);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecCnv);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecCnv);
 };
 
 // Negates every component in the vector,
@@ -415,8 +417,8 @@
 
   DECLARE_INSTRUCTION(VecNeg);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecNeg);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecNeg);
 };
 
 // Takes absolute value of every component in the vector,
@@ -437,8 +439,8 @@
 
   DECLARE_INSTRUCTION(VecAbs);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecAbs);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecAbs);
 };
 
 // Bitwise- or boolean-nots every component in the vector,
@@ -459,8 +461,8 @@
 
   DECLARE_INSTRUCTION(VecNot);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecNot);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecNot);
 };
 
 //
@@ -486,8 +488,8 @@
 
   DECLARE_INSTRUCTION(VecAdd);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecAdd);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecAdd);
 };
 
 // Performs halving add on every component in the two vectors, viz.
@@ -531,14 +533,15 @@
 
   DECLARE_INSTRUCTION(VecHalvingAdd);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecHalvingAdd);
+
  private:
   // Additional packed bits.
   static constexpr size_t kFieldHAddIsUnsigned = HVecOperation::kNumberOfVectorOpPackedBits;
   static constexpr size_t kFieldHAddIsRounded = kFieldHAddIsUnsigned + 1;
   static constexpr size_t kNumberOfHAddPackedBits = kFieldHAddIsRounded + 1;
   static_assert(kNumberOfHAddPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
-
-  DISALLOW_COPY_AND_ASSIGN(HVecHalvingAdd);
 };
 
 // Subtracts every component in the two vectors,
@@ -560,8 +563,8 @@
 
   DECLARE_INSTRUCTION(VecSub);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecSub);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecSub);
 };
 
 // Multiplies every component in the two vectors,
@@ -583,8 +586,8 @@
 
   DECLARE_INSTRUCTION(VecMul);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecMul);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecMul);
 };
 
 // Divides every component in the two vectors,
@@ -606,8 +609,8 @@
 
   DECLARE_INSTRUCTION(VecDiv);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecDiv);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecDiv);
 };
 
 // Takes minimum of every component in the two vectors,
@@ -645,13 +648,14 @@
 
   DECLARE_INSTRUCTION(VecMin);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecMin);
+
  private:
   // Additional packed bits.
   static constexpr size_t kFieldMinOpIsUnsigned = HVecOperation::kNumberOfVectorOpPackedBits;
   static constexpr size_t kNumberOfMinOpPackedBits = kFieldMinOpIsUnsigned + 1;
   static_assert(kNumberOfMinOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
-
-  DISALLOW_COPY_AND_ASSIGN(HVecMin);
 };
 
 // Takes maximum of every component in the two vectors,
@@ -689,13 +693,14 @@
 
   DECLARE_INSTRUCTION(VecMax);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecMax);
+
  private:
   // Additional packed bits.
   static constexpr size_t kFieldMaxOpIsUnsigned = HVecOperation::kNumberOfVectorOpPackedBits;
   static constexpr size_t kNumberOfMaxOpPackedBits = kFieldMaxOpIsUnsigned + 1;
   static_assert(kNumberOfMaxOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
-
-  DISALLOW_COPY_AND_ASSIGN(HVecMax);
 };
 
 // Bitwise-ands every component in the two vectors,
@@ -716,8 +721,8 @@
 
   DECLARE_INSTRUCTION(VecAnd);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecAnd);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecAnd);
 };
 
 // Bitwise-and-nots every component in the two vectors,
@@ -738,8 +743,8 @@
 
   DECLARE_INSTRUCTION(VecAndNot);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecAndNot);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecAndNot);
 };
 
 // Bitwise-ors every component in the two vectors,
@@ -760,8 +765,8 @@
 
   DECLARE_INSTRUCTION(VecOr);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecOr);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecOr);
 };
 
 // Bitwise-xors every component in the two vectors,
@@ -782,8 +787,8 @@
 
   DECLARE_INSTRUCTION(VecXor);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecXor);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecXor);
 };
 
 // Logically shifts every component in the vector left by the given distance,
@@ -804,8 +809,8 @@
 
   DECLARE_INSTRUCTION(VecShl);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecShl);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecShl);
 };
 
 // Arithmetically shifts every component in the vector right by the given distance,
@@ -826,8 +831,8 @@
 
   DECLARE_INSTRUCTION(VecShr);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecShr);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecShr);
 };
 
 // Logically shifts every component in the vector right by the given distance,
@@ -848,8 +853,8 @@
 
   DECLARE_INSTRUCTION(VecUShr);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecUShr);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecUShr);
 };
 
 //
@@ -885,8 +890,8 @@
 
   DECLARE_INSTRUCTION(VecSetScalars);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecSetScalars);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecSetScalars);
 };
 
 // Multiplies every component in the two vectors, adds the result vector to the accumulator vector,
@@ -929,11 +934,12 @@
 
   DECLARE_INSTRUCTION(VecMultiplyAccumulate);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecMultiplyAccumulate);
+
  private:
   // Indicates if this is a MADD or MSUB.
   const InstructionKind op_kind_;
-
-  DISALLOW_COPY_AND_ASSIGN(HVecMultiplyAccumulate);
 };
 
 // Takes the absolute difference of two vectors, and adds the results to
@@ -968,8 +974,8 @@
 
   DECLARE_INSTRUCTION(VecSADAccumulate);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecSADAccumulate);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecSADAccumulate);
 };
 
 // Loads a vector from memory, viz. load(mem, 1)
@@ -1007,13 +1013,14 @@
 
   DECLARE_INSTRUCTION(VecLoad);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecLoad);
+
  private:
   // Additional packed bits.
   static constexpr size_t kFieldIsStringCharAt = HVecOperation::kNumberOfVectorOpPackedBits;
   static constexpr size_t kNumberOfVecLoadPackedBits = kFieldIsStringCharAt + 1;
   static_assert(kNumberOfVecLoadPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
-
-  DISALLOW_COPY_AND_ASSIGN(HVecLoad);
 };
 
 // Stores a vector to memory, viz. store(m, 1, [x1, .. , xn] )
@@ -1045,8 +1052,8 @@
 
   DECLARE_INSTRUCTION(VecStore);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HVecStore);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(VecStore)
 };
 
 }  // namespace art
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
index 22e92ea..6326065 100644
--- a/compiler/optimizing/nodes_x86.h
+++ b/compiler/optimizing/nodes_x86.h
@@ -30,8 +30,8 @@
 
   DECLARE_INSTRUCTION(X86ComputeBaseMethodAddress);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HX86ComputeBaseMethodAddress);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(X86ComputeBaseMethodAddress);
 };
 
 // Load a constant value from the constant table.
@@ -54,8 +54,8 @@
 
   DECLARE_INSTRUCTION(X86LoadFromConstantTable);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HX86LoadFromConstantTable);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(X86LoadFromConstantTable);
 };
 
 // Version of HNeg with access to the constant table for FP types.
@@ -77,8 +77,8 @@
 
   DECLARE_INSTRUCTION(X86FPNeg);
 
- private:
-  DISALLOW_COPY_AND_ASSIGN(HX86FPNeg);
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(X86FPNeg);
 };
 
 // X86 version of HPackedSwitch that holds a pointer to the base method address.
@@ -113,11 +113,12 @@
 
   DECLARE_INSTRUCTION(X86PackedSwitch);
 
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(X86PackedSwitch);
+
  private:
   const int32_t start_value_;
   const int32_t num_entries_;
-
-  DISALLOW_COPY_AND_ASSIGN(HX86PackedSwitch);
 };
 
 }  // namespace art
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 84ccaa0..a7af193 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -1202,9 +1202,11 @@
   // Positions and locals table in the debug info.
   bool is_static = (flags & kAccStatic) != 0;
   fprintf(gOutFile, "      positions     : \n");
-  pDexFile->DecodeDebugPositionInfo(pCode, dumpPositionsCb, nullptr);
+  uint32_t debug_info_offset = pDexFile->GetDebugInfoOffset(pCode);
+  pDexFile->DecodeDebugPositionInfo(pCode, debug_info_offset, dumpPositionsCb, nullptr);
   fprintf(gOutFile, "      locals        : \n");
-  pDexFile->DecodeDebugLocalInfo(pCode, is_static, idx, dumpLocalsCb, nullptr);
+  pDexFile->DecodeDebugLocalInfo(
+      pCode, debug_info_offset, is_static, idx, dumpLocalsCb, nullptr);
 }
 
 /*
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index a8ba950..2af579c 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -570,16 +570,17 @@
   uint32_t tries_size = disk_code_item.tries_size_;
 
   // TODO: Calculate the size of the debug info.
-  const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(&disk_code_item);
+  uint32_t debug_info_offset = dex_file.GetDebugInfoOffset(&disk_code_item);
+  const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(debug_info_offset);
   DebugInfoItem* debug_info = nullptr;
   if (debug_info_stream != nullptr) {
-    debug_info = debug_info_items_map_.GetExistingObject(disk_code_item.debug_info_off_);
+    debug_info = debug_info_items_map_.GetExistingObject(debug_info_offset);
     if (debug_info == nullptr) {
       uint32_t debug_info_size = GetDebugInfoStreamSize(debug_info_stream);
       uint8_t* debug_info_buffer = new uint8_t[debug_info_size];
       memcpy(debug_info_buffer, debug_info_stream, debug_info_size);
       debug_info = new DebugInfoItem(debug_info_size, debug_info_buffer);
-      AddItem(debug_info_items_map_, debug_info_items_, debug_info, disk_code_item.debug_info_off_);
+      AddItem(debug_info_items_map_, debug_info_items_, debug_info, debug_info_offset);
     }
   }
 
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index 61a4eae..8421774 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -396,6 +396,14 @@
     eagerly_assign_offsets_ = eagerly_assign_offsets;
   }
 
+  void SetLinkData(std::vector<uint8_t>&& link_data) {
+    link_data_ = std::move(link_data);
+  }
+
+  const std::vector<uint8_t>& LinkData() const {
+    return link_data_;
+  }
+
  private:
   EncodedValue* ReadEncodedValue(const DexFile& dex_file, const uint8_t** data);
   EncodedValue* ReadEncodedValue(const DexFile& dex_file,
@@ -452,6 +460,9 @@
 
   uint32_t map_list_offset_ = 0;
 
+  // Link data.
+  std::vector<uint8_t> link_data_;
+
   // If we eagerly assign offsets during IR building or later after layout. Must be false if
   // changing the layout is enabled.
   bool eagerly_assign_offsets_;
diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc
index 924dfe0..1fd963f 100644
--- a/dexlayout/dex_ir_builder.cc
+++ b/dexlayout/dex_ir_builder.cc
@@ -80,6 +80,11 @@
   // Sort the vectors by the map order (same order as the file).
   collections.SortVectorsByMapOrder();
 
+  // Load the link data if it exists.
+  collections.SetLinkData(std::vector<uint8_t>(
+      dex_file.Begin() + dex_file.GetHeader().link_off_,
+      dex_file.Begin() + dex_file.GetHeader().link_off_ + dex_file.GetHeader().link_size_));
+
   return header;
 }
 
diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc
index c85bca0..1fac235 100644
--- a/dexlayout/dex_writer.cc
+++ b/dexlayout/dex_writer.cc
@@ -878,7 +878,15 @@
     }
   }
 
-  // TODO: Write link data?
+  // Write link data if it exists.
+  const std::vector<uint8_t>& link_data = collection.LinkData();
+  if (link_data.size() > 0) {
+    CHECK_EQ(header_->LinkSize(), static_cast<uint32_t>(link_data.size()));
+    if (compute_offsets_) {
+      header_->SetLinkOffset(offset);
+    }
+    offset += Write(&link_data[0], link_data.size(), header_->LinkOffset());
+  }
 
   // Write header last.
   if (compute_offsets_) {
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index c4f7acc..f994fd6 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -732,4 +732,46 @@
                             dexlayout_args));
 }
 
+// Test that link data is written out (or at least the header is updated).
+TEST_F(DexLayoutTest, LinkData) {
+  TEST_DISABLED_FOR_TARGET();
+  ScratchFile temp_dex;
+  size_t file_size = 0;
+  MutateDexFile(temp_dex.GetFile(), GetTestDexFileName("ManyMethods"), [&] (DexFile* dex) {
+    DexFile::Header& header = const_cast<DexFile::Header&>(dex->GetHeader());
+    header.link_off_ = header.file_size_;
+    header.link_size_ = 16 * KB;
+    header.file_size_ += header.link_size_;
+    file_size = header.file_size_;
+  });
+  TEMP_FAILURE_RETRY(temp_dex.GetFile()->SetLength(file_size));
+
+  std::string error_msg;
+
+  ScratchFile tmp_file;
+  const std::string& tmp_name = tmp_file.GetFilename();
+  size_t tmp_last_slash = tmp_name.rfind('/');
+  std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
+  ScratchFile profile_file;
+
+  std::vector<std::string> dexlayout_args =
+      { "-i",
+        "-v",
+        "-w", tmp_dir,
+        "-o", tmp_name,
+        "-p", profile_file.GetFilename(),
+        temp_dex.GetFilename()
+      };
+  // -v makes sure that the layout did not corrupt the dex file.
+  ASSERT_TRUE(DexLayoutExec(&temp_dex,
+                            /*dex_filename*/ nullptr,
+                            &profile_file,
+                            dexlayout_args));
+
+  std::string output_dex = temp_dex.GetFilename() + ".new";
+  std::vector<std::string> rm_exec_argv =
+      { "/bin/rm", output_dex };
+  ASSERT_TRUE(::art::Exec(rm_exec_argv, &error_msg));
+}
+
 }  // namespace art
diff --git a/dexlist/Android.bp b/dexlist/Android.bp
index 03943bf..8ecff42 100644
--- a/dexlist/Android.bp
+++ b/dexlist/Android.bp
@@ -17,7 +17,7 @@
     host_supported: true,
     srcs: ["dexlist.cc"],
     cflags: ["-Wall", "-Werror"],
-    shared_libs: ["libart"],
+    shared_libs: ["libart", "libbase"],
 }
 
 art_cc_test {
diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc
index e3ca59c..3bd903d 100644
--- a/dexlist/dexlist.cc
+++ b/dexlist/dexlist.cc
@@ -120,7 +120,8 @@
 
   // Find the first line.
   int firstLine = -1;
-  pDexFile->DecodeDebugPositionInfo(pCode, positionsCb, &firstLine);
+  uint32_t debug_info_offset = pDexFile->GetDebugInfoOffset(pCode);
+  pDexFile->DecodeDebugPositionInfo(pCode, debug_info_offset, positionsCb, &firstLine);
 
   // Method signature.
   const Signature signature = pDexFile->GetMethodSignature(pMethodId);
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index cf93bf0..448ce41 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -48,6 +48,7 @@
 #include "mirror/object_array-inl.h"
 #include "modifiers.h"
 #include "nativehelper/scoped_local_ref.h"
+#include "oat_file.h"
 #include "runtime_callbacks.h"
 #include "scoped_thread_state_change-inl.h"
 #include "stack.h"
@@ -259,7 +260,9 @@
   };
 
   LocalVariableContext context(env);
+  uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item);
   if (!dex_file->DecodeDebugLocalInfo(code_item,
+                                      debug_info_offset,
                                       art_method->IsStatic(),
                                       art_method->GetDexMethodIndex(),
                                       LocalVariableContext::Callback,
@@ -480,7 +483,9 @@
   }
 
   LineNumberContext context;
-  bool success = dex_file->DecodeDebugPositionInfo(code_item, CollectLineNumbers, &context);
+  uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item);
+  bool success = dex_file->DecodeDebugPositionInfo(
+      code_item, debug_info_offset, CollectLineNumbers, &context);
   if (!success) {
     return ERR(ABSENT_INFORMATION);
   }
@@ -648,7 +653,9 @@
     };
 
     GetLocalVariableInfoContext context(slot_, dex_pc, descriptor, type);
+    uint32_t debug_info_offset = art::OatFile::GetDebugInfoOffset(*dex_file, code_item);
     if (!dex_file->DecodeDebugLocalInfo(code_item,
+                                        debug_info_offset,
                                         method->IsStatic(),
                                         method->GetDexMethodIndex(),
                                         GetLocalVariableInfoContext::Callback,
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 613e4fe..1dcd935 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -58,6 +58,7 @@
 #include "mirror/throwable.h"
 #include "nativehelper/scoped_local_ref.h"
 #include "nativehelper/scoped_primitive_array.h"
+#include "oat_file.h"
 #include "obj_ptr-inl.h"
 #include "reflection.h"
 #include "safe_map.h"
@@ -345,7 +346,14 @@
 Dbg::DbgClassLoadCallback Dbg::class_load_callback_;
 
 void DebuggerDdmCallback::DdmPublishChunk(uint32_t type, const ArrayRef<const uint8_t>& data) {
-  Dbg::DdmSendChunk(type, data);
+  if (gJdwpState == nullptr) {
+    VLOG(jdwp) << "Debugger thread not active, ignoring DDM send: " << type;
+  } else {
+    iovec vec[1];
+    vec[0].iov_base = reinterpret_cast<void*>(const_cast<uint8_t*>(data.data()));
+    vec[0].iov_len = data.size();
+    gJdwpState->DdmSendChunkV(type, vec, 1);
+  }
 }
 
 bool DebuggerActiveMethodInspectionCallback::IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED) {
@@ -1673,7 +1681,9 @@
   context.pReply = pReply;
 
   if (code_item != nullptr) {
-    m->GetDexFile()->DecodeDebugPositionInfo(code_item, DebugCallbackContext::Callback, &context);
+    uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item);
+    m->GetDexFile()->DecodeDebugPositionInfo(
+        code_item, debug_info_offset, DebugCallbackContext::Callback, &context);
   }
 
   JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
@@ -1730,9 +1740,10 @@
 
   const DexFile::CodeItem* code_item = m->GetCodeItem();
   if (code_item != nullptr) {
+    uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item);
     m->GetDexFile()->DecodeDebugLocalInfo(
-        code_item, m->IsStatic(), m->GetDexMethodIndex(), DebugCallbackContext::Callback,
-        &context);
+        code_item, debug_info_offset, m->IsStatic(), m->GetDexMethodIndex(),
+        DebugCallbackContext::Callback, &context);
   }
 
   JDWP::Set4BE(expandBufGetBuffer(pReply) + variable_count_offset, context.variable_count);
@@ -3879,7 +3890,9 @@
   if (m != nullptr && !m->IsNative()) {
     const DexFile::CodeItem* const code_item = m->GetCodeItem();
     DebugCallbackContext context(single_step_control, line_number, code_item);
-    m->GetDexFile()->DecodeDebugPositionInfo(code_item, DebugCallbackContext::Callback, &context);
+    uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*(m->GetDexFile()), code_item);
+    m->GetDexFile()->DecodeDebugPositionInfo(
+        code_item, debug_info_offset, DebugCallbackContext::Callback, &context);
   }
 
   // Activate single-step in the thread.
@@ -4458,10 +4471,11 @@
     return;
   }
 
+  RuntimeCallbacks* cb = Runtime::Current()->GetRuntimeCallbacks();
   if (type == CHUNK_TYPE("THDE")) {
     uint8_t buf[4];
     JDWP::Set4BE(&buf[0], t->GetThreadId());
-    Dbg::DdmSendChunk(CHUNK_TYPE("THDE"), 4, buf);
+    cb->DdmPublishChunk(CHUNK_TYPE("THDE"), ArrayRef<const uint8_t>(buf));
   } else {
     CHECK(type == CHUNK_TYPE("THCR") || type == CHUNK_TYPE("THNM")) << type;
     ScopedObjectAccessUnchecked soa(Thread::Current());
@@ -4480,7 +4494,7 @@
       JDWP::AppendUtf16BE(bytes, chars, char_count);
     }
     CHECK_EQ(bytes.size(), char_count*2 + sizeof(uint32_t)*2);
-    Dbg::DdmSendChunk(type, bytes);
+    cb->DdmPublishChunk(type, ArrayRef<const uint8_t>(bytes));
   }
 }
 
@@ -4523,30 +4537,6 @@
   Dbg::PostThreadStartOrStop(t, CHUNK_TYPE("THDE"));
 }
 
-void Dbg::DdmSendChunk(uint32_t type, const ArrayRef<const uint8_t>& data) {
-  DdmSendChunk(type, data.size(), data.data());
-}
-
-void Dbg::DdmSendChunk(uint32_t type, size_t byte_count, const uint8_t* buf) {
-  CHECK(buf != nullptr);
-  iovec vec[1];
-  vec[0].iov_base = reinterpret_cast<void*>(const_cast<uint8_t*>(buf));
-  vec[0].iov_len = byte_count;
-  Dbg::DdmSendChunkV(type, vec, 1);
-}
-
-void Dbg::DdmSendChunk(uint32_t type, const std::vector<uint8_t>& bytes) {
-  DdmSendChunk(type, bytes.size(), &bytes[0]);
-}
-
-void Dbg::DdmSendChunkV(uint32_t type, const iovec* iov, int iov_count) {
-  if (gJdwpState == nullptr) {
-    VLOG(jdwp) << "Debugger thread not active, ignoring DDM send: " << type;
-  } else {
-    gJdwpState->DdmSendChunkV(type, iov, iov_count);
-  }
-}
-
 JDWP::JdwpState* Dbg::GetJdwpState() {
   return gJdwpState;
 }
@@ -4624,7 +4614,8 @@
   JDWP::Append4BE(bytes, heap->GetBytesAllocated());
   JDWP::Append4BE(bytes, heap->GetObjectsAllocated());
   CHECK_EQ(bytes.size(), 4U + (heap_count * (4 + 8 + 1 + 4 + 4 + 4 + 4)));
-  Dbg::DdmSendChunk(CHUNK_TYPE("HPIF"), bytes);
+  Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(CHUNK_TYPE("HPIF"),
+                                                             ArrayRef<const uint8_t>(bytes));
 }
 
 enum HpsgSolidity {
@@ -4710,7 +4701,8 @@
     CHECK_LE(pieceLenField_, p_);
     JDWP::Set4BE(pieceLenField_, totalAllocationUnits_);
 
-    Dbg::DdmSendChunk(type_, p_ - &buf_[0], &buf_[0]);
+    ArrayRef<const uint8_t> out(&buf_[0], p_ - &buf_[0]);
+    Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(type_, out);
     Reset();
   }
 
@@ -4892,6 +4884,7 @@
   if (when == HPSG_WHEN_NEVER) {
     return;
   }
+  RuntimeCallbacks* cb = Runtime::Current()->GetRuntimeCallbacks();
   // Figure out what kind of chunks we'll be sending.
   CHECK(what == HPSG_WHAT_MERGED_OBJECTS || what == HPSG_WHAT_DISTINCT_OBJECTS)
       << static_cast<int>(what);
@@ -4899,7 +4892,8 @@
   // First, send a heap start chunk.
   uint8_t heap_id[4];
   JDWP::Set4BE(&heap_id[0], 1);  // Heap id (bogus; we only have one heap).
-  Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), sizeof(heap_id), heap_id);
+  cb->DdmPublishChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"),
+                      ArrayRef<const uint8_t>(heap_id));
   Thread* self = Thread::Current();
   Locks::mutator_lock_->AssertSharedHeld(self);
 
@@ -4958,7 +4952,8 @@
   }
 
   // Finally, send a heap end chunk.
-  Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), sizeof(heap_id), heap_id);
+  cb->DdmPublishChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"),
+                      ArrayRef<const uint8_t>(heap_id));
 }
 
 void Dbg::SetAllocTrackingEnabled(bool enable) {
diff --git a/runtime/debugger.h b/runtime/debugger.h
index c3184e8..d5bad8d 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -662,14 +662,6 @@
   static bool DdmHandlePacket(JDWP::Request* request, uint8_t** pReplyBuf, int* pReplyLen);
   static void DdmConnected() REQUIRES_SHARED(Locks::mutator_lock_);
   static void DdmDisconnected() REQUIRES_SHARED(Locks::mutator_lock_);
-  static void DdmSendChunk(uint32_t type, const ArrayRef<const uint8_t>& bytes)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-  static void DdmSendChunk(uint32_t type, const std::vector<uint8_t>& bytes)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-  static void DdmSendChunk(uint32_t type, size_t len, const uint8_t* buf)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-  static void DdmSendChunkV(uint32_t type, const iovec* iov, int iov_count)
-      REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Visit breakpoint roots, used to prevent unloading of methods with breakpoints.
   static void VisitRoots(RootVisitor* visitor)
diff --git a/runtime/dex_file-inl.h b/runtime/dex_file-inl.h
index c926a0d..1880968 100644
--- a/runtime/dex_file-inl.h
+++ b/runtime/dex_file-inl.h
@@ -386,6 +386,7 @@
 
 template<typename NewLocalCallback>
 bool DexFile::DecodeDebugLocalInfo(const CodeItem* code_item,
+                                   uint32_t debug_info_offset,
                                    bool is_static,
                                    uint32_t method_idx,
                                    NewLocalCallback new_local_callback,
@@ -398,7 +399,7 @@
   for (; it.HasNext(); it.Next()) {
     arg_descriptors.push_back(it.GetDescriptor());
   }
-  return DecodeDebugLocalInfo(GetDebugInfoStream(code_item),
+  return DecodeDebugLocalInfo(GetDebugInfoStream(debug_info_offset),
                               GetLocation(),
                               GetMethodDeclaringClassDescriptor(GetMethodId(method_idx)),
                               arg_descriptors,
@@ -488,12 +489,13 @@
 
 template<typename DexDebugNewPosition>
 bool DexFile::DecodeDebugPositionInfo(const CodeItem* code_item,
+                                      uint32_t debug_info_offset,
                                       DexDebugNewPosition position_functor,
                                       void* context) const {
   if (code_item == nullptr) {
     return false;
   }
-  return DecodeDebugPositionInfo(GetDebugInfoStream(code_item),
+  return DecodeDebugPositionInfo(GetDebugInfoStream(debug_info_offset),
                                  [this](uint32_t idx) {
                                    return StringDataByIdx(dex::StringIndex(idx));
                                  },
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index cdefb23..2166ed1 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -698,6 +698,15 @@
     return reinterpret_cast<const CodeItem*>(addr);
   }
 
+  uint32_t GetDebugInfoOffset(const CodeItem* code_item) const {
+    if (code_item == nullptr) {
+      return 0;
+    }
+    CHECK(oat_dex_file_ == nullptr)
+        << "Should only use GetDebugInfoOffset in a non runtime setup";
+    return code_item->debug_info_off_;
+  }
+
   const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const;
 
   // Returns the number of prototype identifiers in the .dex file.
@@ -775,11 +784,10 @@
   static int32_t FindCatchHandlerOffset(const CodeItem &code_item, uint32_t address);
 
   // Get the pointer to the start of the debugging data
-  const uint8_t* GetDebugInfoStream(const CodeItem* code_item) const {
+  const uint8_t* GetDebugInfoStream(uint32_t debug_info_off) const {
     // Check that the offset is in bounds.
     // Note that although the specification says that 0 should be used if there
     // is no debug information, some applications incorrectly use 0xFFFFFFFF.
-    const uint32_t debug_info_off = code_item->debug_info_off_;
     return (debug_info_off == 0 || debug_info_off >= size_) ? nullptr : begin_ + debug_info_off;
   }
 
@@ -936,6 +944,7 @@
                                    void* context);
   template<typename NewLocalCallback>
   bool DecodeDebugLocalInfo(const CodeItem* code_item,
+                            uint32_t debug_info_offset,
                             bool is_static,
                             uint32_t method_idx,
                             NewLocalCallback new_local,
@@ -949,6 +958,7 @@
                                       void* context);
   template<typename DexDebugNewPosition>
   bool DecodeDebugPositionInfo(const CodeItem* code_item,
+                               uint32_t debug_info_offset,
                                DexDebugNewPosition position_functor,
                                void* context) const;
 
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index 27060ae..b44bd51 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -28,6 +28,7 @@
 #include "jvalue-inl.h"
 #include "mirror/field.h"
 #include "mirror/method.h"
+#include "oat_file.h"
 #include "reflection.h"
 #include "thread.h"
 #include "well_known_classes.h"
@@ -1571,7 +1572,9 @@
 
   // A method with no line number info should return -1
   DexFile::LineNumFromPcContext context(rel_pc, -1);
-  dex_file->DecodeDebugPositionInfo(code_item, DexFile::LineNumForPcCb, &context);
+  uint32_t debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file, code_item);
+  dex_file->DecodeDebugPositionInfo(
+      code_item, debug_info_offset, DexFile::LineNumForPcCb, &context);
   return context.line_num_;
 }
 
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index c963f6e..14c36b4 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -730,7 +730,8 @@
       kRawDexDebugInfoLocalNullType, tmp.GetFilename().c_str(), 0xf25f2b38U, true);
   const DexFile::ClassDef& class_def = raw->GetClassDef(0);
   const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def, 1));
-  ASSERT_TRUE(raw->DecodeDebugLocalInfo(code_item, true, 1, Callback, nullptr));
+  uint32_t debug_info_offset = raw->GetDebugInfoOffset(code_item);
+  ASSERT_TRUE(raw->DecodeDebugLocalInfo(code_item, debug_info_offset, true, 1, Callback, nullptr));
 }
 
 }  // namespace art
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index fbac348..5f54d5d 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1499,6 +1499,14 @@
   }
 }
 
+uint32_t OatFile::GetDebugInfoOffset(const DexFile& dex_file ATTRIBUTE_UNUSED,
+                                     const DexFile::CodeItem* code_item) {
+  if (code_item == nullptr) {
+    return 0;
+  }
+  return code_item->debug_info_off_;
+}
+
 const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location,
                                                   const uint32_t* dex_location_checksum,
                                                   std::string* error_msg) const {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index d06cf1b..36a4d7b 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -109,11 +109,15 @@
   static OatFile* OpenWritable(File* file, const std::string& location,
                                const char* abs_dex_location,
                                std::string* error_msg);
-  // Opens an oat file from an already opened File. Maps it PROT_READ, MAP_PRIVATE.
+  // Open an oat file from an already opened File. Maps it PROT_READ, MAP_PRIVATE.
   static OatFile* OpenReadable(File* file, const std::string& location,
                                const char* abs_dex_location,
                                std::string* error_msg);
 
+  // Return the debug info offset of the code item `item` located in `dex_file`.
+  static uint32_t GetDebugInfoOffset(const DexFile& dex_file,
+                                     const DexFile::CodeItem* item);
+
   virtual ~OatFile();
 
   bool IsExecutable() const {
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 4b5a761..a113ab5 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -413,42 +413,40 @@
     sampling_pthread_ = 0U;
   }
 
-  {
+  if (the_trace != nullptr) {
+    stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0;
+    if (finish_tracing) {
+      the_trace->FinishTracing();
+    }
     gc::ScopedGCCriticalSection gcs(self,
                                     gc::kGcCauseInstrumentation,
                                     gc::kCollectorTypeInstrumentation);
     ScopedSuspendAll ssa(__FUNCTION__);
-    if (the_trace != nullptr) {
-      stop_alloc_counting = (the_trace->flags_ & Trace::kTraceCountAllocs) != 0;
-      if (finish_tracing) {
-        the_trace->FinishTracing();
-      }
 
-      if (the_trace->trace_mode_ == TraceMode::kSampling) {
-        MutexLock mu(self, *Locks::thread_list_lock_);
-        runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);
-      } else {
-        runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);
-        runtime->GetInstrumentation()->RemoveListener(
-            the_trace, instrumentation::Instrumentation::kMethodEntered |
-            instrumentation::Instrumentation::kMethodExited |
-            instrumentation::Instrumentation::kMethodUnwind);
-      }
-      if (the_trace->trace_file_.get() != nullptr) {
-        // Do not try to erase, so flush and close explicitly.
-        if (flush_file) {
-          if (the_trace->trace_file_->Flush() != 0) {
-            PLOG(WARNING) << "Could not flush trace file.";
-          }
-        } else {
-          the_trace->trace_file_->MarkUnchecked();  // Do not trigger guard.
-        }
-        if (the_trace->trace_file_->Close() != 0) {
-          PLOG(ERROR) << "Could not close trace file.";
-        }
-      }
-      delete the_trace;
+    if (the_trace->trace_mode_ == TraceMode::kSampling) {
+      MutexLock mu(self, *Locks::thread_list_lock_);
+      runtime->GetThreadList()->ForEach(ClearThreadStackTraceAndClockBase, nullptr);
+    } else {
+      runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);
+      runtime->GetInstrumentation()->RemoveListener(
+          the_trace, instrumentation::Instrumentation::kMethodEntered |
+          instrumentation::Instrumentation::kMethodExited |
+          instrumentation::Instrumentation::kMethodUnwind);
     }
+    if (the_trace->trace_file_.get() != nullptr) {
+      // Do not try to erase, so flush and close explicitly.
+      if (flush_file) {
+        if (the_trace->trace_file_->Flush() != 0) {
+          PLOG(WARNING) << "Could not flush trace file.";
+        }
+      } else {
+        the_trace->trace_file_->MarkUnchecked();  // Do not trigger guard.
+      }
+      if (the_trace->trace_file_->Close() != 0) {
+        PLOG(ERROR) << "Could not close trace file.";
+      }
+    }
+    delete the_trace;
   }
   if (stop_alloc_counting) {
     // Can be racy since SetStatsEnabled is not guarded by any locks.
@@ -717,12 +715,12 @@
     FlushBuf();
   } else {
     if (trace_file_.get() == nullptr) {
-      iovec iov[2];
-      iov[0].iov_base = reinterpret_cast<void*>(const_cast<char*>(header.c_str()));
-      iov[0].iov_len = header.length();
-      iov[1].iov_base = buf_.get();
-      iov[1].iov_len = final_offset;
-      Dbg::DdmSendChunkV(CHUNK_TYPE("MPSE"), iov, 2);
+      std::vector<uint8_t> data;
+      data.resize(header.length() + final_offset);
+      memcpy(data.data(), header.c_str(), header.length());
+      memcpy(data.data() + header.length(), buf_.get(), final_offset);
+      Runtime::Current()->GetRuntimeCallbacks()->DdmPublishChunk(CHUNK_TYPE("MPSE"),
+                                                                 ArrayRef<const uint8_t>(data));
       const bool kDumpTraceInfo = false;
       if (kDumpTraceInfo) {
         LOG(INFO) << "Trace sent:\n" << header;
diff --git a/test/1940-ddms-ext/expected.txt b/test/1940-ddms-ext/expected.txt
index cf4ad50..62d3b7b 100644
--- a/test/1940-ddms-ext/expected.txt
+++ b/test/1940-ddms-ext/expected.txt
@@ -5,3 +5,6 @@
 JVMTI returned chunk: Chunk(Type: 0xFADE7357, Len: 8, data: [0, 0, 0, 0, 0, -128, 0, 37])
 Sending chunk: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16])
 Chunk published: Chunk(Type: 0xDEADBEEF, Len: 8, data: [9, 10, 11, 12, 13, 14, 15, 16])
+Saw expected thread events.
+Expected chunk type published: 1213221190
+Expected chunk type published: 1297109829
diff --git a/test/1940-ddms-ext/src-art/art/Test1940.java b/test/1940-ddms-ext/src-art/art/Test1940.java
index f0ee710..9f79eae 100644
--- a/test/1940-ddms-ext/src-art/art/Test1940.java
+++ b/test/1940-ddms-ext/src-art/art/Test1940.java
@@ -17,6 +17,7 @@
 package art;
 
 import org.apache.harmony.dalvik.ddmc.*;
+import dalvik.system.VMDebug;
 
 import java.lang.reflect.Method;
 import java.util.Arrays;
@@ -30,6 +31,12 @@
   public static final int MY_DDMS_TYPE = 0xDEADBEEF;
   public static final int MY_DDMS_RESPONSE_TYPE = 0xFADE7357;
 
+  public static final boolean PRINT_ALL_CHUNKS = false;
+
+  public static interface DdmHandler {
+    public void HandleChunk(int type, byte[] data);
+  }
+
   public static final class TestError extends Error {
     public TestError(String s) { super(s); }
   }
@@ -69,11 +76,38 @@
 
   public static final ChunkHandler SINGLE_HANDLER = new MyDdmHandler();
 
+  public static DdmHandler CURRENT_HANDLER;
+
   public static void HandlePublish(int type, byte[] data) {
-    System.out.println("Chunk published: " + printChunk(new Chunk(type, data, 0, data.length)));
+    if (PRINT_ALL_CHUNKS) {
+      System.out.println(
+          "Unknown Chunk published: " + printChunk(new Chunk(type, data, 0, data.length)));
+    }
+    CURRENT_HANDLER.HandleChunk(type, data);
+  }
+
+  // TYPE Thread Create
+  public static final int TYPE_THCR = 0x54484352;
+  // Type Thread name
+  public static final int TYPE_THNM = 0x54484E4D;
+  // Type Thread death.
+  public static final int TYPE_THDE = 0x54484445;
+  // Type Heap info
+  public static final int TYPE_HPIF = 0x48504946;
+  // Type Trace Results
+  public static final int TYPE_MPSE = 0x4D505345;
+
+  public static boolean IsFromThread(Thread t, byte[] data) {
+    // DDMS always puts the thread-id as the first 4 bytes.
+    ByteBuffer b = ByteBuffer.wrap(data);
+    b.order(ByteOrder.BIG_ENDIAN);
+    return b.getInt() == (int) t.getId();
   }
 
   public static void run() throws Exception {
+    CURRENT_HANDLER = (type, data) -> {
+      System.out.println("Chunk published: " + printChunk(new Chunk(type, data, 0, data.length)));
+    };
     initializeTest(
         Test1940.class,
         Test1940.class.getDeclaredMethod("HandlePublish", Integer.TYPE, new byte[0].getClass()));
@@ -90,8 +124,70 @@
         MY_DDMS_TYPE, new byte[] { 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }, 0, 8);
     System.out.println("Sending chunk: " + printChunk(c));
     DdmServer.sendChunk(c);
+
+    // Test thread chunks are sent.
+    final boolean[] types_seen = new boolean[] { false, false, false };
+    CURRENT_HANDLER = (type, cdata) -> {
+      switch (type) {
+        case TYPE_THCR:
+          types_seen[0] = true;
+          break;
+        case TYPE_THNM:
+          types_seen[1] = true;
+          break;
+        case TYPE_THDE:
+          types_seen[2] = true;
+          break;
+        default:
+          // We don't want to print other types.
+          break;
+      }
+    };
+    DdmVmInternal.threadNotify(true);
+    final Thread thr = new Thread(() -> { return; }, "THREAD");
+    thr.start();
+    thr.join();
+    DdmVmInternal.threadNotify(false);
+    // Make sure we saw at least one of Thread-create, Thread name, & thread death.
+    if (!types_seen[0] || !types_seen[1] || !types_seen[2]) {
+      System.out.println("Didn't see expected chunks for thread creation! got: " +
+          Arrays.toString(types_seen));
+    } else {
+      System.out.println("Saw expected thread events.");
+    }
+
+    // Test heap chunks are sent.
+    CURRENT_HANDLER = (type, cdata) -> {
+      // The actual data is far to noisy for this test as it includes information about global heap
+      // state.
+      if (type == TYPE_HPIF) {
+        System.out.println("Expected chunk type published: " + type);
+      }
+    };
+    final int HPIF_WHEN_NOW = 1;
+    if (!DdmVmInternal.heapInfoNotify(HPIF_WHEN_NOW)) {
+      System.out.println("Unexpected failure for heapInfoNotify!");
+    }
+
+    // method Tracing
+    CURRENT_HANDLER = (type, cdata) -> {
+      // This chunk includes timing and thread information so we just check the type.
+      if (type == TYPE_MPSE) {
+        System.out.println("Expected chunk type published: " + type);
+      }
+    };
+    VMDebug.startMethodTracingDdms(/*size: default*/0,
+                                   /*flags: none*/ 0,
+                                   /*sampling*/ false,
+                                   /*interval*/ 0);
+    doNothing();
+    doNothing();
+    doNothing();
+    doNothing();
+    VMDebug.stopMethodTracing();
   }
 
+  private static void doNothing() {}
   private static Chunk processChunk(byte[] val) {
     return processChunk(new Chunk(MY_DDMS_TYPE, val, 0, val.length));
   }
diff --git a/test/669-checker-break/expected.txt b/test/669-checker-break/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/669-checker-break/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/669-checker-break/info.txt b/test/669-checker-break/info.txt
new file mode 100644
index 0000000..3408b3b
--- /dev/null
+++ b/test/669-checker-break/info.txt
@@ -0,0 +1 @@
+Test optimizations of "break" loops.
diff --git a/test/669-checker-break/src/Main.java b/test/669-checker-break/src/Main.java
new file mode 100644
index 0000000..e59061b
--- /dev/null
+++ b/test/669-checker-break/src/Main.java
@@ -0,0 +1,328 @@
+/*
+ * 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.
+ */
+
+/**
+ * Tests for optimizations of break-loops, i.e. loops that break
+ * out of a while-true loop when the end condition is satisfied.
+ * In particular, the tests focus on break-loops that can be
+ * rewritten into regular countable loops (this may improve certain
+ * loops generated by the Kotlin compiler for inclusive ranges).
+ */
+public class Main {
+
+  /// CHECK-START: int Main.breakLoop(int[]) induction_var_analysis (before)
+  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                     loop:none
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                      loop:none
+  /// CHECK-DAG: <<One:i\d+>>  IntConstant 1                      loop:none
+  /// CHECK-DAG: <<Nil:l\d+>>  NullCheck [<<Par>>]                loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi [<<Zero>>,<<AddI:i\d+>>]       loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Bnd:i\d+>>  BoundsCheck [<<Phi>>,{{i\d+}}]     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               ArraySet [<<Nil>>,<<Bnd>>,<<One>>] loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<NE:z\d+>>   NotEqual [{{i\d+}},<<Phi>>]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               If [<<NE>>]                        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddI>>      Add [<<Phi>>,<<One>>]              loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: int Main.breakLoop(int[]) induction_var_analysis (after)
+  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                     loop:none
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                      loop:none
+  /// CHECK-DAG: <<One:i\d+>>  IntConstant 1                      loop:none
+  /// CHECK-DAG: <<Nil:l\d+>>  NullCheck [<<Par>>]                loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi [<<Zero>>,<<AddI:i\d+>>]       loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<LE:z\d+>>   LessThanOrEqual [<<Phi>>,{{i\d+}}] loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               If [<<LE>>]                        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Bnd:i\d+>>  BoundsCheck [<<Phi>>,{{i\d+}}]     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               ArraySet [<<Nil>>,<<Bnd>>,<<One>>] loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddI>>      Add [<<Phi>>,<<One>>]              loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: int Main.breakLoop(int[]) loop_optimization (after)
+  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                          loop:none
+  /// CHECK-DAG: <<One:i\d+>>  IntConstant 1                           loop:none
+  /// CHECK-DAG: <<Four:i\d+>> IntConstant 4                           loop:none
+  /// CHECK-DAG: <<Nil:l\d+>>  NullCheck [<<Par>>]                     loop:none
+  /// CHECK-DAG: <<Rep:d\d+>>  VecReplicateScalar [<<One>>]            loop:none
+  /// CHECK-DAG:               VecStore [<<Nil>>,<<Phi:i\d+>>,<<Rep>>] loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               Add [<<Phi>>,<<Four>>]                  loop:<<Loop>>      outer_loop:none
+  static int breakLoop(int[] a) {
+    int l = 0;
+    int u = a.length - 1;
+    int i = l;
+    if (l <= u) {
+      while (true) {
+        a[i] = 1;
+        if (i == u) break;
+        i++;
+      }
+    }
+    return i;
+  }
+
+  /// CHECK-START: int Main.breakLoopDown(int[]) induction_var_analysis (before)
+  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                     loop:none
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                      loop:none
+  /// CHECK-DAG: <<MOne:i\d+>> IntConstant -1                     loop:none
+  /// CHECK-DAG: <<Two:i\d+>>  IntConstant 2                      loop:none
+  /// CHECK-DAG: <<Nil:l\d+>>  NullCheck [<<Par>>]                loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi [{{i\d+}},<<AddI:i\d+>>]       loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Bnd:i\d+>>  BoundsCheck [<<Phi>>,{{i\d+}}]     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               ArraySet [<<Nil>>,<<Bnd>>,<<Two>>] loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<NE:z\d+>>   NotEqual [<<Phi>>,<<Zero>>]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               If [<<NE>>]                        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddI>>      Add [<<Phi>>,<<MOne>>]             loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: int Main.breakLoopDown(int[]) induction_var_analysis (after)
+  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                        loop:none
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                         loop:none
+  /// CHECK-DAG: <<MOne:i\d+>> IntConstant -1                        loop:none
+  /// CHECK-DAG: <<Two:i\d+>>  IntConstant 2                         loop:none
+  /// CHECK-DAG: <<Nil:l\d+>>  NullCheck [<<Par>>]                   loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi [{{i\d+}},<<AddI:i\d+>>]          loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<GE:z\d+>>   GreaterThanOrEqual [<<Phi>>,<<Zero>>] loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               If [<<GE>>]                           loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Bnd:i\d+>>  BoundsCheck [<<Phi>>,{{i\d+}}]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               ArraySet [<<Nil>>,<<Bnd>>,<<Two>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddI>>      Add [<<Phi>>,<<MOne>>]                loop:<<Loop>>      outer_loop:none
+  static int breakLoopDown(int[] a) {
+    int l = 0;
+    int u = a.length - 1;
+    int i = u;
+    if (u >= l) {
+      while (true) {
+        a[i] = 2;
+        if (i == l) break;
+        i--;
+      }
+    }
+    return i;
+  }
+
+  /// CHECK-START: int Main.breakLoopSafeConst(int[]) induction_var_analysis (before)
+  /// CHECK-DAG: <<Par:l\d+>>   ParameterValue                       loop:none
+  /// CHECK-DAG: <<One:i\d+>>   IntConstant 1                        loop:none
+  /// CHECK-DAG: <<Three:i\d+>> IntConstant 3                        loop:none
+  /// CHECK-DAG: <<L1:i\d+>>    IntConstant 2147483631               loop:none
+  /// CHECK-DAG: <<L2:i\d+>>    IntConstant 2147483646               loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>   Phi [<<L1>>,<<AddI:i\d+>>]           loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Sub:i\d+>>   Sub [<<Phi>>,<<L1>>]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Nil:l\d+>>   NullCheck [<<Par>>]                  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Bnd:i\d+>>   BoundsCheck [<<Sub>>,{{i\d+}}]       loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                ArraySet [<<Nil>>,<<Bnd>>,<<Three>>] loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<NE:z\d+>>    NotEqual [<<Phi>>,<<L2>>]            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                If [<<NE>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddI>>       Add [<<Phi>>,<<One>>]                loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: int Main.breakLoopSafeConst(int[]) induction_var_analysis (after)
+  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                        loop:none
+  /// CHECK-DAG: <<One:i\d+>>   IntConstant 1                        loop:none
+  /// CHECK-DAG: <<Three:i\d+>> IntConstant 3                        loop:none
+  /// CHECK-DAG: <<L1:i\d+>>    IntConstant 2147483631               loop:none
+  /// CHECK-DAG: <<L2:i\d+>>    IntConstant 2147483646               loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>   Phi [<<L1>>,<<AddI:i\d+>>]           loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<LE:z\d+>>    LessThanOrEqual [<<Phi>>,<<L2>>]     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Sub:i\d+>>   Sub [<<Phi>>,<<L1>>]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Nil:l\d+>>   NullCheck [<<Par>>]                  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Bnd:i\d+>>   BoundsCheck [<<Sub>>,{{i\d+}}]       loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                ArraySet [<<Nil>>,<<Bnd>>,<<Three>>] loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddI>>       Add [<<Phi>>,<<One>>]                loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: int Main.breakLoopSafeConst(int[]) loop_optimization (after)
+  /// CHECK-DAG: <<Par:l\d+>>   ParameterValue                          loop:none
+  /// CHECK-DAG: <<One:i\d+>>   IntConstant 1                           loop:none
+  /// CHECK-DAG: <<Three:i\d+>> IntConstant 3                           loop:none
+  /// CHECK-DAG: <<Four:i\d+>>  IntConstant 4                           loop:none
+  /// CHECK-DAG: <<Rep:d\d+>>   VecReplicateScalar [<<Three>>]          loop:none
+  /// CHECK-DAG:                VecStore [<<Par>>,<<Phi:i\d+>>,<<Rep>>] loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:                Add [<<Phi>>,<<Four>>]                  loop:<<Loop>>      outer_loop:none
+  static int breakLoopSafeConst(int[] a) {
+    int l = Integer.MAX_VALUE - 16;
+    int u = Integer.MAX_VALUE - 1;
+    int i = l;
+    if (l <= u) {  // will be removed by simplifier
+      while (true) {
+        a[i - l] = 3;
+        if (i == u) break;
+        i++;
+      }
+    }
+    return i;
+  }
+
+  /// CHECK-START: int Main.breakLoopUnsafeConst(int[]) induction_var_analysis (before)
+  /// CHECK-DAG: <<Par:l\d+>>   ParameterValue                       loop:none
+  /// CHECK-DAG: <<One:i\d+>>   IntConstant 1                        loop:none
+  /// CHECK-DAG: <<Four:i\d+>>  IntConstant 4                        loop:none
+  /// CHECK-DAG: <<L1:i\d+>>    IntConstant 2147483632               loop:none
+  /// CHECK-DAG: <<L2:i\d+>>    IntConstant 2147483647               loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>   Phi [<<L1>>,<<AddI:i\d+>>]           loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Sub:i\d+>>   Sub [<<Phi>>,<<L1>>]                 loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Nil:l\d+>>   NullCheck [<<Par>>]                  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Bnd:i\d+>>   BoundsCheck [<<Sub>>,{{i\d+}}]       loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                ArraySet [<<Nil>>,<<Bnd>>,<<Four>>]  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<NE:z\d+>>    NotEqual [<<Phi>>,<<L2>>]            loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                If [<<NE>>]                          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddI>>       Add [<<Phi>>,<<One>>]                loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: int Main.breakLoopUnsafeConst(int[]) induction_var_analysis (after)
+  /// CHECK-DAG:                NotEqual
+  /// CHECK-NOT:                LessThanOrEqual
+  static int breakLoopUnsafeConst(int[] a) {
+    int l = Integer.MAX_VALUE - 15;
+    int u = Integer.MAX_VALUE;
+    int i = l;
+    if (l <= u) {  // will be removed by simplifier
+      while (true) {
+        a[i - l] = 4;
+        if (i == u) break;  // rewriting exit not safe!
+        i++;
+      }
+    }
+    return i;
+  }
+
+  /// CHECK-START: int Main.breakLoopNastyPhi(int[]) induction_var_analysis (before)
+  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                      loop:none
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                       loop:none
+  /// CHECK-DAG: <<One:i\d+>>  IntConstant 1                       loop:none
+  /// CHECK-DAG: <<Five:i\d+>> IntConstant 5                       loop:none
+  /// CHECK-DAG: <<M123:i\d+>> IntConstant -123                    loop:none
+  /// CHECK-DAG: <<Nil:l\d+>>  NullCheck [<<Par>>]                 loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi [<<Zero>>,<<AddI:i\d+>>]        loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Wrap:i\d+>> Phi [<<M123>>,<<Phi>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Bnd:i\d+>>  BoundsCheck [<<Phi>>,{{i\d+}}]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               ArraySet [<<Nil>>,<<Bnd>>,<<Five>>] loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<NE:z\d+>>   NotEqual [{{i\d+}},<<Phi>>]         loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               If [<<NE>>]                         loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddI>>      Add [<<Phi>>,<<One>>]               loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Comb:i\d+>> Phi [<<M123>>,<<Wrap>>]             loop:none
+  /// CHECK-DAG:               Return [<<Comb>>]                   loop:none
+  //
+  /// CHECK-START: int Main.breakLoopNastyPhi(int[]) induction_var_analysis (after)
+  /// CHECK-DAG:               NotEqual
+  /// CHECK-NOT:               LessThanOrEqual
+  static int breakLoopNastyPhi(int[] a) {
+    int l = 0;
+    int u = a.length - 1;
+    int x = -123;
+    if (l <= u) {
+      int i = l;
+      while (true) {
+        a[i] = 5;
+        if (i == u) break;
+        x = i;
+        i++;
+      }
+    }
+    return x;  // keep another phi live
+  }
+
+  /// CHECK-START: int Main.breakLoopReduction(int[]) induction_var_analysis (before)
+  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                 loop:none
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                  loop:none
+  /// CHECK-DAG: <<One:i\d+>>  IntConstant 1                  loop:none
+  /// CHECK-DAG: <<Nil:l\d+>>  NullCheck [<<Par>>]                 loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi [<<Zero>>,<<AddI:i\d+>>]   loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Red:i\d+>>  Phi [<<Zero>>,<<RedI:i\d+>>]   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Bnd:i\d+>>  BoundsCheck [<<Phi>>,{{i\d+}}] loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get:i\d+>>  ArrayGet [<<Nil>>,<<Bnd>>]     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<RedI>>      Add [<<Red>>,<<Get>>]          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<NE:z\d+>>   NotEqual [{{i\d+}},<<Phi>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               If [<<NE>>]                    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddI>>      Add [<<Phi>>,<<One>>]          loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Comb:i\d+>> Phi [<<Zero>>,<<RedI>>]        loop:none
+  /// CHECK-DAG:               Return [<<Comb>>]              loop:none
+  //
+  /// CHECK-START: int Main.breakLoopReduction(int[]) induction_var_analysis (after)
+  /// CHECK-DAG: <<Par:l\d+>>  ParameterValue                     loop:none
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                      loop:none
+  /// CHECK-DAG: <<One:i\d+>>  IntConstant 1                      loop:none
+  /// CHECK-DAG: <<Nil:l\d+>>  NullCheck [<<Par>>]                loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi [<<Zero>>,<<AddI:i\d+>>]       loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Red:i\d+>>  Phi [<<Zero>>,<<RedI:i\d+>>]       loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<LE:z\d+>>   LessThanOrEqual [<<Phi>>,{{i\d+}}] loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:               If [<<LE>>]                        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Bnd:i\d+>>  BoundsCheck [<<Phi>>,{{i\d+}}]     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get:i\d+>>  ArrayGet [<<Nil>>,<<Bnd>>]         loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<RedI>>      Add [<<Red>>,<<Get>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<AddI>>      Add [<<Phi>>,<<One>>]              loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Comb:i\d+>> Phi [<<Zero>>,<<Red>>]             loop:none
+  /// CHECK-DAG:               Return [<<Comb>>]                  loop:none
+  //
+  /// CHECK-START-ARM64: int Main.breakLoopReduction(int[]) loop_optimization (after)
+  /// CHECK-DAG: <<Par:l\d+>>   ParameterValue              loop:none
+  /// CHECK-DAG: <<Zero:i\d+>>  IntConstant 0               loop:none
+  /// CHECK-DAG: <<Exp:d\d+>>   VecSetScalars [<<Zero>>]    loop:none
+  /// CHECK-DAG: <<VPhi:d\d+>>  Phi [<<Exp>>,<<VAdd:d\d+>>] loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<VLoad:d\d+>> VecLoad                     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<VAdd>>       VecAdd [<<VPhi>>,<<VLoad>>] loop:<<Loop>>      outer_loop:none
+  static int breakLoopReduction(int[] a) {
+    int l = 0;
+    int u = a.length - 1;
+    int x = 0;
+    if (l <= u) {
+      int i = l;
+      while (true) {
+        x += a[i];
+        if (i == u) break;
+        i++;
+      }
+    }
+    return x;
+  }
+
+  //
+  // Test driver.
+  //
+
+  public static void main(String[] args) {
+    int[] a = new int[100];
+
+    expectEquals(99, breakLoop(a));
+    for (int i = 0; i < a.length; i++) {
+      expectEquals(1, a[i]);
+    }
+
+    expectEquals(0, breakLoopDown(a));
+    for (int i = 0; i < a.length; i++) {
+      expectEquals(2, a[i]);
+    }
+
+    expectEquals(Integer.MAX_VALUE - 1, breakLoopSafeConst(a));
+    for (int i = 0; i < a.length; i++) {
+      int e = i < 16 ? 3 : 2;
+      expectEquals(e, a[i]);
+    }
+
+    expectEquals(Integer.MAX_VALUE, breakLoopUnsafeConst(a));
+    for (int i = 0; i < a.length; i++) {
+      int e = i < 16 ? 4 : 2;
+      expectEquals(e, a[i]);
+    }
+
+    expectEquals(98, breakLoopNastyPhi(a));
+    for (int i = 0; i < a.length; i++) {
+      expectEquals(5, a[i]);
+    }
+
+    expectEquals(500, breakLoopReduction(a));
+
+    System.out.println("passed");
+  }
+
+  private static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index c6b6574..5b2ebf5 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -202,6 +202,12 @@
         "bug": "http://b/34369284"
     },
     {
+        "tests": "1940-ddms-ext",
+        "description": ["Test expects to be able to start tracing but we cannot",
+                        "do that if tracing is already ongoing."],
+        "variant": "trace | stream"
+    },
+    {
         "tests": "137-cfi",
         "description": ["This test unrolls and expects managed frames, but",
                         "tracing means we run the interpreter."],
diff --git a/tools/libjdwp_art_failures.txt b/tools/libjdwp_art_failures.txt
index fd711bb..abcc728 100644
--- a/tools/libjdwp_art_failures.txt
+++ b/tools/libjdwp_art_failures.txt
@@ -103,5 +103,11 @@
   result: EXEC_FAILED,
   bug: 69169846,
   name: "org.apache.harmony.jpda.tests.jdwp.DDM.DDMTest#testChunk001"
+},
+{
+  description: "Test crashes",
+  result: EXEC_FAILED,
+  bug: 69591477,
+  name: "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ExitTest#testExit001"
 }
 ]
diff --git a/tools/libjdwp_oj_art_failures.txt b/tools/libjdwp_oj_art_failures.txt
index 3d06bcf..e1cc831 100644
--- a/tools/libjdwp_oj_art_failures.txt
+++ b/tools/libjdwp_oj_art_failures.txt
@@ -73,5 +73,11 @@
   result: EXEC_FAILED,
   bug: 69169846,
   name: "org.apache.harmony.jpda.tests.jdwp.DDM.DDMTest#testChunk001"
+},
+{
+  description: "Test crashes",
+  result: EXEC_FAILED,
+  bug: 69591477,
+  name: "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ExitTest#testExit001"
 }
 ]