Merge "Fix and improve shift and rotate operations."
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 07bd0e3..c747ffa 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -1986,7 +1986,7 @@
                                          const ImageInfo& image_info,
                                          bool* quick_is_interpreted) {
   DCHECK(!method->IsResolutionMethod()) << PrettyMethod(method);
-  DCHECK(!method->IsImtConflictMethod()) << PrettyMethod(method);
+  DCHECK_NE(method, Runtime::Current()->GetImtConflictMethod()) << PrettyMethod(method);
   DCHECK(!method->IsImtUnimplementedMethod()) << PrettyMethod(method);
   DCHECK(method->IsInvokable()) << PrettyMethod(method);
   DCHECK(!IsInBootImage(method)) << PrettyMethod(method);
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 055061d..528fe44 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -17,7 +17,6 @@
 #include "graph_checker.h"
 
 #include <algorithm>
-#include <map>
 #include <string>
 #include <sstream>
 
@@ -32,19 +31,19 @@
   current_block_ = block;
 
   // Check consistency with respect to predecessors of `block`.
-  ArenaSafeMap<HBasicBlock*, size_t> predecessors_count(
-      std::less<HBasicBlock*>(), GetGraph()->GetArena()->Adapter(kArenaAllocGraphChecker));
-  for (HBasicBlock* p : block->GetPredecessors()) {
-    auto it = predecessors_count.find(p);
-    if (it != predecessors_count.end()) {
-      ++it->second;
-    } else {
-      predecessors_count.Put(p, 1u);
+  // Note: Counting duplicates with a sorted vector uses up to 6x less memory
+  // than ArenaSafeMap<HBasicBlock*, size_t>.
+  ArenaVector<HBasicBlock*> sorted_predecessors(
+      block->GetPredecessors().begin(),
+      block->GetPredecessors().end(),
+      GetGraph()->GetArena()->Adapter(kArenaAllocGraphChecker));
+  std::sort(sorted_predecessors.begin(), sorted_predecessors.end());
+  for (auto it = sorted_predecessors.begin(), end = sorted_predecessors.end(); it != end; ) {
+    HBasicBlock* p = *it++;
+    size_t p_count_in_block_predecessors = 1u;
+    for (; it != end && *it == p; ++it) {
+      ++p_count_in_block_predecessors;
     }
-  }
-  for (auto& pc : predecessors_count) {
-    HBasicBlock* p = pc.first;
-    size_t p_count_in_block_predecessors = pc.second;
     size_t block_count_in_p_successors =
         std::count(p->GetSuccessors().begin(), p->GetSuccessors().end(), block);
     if (p_count_in_block_predecessors != block_count_in_p_successors) {
@@ -57,19 +56,19 @@
   }
 
   // Check consistency with respect to successors of `block`.
-  ArenaSafeMap<HBasicBlock*, size_t> successors_count(
-      std::less<HBasicBlock*>(), GetGraph()->GetArena()->Adapter(kArenaAllocGraphChecker));
-  for (HBasicBlock* s : block->GetSuccessors()) {
-    auto it = successors_count.find(s);
-    if (it != successors_count.end()) {
-      ++it->second;
-    } else {
-      successors_count.Put(s, 1u);
+  // Note: Counting duplicates with a sorted vector uses up to 6x less memory
+  // than ArenaSafeMap<HBasicBlock*, size_t>.
+  ArenaVector<HBasicBlock*> sorted_successors(
+      block->GetSuccessors().begin(),
+      block->GetSuccessors().end(),
+      GetGraph()->GetArena()->Adapter(kArenaAllocGraphChecker));
+  std::sort(sorted_successors.begin(), sorted_successors.end());
+  for (auto it = sorted_successors.begin(), end = sorted_successors.end(); it != end; ) {
+    HBasicBlock* s = *it++;
+    size_t s_count_in_block_successors = 1u;
+    for (; it != end && *it == s; ++it) {
+      ++s_count_in_block_successors;
     }
-  }
-  for (auto& sc : successors_count) {
-    HBasicBlock* s = sc.first;
-    size_t s_count_in_block_successors = sc.second;
     size_t block_count_in_s_predecessors =
         std::count(s->GetPredecessors().begin(), s->GetPredecessors().end(), block);
     if (s_count_in_block_successors != block_count_in_s_predecessors) {
@@ -920,6 +919,19 @@
   }
 }
 
+void GraphChecker::VisitNeg(HNeg* instruction) {
+  VisitInstruction(instruction);
+  Primitive::Type input_type = instruction->InputAt(0)->GetType();
+  Primitive::Type result_type = instruction->GetType();
+  if (result_type != Primitive::PrimitiveKind(input_type)) {
+    AddError(StringPrintf("Binary operation %s %d has a result type different "
+                          "from its input kind: %s vs %s.",
+                          instruction->DebugName(), instruction->GetId(),
+                          Primitive::PrettyDescriptor(result_type),
+                          Primitive::PrettyDescriptor(input_type)));
+  }
+}
+
 void GraphChecker::VisitBinaryOperation(HBinaryOperation* op) {
   VisitInstruction(op);
   Primitive::Type lhs_type = op->InputAt(0)->GetType();
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index 8da8457..27d5621 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -59,6 +59,7 @@
   void VisitInstanceOf(HInstanceOf* check) OVERRIDE;
   void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
   void VisitLoadException(HLoadException* load) OVERRIDE;
+  void VisitNeg(HNeg* instruction) OVERRIDE;
   void VisitPackedSwitch(HPackedSwitch* instruction) OVERRIDE;
   void VisitReturn(HReturn* ret) OVERRIDE;
   void VisitReturnVoid(HReturnVoid* ret) OVERRIDE;
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 4f1e90c..3a9d242 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -382,6 +382,8 @@
   void VisitArraySet(HArraySet* array_set) OVERRIDE {
     StartAttributeStream("value_can_be_null") << std::boolalpha
         << array_set->GetValueCanBeNull() << std::noboolalpha;
+    StartAttributeStream("needs_type_check") << std::boolalpha
+        << array_set->NeedsTypeCheck() << std::noboolalpha;
   }
 
   void VisitCompare(HCompare* compare) OVERRIDE {
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index 8d24e26..266cb10 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -789,7 +789,9 @@
     min++;
   }
   // Do both bounds fit the range?
-  int64_t value;
+  // Note: The `value` is initialized to please valgrind - the compiler can reorder
+  // the return value check with the `value` check, b/27651442 .
+  int64_t value = 0;
   return IsAtLeast(lower_expr, &value) && value >= min &&
          IsAtMost(lower_expr, &value)  && value <= max &&
          IsAtLeast(upper_expr, &value) && value >= min &&
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 72ea4b6..5b263b3 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -1539,7 +1539,10 @@
   HInstruction* distance = invoke->InputAt(1);
   // Replace the invoke with an HRor.
   if (is_left) {
-    distance = new (GetGraph()->GetArena()) HNeg(distance->GetType(), distance);
+    // Unconditionally set the type of the negated distance to `int`,
+    // as shift and rotate operations expect a 32-bit (or narrower)
+    // value for their distance input.
+    distance = new (GetGraph()->GetArena()) HNeg(Primitive::kPrimInt, distance);
     invoke->GetBlock()->InsertInstructionBefore(distance, invoke);
   }
   HRor* ror = new (GetGraph()->GetArena()) HRor(type, value, distance);
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 76e6bbd..1280587 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1780,6 +1780,99 @@
                codegen_);
 }
 
+static void CreateIntIntIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::NoLocation());        // Unused receiver.
+  locations->SetInAt(1, Location::RequiresRegister());
+  locations->SetInAt(2, Location::RequiresRegister());
+  locations->SetInAt(3, Location::RequiresRegister());
+  locations->SetInAt(4, Location::RequiresRegister());
+
+  locations->SetOut(Location::RequiresRegister());
+}
+
+static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorMIPS* codegen) {
+  MipsAssembler* assembler = codegen->GetAssembler();
+  bool isR6 = codegen->GetInstructionSetFeatures().IsR6();
+  Register base = locations->InAt(1).AsRegister<Register>();
+  Register offset_lo = locations->InAt(2).AsRegisterPairLow<Register>();
+  Register expected = locations->InAt(3).AsRegister<Register>();
+  Register value = locations->InAt(4).AsRegister<Register>();
+  Register out = locations->Out().AsRegister<Register>();
+
+  DCHECK_NE(base, out);
+  DCHECK_NE(offset_lo, out);
+  DCHECK_NE(expected, out);
+
+  if (type == Primitive::kPrimNot) {
+    // Mark card for object assuming new value is stored.
+    codegen->MarkGCCard(base, value);
+  }
+
+  // do {
+  //   tmp_value = [tmp_ptr] - expected;
+  // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
+  // result = tmp_value != 0;
+
+  MipsLabel loop_head, exit_loop;
+  __ Addu(TMP, base, offset_lo);
+  __ Sync(0);
+  __ Bind(&loop_head);
+  if ((type == Primitive::kPrimInt) || (type == Primitive::kPrimNot)) {
+    if (isR6) {
+      __ LlR6(out, TMP);
+    } else {
+      __ LlR2(out, TMP);
+    }
+  } else {
+      LOG(FATAL) << "Unsupported op size " << type;
+      UNREACHABLE();
+  }
+  __ Subu(out, out, expected);          // If we didn't get the 'expected'
+  __ Sltiu(out, out, 1);                // value, set 'out' to false, and
+  __ Beqz(out, &exit_loop);             // return.
+  __ Move(out, value);  // Use 'out' for the 'store conditional' instruction.
+                        // If we use 'value' directly, we would lose 'value'
+                        // in the case that the store fails.  Whether the
+                        // store succeeds, or fails, it will load the
+                        // correct boolean value into the 'out' register.
+  // This test isn't really necessary. We only support Primitive::kPrimInt,
+  // Primitive::kPrimNot, and we already verified that we're working on one
+  // of those two types. It's left here in case the code needs to support
+  // other types in the future.
+  if ((type == Primitive::kPrimInt) || (type == Primitive::kPrimNot)) {
+    if (isR6) {
+      __ ScR6(out, TMP);
+    } else {
+      __ ScR2(out, TMP);
+    }
+  }
+  __ Beqz(out, &loop_head);     // If we couldn't do the read-modify-write
+                                // cycle atomically then retry.
+  __ Bind(&exit_loop);
+  __ Sync(0);
+}
+
+// boolean sun.misc.Unsafe.compareAndSwapInt(Object o, long offset, int expected, int x)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafeCASInt(HInvoke* invoke) {
+  CreateIntIntIntIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafeCASInt(HInvoke* invoke) {
+  GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
+}
+
+// boolean sun.misc.Unsafe.compareAndSwapObject(Object o, long offset, Object expected, Object x)
+void IntrinsicLocationsBuilderMIPS::VisitUnsafeCASObject(HInvoke* invoke) {
+  CreateIntIntIntIntIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitUnsafeCASObject(HInvoke* invoke) {
+  GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
+}
+
 // char java.lang.String.charAt(int index)
 void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) {
   LocationSummary* locations = new (arena_) LocationSummary(invoke,
@@ -2347,9 +2440,7 @@
 UNIMPLEMENTED_INTRINSIC(MIPS, MathRint)
 UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundDouble)
 UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundFloat)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASInt)
 UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASObject)
 
 UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent)
 UNIMPLEMENTED_INTRINSIC(MIPS, StringGetCharsNoCheck)
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index c83340b..e08e8fb 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -162,7 +162,7 @@
   // (6) Compute the dominance information and the reverse post order.
   ComputeDominanceInformation();
 
-  // (7) Analyze loops discover through back edge analysis, and
+  // (7) Analyze loops discovered through back edge analysis, and
   //     set the loop information on each block.
   GraphAnalysisResult result = AnalyzeLoops();
   if (result != kAnalysisSuccess) {
@@ -247,7 +247,7 @@
   }
 
   // Populate `dominated_blocks_` information after computing all dominators.
-  // The potential presence of irreducible loops require to do it after.
+  // The potential presence of irreducible loops requires to do it after.
   for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
     HBasicBlock* block = it.Current();
     if (!block->IsEntryBlock()) {
@@ -460,7 +460,7 @@
     if (block->IsLoopHeader()) {
       SimplifyLoop(block);
     } else if (!block->IsEntryBlock() && block->GetFirstInstruction()->IsSuspendCheck()) {
-      // We are being called by the dead code elimiation pass, and what used to be
+      // We are being called by the dead code elimination pass, and what used to be
       // a loop got dismantled. Just remove the suspend check.
       block->RemoveInstruction(block->GetFirstInstruction());
     }
@@ -2373,7 +2373,7 @@
   env_uses_.Clear();
 }
 
-// Returns an instruction with the opposite boolean value from 'cond'.
+// Returns an instruction with the opposite Boolean value from 'cond'.
 HInstruction* HGraph::InsertOppositeCondition(HInstruction* cond, HInstruction* cursor) {
   ArenaAllocator* allocator = GetArena();
 
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 53da069..521bd14 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -4148,7 +4148,9 @@
 class HNeg : public HUnaryOperation {
  public:
   HNeg(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
-      : HUnaryOperation(result_type, input, dex_pc) {}
+      : HUnaryOperation(result_type, input, dex_pc) {
+    DCHECK_EQ(result_type, Primitive::PrimitiveKind(input->GetType()));
+  }
 
   template <typename T> T Compute(T x) const { return -x; }
 
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 4d2469c..f1c5581 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -84,6 +84,8 @@
 
 namespace art {
 
+static constexpr size_t kArenaAllocatorMemoryReportThreshold = 8 * MB;
+
 /**
  * Used by the code generator, to allocate the code in a vector.
  */
@@ -761,13 +763,6 @@
     pass_observer.DumpDisassembly();
   }
 
-  if (kArenaAllocatorCountAllocations) {
-    if (arena->BytesAllocated() > 4 * MB) {
-      MemStats mem_stats(arena->GetMemStats());
-      LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(mem_stats);
-    }
-  }
-
   return codegen.release();
 }
 
@@ -812,6 +807,13 @@
     if (codegen.get() != nullptr) {
       MaybeRecordStat(MethodCompilationStat::kCompiled);
       method = Emit(&arena, &code_allocator, codegen.get(), compiler_driver, code_item);
+
+      if (kArenaAllocatorCountAllocations) {
+        if (arena.BytesAllocated() > kArenaAllocatorMemoryReportThreshold) {
+          MemStats mem_stats(arena.GetMemStats());
+          LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(mem_stats);
+        }
+      }
     }
   } else {
     if (compiler_driver->GetCompilerOptions().VerifyAtRuntime()) {
@@ -890,6 +892,13 @@
     if (codegen.get() == nullptr) {
       return false;
     }
+
+    if (kArenaAllocatorCountAllocations) {
+      if (arena.BytesAllocated() > kArenaAllocatorMemoryReportThreshold) {
+        MemStats mem_stats(arena.GetMemStats());
+        LOG(INFO) << PrettyMethod(method_idx, *dex_file) << " " << Dumpable<MemStats>(mem_stats);
+      }
+    }
   }
 
   size_t stack_map_size = codegen->ComputeStackMapsSize();
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 0ad104e..fc72727 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -47,6 +47,19 @@
   bound_type->GetBlock()->RemoveInstruction(bound_type);
 }
 
+void PrepareForRegisterAllocation::VisitArraySet(HArraySet* instruction) {
+  HInstruction* value = instruction->GetValue();
+  // PrepareForRegisterAllocation::VisitBoundType may have replaced a
+  // BoundType (as value input of this ArraySet) with a NullConstant.
+  // If so, this ArraySet no longer needs a type check.
+  if (value->IsNullConstant()) {
+    DCHECK_EQ(value->GetType(), Primitive::kPrimNot);
+    if (instruction->NeedsTypeCheck()) {
+      instruction->ClearNeedsTypeCheck();
+    }
+  }
+}
+
 void PrepareForRegisterAllocation::VisitClinitCheck(HClinitCheck* check) {
   // Try to find a static invoke or a new-instance from which this check originated.
   HInstruction* implicit_clinit = nullptr;
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
index c90724c..a679148 100644
--- a/compiler/optimizing/prepare_for_register_allocation.h
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -40,6 +40,7 @@
   void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE;
   void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE;
   void VisitBoundType(HBoundType* bound_type) OVERRIDE;
+  void VisitArraySet(HArraySet* instruction) OVERRIDE;
   void VisitClinitCheck(HClinitCheck* check) OVERRIDE;
   void VisitCondition(HCondition* condition) OVERRIDE;
   void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index 34d9af1..44bede8 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -1927,7 +1927,13 @@
         BitVector* live = liveness_.GetLiveInSet(*block);
         for (uint32_t idx : live->Indexes()) {
           LiveInterval* interval = liveness_.GetInstructionFromSsaIndex(idx)->GetLiveInterval();
-          DCHECK(!interval->GetSiblingAt(block->GetLifetimeStart())->HasRegister());
+          LiveInterval* sibling = interval->GetSiblingAt(block->GetLifetimeStart());
+          // `GetSiblingAt` returns the sibling that contains a position, but there could be
+          // a lifetime hole in it. `CoversSlow` returns whether the interval is live at that
+          // position.
+          if (sibling->CoversSlow(block->GetLifetimeStart())) {
+            DCHECK(!sibling->HasRegister());
+          }
         }
       }
     } else {
diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc
index 7c41813..a1798c0 100644
--- a/compiler/utils/mips/assembler_mips.cc
+++ b/compiler/utils/mips/assembler_mips.cc
@@ -485,6 +485,28 @@
   EmitI(0x2e, rs, rt, imm16);
 }
 
+void MipsAssembler::LlR2(Register rt, Register base, int16_t imm16) {
+  CHECK(!IsR6());
+  EmitI(0x30, base, rt, imm16);
+}
+
+void MipsAssembler::ScR2(Register rt, Register base, int16_t imm16) {
+  CHECK(!IsR6());
+  EmitI(0x38, base, rt, imm16);
+}
+
+void MipsAssembler::LlR6(Register rt, Register base, int16_t imm9) {
+  CHECK(IsR6());
+  CHECK(IsInt<9>(imm9));
+  EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x36);
+}
+
+void MipsAssembler::ScR6(Register rt, Register base, int16_t imm9) {
+  CHECK(IsR6());
+  CHECK(IsInt<9>(imm9));
+  EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x26);
+}
+
 void MipsAssembler::Slt(Register rd, Register rs, Register rt) {
   EmitR(0, rs, rt, rd, 0, 0x2a);
 }
diff --git a/compiler/utils/mips/assembler_mips.h b/compiler/utils/mips/assembler_mips.h
index a7179fd..ffac4c4 100644
--- a/compiler/utils/mips/assembler_mips.h
+++ b/compiler/utils/mips/assembler_mips.h
@@ -191,6 +191,11 @@
   void Swl(Register rt, Register rs, uint16_t imm16);
   void Swr(Register rt, Register rs, uint16_t imm16);
 
+  void LlR2(Register rt, Register base, int16_t imm16 = 0);
+  void ScR2(Register rt, Register base, int16_t imm16 = 0);
+  void LlR6(Register rt, Register base, int16_t imm9 = 0);
+  void ScR6(Register rt, Register base, int16_t imm9 = 0);
+
   void Slt(Register rd, Register rs, Register rt);
   void Sltu(Register rd, Register rs, Register rt);
   void Slti(Register rt, Register rs, uint16_t imm16);
diff --git a/compiler/utils/mips/assembler_mips_test.cc b/compiler/utils/mips/assembler_mips_test.cc
index 9e27f07..cec43ba 100644
--- a/compiler/utils/mips/assembler_mips_test.cc
+++ b/compiler/utils/mips/assembler_mips_test.cc
@@ -489,6 +489,14 @@
   DriverStr(RepeatRRIb(&mips::MipsAssembler::Swr, -16, "swr ${reg1}, {imm}(${reg2})"), "Swr");
 }
 
+TEST_F(AssemblerMIPSTest, LlR2) {
+  DriverStr(RepeatRRIb(&mips::MipsAssembler::LlR2, -16, "ll ${reg1}, {imm}(${reg2})"), "LlR2");
+}
+
+TEST_F(AssemblerMIPSTest, ScR2) {
+  DriverStr(RepeatRRIb(&mips::MipsAssembler::ScR2, -16, "sc ${reg1}, {imm}(${reg2})"), "ScR2");
+}
+
 TEST_F(AssemblerMIPSTest, Slt) {
   DriverStr(RepeatRRR(&mips::MipsAssembler::Slt, "slt ${reg1}, ${reg2}, ${reg3}"), "Slt");
 }
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index 428266f..a95ea64 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -317,6 +317,7 @@
   { kITypeMask, 42u << kOpcodeShift, "swl", "TO", },
   { kITypeMask, 43u << kOpcodeShift, "sw", "TO", },
   { kITypeMask, 46u << kOpcodeShift, "swr", "TO", },
+  { kITypeMask, 48u << kOpcodeShift, "ll", "TO", },
   { kITypeMask, 49u << kOpcodeShift, "lwc1", "tO", },
   { kJTypeMask, 50u << kOpcodeShift, "bc", "P" },
   { kITypeMask, 53u << kOpcodeShift, "ldc1", "tO", },
@@ -327,6 +328,7 @@
   { kITypeMask | (1 << 24), (54u << kOpcodeShift) | (1 << 24), "beqzc", "Sb" },
   { kITypeMask | (1 << 25), (54u << kOpcodeShift) | (1 << 25), "beqzc", "Sb" },
   { kITypeMask, 55u << kOpcodeShift, "ld", "TO", },
+  { kITypeMask, 56u << kOpcodeShift, "sc", "TO", },
   { kITypeMask, 57u << kOpcodeShift, "swc1", "tO", },
   { kITypeMask | (0x1f << 16), (59u << kOpcodeShift) | (30 << 16), "auipc", "Si" },
   { kITypeMask | (0x3 << 19), (59u << kOpcodeShift) | (0 << 19), "addiupc", "Sp" },
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 9ac18e8..48a9d91 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1997,9 +1997,13 @@
           image_header_.GetPointerSize())) {
         indent_os << StringPrintf("OAT CODE: %p\n", quick_oat_code_begin);
       }
-    } else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
-      method->IsResolutionMethod() || method->IsImtConflictMethod() ||
-      method->IsImtUnimplementedMethod() || method->IsClassInitializer()) {
+    } else if (method->IsAbstract() ||
+               method->IsCalleeSaveMethod() ||
+               method->IsResolutionMethod() ||
+               (method == Runtime::Current()->GetImtConflictMethod()) ||
+               method->IsImtUnimplementedMethod() ||
+               method->IsClassInitializer()) {
+      // Don't print information for these.
     } else {
       const DexFile::CodeItem* code_item = method->GetCodeItem();
       size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2;
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 82ec0b7..5a901f1 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1225,11 +1225,35 @@
 END art_quick_proxy_invoke_handler
 
     /*
-     * Called to resolve an imt conflict. r12 is a hidden argument that holds the target method's
-     * dex method index.
+     * Called to resolve an imt conflict.
+     * r0 is the conflict ArtMethod.
+     * r12 is a hidden argument that holds the target interface method's dex method index.
+     *
+     * Note that this stub writes to r0, r4, and r12.
      */
 ENTRY art_quick_imt_conflict_trampoline
-    mov    r0, r12
+    ldr r4, [sp, #0]  // Load referrer
+    ldr r4, [r4, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_32]   // Load dex cache methods array
+    ldr r12, [r4, r12, lsl #POINTER_SIZE_SHIFT]  // Load interface method
+    ldr r0, [r0, #ART_METHOD_JNI_OFFSET_32]  // Load ImtConflictTable
+    ldr r4, [r0]  // Load first entry in ImtConflictTable.
+.Limt_table_iterate:
+    cmp r4, r12
+    // Branch if found. Benchmarks have shown doing a branch here is better.
+    beq .Limt_table_found
+    // If the entry is null, the interface method is not in the ImtConflictTable.
+    cbz r4, .Lconflict_trampoline
+    // Iterate over the entries of the ImtConflictTable.
+    ldr r4, [r0, #(2 * __SIZEOF_POINTER__)]!
+    b .Limt_table_iterate
+.Limt_table_found:
+    // We successuflly hit an entry in the table. Load the target method
+    // and jump to it.
+    ldr r0, [r0, #__SIZEOF_POINTER__]
+    ldr pc, [r0, #ART_METHOD_QUICK_CODE_OFFSET_32]
+.Lconflict_trampoline:
+    // Call the runtime stub to populate the ImtConflictTable and jump to the
+    // resolved method.
     INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
 END art_quick_imt_conflict_trampoline
 
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 65d5b46..8b497fe 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1757,12 +1757,37 @@
 END art_quick_proxy_invoke_handler
 
     /*
-     * Called to resolve an imt conflict. xIP1 is a hidden argument that holds the target method's
-     * dex method index.
+     * Called to resolve an imt conflict.
+     * x0 is the conflict ArtMethod.
+     * xIP1 is a hidden argument that holds the target interface method's dex method index.
+     *
+     * Note that this stub writes to xIP0, xIP1, and x0.
      */
     .extern artInvokeInterfaceTrampoline
 ENTRY art_quick_imt_conflict_trampoline
-    mov    x0, xIP1
+    ldr xIP0, [sp, #0]  // Load referrer
+    ldr xIP0, [xIP0, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_64]   // Load dex cache methods array
+    ldr xIP0, [xIP0, xIP1, lsl #POINTER_SIZE_SHIFT]  // Load interface method
+    ldr xIP1, [x0, #ART_METHOD_JNI_OFFSET_64]  // Load ImtConflictTable
+    ldr x0, [xIP1]  // Load first entry in ImtConflictTable.
+.Limt_table_iterate:
+    cmp x0, xIP0
+    // Branch if found. Benchmarks have shown doing a branch here is better.
+    beq .Limt_table_found
+    // If the entry is null, the interface method is not in the ImtConflictTable.
+    cbz x0, .Lconflict_trampoline
+    // Iterate over the entries of the ImtConflictTable.
+    ldr x0, [xIP1, #(2 * __SIZEOF_POINTER__)]!
+    b .Limt_table_iterate
+.Limt_table_found:
+    // We successuflly hit an entry in the table. Load the target method
+    // and jump to it.
+    ldr x0, [xIP1, #__SIZEOF_POINTER__]
+    ldr xIP0, [x0, #ART_METHOD_QUICK_CODE_OFFSET_64]
+    br xIP0
+.Lconflict_trampoline:
+    // Call the runtime stub to populate the ImtConflictTable and jump to the
+    // resolved method.
     INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
 END art_quick_imt_conflict_trampoline
 
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 4236c28..1c4b470 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -1999,7 +1999,9 @@
   // 1. imt_conflict
 
   // Contains.
-
+  // TODO(ngeoffray): Re-enable this test. They are now broken with the ImtConflictTable.
+  // b/27794971
+/*
   size_t result =
       Invoke3WithReferrerAndHidden(0U, reinterpret_cast<size_t>(array_list.Get()),
                                    reinterpret_cast<size_t>(obj.Get()),
@@ -2025,18 +2027,18 @@
 
   ASSERT_FALSE(self->IsExceptionPending());
   EXPECT_EQ(static_cast<size_t>(JNI_TRUE), result);
-
+*/
   // 2. regular interface trampoline
 
-  result = Invoke3WithReferrer(static_cast<size_t>(inf_contains->GetDexMethodIndex()),
-                               reinterpret_cast<size_t>(array_list.Get()),
-                               reinterpret_cast<size_t>(obj.Get()),
-                               StubTest::GetEntrypoint(self,
-                                   kQuickInvokeInterfaceTrampolineWithAccessCheck),
-                               self, contains_amethod);
+  size_t result = Invoke3WithReferrer(static_cast<size_t>(inf_contains->GetDexMethodIndex()),
+                                      reinterpret_cast<size_t>(array_list.Get()),
+                                      reinterpret_cast<size_t>(obj.Get()),
+                                      StubTest::GetEntrypoint(self,
+                                         kQuickInvokeInterfaceTrampolineWithAccessCheck),
+                                      self, contains_amethod);
 
   ASSERT_FALSE(self->IsExceptionPending());
-  EXPECT_EQ(static_cast<size_t>(JNI_TRUE), result);
+  EXPECT_EQ(static_cast<size_t>(JNI_FALSE), result);
 
   result = Invoke3WithReferrer(
       static_cast<size_t>(inf_contains->GetDexMethodIndex()),
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 125570d..82ac574 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1402,11 +1402,42 @@
 END_FUNCTION art_quick_proxy_invoke_handler
 
     /*
-     * Called to resolve an imt conflict. xmm7 is a hidden argument that holds the target method's
-     * dex method index.
+     * Called to resolve an imt conflict.
+     * eax is the conflict ArtMethod.
+     * xmm7 is a hidden argument that holds the target interface method's dex method index.
+     *
+     * Note that this stub writes to eax.
+     * Because of lack of free registers, it also saves and restores edi.
      */
 DEFINE_FUNCTION art_quick_imt_conflict_trampoline
+    PUSH EDI
+    movl 8(%esp), %edi // Load referrer
+    movl ART_METHOD_DEX_CACHE_METHODS_OFFSET_32(%edi), %edi   // Load dex cache methods array
+    pushl ART_METHOD_JNI_OFFSET_32(%eax)  // Push ImtConflictTable.
+    CFI_ADJUST_CFA_OFFSET(4)
     movd %xmm7, %eax              // get target method index stored in xmm7
+    movl 0(%edi, %eax, __SIZEOF_POINTER__), %edi  // Load interface method
+    popl %eax  // Pop ImtConflictTable.
+    CFI_ADJUST_CFA_OFFSET(-4)
+.Limt_table_iterate:
+    cmpl %edi, 0(%eax)
+    jne .Limt_table_next_entry
+    // We successuflly hit an entry in the table. Load the target method
+    // and jump to it.
+    POP EDI
+    movl __SIZEOF_POINTER__(%eax), %eax
+    jmp *ART_METHOD_QUICK_CODE_OFFSET_32(%eax)
+.Limt_table_next_entry:
+    // If the entry is null, the interface method is not in the ImtConflictTable.
+    cmpl LITERAL(0), 0(%eax)
+    jz .Lconflict_trampoline
+    // Iterate over the entries of the ImtConflictTable.
+    addl LITERAL(2 * __SIZEOF_POINTER__), %eax
+    jmp .Limt_table_iterate
+.Lconflict_trampoline:
+    // Call the runtime stub to populate the ImtConflictTable and jump to the
+    // resolved method.
+    POP EDI
     INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
 END_FUNCTION art_quick_imt_conflict_trampoline
 
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index dee8d3c..90049cc 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1314,14 +1314,37 @@
 
     /*
      * Called to resolve an imt conflict.
-     * rax is a hidden argument that holds the target method's dex method index.
+     * rdi is the conflict ArtMethod.
+     * rax is a hidden argument that holds the target interface method's dex method index.
+     *
+     * Note that this stub writes to r10 and rdi.
      */
 DEFINE_FUNCTION art_quick_imt_conflict_trampoline
 #if defined(__APPLE__)
     int3
     int3
 #else
-    movq %rax, %rdi
+    movq __SIZEOF_POINTER__(%rsp), %r10 // Load referrer
+    movq ART_METHOD_DEX_CACHE_METHODS_OFFSET_64(%r10), %r10   // Load dex cache methods array
+    movq 0(%r10, %rax, __SIZEOF_POINTER__), %r10 // Load interface method
+    movq ART_METHOD_JNI_OFFSET_64(%rdi), %rdi  // Load ImtConflictTable
+.Limt_table_iterate:
+    cmpq %r10, 0(%rdi)
+    jne .Limt_table_next_entry
+    // We successuflly hit an entry in the table. Load the target method
+    // and jump to it.
+    movq __SIZEOF_POINTER__(%rdi), %rdi
+    jmp *ART_METHOD_QUICK_CODE_OFFSET_64(%rdi)
+.Limt_table_next_entry:
+    // If the entry is null, the interface method is not in the ImtConflictTable.
+    cmpq LITERAL(0), 0(%rdi)
+    jz .Lconflict_trampoline
+    // Iterate over the entries of the ImtConflictTable.
+    addq LITERAL(2 * __SIZEOF_POINTER__), %rdi
+    jmp .Limt_table_iterate
+.Lconflict_trampoline:
+    // Call the runtime stub to populate the ImtConflictTable and jump to the
+    // resolved method.
     INVOKE_TRAMPOLINE_BODY artInvokeInterfaceTrampoline
 #endif  // __APPLE__
 END_FUNCTION art_quick_imt_conflict_trampoline
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 8541210..6449efa 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -264,13 +264,6 @@
   return result;
 }
 
-inline bool ArtMethod::IsImtConflictMethod() {
-  bool result = this == Runtime::Current()->GetImtConflictMethod();
-  // Check that if we do think it is phony it looks like the imt conflict method.
-  DCHECK(!result || IsRuntimeMethod());
-  return result;
-}
-
 inline bool ArtMethod::IsImtUnimplementedMethod() {
   bool result = this == Runtime::Current()->GetImtUnimplementedMethod();
   // Check that if we do think it is phony it looks like the imt unimplemented method.
@@ -463,7 +456,10 @@
       interface_method->VisitRoots(visitor, pointer_size);
     }
     visitor.VisitRoot(declaring_class_.AddressWithoutBarrier());
-    if (!IsNative()) {
+    // Runtime methods and native methods use the same field as the profiling info for
+    // storing their own data (jni entrypoint for native methods, and ImtConflictTable for
+    // some runtime methods).
+    if (!IsNative() && !IsRuntimeMethod()) {
       ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size);
       if (profiling_info != nullptr) {
         profiling_info->VisitRoots(visitor);
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 5ca362c..3dbcd58 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -44,6 +44,76 @@
 class PointerArray;
 }  // namespace mirror
 
+// Table to resolve IMT conflicts at runtime. The table is attached to
+// the jni entrypoint of IMT conflict ArtMethods.
+// The table contains a list of pairs of { interface_method, implementation_method }
+// with the last entry being null to make an assembly implementation of a lookup
+// faster.
+class ImtConflictTable {
+ public:
+  // Build a new table copying `other` and adding the new entry formed of
+  // the pair { `interface_method`, `implementation_method` }
+  ImtConflictTable(ImtConflictTable* other,
+                   ArtMethod* interface_method,
+                   ArtMethod* implementation_method) {
+    size_t index = 0;
+    while (other->entries_[index].interface_method != nullptr) {
+      entries_[index] = other->entries_[index];
+      index++;
+    }
+    entries_[index].interface_method = interface_method;
+    entries_[index].implementation_method = implementation_method;
+    // Add the null marker.
+    entries_[index + 1].interface_method = nullptr;
+    entries_[index + 1].implementation_method = nullptr;
+  }
+
+  // Lookup the implementation ArtMethod associated to `interface_method`. Return null
+  // if not found.
+  ArtMethod* Lookup(ArtMethod* interface_method) const {
+    uint32_t table_index = 0;
+    ArtMethod* current_interface_method;
+    while ((current_interface_method = entries_[table_index].interface_method) != nullptr) {
+      if (current_interface_method == interface_method) {
+        return entries_[table_index].implementation_method;
+      }
+      table_index++;
+    }
+    return nullptr;
+  }
+
+  // Compute the size in bytes taken by this table.
+  size_t ComputeSize() const {
+    uint32_t table_index = 0;
+    size_t total_size = 0;
+    while ((entries_[table_index].interface_method) != nullptr) {
+      total_size += sizeof(Entry);
+      table_index++;
+    }
+    // Add the end marker.
+    return total_size + sizeof(Entry);
+  }
+
+  // Compute the size in bytes needed for copying the given `table` and add
+  // one more entry.
+  static size_t ComputeSizeWithOneMoreEntry(ImtConflictTable* table) {
+    return table->ComputeSize() + sizeof(Entry);
+  }
+
+  struct Entry {
+    ArtMethod* interface_method;
+    ArtMethod* implementation_method;
+  };
+
+ private:
+  // Array of entries that the assembly stubs will iterate over. Note that this is
+  // not fixed size, and we allocate data prior to calling the constructor
+  // of ImtConflictTable.
+  Entry entries_[0];
+
+  DISALLOW_COPY_AND_ASSIGN(ImtConflictTable);
+};
+
 class ArtMethod FINAL {
  public:
   ArtMethod() : access_flags_(0), dex_code_item_offset_(0), dex_method_index_(0),
@@ -338,6 +408,15 @@
     return reinterpret_cast<ProfilingInfo*>(GetEntryPointFromJniPtrSize(pointer_size));
   }
 
+  ImtConflictTable* GetImtConflictTable(size_t pointer_size) {
+    DCHECK(IsRuntimeMethod());
+    return reinterpret_cast<ImtConflictTable*>(GetEntryPointFromJniPtrSize(pointer_size));
+  }
+
+  ALWAYS_INLINE void SetImtConflictTable(ImtConflictTable* table) {
+    SetEntryPointFromJniPtrSize(table, sizeof(void*));
+  }
+
   ALWAYS_INLINE void SetProfilingInfo(ProfilingInfo* info) {
     SetEntryPointFromJniPtrSize(info, sizeof(void*));
   }
@@ -376,8 +455,6 @@
 
   bool IsResolutionMethod() SHARED_REQUIRES(Locks::mutator_lock_);
 
-  bool IsImtConflictMethod() SHARED_REQUIRES(Locks::mutator_lock_);
-
   bool IsImtUnimplementedMethod() SHARED_REQUIRES(Locks::mutator_lock_);
 
   MethodReference ToMethodReference() SHARED_REQUIRES(Locks::mutator_lock_) {
@@ -537,7 +614,7 @@
     GcRoot<mirror::Class>* dex_cache_resolved_types_;
 
     // Pointer to JNI function registered to this method, or a function to resolve the JNI function,
-    // or the profiling data for non-native methods.
+    // or the profiling data for non-native methods, or an ImtConflictTable.
     void* entry_point_from_jni_;
 
     // Method dispatch from quick compiled code invokes this pointer which may cause bridging into
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 1f24f45..942f9de 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -299,6 +299,14 @@
 ADD_TEST_EQ(ART_METHOD_DEX_CACHE_TYPES_OFFSET_64,
             art::ArtMethod::DexCacheResolvedTypesOffset(8).Int32Value())
 
+#define ART_METHOD_JNI_OFFSET_32 28
+ADD_TEST_EQ(ART_METHOD_JNI_OFFSET_32,
+            art::ArtMethod::EntryPointFromJniOffset(4).Int32Value())
+
+#define ART_METHOD_JNI_OFFSET_64 40
+ADD_TEST_EQ(ART_METHOD_JNI_OFFSET_64,
+            art::ArtMethod::EntryPointFromJniOffset(8).Int32Value())
+
 #define ART_METHOD_QUICK_CODE_OFFSET_32 32
 ADD_TEST_EQ(ART_METHOD_QUICK_CODE_OFFSET_32,
             art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value())
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 7539943..70ff60f 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -33,27 +33,9 @@
 template <bool kCount>
 const char* const ArenaAllocatorStatsImpl<kCount>::kAllocNames[] = {
   "Misc         ",
-  "BBList       ",
-  "BBPreds      ",
-  "DfsPreOrd    ",
-  "DfsPostOrd   ",
-  "DomPostOrd   ",
-  "TopoOrd      ",
-  "Lowering     ",
-  "LIR          ",
-  "LIR masks    ",
   "SwitchTbl    ",
-  "FillArray    ",
   "SlowPaths    ",
-  "MIR          ",
-  "DataFlow     ",
-  "GrowList     ",
   "GrowBitMap   ",
-  "SSA2Dalvik   ",
-  "Dalvik2SSA   ",
-  "DebugInfo    ",
-  "RegAlloc     ",
-  "Data         ",
   "STL          ",
   "GraphBuilder ",
   "Graph        ",
@@ -80,7 +62,6 @@
   "MoveOperands ",
   "CodeBuffer   ",
   "StackMaps    ",
-  "BaselineMaps ",
   "Optimization ",
   "GVN          ",
   "InductionVar ",
@@ -91,7 +72,6 @@
   "SsaLiveness  ",
   "SsaPhiElim   ",
   "RefTypeProp  ",
-  "PrimTypeProp ",
   "SideEffects  ",
   "RegAllocator ",
   "RegAllocVldt ",
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index f8f7396..697f7e0 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -44,27 +44,9 @@
 // Type of allocation for memory tuning.
 enum ArenaAllocKind {
   kArenaAllocMisc,
-  kArenaAllocBBList,
-  kArenaAllocBBPredecessors,
-  kArenaAllocDfsPreOrder,
-  kArenaAllocDfsPostOrder,
-  kArenaAllocDomPostOrder,
-  kArenaAllocTopologicalSortOrder,
-  kArenaAllocLoweringInfo,
-  kArenaAllocLIR,
-  kArenaAllocLIRResourceMask,
   kArenaAllocSwitchTable,
-  kArenaAllocFillArrayData,
   kArenaAllocSlowPaths,
-  kArenaAllocMIR,
-  kArenaAllocDFInfo,
-  kArenaAllocGrowableArray,
   kArenaAllocGrowableBitMap,
-  kArenaAllocSSAToDalvikMap,
-  kArenaAllocDalvikToSSAMap,
-  kArenaAllocDebugInfo,
-  kArenaAllocRegAlloc,
-  kArenaAllocData,
   kArenaAllocSTL,
   kArenaAllocGraphBuilder,
   kArenaAllocGraph,
@@ -91,7 +73,6 @@
   kArenaAllocMoveOperands,
   kArenaAllocCodeBuffer,
   kArenaAllocStackMaps,
-  kArenaAllocBaselineMaps,
   kArenaAllocOptimization,
   kArenaAllocGvn,
   kArenaAllocInductionVarAnalysis,
@@ -102,7 +83,6 @@
   kArenaAllocSsaLiveness,
   kArenaAllocSsaPhiElimination,
   kArenaAllocReferenceTypePropagation,
-  kArenaAllocPrimitiveTypePropagation,
   kArenaAllocSideEffectsAnalysis,
   kArenaAllocRegisterAllocator,
   kArenaAllocRegisterAllocatorValidate,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index c1115da..99e38d9 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -498,10 +498,11 @@
   object_array_string->SetComponentType(java_lang_String.Get());
   SetClassRoot(kJavaLangStringArrayClass, object_array_string.Get());
 
+  LinearAlloc* linear_alloc = runtime->GetLinearAlloc();
   // Create runtime resolution and imt conflict methods.
   runtime->SetResolutionMethod(runtime->CreateResolutionMethod());
-  runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod());
-  runtime->SetImtUnimplementedMethod(runtime->CreateImtConflictMethod());
+  runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod(linear_alloc));
+  runtime->SetImtUnimplementedMethod(runtime->CreateImtConflictMethod(linear_alloc));
 
   // Setup boot_class_path_ and register class_path now that we can use AllocObjectArray to create
   // DexCache instances. Needs to be after String, Field, Method arrays since AllocDexCache uses
@@ -5904,9 +5905,11 @@
   // Place method in imt if entry is empty, place conflict otherwise.
   if (*imt_ref == unimplemented_method) {
     *imt_ref = current_method;
-  } else if (*imt_ref != imt_conflict_method) {
+  } else if (!(*imt_ref)->IsRuntimeMethod()) {
     // If we are not a conflict and we have the same signature and name as the imt
     // entry, it must be that we overwrote a superclass vtable entry.
+    // Note that we have checked IsRuntimeMethod, as there may be multiple different
+    // conflict methods.
     MethodNameAndSignatureComparator imt_comparator(
         (*imt_ref)->GetInterfaceMethodIfProxy(image_pointer_size));
     if (imt_comparator.HasSameNameAndSignature(
@@ -5915,6 +5918,11 @@
     } else {
       *imt_ref = imt_conflict_method;
     }
+  } else {
+    // Place the default conflict method. Note that there may be an existing conflict
+    // method in the IMT, but it could be one tailored to the super class, with a
+    // specific ImtConflictTable.
+    *imt_ref = imt_conflict_method;
   }
 }
 
@@ -7654,12 +7662,12 @@
   return soa.Env()->NewGlobalRef(local_ref.get());
 }
 
-ArtMethod* ClassLinker::CreateRuntimeMethod() {
+ArtMethod* ClassLinker::CreateRuntimeMethod(LinearAlloc* linear_alloc) {
   const size_t method_alignment = ArtMethod::Alignment(image_pointer_size_);
   const size_t method_size = ArtMethod::Size(image_pointer_size_);
   LengthPrefixedArray<ArtMethod>* method_array = AllocArtMethodArray(
       Thread::Current(),
-      Runtime::Current()->GetLinearAlloc(),
+      linear_alloc,
       1);
   ArtMethod* method = &method_array->At(0, method_size, method_alignment);
   CHECK(method != nullptr);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index c368a3a..b4fbe1c 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -565,7 +565,7 @@
       REQUIRES(Locks::classlinker_classes_lock_)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  ArtMethod* CreateRuntimeMethod();
+  ArtMethod* CreateRuntimeMethod(LinearAlloc* linear_alloc);
 
   // Clear the ArrayClass cache. This is necessary when cleaning up for the image, as the cache
   // entries are roots, but potentially not image classes.
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 3e6b453..5344cdd 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -548,7 +548,7 @@
       uint32_t imt_index = resolved_method->GetDexMethodIndex() % mirror::Class::kImtSize;
       ArtMethod* imt_method = (*this_object)->GetClass()->GetEmbeddedImTableEntry(
           imt_index, class_linker->GetImagePointerSize());
-      if (!imt_method->IsImtConflictMethod() && !imt_method->IsImtUnimplementedMethod()) {
+      if (!imt_method->IsRuntimeMethod()) {
         if (kIsDebugBuild) {
           mirror::Class* klass = (*this_object)->GetClass();
           ArtMethod* method = klass->FindVirtualMethodForInterface(
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 7005aa5..35f2102 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -23,6 +23,7 @@
 #include "entrypoints/runtime_asm_entrypoints.h"
 #include "gc/accounting/card_table-inl.h"
 #include "interpreter/interpreter.h"
+#include "linear_alloc.h"
 #include "method_reference.h"
 #include "mirror/class-inl.h"
 #include "mirror/dex_cache-inl.h"
@@ -2118,48 +2119,73 @@
   return artInvokeCommon<kVirtual, true>(method_idx, this_object, self, sp);
 }
 
-// Determine target of interface dispatch. This object is known non-null.
-extern "C" TwoWordReturn artInvokeInterfaceTrampoline(uint32_t dex_method_idx,
+// Determine target of interface dispatch. This object is known non-null. First argument
+// is there for consistency but should not be used, as some architectures overwrite it
+// in the assembly trampoline.
+extern "C" TwoWordReturn artInvokeInterfaceTrampoline(uint32_t deadbeef ATTRIBUTE_UNUSED,
                                                       mirror::Object* this_object,
-                                                      Thread* self, ArtMethod** sp)
+                                                      Thread* self,
+                                                      ArtMethod** sp)
     SHARED_REQUIRES(Locks::mutator_lock_) {
   ScopedQuickEntrypointChecks sqec(self);
+  StackHandleScope<1> hs(self);
+  Handle<mirror::Class> cls(hs.NewHandle(this_object->GetClass()));
+
   // The optimizing compiler currently does not inline methods that have an interface
   // invocation. We use the outer method directly to avoid fetching a stack map, which is
   // more expensive.
   ArtMethod* caller_method = QuickArgumentVisitor::GetOuterMethod(sp);
   DCHECK_EQ(caller_method, QuickArgumentVisitor::GetCallingMethod(sp));
+
+  // Fetch the dex_method_idx of the target interface method from the caller.
+  uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
+
+  const DexFile::CodeItem* code_item = caller_method->GetCodeItem();
+  CHECK_LT(dex_pc, code_item->insns_size_in_code_units_);
+  const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]);
+  Instruction::Code instr_code = instr->Opcode();
+  CHECK(instr_code == Instruction::INVOKE_INTERFACE ||
+        instr_code == Instruction::INVOKE_INTERFACE_RANGE)
+      << "Unexpected call into interface trampoline: " << instr->DumpString(nullptr);
+  uint32_t dex_method_idx;
+  if (instr_code == Instruction::INVOKE_INTERFACE) {
+    dex_method_idx = instr->VRegB_35c();
+  } else {
+    CHECK_EQ(instr_code, Instruction::INVOKE_INTERFACE_RANGE);
+    dex_method_idx = instr->VRegB_3rc();
+  }
+
   ArtMethod* interface_method = caller_method->GetDexCacheResolvedMethod(
       dex_method_idx, sizeof(void*));
   DCHECK(interface_method != nullptr) << dex_method_idx << " " << PrettyMethod(caller_method);
-  ArtMethod* method;
+  ArtMethod* method = nullptr;
+
   if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex)) {
-    method = this_object->GetClass()->FindVirtualMethodForInterface(
-        interface_method, sizeof(void*));
+    // If the dex cache already resolved the interface method, look whether we have
+    // a match in the ImtConflictTable.
+    uint32_t imt_index = interface_method->GetDexMethodIndex();
+    ArtMethod* conflict_method = cls->GetEmbeddedImTableEntry(
+        imt_index % mirror::Class::kImtSize, sizeof(void*));
+    DCHECK(conflict_method->IsRuntimeMethod()) << PrettyMethod(conflict_method);
+    ImtConflictTable* current_table = conflict_method->GetImtConflictTable(sizeof(void*));
+    method = current_table->Lookup(interface_method);
+    if (method != nullptr) {
+      return GetTwoWordSuccessValue(
+          reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCode()),
+          reinterpret_cast<uintptr_t>(method));
+    }
+
+    // No match, use the IfTable.
+    method = cls->FindVirtualMethodForInterface(interface_method, sizeof(void*));
     if (UNLIKELY(method == nullptr)) {
       ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(
           interface_method, this_object, caller_method);
       return GetTwoWordFailureValue();  // Failure.
     }
   } else {
+    // The dex cache did not resolve the method, look it up in the dex file
+    // of the caller,
     DCHECK_EQ(interface_method, Runtime::Current()->GetResolutionMethod());
-    if (kIsDebugBuild) {
-      uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
-      const DexFile::CodeItem* code = caller_method->GetCodeItem();
-      CHECK_LT(dex_pc, code->insns_size_in_code_units_);
-      const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
-      Instruction::Code instr_code = instr->Opcode();
-      CHECK(instr_code == Instruction::INVOKE_INTERFACE ||
-            instr_code == Instruction::INVOKE_INTERFACE_RANGE)
-          << "Unexpected call into interface trampoline: " << instr->DumpString(nullptr);
-      if (instr_code == Instruction::INVOKE_INTERFACE) {
-        CHECK_EQ(dex_method_idx, instr->VRegB_35c());
-      } else {
-        CHECK_EQ(instr_code, Instruction::INVOKE_INTERFACE_RANGE);
-        CHECK_EQ(dex_method_idx, instr->VRegB_3rc());
-      }
-    }
-
     const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()
         ->GetDexFile();
     uint32_t shorty_len;
@@ -2179,7 +2205,50 @@
       CHECK(self->IsExceptionPending());
       return GetTwoWordFailureValue();  // Failure.
     }
+    interface_method = caller_method->GetDexCacheResolvedMethod(dex_method_idx, sizeof(void*));
+    DCHECK(!interface_method->IsRuntimeMethod());
   }
+
+  // We arrive here if we have found an implementation, and it is not in the ImtConflictTable.
+  // We create a new table with the new pair { interface_method, method }.
+  uint32_t imt_index = interface_method->GetDexMethodIndex();
+  ArtMethod* conflict_method = cls->GetEmbeddedImTableEntry(
+      imt_index % mirror::Class::kImtSize, sizeof(void*));
+  ImtConflictTable* current_table = conflict_method->GetImtConflictTable(sizeof(void*));
+  Runtime* runtime = Runtime::Current();
+  LinearAlloc* linear_alloc = (cls->GetClassLoader() == nullptr)
+      ? runtime->GetLinearAlloc()
+      : cls->GetClassLoader()->GetAllocator();
+  bool is_new_entry = (conflict_method == runtime->GetImtConflictMethod());
+
+  // Create a new entry if the existing one is the shared conflict method.
+  ArtMethod* new_conflict_method = is_new_entry
+      ? runtime->CreateImtConflictMethod(linear_alloc)
+      : conflict_method;
+
+  // Allocate a new table. Note that we will leak this table at the next conflict,
+  // but that's a tradeoff compared to making the table fixed size.
+  void* data = linear_alloc->Alloc(
+      self, ImtConflictTable::ComputeSizeWithOneMoreEntry(current_table));
+  CHECK(data != nullptr) << "Out of memory";
+  ImtConflictTable* new_table = new (data) ImtConflictTable(
+      current_table, interface_method, method);
+
+  // Do a fence to ensure threads see the data in the table before it is assigned
+  // to the conlict method.
+  // Note that there is a race in the presence of multiple threads and we may leak
+  // memory from the LinearAlloc, but that's a tradeoff compared to using
+  // atomic operations.
+  QuasiAtomic::ThreadFenceRelease();
+  new_conflict_method->SetImtConflictTable(new_table);
+  if (is_new_entry) {
+    // Update the IMT if we create a new conflict method. No fence needed here, as the
+    // data is consistent.
+    cls->SetEmbeddedImTableEntry(imt_index % mirror::Class::kImtSize,
+                                 new_conflict_method,
+                                 sizeof(void*));
+  }
+
   const void* code = method->GetEntryPointFromQuickCompiledCode();
 
   // When we return, the caller will branch to this address, so it had better not be 0!
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index c893a0f..7bd85ec 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -2419,8 +2419,6 @@
     gc::Heap* heap = Runtime::Current()->GetHeap();
     bool is_copy = array_data != elements;
     size_t bytes = array->GetLength() * component_size;
-    VLOG(heap) << "Release primitive array " << soa.Env() << " array_data " << array_data
-               << " elements " << elements;
     if (is_copy) {
       // Sanity check: If elements is not the same as the java array's data, it better not be a
       // heap address. TODO: This might be slow to check, may be worth keeping track of which
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 1d64d6d..941217f 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1596,8 +1596,10 @@
   }
 }
 
-ArtMethod* Runtime::CreateImtConflictMethod() {
-  auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod();
+static ImtConflictTable::Entry empty_entry = { nullptr, nullptr };
+
+ArtMethod* Runtime::CreateImtConflictMethod(LinearAlloc* linear_alloc) {
+  auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod(linear_alloc);
   // When compiling, the code pointer will get set later when the image is loaded.
   if (IsAotCompiler()) {
     size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
@@ -1605,6 +1607,7 @@
   } else {
     method->SetEntryPointFromQuickCompiledCode(GetQuickImtConflictStub());
   }
+  method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
   return method;
 }
 
@@ -1612,10 +1615,11 @@
   CHECK(method != nullptr);
   CHECK(method->IsRuntimeMethod());
   imt_conflict_method_ = method;
+  method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
 }
 
 ArtMethod* Runtime::CreateResolutionMethod() {
-  auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod();
+  auto* method = GetClassLinker()->CreateRuntimeMethod(GetLinearAlloc());
   // When compiling, the code pointer will get set later when the image is loaded.
   if (IsAotCompiler()) {
     size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
@@ -1627,7 +1631,7 @@
 }
 
 ArtMethod* Runtime::CreateCalleeSaveMethod() {
-  auto* method = Runtime::Current()->GetClassLinker()->CreateRuntimeMethod();
+  auto* method = GetClassLinker()->CreateRuntimeMethod(GetLinearAlloc());
   size_t pointer_size = GetInstructionSetPointerSize(instruction_set_);
   method->SetEntryPointFromQuickCompiledCodePtrSize(nullptr, pointer_size);
   DCHECK_NE(instruction_set_, kNone);
@@ -1929,6 +1933,7 @@
   CHECK(method != nullptr);
   CHECK(method->IsRuntimeMethod());
   imt_unimplemented_method_ = method;
+  method->SetImtConflictTable(reinterpret_cast<ImtConflictTable*>(&empty_entry));
 }
 
 bool Runtime::IsVerificationEnabled() const {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index ac6e689..6a6fdb7 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -386,7 +386,8 @@
   void SetImtConflictMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
   void SetImtUnimplementedMethod(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_);
 
-  ArtMethod* CreateImtConflictMethod() SHARED_REQUIRES(Locks::mutator_lock_);
+  ArtMethod* CreateImtConflictMethod(LinearAlloc* linear_alloc)
+      SHARED_REQUIRES(Locks::mutator_lock_);
 
   // Returns a special method that describes all callee saves being spilled to the stack.
   enum CalleeSaveType {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 4dc6d57..42b5a4a 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -44,6 +44,7 @@
 #include "entrypoints/quick/quick_alloc_entrypoints.h"
 #include "gc_map.h"
 #include "gc/accounting/card_table-inl.h"
+#include "gc/accounting/heap_bitmap-inl.h"
 #include "gc/allocator/rosalloc.h"
 #include "gc/heap.h"
 #include "gc/space/space-inl.h"
diff --git a/test/097-duplicate-method/build b/test/097-duplicate-method/build
deleted file mode 100644
index 4525549..0000000
--- a/test/097-duplicate-method/build
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2012 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.
-
-# Stop if something fails.
-set -e
-
-mkdir classes
-
-if [ ${USE_JACK} = "true" ]; then
-  ${JACK} --output-jack src.jack src
-
-  ${JASMIN} -d classes src/*.j
-  jar cf jasmin.jill.jar -C classes .
-
-  # We set jack.import.type.policy=keep-first to consider class definitions from jasmin first.
-  ${JACK} --import jasmin.jill.jar --import src.jack -D jack.import.type.policy=keep-first --output-dex .
-else
-  ${JAVAC} -d classes src/*.java
-  ${JASMIN} -d classes src/*.j
-
-  ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes
-fi
-zip $TEST_NAME.jar classes.dex
diff --git a/test/097-duplicate-method/classes.dex b/test/097-duplicate-method/classes.dex
new file mode 100644
index 0000000..18f8958
--- /dev/null
+++ b/test/097-duplicate-method/classes.dex
Binary files differ
diff --git a/test/097-duplicate-method/info.txt b/test/097-duplicate-method/info.txt
index 4e7e0ee..ed0daed 100644
--- a/test/097-duplicate-method/info.txt
+++ b/test/097-duplicate-method/info.txt
@@ -1,2 +1,7 @@
 This is a test to verify that duplicate methods in a dex file are handled
 properly (all but the first are ignored).
+
+We need to build a dex file with duplicate methods. We cannot do that
+with Jack (this is invalid) or smali (it does not keep duplicate
+methods, only one is in the dex). Therefore, having a precompiled
+dex file allows to run the test on whatever toolchain.
diff --git a/test/097-duplicate-method/src/Main.java b/test/097-duplicate-method/src/Main.java
deleted file mode 100644
index bb3d36a..0000000
--- a/test/097-duplicate-method/src/Main.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-/**
- * Certain class files generated by smali can have encoded methods with an
- * identical method_idx. In these cases, the behavior should be to only use the
- * first one, and to ignore all following duplicates.
- */
-public class Main {
-    public static void main(String args[]) {
-        if (Test.run() != null) {
-          System.out.println("Success!");
-        }
-    }
-}
diff --git a/test/097-duplicate-method/src/Test.j b/test/097-duplicate-method/src/Test.j
deleted file mode 100644
index f96a9a3..0000000
--- a/test/097-duplicate-method/src/Test.j
+++ /dev/null
@@ -1,29 +0,0 @@
-; Copyright (C) 2012 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.
-
-.class Test
-.super java/lang/Object
-
-.method public static run()Ljava/lang/Object;
-    .limit stack 2
-    new java/lang/Object
-    dup
-    invokespecial java/lang/Object/<init>()V
-    areturn
-.end method
-
-.method public static run()Ljava/lang/Object;
-    aconst_null
-    areturn
-.end method
diff --git a/test/097-duplicate-method/src/Test.java b/test/097-duplicate-method/src/Test.java
deleted file mode 100644
index 7dd61e6..0000000
--- a/test/097-duplicate-method/src/Test.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * This class and method will be overwritten by smali to return non-null.
- */
-public class Test {
-    public static Object run() {
-        return null;
-    }
-}
diff --git a/test/431-optimizing-arith-shifts/src/Main.java b/test/431-optimizing-arith-shifts/src/Main.java
index 86422bd..b7a112f 100644
--- a/test/431-optimizing-arith-shifts/src/Main.java
+++ b/test/431-optimizing-arith-shifts/src/Main.java
@@ -29,304 +29,302 @@
   }
 
   public static void main(String[] args) {
-    shlInt();
-    shlLong();
-    shrInt();
-    shrLong();
-    ushrInt();
-    ushrLong();
+    testShlInt();
+    testShlLong();
+    testShrInt();
+    testShrLong();
+    testUShrInt();
+    testUShrLong();
   }
 
-  private static void shlInt() {
-    expectEquals(48, $opt$ShlConst2(12));
-    expectEquals(12, $opt$ShlConst0(12));
-    expectEquals(-48, $opt$Shl(-12, 2));
-    expectEquals(1024, $opt$Shl(32, 5));
+  private static void testShlInt() {
+    expectEquals(48, $opt$ShlIntConst2(12));
+    expectEquals(12, $opt$ShlIntConst0(12));
+    expectEquals(-48, $opt$ShlInt(-12, 2));
+    expectEquals(1024, $opt$ShlInt(32, 5));
 
-    expectEquals(7, $opt$Shl(7, 0));
-    expectEquals(14, $opt$Shl(7, 1));
-    expectEquals(0, $opt$Shl(0, 30));
+    expectEquals(7, $opt$ShlInt(7, 0));
+    expectEquals(14, $opt$ShlInt(7, 1));
+    expectEquals(0, $opt$ShlInt(0, 30));
 
-    expectEquals(1073741824L, $opt$Shl(1, 30));
-    expectEquals(Integer.MIN_VALUE, $opt$Shl(1, 31));  // overflow
-    expectEquals(Integer.MIN_VALUE, $opt$Shl(1073741824, 1));  // overflow
-    expectEquals(1073741824, $opt$Shl(268435456, 2));
+    expectEquals(1073741824L, $opt$ShlInt(1, 30));
+    expectEquals(Integer.MIN_VALUE, $opt$ShlInt(1, 31));  // overflow
+    expectEquals(Integer.MIN_VALUE, $opt$ShlInt(1073741824, 1));  // overflow
+    expectEquals(1073741824, $opt$ShlInt(268435456, 2));
 
     // Only the 5 lower bits should be used for shifting (& 0x1f).
-    expectEquals(7, $opt$Shl(7, 32));  // 32 & 0x1f = 0
-    expectEquals(14, $opt$Shl(7, 33));  // 33 & 0x1f = 1
-    expectEquals(32, $opt$Shl(1, 101));  // 101 & 0x1f = 5
+    expectEquals(7, $opt$ShlInt(7, 32));  // 32 & 0x1f = 0
+    expectEquals(14, $opt$ShlInt(7, 33));  // 33 & 0x1f = 1
+    expectEquals(32, $opt$ShlInt(1, 101));  // 101 & 0x1f = 5
 
-    expectEquals(Integer.MIN_VALUE, $opt$Shl(1, -1));  // -1 & 0x1f = 31
-    expectEquals(14, $opt$Shl(7, -31));  // -31 & 0x1f = 1
-    expectEquals(7, $opt$Shl(7, -32));  // -32 & 0x1f = 0
-    expectEquals(-536870912, $opt$Shl(7, -3));  // -3 & 0x1f = 29
+    expectEquals(Integer.MIN_VALUE, $opt$ShlInt(1, -1));  // -1 & 0x1f = 31
+    expectEquals(14, $opt$ShlInt(7, -31));  // -31 & 0x1f = 1
+    expectEquals(7, $opt$ShlInt(7, -32));  // -32 & 0x1f = 0
+    expectEquals(-536870912, $opt$ShlInt(7, -3));  // -3 & 0x1f = 29
 
-    expectEquals(Integer.MIN_VALUE, $opt$Shl(7, Integer.MAX_VALUE));
-    expectEquals(7, $opt$Shl(7, Integer.MIN_VALUE));
+    expectEquals(Integer.MIN_VALUE, $opt$ShlInt(7, Integer.MAX_VALUE));
+    expectEquals(7, $opt$ShlInt(7, Integer.MIN_VALUE));
   }
 
-  private static void shlLong() {
-    expectEquals(48L, $opt$ShlConst2(12L));
-    expectEquals(12L, $opt$ShlConst0(12L));
-    expectEquals(-48L, $opt$Shl(-12L, 2L));
-    expectEquals(1024L, $opt$Shl(32L, 5L));
+  private static void testShlLong() {
+    expectEquals(48L, $opt$ShlLongConst2(12L));
+    expectEquals(12L, $opt$ShlLongConst0(12L));
+    expectEquals(-48L, $opt$ShlLong(-12L, 2));
+    expectEquals(1024L, $opt$ShlLong(32L, 5));
 
-    expectEquals(7L, $opt$Shl(7L, 0L));
-    expectEquals(14L, $opt$Shl(7L, 1L));
-    expectEquals(0L, $opt$Shl(0L, 30L));
+    expectEquals(7L, $opt$ShlLong(7L, 0));
+    expectEquals(14L, $opt$ShlLong(7L, 1));
+    expectEquals(0L, $opt$ShlLong(0L, 30));
 
-    expectEquals(1073741824L, $opt$Shl(1L, 30L));
-    expectEquals(2147483648L, $opt$Shl(1L, 31L));
-    expectEquals(2147483648L, $opt$Shl(1073741824L, 1L));
+    expectEquals(1073741824L, $opt$ShlLong(1L, 30));
+    expectEquals(2147483648L, $opt$ShlLong(1L, 31));
+    expectEquals(2147483648L, $opt$ShlLong(1073741824L, 1));
 
     // Long shifts can use up to 6 lower bits.
-    expectEquals(4294967296L, $opt$Shl(1L, 32L));
-    expectEquals(60129542144L, $opt$Shl(7L, 33L));
-    expectEquals(Long.MIN_VALUE, $opt$Shl(1L, 63L));  // overflow
+    expectEquals(4294967296L, $opt$ShlLong(1L, 32));
+    expectEquals(60129542144L, $opt$ShlLong(7L, 33));
+    expectEquals(Long.MIN_VALUE, $opt$ShlLong(1L, 63));  // overflow
 
     // Only the 6 lower bits should be used for shifting (& 0x3f).
-    expectEquals(7L, $opt$Shl(7L, 64L));  // 64 & 0x3f = 0
-    expectEquals(14L, $opt$Shl(7L, 65L));  // 65 & 0x3f = 1
-    expectEquals(137438953472L, $opt$Shl(1L, 101L));  // 101 & 0x3f = 37
+    expectEquals(7L, $opt$ShlLong(7L, 64));  // 64 & 0x3f = 0
+    expectEquals(14L, $opt$ShlLong(7L, 65));  // 65 & 0x3f = 1
+    expectEquals(137438953472L, $opt$ShlLong(1L, 101));  // 101 & 0x3f = 37
 
-    expectEquals(Long.MIN_VALUE, $opt$Shl(1L, -1L));  // -1 & 0x3f = 63
-    expectEquals(14L, $opt$Shl(7L, -63L));  // -63 & 0x3f = 1
-    expectEquals(7L, $opt$Shl(7L, -64L));  // -64 & 0x3f = 0
-    expectEquals(2305843009213693952L, $opt$Shl(1L, -3L));  // -3 & 0x3f = 61
+    expectEquals(Long.MIN_VALUE, $opt$ShlLong(1L, -1));  // -1 & 0x3f = 63
+    expectEquals(14L, $opt$ShlLong(7L, -63));  // -63 & 0x3f = 1
+    expectEquals(7L, $opt$ShlLong(7L, -64));  // -64 & 0x3f = 0
+    expectEquals(2305843009213693952L, $opt$ShlLong(1L, -3));  // -3 & 0x3f = 61
 
-    expectEquals(Long.MIN_VALUE, $opt$Shl(7L, Long.MAX_VALUE));
-    expectEquals(7L, $opt$Shl(7L, Long.MIN_VALUE));
+    expectEquals(Long.MIN_VALUE, $opt$ShlLong(7L, Integer.MAX_VALUE));
+    expectEquals(7L, $opt$ShlLong(7L, Integer.MIN_VALUE));
 
     // Exercise some special cases handled by backends/simplifier.
-    expectEquals(24L, $opt$ShlConst1(12L));
-    expectEquals(0x2345678900000000L, $opt$ShlConst32(0x123456789L));
-    expectEquals(0x2490249000000000L, $opt$ShlConst33(0x12481248L));
-    expectEquals(0x4920492000000000L, $opt$ShlConst34(0x12481248L));
-    expectEquals(0x9240924000000000L, $opt$ShlConst35(0x12481248L));
+    expectEquals(24L, $opt$ShlLongConst1(12L));
+    expectEquals(0x2345678900000000L, $opt$ShlLongConst32(0x123456789L));
+    expectEquals(0x2490249000000000L, $opt$ShlLongConst33(0x12481248L));
+    expectEquals(0x4920492000000000L, $opt$ShlLongConst34(0x12481248L));
+    expectEquals(0x9240924000000000L, $opt$ShlLongConst35(0x12481248L));
   }
 
-  private static void shrInt() {
-    expectEquals(3, $opt$ShrConst2(12));
-    expectEquals(12, $opt$ShrConst0(12));
-    expectEquals(-3, $opt$Shr(-12, 2));
-    expectEquals(1, $opt$Shr(32, 5));
+  private static void testShrInt() {
+    expectEquals(3, $opt$ShrIntConst2(12));
+    expectEquals(12, $opt$ShrIntConst0(12));
+    expectEquals(-3, $opt$ShrInt(-12, 2));
+    expectEquals(1, $opt$ShrInt(32, 5));
 
-    expectEquals(7, $opt$Shr(7, 0));
-    expectEquals(3, $opt$Shr(7, 1));
-    expectEquals(0, $opt$Shr(0, 30));
-    expectEquals(0, $opt$Shr(1, 30));
-    expectEquals(-1, $opt$Shr(-1, 30));
+    expectEquals(7, $opt$ShrInt(7, 0));
+    expectEquals(3, $opt$ShrInt(7, 1));
+    expectEquals(0, $opt$ShrInt(0, 30));
+    expectEquals(0, $opt$ShrInt(1, 30));
+    expectEquals(-1, $opt$ShrInt(-1, 30));
 
-    expectEquals(0, $opt$Shr(Integer.MAX_VALUE, 31));
-    expectEquals(-1, $opt$Shr(Integer.MIN_VALUE, 31));
+    expectEquals(0, $opt$ShrInt(Integer.MAX_VALUE, 31));
+    expectEquals(-1, $opt$ShrInt(Integer.MIN_VALUE, 31));
 
     // Only the 5 lower bits should be used for shifting (& 0x1f).
-    expectEquals(7, $opt$Shr(7, 32));  // 32 & 0x1f = 0
-    expectEquals(3, $opt$Shr(7, 33));  // 33 & 0x1f = 1
+    expectEquals(7, $opt$ShrInt(7, 32));  // 32 & 0x1f = 0
+    expectEquals(3, $opt$ShrInt(7, 33));  // 33 & 0x1f = 1
 
-    expectEquals(0, $opt$Shr(1, -1));  // -1 & 0x1f = 31
-    expectEquals(3, $opt$Shr(7, -31));  // -31 & 0x1f = 1
-    expectEquals(7, $opt$Shr(7, -32));  // -32 & 0x1f = 0
-    expectEquals(-4, $opt$Shr(Integer.MIN_VALUE, -3));  // -3 & 0x1f = 29
+    expectEquals(0, $opt$ShrInt(1, -1));  // -1 & 0x1f = 31
+    expectEquals(3, $opt$ShrInt(7, -31));  // -31 & 0x1f = 1
+    expectEquals(7, $opt$ShrInt(7, -32));  // -32 & 0x1f = 0
+    expectEquals(-4, $opt$ShrInt(Integer.MIN_VALUE, -3));  // -3 & 0x1f = 29
 
-    expectEquals(0, $opt$Shr(7, Integer.MAX_VALUE));
-    expectEquals(7, $opt$Shr(7, Integer.MIN_VALUE));
+    expectEquals(0, $opt$ShrInt(7, Integer.MAX_VALUE));
+    expectEquals(7, $opt$ShrInt(7, Integer.MIN_VALUE));
   }
 
-  private static void shrLong() {
-    expectEquals(3L, $opt$ShrConst2(12L));
-    expectEquals(12L, $opt$ShrConst0(12L));
-    expectEquals(-3L, $opt$Shr(-12L, 2L));
-    expectEquals(1, $opt$Shr(32, 5));
+  private static void testShrLong() {
+    expectEquals(3L, $opt$ShrLongConst2(12L));
+    expectEquals(12L, $opt$ShrLongConst0(12L));
+    expectEquals(-3L, $opt$ShrLong(-12L, 2));
+    expectEquals(1, $opt$ShrLong(32, 5));
 
-    expectEquals(7L, $opt$Shr(7L, 0L));
-    expectEquals(3L, $opt$Shr(7L, 1L));
-    expectEquals(0L, $opt$Shr(0L, 30L));
-    expectEquals(0L, $opt$Shr(1L, 30L));
-    expectEquals(-1L, $opt$Shr(-1L, 30L));
+    expectEquals(7L, $opt$ShrLong(7L, 0));
+    expectEquals(3L, $opt$ShrLong(7L, 1));
+    expectEquals(0L, $opt$ShrLong(0L, 30));
+    expectEquals(0L, $opt$ShrLong(1L, 30));
+    expectEquals(-1L, $opt$ShrLong(-1L, 30));
 
-
-    expectEquals(1L, $opt$Shr(1073741824L, 30L));
-    expectEquals(1L, $opt$Shr(2147483648L, 31L));
-    expectEquals(1073741824L, $opt$Shr(2147483648L, 1L));
+    expectEquals(1L, $opt$ShrLong(1073741824L, 30));
+    expectEquals(1L, $opt$ShrLong(2147483648L, 31));
+    expectEquals(1073741824L, $opt$ShrLong(2147483648L, 1));
 
     // Long shifts can use up to 6 lower bits.
-    expectEquals(1L, $opt$Shr(4294967296L, 32L));
-    expectEquals(7L, $opt$Shr(60129542144L, 33L));
-    expectEquals(0L, $opt$Shr(Long.MAX_VALUE, 63L));
-    expectEquals(-1L, $opt$Shr(Long.MIN_VALUE, 63L));
+    expectEquals(1L, $opt$ShrLong(4294967296L, 32));
+    expectEquals(7L, $opt$ShrLong(60129542144L, 33));
+    expectEquals(0L, $opt$ShrLong(Long.MAX_VALUE, 63));
+    expectEquals(-1L, $opt$ShrLong(Long.MIN_VALUE, 63));
 
     // Only the 6 lower bits should be used for shifting (& 0x3f).
-    expectEquals(7L, $opt$Shr(7L, 64L));  // 64 & 0x3f = 0
-    expectEquals(3L, $opt$Shr(7L, 65L));  // 65 & 0x3f = 1
+    expectEquals(7L, $opt$ShrLong(7L, 64));  // 64 & 0x3f = 0
+    expectEquals(3L, $opt$ShrLong(7L, 65));  // 65 & 0x3f = 1
 
-    expectEquals(-1L, $opt$Shr(Long.MIN_VALUE, -1L));  // -1 & 0x3f = 63
-    expectEquals(3L, $opt$Shr(7L, -63L));  // -63 & 0x3f = 1
-    expectEquals(7L, $opt$Shr(7L, -64L));  // -64 & 0x3f = 0
-    expectEquals(1L, $opt$Shr(2305843009213693952L, -3L));  // -3 & 0x3f = 61
-    expectEquals(-4L, $opt$Shr(Integer.MIN_VALUE, -3));  // -3 & 0x1f = 29
+    expectEquals(-1L, $opt$ShrLong(Long.MIN_VALUE, -1));  // -1 & 0x3f = 63
+    expectEquals(3L, $opt$ShrLong(7L, -63));  // -63 & 0x3f = 1
+    expectEquals(7L, $opt$ShrLong(7L, -64));  // -64 & 0x3f = 0
+    expectEquals(1L, $opt$ShrLong(2305843009213693952L, -3));  // -3 & 0x3f = 61
+    expectEquals(-1L, $opt$ShrLong(Integer.MIN_VALUE, -3));  // -3 & 0x1f = 29
 
-    expectEquals(0L, $opt$Shr(7L, Long.MAX_VALUE));
-    expectEquals(7L, $opt$Shr(7L, Long.MIN_VALUE));
+    expectEquals(0L, $opt$ShrLong(7L, Integer.MAX_VALUE));
+    expectEquals(7L, $opt$ShrLong(7L, Integer.MIN_VALUE));
   }
 
-  private static void ushrInt() {
-    expectEquals(3, $opt$UShrConst2(12));
-    expectEquals(12, $opt$UShrConst0(12));
-    expectEquals(1073741821, $opt$UShr(-12, 2));
-    expectEquals(1, $opt$UShr(32, 5));
+  private static void testUShrInt() {
+    expectEquals(3, $opt$UShrIntConst2(12));
+    expectEquals(12, $opt$UShrIntConst0(12));
+    expectEquals(1073741821, $opt$UShrInt(-12, 2));
+    expectEquals(1, $opt$UShrInt(32, 5));
 
-    expectEquals(7, $opt$UShr(7, 0));
-    expectEquals(3, $opt$UShr(7, 1));
-    expectEquals(0, $opt$UShr(0, 30));
-    expectEquals(0, $opt$UShr(1, 30));
-    expectEquals(3, $opt$UShr(-1, 30));
+    expectEquals(7, $opt$UShrInt(7, 0));
+    expectEquals(3, $opt$UShrInt(7, 1));
+    expectEquals(0, $opt$UShrInt(0, 30));
+    expectEquals(0, $opt$UShrInt(1, 30));
+    expectEquals(3, $opt$UShrInt(-1, 30));
 
-    expectEquals(0, $opt$UShr(Integer.MAX_VALUE, 31));
-    expectEquals(1, $opt$UShr(Integer.MIN_VALUE, 31));
+    expectEquals(0, $opt$UShrInt(Integer.MAX_VALUE, 31));
+    expectEquals(1, $opt$UShrInt(Integer.MIN_VALUE, 31));
 
     // Only the 5 lower bits should be used for shifting (& 0x1f).
-    expectEquals(7, $opt$UShr(7, 32));  // 32 & 0x1f = 0
-    expectEquals(3, $opt$UShr(7, 33));  // 33 & 0x1f = 1
+    expectEquals(7, $opt$UShrInt(7, 32));  // 32 & 0x1f = 0
+    expectEquals(3, $opt$UShrInt(7, 33));  // 33 & 0x1f = 1
 
-    expectEquals(0, $opt$UShr(1, -1));  // -1 & 0x1f = 31
-    expectEquals(3, $opt$UShr(7, -31));  // -31 & 0x1f = 1
-    expectEquals(7, $opt$UShr(7, -32));  // -32 & 0x1f = 0
-    expectEquals(4, $opt$UShr(Integer.MIN_VALUE, -3));  // -3 & 0x1f = 29
+    expectEquals(0, $opt$UShrInt(1, -1));  // -1 & 0x1f = 31
+    expectEquals(3, $opt$UShrInt(7, -31));  // -31 & 0x1f = 1
+    expectEquals(7, $opt$UShrInt(7, -32));  // -32 & 0x1f = 0
+    expectEquals(4, $opt$UShrInt(Integer.MIN_VALUE, -3));  // -3 & 0x1f = 29
 
-    expectEquals(0, $opt$UShr(7, Integer.MAX_VALUE));
-    expectEquals(7, $opt$UShr(7, Integer.MIN_VALUE));
+    expectEquals(0, $opt$UShrInt(7, Integer.MAX_VALUE));
+    expectEquals(7, $opt$UShrInt(7, Integer.MIN_VALUE));
   }
 
-  private static void ushrLong() {
-    expectEquals(3L, $opt$UShrConst2(12L));
-    expectEquals(12L, $opt$UShrConst0(12L));
-    expectEquals(4611686018427387901L, $opt$UShr(-12L, 2L));
-    expectEquals(1, $opt$UShr(32, 5));
+  private static void testUShrLong() {
+    expectEquals(3L, $opt$UShrLongConst2(12L));
+    expectEquals(12L, $opt$UShrLongConst0(12L));
+    expectEquals(4611686018427387901L, $opt$UShrLong(-12L, 2));
+    expectEquals(1, $opt$UShrLong(32, 5));
 
-    expectEquals(7L, $opt$UShr(7L, 0L));
-    expectEquals(3L, $opt$UShr(7L, 1L));
-    expectEquals(0L, $opt$UShr(0L, 30L));
-    expectEquals(0L, $opt$UShr(1L, 30L));
-    expectEquals(17179869183L, $opt$UShr(-1L, 30L));
+    expectEquals(7L, $opt$UShrLong(7L, 0));
+    expectEquals(3L, $opt$UShrLong(7L, 1));
+    expectEquals(0L, $opt$UShrLong(0L, 30));
+    expectEquals(0L, $opt$UShrLong(1L, 30));
+    expectEquals(17179869183L, $opt$UShrLong(-1L, 30));
 
-
-    expectEquals(1L, $opt$UShr(1073741824L, 30L));
-    expectEquals(1L, $opt$UShr(2147483648L, 31L));
-    expectEquals(1073741824L, $opt$UShr(2147483648L, 1L));
+    expectEquals(1L, $opt$UShrLong(1073741824L, 30));
+    expectEquals(1L, $opt$UShrLong(2147483648L, 31));
+    expectEquals(1073741824L, $opt$UShrLong(2147483648L, 1));
 
     // Long shifts can use use up to 6 lower bits.
-    expectEquals(1L, $opt$UShr(4294967296L, 32L));
-    expectEquals(7L, $opt$UShr(60129542144L, 33L));
-    expectEquals(0L, $opt$UShr(Long.MAX_VALUE, 63L));
-    expectEquals(1L, $opt$UShr(Long.MIN_VALUE, 63L));
+    expectEquals(1L, $opt$UShrLong(4294967296L, 32));
+    expectEquals(7L, $opt$UShrLong(60129542144L, 33));
+    expectEquals(0L, $opt$UShrLong(Long.MAX_VALUE, 63));
+    expectEquals(1L, $opt$UShrLong(Long.MIN_VALUE, 63));
 
     // Only the 6 lower bits should be used for shifting (& 0x3f).
-    expectEquals(7L, $opt$UShr(7L, 64L));  // 64 & 0x3f = 0
-    expectEquals(3L, $opt$UShr(7L, 65L));  // 65 & 0x3f = 1
+    expectEquals(7L, $opt$UShrLong(7L, 64));  // 64 & 0x3f = 0
+    expectEquals(3L, $opt$UShrLong(7L, 65));  // 65 & 0x3f = 1
 
-    expectEquals(1L, $opt$UShr(Long.MIN_VALUE, -1L));  // -1 & 0x3f = 63
-    expectEquals(3L, $opt$UShr(7L, -63L));  // -63 & 0x3f = 1
-    expectEquals(7L, $opt$UShr(7L, -64L));  // -64 & 0x3f = 0
-    expectEquals(1L, $opt$UShr(2305843009213693952L, -3L));  // -3 & 0x3f = 61
-    expectEquals(4L, $opt$UShr(Long.MIN_VALUE, -3L));  // -3 & 0x3f = 61
+    expectEquals(1L, $opt$UShrLong(Long.MIN_VALUE, -1));  // -1 & 0x3f = 63
+    expectEquals(3L, $opt$UShrLong(7L, -63));  // -63 & 0x3f = 1
+    expectEquals(7L, $opt$UShrLong(7L, -64));  // -64 & 0x3f = 0
+    expectEquals(1L, $opt$UShrLong(2305843009213693952L, -3));  // -3 & 0x3f = 61
+    expectEquals(4L, $opt$UShrLong(Long.MIN_VALUE, -3));  // -3 & 0x3f = 61
 
-    expectEquals(0L, $opt$UShr(7L, Long.MAX_VALUE));
-    expectEquals(7L, $opt$UShr(7L, Long.MIN_VALUE));
+    expectEquals(0L, $opt$UShrLong(7L, Integer.MAX_VALUE));
+    expectEquals(7L, $opt$UShrLong(7L, Integer.MIN_VALUE));
   }
 
-  static int $opt$Shl(int a, int b) {
-    return a << b;
+
+  static int $opt$ShlInt(int value, int distance) {
+    return value << distance;
   }
 
-  static long $opt$Shl(long a, long b) {
-    return a << b;
+  static long $opt$ShlLong(long value, int distance) {
+    return value << distance;
   }
 
-  static int $opt$Shr(int a, int b) {
-    return a >> b;
+  static int $opt$ShrInt(int value, int distance) {
+    return value >> distance;
   }
 
-  static long $opt$Shr(long a, long b) {
-    return a >> b;
+  static long $opt$ShrLong(long value, int distance) {
+    return value >> distance;
   }
 
-  static int $opt$UShr(int a, int b) {
-    return a >>> b;
+  static int $opt$UShrInt(int value, int distance) {
+    return value >>> distance;
   }
 
-  static long $opt$UShr(long a, long b) {
-    return a >>> b;
+  static long $opt$UShrLong(long value, int distance) {
+    return value >>> distance;
   }
 
-  static int $opt$ShlConst2(int a) {
-    return a << 2;
+  static int $opt$ShlIntConst2(int value) {
+    return value << 2;
   }
 
-  static long $opt$ShlConst2(long a) {
-    return a << 2L;
+  static long $opt$ShlLongConst2(long value) {
+    return value << 2;
   }
 
-  static int $opt$ShrConst2(int a) {
-    return a >> 2;
+  static int $opt$ShrIntConst2(int value) {
+    return value >> 2;
   }
 
-  static long $opt$ShrConst2(long a) {
-    return a >> 2L;
+  static long $opt$ShrLongConst2(long value) {
+    return value >> 2;
   }
 
-  static int $opt$UShrConst2(int a) {
-    return a >>> 2;
+  static int $opt$UShrIntConst2(int value) {
+    return value >>> 2;
   }
 
-  static long $opt$UShrConst2(long a) {
-    return a >>> 2L;
+  static long $opt$UShrLongConst2(long value) {
+    return value >>> 2;
   }
 
-  static int $opt$ShlConst0(int a) {
-    return a << 0;
+  static int $opt$ShlIntConst0(int value) {
+    return value << 0;
   }
 
-  static long $opt$ShlConst0(long a) {
-    return a << 0L;
+  static long $opt$ShlLongConst0(long value) {
+    return value << 0;
   }
 
-  static int $opt$ShrConst0(int a) {
-    return a >> 0;
+  static int $opt$ShrIntConst0(int value) {
+    return value >> 0;
   }
 
-  static long $opt$ShrConst0(long a) {
-    return a >> 0L;
+  static long $opt$ShrLongConst0(long value) {
+    return value >> 0;
   }
 
-  static int $opt$UShrConst0(int a) {
-    return a >>> 0;
+  static int $opt$UShrIntConst0(int value) {
+    return value >>> 0;
   }
 
-  static long $opt$UShrConst0(long a) {
-    return a >>> 0L;
+  static long $opt$UShrLongConst0(long value) {
+    return value >>> 0;
   }
 
-  static long $opt$ShlConst1(long a) {
-    return a << 1L;
+  static long $opt$ShlLongConst1(long value) {
+    return value << 1;
   }
 
-  static long $opt$ShlConst32(long a) {
-    return a << 32L;
+  static long $opt$ShlLongConst32(long value) {
+    return value << 32;
   }
 
-  static long $opt$ShlConst33(long a) {
-    return a << 33L;
+  static long $opt$ShlLongConst33(long value) {
+    return value << 33;
   }
 
-  static long $opt$ShlConst34(long a) {
-    return a << 34L;
+  static long $opt$ShlLongConst34(long value) {
+    return value << 34;
   }
 
-  static long $opt$ShlConst35(long a) {
-    return a << 35L;
+  static long $opt$ShlLongConst35(long value) {
+    return value << 35;
   }
 
 }
-
diff --git a/test/469-condition-materialization-regression/expected.txt b/test/469-condition-materialization/expected.txt
similarity index 100%
rename from test/469-condition-materialization-regression/expected.txt
rename to test/469-condition-materialization/expected.txt
diff --git a/test/469-condition-materialization-regression/info.txt b/test/469-condition-materialization/info.txt
similarity index 100%
rename from test/469-condition-materialization-regression/info.txt
rename to test/469-condition-materialization/info.txt
diff --git a/test/469-condition-materialization-regression/src/Main.java b/test/469-condition-materialization/src/Main.java
similarity index 100%
rename from test/469-condition-materialization-regression/src/Main.java
rename to test/469-condition-materialization/src/Main.java
diff --git a/test/476-clinit-check-inlining-static-invoke/expected.txt b/test/476-clinit-inline-static-invoke/expected.txt
similarity index 100%
rename from test/476-clinit-check-inlining-static-invoke/expected.txt
rename to test/476-clinit-inline-static-invoke/expected.txt
diff --git a/test/476-clinit-check-inlining-static-invoke/info.txt b/test/476-clinit-inline-static-invoke/info.txt
similarity index 100%
rename from test/476-clinit-check-inlining-static-invoke/info.txt
rename to test/476-clinit-inline-static-invoke/info.txt
diff --git a/test/476-clinit-check-inlining-static-invoke/src/Main.java b/test/476-clinit-inline-static-invoke/src/Main.java
similarity index 100%
rename from test/476-clinit-check-inlining-static-invoke/src/Main.java
rename to test/476-clinit-inline-static-invoke/src/Main.java
diff --git a/test/565-checker-rotate/src/Main.java b/test/565-checker-rotate/src/Main.java
index d7f57d8..aadb597 100644
--- a/test/565-checker-rotate/src/Main.java
+++ b/test/565-checker-rotate/src/Main.java
@@ -320,6 +320,48 @@
   }
 
 
+  /// CHECK-START: int Main.rotateLeftIntWithByteDistance(int, byte) intrinsics_recognition (after)
+  /// CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
+  /// CHECK:         <<ArgVal:i\d+>>  ParameterValue
+  /// CHECK:         <<ArgDist:b\d+>> ParameterValue
+  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateLeft
+  /// CHECK-DAG:                      Return [<<Result>>]
+
+  /// CHECK-START: int Main.rotateLeftIntWithByteDistance(int, byte) instruction_simplifier (after)
+  /// CHECK:         <<ArgVal:i\d+>>  ParameterValue
+  /// CHECK:         <<ArgDist:b\d+>> ParameterValue
+  /// CHECK-DAG:     <<NegDist:i\d+>> Neg [<<ArgDist>>]
+  /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<ArgVal>>,<<NegDist>>]
+  /// CHECK-DAG:                      Return [<<Result>>]
+
+  /// CHECK-START: int Main.rotateLeftIntWithByteDistance(int, byte) instruction_simplifier (after)
+  /// CHECK-NOT:                      InvokeStaticOrDirect
+
+  private static int rotateLeftIntWithByteDistance(int value, byte distance) {
+    return Integer.rotateLeft(value, distance);
+  }
+
+  /// CHECK-START: int Main.rotateRightIntWithByteDistance(int, byte) intrinsics_recognition (after)
+  /// CHECK-DAG:     <<Method:[ij]\d+>> CurrentMethod
+  /// CHECK:         <<ArgVal:i\d+>>  ParameterValue
+  /// CHECK:         <<ArgDist:b\d+>> ParameterValue
+  /// CHECK-DAG:     <<Result:i\d+>>  InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateRight
+  /// CHECK-DAG:                      Return [<<Result>>]
+
+  /// CHECK-START: int Main.rotateRightIntWithByteDistance(int, byte) instruction_simplifier (after)
+  /// CHECK:         <<ArgVal:i\d+>>  ParameterValue
+  /// CHECK:         <<ArgDist:b\d+>> ParameterValue
+  /// CHECK-DAG:     <<Result:i\d+>>  Ror [<<ArgVal>>,<<ArgDist>>]
+  /// CHECK-DAG:                      Return [<<Result>>]
+
+  /// CHECK-START: int Main.rotateRightIntWithByteDistance(int, byte) instruction_simplifier (after)
+  /// CHECK-NOT:                      InvokeStaticOrDirect
+
+  private static int rotateRightIntWithByteDistance(int value, byte distance) {
+    return Integer.rotateRight(value, distance);
+  }
+
+
   public static void testRotateLeftBoolean() {
     for (int i = 0; i < 40; i++) {  // overshoot a bit
       int j = i & 31;
@@ -518,6 +560,45 @@
   }
 
 
+  public static void testRotateLeftIntWithByteDistance() {
+    expectEqualsInt(0x00000001, rotateLeftIntWithByteDistance(0x00000001, (byte)0));
+    expectEqualsInt(0x00000002, rotateLeftIntWithByteDistance(0x00000001, (byte)1));
+    expectEqualsInt(0x80000000, rotateLeftIntWithByteDistance(0x00000001, (byte)31));
+    expectEqualsInt(0x00000001, rotateLeftIntWithByteDistance(0x00000001, (byte)32));  // overshoot
+    expectEqualsInt(0x00000003, rotateLeftIntWithByteDistance(0x80000001, (byte)1));
+    expectEqualsInt(0x00000006, rotateLeftIntWithByteDistance(0x80000001, (byte)2));
+    expectEqualsInt(0x23456781, rotateLeftIntWithByteDistance(0x12345678, (byte)4));
+    expectEqualsInt(0xBCDEF09A, rotateLeftIntWithByteDistance(0x9ABCDEF0, (byte)8));
+    for (byte i = 0; i < 40; i++) {  // overshoot a bit
+      byte j = (byte)(i & 31);
+      expectEqualsInt(0x00000000, rotateLeftIntWithByteDistance(0x00000000, i));
+      expectEqualsInt(0xFFFFFFFF, rotateLeftIntWithByteDistance(0xFFFFFFFF, i));
+      expectEqualsInt(1 << j, rotateLeftIntWithByteDistance(0x00000001, i));
+      expectEqualsInt((0x12345678 << j) | (0x12345678 >>> -j),
+                      rotateLeftIntWithByteDistance(0x12345678, i));
+    }
+  }
+
+  public static void testRotateRightIntWithByteDistance() {
+    expectEqualsInt(0x80000000, rotateRightIntWithByteDistance(0x80000000, (byte)0));
+    expectEqualsInt(0x40000000, rotateRightIntWithByteDistance(0x80000000, (byte)1));
+    expectEqualsInt(0x00000001, rotateRightIntWithByteDistance(0x80000000, (byte)31));
+    expectEqualsInt(0x80000000, rotateRightIntWithByteDistance(0x80000000, (byte)32));  // overshoot
+    expectEqualsInt(0xC0000000, rotateRightIntWithByteDistance(0x80000001, (byte)1));
+    expectEqualsInt(0x60000000, rotateRightIntWithByteDistance(0x80000001, (byte)2));
+    expectEqualsInt(0x81234567, rotateRightIntWithByteDistance(0x12345678, (byte)4));
+    expectEqualsInt(0xF09ABCDE, rotateRightIntWithByteDistance(0x9ABCDEF0, (byte)8));
+    for (byte i = 0; i < 40; i++) {  // overshoot a bit
+      byte j = (byte)(i & 31);
+      expectEqualsInt(0x00000000, rotateRightIntWithByteDistance(0x00000000, i));
+      expectEqualsInt(0xFFFFFFFF, rotateRightIntWithByteDistance(0xFFFFFFFF, i));
+      expectEqualsInt(0x80000000 >>> j, rotateRightIntWithByteDistance(0x80000000, i));
+      expectEqualsInt((0x12345678 >>> j) | (0x12345678 << -j),
+                      rotateRightIntWithByteDistance(0x12345678, i));
+    }
+  }
+
+
   public static void main(String args[]) {
     testRotateLeftBoolean();
     testRotateLeftByte();
@@ -533,6 +614,10 @@
     testRotateRightInt();
     testRotateRightLong();
 
+    // Also exercise distance values with types other than int.
+    testRotateLeftIntWithByteDistance();
+    testRotateRightIntWithByteDistance();
+
     System.out.println("passed");
   }
 
diff --git a/test/588-checker-irreducible-lifetime-hole/expected.txt b/test/588-checker-irreducible-lifetime-hole/expected.txt
new file mode 100644
index 0000000..d81cc07
--- /dev/null
+++ b/test/588-checker-irreducible-lifetime-hole/expected.txt
@@ -0,0 +1 @@
+42
diff --git a/test/588-checker-irreducible-lifetime-hole/info.txt b/test/588-checker-irreducible-lifetime-hole/info.txt
new file mode 100644
index 0000000..a2861a9
--- /dev/null
+++ b/test/588-checker-irreducible-lifetime-hole/info.txt
@@ -0,0 +1,3 @@
+Regression test for optimizing that used to have a too
+strong DCHECK in the presence of a combination of irreducible loops
+and try/catch.
diff --git a/test/588-checker-irreducible-lifetime-hole/smali/IrreducibleLoop.smali b/test/588-checker-irreducible-lifetime-hole/smali/IrreducibleLoop.smali
new file mode 100644
index 0000000..207c77e
--- /dev/null
+++ b/test/588-checker-irreducible-lifetime-hole/smali/IrreducibleLoop.smali
@@ -0,0 +1,71 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LIrreducibleLoop;
+
+.super Ljava/lang/Object;
+
+## CHECK-START-X86: int IrreducibleLoop.simpleLoop(int) dead_code_elimination (before)
+## CHECK-DAG: <<Method:(i|j)\d+>> CurrentMethod
+## CHECK-DAG: <<Constant:i\d+>>   IntConstant 42
+## CHECK-DAG:                     Goto irreducible:true
+## CHECK-DAG:                     InvokeStaticOrDirect [<<Constant>>,<<Method>>] loop:none
+## CHECK-DAG:                     InvokeStaticOrDirect [{{i\d+}},<<Method>>] loop:none
+.method public static simpleLoop(I)I
+   .registers 3
+   const/16 v0, 42
+   invoke-static {v0}, LIrreducibleLoop;->$noinline$m(I)V
+   if-eqz p0, :b22
+   goto :b34
+
+   :b34
+   goto :b20
+
+   :b20
+   if-nez p0, :b45
+   goto :b46
+
+   :b46
+   goto :b21
+
+   :b21
+   goto :b34
+
+   :b22
+   :try_start
+   div-int v0, v0, v0
+   :try_end
+   .catchall {:try_start .. :try_end} :b34
+   goto :b20
+
+   :b45
+   invoke-static {v0}, LIrreducibleLoop;->$noinline$m(I)V
+   goto :b26
+
+   :b26
+   return v0
+.end method
+
+.method public static $noinline$m(I)V
+   .registers 3
+   const/16 v0, 0
+   sget-boolean v1,LIrreducibleLoop;->doThrow:Z
+   if-eqz v1, :exit
+   # Prevent inlining.
+   throw v0
+   :exit
+   return-void
+.end method
+
+.field public static doThrow:Z
diff --git a/test/588-checker-irreducible-lifetime-hole/src/Main.java b/test/588-checker-irreducible-lifetime-hole/src/Main.java
new file mode 100644
index 0000000..94e3357
--- /dev/null
+++ b/test/588-checker-irreducible-lifetime-hole/src/Main.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+  // Workaround for b/18051191.
+  class InnerClass {}
+
+  public static void main(String[] args) throws Exception {
+    Class<?> c = Class.forName("IrreducibleLoop");
+    Method m = c.getMethod("simpleLoop", int.class);
+    Object[] arguments = { 42 };
+    System.out.println(m.invoke(null, arguments));
+  }
+}
diff --git a/test/469-condition-materialization-regression/expected.txt b/test/589-super-imt/expected.txt
similarity index 100%
copy from test/469-condition-materialization-regression/expected.txt
copy to test/589-super-imt/expected.txt
diff --git a/test/589-super-imt/info.txt b/test/589-super-imt/info.txt
new file mode 100644
index 0000000..c815dc9
--- /dev/null
+++ b/test/589-super-imt/info.txt
@@ -0,0 +1,2 @@
+Test what the IMT is properly set for a subclass, and that the
+subclass won't use the ImtConflictTable table of its super class.
diff --git a/test/589-super-imt/src/Main.java b/test/589-super-imt/src/Main.java
new file mode 100644
index 0000000..e381ca7
--- /dev/null
+++ b/test/589-super-imt/src/Main.java
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+interface Itf {
+  public Class<?> method1();
+  public Class<?> method2();
+  public Class<?> method3();
+  public Class<?> method4();
+  public Class<?> method5();
+  public Class<?> method6();
+  public Class<?> method7();
+  public Class<?> method8();
+  public Class<?> method9();
+  public Class<?> method10();
+  public Class<?> method11();
+  public Class<?> method12();
+  public Class<?> method13();
+  public Class<?> method14();
+  public Class<?> method15();
+  public Class<?> method16();
+  public Class<?> method17();
+  public Class<?> method18();
+  public Class<?> method19();
+  public Class<?> method20();
+  public Class<?> method21();
+  public Class<?> method22();
+  public Class<?> method23();
+  public Class<?> method24();
+  public Class<?> method25();
+  public Class<?> method26();
+  public Class<?> method27();
+  public Class<?> method28();
+  public Class<?> method29();
+  public Class<?> method30();
+  public Class<?> method31();
+  public Class<?> method32();
+  public Class<?> method33();
+  public Class<?> method34();
+  public Class<?> method35();
+  public Class<?> method36();
+  public Class<?> method37();
+  public Class<?> method38();
+  public Class<?> method39();
+  public Class<?> method40();
+  public Class<?> method41();
+  public Class<?> method42();
+  public Class<?> method43();
+  public Class<?> method44();
+  public Class<?> method45();
+  public Class<?> method46();
+  public Class<?> method47();
+  public Class<?> method48();
+  public Class<?> method49();
+  public Class<?> method50();
+  public Class<?> method51();
+  public Class<?> method52();
+  public Class<?> method53();
+  public Class<?> method54();
+  public Class<?> method55();
+  public Class<?> method56();
+  public Class<?> method57();
+  public Class<?> method58();
+  public Class<?> method59();
+  public Class<?> method60();
+  public Class<?> method61();
+  public Class<?> method62();
+  public Class<?> method63();
+  public Class<?> method64();
+  public Class<?> method65();
+  public Class<?> method66();
+  public Class<?> method67();
+  public Class<?> method68();
+  public Class<?> method69();
+  public Class<?> method70();
+  public Class<?> method71();
+  public Class<?> method72();
+  public Class<?> method73();
+  public Class<?> method74();
+  public Class<?> method75();
+  public Class<?> method76();
+  public Class<?> method77();
+  public Class<?> method78();
+  public Class<?> method79();
+}
+
+public class Main implements Itf {
+  public static Itf main;
+  public static void main(String[] args) {
+    main = new Main();
+    callMains();
+    main = new SubMain();
+    callSubMains();
+  }
+
+  public static void callMains() {
+    // We loop to artificially create branches. The compiler will
+    // not compile this method otherwise.
+    for (int i = 0; i < 2; ++i) {
+      expectEquals(main.method1(), Main.class);
+      expectEquals(main.method2(), Main.class);
+      expectEquals(main.method3(), Main.class);
+      expectEquals(main.method4(), Main.class);
+      expectEquals(main.method5(), Main.class);
+      expectEquals(main.method6(), Main.class);
+      expectEquals(main.method7(), Main.class);
+      expectEquals(main.method8(), Main.class);
+      expectEquals(main.method9(), Main.class);
+      expectEquals(main.method10(), Main.class);
+      expectEquals(main.method11(), Main.class);
+      expectEquals(main.method12(), Main.class);
+      expectEquals(main.method13(), Main.class);
+      expectEquals(main.method14(), Main.class);
+      expectEquals(main.method15(), Main.class);
+      expectEquals(main.method16(), Main.class);
+      expectEquals(main.method17(), Main.class);
+      expectEquals(main.method18(), Main.class);
+      expectEquals(main.method19(), Main.class);
+      expectEquals(main.method20(), Main.class);
+      expectEquals(main.method21(), Main.class);
+      expectEquals(main.method22(), Main.class);
+      expectEquals(main.method23(), Main.class);
+      expectEquals(main.method24(), Main.class);
+      expectEquals(main.method25(), Main.class);
+      expectEquals(main.method26(), Main.class);
+      expectEquals(main.method27(), Main.class);
+      expectEquals(main.method28(), Main.class);
+      expectEquals(main.method29(), Main.class);
+      expectEquals(main.method30(), Main.class);
+      expectEquals(main.method31(), Main.class);
+      expectEquals(main.method32(), Main.class);
+      expectEquals(main.method33(), Main.class);
+      expectEquals(main.method34(), Main.class);
+      expectEquals(main.method35(), Main.class);
+      expectEquals(main.method36(), Main.class);
+      expectEquals(main.method37(), Main.class);
+      expectEquals(main.method38(), Main.class);
+      expectEquals(main.method39(), Main.class);
+      expectEquals(main.method40(), Main.class);
+      expectEquals(main.method41(), Main.class);
+      expectEquals(main.method42(), Main.class);
+      expectEquals(main.method43(), Main.class);
+      expectEquals(main.method44(), Main.class);
+      expectEquals(main.method45(), Main.class);
+      expectEquals(main.method46(), Main.class);
+      expectEquals(main.method47(), Main.class);
+      expectEquals(main.method48(), Main.class);
+      expectEquals(main.method49(), Main.class);
+      expectEquals(main.method50(), Main.class);
+      expectEquals(main.method51(), Main.class);
+      expectEquals(main.method52(), Main.class);
+      expectEquals(main.method53(), Main.class);
+      expectEquals(main.method54(), Main.class);
+      expectEquals(main.method55(), Main.class);
+      expectEquals(main.method56(), Main.class);
+      expectEquals(main.method57(), Main.class);
+      expectEquals(main.method58(), Main.class);
+      expectEquals(main.method59(), Main.class);
+      expectEquals(main.method60(), Main.class);
+      expectEquals(main.method61(), Main.class);
+      expectEquals(main.method62(), Main.class);
+      expectEquals(main.method63(), Main.class);
+      expectEquals(main.method64(), Main.class);
+      expectEquals(main.method65(), Main.class);
+      expectEquals(main.method66(), Main.class);
+      expectEquals(main.method67(), Main.class);
+      expectEquals(main.method68(), Main.class);
+      expectEquals(main.method69(), Main.class);
+      expectEquals(main.method70(), Main.class);
+      expectEquals(main.method71(), Main.class);
+      expectEquals(main.method72(), Main.class);
+      expectEquals(main.method73(), Main.class);
+      expectEquals(main.method74(), Main.class);
+      expectEquals(main.method75(), Main.class);
+      expectEquals(main.method76(), Main.class);
+      expectEquals(main.method77(), Main.class);
+      expectEquals(main.method78(), Main.class);
+      expectEquals(main.method79(), Main.class);
+    }
+  }
+
+  public static void callSubMains() {
+    // We loop to artificially create branches. The compiler will
+    // not compile this method otherwise.
+    for (int i = 0; i < 2; ++i) {
+      expectEquals(main.method1(), SubMain.class);
+      expectEquals(main.method2(), SubMain.class);
+      expectEquals(main.method3(), SubMain.class);
+      expectEquals(main.method4(), SubMain.class);
+      expectEquals(main.method5(), SubMain.class);
+      expectEquals(main.method6(), SubMain.class);
+      expectEquals(main.method7(), SubMain.class);
+      expectEquals(main.method8(), SubMain.class);
+      expectEquals(main.method9(), SubMain.class);
+      expectEquals(main.method10(), SubMain.class);
+      expectEquals(main.method11(), SubMain.class);
+      expectEquals(main.method12(), SubMain.class);
+      expectEquals(main.method13(), SubMain.class);
+      expectEquals(main.method14(), SubMain.class);
+      expectEquals(main.method15(), SubMain.class);
+      expectEquals(main.method16(), SubMain.class);
+      expectEquals(main.method17(), SubMain.class);
+      expectEquals(main.method18(), SubMain.class);
+      expectEquals(main.method19(), SubMain.class);
+      expectEquals(main.method20(), SubMain.class);
+      expectEquals(main.method21(), SubMain.class);
+      expectEquals(main.method22(), SubMain.class);
+      expectEquals(main.method23(), SubMain.class);
+      expectEquals(main.method24(), SubMain.class);
+      expectEquals(main.method25(), SubMain.class);
+      expectEquals(main.method26(), SubMain.class);
+      expectEquals(main.method27(), SubMain.class);
+      expectEquals(main.method28(), SubMain.class);
+      expectEquals(main.method29(), SubMain.class);
+      expectEquals(main.method30(), SubMain.class);
+      expectEquals(main.method31(), SubMain.class);
+      expectEquals(main.method32(), SubMain.class);
+      expectEquals(main.method33(), SubMain.class);
+      expectEquals(main.method34(), SubMain.class);
+      expectEquals(main.method35(), SubMain.class);
+      expectEquals(main.method36(), SubMain.class);
+      expectEquals(main.method37(), SubMain.class);
+      expectEquals(main.method38(), SubMain.class);
+      expectEquals(main.method39(), SubMain.class);
+      expectEquals(main.method40(), SubMain.class);
+      expectEquals(main.method41(), SubMain.class);
+      expectEquals(main.method42(), SubMain.class);
+      expectEquals(main.method43(), SubMain.class);
+      expectEquals(main.method44(), SubMain.class);
+      expectEquals(main.method45(), SubMain.class);
+      expectEquals(main.method46(), SubMain.class);
+      expectEquals(main.method47(), SubMain.class);
+      expectEquals(main.method48(), SubMain.class);
+      expectEquals(main.method49(), SubMain.class);
+      expectEquals(main.method50(), SubMain.class);
+      expectEquals(main.method51(), SubMain.class);
+      expectEquals(main.method52(), SubMain.class);
+      expectEquals(main.method53(), SubMain.class);
+      expectEquals(main.method54(), SubMain.class);
+      expectEquals(main.method55(), SubMain.class);
+      expectEquals(main.method56(), SubMain.class);
+      expectEquals(main.method57(), SubMain.class);
+      expectEquals(main.method58(), SubMain.class);
+      expectEquals(main.method59(), SubMain.class);
+      expectEquals(main.method60(), SubMain.class);
+      expectEquals(main.method61(), SubMain.class);
+      expectEquals(main.method62(), SubMain.class);
+      expectEquals(main.method63(), SubMain.class);
+      expectEquals(main.method64(), SubMain.class);
+      expectEquals(main.method65(), SubMain.class);
+      expectEquals(main.method66(), SubMain.class);
+      expectEquals(main.method67(), SubMain.class);
+      expectEquals(main.method68(), SubMain.class);
+      expectEquals(main.method69(), SubMain.class);
+      expectEquals(main.method70(), SubMain.class);
+      expectEquals(main.method71(), SubMain.class);
+      expectEquals(main.method72(), SubMain.class);
+      expectEquals(main.method73(), SubMain.class);
+      expectEquals(main.method74(), SubMain.class);
+      expectEquals(main.method75(), SubMain.class);
+      expectEquals(main.method76(), SubMain.class);
+      expectEquals(main.method77(), SubMain.class);
+      expectEquals(main.method78(), SubMain.class);
+      expectEquals(main.method79(), SubMain.class);
+    }
+  }
+
+  public static void expectEquals(Object actual, Object expected) {
+    if (!actual.equals(expected)) {
+      throw new Error("Expected " + expected + ", got " + actual);
+    }
+  }
+
+  public Class<?> method1() { return Main.class; }
+  public Class<?> method2() { return Main.class; }
+  public Class<?> method3() { return Main.class; }
+  public Class<?> method4() { return Main.class; }
+  public Class<?> method5() { return Main.class; }
+  public Class<?> method6() { return Main.class; }
+  public Class<?> method7() { return Main.class; }
+  public Class<?> method8() { return Main.class; }
+  public Class<?> method9() { return Main.class; }
+  public Class<?> method10() { return Main.class; }
+  public Class<?> method11() { return Main.class; }
+  public Class<?> method12() { return Main.class; }
+  public Class<?> method13() { return Main.class; }
+  public Class<?> method14() { return Main.class; }
+  public Class<?> method15() { return Main.class; }
+  public Class<?> method16() { return Main.class; }
+  public Class<?> method17() { return Main.class; }
+  public Class<?> method18() { return Main.class; }
+  public Class<?> method19() { return Main.class; }
+  public Class<?> method20() { return Main.class; }
+  public Class<?> method21() { return Main.class; }
+  public Class<?> method22() { return Main.class; }
+  public Class<?> method23() { return Main.class; }
+  public Class<?> method24() { return Main.class; }
+  public Class<?> method25() { return Main.class; }
+  public Class<?> method26() { return Main.class; }
+  public Class<?> method27() { return Main.class; }
+  public Class<?> method28() { return Main.class; }
+  public Class<?> method29() { return Main.class; }
+  public Class<?> method30() { return Main.class; }
+  public Class<?> method31() { return Main.class; }
+  public Class<?> method32() { return Main.class; }
+  public Class<?> method33() { return Main.class; }
+  public Class<?> method34() { return Main.class; }
+  public Class<?> method35() { return Main.class; }
+  public Class<?> method36() { return Main.class; }
+  public Class<?> method37() { return Main.class; }
+  public Class<?> method38() { return Main.class; }
+  public Class<?> method39() { return Main.class; }
+  public Class<?> method40() { return Main.class; }
+  public Class<?> method41() { return Main.class; }
+  public Class<?> method42() { return Main.class; }
+  public Class<?> method43() { return Main.class; }
+  public Class<?> method44() { return Main.class; }
+  public Class<?> method45() { return Main.class; }
+  public Class<?> method46() { return Main.class; }
+  public Class<?> method47() { return Main.class; }
+  public Class<?> method48() { return Main.class; }
+  public Class<?> method49() { return Main.class; }
+  public Class<?> method50() { return Main.class; }
+  public Class<?> method51() { return Main.class; }
+  public Class<?> method52() { return Main.class; }
+  public Class<?> method53() { return Main.class; }
+  public Class<?> method54() { return Main.class; }
+  public Class<?> method55() { return Main.class; }
+  public Class<?> method56() { return Main.class; }
+  public Class<?> method57() { return Main.class; }
+  public Class<?> method58() { return Main.class; }
+  public Class<?> method59() { return Main.class; }
+  public Class<?> method60() { return Main.class; }
+  public Class<?> method61() { return Main.class; }
+  public Class<?> method62() { return Main.class; }
+  public Class<?> method63() { return Main.class; }
+  public Class<?> method64() { return Main.class; }
+  public Class<?> method65() { return Main.class; }
+  public Class<?> method66() { return Main.class; }
+  public Class<?> method67() { return Main.class; }
+  public Class<?> method68() { return Main.class; }
+  public Class<?> method69() { return Main.class; }
+  public Class<?> method70() { return Main.class; }
+  public Class<?> method71() { return Main.class; }
+  public Class<?> method72() { return Main.class; }
+  public Class<?> method73() { return Main.class; }
+  public Class<?> method74() { return Main.class; }
+  public Class<?> method75() { return Main.class; }
+  public Class<?> method76() { return Main.class; }
+  public Class<?> method77() { return Main.class; }
+  public Class<?> method78() { return Main.class; }
+  public Class<?> method79() { return Main.class; }
+}
+
+class SubMain extends Main {
+  public Class<?> method1() { return SubMain.class; }
+  public Class<?> method2() { return SubMain.class; }
+  public Class<?> method3() { return SubMain.class; }
+  public Class<?> method4() { return SubMain.class; }
+  public Class<?> method5() { return SubMain.class; }
+  public Class<?> method6() { return SubMain.class; }
+  public Class<?> method7() { return SubMain.class; }
+  public Class<?> method8() { return SubMain.class; }
+  public Class<?> method9() { return SubMain.class; }
+  public Class<?> method10() { return SubMain.class; }
+  public Class<?> method11() { return SubMain.class; }
+  public Class<?> method12() { return SubMain.class; }
+  public Class<?> method13() { return SubMain.class; }
+  public Class<?> method14() { return SubMain.class; }
+  public Class<?> method15() { return SubMain.class; }
+  public Class<?> method16() { return SubMain.class; }
+  public Class<?> method17() { return SubMain.class; }
+  public Class<?> method18() { return SubMain.class; }
+  public Class<?> method19() { return SubMain.class; }
+  public Class<?> method20() { return SubMain.class; }
+  public Class<?> method21() { return SubMain.class; }
+  public Class<?> method22() { return SubMain.class; }
+  public Class<?> method23() { return SubMain.class; }
+  public Class<?> method24() { return SubMain.class; }
+  public Class<?> method25() { return SubMain.class; }
+  public Class<?> method26() { return SubMain.class; }
+  public Class<?> method27() { return SubMain.class; }
+  public Class<?> method28() { return SubMain.class; }
+  public Class<?> method29() { return SubMain.class; }
+  public Class<?> method30() { return SubMain.class; }
+  public Class<?> method31() { return SubMain.class; }
+  public Class<?> method32() { return SubMain.class; }
+  public Class<?> method33() { return SubMain.class; }
+  public Class<?> method34() { return SubMain.class; }
+  public Class<?> method35() { return SubMain.class; }
+  public Class<?> method36() { return SubMain.class; }
+  public Class<?> method37() { return SubMain.class; }
+  public Class<?> method38() { return SubMain.class; }
+  public Class<?> method39() { return SubMain.class; }
+  public Class<?> method40() { return SubMain.class; }
+  public Class<?> method41() { return SubMain.class; }
+  public Class<?> method42() { return SubMain.class; }
+  public Class<?> method43() { return SubMain.class; }
+  public Class<?> method44() { return SubMain.class; }
+  public Class<?> method45() { return SubMain.class; }
+  public Class<?> method46() { return SubMain.class; }
+  public Class<?> method47() { return SubMain.class; }
+  public Class<?> method48() { return SubMain.class; }
+  public Class<?> method49() { return SubMain.class; }
+  public Class<?> method50() { return SubMain.class; }
+  public Class<?> method51() { return SubMain.class; }
+  public Class<?> method52() { return SubMain.class; }
+  public Class<?> method53() { return SubMain.class; }
+  public Class<?> method54() { return SubMain.class; }
+  public Class<?> method55() { return SubMain.class; }
+  public Class<?> method56() { return SubMain.class; }
+  public Class<?> method57() { return SubMain.class; }
+  public Class<?> method58() { return SubMain.class; }
+  public Class<?> method59() { return SubMain.class; }
+  public Class<?> method60() { return SubMain.class; }
+  public Class<?> method61() { return SubMain.class; }
+  public Class<?> method62() { return SubMain.class; }
+  public Class<?> method63() { return SubMain.class; }
+  public Class<?> method64() { return SubMain.class; }
+  public Class<?> method65() { return SubMain.class; }
+  public Class<?> method66() { return SubMain.class; }
+  public Class<?> method67() { return SubMain.class; }
+  public Class<?> method68() { return SubMain.class; }
+  public Class<?> method69() { return SubMain.class; }
+  public Class<?> method70() { return SubMain.class; }
+  public Class<?> method71() { return SubMain.class; }
+  public Class<?> method72() { return SubMain.class; }
+  public Class<?> method73() { return SubMain.class; }
+  public Class<?> method74() { return SubMain.class; }
+  public Class<?> method75() { return SubMain.class; }
+  public Class<?> method76() { return SubMain.class; }
+  public Class<?> method77() { return SubMain.class; }
+  public Class<?> method78() { return SubMain.class; }
+  public Class<?> method79() { return SubMain.class; }
+}
diff --git a/test/590-checker-array-set-null-regression/expected.txt b/test/590-checker-array-set-null-regression/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/590-checker-array-set-null-regression/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/590-checker-array-set-null-regression/info.txt b/test/590-checker-array-set-null-regression/info.txt
new file mode 100644
index 0000000..fe173a3
--- /dev/null
+++ b/test/590-checker-array-set-null-regression/info.txt
@@ -0,0 +1,11 @@
+Regression test for art::PrepareForRegisterAllocation, which replaces
+
+  ArraySet[array, index, BoundType[NullConstant]]
+
+with
+
+  ArraySet[array, index, NullConstant]
+
+but used to forget to remove the "need for a type check" bit in the
+ArraySet, thus failing "!may_need_runtime_call_for_type_check"
+assertions in code generators.
diff --git a/test/590-checker-array-set-null-regression/src/Main.java b/test/590-checker-array-set-null-regression/src/Main.java
new file mode 100644
index 0000000..792ee4e
--- /dev/null
+++ b/test/590-checker-array-set-null-regression/src/Main.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+  public static void main(String args[]) {
+    Element[] elements = new Element[51];
+    testArraySetCheckCastNull(elements);
+
+    System.out.println("passed");
+  }
+
+  /// CHECK-START: void Main.testArraySetCheckCastNull(Main$Element[]) builder (after)
+  /// CHECK:         <<Array:l\d+>>         ParameterValue
+  /// CHECK-DAG:     <<Index:i\d+>>         IntConstant 42
+  /// CHECK-DAG:     <<Null:l\d+>>          NullConstant
+  /// CHECK-DAG:     <<Class:l\d+>>         LoadClass
+  /// CHECK-DAG:                            CheckCast [<<Null>>,<<Class>>]
+  /// CHECK-DAG:     <<CheckedValue:l\d+>>  BoundType [<<Null>>] klass:Main$Element can_be_null:true
+  /// CHECK-DAG:     <<CheckedArray:l\d+>>  NullCheck [<<Array>>]
+  /// CHECK-DAG:     <<Length:i\d+>>        ArrayLength [<<CheckedArray>>]
+  /// CHECK-DAG:     <<CheckedIndex:i\d+>>  BoundsCheck [<<Index>>,<<Length>>]
+  /// CHECK-DAG:     <<ArraySet:v\d+>>      ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<CheckedValue>>] needs_type_check:true
+
+  /// CHECK-START: void Main.testArraySetCheckCastNull(Main$Element[]) instruction_simplifier (after)
+  /// CHECK-NOT:                            CheckCast
+
+  /// CHECK-START: void Main.testArraySetCheckCastNull(Main$Element[]) prepare_for_register_allocation (before)
+  /// CHECK:         <<Array:l\d+>>         ParameterValue
+  /// CHECK-DAG:     <<Index:i\d+>>         IntConstant 42
+  /// CHECK-DAG:     <<Null:l\d+>>          NullConstant
+  /// CHECK-DAG:     <<Class:l\d+>>         LoadClass
+  /// CHECK-DAG:     <<CheckedValue:l\d+>>  BoundType [<<Null>>]
+  /// CHECK-DAG:     <<CheckedArray:l\d+>>  NullCheck [<<Array>>]
+  /// CHECK-DAG:     <<Length:i\d+>>        ArrayLength [<<CheckedArray>>]
+  /// CHECK-DAG:     <<CheckedIndex:i\d+>>  BoundsCheck [<<Index>>,<<Length>>]
+  /// CHECK-DAG:     <<ArraySet:v\d+>>      ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<CheckedValue>>] needs_type_check:true
+
+  /// CHECK-START: void Main.testArraySetCheckCastNull(Main$Element[]) prepare_for_register_allocation (after)
+  /// CHECK:         <<Array:l\d+>>         ParameterValue
+  /// CHECK-DAG:     <<Index:i\d+>>         IntConstant 42
+  /// CHECK-DAG:     <<Null:l\d+>>          NullConstant
+  /// CHECK-DAG:     <<Class:l\d+>>         LoadClass
+  /// CHECK-DAG:     <<Length:i\d+>>        ArrayLength [<<Array>>]
+  /// CHECK-DAG:     <<ArraySet:v\d+>>      ArraySet [<<Array>>,<<Index>>,<<Null>>] needs_type_check:false
+
+  static void testArraySetCheckCastNull(Element[] elements) {
+    Object object = null;
+    Element element = (Element) object;
+    elements[42] = element;
+  }
+
+  class Element {}
+
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 1b00270..11a38cb 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -218,11 +218,9 @@
         $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(ART_TEST_RUN_TEST_SKIP), $(ALL_ADDRESS_SIZES))
 
 
-# Disable 097-duplicate-method while investigation (broken by latest Jack release, b/27358065)
 # Disable 137-cfi (b/27391690).
 # Disable 577-profile-foreign-dex (b/27454772).
 TEST_ART_BROKEN_ALL_TARGET_TESTS := \
-  097-duplicate-method \
   577-profile-foreign-dex \
 
 ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -295,9 +293,12 @@
 TEST_ART_BROKEN_PREBUILD_RUN_TESTS :=
 
 # 554-jit-profile-file is disabled because it needs a primary oat file to know what it should save.
+# 529 and 555: b/27784033
 TEST_ART_BROKEN_NO_PREBUILD_TESTS := \
   117-nopatchoat \
-  554-jit-profile-file
+  554-jit-profile-file \
+  529-checker-unresolved \
+  555-checker-regression-x86const
 
 ifneq (,$(filter no-prebuild,$(PREBUILD_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),no-prebuild, \