Merge "Adding optimizing compiler test case for String.<init>." into mnc-dev
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
index 58236e2..ff4659a 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -33,6 +33,7 @@
 #include "dex/pass_driver_me_post_opt.h"
 #include "dex/pass_manager.h"
 #include "dex/quick/mir_to_lir.h"
+#include "dex/verified_method.h"
 #include "driver/compiler_driver.h"
 #include "driver/compiler_options.h"
 #include "elf_writer_quick.h"
@@ -624,6 +625,10 @@
     return nullptr;
   }
 
+  if (driver->GetVerifiedMethod(&dex_file, method_idx)->HasRuntimeThrow()) {
+    return nullptr;
+  }
+
   DCHECK(driver->GetCompilerOptions().IsCompilationEnabled());
 
   Runtime* const runtime = Runtime::Current();
diff --git a/compiler/dex/verified_method.cc b/compiler/dex/verified_method.cc
index 9b141cc..2bc8042 100644
--- a/compiler/dex/verified_method.cc
+++ b/compiler/dex/verified_method.cc
@@ -41,6 +41,7 @@
                                              bool compile) {
   std::unique_ptr<VerifiedMethod> verified_method(new VerifiedMethod);
   verified_method->has_verification_failures_ = method_verifier->HasFailures();
+  verified_method->has_runtime_throw_ = method_verifier->HasInstructionThatWillThrow();
   if (compile) {
     /* Generate a register map. */
     if (!verified_method->GenerateGcMap(method_verifier)) {
diff --git a/compiler/dex/verified_method.h b/compiler/dex/verified_method.h
index 242e3df..bad4495 100644
--- a/compiler/dex/verified_method.h
+++ b/compiler/dex/verified_method.h
@@ -75,6 +75,10 @@
     return has_verification_failures_;
   }
 
+  bool HasRuntimeThrow() const {
+    return has_runtime_throw_;
+  }
+
   void SetStringInitPcRegMap(SafeMap<uint32_t, std::set<uint32_t>>& string_init_pc_reg_map) {
     string_init_pc_reg_map_ = string_init_pc_reg_map;
   }
@@ -121,6 +125,7 @@
   SafeCastSet safe_cast_set_;
 
   bool has_verification_failures_;
+  bool has_runtime_throw_ = false;
 
   // Copy of mapping generated by verifier of dex PCs of string init invocations
   // to the set of other registers that the receiver has been copied into.
diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc
index 8100a29..daf7d67 100644
--- a/compiler/optimizing/boolean_simplifier.cc
+++ b/compiler/optimizing/boolean_simplifier.cc
@@ -141,6 +141,12 @@
   block->MergeWith(false_block);
   block->MergeWith(merge_block);
 
+  // No need to update any dominance information, as we are simplifying
+  // a simple diamond shape, where the join block is merged with the
+  // entry block. Any following blocks would have had the join block
+  // as a dominator, and `MergeWith` handles changing that to the
+  // entry block.
+
   // Remove the original condition if it is now unused.
   if (!if_condition->HasUses()) {
     if_condition->GetBlock()->RemoveInstructionOrPhi(if_condition);
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 900dabe..ebc0adc 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -1180,9 +1180,7 @@
       }
     }
     ValueRange* narrowed_range = existing_range->Narrow(range);
-    if (narrowed_range != nullptr) {
-      GetValueRangeMap(successor)->Overwrite(instruction->GetId(), narrowed_range);
-    }
+    GetValueRangeMap(successor)->Overwrite(instruction->GetId(), narrowed_range);
   }
 
   // Special case that we may simultaneously narrow two MonotonicValueRange's to
@@ -1388,7 +1386,7 @@
     if (array_length->IsPhi()) {
       // Input 1 of the phi contains the real array.length once the loop body is
       // entered. That value will be used for bound analysis. The graph is still
-      // strickly in SSA form.
+      // strictly in SSA form.
       array_length = array_length->AsPhi()->InputAt(1)->AsArrayLength();
     }
 
@@ -1730,6 +1728,10 @@
         ValueBound upper = ValueBound(new_array, -right_const);
         ValueRange* range = new (GetGraph()->GetArena())
             ValueRange(GetGraph()->GetArena(), lower, upper);
+        ValueRange* existing_range = LookupValueRange(left, new_array->GetBlock());
+        if (existing_range != nullptr) {
+          range = existing_range->Narrow(range);
+        }
         GetValueRangeMap(new_array->GetBlock())->Overwrite(left->GetId(), range);
       }
     }
@@ -1780,7 +1782,13 @@
          it != first_constant_index_bounds_check_map_.end();
          ++it) {
       HBoundsCheck* bounds_check = it->second;
-      HArrayLength* array_length = bounds_check->InputAt(1)->AsArrayLength();
+      HInstruction* array_length = bounds_check->InputAt(1);
+      if (!array_length->IsArrayLength()) {
+        // Prior deoptimizations may have changed the array length to a phi.
+        // TODO(mingyao): propagate the range to the phi?
+        DCHECK(array_length->IsPhi()) << array_length->DebugName();
+        continue;
+      }
       HIntConstant* lower_bound_const_instr = nullptr;
       int32_t lower_bound_const = INT_MIN;
       size_t counter = 0;
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index ed39923..b564aca 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -2062,8 +2062,13 @@
     case Instruction::MOVE_RESULT:
     case Instruction::MOVE_RESULT_WIDE:
     case Instruction::MOVE_RESULT_OBJECT:
-      UpdateLocal(instruction.VRegA(), latest_result_);
-      latest_result_ = nullptr;
+      if (latest_result_ == nullptr) {
+        // Only dead code can lead to this situation, where the verifier
+        // does not reject the method.
+      } else {
+        UpdateLocal(instruction.VRegA(), latest_result_);
+        latest_result_ = nullptr;
+      }
       break;
 
     case Instruction::CMP_LONG: {
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index bfed1a8..4db3b43 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -180,7 +180,10 @@
                              std::function<void(HGraph*)> hook_before_codegen,
                              bool has_result,
                              Expected expected) {
-  graph->BuildDominatorTree();
+  // Tests may have already computed it.
+  if (graph->GetReversePostOrder().IsEmpty()) {
+    graph->BuildDominatorTree();
+  }
   SsaLivenessAnalysis liveness(graph, codegen);
   liveness.Analyze();
 
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index 6fbe75e..2362cc1 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -67,6 +67,7 @@
   ArenaBitVector affected_loops(allocator, graph_->GetBlocks().Size(), false);
 
   MarkReachableBlocks(graph_->GetEntryBlock(), &live_blocks);
+  bool removed_one_or_more_blocks = false;
 
   // Remove all dead blocks. Iterate in post order because removal needs the
   // block's chain of dominators and nested loops need to be updated from the
@@ -83,9 +84,17 @@
       MaybeRecordDeadBlock(block);
       MarkLoopHeadersContaining(*block, &affected_loops);
       block->DisconnectAndDelete();
+      removed_one_or_more_blocks = true;
     }
   }
 
+  // If we removed at least one block, we need to recompute the full
+  // dominator tree.
+  if (removed_one_or_more_blocks) {
+    graph_->ClearDominanceInformation();
+    graph_->ComputeDominanceInformation();
+  }
+
   // Connect successive blocks created by dead branches. Order does not matter.
   for (HReversePostOrderIterator it(*graph_); !it.Done();) {
     HBasicBlock* block  = it.Current();
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index c01364a..88490d0 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -116,9 +116,24 @@
   //     dominators and the reverse post order.
   SimplifyCFG();
 
-  // (5) Compute the immediate dominator of each block. We visit
-  //     the successors of a block only when all its forward branches
-  //     have been processed.
+  // (5) Compute the dominance information and the reverse post order.
+  ComputeDominanceInformation();
+}
+
+void HGraph::ClearDominanceInformation() {
+  for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
+    it.Current()->ClearDominanceInformation();
+  }
+  reverse_post_order_.Reset();
+}
+
+void HBasicBlock::ClearDominanceInformation() {
+  dominated_blocks_.Reset();
+  dominator_ = nullptr;
+}
+
+void HGraph::ComputeDominanceInformation() {
+  DCHECK(reverse_post_order_.IsEmpty());
   GrowableArray<size_t> visits(arena_, blocks_.Size());
   visits.SetSize(blocks_.Size());
   reverse_post_order_.Add(entry_block_);
@@ -1037,8 +1052,7 @@
   }
   predecessors_.Reset();
 
-  // Disconnect the block from its successors and update their dominators
-  // and phis.
+  // Disconnect the block from its successors and update their phis.
   for (size_t i = 0, e = successors_.Size(); i < e; ++i) {
     HBasicBlock* successor = successors_.Get(i);
     // Delete this block from the list of predecessors.
@@ -1049,19 +1063,6 @@
     // dominator of `successor` which violates the order DCHECKed at the top.
     DCHECK(!successor->predecessors_.IsEmpty());
 
-    // Recompute the successor's dominator.
-    HBasicBlock* old_dominator = successor->GetDominator();
-    HBasicBlock* new_dominator = successor->predecessors_.Get(0);
-    for (size_t j = 1, f = successor->predecessors_.Size(); j < f; ++j) {
-      new_dominator = graph_->FindCommonDominator(
-          new_dominator, successor->predecessors_.Get(j));
-    }
-    if (old_dominator != new_dominator) {
-      successor->SetDominator(new_dominator);
-      old_dominator->RemoveDominatedBlock(successor);
-      new_dominator->AddDominatedBlock(successor);
-    }
-
     // Remove this block's entries in the successor's phis.
     if (successor->predecessors_.Size() == 1u) {
       // The successor has just one predecessor left. Replace phis with the only
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 394e3fc..b36d9b8 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -170,6 +170,9 @@
     return true;
   }
 
+  void ComputeDominanceInformation();
+  void ClearDominanceInformation();
+
   void BuildDominatorTree();
   void TransformToSsa();
   void SimplifyCFG();
@@ -547,11 +550,10 @@
     LOG(FATAL) << "Unreachable";
     UNREACHABLE();
   }
+  void ClearDominanceInformation();
 
   int NumberOfBackEdges() const {
-    return loop_information_ == nullptr
-        ? 0
-        : loop_information_->NumberOfBackEdges();
+    return IsLoopHeader() ? loop_information_->NumberOfBackEdges() : 0;
   }
 
   HInstruction* GetFirstInstruction() const { return instructions_.first_instruction_; }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 810b4f8..8958932 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -618,7 +618,8 @@
                                             const DexFile& dex_file) const {
   CompilerDriver* compiler_driver = GetCompilerDriver();
   CompiledMethod* method = nullptr;
-  if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file)) {
+  if (compiler_driver->IsMethodVerifiedWithoutFailures(method_idx, class_def_idx, dex_file) &&
+      !compiler_driver->GetVerifiedMethod(&dex_file, method_idx)->HasRuntimeThrow()) {
      method = TryCompile(code_item, access_flags, invoke_type, class_def_idx,
                          method_idx, jclass_loader, dex_file);
   } else {
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 6c55356..bcad9b6 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2401,9 +2401,9 @@
         StackHandleScope<1> hs(self_);
         mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
         if (return_type_class != nullptr) {
-          return_type = &reg_types_.FromClass(called_method->GetReturnTypeDescriptor(),
-                                              return_type_class,
-                                              return_type_class->CannotBeAssignedFromOtherTypes());
+          return_type = &FromClass(called_method->GetReturnTypeDescriptor(),
+                                   return_type_class,
+                                   return_type_class->CannotBeAssignedFromOtherTypes());
         } else {
           DCHECK(!can_load_classes_ || self_->IsExceptionPending());
           self_->ClearException();
@@ -2446,9 +2446,9 @@
         StackHandleScope<1> hs(self_);
         mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
         if (return_type_class != nullptr) {
-          return_type = &reg_types_.FromClass(return_type_descriptor,
-                                              return_type_class,
-                                              return_type_class->CannotBeAssignedFromOtherTypes());
+          return_type = &FromClass(return_type_descriptor,
+                                   return_type_class,
+                                   return_type_class->CannotBeAssignedFromOtherTypes());
         } else {
           DCHECK(!can_load_classes_ || self_->IsExceptionPending());
           self_->ClearException();
@@ -3164,7 +3164,7 @@
   const RegType& referrer = GetDeclaringClass();
   mirror::Class* klass = dex_cache_->GetResolvedType(class_idx);
   const RegType& result = klass != nullptr ?
-      reg_types_.FromClass(descriptor, klass, klass->CannotBeAssignedFromOtherTypes()) :
+      FromClass(descriptor, klass, klass->CannotBeAssignedFromOtherTypes()) :
       reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
   if (result.IsConflict()) {
     Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "accessing broken descriptor '" << descriptor
@@ -3380,8 +3380,8 @@
       if (res_method != nullptr) {
         mirror::Class* klass = res_method->GetDeclaringClass();
         std::string temp;
-        res_method_class = &reg_types_.FromClass(klass->GetDescriptor(&temp), klass,
-                                                 klass->CannotBeAssignedFromOtherTypes());
+        res_method_class = &FromClass(klass->GetDescriptor(&temp), klass,
+                                      klass->CannotBeAssignedFromOtherTypes());
       } else {
         const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
         const uint16_t class_idx = dex_file_->GetMethodId(method_idx).class_idx_;
@@ -3622,8 +3622,7 @@
     mirror::Class* klass = res_method->GetDeclaringClass();
     std::string temp;
     const RegType& res_method_class =
-        reg_types_.FromClass(klass->GetDescriptor(&temp), klass,
-                             klass->CannotBeAssignedFromOtherTypes());
+        FromClass(klass->GetDescriptor(&temp), klass, klass->CannotBeAssignedFromOtherTypes());
     if (!res_method_class.IsAssignableFrom(actual_arg_type)) {
       Fail(actual_arg_type.IsUnresolvedTypes() ? VERIFY_ERROR_NO_CLASS :
           VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type
@@ -3729,6 +3728,7 @@
   } else {
     const RegType& array_type = work_line_->GetRegisterType(this, inst->VRegB_23x());
     if (array_type.IsZero()) {
+      have_pending_runtime_throw_failure_ = true;
       // Null array class; this code path will fail at runtime. Infer a merge-able type from the
       // instruction type. TODO: have a proper notion of bottom here.
       if (!is_primitive || insn_type.IsCategory1Types()) {
@@ -3933,8 +3933,8 @@
   } else {
     mirror::Class* klass = field->GetDeclaringClass();
     const RegType& field_klass =
-        reg_types_.FromClass(dex_file_->GetFieldDeclaringClassDescriptor(field_id),
-                             klass, klass->CannotBeAssignedFromOtherTypes());
+        FromClass(dex_file_->GetFieldDeclaringClassDescriptor(field_id),
+                  klass, klass->CannotBeAssignedFromOtherTypes());
     if (obj_type.IsUninitializedTypes() &&
         (!IsConstructor() || GetDeclaringClass().Equals(obj_type) ||
             !field_klass.Equals(GetDeclaringClass()))) {
@@ -3984,8 +3984,8 @@
     mirror::Class* field_type_class =
         can_load_classes_ ? field->GetType<true>() : field->GetType<false>();
     if (field_type_class != nullptr) {
-      field_type = &reg_types_.FromClass(field->GetTypeDescriptor(), field_type_class,
-                                         field_type_class->CannotBeAssignedFromOtherTypes());
+      field_type = &FromClass(field->GetTypeDescriptor(), field_type_class,
+                              field_type_class->CannotBeAssignedFromOtherTypes());
     } else {
       DCHECK(!can_load_classes_ || self_->IsExceptionPending());
       self_->ClearException();
@@ -4096,8 +4096,8 @@
         field->GetType<false>();
 
     if (field_type_class != nullptr) {
-      field_type = &reg_types_.FromClass(field->GetTypeDescriptor(), field_type_class,
-                                         field_type_class->CannotBeAssignedFromOtherTypes());
+      field_type = &FromClass(field->GetTypeDescriptor(), field_type_class,
+                              field_type_class->CannotBeAssignedFromOtherTypes());
     } else {
       Thread* self = Thread::Current();
       DCHECK(!can_load_classes_ || self->IsExceptionPending());
@@ -4289,9 +4289,9 @@
     if (mirror_method_ != nullptr) {
       mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_);
       if (return_type_class != nullptr) {
-        return_type_ = &reg_types_.FromClass(mirror_method_->GetReturnTypeDescriptor(),
-                                             return_type_class,
-                                             return_type_class->CannotBeAssignedFromOtherTypes());
+        return_type_ = &FromClass(mirror_method_->GetReturnTypeDescriptor(),
+                                  return_type_class,
+                                  return_type_class->CannotBeAssignedFromOtherTypes());
       } else {
         DCHECK(!can_load_classes_ || self_->IsExceptionPending());
         self_->ClearException();
@@ -4315,8 +4315,8 @@
         = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
     if (mirror_method_ != nullptr) {
       mirror::Class* klass = mirror_method_->GetDeclaringClass();
-      declaring_class_ = &reg_types_.FromClass(descriptor, klass,
-                                               klass->CannotBeAssignedFromOtherTypes());
+      declaring_class_ = &FromClass(descriptor, klass,
+                                    klass->CannotBeAssignedFromOtherTypes());
     } else {
       declaring_class_ = &reg_types_.FromDescriptor(GetClassLoader(), descriptor, false);
     }
@@ -4416,5 +4416,17 @@
   reg_types_.VisitRoots(visitor, root_info);
 }
 
+const RegType& MethodVerifier::FromClass(const char* descriptor,
+                                         mirror::Class* klass,
+                                         bool precise) {
+  DCHECK(klass != nullptr);
+  if (precise && !klass->IsInstantiable() && !klass->IsPrimitive()) {
+    Fail(VerifyError::VERIFY_ERROR_NO_CLASS) << "Could not create precise reference for "
+        << "non-instantiable klass " << descriptor;
+    precise = false;
+  }
+  return reg_types_.FromClass(descriptor, klass, precise);
+}
+
 }  // namespace verifier
 }  // namespace art
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 873b8ab..204b18e 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -244,6 +244,10 @@
   bool HasCheckCasts() const;
   bool HasVirtualOrInterfaceInvokes() const;
   bool HasFailures() const;
+  bool HasInstructionThatWillThrow() const {
+    return have_pending_runtime_throw_failure_;
+  }
+
   const RegType& ResolveCheckedClass(uint32_t class_idx)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   // Returns the method of a quick invoke or null if it cannot be found.
@@ -660,6 +664,14 @@
   const RegType& DetermineCat1Constant(int32_t value, bool precise)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Try to create a register type from the given class. In case a precise type is requested, but
+  // the class is not instantiable, a soft error (of type NO_CLASS) will be enqueued and a
+  // non-precise reference will be returned.
+  // Note: we reuse NO_CLASS as this will throw an exception at runtime, when the failing class is
+  //       actually touched.
+  const RegType& FromClass(const char* descriptor, mirror::Class* klass, bool precise)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // The thread we're verifying on.
   Thread* const self_;
 
diff --git a/test/499-bce-phi-array-length/src/Main.java b/test/499-bce-phi-array-length/src/Main.java
index c8c84a1..e917bc1 100644
--- a/test/499-bce-phi-array-length/src/Main.java
+++ b/test/499-bce-phi-array-length/src/Main.java
@@ -32,11 +32,33 @@
     return result;
   }
 
+  public static int bar(int start, int[] array) {
+    int result = 0;
+    for (int i = start; i < 3; i++) {
+      result += array[i];
+      for (int j = 0; j < 2; ++j) {
+        result += array[j];
+        // The following operations would lead to BCE wanting to add another
+        // deoptimization, but it crashed assuming the input of a `HBoundsCheck`
+        // must be a `HArrayLength`.
+        result += array[0];
+        result += array[1];
+        result += array[2];
+      }
+    }
+    return result;
+  }
+
   public static void main(String[] args) {
     int[] a = new int[] { 1, 2, 3, 4, 5 };
     int result = foo(1, a);
     if (result != 11) {
       throw new Error("Got " + result + ", expected " + 11);
     }
+
+    result = bar(1, a);
+    if (result != 35) {
+      throw new Error("Got " + result + ", expected " + 35);
+    }
   }
 }
diff --git a/test/513-array-deopt/src/Main.java b/test/513-array-deopt/src/Main.java
index a0ae4c3..5ee4d55 100644
--- a/test/513-array-deopt/src/Main.java
+++ b/test/513-array-deopt/src/Main.java
@@ -19,19 +19,36 @@
     a[0] = 0;
     a[1] = 0;
     a[2] = 0;
-    // Up to this point, we record that the lower bound is 2.
+    // Up to this point, we record that the lower bound (inclusive) is 3.
     // The next instruction will record that the lower bound is 5.
     // The deoptimization code used to assume the lower bound has
-    // to be check it will add for the deoptimization (here, it
-    // would be 2).
+    // to be the one it will add for the deoptimization check (here, it
+    // would be 3).
     return new int[a.length - 5];
   }
 
+  public static int[] foo(int[] a) {
+    a[0] = 0;
+    a[1] = 0;
+    a[2] = 0;
+    // Up to this point, we record that the lower bound (inclusive) is 3.
+    // The next instruction will record that the lower bound is 1.
+    // The deoptimization code used to assume the lower bound has
+    // to be the one it will add for the deoptimization check (here, it
+    // would be 3).
+    return new int[a.length - 1];
+  }
+
   public static void main(String[] args) {
     int[] a = new int[5];
-    a = bar(a);
-    if (a.length != 0) {
-      throw new Error("Expected 0, got " + a.length);
+    int[] result = bar(a);
+    if (result.length != 0) {
+      throw new Error("Expected 0, got " + result.length);
+    }
+
+    result = foo(a);
+    if (result.length != 4) {
+      throw new Error("Expected 5, got " + result.length);
     }
   }
 }
diff --git a/test/515-dce-dominator/expected.txt b/test/515-dce-dominator/expected.txt
new file mode 100644
index 0000000..ccaf6f8
--- /dev/null
+++ b/test/515-dce-dominator/expected.txt
@@ -0,0 +1 @@
+Enter
diff --git a/test/515-dce-dominator/info.txt b/test/515-dce-dominator/info.txt
new file mode 100644
index 0000000..af706e0
--- /dev/null
+++ b/test/515-dce-dominator/info.txt
@@ -0,0 +1,3 @@
+Regression test for the DCE phase of optimizing, where
+we need to recompute the full dominance information of
+the graph when blocks get removed.
diff --git a/test/515-dce-dominator/smali/Dominator.smali b/test/515-dce-dominator/smali/Dominator.smali
new file mode 100644
index 0000000..a504aba
--- /dev/null
+++ b/test/515-dce-dominator/smali/Dominator.smali
@@ -0,0 +1,37 @@
+# Copyright (C) 2015 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 LDominator;
+
+.super Ljava/lang/Object;
+
+.method public static method(I)I
+   .registers 2
+   const/4 v0, 0
+   :b1
+   if-ne v0, v0, :b3
+   :b2
+   if-eq v0, p0, :b4
+   :b5
+   if-eq v0, p0, :b2
+   goto :b6
+   :b4
+   goto :b7
+   :b3
+   goto :b6
+   :b6
+   goto :b7
+   :b7
+   return v1
+.end method
diff --git a/test/515-dce-dominator/src/Main.java b/test/515-dce-dominator/src/Main.java
new file mode 100644
index 0000000..bf9ee25
--- /dev/null
+++ b/test/515-dce-dominator/src/Main.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 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 {
+  public static void main(String[] args) throws Exception {
+    // Workaround for b/18051191.
+    System.out.println("Enter");
+    Class<?> c = Class.forName("Dominator");
+    Method m = c.getMethod("method", int.class);
+    Object[] arguments = { 5 };
+    m.invoke(null, arguments);
+  }
+}
diff --git a/test/516-dead-move-result/expected.txt b/test/516-dead-move-result/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/516-dead-move-result/expected.txt
diff --git a/test/516-dead-move-result/info.txt b/test/516-dead-move-result/info.txt
new file mode 100644
index 0000000..49d9489
--- /dev/null
+++ b/test/516-dead-move-result/info.txt
@@ -0,0 +1,3 @@
+Regression test for the graph builder in optimizing,
+where a move-result was bogus, but it passed the verifier
+because it was dead code.
diff --git a/test/516-dead-move-result/smali/MoveResult.smali b/test/516-dead-move-result/smali/MoveResult.smali
new file mode 100644
index 0000000..9650b58
--- /dev/null
+++ b/test/516-dead-move-result/smali/MoveResult.smali
@@ -0,0 +1,25 @@
+# Copyright (C) 2015 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 LMoveResult;
+
+.super Ljava/lang/Object;
+
+.method public static method()V
+   .registers 1
+   goto :b1
+   move-result v0
+   :b1
+   return-void
+.end method
diff --git a/test/516-dead-move-result/src/Main.java b/test/516-dead-move-result/src/Main.java
new file mode 100644
index 0000000..90580a8
--- /dev/null
+++ b/test/516-dead-move-result/src/Main.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 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("MoveResult");
+    Method m = c.getMethod("method");
+    Object[] arguments = { };
+    m.invoke(null, arguments);
+  }
+}
diff --git a/test/518-null-array-get/expected.txt b/test/518-null-array-get/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/518-null-array-get/expected.txt
diff --git a/test/518-null-array-get/info.txt b/test/518-null-array-get/info.txt
new file mode 100644
index 0000000..407f590
--- /dev/null
+++ b/test/518-null-array-get/info.txt
@@ -0,0 +1,3 @@
+Regression test for Quick and Optimizing that used
+to crash on an aget-object + int-to-byte sequence
+(accepted by the verifier in the case the array was null).
diff --git a/test/518-null-array-get/smali/NullArray.smali b/test/518-null-array-get/smali/NullArray.smali
new file mode 100644
index 0000000..52abc38
--- /dev/null
+++ b/test/518-null-array-get/smali/NullArray.smali
@@ -0,0 +1,26 @@
+# Copyright (C) 2015 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 LNullArray;
+
+.super Ljava/lang/Object;
+
+.method public static method()B
+   .registers 2
+   const/4 v0, 0
+   const/4 v1, 0
+   aget-object v0, v0, v1
+   int-to-byte v0, v0
+   return v0
+.end method
diff --git a/test/518-null-array-get/src/Main.java b/test/518-null-array-get/src/Main.java
new file mode 100644
index 0000000..66e50aa
--- /dev/null
+++ b/test/518-null-array-get/src/Main.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 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.InvocationTargetException;
+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("NullArray");
+    Method m = c.getMethod("method");
+    Object[] arguments = { };
+    try {
+      m.invoke(null, arguments);
+      throw new Error("Expected an InvocationTargetException");
+    } catch (InvocationTargetException e) {
+      if (!(e.getCause() instanceof NullPointerException)) {
+        throw new Error("Expected a NullPointerException");
+      }
+    }
+  }
+}