Merge "Add one more EnsureJitCompiled() to 088-monitor-verification"
diff --git a/compiler/dex/quick_compiler_callbacks.cc b/compiler/dex/quick_compiler_callbacks.cc
index c7e9f4f..23511e5 100644
--- a/compiler/dex/quick_compiler_callbacks.cc
+++ b/compiler/dex/quick_compiler_callbacks.cc
@@ -34,17 +34,21 @@
   }
 }
 
-bool QuickCompilerCallbacks::CanAssumeVerified(ClassReference ref) {
+ClassStatus QuickCompilerCallbacks::GetPreviousClassState(ClassReference ref) {
   // If we don't have class unloading enabled in the compiler, we will never see class that were
   // previously verified. Return false to avoid overhead from the lookup in the compiler driver.
   if (!does_class_unloading_) {
-    return false;
+    return ClassStatus::kStatusNotReady;
   }
   DCHECK(compiler_driver_ != nullptr);
   // In the case of the quicken filter: avoiding verification of quickened instructions, which the
   // verifier doesn't currently support.
   // In the case of the verify filter, avoiding verifiying twice.
-  return compiler_driver_->CanAssumeVerified(ref);
+  ClassStatus status;
+  if (!compiler_driver_->GetCompiledClass(ref, &status)) {
+    return ClassStatus::kStatusNotReady;
+  }
+  return status;
 }
 
 }  // namespace art
diff --git a/compiler/dex/quick_compiler_callbacks.h b/compiler/dex/quick_compiler_callbacks.h
index 578aff4..45456f2 100644
--- a/compiler/dex/quick_compiler_callbacks.h
+++ b/compiler/dex/quick_compiler_callbacks.h
@@ -54,7 +54,7 @@
       verification_results_ = verification_results;
     }
 
-    bool CanAssumeVerified(ClassReference ref) OVERRIDE;
+    ClassStatus GetPreviousClassState(ClassReference ref) OVERRIDE;
 
     void SetDoesClassUnloading(bool does_class_unloading, CompilerDriver* compiler_driver)
         OVERRIDE {
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index ee36a92..18b54ee 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -3053,10 +3053,4 @@
   }
 }
 
-bool CompilerDriver::CanAssumeVerified(ClassReference ref) const {
-  mirror::Class::Status existing = mirror::Class::kStatusNotReady;
-  compiled_classes_.Get(DexFileReference(ref.first, ref.second), &existing);
-  return existing >= mirror::Class::kStatusVerified;
-}
-
 }  // namespace art
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 11808c1..d08d9d7 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -379,8 +379,6 @@
     return profile_compilation_info_;
   }
 
-  bool CanAssumeVerified(ClassReference ref) const;
-
   // Is `boot_image_filename` the name of a core image (small boot
   // image used for ART testing only)?
   static bool IsCoreImageFilename(const std::string& boot_image_filename) {
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 392d57c..278358b 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -369,8 +369,6 @@
 
 // Test that a class of status kStatusRetryVerificationAtRuntime is indeed recorded that way in the
 // driver.
-// Test that checks that classes can be assumed as verified if unloading mode is enabled and
-// the class status is at least verified.
 TEST_F(CompilerDriverVerifyTest, RetryVerifcationStatusCheckVerified) {
   Thread* const self = Thread::Current();
   jobject class_loader;
@@ -401,12 +399,6 @@
     mirror::Class::Status status = {};
     ASSERT_TRUE(compiler_driver_->GetCompiledClass(ref, &status));
     EXPECT_EQ(status, expected_status);
-
-    // Check that we can assume verified if we are a status that is at least verified.
-    if (status >= mirror::Class::kStatusVerified) {
-      // Check that the class can be assumed as verified in the compiler driver.
-      EXPECT_TRUE(callbacks_->CanAssumeVerified(ref)) << status;
-    }
   }
 }
 
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 6fbb2bd..58b6137 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -1943,20 +1943,15 @@
   DCHECK_EQ(bss_size_, 0u);
   if (HasBootImage()) {
     DCHECK(bss_string_entries_.empty());
-    if (bss_method_entries_.empty() && bss_type_entries_.empty()) {
-      // Nothing to put to the .bss section.
-      return;
-    }
+  }
+  if (bss_method_entries_.empty() &&
+      bss_type_entries_.empty() &&
+      bss_string_entries_.empty()) {
+    // Nothing to put to the .bss section.
+    return;
   }
 
-  // Allocate space for app dex cache arrays in the .bss section.
   PointerSize pointer_size = GetInstructionSetPointerSize(instruction_set);
-  if (!HasBootImage()) {
-    for (const DexFile* dex_file : *dex_files_) {
-      DexCacheArraysLayout layout(pointer_size, dex_file);
-      bss_size_ += layout.Size();
-    }
-  }
 
   bss_methods_offset_ = bss_size_;
 
diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc
index 9095ecd..18a55c8 100644
--- a/compiler/optimizing/code_generator_vector_arm64.cc
+++ b/compiler/optimizing/code_generator_vector_arm64.cc
@@ -27,12 +27,13 @@
 using helpers::ARM64EncodableConstantOrRegister;
 using helpers::Arm64CanEncodeConstantAsImmediate;
 using helpers::DRegisterFrom;
-using helpers::VRegisterFrom;
 using helpers::HeapOperand;
 using helpers::InputRegisterAt;
 using helpers::Int64ConstantFrom;
-using helpers::XRegisterFrom;
+using helpers::OutputRegister;
+using helpers::VRegisterFrom;
 using helpers::WRegisterFrom;
+using helpers::XRegisterFrom;
 
 #define __ GetVIXLAssembler()->
 
@@ -127,20 +128,51 @@
   }
 }
 
-void LocationsBuilderARM64::VisitVecSetScalars(HVecSetScalars* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+void LocationsBuilderARM64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresRegister());
+      break;
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
-void InstructionCodeGeneratorARM64::VisitVecSetScalars(HVecSetScalars* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARM64::VisitVecSumReduce(HVecSumReduce* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorARM64::VisitVecSumReduce(HVecSumReduce* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+void InstructionCodeGeneratorARM64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  VRegister src = VRegisterFrom(locations->InAt(0));
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Umov(OutputRegister(instruction), src.V4S(), 0);
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Umov(OutputRegister(instruction), src.V2D(), 0);
+      break;
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 4u);
+      DCHECK(locations->InAt(0).Equals(locations->Out()));  // no code required
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
 }
 
 // Helper to set up locations for vector unary operations.
@@ -169,6 +201,46 @@
   }
 }
 
+void LocationsBuilderARM64::VisitVecReduce(HVecReduce* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecReduce(HVecReduce* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  VRegister src = VRegisterFrom(locations->InAt(0));
+  VRegister dst = DRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      switch (instruction->GetKind()) {
+        case HVecReduce::kSum:
+          __ Addv(dst.S(), src.V4S());
+          break;
+        case HVecReduce::kMin:
+          __ Sminv(dst.S(), src.V4S());
+          break;
+        case HVecReduce::kMax:
+          __ Smaxv(dst.S(), src.V4S());
+          break;
+      }
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      switch (instruction->GetKind()) {
+        case HVecReduce::kSum:
+          __ Addp(dst.D(), src.V2D());
+          break;
+        default:
+          LOG(FATAL) << "Unsupported SIMD min/max";
+          UNREACHABLE();
+      }
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
 void LocationsBuilderARM64::VisitVecCnv(HVecCnv* instruction) {
   CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
 }
@@ -263,6 +335,7 @@
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
   }
 }
 
@@ -805,6 +878,77 @@
   }
 }
 
+void LocationsBuilderARM64::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+
+  DCHECK_EQ(1u, instruction->InputCount());  // only one input currently implemented
+
+  HInstruction* input = instruction->InputAt(0);
+  bool is_zero = IsZeroBitPattern(input);
+
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+                                    : Location::RequiresRegister());
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+                                    : Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void InstructionCodeGeneratorARM64::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  VRegister dst = VRegisterFrom(locations->Out());
+
+  DCHECK_EQ(1u, instruction->InputCount());  // only one input currently implemented
+
+  // Zero out all other elements first.
+  __ Movi(dst.V16B(), 0);
+
+  // Shorthand for any type of zero.
+  if (IsZeroBitPattern(instruction->InputAt(0))) {
+    return;
+  }
+
+  // Set required elements.
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ Mov(dst.V16B(), 0, InputRegisterAt(instruction, 0));
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Mov(dst.V8H(), 0, InputRegisterAt(instruction, 0));
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Mov(dst.V4S(), 0, InputRegisterAt(instruction, 0));
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Mov(dst.V2D(), 0, InputRegisterAt(instruction, 0));
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
 void LocationsBuilderARM64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) {
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr);
   switch (instr->GetPackedType()) {
diff --git a/compiler/optimizing/code_generator_vector_arm_vixl.cc b/compiler/optimizing/code_generator_vector_arm_vixl.cc
index 527691d..7a11dff 100644
--- a/compiler/optimizing/code_generator_vector_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_vector_arm_vixl.cc
@@ -73,19 +73,11 @@
   }
 }
 
-void LocationsBuilderARMVIXL::VisitVecSetScalars(HVecSetScalars* instruction) {
+void LocationsBuilderARMVIXL::VisitVecExtractScalar(HVecExtractScalar* instruction) {
   LOG(FATAL) << "No SIMD for " << instruction->GetId();
 }
 
-void InstructionCodeGeneratorARMVIXL::VisitVecSetScalars(HVecSetScalars* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderARMVIXL::VisitVecSumReduce(HVecSumReduce* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorARMVIXL::VisitVecSumReduce(HVecSumReduce* instruction) {
+void InstructionCodeGeneratorARMVIXL::VisitVecExtractScalar(HVecExtractScalar* instruction) {
   LOG(FATAL) << "No SIMD for " << instruction->GetId();
 }
 
@@ -112,6 +104,14 @@
   }
 }
 
+void LocationsBuilderARMVIXL::VisitVecReduce(HVecReduce* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecReduce(HVecReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
 void LocationsBuilderARMVIXL::VisitVecCnv(HVecCnv* instruction) {
   CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
 }
@@ -621,6 +621,14 @@
   }
 }
 
+void LocationsBuilderARMVIXL::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
 void LocationsBuilderARMVIXL::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) {
   LOG(FATAL) << "No SIMD for " << instr->GetId();
 }
diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc
index 6bf28ab..c2fbf7f 100644
--- a/compiler/optimizing/code_generator_vector_mips.cc
+++ b/compiler/optimizing/code_generator_vector_mips.cc
@@ -88,19 +88,11 @@
   }
 }
 
-void LocationsBuilderMIPS::VisitVecSetScalars(HVecSetScalars* instruction) {
+void LocationsBuilderMIPS::VisitVecExtractScalar(HVecExtractScalar* instruction) {
   LOG(FATAL) << "No SIMD for " << instruction->GetId();
 }
 
-void InstructionCodeGeneratorMIPS::VisitVecSetScalars(HVecSetScalars* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderMIPS::VisitVecSumReduce(HVecSumReduce* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorMIPS::VisitVecSumReduce(HVecSumReduce* instruction) {
+void InstructionCodeGeneratorMIPS::VisitVecExtractScalar(HVecExtractScalar* instruction) {
   LOG(FATAL) << "No SIMD for " << instruction->GetId();
 }
 
@@ -133,6 +125,14 @@
   }
 }
 
+void LocationsBuilderMIPS::VisitVecReduce(HVecReduce* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecReduce(HVecReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
 void LocationsBuilderMIPS::VisitVecCnv(HVecCnv* instruction) {
   CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
 }
@@ -818,6 +818,14 @@
   }
 }
 
+void LocationsBuilderMIPS::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
 void LocationsBuilderMIPS::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) {
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr);
   switch (instr->GetPackedType()) {
diff --git a/compiler/optimizing/code_generator_vector_mips64.cc b/compiler/optimizing/code_generator_vector_mips64.cc
index 75bf7a7..9d3a777 100644
--- a/compiler/optimizing/code_generator_vector_mips64.cc
+++ b/compiler/optimizing/code_generator_vector_mips64.cc
@@ -91,19 +91,11 @@
   }
 }
 
-void LocationsBuilderMIPS64::VisitVecSetScalars(HVecSetScalars* instruction) {
+void LocationsBuilderMIPS64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
   LOG(FATAL) << "No SIMD for " << instruction->GetId();
 }
 
-void InstructionCodeGeneratorMIPS64::VisitVecSetScalars(HVecSetScalars* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderMIPS64::VisitVecSumReduce(HVecSumReduce* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorMIPS64::VisitVecSumReduce(HVecSumReduce* instruction) {
+void InstructionCodeGeneratorMIPS64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
   LOG(FATAL) << "No SIMD for " << instruction->GetId();
 }
 
@@ -136,6 +128,14 @@
   }
 }
 
+void LocationsBuilderMIPS64::VisitVecReduce(HVecReduce* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecReduce(HVecReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
 void LocationsBuilderMIPS64::VisitVecCnv(HVecCnv* instruction) {
   CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
 }
@@ -822,6 +822,14 @@
   }
 }
 
+void LocationsBuilderMIPS64::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
 void LocationsBuilderMIPS64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) {
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr);
   switch (instr->GetPackedType()) {
diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc
index e7aec76..37190f8 100644
--- a/compiler/optimizing/code_generator_vector_x86.cc
+++ b/compiler/optimizing/code_generator_vector_x86.cc
@@ -27,9 +27,99 @@
 
 void LocationsBuilderX86::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  HInstruction* input = instruction->InputAt(0);
+  bool is_zero = IsZeroBitPattern(input);
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimLong:
-      // Long needs extra temporary to load the register pair.
+      // Long needs extra temporary to load from the register pair.
+      if (!is_zero) {
+        locations->AddTemp(Location::RequiresFpuRegister());
+      }
+      FALLTHROUGH_INTENDED;
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+      locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+                                    : Location::RequiresRegister());
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+                                    : Location::RequiresFpuRegister());
+      locations->SetOut(is_zero ? Location::RequiresFpuRegister()
+                                : Location::SameAsFirstInput());
+
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void InstructionCodeGeneratorX86::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+
+  // Shorthand for any type of zero.
+  if (IsZeroBitPattern(instruction->InputAt(0))) {
+    __ xorps(dst, dst);
+    return;
+  }
+
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ movd(dst, locations->InAt(0).AsRegister<Register>());
+      __ punpcklbw(dst, dst);
+      __ punpcklwd(dst, dst);
+      __ pshufd(dst, dst, Immediate(0));
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ movd(dst, locations->InAt(0).AsRegister<Register>());
+      __ punpcklwd(dst, dst);
+      __ pshufd(dst, dst, Immediate(0));
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ movd(dst, locations->InAt(0).AsRegister<Register>());
+      __ pshufd(dst, dst, Immediate(0));
+      break;
+    case Primitive::kPrimLong: {
+      XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ movd(dst, locations->InAt(0).AsRegisterPairLow<Register>());
+      __ movd(tmp, locations->InAt(0).AsRegisterPairHigh<Register>());
+      __ punpckldq(dst, tmp);
+      __ punpcklqdq(dst, dst);
+      break;
+    }
+    case Primitive::kPrimFloat:
+      DCHECK(locations->InAt(0).Equals(locations->Out()));
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ shufps(dst, dst, Immediate(0));
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK(locations->InAt(0).Equals(locations->Out()));
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ shufpd(dst, dst, Immediate(0));
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitVecExtractScalar(HVecExtractScalar* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimLong:
+      // Long needs extra temporary to store into the register pair.
       locations->AddTemp(Location::RequiresFpuRegister());
       FALLTHROUGH_INTENDED;
     case Primitive::kPrimBoolean:
@@ -37,8 +127,8 @@
     case Primitive::kPrimChar:
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
-      locations->SetInAt(0, Location::RequiresRegister());
-      locations->SetOut(Location::RequiresFpuRegister());
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresRegister());
       break;
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble:
@@ -51,48 +141,34 @@
   }
 }
 
-void InstructionCodeGeneratorX86::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+void InstructionCodeGeneratorX86::VisitVecExtractScalar(HVecExtractScalar* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  XmmRegister reg = locations->Out().AsFpuRegister<XmmRegister>();
+  XmmRegister src = locations->InAt(0).AsFpuRegister<XmmRegister>();
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
-      DCHECK_EQ(16u, instruction->GetVectorLength());
-      __ movd(reg, locations->InAt(0).AsRegister<Register>());
-      __ punpcklbw(reg, reg);
-      __ punpcklwd(reg, reg);
-      __ pshufd(reg, reg, Immediate(0));
-      break;
     case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-      DCHECK_EQ(8u, instruction->GetVectorLength());
-      __ movd(reg, locations->InAt(0).AsRegister<Register>());
-      __ punpcklwd(reg, reg);
-      __ pshufd(reg, reg, Immediate(0));
-      break;
+    case Primitive::kPrimShort:  // TODO: up to here, and?
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
     case Primitive::kPrimInt:
-      DCHECK_EQ(4u, instruction->GetVectorLength());
-      __ movd(reg, locations->InAt(0).AsRegister<Register>());
-      __ pshufd(reg, reg, Immediate(0));
+      DCHECK_LE(4u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      __ movd(locations->Out().AsRegister<Register>(), src);
       break;
     case Primitive::kPrimLong: {
       XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ movd(reg, locations->InAt(0).AsRegisterPairLow<Register>());
-      __ movd(tmp, locations->InAt(0).AsRegisterPairHigh<Register>());
-      __ punpckldq(reg, tmp);
-      __ punpcklqdq(reg, reg);
+      __ movd(locations->Out().AsRegisterPairLow<Register>(), src);
+      __ pshufd(tmp, src, Immediate(1));
+      __ movd(locations->Out().AsRegisterPairHigh<Register>(), tmp);
       break;
     }
     case Primitive::kPrimFloat:
-      DCHECK(locations->InAt(0).Equals(locations->Out()));
-      DCHECK_EQ(4u, instruction->GetVectorLength());
-      __ shufps(reg, reg, Immediate(0));
-      break;
     case Primitive::kPrimDouble:
-      DCHECK(locations->InAt(0).Equals(locations->Out()));
-      DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ shufpd(reg, reg, Immediate(0));
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 4u);
+      DCHECK(locations->InAt(0).Equals(locations->Out()));  // no code required
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -100,22 +176,6 @@
   }
 }
 
-void LocationsBuilderX86::VisitVecSetScalars(HVecSetScalars* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorX86::VisitVecSetScalars(HVecSetScalars* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderX86::VisitVecSumReduce(HVecSumReduce* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorX86::VisitVecSumReduce(HVecSumReduce* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
 // Helper to set up locations for vector unary operations.
 static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* instruction) {
   LocationSummary* locations = new (arena) LocationSummary(instruction);
@@ -137,6 +197,73 @@
   }
 }
 
+void LocationsBuilderX86::VisitVecReduce(HVecReduce* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+  // Long reduction or min/max require a temporary.
+  if (instruction->GetPackedType() == Primitive::kPrimLong ||
+      instruction->GetKind() == HVecReduce::kMin ||
+      instruction->GetKind() == HVecReduce::kMax) {
+    instruction->GetLocations()->AddTemp(Location::RequiresFpuRegister());
+  }
+}
+
+void InstructionCodeGeneratorX86::VisitVecReduce(HVecReduce* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  XmmRegister src = locations->InAt(0).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      switch (instruction->GetKind()) {
+        case HVecReduce::kSum:
+          __ movaps(dst, src);
+          __ phaddd(dst, dst);
+          __ phaddd(dst, dst);
+          break;
+        case HVecReduce::kMin: {
+          XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+          __ movaps(tmp, src);
+          __ movaps(dst, src);
+          __ psrldq(tmp, Immediate(8));
+          __ pminsd(dst, tmp);
+          __ psrldq(tmp, Immediate(4));
+          __ pminsd(dst, tmp);
+          break;
+        }
+        case HVecReduce::kMax: {
+          XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+          __ movaps(tmp, src);
+          __ movaps(dst, src);
+          __ psrldq(tmp, Immediate(8));
+          __ pmaxsd(dst, tmp);
+          __ psrldq(tmp, Immediate(4));
+          __ pmaxsd(dst, tmp);
+          break;
+        }
+      }
+      break;
+    case Primitive::kPrimLong: {
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+      switch (instruction->GetKind()) {
+        case HVecReduce::kSum:
+          __ movaps(tmp, src);
+          __ movaps(dst, src);
+          __ punpckhqdq(tmp, tmp);
+          __ paddq(dst, tmp);
+          break;
+        case HVecReduce::kMin:
+        case HVecReduce::kMax:
+          LOG(FATAL) << "Unsupported SIMD type";
+      }
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
 void LocationsBuilderX86::VisitVecCnv(HVecCnv* instruction) {
   CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
 }
@@ -821,6 +948,91 @@
   }
 }
 
+void LocationsBuilderX86::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+
+  DCHECK_EQ(1u, instruction->InputCount());  // only one input currently implemented
+
+  HInstruction* input = instruction->InputAt(0);
+  bool is_zero = IsZeroBitPattern(input);
+
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimLong:
+      // Long needs extra temporary to load from register pairs.
+      if (!is_zero) {
+        locations->AddTemp(Location::RequiresFpuRegister());
+      }
+      FALLTHROUGH_INTENDED;
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+      locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+                                    : Location::RequiresRegister());
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+                                    : Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void InstructionCodeGeneratorX86::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+
+  DCHECK_EQ(1u, instruction->InputCount());  // only one input currently implemented
+
+  // Zero out all other elements first.
+  __ xorps(dst, dst);
+
+  // Shorthand for any type of zero.
+  if (IsZeroBitPattern(instruction->InputAt(0))) {
+    return;
+  }
+
+  // Set required elements.
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:  // TODO: up to here, and?
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ movd(dst, locations->InAt(0).AsRegister<Register>());
+      break;
+    case Primitive::kPrimLong: {
+      XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ xorps(tmp, tmp);
+      __ movd(dst, locations->InAt(0).AsRegisterPairLow<Register>());
+      __ movd(tmp, locations->InAt(0).AsRegisterPairHigh<Register>());
+      __ punpckldq(dst, tmp);
+      break;
+    }
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ movss(dst, locations->InAt(1).AsFpuRegister<XmmRegister>());
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ movsd(dst, locations->InAt(1).AsFpuRegister<XmmRegister>());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
 void LocationsBuilderX86::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) {
   LOG(FATAL) << "No SIMD for " << instr->GetId();
 }
@@ -868,6 +1080,7 @@
     case 8: scale = TIMES_8; break;
     default: break;
   }
+  // Incorporate the string or array offset in the address computation.
   uint32_t offset = is_string_char_at
       ? mirror::String::ValueOffset().Uint32Value()
       : mirror::Array::DataOffset(size).Uint32Value();
@@ -902,7 +1115,7 @@
         __ testb(Address(locations->InAt(0).AsRegister<Register>(), count_offset), Immediate(1));
         __ j(kNotZero, &not_compressed);
         // Zero extend 8 compressed bytes into 8 chars.
-        __ movsd(reg, VecAddress(locations, 1, /*is_string_char_at*/ true));
+        __ movsd(reg, VecAddress(locations, 1, instruction->IsStringCharAt()));
         __ pxor(tmp, tmp);
         __ punpcklbw(reg, tmp);
         __ jmp(&done);
diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc
index c7ee81c..7051ba0 100644
--- a/compiler/optimizing/code_generator_vector_x86_64.cc
+++ b/compiler/optimizing/code_generator_vector_x86_64.cc
@@ -27,6 +27,8 @@
 
 void LocationsBuilderX86_64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
   LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  HInstruction* input = instruction->InputAt(0);
+  bool is_zero = IsZeroBitPattern(input);
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
@@ -34,11 +36,89 @@
     case Primitive::kPrimShort:
     case Primitive::kPrimInt:
     case Primitive::kPrimLong:
-      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+                                    : Location::RequiresRegister());
       locations->SetOut(Location::RequiresFpuRegister());
       break;
     case Primitive::kPrimFloat:
     case Primitive::kPrimDouble:
+      locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+                                    : Location::RequiresFpuRegister());
+      locations->SetOut(is_zero ? Location::RequiresFpuRegister()
+                                : Location::SameAsFirstInput());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+
+  // Shorthand for any type of zero.
+  if (IsZeroBitPattern(instruction->InputAt(0))) {
+    __ xorps(dst, dst);
+    return;
+  }
+
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ movd(dst, locations->InAt(0).AsRegister<CpuRegister>());
+      __ punpcklbw(dst, dst);
+      __ punpcklwd(dst, dst);
+      __ pshufd(dst, dst, Immediate(0));
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ movd(dst, locations->InAt(0).AsRegister<CpuRegister>());
+      __ punpcklwd(dst, dst);
+      __ pshufd(dst, dst, Immediate(0));
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ movd(dst, locations->InAt(0).AsRegister<CpuRegister>());
+      __ pshufd(dst, dst, Immediate(0));
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ movd(dst, locations->InAt(0).AsRegister<CpuRegister>());  // is 64-bit
+      __ punpcklqdq(dst, dst);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK(locations->InAt(0).Equals(locations->Out()));
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ shufps(dst, dst, Immediate(0));
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK(locations->InAt(0).Equals(locations->Out()));
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ shufpd(dst, dst, Immediate(0));
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresRegister());
+      break;
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
       locations->SetInAt(0, Location::RequiresFpuRegister());
       locations->SetOut(Location::SameAsFirstInput());
       break;
@@ -48,44 +128,29 @@
   }
 }
 
-void InstructionCodeGeneratorX86_64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+void InstructionCodeGeneratorX86_64::VisitVecExtractScalar(HVecExtractScalar* instruction) {
   LocationSummary* locations = instruction->GetLocations();
-  XmmRegister reg = locations->Out().AsFpuRegister<XmmRegister>();
+  XmmRegister src = locations->InAt(0).AsFpuRegister<XmmRegister>();
   switch (instruction->GetPackedType()) {
     case Primitive::kPrimBoolean:
     case Primitive::kPrimByte:
-      DCHECK_EQ(16u, instruction->GetVectorLength());
-      __ movd(reg, locations->InAt(0).AsRegister<CpuRegister>());
-      __ punpcklbw(reg, reg);
-      __ punpcklwd(reg, reg);
-      __ pshufd(reg, reg, Immediate(0));
-      break;
     case Primitive::kPrimChar:
-    case Primitive::kPrimShort:
-      DCHECK_EQ(8u, instruction->GetVectorLength());
-      __ movd(reg, locations->InAt(0).AsRegister<CpuRegister>());
-      __ punpcklwd(reg, reg);
-      __ pshufd(reg, reg, Immediate(0));
-      break;
+    case Primitive::kPrimShort:  // TODO: up to here, and?
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
     case Primitive::kPrimInt:
       DCHECK_EQ(4u, instruction->GetVectorLength());
-      __ movd(reg, locations->InAt(0).AsRegister<CpuRegister>());
-      __ pshufd(reg, reg, Immediate(0));
+      __ movd(locations->Out().AsRegister<CpuRegister>(), src);
       break;
     case Primitive::kPrimLong:
       DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ movd(reg, locations->InAt(0).AsRegister<CpuRegister>());  // is 64-bit
-      __ punpcklqdq(reg, reg);
+      __ movd(locations->Out().AsRegister<CpuRegister>(), src);   // is 64-bit
       break;
     case Primitive::kPrimFloat:
-      DCHECK(locations->InAt(0).Equals(locations->Out()));
-      DCHECK_EQ(4u, instruction->GetVectorLength());
-      __ shufps(reg, reg, Immediate(0));
-      break;
     case Primitive::kPrimDouble:
-      DCHECK(locations->InAt(0).Equals(locations->Out()));
-      DCHECK_EQ(2u, instruction->GetVectorLength());
-      __ shufpd(reg, reg, Immediate(0));
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 4u);
+      DCHECK(locations->InAt(0).Equals(locations->Out()));  // no code required
       break;
     default:
       LOG(FATAL) << "Unsupported SIMD type";
@@ -93,22 +158,6 @@
   }
 }
 
-void LocationsBuilderX86_64::VisitVecSetScalars(HVecSetScalars* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorX86_64::VisitVecSetScalars(HVecSetScalars* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void LocationsBuilderX86_64::VisitVecSumReduce(HVecSumReduce* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
-void InstructionCodeGeneratorX86_64::VisitVecSumReduce(HVecSumReduce* instruction) {
-  LOG(FATAL) << "No SIMD for " << instruction->GetId();
-}
-
 // Helper to set up locations for vector unary operations.
 static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* instruction) {
   LocationSummary* locations = new (arena) LocationSummary(instruction);
@@ -130,6 +179,73 @@
   }
 }
 
+void LocationsBuilderX86_64::VisitVecReduce(HVecReduce* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+  // Long reduction or min/max require a temporary.
+  if (instruction->GetPackedType() == Primitive::kPrimLong ||
+      instruction->GetKind() == HVecReduce::kMin ||
+      instruction->GetKind() == HVecReduce::kMax) {
+    instruction->GetLocations()->AddTemp(Location::RequiresFpuRegister());
+  }
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecReduce(HVecReduce* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  XmmRegister src = locations->InAt(0).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      switch (instruction->GetKind()) {
+        case HVecReduce::kSum:
+          __ movaps(dst, src);
+          __ phaddd(dst, dst);
+          __ phaddd(dst, dst);
+          break;
+        case HVecReduce::kMin: {
+          XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+          __ movaps(tmp, src);
+          __ movaps(dst, src);
+          __ psrldq(tmp, Immediate(8));
+          __ pminsd(dst, tmp);
+          __ psrldq(tmp, Immediate(4));
+          __ pminsd(dst, tmp);
+          break;
+        }
+        case HVecReduce::kMax: {
+          XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+          __ movaps(tmp, src);
+          __ movaps(dst, src);
+          __ psrldq(tmp, Immediate(8));
+          __ pmaxsd(dst, tmp);
+          __ psrldq(tmp, Immediate(4));
+          __ pmaxsd(dst, tmp);
+          break;
+        }
+      }
+      break;
+    case Primitive::kPrimLong: {
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+      switch (instruction->GetKind()) {
+        case HVecReduce::kSum:
+          __ movaps(tmp, src);
+          __ movaps(dst, src);
+          __ punpckhqdq(tmp, tmp);
+          __ paddq(dst, tmp);
+          break;
+        case HVecReduce::kMin:
+        case HVecReduce::kMax:
+          LOG(FATAL) << "Unsupported SIMD type";
+      }
+      break;
+    }
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
 void LocationsBuilderX86_64::VisitVecCnv(HVecCnv* instruction) {
   CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
 }
@@ -814,6 +930,81 @@
   }
 }
 
+void LocationsBuilderX86_64::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+
+  DCHECK_EQ(1u, instruction->InputCount());  // only one input currently implemented
+
+  HInstruction* input = instruction->InputAt(0);
+  bool is_zero = IsZeroBitPattern(input);
+
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+                                    : Location::RequiresRegister());
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, is_zero ? Location::ConstantLocation(input->AsConstant())
+                                    : Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+
+  DCHECK_EQ(1u, instruction->InputCount());  // only one input currently implemented
+
+  // Zero out all other elements first.
+  __ xorps(dst, dst);
+
+  // Shorthand for any type of zero.
+  if (IsZeroBitPattern(instruction->InputAt(0))) {
+    return;
+  }
+
+  // Set required elements.
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:  // TODO: up to here, and?
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ movd(dst, locations->InAt(0).AsRegister<CpuRegister>());
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ movd(dst, locations->InAt(0).AsRegister<CpuRegister>());  // is 64-bit
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ movss(dst, locations->InAt(0).AsFpuRegister<XmmRegister>());
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ movsd(dst, locations->InAt(0).AsFpuRegister<XmmRegister>());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
 void LocationsBuilderX86_64::VisitVecMultiplyAccumulate(HVecMultiplyAccumulate* instr) {
   LOG(FATAL) << "No SIMD for " << instr->GetId();
 }
@@ -861,6 +1052,7 @@
     case 8: scale = TIMES_8; break;
     default: break;
   }
+  // Incorporate the string or array offset in the address computation.
   uint32_t offset = is_string_char_at
       ? mirror::String::ValueOffset().Uint32Value()
       : mirror::Array::DataOffset(size).Uint32Value();
@@ -895,7 +1087,7 @@
         __ testb(Address(locations->InAt(0).AsRegister<CpuRegister>(), count_offset), Immediate(1));
         __ j(kNotZero, &not_compressed);
         // Zero extend 8 compressed bytes into 8 chars.
-        __ movsd(reg, VecAddress(locations, 1, /*is_string_char_at*/ true));
+        __ movsd(reg, VecAddress(locations, 1, instruction->IsStringCharAt()));
         __ pxor(tmp, tmp);
         __ punpcklbw(reg, tmp);
         __ jmp(&done);
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 027ba77..a249cac 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -285,6 +285,19 @@
   return false;
 }
 
+// Translates operation to reduction kind.
+static HVecReduce::ReductionKind GetReductionKind(HInstruction* reduction) {
+  if (reduction->IsVecAdd() || reduction->IsVecSub()) {
+    return HVecReduce::kSum;
+  } else if (reduction->IsVecMin()) {
+    return HVecReduce::kMin;
+  } else if (reduction->IsVecMax()) {
+    return HVecReduce::kMax;
+  }
+  LOG(FATAL) << "Unsupported SIMD reduction";
+  UNREACHABLE();
+}
+
 // Test vector restrictions.
 static bool HasVectorRestrictions(uint64_t restrictions, uint64_t tested) {
   return (restrictions & tested) != 0;
@@ -318,8 +331,9 @@
 
 HLoopOptimization::HLoopOptimization(HGraph* graph,
                                      CompilerDriver* compiler_driver,
-                                     HInductionVarAnalysis* induction_analysis)
-    : HOptimization(graph, kLoopOptimizationPassName),
+                                     HInductionVarAnalysis* induction_analysis,
+                                     OptimizingCompilerStats* stats)
+    : HOptimization(graph, kLoopOptimizationPassName, stats),
       compiler_driver_(compiler_driver),
       induction_range_(induction_analysis),
       loop_allocator_(nullptr),
@@ -334,7 +348,8 @@
       vector_peeling_candidate_(nullptr),
       vector_runtime_test_a_(nullptr),
       vector_runtime_test_b_(nullptr),
-      vector_map_(nullptr) {
+      vector_map_(nullptr),
+      vector_permanent_map_(nullptr) {
 }
 
 void HLoopOptimization::Run() {
@@ -388,11 +403,14 @@
     ArenaSet<ArrayReference> refs(loop_allocator_->Adapter(kArenaAllocLoopOptimization));
     ArenaSafeMap<HInstruction*, HInstruction*> map(
         std::less<HInstruction*>(), loop_allocator_->Adapter(kArenaAllocLoopOptimization));
+    ArenaSafeMap<HInstruction*, HInstruction*> perm(
+        std::less<HInstruction*>(), loop_allocator_->Adapter(kArenaAllocLoopOptimization));
     // Attach.
     iset_ = &iset;
     reductions_ = &reds;
     vector_refs_ = &refs;
     vector_map_ = &map;
+    vector_permanent_map_ = &perm;
     // Traverse.
     TraverseLoopsInnerToOuter(top_loop_);
     // Detach.
@@ -400,6 +418,7 @@
     reductions_ = nullptr;
     vector_refs_ = nullptr;
     vector_map_ = nullptr;
+    vector_permanent_map_ = nullptr;
   }
 }
 
@@ -603,11 +622,11 @@
   // Vectorize loop, if possible and valid.
   if (kEnableVectorization &&
       TrySetSimpleLoopHeader(header, &main_phi) &&
-      reductions_->empty() &&  // TODO: possible with some effort
       ShouldVectorize(node, body, trip_count) &&
       TryAssignLastValue(node->loop_info, main_phi, preheader, /*collect_loop_uses*/ true)) {
     Vectorize(node, body, exit, trip_count);
     graph_->SetHasSIMD(true);  // flag SIMD usage
+    MaybeRecordStat(stats_, MethodCompilationStat::kLoopVectorized);
     return true;
   }
   return false;
@@ -802,6 +821,13 @@
                     /*unroll*/ 1);
   }
 
+  // Link reductions to their final uses.
+  for (auto i = reductions_->begin(); i != reductions_->end(); ++i) {
+    if (i->first->IsPhi()) {
+      i->first->ReplaceWith(ReduceAndExtractIfNeeded(i->second));
+    }
+  }
+
   // Remove the original loop by disconnecting the body block
   // and removing all instructions from the header.
   block->DisconnectAndDelete();
@@ -841,21 +867,10 @@
   vector_header_->AddInstruction(cond);
   vector_header_->AddInstruction(new (global_allocator_) HIf(cond));
   vector_index_ = phi;
+  vector_permanent_map_->clear();  // preserved over unrolling
   for (uint32_t u = 0; u < unroll; u++) {
-    // Clear map, leaving loop invariants setup during unrolling.
-    if (u == 0) {
-      vector_map_->clear();
-    } else {
-      for (auto i = vector_map_->begin(); i != vector_map_->end(); ) {
-        if (i->second->IsVecReplicateScalar()) {
-          DCHECK(node->loop_info->IsDefinedOutOfTheLoop(i->first));
-          ++i;
-        } else {
-          i = vector_map_->erase(i);
-        }
-      }
-    }
     // Generate instruction map.
+    vector_map_->clear();
     for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
       bool vectorized_def = VectorizeDef(node, it.Current(), /*generate_code*/ true);
       DCHECK(vectorized_def);
@@ -872,9 +887,17 @@
         }
       }
     }
+    // Generate the induction.
     vector_index_ = new (global_allocator_) HAdd(induc_type, vector_index_, step);
     Insert(vector_body_, vector_index_);
   }
+  // Finalize phi inputs for the reductions (if any).
+  for (auto i = reductions_->begin(); i != reductions_->end(); ++i) {
+    if (!i->first->IsPhi()) {
+      DCHECK(i->second->IsPhi());
+      GenerateVecReductionPhiInputs(i->second->AsPhi(), i->first);
+    }
+  }
   // Finalize phi inputs for the loop index.
   phi->AddInput(lo);
   phi->AddInput(vector_index_);
@@ -910,6 +933,23 @@
     }
     return false;
   }
+  // Accept a left-hand-side reduction for
+  // (1) supported vector type,
+  // (2) vectorizable right-hand-side value.
+  auto redit = reductions_->find(instruction);
+  if (redit != reductions_->end()) {
+    Primitive::Type type = instruction->GetType();
+    if (TrySetVectorType(type, &restrictions) &&
+        VectorizeUse(node, instruction, generate_code, type, restrictions)) {
+      if (generate_code) {
+        HInstruction* new_red = vector_map_->Get(instruction);
+        vector_permanent_map_->Put(new_red, vector_map_->Get(redit->second));
+        vector_permanent_map_->Overwrite(redit->second, new_red);
+      }
+      return true;
+    }
+    return false;
+  }
   // Branch back okay.
   if (instruction->IsGoto()) {
     return true;
@@ -965,6 +1005,21 @@
       }
       return true;
     }
+  } else if (instruction->IsPhi()) {
+    // Accept particular phi operations.
+    if (reductions_->find(instruction) != reductions_->end()) {
+      // Deal with vector restrictions.
+      if (HasVectorRestrictions(restrictions, kNoReduction)) {
+        return false;
+      }
+      // Accept a reduction.
+      if (generate_code) {
+        GenerateVecReductionPhi(instruction->AsPhi());
+      }
+      return true;
+    }
+    // TODO: accept right-hand-side induction?
+    return false;
   } else if (instruction->IsTypeConversion()) {
     // Accept particular type conversions.
     HTypeConversion* conversion = instruction->AsTypeConversion();
@@ -1155,14 +1210,14 @@
       switch (type) {
         case Primitive::kPrimBoolean:
         case Primitive::kPrimByte:
-          *restrictions |= kNoDiv;
+          *restrictions |= kNoDiv | kNoReduction;
           return TrySetVectorLength(8);
         case Primitive::kPrimChar:
         case Primitive::kPrimShort:
-          *restrictions |= kNoDiv | kNoStringCharAt;
+          *restrictions |= kNoDiv | kNoStringCharAt | kNoReduction;
           return TrySetVectorLength(4);
         case Primitive::kPrimInt:
-          *restrictions |= kNoDiv;
+          *restrictions |= kNoDiv | kNoReduction;
           return TrySetVectorLength(2);
         default:
           break;
@@ -1174,11 +1229,11 @@
       switch (type) {
         case Primitive::kPrimBoolean:
         case Primitive::kPrimByte:
-          *restrictions |= kNoDiv;
+          *restrictions |= kNoDiv | kNoReduction;
           return TrySetVectorLength(16);
         case Primitive::kPrimChar:
         case Primitive::kPrimShort:
-          *restrictions |= kNoDiv;
+          *restrictions |= kNoDiv | kNoReduction;
           return TrySetVectorLength(8);
         case Primitive::kPrimInt:
           *restrictions |= kNoDiv;
@@ -1187,8 +1242,10 @@
           *restrictions |= kNoDiv | kNoMul | kNoMinMax;
           return TrySetVectorLength(2);
         case Primitive::kPrimFloat:
+          *restrictions |= kNoReduction;
           return TrySetVectorLength(4);
         case Primitive::kPrimDouble:
+          *restrictions |= kNoReduction;
           return TrySetVectorLength(2);
         default:
           return false;
@@ -1200,11 +1257,12 @@
         switch (type) {
           case Primitive::kPrimBoolean:
           case Primitive::kPrimByte:
-            *restrictions |= kNoMul | kNoDiv | kNoShift | kNoAbs | kNoSignedHAdd | kNoUnroundedHAdd;
+            *restrictions |=
+                kNoMul | kNoDiv | kNoShift | kNoAbs | kNoSignedHAdd | kNoUnroundedHAdd | kNoReduction;
             return TrySetVectorLength(16);
           case Primitive::kPrimChar:
           case Primitive::kPrimShort:
-            *restrictions |= kNoDiv | kNoAbs | kNoSignedHAdd | kNoUnroundedHAdd;
+            *restrictions |= kNoDiv | kNoAbs | kNoSignedHAdd | kNoUnroundedHAdd | kNoReduction;
             return TrySetVectorLength(8);
           case Primitive::kPrimInt:
             *restrictions |= kNoDiv;
@@ -1213,10 +1271,10 @@
             *restrictions |= kNoMul | kNoDiv | kNoShr | kNoAbs | kNoMinMax;
             return TrySetVectorLength(2);
           case Primitive::kPrimFloat:
-            *restrictions |= kNoMinMax;  // -0.0 vs +0.0
+            *restrictions |= kNoMinMax | kNoReduction;  // minmax: -0.0 vs +0.0
             return TrySetVectorLength(4);
           case Primitive::kPrimDouble:
-            *restrictions |= kNoMinMax;  // -0.0 vs +0.0
+            *restrictions |= kNoMinMax | kNoReduction;  // minmax: -0.0 vs +0.0
             return TrySetVectorLength(2);
           default:
             break;
@@ -1228,23 +1286,23 @@
         switch (type) {
           case Primitive::kPrimBoolean:
           case Primitive::kPrimByte:
-            *restrictions |= kNoDiv;
+            *restrictions |= kNoDiv | kNoReduction;
             return TrySetVectorLength(16);
           case Primitive::kPrimChar:
           case Primitive::kPrimShort:
-            *restrictions |= kNoDiv | kNoStringCharAt;
+            *restrictions |= kNoDiv | kNoStringCharAt | kNoReduction;
             return TrySetVectorLength(8);
           case Primitive::kPrimInt:
-            *restrictions |= kNoDiv;
+            *restrictions |= kNoDiv | kNoReduction;
             return TrySetVectorLength(4);
           case Primitive::kPrimLong:
-            *restrictions |= kNoDiv;
+            *restrictions |= kNoDiv | kNoReduction;
             return TrySetVectorLength(2);
           case Primitive::kPrimFloat:
-            *restrictions |= kNoMinMax;  // min/max(x, NaN)
+            *restrictions |= kNoMinMax | kNoReduction;  // min/max(x, NaN)
             return TrySetVectorLength(4);
           case Primitive::kPrimDouble:
-            *restrictions |= kNoMinMax;  // min/max(x, NaN)
+            *restrictions |= kNoMinMax | kNoReduction;  // min/max(x, NaN)
             return TrySetVectorLength(2);
           default:
             break;
@@ -1256,23 +1314,23 @@
         switch (type) {
           case Primitive::kPrimBoolean:
           case Primitive::kPrimByte:
-            *restrictions |= kNoDiv;
+            *restrictions |= kNoDiv | kNoReduction;
             return TrySetVectorLength(16);
           case Primitive::kPrimChar:
           case Primitive::kPrimShort:
-            *restrictions |= kNoDiv | kNoStringCharAt;
+            *restrictions |= kNoDiv | kNoStringCharAt | kNoReduction;
             return TrySetVectorLength(8);
           case Primitive::kPrimInt:
-            *restrictions |= kNoDiv;
+            *restrictions |= kNoDiv | kNoReduction;
             return TrySetVectorLength(4);
           case Primitive::kPrimLong:
-            *restrictions |= kNoDiv;
+            *restrictions |= kNoDiv | kNoReduction;
             return TrySetVectorLength(2);
           case Primitive::kPrimFloat:
-            *restrictions |= kNoMinMax;  // min/max(x, NaN)
+            *restrictions |= kNoMinMax | kNoReduction;  // min/max(x, NaN)
             return TrySetVectorLength(4);
           case Primitive::kPrimDouble:
-            *restrictions |= kNoMinMax;  // min/max(x, NaN)
+            *restrictions |= kNoMinMax | kNoReduction;  // min/max(x, NaN)
             return TrySetVectorLength(2);
           default:
             break;
@@ -1305,9 +1363,16 @@
       return;
     }
     // In vector code, explicit scalar expansion is needed.
-    HInstruction* vector = new (global_allocator_) HVecReplicateScalar(
-        global_allocator_, org, type, vector_length_);
-    vector_map_->Put(org, Insert(vector_preheader_, vector));
+    HInstruction* vector = nullptr;
+    auto it = vector_permanent_map_->find(org);
+    if (it != vector_permanent_map_->end()) {
+      vector = it->second;  // reuse during unrolling
+    } else {
+      vector = new (global_allocator_) HVecReplicateScalar(
+          global_allocator_, org, type, vector_length_);
+      vector_permanent_map_->Put(org, Insert(vector_preheader_, vector));
+    }
+    vector_map_->Put(org, vector);
   }
 }
 
@@ -1362,6 +1427,78 @@
   vector_map_->Put(org, vector);
 }
 
+void HLoopOptimization::GenerateVecReductionPhi(HPhi* phi) {
+  DCHECK(reductions_->find(phi) != reductions_->end());
+  DCHECK(reductions_->Get(phi->InputAt(1)) == phi);
+  HInstruction* vector = nullptr;
+  if (vector_mode_ == kSequential) {
+    HPhi* new_phi = new (global_allocator_) HPhi(
+        global_allocator_, kNoRegNumber, 0, phi->GetType());
+    vector_header_->AddPhi(new_phi);
+    vector = new_phi;
+  } else {
+    // Link vector reduction back to prior unrolled update, or a first phi.
+    auto it = vector_permanent_map_->find(phi);
+    if (it != vector_permanent_map_->end()) {
+      vector = it->second;
+    } else {
+      HPhi* new_phi = new (global_allocator_) HPhi(
+          global_allocator_, kNoRegNumber, 0, HVecOperation::kSIMDType);
+      vector_header_->AddPhi(new_phi);
+      vector = new_phi;
+    }
+  }
+  vector_map_->Put(phi, vector);
+}
+
+void HLoopOptimization::GenerateVecReductionPhiInputs(HPhi* phi, HInstruction* reduction) {
+  HInstruction* new_phi = vector_map_->Get(phi);
+  HInstruction* new_init = reductions_->Get(phi);
+  HInstruction* new_red = vector_map_->Get(reduction);
+  // Link unrolled vector loop back to new phi.
+  for (; !new_phi->IsPhi(); new_phi = vector_permanent_map_->Get(new_phi)) {
+    DCHECK(new_phi->IsVecOperation());
+  }
+  // Prepare the new initialization.
+  if (vector_mode_ == kVector) {
+    // Generate a [initial, 0, .., 0] vector.
+    new_init = Insert(
+            vector_preheader_,
+            new (global_allocator_) HVecSetScalars(
+                global_allocator_, &new_init, phi->GetType(), vector_length_, 1));
+  } else {
+    new_init = ReduceAndExtractIfNeeded(new_init);
+  }
+  // Set the phi inputs.
+  DCHECK(new_phi->IsPhi());
+  new_phi->AsPhi()->AddInput(new_init);
+  new_phi->AsPhi()->AddInput(new_red);
+  // New feed value for next phi (safe mutation in iteration).
+  reductions_->find(phi)->second = new_phi;
+}
+
+HInstruction* HLoopOptimization::ReduceAndExtractIfNeeded(HInstruction* instruction) {
+  if (instruction->IsPhi()) {
+    HInstruction* input = instruction->InputAt(1);
+    if (input->IsVecOperation()) {
+      Primitive::Type type = input->AsVecOperation()->GetPackedType();
+      HBasicBlock* exit = instruction->GetBlock()->GetSuccessors()[0];
+      // Generate a vector reduction and scalar extract
+      //    x = REDUCE( [x_1, .., x_n] )
+      //    y = x_1
+      // along the exit of the defining loop.
+      HVecReduce::ReductionKind kind = GetReductionKind(input);
+      HInstruction* reduce = new (global_allocator_) HVecReduce(
+          global_allocator_, instruction, type, vector_length_, kind);
+      exit->InsertInstructionBefore(reduce, exit->GetFirstInstruction());
+      instruction = new (global_allocator_) HVecExtractScalar(
+          global_allocator_, reduce, type, vector_length_, 0);
+      exit->InsertInstructionAfter(instruction, reduce);
+    }
+  }
+  return instruction;
+}
+
 #define GENERATE_VEC(x, y) \
   if (vector_mode_ == kVector) { \
     vector = (x); \
@@ -1542,10 +1679,9 @@
   // Test for top level arithmetic shift right x >> 1 or logical shift right x >>> 1
   // (note whether the sign bit in wider precision is shifted in has no effect
   // on the narrow precision computed by the idiom).
-  int64_t distance = 0;
   if ((instruction->IsShr() ||
        instruction->IsUShr()) &&
-      IsInt64AndGet(instruction->InputAt(1), /*out*/ &distance) && distance == 1) {
+      IsInt64Value(instruction->InputAt(1), 1)) {
     // Test for (a + b + c) >> 1 for optional constant c.
     HInstruction* a = nullptr;
     HInstruction* b = nullptr;
@@ -1590,6 +1726,7 @@
                 vector_length_,
                 is_unsigned,
                 is_rounded));
+            MaybeRecordStat(stats_, MethodCompilationStat::kLoopVectorizedIdiom);
           } else {
             GenerateVecOp(instruction, vector_map_->Get(r), vector_map_->Get(s), type);
           }
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index 49be8a3..f347518 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -34,7 +34,8 @@
  public:
   HLoopOptimization(HGraph* graph,
                     CompilerDriver* compiler_driver,
-                    HInductionVarAnalysis* induction_analysis);
+                    HInductionVarAnalysis* induction_analysis,
+                    OptimizingCompilerStats* stats);
 
   void Run() OVERRIDE;
 
@@ -62,17 +63,18 @@
    * Vectorization restrictions (bit mask).
    */
   enum VectorRestrictions {
-    kNone            = 0,    // no restrictions
-    kNoMul           = 1,    // no multiplication
-    kNoDiv           = 2,    // no division
-    kNoShift         = 4,    // no shift
-    kNoShr           = 8,    // no arithmetic shift right
-    kNoHiBits        = 16,   // "wider" operations cannot bring in higher order bits
-    kNoSignedHAdd    = 32,   // no signed halving add
-    kNoUnroundedHAdd = 64,   // no unrounded halving add
-    kNoAbs           = 128,  // no absolute value
-    kNoMinMax        = 256,  // no min/max
-    kNoStringCharAt  = 512,  // no StringCharAt
+    kNone            = 0,        // no restrictions
+    kNoMul           = 1 << 0,   // no multiplication
+    kNoDiv           = 1 << 1,   // no division
+    kNoShift         = 1 << 2,   // no shift
+    kNoShr           = 1 << 3,   // no arithmetic shift right
+    kNoHiBits        = 1 << 4,   // "wider" operations cannot bring in higher order bits
+    kNoSignedHAdd    = 1 << 5,   // no signed halving add
+    kNoUnroundedHAdd = 1 << 6,   // no unrounded halving add
+    kNoAbs           = 1 << 7,   // no absolute value
+    kNoMinMax        = 1 << 8,   // no min/max
+    kNoStringCharAt  = 1 << 9,   // no StringCharAt
+    kNoReduction     = 1 << 10,  // no reduction
   };
 
   /*
@@ -155,6 +157,9 @@
                       HInstruction* opb,
                       HInstruction* offset,
                       Primitive::Type type);
+  void GenerateVecReductionPhi(HPhi* phi);
+  void GenerateVecReductionPhiInputs(HPhi* phi, HInstruction* reduction);
+  HInstruction* ReduceAndExtractIfNeeded(HInstruction* instruction);
   void GenerateVecOp(HInstruction* org,
                      HInstruction* opa,
                      HInstruction* opb,
@@ -253,6 +258,10 @@
   // Contents reside in phase-local heap memory.
   ArenaSafeMap<HInstruction*, HInstruction*>* vector_map_;
 
+  // Permanent mapping used during vectorization synthesis.
+  // Contents reside in phase-local heap memory.
+  ArenaSafeMap<HInstruction*, HInstruction*>* vector_permanent_map_;
+
   // Temporary vectorization bookkeeping.
   VectorMode vector_mode_;  // synthesis mode
   HBasicBlock* vector_preheader_;  // preheader of the new loop
diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc
index b5b03d8..1c5603d 100644
--- a/compiler/optimizing/loop_optimization_test.cc
+++ b/compiler/optimizing/loop_optimization_test.cc
@@ -31,7 +31,7 @@
         allocator_(&pool_),
         graph_(CreateGraph(&allocator_)),
         iva_(new (&allocator_) HInductionVarAnalysis(graph_)),
-        loop_opt_(new (&allocator_) HLoopOptimization(graph_, nullptr, iva_)) {
+        loop_opt_(new (&allocator_) HLoopOptimization(graph_, nullptr, iva_, nullptr)) {
     BuildGraph();
   }
 
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index f60d532..869fdd4 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1374,7 +1374,8 @@
   M(UShr, BinaryOperation)                                              \
   M(Xor, BinaryOperation)                                               \
   M(VecReplicateScalar, VecUnaryOperation)                              \
-  M(VecSumReduce, VecUnaryOperation)                                    \
+  M(VecExtractScalar, VecUnaryOperation)                                \
+  M(VecReduce, VecUnaryOperation)                                       \
   M(VecCnv, VecUnaryOperation)                                          \
   M(VecNeg, VecUnaryOperation)                                          \
   M(VecAbs, VecUnaryOperation)                                          \
@@ -7030,6 +7031,17 @@
   return false;
 }
 
+// Returns true iff instruction is the given integral constant.
+inline bool IsInt64Value(HInstruction* instruction, int64_t value) {
+  int64_t val = 0;
+  return IsInt64AndGet(instruction, &val) && val == value;
+}
+
+// Returns true iff instruction is a zero bit pattern.
+inline bool IsZeroBitPattern(HInstruction* instruction) {
+  return instruction->IsConstant() && instruction->AsConstant()->IsZeroBitPattern();
+}
+
 #define INSTRUCTION_TYPE_CHECK(type, super)                                    \
   inline bool HInstruction::Is##type() const { return GetKind() == k##type; }  \
   inline const H##type* HInstruction::As##type() const {                       \
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index 6261171..886d75e 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -63,6 +63,10 @@
 // GetVectorLength() x GetPackedType() operations simultaneously.
 class HVecOperation : public HVariableInputSizeInstruction {
  public:
+  // A SIMD operation looks like a FPU location.
+  // TODO: we could introduce SIMD types in HIR.
+  static constexpr Primitive::Type kSIMDType = Primitive::kPrimDouble;
+
   HVecOperation(ArenaAllocator* arena,
                 Primitive::Type packed_type,
                 SideEffects side_effects,
@@ -89,10 +93,9 @@
     return vector_length_ * Primitive::ComponentSize(GetPackedType());
   }
 
-  // Returns the type of the vector operation: a SIMD operation looks like a FPU location.
-  // TODO: we could introduce SIMD types in HIR.
+  // Returns the type of the vector operation.
   Primitive::Type GetType() const OVERRIDE {
-    return Primitive::kPrimDouble;
+    return kSIMDType;
   }
 
   // Returns the true component type packed in a vector.
@@ -220,8 +223,11 @@
   DISALLOW_COPY_AND_ASSIGN(HVecMemoryOperation);
 };
 
-// Packed type consistency checker (same vector length integral types may mix freely).
+// Packed type consistency checker ("same vector length" integral types may mix freely).
 inline static bool HasConsistentPackedTypes(HInstruction* input, Primitive::Type type) {
+  if (input->IsPhi()) {
+    return input->GetType() == HVecOperation::kSIMDType;  // carries SIMD
+  }
   DCHECK(input->IsVecOperation());
   Primitive::Type input_type = input->AsVecOperation()->GetPackedType();
   switch (input_type) {
@@ -265,27 +271,77 @@
   DISALLOW_COPY_AND_ASSIGN(HVecReplicateScalar);
 };
 
-// Sum-reduces the given vector into a shorter vector (m < n) or scalar (m = 1),
-// viz. sum-reduce[ x1, .. , xn ] = [ y1, .., ym ], where yi = sum_j x_j.
-class HVecSumReduce FINAL : public HVecUnaryOperation {
-  HVecSumReduce(ArenaAllocator* arena,
-                HInstruction* input,
-                Primitive::Type packed_type,
-                size_t vector_length,
-                uint32_t dex_pc = kNoDexPc)
+// Extracts a particular scalar from the given vector,
+// viz. extract[ x1, .. , xn ] = x_i.
+//
+// TODO: for now only i == 1 case supported.
+class HVecExtractScalar FINAL : public HVecUnaryOperation {
+ public:
+  HVecExtractScalar(ArenaAllocator* arena,
+                    HInstruction* input,
+                    Primitive::Type packed_type,
+                    size_t vector_length,
+                    size_t index,
+                    uint32_t dex_pc = kNoDexPc)
       : HVecUnaryOperation(arena, input, packed_type, vector_length, dex_pc) {
     DCHECK(HasConsistentPackedTypes(input, packed_type));
+    DCHECK_LT(index, vector_length);
+    DCHECK_EQ(index, 0u);
+  }
+
+  // Yields a single component in the vector.
+  Primitive::Type GetType() const OVERRIDE {
+    return GetPackedType();
+  }
+
+  // An extract needs to stay in place, since SIMD registers are not
+  // kept alive across vector loop boundaries (yet).
+  bool CanBeMoved() const OVERRIDE { return false; }
+
+  DECLARE_INSTRUCTION(VecExtractScalar);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecExtractScalar);
+};
+
+// Reduces the given vector into the first element as sum/min/max,
+// viz. sum-reduce[ x1, .. , xn ] = [ y, ---- ], where y = sum xi
+// and the "-" denotes "don't care" (implementation dependent).
+class HVecReduce FINAL : public HVecUnaryOperation {
+ public:
+  enum ReductionKind {
+    kSum = 1,
+    kMin = 2,
+    kMax = 3
+  };
+
+  HVecReduce(ArenaAllocator* arena,
+             HInstruction* input,
+             Primitive::Type packed_type,
+             size_t vector_length,
+             ReductionKind kind,
+             uint32_t dex_pc = kNoDexPc)
+      : HVecUnaryOperation(arena, input, packed_type, vector_length, dex_pc),
+        kind_(kind) {
+    DCHECK(HasConsistentPackedTypes(input, packed_type));
   }
 
-  // TODO: probably integral promotion
-  Primitive::Type GetType() const OVERRIDE { return GetPackedType(); }
+  ReductionKind GetKind() const { return kind_; }
 
   bool CanBeMoved() const OVERRIDE { return true; }
 
-  DECLARE_INSTRUCTION(VecSumReduce);
+  bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
+    DCHECK(other->IsVecReduce());
+    const HVecReduce* o = other->AsVecReduce();
+    return HVecOperation::InstructionDataEquals(o) && GetKind() == o->GetKind();
+  }
+
+  DECLARE_INSTRUCTION(VecReduce);
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(HVecSumReduce);
+  const ReductionKind kind_;
+
+  DISALLOW_COPY_AND_ASSIGN(HVecReduce);
 };
 
 // Converts every component in the vector,
@@ -754,20 +810,23 @@
 //
 
 // Assigns the given scalar elements to a vector,
-// viz. set( array(x1, .., xn) ) = [ x1, .. , xn ].
+// viz. set( array(x1, .., xn) ) = [ x1, .. ,           xn ] if n == m,
+//      set( array(x1, .., xm) ) = [ x1, .. , xm, 0, .., 0 ] if m <  n.
 class HVecSetScalars FINAL : public HVecOperation {
+ public:
   HVecSetScalars(ArenaAllocator* arena,
                  HInstruction** scalars,  // array
                  Primitive::Type packed_type,
                  size_t vector_length,
+                 size_t number_of_scalars,
                  uint32_t dex_pc = kNoDexPc)
       : HVecOperation(arena,
                       packed_type,
                       SideEffects::None(),
-                      /* number_of_inputs */ vector_length,
+                      number_of_scalars,
                       vector_length,
                       dex_pc) {
-    for (size_t i = 0; i < vector_length; i++) {
+    for (size_t i = 0; i < number_of_scalars; i++) {
       DCHECK(!scalars[i]->IsVecOperation());
       SetRawInputAt(0, scalars[i]);
     }
diff --git a/compiler/optimizing/nodes_vector_test.cc b/compiler/optimizing/nodes_vector_test.cc
index 0238ea4..5a56a2c 100644
--- a/compiler/optimizing/nodes_vector_test.cc
+++ b/compiler/optimizing/nodes_vector_test.cc
@@ -332,4 +332,32 @@
   EXPECT_FALSE(v1->Equals(v3));  // different vector lengths
 }
 
+TEST_F(NodesVectorTest, VectorKindMattersOnReduce) {
+  HVecOperation* v0 = new (&allocator_)
+      HVecReplicateScalar(&allocator_, parameter_, Primitive::kPrimInt, 4);
+
+  HVecReduce* v1 = new (&allocator_) HVecReduce(
+      &allocator_, v0, Primitive::kPrimInt, 4, HVecReduce::kSum);
+  HVecReduce* v2 = new (&allocator_) HVecReduce(
+      &allocator_, v0, Primitive::kPrimInt, 4, HVecReduce::kMin);
+  HVecReduce* v3 = new (&allocator_) HVecReduce(
+      &allocator_, v0, Primitive::kPrimInt, 4, HVecReduce::kMax);
+
+  EXPECT_FALSE(v0->CanBeMoved());
+  EXPECT_TRUE(v1->CanBeMoved());
+  EXPECT_TRUE(v2->CanBeMoved());
+  EXPECT_TRUE(v3->CanBeMoved());
+
+  EXPECT_EQ(HVecReduce::kSum, v1->GetKind());
+  EXPECT_EQ(HVecReduce::kMin, v2->GetKind());
+  EXPECT_EQ(HVecReduce::kMax, v3->GetKind());
+
+  EXPECT_TRUE(v1->Equals(v1));
+  EXPECT_TRUE(v2->Equals(v2));
+  EXPECT_TRUE(v3->Equals(v3));
+
+  EXPECT_FALSE(v1->Equals(v2));  // different kinds
+  EXPECT_FALSE(v1->Equals(v3));
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index e98c97c..71d91ae 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -509,7 +509,7 @@
   } else if (opt_name == SideEffectsAnalysis::kSideEffectsAnalysisPassName) {
     return new (arena) SideEffectsAnalysis(graph);
   } else if (opt_name == HLoopOptimization::kLoopOptimizationPassName) {
-    return new (arena) HLoopOptimization(graph, driver, most_recent_induction);
+    return new (arena) HLoopOptimization(graph, driver, most_recent_induction, stats);
   } else if (opt_name == CHAGuardOptimization::kCHAGuardOptimizationPassName) {
     return new (arena) CHAGuardOptimization(graph);
   } else if (opt_name == CodeSinking::kCodeSinkingPassName) {
@@ -770,7 +770,7 @@
   LICM* licm = new (arena) LICM(graph, *side_effects1, stats);
   HInductionVarAnalysis* induction = new (arena) HInductionVarAnalysis(graph);
   BoundsCheckElimination* bce = new (arena) BoundsCheckElimination(graph, *side_effects1, induction);
-  HLoopOptimization* loop = new (arena) HLoopOptimization(graph, driver, induction);
+  HLoopOptimization* loop = new (arena) HLoopOptimization(graph, driver, induction, stats);
   LoadStoreAnalysis* lsa = new (arena) LoadStoreAnalysis(graph);
   LoadStoreElimination* lse = new (arena) LoadStoreElimination(graph, *side_effects2, *lsa, stats);
   HSharpening* sharpening = new (arena) HSharpening(
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index d6da73c..ff49056 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -63,6 +63,8 @@
   kBooleanSimplified,
   kIntrinsicRecognized,
   kLoopInvariantMoved,
+  kLoopVectorized,
+  kLoopVectorizedIdiom,
   kSelectGenerated,
   kRemovedInstanceOf,
   kInlinedInvokeVirtualOrInterface,
@@ -183,6 +185,8 @@
       case kBooleanSimplified : name = "BooleanSimplified"; break;
       case kIntrinsicRecognized : name = "IntrinsicRecognized"; break;
       case kLoopInvariantMoved : name = "LoopInvariantMoved"; break;
+      case kLoopVectorized : name = "LoopVectorized"; break;
+      case kLoopVectorizedIdiom : name = "LoopVectorizedIdiom"; break;
       case kSelectGenerated : name = "SelectGenerated"; break;
       case kRemovedInstanceOf: name = "RemovedInstanceOf"; break;
       case kInlinedInvokeVirtualOrInterface: name = "InlinedInvokeVirtualOrInterface"; break;
diff --git a/compiler/optimizing/scheduler_arm64.cc b/compiler/optimizing/scheduler_arm64.cc
index 510619f..1d9d28a 100644
--- a/compiler/optimizing/scheduler_arm64.cc
+++ b/compiler/optimizing/scheduler_arm64.cc
@@ -215,12 +215,12 @@
   last_visited_latency_ = kArm64SIMDReplicateOpLatency;
 }
 
-void SchedulingLatencyVisitorARM64::VisitVecSetScalars(HVecSetScalars* instr) {
-  LOG(FATAL) << "Unsupported SIMD instruction " << instr->GetId();
+void SchedulingLatencyVisitorARM64::VisitVecExtractScalar(HVecExtractScalar* instr) {
+  HandleSimpleArithmeticSIMD(instr);
 }
 
-void SchedulingLatencyVisitorARM64::VisitVecSumReduce(HVecSumReduce* instr) {
-  LOG(FATAL) << "Unsupported SIMD instruction " << instr->GetId();
+void SchedulingLatencyVisitorARM64::VisitVecReduce(HVecReduce* instr) {
+  HandleSimpleArithmeticSIMD(instr);
 }
 
 void SchedulingLatencyVisitorARM64::VisitVecCnv(HVecCnv* instr ATTRIBUTE_UNUSED) {
@@ -283,8 +283,8 @@
   last_visited_latency_ = kArm64SIMDIntegerOpLatency;
 }
 
-void SchedulingLatencyVisitorARM64::VisitVecAndNot(HVecAndNot* instr) {
-  LOG(FATAL) << "Unsupported SIMD instruction " << instr->GetId();
+void SchedulingLatencyVisitorARM64::VisitVecAndNot(HVecAndNot* instr ATTRIBUTE_UNUSED) {
+  last_visited_latency_ = kArm64SIMDIntegerOpLatency;
 }
 
 void SchedulingLatencyVisitorARM64::VisitVecOr(HVecOr* instr ATTRIBUTE_UNUSED) {
@@ -307,6 +307,10 @@
   HandleSimpleArithmeticSIMD(instr);
 }
 
+void SchedulingLatencyVisitorARM64::VisitVecSetScalars(HVecSetScalars* instr) {
+  HandleSimpleArithmeticSIMD(instr);
+}
+
 void SchedulingLatencyVisitorARM64::VisitVecMultiplyAccumulate(
     HVecMultiplyAccumulate* instr ATTRIBUTE_UNUSED) {
   last_visited_latency_ = kArm64SIMDMulIntegerLatency;
diff --git a/compiler/optimizing/scheduler_arm64.h b/compiler/optimizing/scheduler_arm64.h
index 63d5b7d..e1a80ec 100644
--- a/compiler/optimizing/scheduler_arm64.h
+++ b/compiler/optimizing/scheduler_arm64.h
@@ -83,8 +83,8 @@
   M(SuspendCheck         , unused)                   \
   M(TypeConversion       , unused)                   \
   M(VecReplicateScalar   , unused)                   \
-  M(VecSetScalars        , unused)                   \
-  M(VecSumReduce         , unused)                   \
+  M(VecExtractScalar     , unused)                   \
+  M(VecReduce            , unused)                   \
   M(VecCnv               , unused)                   \
   M(VecNeg               , unused)                   \
   M(VecAbs               , unused)                   \
@@ -103,6 +103,7 @@
   M(VecShl               , unused)                   \
   M(VecShr               , unused)                   \
   M(VecUShr              , unused)                   \
+  M(VecSetScalars        , unused)                   \
   M(VecMultiplyAccumulate, unused)                   \
   M(VecLoad              , unused)                   \
   M(VecStore             , unused)
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index 6c0d492..277f611 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -810,11 +810,11 @@
   }
 
   static jvmtiError GetObjectMonitorUsage(jvmtiEnv* env,
-                                          jobject object ATTRIBUTE_UNUSED,
-                                          jvmtiMonitorUsage* info_ptr ATTRIBUTE_UNUSED) {
+                                          jobject object,
+                                          jvmtiMonitorUsage* info_ptr) {
     ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_monitor_info);
-    return ERR(NOT_IMPLEMENTED);
+    return ObjectUtil::GetObjectMonitorUsage(env, object, info_ptr);
   }
 
   static jvmtiError GetFieldName(jvmtiEnv* env,
diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
index d74e25b..d3f52f6 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -226,7 +226,7 @@
     .can_get_synthetic_attribute                     = 1,
     .can_get_owned_monitor_info                      = 1,
     .can_get_current_contended_monitor               = 0,
-    .can_get_monitor_info                            = 0,
+    .can_get_monitor_info                            = 1,
     .can_pop_frame                                   = 0,
     .can_redefine_classes                            = 1,
     .can_signal_thread                               = 0,
@@ -236,7 +236,7 @@
     .can_access_local_variables                      = 1,
     .can_maintain_original_method_order              = 1,
     .can_generate_single_step_events                 = 1,
-    .can_generate_exception_events                   = 0,
+    .can_generate_exception_events                   = 1,
     .can_generate_frame_pop_events                   = 1,
     .can_generate_breakpoint_events                  = 1,
     .can_suspend                                     = 1,
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index acef682..c41e15e 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -48,6 +48,7 @@
 #include "nativehelper/ScopedLocalRef.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
+#include "stack.h"
 #include "thread-inl.h"
 #include "thread_list.h"
 #include "ti_phase.h"
@@ -552,10 +553,123 @@
     }
   }
 
+  static void FindCatchMethodsFromThrow(art::Thread* self,
+                                        art::Handle<art::mirror::Throwable> exception,
+                                        /*out*/ art::ArtMethod** out_method,
+                                        /*out*/ uint32_t* dex_pc)
+      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    // Finds the location where this exception will most likely be caught. We ignore intervening
+    // native frames (which could catch the exception) and return the closest java frame with a
+    // compatible catch statement.
+    class CatchLocationFinder FINAL : public art::StackVisitor {
+     public:
+      CatchLocationFinder(art::Thread* target,
+                          art::Handle<art::mirror::Class> exception_class,
+                          art::Context* context,
+                          /*out*/ art::ArtMethod** out_catch_method,
+                          /*out*/ uint32_t* out_catch_pc)
+          REQUIRES_SHARED(art::Locks::mutator_lock_)
+        : StackVisitor(target, context, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+          exception_class_(exception_class),
+          catch_method_ptr_(out_catch_method),
+          catch_dex_pc_ptr_(out_catch_pc) {}
+
+      bool VisitFrame() OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+        art::ArtMethod* method = GetMethod();
+        DCHECK(method != nullptr);
+        if (method->IsRuntimeMethod()) {
+          return true;
+        }
+
+        if (!method->IsNative()) {
+          uint32_t cur_dex_pc = GetDexPc();
+          if (cur_dex_pc == art::DexFile::kDexNoIndex) {
+            // This frame looks opaque. Just keep on going.
+            return true;
+          }
+          bool has_no_move_exception = false;
+          uint32_t found_dex_pc = method->FindCatchBlock(
+              exception_class_, cur_dex_pc, &has_no_move_exception);
+          if (found_dex_pc != art::DexFile::kDexNoIndex) {
+            // We found the catch. Store the result and return.
+            *catch_method_ptr_ = method;
+            *catch_dex_pc_ptr_ = found_dex_pc;
+            return false;
+          }
+        }
+        return true;
+      }
+
+     private:
+      art::Handle<art::mirror::Class> exception_class_;
+      art::ArtMethod** catch_method_ptr_;
+      uint32_t* catch_dex_pc_ptr_;
+
+      DISALLOW_COPY_AND_ASSIGN(CatchLocationFinder);
+    };
+
+    art::StackHandleScope<1> hs(self);
+    *out_method = nullptr;
+    *dex_pc = 0;
+    std::unique_ptr<art::Context> context(art::Context::Create());
+
+    CatchLocationFinder clf(self,
+                            hs.NewHandle(exception->GetClass()),
+                            context.get(),
+                            /*out*/ out_method,
+                            /*out*/ dex_pc);
+    clf.WalkStack(/* include_transitions */ false);
+  }
+
   // Call-back when an exception is thrown.
-  void ExceptionThrown(art::Thread* self ATTRIBUTE_UNUSED,
-                       art::Handle<art::mirror::Throwable> exception_object ATTRIBUTE_UNUSED)
+  void ExceptionThrown(art::Thread* self, art::Handle<art::mirror::Throwable> exception_object)
       REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE {
+    DCHECK(self->IsExceptionThrownByCurrentMethod(exception_object.Get()));
+    // The instrumentation events get rid of this for us.
+    DCHECK(!self->IsExceptionPending());
+    if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kException)) {
+      art::JNIEnvExt* jnienv = self->GetJniEnv();
+      art::ArtMethod* catch_method;
+      uint32_t catch_pc;
+      FindCatchMethodsFromThrow(self, exception_object, &catch_method, &catch_pc);
+      uint32_t dex_pc = 0;
+      art::ArtMethod* method = self->GetCurrentMethod(&dex_pc,
+                                                      /* check_suspended */ true,
+                                                      /* abort_on_error */ art::kIsDebugBuild);
+      ScopedLocalRef<jobject> exception(jnienv,
+                                        AddLocalRef<jobject>(jnienv, exception_object.Get()));
+      RunEventCallback<ArtJvmtiEvent::kException>(
+          self,
+          jnienv,
+          art::jni::EncodeArtMethod(method),
+          static_cast<jlocation>(dex_pc),
+          exception.get(),
+          art::jni::EncodeArtMethod(catch_method),
+          static_cast<jlocation>(catch_pc));
+    }
+    return;
+  }
+
+  // Call-back when an exception is handled.
+  void ExceptionHandled(art::Thread* self, art::Handle<art::mirror::Throwable> exception_object)
+      REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE {
+    // Since the exception has already been handled there shouldn't be one pending.
+    DCHECK(!self->IsExceptionPending());
+    if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kExceptionCatch)) {
+      art::JNIEnvExt* jnienv = self->GetJniEnv();
+      uint32_t dex_pc;
+      art::ArtMethod* method = self->GetCurrentMethod(&dex_pc,
+                                                      /* check_suspended */ true,
+                                                      /* abort_on_error */ art::kIsDebugBuild);
+      ScopedLocalRef<jobject> exception(jnienv,
+                                        AddLocalRef<jobject>(jnienv, exception_object.Get()));
+      RunEventCallback<ArtJvmtiEvent::kExceptionCatch>(
+          self,
+          jnienv,
+          art::jni::EncodeArtMethod(method),
+          static_cast<jlocation>(dex_pc),
+          exception.get());
+    }
     return;
   }
 
@@ -598,6 +712,10 @@
       return art::instrumentation::Instrumentation::kDexPcMoved;
     case ArtJvmtiEvent::kFramePop:
       return art::instrumentation::Instrumentation::kWatchedFramePop;
+    case ArtJvmtiEvent::kException:
+      return art::instrumentation::Instrumentation::kExceptionThrown;
+    case ArtJvmtiEvent::kExceptionCatch:
+      return art::instrumentation::Instrumentation::kExceptionHandled;
     default:
       LOG(FATAL) << "Unknown event ";
       return 0;
@@ -677,6 +795,8 @@
     case ArtJvmtiEvent::kMethodExit:
     case ArtJvmtiEvent::kFieldAccess:
     case ArtJvmtiEvent::kFieldModification:
+    case ArtJvmtiEvent::kException:
+    case ArtJvmtiEvent::kExceptionCatch:
       SetupTraceListener(method_trace_listener_.get(), event, enable);
       return;
 
diff --git a/openjdkjvmti/ti_object.cc b/openjdkjvmti/ti_object.cc
index 2506aca..89ce352 100644
--- a/openjdkjvmti/ti_object.cc
+++ b/openjdkjvmti/ti_object.cc
@@ -35,6 +35,8 @@
 #include "mirror/object-inl.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-current-inl.h"
+#include "thread_list.h"
+#include "ti_thread.h"
 
 namespace openjdkjvmti {
 
@@ -73,4 +75,59 @@
   return ERR(NONE);
 }
 
+jvmtiError ObjectUtil::GetObjectMonitorUsage(
+    jvmtiEnv* baseenv, jobject obj, jvmtiMonitorUsage* usage) {
+  ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(baseenv);
+  if (obj == nullptr) {
+    return ERR(INVALID_OBJECT);
+  }
+  if (usage == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+  art::Thread* self = art::Thread::Current();
+  ThreadUtil::SuspendCheck(self);
+  art::JNIEnvExt* jni = self->GetJniEnv();
+  std::vector<jthread> wait;
+  std::vector<jthread> notify_wait;
+  {
+    art::ScopedObjectAccess soa(self);      // Now we know we have the shared lock.
+    art::ScopedThreadSuspension sts(self, art::kNative);
+    art::ScopedSuspendAll ssa("GetObjectMonitorUsage", /*long_suspend*/false);
+    art::ObjPtr<art::mirror::Object> target(self->DecodeJObject(obj));
+    // This gets the list of threads trying to lock or wait on the monitor.
+    art::MonitorInfo info(target.Ptr());
+    usage->owner = info.owner_ != nullptr ?
+        jni->AddLocalReference<jthread>(info.owner_->GetPeerFromOtherThread()) : nullptr;
+    usage->entry_count = info.entry_count_;
+    for (art::Thread* thd : info.waiters_) {
+      // RI seems to consider waiting for notify to be included in those waiting to acquire the
+      // monitor. We will match this behavior.
+      notify_wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread()));
+      wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread()));
+    }
+    {
+      // Scan all threads to see which are waiting on this particular monitor.
+      art::MutexLock tll(self, *art::Locks::thread_list_lock_);
+      for (art::Thread* thd : art::Runtime::Current()->GetThreadList()->GetList()) {
+        if (thd != info.owner_ && target.Ptr() == thd->GetMonitorEnterObject()) {
+          wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread()));
+        }
+      }
+    }
+  }
+  usage->waiter_count = wait.size();
+  usage->notify_waiter_count = notify_wait.size();
+  jvmtiError ret = CopyDataIntoJvmtiBuffer(env,
+                                           reinterpret_cast<const unsigned char*>(wait.data()),
+                                           wait.size() * sizeof(jthread),
+                                           reinterpret_cast<unsigned char**>(&usage->waiters));
+  if (ret != OK) {
+    return ret;
+  }
+  return CopyDataIntoJvmtiBuffer(env,
+                                 reinterpret_cast<const unsigned char*>(notify_wait.data()),
+                                 notify_wait.size() * sizeof(jthread),
+                                 reinterpret_cast<unsigned char**>(&usage->notify_waiters));
+}
+
 }  // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_object.h b/openjdkjvmti/ti_object.h
index fa3bd0f..977ec39 100644
--- a/openjdkjvmti/ti_object.h
+++ b/openjdkjvmti/ti_object.h
@@ -42,6 +42,8 @@
   static jvmtiError GetObjectSize(jvmtiEnv* env, jobject object, jlong* size_ptr);
 
   static jvmtiError GetObjectHashCode(jvmtiEnv* env, jobject object, jint* hash_code_ptr);
+
+  static jvmtiError GetObjectMonitorUsage(jvmtiEnv* env, jobject object, jvmtiMonitorUsage* usage);
 };
 
 }  // namespace openjdkjvmti
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 71b803d..6144869 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -443,6 +443,7 @@
         "base/mutex.h",
         "debugger.h",
         "base/unix_file/fd_file.h",
+        "class_status.h",
         "dex_file.h",
         "dex_file_layout.h",
         "dex_instruction.h",
@@ -463,7 +464,6 @@
         "jdwp/jdwp.h",
         "jdwp/jdwp_constants.h",
         "lock_word.h",
-        "mirror/class.h",
         "oat.h",
         "object_callbacks.h",
         "process_state.h",
diff --git a/runtime/aot_class_linker.cc b/runtime/aot_class_linker.cc
index 9396565..a62cbec 100644
--- a/runtime/aot_class_linker.cc
+++ b/runtime/aot_class_linker.cc
@@ -17,6 +17,7 @@
 #include "aot_class_linker.h"
 
 #include "class_reference.h"
+#include "class_status.h"
 #include "compiler_callbacks.h"
 #include "handle_scope-inl.h"
 #include "mirror/class-inl.h"
@@ -75,10 +76,19 @@
                                                                std::string* error_msg) {
   Runtime* const runtime = Runtime::Current();
   CompilerCallbacks* callbacks = runtime->GetCompilerCallbacks();
-  if (callbacks->CanAssumeVerified(ClassReference(&klass->GetDexFile(),
-                                                  klass->GetDexClassDefIndex()))) {
+  ClassStatus old_status = callbacks->GetPreviousClassState(
+      ClassReference(&klass->GetDexFile(), klass->GetDexClassDefIndex()));
+  // Was it verified? Report no failure.
+  if (old_status >= ClassStatus::kStatusVerified) {
     return verifier::FailureKind::kNoFailure;
   }
+  // Does it need to be verified at runtime? Report soft failure.
+  if (old_status >= ClassStatus::kStatusRetryVerificationAtRuntime) {
+    // Error messages from here are only reported through -verbose:class. It is not worth it to
+    // create a message.
+    return verifier::FailureKind::kSoftFailure;
+  }
+  // Do the actual work.
   return ClassLinker::PerformClassVerification(self, klass, log_level, error_msg);
 }
 
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d22482f..efef975 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1189,219 +1189,41 @@
   }
 };
 
-// Copies data from one array to another array at the same position
-// if pred returns false. If there is a page of continuous data in
-// the src array for which pred consistently returns true then
-// corresponding page in the dst array will not be touched.
-// This should reduce number of allocated physical pages.
-template <class T, class NullPred>
-static void CopyNonNull(const T* src, size_t count, T* dst, const NullPred& pred) {
-  for (size_t i = 0; i < count; ++i) {
-    if (!pred(src[i])) {
-      dst[i] = src[i];
-    }
-  }
-}
-
-template <typename T>
-static void CopyDexCachePairs(const std::atomic<mirror::DexCachePair<T>>* src,
-                              size_t count,
-                              std::atomic<mirror::DexCachePair<T>>* dst) {
-  DCHECK_NE(count, 0u);
-  DCHECK(!src[0].load(std::memory_order_relaxed).object.IsNull() ||
-         src[0].load(std::memory_order_relaxed).index != 0u);
-  for (size_t i = 0; i < count; ++i) {
-    DCHECK_EQ(dst[i].load(std::memory_order_relaxed).index, 0u);
-    DCHECK(dst[i].load(std::memory_order_relaxed).object.IsNull());
-    mirror::DexCachePair<T> source = src[i].load(std::memory_order_relaxed);
-    if (source.index != 0u || !source.object.IsNull()) {
-      dst[i].store(source, std::memory_order_relaxed);
-    }
-  }
-}
-
-template <typename T>
-static void CopyNativeDexCachePairs(std::atomic<mirror::NativeDexCachePair<T>>* src,
-                                    size_t count,
-                                    std::atomic<mirror::NativeDexCachePair<T>>* dst,
-                                    PointerSize pointer_size) {
-  DCHECK_NE(count, 0u);
-  DCHECK(mirror::DexCache::GetNativePairPtrSize(src, 0, pointer_size).object != nullptr ||
-         mirror::DexCache::GetNativePairPtrSize(src, 0, pointer_size).index != 0u);
-  for (size_t i = 0; i < count; ++i) {
-    DCHECK_EQ(mirror::DexCache::GetNativePairPtrSize(dst, i, pointer_size).index, 0u);
-    DCHECK(mirror::DexCache::GetNativePairPtrSize(dst, i, pointer_size).object == nullptr);
-    mirror::NativeDexCachePair<T> source =
-        mirror::DexCache::GetNativePairPtrSize(src, i, pointer_size);
-    if (source.index != 0u || source.object != nullptr) {
-      mirror::DexCache::SetNativePairPtrSize(dst, i, source, pointer_size);
-    }
-  }
-}
-
 // new_class_set is the set of classes that were read from the class table section in the image.
 // If there was no class table section, it is null.
 // Note: using a class here to avoid having to make ClassLinker internals public.
 class AppImageClassLoadersAndDexCachesHelper {
  public:
-  static bool Update(
+  static void Update(
       ClassLinker* class_linker,
       gc::space::ImageSpace* space,
       Handle<mirror::ClassLoader> class_loader,
       Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches,
-      ClassTable::ClassSet* new_class_set,
-      bool* out_forward_dex_cache_array,
-      std::string* out_error_msg)
+      ClassTable::ClassSet* new_class_set)
       REQUIRES(!Locks::dex_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 };
 
-bool AppImageClassLoadersAndDexCachesHelper::Update(
+void AppImageClassLoadersAndDexCachesHelper::Update(
     ClassLinker* class_linker,
     gc::space::ImageSpace* space,
     Handle<mirror::ClassLoader> class_loader,
     Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches,
-    ClassTable::ClassSet* new_class_set,
-    bool* out_forward_dex_cache_array,
-    std::string* out_error_msg)
+    ClassTable::ClassSet* new_class_set)
     REQUIRES(!Locks::dex_lock_)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  DCHECK(out_forward_dex_cache_array != nullptr);
-  DCHECK(out_error_msg != nullptr);
-  PointerSize image_pointer_size = class_linker->GetImagePointerSize();
   Thread* const self = Thread::Current();
   gc::Heap* const heap = Runtime::Current()->GetHeap();
   const ImageHeader& header = space->GetImageHeader();
   {
-    // Add image classes into the class table for the class loader, and fixup the dex caches and
-    // class loader fields.
+    // Register dex caches with the class loader.
     WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
-    // Dex cache array fixup is all or nothing, we must reject app images that have mixed since we
-    // rely on clobering the dex cache arrays in the image to forward to bss.
-    size_t num_dex_caches_with_bss_arrays = 0;
     const size_t num_dex_caches = dex_caches->GetLength();
     for (size_t i = 0; i < num_dex_caches; i++) {
-      ObjPtr<mirror::DexCache> const dex_cache = dex_caches->Get(i);
-      const DexFile* const dex_file = dex_cache->GetDexFile();
-      const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
-      if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
-        ++num_dex_caches_with_bss_arrays;
-      }
-    }
-    *out_forward_dex_cache_array = num_dex_caches_with_bss_arrays != 0;
-    if (*out_forward_dex_cache_array) {
-      if (num_dex_caches_with_bss_arrays != num_dex_caches) {
-        // Reject application image since we cannot forward only some of the dex cache arrays.
-        // TODO: We could get around this by having a dedicated forwarding slot. It should be an
-        // uncommon case.
-        *out_error_msg = StringPrintf("Dex caches in bss does not match total: %zu vs %zu",
-                                      num_dex_caches_with_bss_arrays,
-                                      num_dex_caches);
-        return false;
-      }
-    }
-
-    // Only add the classes to the class loader after the points where we can return false.
-    for (size_t i = 0; i < num_dex_caches; i++) {
       ObjPtr<mirror::DexCache> dex_cache = dex_caches->Get(i);
       const DexFile* const dex_file = dex_cache->GetDexFile();
-      const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
-      if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
-        // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and
-        // copy over the arrays.
-        DCHECK(dex_file != nullptr);
-        size_t num_strings = mirror::DexCache::kDexCacheStringCacheSize;
-        if (dex_file->NumStringIds() < num_strings) {
-          num_strings = dex_file->NumStringIds();
-        }
-        size_t num_types = mirror::DexCache::kDexCacheTypeCacheSize;
-        if (dex_file->NumTypeIds() < num_types) {
-          num_types = dex_file->NumTypeIds();
-        }
-        size_t num_methods = mirror::DexCache::kDexCacheMethodCacheSize;
-        if (dex_file->NumMethodIds() < num_methods) {
-          num_methods = dex_file->NumMethodIds();
-        }
-        size_t num_fields = mirror::DexCache::kDexCacheFieldCacheSize;
-        if (dex_file->NumFieldIds() < num_fields) {
-          num_fields = dex_file->NumFieldIds();
-        }
-        size_t num_method_types = mirror::DexCache::kDexCacheMethodTypeCacheSize;
-        if (dex_file->NumProtoIds() < num_method_types) {
-          num_method_types = dex_file->NumProtoIds();
-        }
-        const size_t num_call_sites = dex_file->NumCallSiteIds();
-        CHECK_EQ(num_strings, dex_cache->NumStrings());
-        CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
-        CHECK_EQ(num_methods, dex_cache->NumResolvedMethods());
-        CHECK_EQ(num_fields, dex_cache->NumResolvedFields());
-        CHECK_EQ(num_method_types, dex_cache->NumResolvedMethodTypes());
-        CHECK_EQ(num_call_sites, dex_cache->NumResolvedCallSites());
-        DexCacheArraysLayout layout(image_pointer_size, dex_file);
-        uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays();
-        if (num_strings != 0u) {
-          mirror::StringDexCacheType* const image_resolved_strings = dex_cache->GetStrings();
-          mirror::StringDexCacheType* const strings =
-              reinterpret_cast<mirror::StringDexCacheType*>(raw_arrays + layout.StringsOffset());
-          CopyDexCachePairs(image_resolved_strings, num_strings, strings);
-          dex_cache->SetStrings(strings);
-        }
-        if (num_types != 0u) {
-          mirror::TypeDexCacheType* const image_resolved_types = dex_cache->GetResolvedTypes();
-          mirror::TypeDexCacheType* const types =
-              reinterpret_cast<mirror::TypeDexCacheType*>(raw_arrays + layout.TypesOffset());
-          CopyDexCachePairs(image_resolved_types, num_types, types);
-          dex_cache->SetResolvedTypes(types);
-        }
-        if (num_methods != 0u) {
-          mirror::MethodDexCacheType* const image_resolved_methods =
-              dex_cache->GetResolvedMethods();
-          mirror::MethodDexCacheType* const methods =
-              reinterpret_cast<mirror::MethodDexCacheType*>(raw_arrays + layout.MethodsOffset());
-          CopyNativeDexCachePairs(image_resolved_methods, num_methods, methods, image_pointer_size);
-          dex_cache->SetResolvedMethods(methods);
-        }
-        if (num_fields != 0u) {
-          mirror::FieldDexCacheType* const image_resolved_fields = dex_cache->GetResolvedFields();
-          mirror::FieldDexCacheType* const fields =
-              reinterpret_cast<mirror::FieldDexCacheType*>(raw_arrays + layout.FieldsOffset());
-          CopyNativeDexCachePairs(image_resolved_fields, num_fields, fields, image_pointer_size);
-          dex_cache->SetResolvedFields(fields);
-        }
-        if (num_method_types != 0u) {
-          // NOTE: We currently (Sep 2016) do not resolve any method types at
-          // compile time, but plan to in the future. This code exists for the
-          // sake of completeness.
-          mirror::MethodTypeDexCacheType* const image_resolved_method_types =
-              dex_cache->GetResolvedMethodTypes();
-          mirror::MethodTypeDexCacheType* const method_types =
-              reinterpret_cast<mirror::MethodTypeDexCacheType*>(
-                  raw_arrays + layout.MethodTypesOffset());
-          CopyDexCachePairs(image_resolved_method_types, num_method_types, method_types);
-          dex_cache->SetResolvedMethodTypes(method_types);
-        }
-        if (num_call_sites != 0u) {
-          GcRoot<mirror::CallSite>* const image_resolved_call_sites =
-              dex_cache->GetResolvedCallSites();
-          GcRoot<mirror::CallSite>* const call_sites =
-              reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset());
-          for (size_t j = 0; kIsDebugBuild && j < num_call_sites; ++j) {
-            DCHECK(call_sites[j].IsNull());
-          }
-          CopyNonNull(image_resolved_call_sites,
-                      num_call_sites,
-                      call_sites,
-                      [](const GcRoot<mirror::CallSite>& elem) {
-                          return elem.IsNull();
-                      });
-          dex_cache->SetResolvedCallSites(call_sites);
-        }
-      }
       {
         WriterMutexLock mu2(self, *Locks::dex_lock_);
-        // Make sure to do this after we update the arrays since we store the resolved types array
-        // in DexCacheData in RegisterDexFileLocked. We need the array pointer to be the one in the
-        // BSS.
         CHECK(!class_linker->FindDexCacheDataLocked(*dex_file).IsValid());
         class_linker->RegisterDexFileLocked(*dex_file, dex_cache, class_loader.Get());
       }
@@ -1468,7 +1290,6 @@
     VerifyDeclaringClassVisitor visitor;
     header.VisitPackedArtMethods(&visitor, space->Begin(), kRuntimePointerSize);
   }
-  return true;
 }
 
 // Update the class loader. Should only be used on classes in the image space.
@@ -1947,7 +1768,7 @@
   // If we have a class table section, read it and use it for verification in
   // UpdateAppImageClassLoadersAndDexCaches.
   ClassTable::ClassSet temp_set;
-  const ImageSection& class_table_section = header.GetImageSection(ImageHeader::kSectionClassTable);
+  const ImageSection& class_table_section = header.GetClassTableSection();
   const bool added_class_table = class_table_section.Size() > 0u;
   if (added_class_table) {
     const uint64_t start_time2 = NanoTime();
@@ -1958,37 +1779,17 @@
     VLOG(image) << "Adding class table classes took " << PrettyDuration(NanoTime() - start_time2);
   }
   if (app_image) {
-    bool forward_dex_cache_arrays = false;
-    if (!AppImageClassLoadersAndDexCachesHelper::Update(this,
-                                                        space,
-                                                        class_loader,
-                                                        dex_caches,
-                                                        &temp_set,
-                                                        /*out*/&forward_dex_cache_arrays,
-                                                        /*out*/error_msg)) {
-      return false;
-    }
+    AppImageClassLoadersAndDexCachesHelper::Update(this,
+                                                   space,
+                                                   class_loader,
+                                                   dex_caches,
+                                                   &temp_set);
     // Update class loader and resolved strings. If added_class_table is false, the resolved
     // strings were forwarded UpdateAppImageClassLoadersAndDexCaches.
     UpdateClassLoaderVisitor visitor(space, class_loader.Get());
     for (const ClassTable::TableSlot& root : temp_set) {
       visitor(root.Read());
     }
-    // forward_dex_cache_arrays is true iff we copied all of the dex cache arrays into the .bss.
-    // In this case, madvise away the dex cache arrays section of the image to reduce RAM usage and
-    // mark as PROT_NONE to catch any invalid accesses.
-    if (forward_dex_cache_arrays) {
-      const ImageSection& dex_cache_section = header.GetDexCacheArraysSection();
-      uint8_t* section_begin = AlignUp(space->Begin() + dex_cache_section.Offset(), kPageSize);
-      uint8_t* section_end = AlignDown(space->Begin() + dex_cache_section.End(), kPageSize);
-      if (section_begin < section_end) {
-        madvise(section_begin, section_end - section_begin, MADV_DONTNEED);
-        mprotect(section_begin, section_end - section_begin, PROT_NONE);
-        VLOG(image) << "Released and protected dex cache array image section from "
-                    << reinterpret_cast<const void*>(section_begin) << "-"
-                    << reinterpret_cast<const void*>(section_end);
-      }
-    }
   }
   if (!oat_file->GetBssGcRoots().empty()) {
     // Insert oat file to class table for visiting .bss GC roots.
@@ -7951,7 +7752,8 @@
     // We have a valid method from the DexCache but we need to perform ICCE and IAE checks.
     DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex();
     klass = LookupResolvedType(dex_file, method_id.class_idx_, dex_cache.Get(), class_loader.Get());
-    DCHECK(klass != nullptr);
+    CHECK(klass != nullptr) << resolved->PrettyMethod() << " " << resolved << " "
+                            << resolved->GetAccessFlags();
   } else {
     // The method was not in the DexCache, resolve the declaring class.
     klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader);
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 07afedf..56573f5 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -632,6 +632,10 @@
   }
 }
 
+static bool IsAbsoluteLocation(const std::string& location) {
+  return !location.empty() && location[0] == '/';
+}
+
 bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) const {
   ClassLoaderContext expected_context;
   if (!expected_context.Parse(context_spec, /*parse_checksums*/ true)) {
@@ -673,18 +677,52 @@
     DCHECK_EQ(expected_info.classpath.size(), expected_info.checksums.size());
 
     for (size_t k = 0; k < info.classpath.size(); k++) {
-      if (info.classpath[k] != expected_info.classpath[k]) {
+      // Compute the dex location that must be compared.
+      // We shouldn't do a naive comparison `info.classpath[k] == expected_info.classpath[k]`
+      // because even if they refer to the same file, one could be encoded as a relative location
+      // and the other as an absolute one.
+      bool is_dex_name_absolute = IsAbsoluteLocation(info.classpath[k]);
+      bool is_expected_dex_name_absolute = IsAbsoluteLocation(expected_info.classpath[k]);
+      std::string dex_name;
+      std::string expected_dex_name;
+
+      if (is_dex_name_absolute == is_expected_dex_name_absolute) {
+        // If both locations are absolute or relative then compare them as they are.
+        // This is usually the case for: shared libraries and secondary dex files.
+        dex_name = info.classpath[k];
+        expected_dex_name = expected_info.classpath[k];
+      } else if (is_dex_name_absolute) {
+        // The runtime name is absolute but the compiled name (the expected one) is relative.
+        // This is the case for split apks which depend on base or on other splits.
+        dex_name = info.classpath[k];
+        expected_dex_name = OatFile::ResolveRelativeEncodedDexLocation(
+            info.classpath[k].c_str(), expected_info.classpath[k]);
+      } else {
+        // The runtime name is relative but the compiled name is absolute.
+        // There is no expected use case that would end up here as dex files are always loaded
+        // with their absolute location. However, be tolerant and do the best effort (in case
+        // there are unexpected new use case...).
+        DCHECK(is_expected_dex_name_absolute);
+        dex_name = OatFile::ResolveRelativeEncodedDexLocation(
+            expected_info.classpath[k].c_str(), info.classpath[k]);
+        expected_dex_name = expected_info.classpath[k];
+      }
+
+      // Compare the locations.
+      if (dex_name != expected_dex_name) {
         LOG(WARNING) << "ClassLoaderContext classpath element mismatch for position " << i
             << ". expected=" << expected_info.classpath[k]
             << ", found=" << info.classpath[k]
             << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
         return false;
       }
+
+      // Compare the checksums.
       if (info.checksums[k] != expected_info.checksums[k]) {
         LOG(WARNING) << "ClassLoaderContext classpath element checksum mismatch for position " << i
-            << ". expected=" << expected_info.checksums[k]
-            << ", found=" << info.checksums[k]
-            << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
+                     << ". expected=" << expected_info.checksums[k]
+                     << ", found=" << info.checksums[k]
+                     << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
         return false;
       }
     }
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index ddbb73b..1847274 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -697,7 +697,17 @@
 
   std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d);
 
-  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")));
+  std::string context_with_no_base_dir = context->EncodeContextForOatFile("");
+  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_no_base_dir));
+
+  std::string dex_location = GetTestDexFileName("ForClassLoaderA");
+  size_t pos = dex_location.rfind('/');
+  ASSERT_NE(std::string::npos, pos);
+  std::string parent = dex_location.substr(0, pos);
+
+  std::string context_with_base_dir = context->EncodeContextForOatFile(parent);
+  ASSERT_NE(context_with_base_dir, context_with_no_base_dir);
+  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_base_dir));
 }
 
 TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) {
diff --git a/runtime/class_status.h b/runtime/class_status.h
new file mode 100644
index 0000000..0877e68
--- /dev/null
+++ b/runtime/class_status.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_CLASS_STATUS_H_
+#define ART_RUNTIME_CLASS_STATUS_H_
+
+#include <iosfwd>
+
+namespace art {
+
+// Class Status
+//
+// kStatusRetired: Class that's temporarily used till class linking time
+// has its (vtable) size figured out and has been cloned to one with the
+// right size which will be the one used later. The old one is retired and
+// will be gc'ed once all refs to the class point to the newly
+// cloned version.
+//
+// kStatusErrorUnresolved, kStatusErrorResolved: Class is erroneous. We need
+// to distinguish between classes that have been resolved and classes that
+// have not. This is important because the const-class instruction needs to
+// return a previously resolved class even if its subsequent initialization
+// failed. We also need this to decide whether to wrap a previous
+// initialization failure in ClassDefNotFound error or not.
+//
+// kStatusNotReady: If a Class cannot be found in the class table by
+// FindClass, it allocates an new one with AllocClass in the
+// kStatusNotReady and calls LoadClass. Note if it does find a
+// class, it may not be kStatusResolved and it will try to push it
+// forward toward kStatusResolved.
+//
+// kStatusIdx: LoadClass populates with Class with information from
+// the DexFile, moving the status to kStatusIdx, indicating that the
+// Class value in super_class_ has not been populated. The new Class
+// can then be inserted into the classes table.
+//
+// kStatusLoaded: After taking a lock on Class, the ClassLinker will
+// attempt to move a kStatusIdx class forward to kStatusLoaded by
+// using ResolveClass to initialize the super_class_ and ensuring the
+// interfaces are resolved.
+//
+// kStatusResolving: Class is just cloned with the right size from
+// temporary class that's acting as a placeholder for linking. The old
+// class will be retired. New class is set to this status first before
+// moving on to being resolved.
+//
+// kStatusResolved: Still holding the lock on Class, the ClassLinker
+// shows linking is complete and fields of the Class populated by making
+// it kStatusResolved. Java allows circularities of the form where a super
+// class has a field that is of the type of the sub class. We need to be able
+// to fully resolve super classes while resolving types for fields.
+//
+// kStatusRetryVerificationAtRuntime: The verifier sets a class to
+// this state if it encounters a soft failure at compile time. This
+// often happens when there are unresolved classes in other dex
+// files, and this status marks a class as needing to be verified
+// again at runtime.
+//
+// TODO: Explain the other states
+enum ClassStatus {
+  kStatusRetired = -3,  // Retired, should not be used. Use the newly cloned one instead.
+  kStatusErrorResolved = -2,
+  kStatusErrorUnresolved = -1,
+  kStatusNotReady = 0,
+  kStatusIdx = 1,  // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_.
+  kStatusLoaded = 2,  // DEX idx values resolved.
+  kStatusResolving = 3,  // Just cloned from temporary class object.
+  kStatusResolved = 4,  // Part of linking.
+  kStatusVerifying = 5,  // In the process of being verified.
+  kStatusRetryVerificationAtRuntime = 6,  // Compile time verification failed, retry at runtime.
+  kStatusVerifyingAtRuntime = 7,  // Retrying verification at runtime.
+  kStatusVerified = 8,  // Logically part of linking; done pre-init.
+  kStatusSuperclassValidated = 9,  // Superclass validation part of init done.
+  kStatusInitializing = 10,  // Class init in progress.
+  kStatusInitialized = 11,  // Ready to go.
+  kStatusMax = 12,
+};
+
+std::ostream& operator<<(std::ostream& os, const ClassStatus& rhs);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_CLASS_STATUS_H_
diff --git a/runtime/compiler_callbacks.h b/runtime/compiler_callbacks.h
index c51bb5e..9b22714 100644
--- a/runtime/compiler_callbacks.h
+++ b/runtime/compiler_callbacks.h
@@ -19,6 +19,7 @@
 
 #include "base/mutex.h"
 #include "class_reference.h"
+#include "class_status.h"
 
 namespace art {
 
@@ -51,8 +52,10 @@
   virtual verifier::VerifierDeps* GetVerifierDeps() const = 0;
   virtual void SetVerifierDeps(verifier::VerifierDeps* deps ATTRIBUTE_UNUSED) {}
 
-  virtual bool CanAssumeVerified(ClassReference ref ATTRIBUTE_UNUSED) {
-    return false;
+  // Return the class status of a previous stage of the compilation. This can be used, for example,
+  // when class unloading is enabled during multidex compilation.
+  virtual ClassStatus GetPreviousClassState(ClassReference ref ATTRIBUTE_UNUSED) {
+    return ClassStatus::kStatusNotReady;
   }
 
   virtual void SetDoesClassUnloading(bool does_class_unloading ATTRIBUTE_UNUSED,
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index fe0bad2..af56810 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -262,6 +262,13 @@
                << " " << dex_pc;
   }
 
+  // TODO Might be worth it to post ExceptionCatch event.
+  void ExceptionHandled(Thread* thread ATTRIBUTE_UNUSED,
+                        Handle<mirror::Throwable> throwable ATTRIBUTE_UNUSED) OVERRIDE {
+    LOG(ERROR) << "Unexpected exception handled event in debugger";
+  }
+
+
  private:
   static bool IsReturn(ArtMethod* method, uint32_t dex_pc)
       REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index b3528d8..4d8c687 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -110,6 +110,7 @@
       have_watched_frame_pop_listeners_(false),
       have_branch_listeners_(false),
       have_invoke_virtual_or_interface_listeners_(false),
+      have_exception_handled_listeners_(false),
       deoptimized_methods_lock_("deoptimized methods lock", kDeoptimizedMethodsLock),
       deoptimization_enabled_(false),
       interpreter_handler_table_(kMainHandlerTable),
@@ -513,6 +514,11 @@
                            watched_frame_pop_listeners_,
                            listener,
                            &have_watched_frame_pop_listeners_);
+  PotentiallyAddListenerTo(kExceptionHandled,
+                           events,
+                           exception_handled_listeners_,
+                           listener,
+                           &have_exception_handled_listeners_);
   UpdateInterpreterHandlerTable();
 }
 
@@ -595,6 +601,11 @@
                                 watched_frame_pop_listeners_,
                                 listener,
                                 &have_watched_frame_pop_listeners_);
+  PotentiallyRemoveListenerFrom(kExceptionHandled,
+                                events,
+                                exception_handled_listeners_,
+                                listener,
+                                &have_exception_handled_listeners_);
   UpdateInterpreterHandlerTable();
 }
 
@@ -1118,10 +1129,28 @@
         listener->ExceptionThrown(thread, h_exception);
       }
     }
+    // See b/65049545 for discussion about this behavior.
+    thread->AssertNoPendingException();
     thread->SetException(h_exception.Get());
   }
 }
 
+void Instrumentation::ExceptionHandledEvent(Thread* thread,
+                                            mirror::Throwable* exception_object) const {
+  Thread* self = Thread::Current();
+  StackHandleScope<1> hs(self);
+  Handle<mirror::Throwable> h_exception(hs.NewHandle(exception_object));
+  if (HasExceptionHandledListeners()) {
+    // We should have cleared the exception so that callers can detect a new one.
+    DCHECK(thread->GetException() == nullptr);
+    for (InstrumentationListener* listener : exception_handled_listeners_) {
+      if (listener != nullptr) {
+        listener->ExceptionHandled(thread, h_exception);
+      }
+    }
+  }
+}
+
 // Computes a frame ID by ignoring inlined frames.
 size_t Instrumentation::ComputeFrameId(Thread* self,
                                        size_t frame_depth,
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index 43e29cf..5763a41 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -131,6 +131,10 @@
                                Handle<mirror::Throwable> exception_object)
       REQUIRES_SHARED(Locks::mutator_lock_) = 0;
 
+  // Call-back when an exception is caught/handled by java code.
+  virtual void ExceptionHandled(Thread* thread, Handle<mirror::Throwable> exception_object)
+      REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
   // Call-back for when we execute a branch.
   virtual void Branch(Thread* thread,
                       ArtMethod* method,
@@ -173,6 +177,7 @@
     kBranch = 0x80,
     kInvokeVirtualOrInterface = 0x100,
     kWatchedFramePop = 0x200,
+    kExceptionHandled = 0x400,
   };
 
   enum class InstrumentationLevel {
@@ -350,12 +355,16 @@
     return have_watched_frame_pop_listeners_;
   }
 
+  bool HasExceptionHandledListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return have_exception_handled_listeners_;
+  }
+
   bool IsActive() const REQUIRES_SHARED(Locks::mutator_lock_) {
     return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ ||
         have_field_read_listeners_ || have_field_write_listeners_ ||
         have_exception_thrown_listeners_ || have_method_unwind_listeners_ ||
         have_branch_listeners_ || have_invoke_virtual_or_interface_listeners_ ||
-        have_watched_frame_pop_listeners_;
+        have_watched_frame_pop_listeners_ || have_exception_handled_listeners_;
   }
 
   // Any instrumentation *other* than what is needed for Jit profiling active?
@@ -363,7 +372,8 @@
     return have_dex_pc_listeners_ || have_method_exit_listeners_ ||
         have_field_read_listeners_ || have_field_write_listeners_ ||
         have_exception_thrown_listeners_ || have_method_unwind_listeners_ ||
-        have_branch_listeners_ || have_watched_frame_pop_listeners_;
+        have_branch_listeners_ || have_watched_frame_pop_listeners_ ||
+        have_exception_handled_listeners_;
   }
 
   // Inform listeners that a method has been entered. A dex PC is provided as we may install
@@ -453,6 +463,11 @@
   void ExceptionThrownEvent(Thread* thread, mirror::Throwable* exception_object) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Inform listeners that an exception has been handled. This is not sent for native code or for
+  // exceptions which reach the end of the thread's stack.
+  void ExceptionHandledEvent(Thread* thread, mirror::Throwable* exception_object) const
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Called when an instrumented method is entered. The intended link register (lr) is saved so
   // that returning causes a branch to the method exit stub. Generates method enter events.
   void PushInstrumentationStackFrame(Thread* self, mirror::Object* this_object,
@@ -641,6 +656,10 @@
   // Do we have any invoke listeners? Short-cut to avoid taking the instrumentation_lock_.
   bool have_invoke_virtual_or_interface_listeners_ GUARDED_BY(Locks::mutator_lock_);
 
+  // Do we have any exception handled listeners? Short-cut to avoid taking the
+  // instrumentation_lock_.
+  bool have_exception_handled_listeners_ GUARDED_BY(Locks::mutator_lock_);
+
   // Contains the instrumentation level required by each client of the instrumentation identified
   // by a string key.
   typedef SafeMap<const char*, InstrumentationLevel> InstrumentationLevelTable;
@@ -667,6 +686,7 @@
   std::list<InstrumentationListener*> field_write_listeners_ GUARDED_BY(Locks::mutator_lock_);
   std::list<InstrumentationListener*> exception_thrown_listeners_ GUARDED_BY(Locks::mutator_lock_);
   std::list<InstrumentationListener*> watched_frame_pop_listeners_ GUARDED_BY(Locks::mutator_lock_);
+  std::list<InstrumentationListener*> exception_handled_listeners_ GUARDED_BY(Locks::mutator_lock_);
 
   // The set of methods being deoptimized (by the debugger) which must be executed with interpreter
   // only.
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
index 1aa4e91..89baa35 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -48,6 +48,7 @@
       received_field_written_event(false),
       received_field_written_object_event(false),
       received_exception_thrown_event(false),
+      received_exception_handled_event(false),
       received_branch_event(false),
       received_invoke_virtual_or_interface_event(false),
       received_watched_frame_pop(false) {}
@@ -131,6 +132,12 @@
     received_exception_thrown_event = true;
   }
 
+  void ExceptionHandled(Thread* self ATTRIBUTE_UNUSED,
+                        Handle<mirror::Throwable> throwable ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+    received_exception_handled_event = true;
+  }
+
   void Branch(Thread* thread ATTRIBUTE_UNUSED,
               ArtMethod* method ATTRIBUTE_UNUSED,
               uint32_t dex_pc ATTRIBUTE_UNUSED,
@@ -163,6 +170,7 @@
     received_field_written_event = false;
     received_field_written_object_event = false;
     received_exception_thrown_event = false;
+    received_exception_handled_event = false;
     received_branch_event = false;
     received_invoke_virtual_or_interface_event = false;
     received_watched_frame_pop = false;
@@ -177,6 +185,7 @@
   bool received_field_written_event;
   bool received_field_written_object_event;
   bool received_exception_thrown_event;
+  bool received_exception_handled_event;
   bool received_branch_event;
   bool received_invoke_virtual_or_interface_event;
   bool received_watched_frame_pop;
@@ -369,6 +378,8 @@
         return instr->HasFieldWriteListeners();
       case instrumentation::Instrumentation::kExceptionThrown:
         return instr->HasExceptionThrownListeners();
+      case instrumentation::Instrumentation::kExceptionHandled:
+        return instr->HasExceptionHandledListeners();
       case instrumentation::Instrumentation::kBranch:
         return instr->HasBranchListeners();
       case instrumentation::Instrumentation::kInvokeVirtualOrInterface:
@@ -429,6 +440,13 @@
       case instrumentation::Instrumentation::kWatchedFramePop:
         instr->WatchedFramePopped(self, frame);
         break;
+      case instrumentation::Instrumentation::kExceptionHandled: {
+        ThrowArithmeticExceptionDivideByZero();
+        mirror::Throwable* event_exception = self->GetException();
+        self->ClearException();
+        instr->ExceptionHandledEvent(self, event_exception);
+        break;
+      }
       default:
         LOG(FATAL) << "Unknown instrumentation event " << event_type;
         UNREACHABLE();
@@ -455,6 +473,8 @@
             (with_object && listener.received_field_written_object_event);
       case instrumentation::Instrumentation::kExceptionThrown:
         return listener.received_exception_thrown_event;
+      case instrumentation::Instrumentation::kExceptionHandled:
+        return listener.received_exception_handled_event;
       case instrumentation::Instrumentation::kBranch:
         return listener.received_branch_event;
       case instrumentation::Instrumentation::kInvokeVirtualOrInterface:
@@ -484,6 +504,7 @@
   // Check there is no registered listener.
   EXPECT_FALSE(instr->HasDexPcListeners());
   EXPECT_FALSE(instr->HasExceptionThrownListeners());
+  EXPECT_FALSE(instr->HasExceptionHandledListeners());
   EXPECT_FALSE(instr->HasFieldReadListeners());
   EXPECT_FALSE(instr->HasFieldWriteListeners());
   EXPECT_FALSE(instr->HasMethodEntryListeners());
@@ -603,6 +624,10 @@
             /*with_object*/ false);
 }
 
+TEST_F(InstrumentationTest, ExceptionHandledEvent) {
+  TestEvent(instrumentation::Instrumentation::kExceptionHandled);
+}
+
 TEST_F(InstrumentationTest, ExceptionThrownEvent) {
   TestEvent(instrumentation::Instrumentation::kExceptionThrown);
 }
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index ec183b3..a1f2123 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -500,10 +500,8 @@
       // null Instrumentation*.
       const instrumentation::Instrumentation* const instrumentation =
           first ? nullptr : Runtime::Current()->GetInstrumentation();
-      uint32_t found_dex_pc = FindNextInstructionFollowingException(self, *shadow_frame, dex_pc,
-                                                                    instrumentation);
-      new_dex_pc = found_dex_pc;  // the dex pc of a matching catch handler
-                                  // or DexFile::kDexNoIndex if there is none.
+      new_dex_pc = MoveToExceptionHandler(
+          self, *shadow_frame, instrumentation) ? shadow_frame->GetDexPC() : DexFile::kDexNoIndex;
     } else if (!from_code) {
       // Deoptimization is not called from code directly.
       const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]);
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index ae461fd..0028b21 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -416,35 +416,57 @@
 #undef EXPLICIT_DO_IPUT_QUICK_ALL_TEMPLATE_DECL
 #undef EXPLICIT_DO_IPUT_QUICK_TEMPLATE_DECL
 
+// We execute any instrumentation events that are triggered by this exception and change the
+// shadow_frame's dex_pc to that of the exception handler if there is one in the current method.
+// Return true if we should continue executing in the current method and false if we need to go up
+// the stack to find an exception handler.
 // We accept a null Instrumentation* meaning we must not report anything to the instrumentation.
-uint32_t FindNextInstructionFollowingException(
-    Thread* self, ShadowFrame& shadow_frame, uint32_t dex_pc,
-    const instrumentation::Instrumentation* instrumentation) {
+// TODO We should have a better way to skip instrumentation reporting or possibly rethink that
+// behavior.
+bool MoveToExceptionHandler(Thread* self,
+                            ShadowFrame& shadow_frame,
+                            const instrumentation::Instrumentation* instrumentation) {
   self->VerifyStack();
   StackHandleScope<2> hs(self);
   Handle<mirror::Throwable> exception(hs.NewHandle(self->GetException()));
-  if (instrumentation != nullptr && instrumentation->HasExceptionThrownListeners()
-      && self->IsExceptionThrownByCurrentMethod(exception.Get())) {
+  if (instrumentation != nullptr &&
+      instrumentation->HasExceptionThrownListeners() &&
+      self->IsExceptionThrownByCurrentMethod(exception.Get())) {
+    // See b/65049545 for why we don't need to check to see if the exception has changed.
     instrumentation->ExceptionThrownEvent(self, exception.Get());
   }
   bool clear_exception = false;
   uint32_t found_dex_pc = shadow_frame.GetMethod()->FindCatchBlock(
-      hs.NewHandle(exception->GetClass()), dex_pc, &clear_exception);
-  if (found_dex_pc == DexFile::kDexNoIndex && instrumentation != nullptr) {
-    if (shadow_frame.NeedsNotifyPop()) {
-      instrumentation->WatchedFramePopped(self, shadow_frame);
+      hs.NewHandle(exception->GetClass()), shadow_frame.GetDexPC(), &clear_exception);
+  if (found_dex_pc == DexFile::kDexNoIndex) {
+    if (instrumentation != nullptr) {
+      if (shadow_frame.NeedsNotifyPop()) {
+        instrumentation->WatchedFramePopped(self, shadow_frame);
+      }
+      // Exception is not caught by the current method. We will unwind to the
+      // caller. Notify any instrumentation listener.
+      instrumentation->MethodUnwindEvent(self,
+                                         shadow_frame.GetThisObject(),
+                                         shadow_frame.GetMethod(),
+                                         shadow_frame.GetDexPC());
     }
-    // Exception is not caught by the current method. We will unwind to the
-    // caller. Notify any instrumentation listener.
-    instrumentation->MethodUnwindEvent(self, shadow_frame.GetThisObject(),
-                                       shadow_frame.GetMethod(), dex_pc);
+    return false;
   } else {
-    // Exception is caught in the current method. We will jump to the found_dex_pc.
-    if (clear_exception) {
+    shadow_frame.SetDexPC(found_dex_pc);
+    if (instrumentation != nullptr && instrumentation->HasExceptionHandledListeners()) {
+      self->ClearException();
+      instrumentation->ExceptionHandledEvent(self, exception.Get());
+      if (UNLIKELY(self->IsExceptionPending())) {
+        // Exception handled event threw an exception. Try to find the handler for this one.
+        return MoveToExceptionHandler(self, shadow_frame, instrumentation);
+      } else if (!clear_exception) {
+        self->SetException(exception.Get());
+      }
+    } else if (clear_exception) {
       self->ClearException();
     }
+    return true;
   }
-  return found_dex_pc;
 }
 
 void UnexpectedOpcode(const Instruction* inst, const ShadowFrame& shadow_frame) {
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 4db8184..3ccab85 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -462,9 +462,17 @@
   return 3;
 }
 
-uint32_t FindNextInstructionFollowingException(Thread* self, ShadowFrame& shadow_frame,
-    uint32_t dex_pc, const instrumentation::Instrumentation* instrumentation)
-        REQUIRES_SHARED(Locks::mutator_lock_);
+// We execute any instrumentation events triggered by throwing and/or handing the pending exception
+// and change the shadow_frames dex_pc to the appropriate exception handler if the current method
+// has one. If the exception has been handled and the shadow_frame is now pointing to a catch clause
+// we return true. If the current method is unable to handle the exception we return false.
+// This function accepts a null Instrumentation* as a way to cause instrumentation events not to be
+// reported.
+// TODO We might wish to reconsider how we cause some events to be ignored.
+bool MoveToExceptionHandler(Thread* self,
+                            ShadowFrame& shadow_frame,
+                            const instrumentation::Instrumentation* instrumentation)
+    REQUIRES_SHARED(Locks::mutator_lock_);
 
 NO_RETURN void UnexpectedOpcode(const Instruction* inst, const ShadowFrame& shadow_frame)
   __attribute__((cold))
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index f352960..69e091b 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -30,10 +30,7 @@
   do {                                                                                          \
     DCHECK(self->IsExceptionPending());                                                         \
     self->AllowThreadSuspension();                                                              \
-    uint32_t found_dex_pc = FindNextInstructionFollowingException(self, shadow_frame,           \
-                                                                  inst->GetDexPc(insns),        \
-                                                                  instr);                       \
-    if (found_dex_pc == DexFile::kDexNoIndex) {                                                 \
+    if (!MoveToExceptionHandler(self, shadow_frame, instr)) {                                   \
       /* Structured locking is to be enforced for abnormal termination, too. */                 \
       DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame);                        \
       if (interpret_one_instruction) {                                                          \
@@ -42,7 +39,8 @@
       }                                                                                         \
       return JValue(); /* Handled in caller. */                                                 \
     } else {                                                                                    \
-      int32_t displacement = static_cast<int32_t>(found_dex_pc) - static_cast<int32_t>(dex_pc); \
+      int32_t displacement =                                                                    \
+          static_cast<int32_t>(shadow_frame.GetDexPC()) - static_cast<int32_t>(dex_pc);         \
       inst = inst->RelativeAt(displacement);                                                    \
     }                                                                                           \
   } while (false)
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 88254a8..b8a7a2a 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -490,15 +490,7 @@
   DCHECK(self->IsExceptionPending());
   const instrumentation::Instrumentation* const instrumentation =
       Runtime::Current()->GetInstrumentation();
-  uint32_t found_dex_pc = FindNextInstructionFollowingException(self, *shadow_frame,
-                                                                shadow_frame->GetDexPC(),
-                                                                instrumentation);
-  if (found_dex_pc == DexFile::kDexNoIndex) {
-    return false;
-  }
-  // OK - we can deal with it.  Update and continue.
-  shadow_frame->SetDexPC(found_dex_pc);
-  return true;
+  return MoveToExceptionHandler(self, *shadow_frame, instrumentation);
 }
 
 extern "C" void MterpCheckBefore(Thread* self, ShadowFrame* shadow_frame, uint16_t* dex_pc_ptr)
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index c775cf4..40157c4 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -47,6 +47,23 @@
 
 GcRoot<Class> Class::java_lang_Class_;
 
+constexpr Class::Status Class::kStatusRetired;
+constexpr Class::Status Class::kStatusErrorResolved;
+constexpr Class::Status Class::kStatusErrorUnresolved;
+constexpr Class::Status Class::kStatusNotReady;
+constexpr Class::Status Class::kStatusIdx;
+constexpr Class::Status Class::kStatusLoaded;
+constexpr Class::Status Class::kStatusResolving;
+constexpr Class::Status Class::kStatusResolved;
+constexpr Class::Status Class::kStatusVerifying;
+constexpr Class::Status Class::kStatusRetryVerificationAtRuntime;
+constexpr Class::Status Class::kStatusVerifyingAtRuntime;
+constexpr Class::Status Class::kStatusVerified;
+constexpr Class::Status Class::kStatusSuperclassValidated;
+constexpr Class::Status Class::kStatusInitializing;
+constexpr Class::Status Class::kStatusInitialized;
+constexpr Class::Status Class::kStatusMax;
+
 void Class::SetClassClass(ObjPtr<Class> java_lang_Class) {
   CHECK(java_lang_Class_.IsNull())
       << java_lang_Class_.Read()
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 250604b..4c9b146 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -21,6 +21,7 @@
 #include "base/enums.h"
 #include "base/iteration_range.h"
 #include "class_flags.h"
+#include "class_status.h"
 #include "dex_file.h"
 #include "dex_file_types.h"
 #include "gc/allocator_type.h"
@@ -76,73 +77,27 @@
   static constexpr uint32_t kPrimitiveTypeSizeShiftShift = 16;
   static constexpr uint32_t kPrimitiveTypeMask = (1u << kPrimitiveTypeSizeShiftShift) - 1;
 
-  // Class Status
-  //
-  // kStatusRetired: Class that's temporarily used till class linking time
-  // has its (vtable) size figured out and has been cloned to one with the
-  // right size which will be the one used later. The old one is retired and
-  // will be gc'ed once all refs to the class point to the newly
-  // cloned version.
-  //
-  // kStatusErrorUnresolved, kStatusErrorResolved: Class is erroneous. We need
-  // to distinguish between classes that have been resolved and classes that
-  // have not. This is important because the const-class instruction needs to
-  // return a previously resolved class even if its subsequent initialization
-  // failed. We also need this to decide whether to wrap a previous
-  // initialization failure in ClassDefNotFound error or not.
-  //
-  // kStatusNotReady: If a Class cannot be found in the class table by
-  // FindClass, it allocates an new one with AllocClass in the
-  // kStatusNotReady and calls LoadClass. Note if it does find a
-  // class, it may not be kStatusResolved and it will try to push it
-  // forward toward kStatusResolved.
-  //
-  // kStatusIdx: LoadClass populates with Class with information from
-  // the DexFile, moving the status to kStatusIdx, indicating that the
-  // Class value in super_class_ has not been populated. The new Class
-  // can then be inserted into the classes table.
-  //
-  // kStatusLoaded: After taking a lock on Class, the ClassLinker will
-  // attempt to move a kStatusIdx class forward to kStatusLoaded by
-  // using ResolveClass to initialize the super_class_ and ensuring the
-  // interfaces are resolved.
-  //
-  // kStatusResolving: Class is just cloned with the right size from
-  // temporary class that's acting as a placeholder for linking. The old
-  // class will be retired. New class is set to this status first before
-  // moving on to being resolved.
-  //
-  // kStatusResolved: Still holding the lock on Class, the ClassLinker
-  // shows linking is complete and fields of the Class populated by making
-  // it kStatusResolved. Java allows circularities of the form where a super
-  // class has a field that is of the type of the sub class. We need to be able
-  // to fully resolve super classes while resolving types for fields.
-  //
-  // kStatusRetryVerificationAtRuntime: The verifier sets a class to
-  // this state if it encounters a soft failure at compile time. This
-  // often happens when there are unresolved classes in other dex
-  // files, and this status marks a class as needing to be verified
-  // again at runtime.
-  //
-  // TODO: Explain the other states
-  enum Status {
-    kStatusRetired = -3,  // Retired, should not be used. Use the newly cloned one instead.
-    kStatusErrorResolved = -2,
-    kStatusErrorUnresolved = -1,
-    kStatusNotReady = 0,
-    kStatusIdx = 1,  // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_.
-    kStatusLoaded = 2,  // DEX idx values resolved.
-    kStatusResolving = 3,  // Just cloned from temporary class object.
-    kStatusResolved = 4,  // Part of linking.
-    kStatusVerifying = 5,  // In the process of being verified.
-    kStatusRetryVerificationAtRuntime = 6,  // Compile time verification failed, retry at runtime.
-    kStatusVerifyingAtRuntime = 7,  // Retrying verification at runtime.
-    kStatusVerified = 8,  // Logically part of linking; done pre-init.
-    kStatusSuperclassValidated = 9,  // Superclass validation part of init done.
-    kStatusInitializing = 10,  // Class init in progress.
-    kStatusInitialized = 11,  // Ready to go.
-    kStatusMax = 12,
-  };
+  // Make ClassStatus available as Class::Status.
+  using Status = ClassStatus;
+
+  // Required for a minimal change. Fix up and remove in a future change.
+  static constexpr Status kStatusRetired = Status::kStatusRetired;
+  static constexpr Status kStatusErrorResolved = Status::kStatusErrorResolved;
+  static constexpr Status kStatusErrorUnresolved = Status::kStatusErrorUnresolved;
+  static constexpr Status kStatusNotReady = Status::kStatusNotReady;
+  static constexpr Status kStatusIdx = Status::kStatusIdx;
+  static constexpr Status kStatusLoaded = Status::kStatusLoaded;
+  static constexpr Status kStatusResolving = Status::kStatusResolving;
+  static constexpr Status kStatusResolved = Status::kStatusResolved;
+  static constexpr Status kStatusVerifying = Status::kStatusVerifying;
+  static constexpr Status kStatusRetryVerificationAtRuntime =
+      Status::kStatusRetryVerificationAtRuntime;
+  static constexpr Status kStatusVerifyingAtRuntime = Status::kStatusVerifyingAtRuntime;
+  static constexpr Status kStatusVerified = Status::kStatusVerified;
+  static constexpr Status kStatusSuperclassValidated = Status::kStatusSuperclassValidated;
+  static constexpr Status kStatusInitializing = Status::kStatusInitializing;
+  static constexpr Status kStatusInitialized = Status::kStatusInitialized;
+  static constexpr Status kStatusMax = Status::kStatusMax;
 
   template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
   Status GetStatus() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1556,8 +1511,6 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(Class);
 };
 
-std::ostream& operator<<(std::ostream& os, const Class::Status& rhs);
-
 }  // namespace mirror
 }  // namespace art
 
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index f6f20ba..2f63dff 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -46,13 +46,10 @@
   DexCacheArraysLayout layout(image_pointer_size, dex_file);
   uint8_t* raw_arrays = nullptr;
 
-  const OatDexFile* const oat_dex = dex_file->GetOatDexFile();
-  if (oat_dex != nullptr && oat_dex->GetDexCacheArrays() != nullptr) {
-    raw_arrays = oat_dex->GetDexCacheArrays();
-  } else if (dex_file->NumStringIds() != 0u ||
-             dex_file->NumTypeIds() != 0u ||
-             dex_file->NumMethodIds() != 0u ||
-             dex_file->NumFieldIds() != 0u) {
+  if (dex_file->NumStringIds() != 0u ||
+      dex_file->NumTypeIds() != 0u ||
+      dex_file->NumMethodIds() != 0u ||
+      dex_file->NumFieldIds() != 0u) {
     static_assert(ArenaAllocator::kAlignment == 8, "Expecting arena alignment of 8.");
     DCHECK(layout.Alignment() == 8u || layout.Alignment() == 16u);
     // Zero-initialized.
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 5c63dca..80e6ad3 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1498,13 +1498,21 @@
       break;
     case LockWord::kThinLocked:
       owner_ = Runtime::Current()->GetThreadList()->FindThreadByThreadId(lock_word.ThinLockOwner());
+      DCHECK(owner_ != nullptr) << "Thin-locked without owner!";
       entry_count_ = 1 + lock_word.ThinLockCount();
       // Thin locks have no waiters.
       break;
     case LockWord::kFatLocked: {
       Monitor* mon = lock_word.FatLockMonitor();
       owner_ = mon->owner_;
-      entry_count_ = 1 + mon->lock_count_;
+      // Here it is okay for the owner to be null since we don't reset the LockWord back to
+      // kUnlocked until we get a GC. In cases where this hasn't happened yet we will have a fat
+      // lock without an owner.
+      if (owner_ != nullptr) {
+        entry_count_ = 1 + mon->lock_count_;
+      } else {
+        DCHECK_EQ(mon->lock_count_, 0) << "Monitor is fat-locked without any owner!";
+      }
       for (Thread* waiter = mon->wait_set_; waiter != nullptr; waiter = waiter->GetWaitNext()) {
         waiters_.push_back(waiter);
       }
diff --git a/runtime/oat.h b/runtime/oat.h
index 1d79ed6..ab7c42e 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  // Last oat version changed reason: Add dex section layout info to header.
-  static constexpr uint8_t kOatVersion[] = { '1', '3', '2', '\0' };
+  // Last oat version changed reason: Remove DexCache arrays from .bss.
+  static constexpr uint8_t kOatVersion[] = { '1', '3', '3', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index d9cfa53..200681e 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -55,7 +55,6 @@
 #include "type_lookup_table.h"
 #include "utf-inl.h"
 #include "utils.h"
-#include "utils/dex_cache_arrays_layout-inl.h"
 #include "vdex_file.h"
 
 namespace art {
@@ -279,36 +278,6 @@
   return true;
 }
 
-static bool FindDexFileMapItem(const uint8_t* dex_begin,
-                               const uint8_t* dex_end,
-                               DexFile::MapItemType map_item_type,
-                               const DexFile::MapItem** result_item) {
-  *result_item = nullptr;
-
-  const DexFile::Header* header =
-      BoundsCheckedCast<const DexFile::Header*>(dex_begin, dex_begin, dex_end);
-  if (nullptr == header) return false;
-
-  if (!DexFile::IsMagicValid(header->magic_)) return true;  // Not a dex file, not an error.
-
-  const DexFile::MapList* map_list =
-      BoundsCheckedCast<const DexFile::MapList*>(dex_begin + header->map_off_, dex_begin, dex_end);
-  if (nullptr == map_list) return false;
-
-  const DexFile::MapItem* map_item = map_list->list_;
-  size_t count = map_list->size_;
-  while (count--) {
-    if (map_item->type_ == static_cast<uint16_t>(map_item_type)) {
-      *result_item = map_item;
-      break;
-    }
-    map_item = BoundsCheckedCast<const DexFile::MapItem*>(map_item + 1, dex_begin, dex_end);
-    if (nullptr == map_item) return false;
-  }
-
-  return true;
-}
-
 bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) {
   if (!GetOatHeader().IsValid()) {
     std::string cause = GetOatHeader().GetValidationErrorMessage();
@@ -361,7 +330,7 @@
       (bss_roots_ != nullptr && (bss_roots_ < bss_begin_ || bss_roots_ > bss_end_)) ||
       (bss_methods_ != nullptr && bss_roots_ != nullptr && bss_methods_ > bss_roots_)) {
     *error_msg = StringPrintf("In oat file '%s' found bss symbol(s) outside .bss or unordered: "
-                                  "begin = %p, methods_ = %p, roots = %p, end = %p",
+                                  "begin = %p, methods = %p, roots = %p, end = %p",
                               GetLocation().c_str(),
                               bss_begin_,
                               bss_methods_,
@@ -370,11 +339,15 @@
     return false;
   }
 
-  uint8_t* after_arrays = (bss_methods_ != nullptr) ? bss_methods_ : bss_roots_;  // May be null.
-  uint8_t* dex_cache_arrays = (bss_begin_ == after_arrays) ? nullptr : bss_begin_;
-  uint8_t* dex_cache_arrays_end =
-      (bss_begin_ == after_arrays) ? nullptr : (after_arrays != nullptr) ? after_arrays : bss_end_;
-  DCHECK_EQ(dex_cache_arrays != nullptr, dex_cache_arrays_end != nullptr);
+  if (bss_methods_ != nullptr && bss_methods_ != bss_begin_) {
+    *error_msg = StringPrintf("In oat file '%s' found unexpected .bss gap before 'oatbssmethods': "
+                                  "begin = %p, methods = %p",
+                              GetLocation().c_str(),
+                              bss_begin_,
+                              bss_methods_);
+    return false;
+  }
+
   uint32_t dex_file_count = GetOatHeader().GetDexFileCount();
   oat_dex_files_storage_.reserve(dex_file_count);
   for (size_t i = 0; i < dex_file_count; i++) {
@@ -609,37 +582,6 @@
                reinterpret_cast<const DexFile::Header*>(dex_file_pointer)->method_ids_size_);
     }
 
-    uint8_t* current_dex_cache_arrays = nullptr;
-    if (dex_cache_arrays != nullptr) {
-      // All DexCache types except for CallSite have their instance counts in the
-      // DexFile header. For CallSites, we need to read the info from the MapList.
-      const DexFile::MapItem* call_sites_item = nullptr;
-      if (!FindDexFileMapItem(DexBegin(),
-                              DexEnd(),
-                              DexFile::MapItemType::kDexTypeCallSiteIdItem,
-                              &call_sites_item)) {
-        *error_msg = StringPrintf("In oat file '%s' could not read data from truncated DexFile map",
-                                  GetLocation().c_str());
-        return false;
-      }
-      size_t num_call_sites = call_sites_item == nullptr ? 0 : call_sites_item->size_;
-      DexCacheArraysLayout layout(pointer_size, *header, num_call_sites);
-      if (layout.Size() != 0u) {
-        if (static_cast<size_t>(dex_cache_arrays_end - dex_cache_arrays) < layout.Size()) {
-          *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with "
-                                        "truncated dex cache arrays, %zu < %zu.",
-                                    GetLocation().c_str(),
-                                    i,
-                                    dex_file_location.c_str(),
-                                    static_cast<size_t>(dex_cache_arrays_end - dex_cache_arrays),
-                                    layout.Size());
-          return false;
-        }
-        current_dex_cache_arrays = dex_cache_arrays;
-        dex_cache_arrays += layout.Size();
-      }
-    }
-
     std::string canonical_location = DexFile::GetDexCanonicalLocation(dex_file_location.c_str());
 
     // Create the OatDexFile and add it to the owning container.
@@ -651,7 +593,6 @@
                                               lookup_table_data,
                                               method_bss_mapping,
                                               class_offsets_pointer,
-                                              current_dex_cache_arrays,
                                               dex_layout_sections);
     oat_dex_files_storage_.push_back(oat_dex_file);
 
@@ -664,14 +605,6 @@
     }
   }
 
-  if (dex_cache_arrays != dex_cache_arrays_end) {
-    // We expect the bss section to be either empty (dex_cache_arrays and bss_end_
-    // both null) or contain just the dex cache arrays and optionally some GC roots.
-    *error_msg = StringPrintf("In oat file '%s' found unexpected bss size bigger by %zu bytes.",
-                              GetLocation().c_str(),
-                              static_cast<size_t>(bss_end_ - dex_cache_arrays));
-    return false;
-  }
   return true;
 }
 
@@ -1379,7 +1312,6 @@
                                 const uint8_t* lookup_table_data,
                                 const MethodBssMapping* method_bss_mapping_data,
                                 const uint32_t* oat_class_offsets_pointer,
-                                uint8_t* dex_cache_arrays,
                                 const DexLayoutSections* dex_layout_sections)
     : oat_file_(oat_file),
       dex_file_location_(dex_file_location),
@@ -1389,7 +1321,6 @@
       lookup_table_data_(lookup_table_data),
       method_bss_mapping_(method_bss_mapping_data),
       oat_class_offsets_pointer_(oat_class_offsets_pointer),
-      dex_cache_arrays_(dex_cache_arrays),
       dex_layout_sections_(dex_layout_sections) {
   // Initialize TypeLookupTable.
   if (lookup_table_data_ != nullptr) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 9a7fe51..e13126b 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -422,10 +422,6 @@
   // Returns the offset to the OatClass information. Most callers should use GetOatClass.
   uint32_t GetOatClassOffset(uint16_t class_def_index) const;
 
-  uint8_t* GetDexCacheArrays() const {
-    return dex_cache_arrays_;
-  }
-
   const uint8_t* GetLookupTableData() const {
     return lookup_table_data_;
   }
@@ -470,7 +466,6 @@
              const uint8_t* lookup_table_data,
              const MethodBssMapping* method_bss_mapping,
              const uint32_t* oat_class_offsets_pointer,
-             uint8_t* dex_cache_arrays,
              const DexLayoutSections* dex_layout_sections);
 
   static void AssertAotCompiler();
@@ -483,7 +478,6 @@
   const uint8_t* const lookup_table_data_ = nullptr;
   const MethodBssMapping* const method_bss_mapping_ = nullptr;
   const uint32_t* const oat_class_offsets_pointer_ = 0u;
-  uint8_t* const dex_cache_arrays_ = nullptr;
   mutable std::unique_ptr<TypeLookupTable> lookup_table_;
   const DexLayoutSections* const dex_layout_sections_ = nullptr;
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index a8ccf89..a67a6aa 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -417,6 +417,12 @@
       return;
     }
     Thread* self = Thread::Current();
+
+    // Dump all threads first and then the aborting thread. While this is counter the logical flow,
+    // it improves the chance of relevant data surviving in the Android logs.
+
+    DumpAllThreads(os, self);
+
     if (self == nullptr) {
       os << "(Aborting thread was not attached to runtime!)\n";
       DumpKernelStack(os, GetTid(), "  kernel: ", false);
@@ -432,7 +438,6 @@
         }
       }
     }
-    DumpAllThreads(os, self);
   }
 
   // No thread-safety analysis as we do explicitly test for holding the mutator lock.
diff --git a/runtime/trace.cc b/runtime/trace.cc
index d7673f3..b30de79 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -811,6 +811,12 @@
   LOG(ERROR) << "Unexpected exception thrown event in tracing";
 }
 
+void Trace::ExceptionHandled(Thread* thread ATTRIBUTE_UNUSED,
+                             Handle<mirror::Throwable> exception_object ATTRIBUTE_UNUSED)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  LOG(ERROR) << "Unexpected exception thrown event in tracing";
+}
+
 void Trace::Branch(Thread* /*thread*/, ArtMethod* method,
                    uint32_t /*dex_pc*/, int32_t /*dex_pc_offset*/)
       REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/trace.h b/runtime/trace.h
index 8b0931d..49d5b22 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -181,6 +181,8 @@
   void ExceptionThrown(Thread* thread,
                        Handle<mirror::Throwable> exception_object)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE;
+  void ExceptionHandled(Thread* thread, Handle<mirror::Throwable> exception_object)
+      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!*unique_methods_lock_) OVERRIDE;
   void Branch(Thread* thread,
               ArtMethod* method,
               uint32_t dex_pc,
diff --git a/test/1927-exception-event/exception_event.cc b/test/1927-exception-event/exception_event.cc
new file mode 100644
index 0000000..3197bcd
--- /dev/null
+++ b/test/1927-exception-event/exception_event.cc
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+
+#include <cstdio>
+#include <iostream>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "jvmti.h"
+
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1927ExceptionEvent {
+
+static void ThrowNative(JNIEnv* env) {
+  ScopedLocalRef<jclass> exception(env, env->FindClass("art/Test1927$TestException"));
+  env->ThrowNew(exception.get(), "from native");
+}
+
+static void CallMethod(JNIEnv* env, jclass test, const char* name) {
+  jmethodID m = env->GetStaticMethodID(test, name, "()V");
+  env->CallStaticVoidMethod(test, m);
+}
+
+static void ClearAndPrintException(JNIEnv* env, jclass test) {
+  jthrowable e = env->ExceptionOccurred();
+  env->ExceptionClear();
+  jmethodID m = env->GetStaticMethodID(test, "printException", "(Ljava/lang/Throwable;)V");
+  env->CallStaticVoidMethod(test, m, e);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1927_terminal_1N(JNIEnv* env, jclass) {
+  ThrowNative(env);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1927_test_1N(JNIEnv* env, jclass test) {
+  ThrowNative(env);
+  ClearAndPrintException(env, test);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1927_test_1N_1J(JNIEnv* env, jclass test) {
+  CallMethod(env, test, "terminal_J");
+  ClearAndPrintException(env, test);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1927_test_1N_1N(JNIEnv* env, jclass test) {
+  CallMethod(env, test, "terminal_N");
+  ClearAndPrintException(env, test);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1927_intermediate_1N_1J(JNIEnv* env, jclass test) {
+  CallMethod(env, test, "terminal_J");
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1927_intermediate_1N_1N(JNIEnv* env, jclass test) {
+  CallMethod(env, test, "terminal_N");
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1927_test_1N_1J_1J(JNIEnv* env, jclass test) {
+  CallMethod(env, test, "intermediate_J_J");
+  ClearAndPrintException(env, test);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1927_test_1N_1J_1N(JNIEnv* env, jclass test) {
+  CallMethod(env, test, "intermediate_J_N");
+  ClearAndPrintException(env, test);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1927_test_1N_1N_1J(JNIEnv* env, jclass test) {
+  CallMethod(env, test, "intermediate_N_J");
+  ClearAndPrintException(env, test);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1927_test_1N_1N_1N(JNIEnv* env, jclass test) {
+  CallMethod(env, test, "intermediate_N_N");
+  ClearAndPrintException(env, test);
+}
+
+}  // namespace Test1927ExceptionEvent
+}  // namespace art
diff --git a/test/1927-exception-event/expected.txt b/test/1927-exception-event/expected.txt
new file mode 100644
index 0000000..be8f39c
--- /dev/null
+++ b/test/1927-exception-event/expected.txt
@@ -0,0 +1,278 @@
+class art.Test1927$TestException
+Running test_J
+main: public static void art.Test1927.test_J() @ line = 110 throws class art.Test1927$TestException: from java
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static void art.Test1927.test_J() @ line = 110
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 239
+	Will be caught by: public static void art.Test1927.test_J() @ line = 111
+main: public static void art.Test1927.test_J() @ line = 111 caught class art.Test1927$TestException: from java
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 43
+		public static void art.Test1927.test_J() @ line = 111
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 239
+Caught exception: art.Test1927$TestException: from java
+Running test_N()
+Caught exception: art.Test1927$TestException: from native
+Running test_J_J()
+main: public static void art.Test1927.terminal_J() @ line = 103 throws class art.Test1927$TestException: from java
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static void art.Test1927.terminal_J() @ line = 103
+		public static void art.Test1927.test_J_J() @ line = 121
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 243
+	Will be caught by: public static void art.Test1927.test_J_J() @ line = 122
+main: public static void art.Test1927.test_J_J() @ line = 122 caught class art.Test1927$TestException: from java
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 43
+		public static void art.Test1927.test_J_J() @ line = 122
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 243
+Caught exception: art.Test1927$TestException: from java
+Running test_J_N()
+main: public static native void art.Test1927.terminal_N() @ line = -1 throws class art.Test1927$TestException: from native
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static native void art.Test1927.terminal_N() @ line = -1
+		public static void art.Test1927.test_J_N() @ line = 129
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 245
+	Will be caught by: public static void art.Test1927.test_J_N() @ line = 130
+main: public static void art.Test1927.test_J_N() @ line = 130 caught class art.Test1927$TestException: from native
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 43
+		public static void art.Test1927.test_J_N() @ line = 130
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 245
+Caught exception: art.Test1927$TestException: from native
+Running test_N_J()
+main: public static void art.Test1927.terminal_J() @ line = 103 throws class art.Test1927$TestException: from java
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static void art.Test1927.terminal_J() @ line = 103
+		public static native void art.Test1927.test_N_J() @ line = -1
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 247
+	Will be caught by: <UNKNOWN>
+Caught exception: art.Test1927$TestException: from java
+Running test_N_N()
+main: public static native void art.Test1927.terminal_N() @ line = -1 throws class art.Test1927$TestException: from native
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static native void art.Test1927.terminal_N() @ line = -1
+		public static native void art.Test1927.test_N_N() @ line = -1
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 249
+	Will be caught by: <UNKNOWN>
+Caught exception: art.Test1927$TestException: from native
+Running test_J_J_J()
+main: public static void art.Test1927.terminal_J() @ line = 103 throws class art.Test1927$TestException: from java
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static void art.Test1927.terminal_J() @ line = 103
+		public static void art.Test1927.intermediate_J_J() @ line = 138
+		public static void art.Test1927.test_J_J_J() @ line = 145
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 251
+	Will be caught by: public static void art.Test1927.test_J_J_J() @ line = 146
+main: public static void art.Test1927.test_J_J_J() @ line = 146 caught class art.Test1927$TestException: from java
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 43
+		public static void art.Test1927.test_J_J_J() @ line = 146
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 251
+Caught exception: art.Test1927$TestException: from java
+Running test_J_J_N()
+main: public static native void art.Test1927.terminal_N() @ line = -1 throws class art.Test1927$TestException: from native
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static native void art.Test1927.terminal_N() @ line = -1
+		public static void art.Test1927.intermediate_J_N() @ line = 139
+		public static void art.Test1927.test_J_J_N() @ line = 153
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 253
+	Will be caught by: public static void art.Test1927.test_J_J_N() @ line = 154
+main: public static void art.Test1927.test_J_J_N() @ line = 154 caught class art.Test1927$TestException: from native
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 43
+		public static void art.Test1927.test_J_J_N() @ line = 154
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 253
+Caught exception: art.Test1927$TestException: from native
+Running test_J_N_J()
+main: public static void art.Test1927.terminal_J() @ line = 103 throws class art.Test1927$TestException: from java
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static void art.Test1927.terminal_J() @ line = 103
+		public static native void art.Test1927.intermediate_N_J() @ line = -1
+		public static void art.Test1927.test_J_N_J() @ line = 161
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 255
+	Will be caught by: public static void art.Test1927.test_J_N_J() @ line = 162
+main: public static void art.Test1927.test_J_N_J() @ line = 162 caught class art.Test1927$TestException: from java
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 43
+		public static void art.Test1927.test_J_N_J() @ line = 162
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 255
+Caught exception: art.Test1927$TestException: from java
+Running test_J_N_N()
+main: public static native void art.Test1927.terminal_N() @ line = -1 throws class art.Test1927$TestException: from native
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static native void art.Test1927.terminal_N() @ line = -1
+		public static native void art.Test1927.intermediate_N_N() @ line = -1
+		public static void art.Test1927.test_J_N_N() @ line = 169
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 257
+	Will be caught by: public static void art.Test1927.test_J_N_N() @ line = 170
+main: public static void art.Test1927.test_J_N_N() @ line = 170 caught class art.Test1927$TestException: from native
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 43
+		public static void art.Test1927.test_J_N_N() @ line = 170
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 257
+Caught exception: art.Test1927$TestException: from native
+Running test_N_J_J()
+main: public static void art.Test1927.terminal_J() @ line = 103 throws class art.Test1927$TestException: from java
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static void art.Test1927.terminal_J() @ line = 103
+		public static void art.Test1927.intermediate_J_J() @ line = 138
+		public static native void art.Test1927.test_N_J_J() @ line = -1
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 259
+	Will be caught by: <UNKNOWN>
+Caught exception: art.Test1927$TestException: from java
+Running test_N_J_N()
+main: public static native void art.Test1927.terminal_N() @ line = -1 throws class art.Test1927$TestException: from native
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static native void art.Test1927.terminal_N() @ line = -1
+		public static void art.Test1927.intermediate_J_N() @ line = 139
+		public static native void art.Test1927.test_N_J_N() @ line = -1
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 261
+	Will be caught by: <UNKNOWN>
+Caught exception: art.Test1927$TestException: from native
+Running test_N_N_J()
+main: public static void art.Test1927.terminal_J() @ line = 103 throws class art.Test1927$TestException: from java
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static void art.Test1927.terminal_J() @ line = 103
+		public static native void art.Test1927.intermediate_N_J() @ line = -1
+		public static native void art.Test1927.test_N_N_J() @ line = -1
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 263
+	Will be caught by: <UNKNOWN>
+Caught exception: art.Test1927$TestException: from java
+Running test_N_N_N()
+main: public static native void art.Test1927.terminal_N() @ line = -1 throws class art.Test1927$TestException: from native
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static native void art.Test1927.terminal_N() @ line = -1
+		public static native void art.Test1927.intermediate_N_N() @ line = -1
+		public static native void art.Test1927.test_N_N_N() @ line = -1
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 265
+	Will be caught by: <UNKNOWN>
+Caught exception: art.Test1927$TestException: from native
+Running test_extra_N_J_J()
+main: public static void art.Test1927.terminal_J() @ line = 103 throws class art.Test1927$TestException: from java
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static void art.Test1927.terminal_J() @ line = 103
+		public static void art.Test1927.intermediate_J_J() @ line = 138
+		public static native void art.Test1927.test_N_J_J() @ line = -1
+		public static void art.Test1927.test_extra_N_J_J() @ line = 182
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 267
+	Will be caught by: public static void art.Test1927.test_extra_N_J_J() @ line = 183
+Caught exception: art.Test1927$TestException: from java
+Running test_extra_N_J_N()
+main: public static native void art.Test1927.terminal_N() @ line = -1 throws class art.Test1927$TestException: from native
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static native void art.Test1927.terminal_N() @ line = -1
+		public static void art.Test1927.intermediate_J_N() @ line = 139
+		public static native void art.Test1927.test_N_J_N() @ line = -1
+		public static void art.Test1927.test_extra_N_J_N() @ line = 189
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 269
+	Will be caught by: public static void art.Test1927.test_extra_N_J_N() @ line = 190
+Caught exception: art.Test1927$TestException: from native
+Running test_extra_N_N_J()
+main: public static void art.Test1927.terminal_J() @ line = 103 throws class art.Test1927$TestException: from java
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static void art.Test1927.terminal_J() @ line = 103
+		public static native void art.Test1927.intermediate_N_J() @ line = -1
+		public static native void art.Test1927.test_N_N_J() @ line = -1
+		public static void art.Test1927.test_extra_N_N_J() @ line = 196
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 271
+	Will be caught by: public static void art.Test1927.test_extra_N_N_J() @ line = 197
+Caught exception: art.Test1927$TestException: from java
+Running test_extra_N_N_N()
+main: public static native void art.Test1927.terminal_N() @ line = -1 throws class art.Test1927$TestException: from native
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1927.PrintStack() @ line = 28
+		public static void art.Test1927.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 66
+		public static native void art.Test1927.terminal_N() @ line = -1
+		public static native void art.Test1927.intermediate_N_N() @ line = -1
+		public static native void art.Test1927.test_N_N_N() @ line = -1
+		public static void art.Test1927.test_extra_N_N_N() @ line = 203
+		public static void art.Test1927.run() throws java.lang.Exception @ line = 273
+	Will be caught by: public static void art.Test1927.test_extra_N_N_N() @ line = 204
+Caught exception: art.Test1927$TestException: from native
diff --git a/test/1927-exception-event/info.txt b/test/1927-exception-event/info.txt
new file mode 100644
index 0000000..a74167f
--- /dev/null
+++ b/test/1927-exception-event/info.txt
@@ -0,0 +1,3 @@
+Test basic JVMTI exception event functionality
+
+Ensures that we can receive exception and exception catch events from JVMTI.
diff --git a/test/1927-exception-event/run b/test/1927-exception-event/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1927-exception-event/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1927-exception-event/src/Main.java b/test/1927-exception-event/src/Main.java
new file mode 100644
index 0000000..5b9b31b
--- /dev/null
+++ b/test/1927-exception-event/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1927.run();
+  }
+}
diff --git a/test/1927-exception-event/src/art/Breakpoint.java b/test/1927-exception-event/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1927-exception-event/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+  public static class Manager {
+    public static class BP {
+      public final Executable method;
+      public final long location;
+
+      public BP(Executable method) {
+        this(method, getStartLocation(method));
+      }
+
+      public BP(Executable method, long location) {
+        this.method = method;
+        this.location = location;
+      }
+
+      @Override
+      public boolean equals(Object other) {
+        return (other instanceof BP) &&
+            method.equals(((BP)other).method) &&
+            location == ((BP)other).location;
+      }
+
+      @Override
+      public String toString() {
+        return method.toString() + " @ " + getLine();
+      }
+
+      @Override
+      public int hashCode() {
+        return Objects.hash(method, location);
+      }
+
+      public int getLine() {
+        try {
+          LineNumber[] lines = getLineNumberTable(method);
+          int best = -1;
+          for (LineNumber l : lines) {
+            if (l.location > location) {
+              break;
+            } else {
+              best = l.line;
+            }
+          }
+          return best;
+        } catch (Exception e) {
+          return -1;
+        }
+      }
+    }
+
+    private Set<BP> breaks = new HashSet<>();
+
+    public void setBreakpoints(BP... bs) {
+      for (BP b : bs) {
+        if (breaks.add(b)) {
+          Breakpoint.setBreakpoint(b.method, b.location);
+        }
+      }
+    }
+    public void setBreakpoint(Executable method, long location) {
+      setBreakpoints(new BP(method, location));
+    }
+
+    public void clearBreakpoints(BP... bs) {
+      for (BP b : bs) {
+        if (breaks.remove(b)) {
+          Breakpoint.clearBreakpoint(b.method, b.location);
+        }
+      }
+    }
+    public void clearBreakpoint(Executable method, long location) {
+      clearBreakpoints(new BP(method, location));
+    }
+
+    public void clearAllBreakpoints() {
+      clearBreakpoints(breaks.toArray(new BP[0]));
+    }
+  }
+
+  public static void startBreakpointWatch(Class<?> methodClass,
+                                          Executable breakpointReached,
+                                          Thread thr) {
+    startBreakpointWatch(methodClass, breakpointReached, false, thr);
+  }
+
+  /**
+   * Enables the trapping of breakpoint events.
+   *
+   * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+   */
+  public static native void startBreakpointWatch(Class<?> methodClass,
+                                                 Executable breakpointReached,
+                                                 boolean allowRecursive,
+                                                 Thread thr);
+  public static native void stopBreakpointWatch(Thread thr);
+
+  public static final class LineNumber implements Comparable<LineNumber> {
+    public final long location;
+    public final int line;
+
+    private LineNumber(long loc, int line) {
+      this.location = loc;
+      this.line = line;
+    }
+
+    public boolean equals(Object other) {
+      return other instanceof LineNumber && ((LineNumber)other).line == line &&
+          ((LineNumber)other).location == location;
+    }
+
+    public int compareTo(LineNumber other) {
+      int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+      if (v != 0) {
+        return v;
+      } else {
+        return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+      }
+    }
+  }
+
+  public static native void setBreakpoint(Executable m, long loc);
+  public static void setBreakpoint(Executable m, LineNumber l) {
+    setBreakpoint(m, l.location);
+  }
+
+  public static native void clearBreakpoint(Executable m, long loc);
+  public static void clearBreakpoint(Executable m, LineNumber l) {
+    clearBreakpoint(m, l.location);
+  }
+
+  private static native Object[] getLineNumberTableNative(Executable m);
+  public static LineNumber[] getLineNumberTable(Executable m) {
+    Object[] nativeTable = getLineNumberTableNative(m);
+    long[] location = (long[])(nativeTable[0]);
+    int[] lines = (int[])(nativeTable[1]);
+    if (lines.length != location.length) {
+      throw new Error("Lines and locations have different lengths!");
+    }
+    LineNumber[] out = new LineNumber[lines.length];
+    for (int i = 0; i < lines.length; i++) {
+      out[i] = new LineNumber(location[i], lines[i]);
+    }
+    return out;
+  }
+
+  public static native long getStartLocation(Executable m);
+
+  public static int locationToLine(Executable m, long location) {
+    try {
+      Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+      int best = -1;
+      for (Breakpoint.LineNumber l : lines) {
+        if (l.location > location) {
+          break;
+        } else {
+          best = l.line;
+        }
+      }
+      return best;
+    } catch (Exception e) {
+      return -1;
+    }
+  }
+
+  public static long lineToLocation(Executable m, int line) throws Exception {
+    try {
+      Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+      for (Breakpoint.LineNumber l : lines) {
+        if (l.line == line) {
+          return l.location;
+        }
+      }
+      throw new Exception("Unable to find line " + line + " in " + m);
+    } catch (Exception e) {
+      throw new Exception("Unable to get line number info for " + m, e);
+    }
+  }
+}
+
diff --git a/test/1927-exception-event/src/art/Exceptions.java b/test/1927-exception-event/src/art/Exceptions.java
new file mode 100644
index 0000000..2c959ec
--- /dev/null
+++ b/test/1927-exception-event/src/art/Exceptions.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class Exceptions {
+  public static native void setupExceptionTracing(
+      Class<?> methodClass,
+      Class<?> exceptionClass,
+      Method exceptionEventMethod,
+      Method exceptionCaughtEventMethod);
+
+  public static native void enableExceptionCatchEvent(Thread thr);
+  public static native void enableExceptionEvent(Thread thr);
+  public static native void disableExceptionCatchEvent(Thread thr);
+  public static native void disableExceptionEvent(Thread thr);
+}
diff --git a/test/1927-exception-event/src/art/StackTrace.java b/test/1927-exception-event/src/art/StackTrace.java
new file mode 100644
index 0000000..b12c3df
--- /dev/null
+++ b/test/1927-exception-event/src/art/StackTrace.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+  public static class StackFrameData {
+    public final Thread thr;
+    public final Executable method;
+    public final long current_location;
+    public final int depth;
+
+    public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+      this.thr = thr;
+      this.method = e;
+      this.current_location = loc;
+      this.depth = depth;
+    }
+    @Override
+    public String toString() {
+      return String.format(
+          "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+          this.thr,
+          this.method,
+          this.current_location,
+          this.depth);
+    }
+  }
+
+  public static native int GetStackDepth(Thread thr);
+
+  private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+  public static StackFrameData[] GetStackTrace(Thread thr) {
+    // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+    // suspended. The spec says that not being suspended is fine but since we want this to be
+    // consistent we will suspend for the RI.
+    boolean suspend_thread =
+        !System.getProperty("java.vm.name").equals("Dalvik") &&
+        !thr.equals(Thread.currentThread());
+    if (suspend_thread) {
+      Suspension.suspend(thr);
+    }
+    StackFrameData[] out = nativeGetStackTrace(thr);
+    if (suspend_thread) {
+      Suspension.resume(thr);
+    }
+    return out;
+  }
+}
+
diff --git a/test/1927-exception-event/src/art/Suspension.java b/test/1927-exception-event/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1927-exception-event/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+  // Suspends a thread using jvmti.
+  public native static void suspend(Thread thr);
+
+  // Resumes a thread using jvmti.
+  public native static void resume(Thread thr);
+
+  public native static boolean isSuspended(Thread thr);
+
+  public native static int[] suspendList(Thread... threads);
+  public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1927-exception-event/src/art/Test1927.java b/test/1927-exception-event/src/art/Test1927.java
new file mode 100644
index 0000000..c2d13bb
--- /dev/null
+++ b/test/1927-exception-event/src/art/Test1927.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+
+public class Test1927 {
+  private static boolean PRINT_FULL_EXCEPTION = false;
+  private static void PrintStack() {
+    System.out.println("\tCurrent Stack:");
+    for (StackTrace.StackFrameData e : StackTrace.GetStackTrace(Thread.currentThread())) {
+      if (Objects.equals(e.method.getDeclaringClass().getPackage(), Test1927.class.getPackage())) {
+        System.out.println("\t\t" + e.method + " @ line = " +
+            Breakpoint.locationToLine(e.method, e.current_location));
+      }
+    }
+  }
+
+  public static void ExceptionCatchEvent(Thread thr,
+                                         Executable catch_method,
+                                         long catch_location,
+                                         Throwable exception) {
+    System.out.println(thr.getName() + ": " + catch_method + " @ line = " +
+        Breakpoint.locationToLine(catch_method, catch_location) + " caught " +
+        exception.getClass() + ": " + exception.getMessage());
+    PrintStack();
+    if (PRINT_FULL_EXCEPTION) {
+      System.out.print("exception is: ");
+      exception.printStackTrace(System.out);
+    }
+  }
+
+  public static void ExceptionEvent(Thread thr,
+                                    Executable throw_method,
+                                    long throw_location,
+                                    Throwable exception,
+                                    Executable catch_method,
+                                    long catch_location) {
+    System.out.println(thr.getName() + ": " + throw_method + " @ line = " +
+        Breakpoint.locationToLine(throw_method, throw_location) + " throws " +
+        exception.getClass() + ": " + exception.getMessage());
+    String catch_message;
+    if (catch_method == null) {
+      catch_message = "<UNKNOWN>";
+    } else {
+      catch_message = catch_method.toString() + " @ line = " +
+          Breakpoint.locationToLine(catch_method, catch_location);
+    }
+    PrintStack();
+    System.out.println("\tWill be caught by: " + catch_message);
+    if (PRINT_FULL_EXCEPTION) {
+      System.out.print("exception is: ");
+      exception.printStackTrace(System.out);
+    }
+  }
+
+  public static class TestException extends Error {
+    public TestException(String s) { super(s); }
+    public TestException() { super("from java"); }
+  }
+
+  // Possibilities
+  // ( -> is a JNI/Java call.)
+  // Furthest left catches/clears the exception
+  // Furthest right throws it.
+  // J
+  // N
+  // J -> J
+  // J -> N
+  // N -> J
+  // N -> N
+  // J -> J -> J
+  // J -> J -> N
+  // J -> N -> J
+  // J -> N -> N
+  // N -> J -> J
+  // N -> J -> N
+  // N -> N -> J
+  // N -> N -> N
+  // extra -> N -> J -> J
+  // extra -> N -> J -> N
+  // extra -> N -> N -> J
+  // extra -> N -> N -> N
+
+  public static void terminal_J() {
+    throw new TestException();
+  }
+
+  public static native void terminal_N();
+
+  public static void test_J() {
+    try {
+      throw new TestException();
+    } catch (TestException e) {
+      printException(e);
+    }
+  }
+
+  // Do test_J but native
+  public static native void test_N();
+
+  public static void test_J_J() {
+    try {
+      terminal_J();
+    } catch (TestException e) {
+      printException(e);
+    }
+  }
+
+  public static void test_J_N() {
+    try {
+      terminal_N();
+    } catch (TestException e) {
+      printException(e);
+    }
+  }
+
+  public static native void test_N_J();
+  public static native void test_N_N();
+
+  public static void intermediate_J_J() { terminal_J(); }
+  public static void intermediate_J_N() { terminal_N(); }
+  public static native void intermediate_N_J();
+  public static native void intermediate_N_N();
+
+  public static void test_J_J_J() {
+    try {
+      intermediate_J_J();
+    } catch (TestException e) {
+      printException(e);
+    }
+  }
+
+  public static void test_J_J_N() {
+    try {
+      intermediate_J_N();
+    } catch (TestException e) {
+      printException(e);
+    }
+  }
+
+  public static void test_J_N_J() {
+    try {
+      intermediate_N_J();
+    } catch (TestException e) {
+      printException(e);
+    }
+  }
+
+  public static void test_J_N_N() {
+    try {
+      intermediate_N_N();
+    } catch (TestException e) {
+      printException(e);
+    }
+  }
+
+  public static native void test_N_J_J();
+  public static native void test_N_J_N();
+  public static native void test_N_N_J();
+  public static native void test_N_N_N();
+
+  public static void test_extra_N_J_J() {
+    try {
+      test_N_J_J();
+    } catch (TestException e) {
+      printException(e);
+    }
+  }
+  public static void test_extra_N_J_N() {
+    try {
+      test_N_J_N();
+    } catch (TestException e) {
+      printException(e);
+    }
+  }
+  public static void test_extra_N_N_J() {
+    try {
+      test_N_N_J();
+    } catch (TestException e) {
+      printException(e);
+    }
+  }
+  public static void test_extra_N_N_N() {
+    try {
+      test_N_N_N();
+    } catch (TestException e) {
+      printException(e);
+    }
+  }
+
+  public static void printException(Throwable e) {
+    System.out.println("Caught exception: " + e);
+    if (PRINT_FULL_EXCEPTION) {
+      e.printStackTrace(System.out);
+    }
+  }
+
+  public static void run() throws Exception {
+    // Make sure classes are loaded first.
+    System.out.println(TestException.class.toString());
+    Exceptions.setupExceptionTracing(
+        Test1927.class,
+        TestException.class,
+        Test1927.class.getDeclaredMethod(
+            "ExceptionEvent",
+            Thread.class,
+            Executable.class,
+            Long.TYPE,
+            Throwable.class,
+            Executable.class,
+            Long.TYPE),
+        Test1927.class.getDeclaredMethod(
+            "ExceptionCatchEvent",
+            Thread.class,
+            Executable.class,
+            Long.TYPE,
+            Throwable.class));
+    Exceptions.enableExceptionEvent(Thread.currentThread());
+    Exceptions.enableExceptionCatchEvent(Thread.currentThread());
+    System.out.println("Running test_J");
+    test_J();
+    System.out.println("Running test_N()");
+    test_N();
+    System.out.println("Running test_J_J()");
+    test_J_J();
+    System.out.println("Running test_J_N()");
+    test_J_N();
+    System.out.println("Running test_N_J()");
+    test_N_J();
+    System.out.println("Running test_N_N()");
+    test_N_N();
+    System.out.println("Running test_J_J_J()");
+    test_J_J_J();
+    System.out.println("Running test_J_J_N()");
+    test_J_J_N();
+    System.out.println("Running test_J_N_J()");
+    test_J_N_J();
+    System.out.println("Running test_J_N_N()");
+    test_J_N_N();
+    System.out.println("Running test_N_J_J()");
+    test_N_J_J();
+    System.out.println("Running test_N_J_N()");
+    test_N_J_N();
+    System.out.println("Running test_N_N_J()");
+    test_N_N_J();
+    System.out.println("Running test_N_N_N()");
+    test_N_N_N();
+    System.out.println("Running test_extra_N_J_J()");
+    test_extra_N_J_J();
+    System.out.println("Running test_extra_N_J_N()");
+    test_extra_N_J_N();
+    System.out.println("Running test_extra_N_N_J()");
+    test_extra_N_N_J();
+    System.out.println("Running test_extra_N_N_N()");
+    test_extra_N_N_N();
+    Exceptions.disableExceptionCatchEvent(Thread.currentThread());
+    Exceptions.disableExceptionEvent(Thread.currentThread());
+  }
+}
diff --git a/test/1928-exception-event-exception/expected.txt b/test/1928-exception-event-exception/expected.txt
new file mode 100644
index 0000000..1692d04
--- /dev/null
+++ b/test/1928-exception-event-exception/expected.txt
@@ -0,0 +1,236 @@
+Test "art.Test1928$DoThrowClass": Running with handler "art.Test1928$DoNothingHandler"
+main: public static void art.Test1928.doThrow() @ line = 110 throws class art.Test1928$TestException: doThrow
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1928.PrintStack() @ line = 35
+		public static void art.Test1928.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 59
+		public static void art.Test1928.doThrow() @ line = 110
+		public void art.Test1928$DoThrowClass.run() @ line = 114
+		public static void art.Test1928.run() throws java.lang.Exception @ line = 196
+	Will be caught by: public static void art.Test1928.run() throws java.lang.Exception @ line = 199
+exception is: art.Test1928$TestException: doThrow
+	at art.Test1928.doThrow(Test1928.java:110)
+	at art.Test1928$DoThrowClass.run(Test1928.java:114)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+	Doing nothing!
+Test "art.Test1928$DoThrowClass": Caught error art.Test1928$TestException:"doThrow" with handler "art.Test1928$DoNothingHandler"
+art.Test1928$TestException: doThrow
+	at art.Test1928.doThrow(Test1928.java:110)
+	at art.Test1928$DoThrowClass.run(Test1928.java:114)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+Test "art.Test1928$DoThrowClass": Finished running with handler "art.Test1928$DoNothingHandler"
+Test "art.Test1928$DoThrowCatchBaseTestException": Running with handler "art.Test1928$DoNothingHandler"
+main: public static void art.Test1928.throwCatchBaseTestException() @ line = 119 throws class art.Test1928$TestException: throwCatchBaseTestException
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1928.PrintStack() @ line = 35
+		public static void art.Test1928.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 59
+		public static void art.Test1928.throwCatchBaseTestException() @ line = 119
+		public void art.Test1928$DoThrowCatchBaseTestException.run() @ line = 129
+		public static void art.Test1928.run() throws java.lang.Exception @ line = 196
+	Will be caught by: public static void art.Test1928.throwCatchBaseTestException() @ line = 120
+exception is: art.Test1928$TestException: throwCatchBaseTestException
+	at art.Test1928.throwCatchBaseTestException(Test1928.java:119)
+	at art.Test1928$DoThrowCatchBaseTestException.run(Test1928.java:129)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+	Doing nothing!
+Caught art.Test1928$TestException: "throwCatchBaseTestException"
+art.Test1928$TestException: throwCatchBaseTestException
+	at art.Test1928.throwCatchBaseTestException(Test1928.java:119)
+	at art.Test1928$DoThrowCatchBaseTestException.run(Test1928.java:129)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+Test "art.Test1928$DoThrowCatchBaseTestException": No error caught with handler "art.Test1928$DoNothingHandler"
+Test "art.Test1928$DoThrowCatchBaseTestException": Finished running with handler "art.Test1928$DoNothingHandler"
+Test "art.Test1928$DoThrowCatchTestException": Running with handler "art.Test1928$DoNothingHandler"
+main: public static void art.Test1928.throwCatchTestException() @ line = 134 throws class art.Test1928$TestException: throwCatchTestException
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1928.PrintStack() @ line = 35
+		public static void art.Test1928.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 59
+		public static void art.Test1928.throwCatchTestException() @ line = 134
+		public void art.Test1928$DoThrowCatchTestException.run() @ line = 144
+		public static void art.Test1928.run() throws java.lang.Exception @ line = 196
+	Will be caught by: public static void art.Test1928.throwCatchTestException() @ line = 135
+exception is: art.Test1928$TestException: throwCatchTestException
+	at art.Test1928.throwCatchTestException(Test1928.java:134)
+	at art.Test1928$DoThrowCatchTestException.run(Test1928.java:144)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+	Doing nothing!
+Caught art.Test1928$TestException: "throwCatchTestException"
+art.Test1928$TestException: throwCatchTestException
+	at art.Test1928.throwCatchTestException(Test1928.java:134)
+	at art.Test1928$DoThrowCatchTestException.run(Test1928.java:144)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+Test "art.Test1928$DoThrowCatchTestException": No error caught with handler "art.Test1928$DoNothingHandler"
+Test "art.Test1928$DoThrowCatchTestException": Finished running with handler "art.Test1928$DoNothingHandler"
+Test "art.Test1928$DoThrowCatchTestExceptionNoRethrow": Running with handler "art.Test1928$DoNothingHandler"
+main: public static void art.Test1928.throwCatchTestExceptionNoRethrow() @ line = 149 throws class art.Test1928$TestException: throwCatchTestExceptionNoRethrow
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1928.PrintStack() @ line = 35
+		public static void art.Test1928.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 59
+		public static void art.Test1928.throwCatchTestExceptionNoRethrow() @ line = 149
+		public void art.Test1928$DoThrowCatchTestExceptionNoRethrow.run() @ line = 159
+		public static void art.Test1928.run() throws java.lang.Exception @ line = 196
+	Will be caught by: public static void art.Test1928.run() throws java.lang.Exception @ line = 199
+exception is: art.Test1928$TestException: throwCatchTestExceptionNoRethrow
+	at art.Test1928.throwCatchTestExceptionNoRethrow(Test1928.java:149)
+	at art.Test1928$DoThrowCatchTestExceptionNoRethrow.run(Test1928.java:159)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+	Doing nothing!
+Test "art.Test1928$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1928$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1928$DoNothingHandler"
+art.Test1928$TestException: throwCatchTestExceptionNoRethrow
+	at art.Test1928.throwCatchTestExceptionNoRethrow(Test1928.java:149)
+	at art.Test1928$DoThrowCatchTestExceptionNoRethrow.run(Test1928.java:159)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+Test "art.Test1928$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1928$DoNothingHandler"
+Test "art.Test1928$DoThrowClass": Running with handler "art.Test1928$ThrowCatchBase"
+main: public static void art.Test1928.doThrow() @ line = 110 throws class art.Test1928$TestException: doThrow
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1928.PrintStack() @ line = 35
+		public static void art.Test1928.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 59
+		public static void art.Test1928.doThrow() @ line = 110
+		public void art.Test1928$DoThrowClass.run() @ line = 114
+		public static void art.Test1928.run() throws java.lang.Exception @ line = 196
+	Will be caught by: public static void art.Test1928.run() throws java.lang.Exception @ line = 199
+exception is: art.Test1928$TestException: doThrow
+	at art.Test1928.doThrow(Test1928.java:110)
+	at art.Test1928$DoThrowClass.run(Test1928.java:114)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+	Throwing BaseTestException and catching it!
+Caught art.Test1928$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1928.doThrow() @ line = 110"
+art.Test1928$BaseTestException: ThrowBaseHandler during throw from public static void art.Test1928.doThrow() @ line = 110
+	at art.Test1928$ThrowCatchBase.exceptionOccurred(Test1928.java:99)
+	at art.Test1928.ExceptionEvent(Test1928.java:66)
+	at art.Test1928.doThrow(Test1928.java:110)
+	at art.Test1928$DoThrowClass.run(Test1928.java:114)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+Caused by: art.Test1928$TestException: doThrow
+	... 4 more
+Test "art.Test1928$DoThrowClass": Caught error art.Test1928$TestException:"doThrow" with handler "art.Test1928$ThrowCatchBase"
+art.Test1928$TestException: doThrow
+	at art.Test1928.doThrow(Test1928.java:110)
+	at art.Test1928$DoThrowClass.run(Test1928.java:114)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+Test "art.Test1928$DoThrowClass": Finished running with handler "art.Test1928$ThrowCatchBase"
+Test "art.Test1928$DoThrowCatchBaseTestException": Running with handler "art.Test1928$ThrowCatchBase"
+main: public static void art.Test1928.throwCatchBaseTestException() @ line = 119 throws class art.Test1928$TestException: throwCatchBaseTestException
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1928.PrintStack() @ line = 35
+		public static void art.Test1928.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 59
+		public static void art.Test1928.throwCatchBaseTestException() @ line = 119
+		public void art.Test1928$DoThrowCatchBaseTestException.run() @ line = 129
+		public static void art.Test1928.run() throws java.lang.Exception @ line = 196
+	Will be caught by: public static void art.Test1928.throwCatchBaseTestException() @ line = 120
+exception is: art.Test1928$TestException: throwCatchBaseTestException
+	at art.Test1928.throwCatchBaseTestException(Test1928.java:119)
+	at art.Test1928$DoThrowCatchBaseTestException.run(Test1928.java:129)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+	Throwing BaseTestException and catching it!
+Caught art.Test1928$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1928.throwCatchBaseTestException() @ line = 119"
+art.Test1928$BaseTestException: ThrowBaseHandler during throw from public static void art.Test1928.throwCatchBaseTestException() @ line = 119
+	at art.Test1928$ThrowCatchBase.exceptionOccurred(Test1928.java:99)
+	at art.Test1928.ExceptionEvent(Test1928.java:66)
+	at art.Test1928.throwCatchBaseTestException(Test1928.java:119)
+	at art.Test1928$DoThrowCatchBaseTestException.run(Test1928.java:129)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+Caused by: art.Test1928$TestException: throwCatchBaseTestException
+	... 4 more
+Caught art.Test1928$TestException: "throwCatchBaseTestException"
+art.Test1928$TestException: throwCatchBaseTestException
+	at art.Test1928.throwCatchBaseTestException(Test1928.java:119)
+	at art.Test1928$DoThrowCatchBaseTestException.run(Test1928.java:129)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+Test "art.Test1928$DoThrowCatchBaseTestException": No error caught with handler "art.Test1928$ThrowCatchBase"
+Test "art.Test1928$DoThrowCatchBaseTestException": Finished running with handler "art.Test1928$ThrowCatchBase"
+Test "art.Test1928$DoThrowCatchTestException": Running with handler "art.Test1928$ThrowCatchBase"
+main: public static void art.Test1928.throwCatchTestException() @ line = 134 throws class art.Test1928$TestException: throwCatchTestException
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1928.PrintStack() @ line = 35
+		public static void art.Test1928.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 59
+		public static void art.Test1928.throwCatchTestException() @ line = 134
+		public void art.Test1928$DoThrowCatchTestException.run() @ line = 144
+		public static void art.Test1928.run() throws java.lang.Exception @ line = 196
+	Will be caught by: public static void art.Test1928.throwCatchTestException() @ line = 135
+exception is: art.Test1928$TestException: throwCatchTestException
+	at art.Test1928.throwCatchTestException(Test1928.java:134)
+	at art.Test1928$DoThrowCatchTestException.run(Test1928.java:144)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+	Throwing BaseTestException and catching it!
+Caught art.Test1928$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1928.throwCatchTestException() @ line = 134"
+art.Test1928$BaseTestException: ThrowBaseHandler during throw from public static void art.Test1928.throwCatchTestException() @ line = 134
+	at art.Test1928$ThrowCatchBase.exceptionOccurred(Test1928.java:99)
+	at art.Test1928.ExceptionEvent(Test1928.java:66)
+	at art.Test1928.throwCatchTestException(Test1928.java:134)
+	at art.Test1928$DoThrowCatchTestException.run(Test1928.java:144)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+Caused by: art.Test1928$TestException: throwCatchTestException
+	... 4 more
+Caught art.Test1928$TestException: "throwCatchTestException"
+art.Test1928$TestException: throwCatchTestException
+	at art.Test1928.throwCatchTestException(Test1928.java:134)
+	at art.Test1928$DoThrowCatchTestException.run(Test1928.java:144)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+Test "art.Test1928$DoThrowCatchTestException": No error caught with handler "art.Test1928$ThrowCatchBase"
+Test "art.Test1928$DoThrowCatchTestException": Finished running with handler "art.Test1928$ThrowCatchBase"
+Test "art.Test1928$DoThrowCatchTestExceptionNoRethrow": Running with handler "art.Test1928$ThrowCatchBase"
+main: public static void art.Test1928.throwCatchTestExceptionNoRethrow() @ line = 149 throws class art.Test1928$TestException: throwCatchTestExceptionNoRethrow
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1928.PrintStack() @ line = 35
+		public static void art.Test1928.ExceptionEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable,java.lang.reflect.Executable,long) @ line = 59
+		public static void art.Test1928.throwCatchTestExceptionNoRethrow() @ line = 149
+		public void art.Test1928$DoThrowCatchTestExceptionNoRethrow.run() @ line = 159
+		public static void art.Test1928.run() throws java.lang.Exception @ line = 196
+	Will be caught by: public static void art.Test1928.run() throws java.lang.Exception @ line = 199
+exception is: art.Test1928$TestException: throwCatchTestExceptionNoRethrow
+	at art.Test1928.throwCatchTestExceptionNoRethrow(Test1928.java:149)
+	at art.Test1928$DoThrowCatchTestExceptionNoRethrow.run(Test1928.java:159)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+	Throwing BaseTestException and catching it!
+Caught art.Test1928$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1928.throwCatchTestExceptionNoRethrow() @ line = 149"
+art.Test1928$BaseTestException: ThrowBaseHandler during throw from public static void art.Test1928.throwCatchTestExceptionNoRethrow() @ line = 149
+	at art.Test1928$ThrowCatchBase.exceptionOccurred(Test1928.java:99)
+	at art.Test1928.ExceptionEvent(Test1928.java:66)
+	at art.Test1928.throwCatchTestExceptionNoRethrow(Test1928.java:149)
+	at art.Test1928$DoThrowCatchTestExceptionNoRethrow.run(Test1928.java:159)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+Caused by: art.Test1928$TestException: throwCatchTestExceptionNoRethrow
+	... 4 more
+Test "art.Test1928$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1928$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1928$ThrowCatchBase"
+art.Test1928$TestException: throwCatchTestExceptionNoRethrow
+	at art.Test1928.throwCatchTestExceptionNoRethrow(Test1928.java:149)
+	at art.Test1928$DoThrowCatchTestExceptionNoRethrow.run(Test1928.java:159)
+	at art.Test1928.run(Test1928.java:196)
+	at Main.main(Main.java:19)
+Test "art.Test1928$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1928$ThrowCatchBase"
diff --git a/test/1928-exception-event-exception/info.txt b/test/1928-exception-event-exception/info.txt
new file mode 100644
index 0000000..ef84746
--- /dev/null
+++ b/test/1928-exception-event-exception/info.txt
@@ -0,0 +1,5 @@
+Test basic JVMTI exception event functionality.
+
+Ensures we can throw exceptions during the exception event without causing
+problems. Note that we do not allow exceptions to propogate past the event,
+matching RI behavior. See b/65049545.
diff --git a/test/1928-exception-event-exception/run b/test/1928-exception-event-exception/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1928-exception-event-exception/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1928-exception-event-exception/src/Main.java b/test/1928-exception-event-exception/src/Main.java
new file mode 100644
index 0000000..11cc773
--- /dev/null
+++ b/test/1928-exception-event-exception/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1928.run();
+  }
+}
diff --git a/test/1928-exception-event-exception/src/art/Breakpoint.java b/test/1928-exception-event-exception/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1928-exception-event-exception/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+  public static class Manager {
+    public static class BP {
+      public final Executable method;
+      public final long location;
+
+      public BP(Executable method) {
+        this(method, getStartLocation(method));
+      }
+
+      public BP(Executable method, long location) {
+        this.method = method;
+        this.location = location;
+      }
+
+      @Override
+      public boolean equals(Object other) {
+        return (other instanceof BP) &&
+            method.equals(((BP)other).method) &&
+            location == ((BP)other).location;
+      }
+
+      @Override
+      public String toString() {
+        return method.toString() + " @ " + getLine();
+      }
+
+      @Override
+      public int hashCode() {
+        return Objects.hash(method, location);
+      }
+
+      public int getLine() {
+        try {
+          LineNumber[] lines = getLineNumberTable(method);
+          int best = -1;
+          for (LineNumber l : lines) {
+            if (l.location > location) {
+              break;
+            } else {
+              best = l.line;
+            }
+          }
+          return best;
+        } catch (Exception e) {
+          return -1;
+        }
+      }
+    }
+
+    private Set<BP> breaks = new HashSet<>();
+
+    public void setBreakpoints(BP... bs) {
+      for (BP b : bs) {
+        if (breaks.add(b)) {
+          Breakpoint.setBreakpoint(b.method, b.location);
+        }
+      }
+    }
+    public void setBreakpoint(Executable method, long location) {
+      setBreakpoints(new BP(method, location));
+    }
+
+    public void clearBreakpoints(BP... bs) {
+      for (BP b : bs) {
+        if (breaks.remove(b)) {
+          Breakpoint.clearBreakpoint(b.method, b.location);
+        }
+      }
+    }
+    public void clearBreakpoint(Executable method, long location) {
+      clearBreakpoints(new BP(method, location));
+    }
+
+    public void clearAllBreakpoints() {
+      clearBreakpoints(breaks.toArray(new BP[0]));
+    }
+  }
+
+  public static void startBreakpointWatch(Class<?> methodClass,
+                                          Executable breakpointReached,
+                                          Thread thr) {
+    startBreakpointWatch(methodClass, breakpointReached, false, thr);
+  }
+
+  /**
+   * Enables the trapping of breakpoint events.
+   *
+   * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+   */
+  public static native void startBreakpointWatch(Class<?> methodClass,
+                                                 Executable breakpointReached,
+                                                 boolean allowRecursive,
+                                                 Thread thr);
+  public static native void stopBreakpointWatch(Thread thr);
+
+  public static final class LineNumber implements Comparable<LineNumber> {
+    public final long location;
+    public final int line;
+
+    private LineNumber(long loc, int line) {
+      this.location = loc;
+      this.line = line;
+    }
+
+    public boolean equals(Object other) {
+      return other instanceof LineNumber && ((LineNumber)other).line == line &&
+          ((LineNumber)other).location == location;
+    }
+
+    public int compareTo(LineNumber other) {
+      int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+      if (v != 0) {
+        return v;
+      } else {
+        return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+      }
+    }
+  }
+
+  public static native void setBreakpoint(Executable m, long loc);
+  public static void setBreakpoint(Executable m, LineNumber l) {
+    setBreakpoint(m, l.location);
+  }
+
+  public static native void clearBreakpoint(Executable m, long loc);
+  public static void clearBreakpoint(Executable m, LineNumber l) {
+    clearBreakpoint(m, l.location);
+  }
+
+  private static native Object[] getLineNumberTableNative(Executable m);
+  public static LineNumber[] getLineNumberTable(Executable m) {
+    Object[] nativeTable = getLineNumberTableNative(m);
+    long[] location = (long[])(nativeTable[0]);
+    int[] lines = (int[])(nativeTable[1]);
+    if (lines.length != location.length) {
+      throw new Error("Lines and locations have different lengths!");
+    }
+    LineNumber[] out = new LineNumber[lines.length];
+    for (int i = 0; i < lines.length; i++) {
+      out[i] = new LineNumber(location[i], lines[i]);
+    }
+    return out;
+  }
+
+  public static native long getStartLocation(Executable m);
+
+  public static int locationToLine(Executable m, long location) {
+    try {
+      Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+      int best = -1;
+      for (Breakpoint.LineNumber l : lines) {
+        if (l.location > location) {
+          break;
+        } else {
+          best = l.line;
+        }
+      }
+      return best;
+    } catch (Exception e) {
+      return -1;
+    }
+  }
+
+  public static long lineToLocation(Executable m, int line) throws Exception {
+    try {
+      Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+      for (Breakpoint.LineNumber l : lines) {
+        if (l.line == line) {
+          return l.location;
+        }
+      }
+      throw new Exception("Unable to find line " + line + " in " + m);
+    } catch (Exception e) {
+      throw new Exception("Unable to get line number info for " + m, e);
+    }
+  }
+}
+
diff --git a/test/1928-exception-event-exception/src/art/Exceptions.java b/test/1928-exception-event-exception/src/art/Exceptions.java
new file mode 100644
index 0000000..2c959ec
--- /dev/null
+++ b/test/1928-exception-event-exception/src/art/Exceptions.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class Exceptions {
+  public static native void setupExceptionTracing(
+      Class<?> methodClass,
+      Class<?> exceptionClass,
+      Method exceptionEventMethod,
+      Method exceptionCaughtEventMethod);
+
+  public static native void enableExceptionCatchEvent(Thread thr);
+  public static native void enableExceptionEvent(Thread thr);
+  public static native void disableExceptionCatchEvent(Thread thr);
+  public static native void disableExceptionEvent(Thread thr);
+}
diff --git a/test/1928-exception-event-exception/src/art/StackTrace.java b/test/1928-exception-event-exception/src/art/StackTrace.java
new file mode 100644
index 0000000..b12c3df
--- /dev/null
+++ b/test/1928-exception-event-exception/src/art/StackTrace.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+  public static class StackFrameData {
+    public final Thread thr;
+    public final Executable method;
+    public final long current_location;
+    public final int depth;
+
+    public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+      this.thr = thr;
+      this.method = e;
+      this.current_location = loc;
+      this.depth = depth;
+    }
+    @Override
+    public String toString() {
+      return String.format(
+          "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+          this.thr,
+          this.method,
+          this.current_location,
+          this.depth);
+    }
+  }
+
+  public static native int GetStackDepth(Thread thr);
+
+  private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+  public static StackFrameData[] GetStackTrace(Thread thr) {
+    // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+    // suspended. The spec says that not being suspended is fine but since we want this to be
+    // consistent we will suspend for the RI.
+    boolean suspend_thread =
+        !System.getProperty("java.vm.name").equals("Dalvik") &&
+        !thr.equals(Thread.currentThread());
+    if (suspend_thread) {
+      Suspension.suspend(thr);
+    }
+    StackFrameData[] out = nativeGetStackTrace(thr);
+    if (suspend_thread) {
+      Suspension.resume(thr);
+    }
+    return out;
+  }
+}
+
diff --git a/test/1928-exception-event-exception/src/art/Suspension.java b/test/1928-exception-event-exception/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1928-exception-event-exception/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+  // Suspends a thread using jvmti.
+  public native static void suspend(Thread thr);
+
+  // Resumes a thread using jvmti.
+  public native static void resume(Thread thr);
+
+  public native static boolean isSuspended(Thread thr);
+
+  public native static int[] suspendList(Thread... threads);
+  public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1928-exception-event-exception/src/art/Test1928.java b/test/1928-exception-event-exception/src/art/Test1928.java
new file mode 100644
index 0000000..aec88a4
--- /dev/null
+++ b/test/1928-exception-event-exception/src/art/Test1928.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+
+public class Test1928 {
+  public static boolean PRINT_FULL_EXCEPTION = true;
+  public static ExceptionHandler HANDLER = null;
+
+  public static interface ExceptionHandler {
+    public void exceptionOccurred(
+        Executable m, long loc, Throwable exception, Executable catch_m, long catch_l);
+  }
+
+  private static void PrintStack() {
+    System.out.println("\tCurrent Stack:");
+    for (StackTrace.StackFrameData e : StackTrace.GetStackTrace(Thread.currentThread())) {
+      if (Objects.equals(e.method.getDeclaringClass().getPackage(), Test1928.class.getPackage())) {
+        System.out.println("\t\t" + e.method + " @ line = " +
+            Breakpoint.locationToLine(e.method, e.current_location));
+      }
+    }
+  }
+
+  public static void ExceptionEvent(Thread thr,
+                                    Executable throw_method,
+                                    long throw_location,
+                                    Throwable exception,
+                                    Executable catch_method,
+                                    long catch_location) {
+    System.out.println(thr.getName() + ": " + throw_method + " @ line = " +
+        Breakpoint.locationToLine(throw_method, throw_location) + " throws " +
+        exception.getClass() + ": " + exception.getMessage());
+    String catch_message;
+    if (catch_method == null) {
+      catch_message = "<UNKNOWN>";
+    } else {
+      catch_message = catch_method.toString() + " @ line = " +
+          Breakpoint.locationToLine(catch_method, catch_location);
+    }
+    PrintStack();
+    System.out.println("\tWill be caught by: " + catch_message);
+    if (PRINT_FULL_EXCEPTION) {
+      System.out.print("exception is: ");
+      exception.printStackTrace(System.out);
+    }
+    if (HANDLER != null) {
+      HANDLER.exceptionOccurred(
+          throw_method, throw_location, exception, catch_method, catch_location);
+    }
+  }
+
+  public static class BaseTestException extends Error {
+    public BaseTestException(String e) { super(e); }
+    public BaseTestException(String e, Throwable t) { super(e, t); }
+  }
+  public static class TestException extends BaseTestException {
+    public TestException(String e) { super(e); }
+    public TestException(String e, Throwable t) { super(e, t); }
+  }
+
+  public static class TestExceptionNoRethrow extends TestException {
+    public TestExceptionNoRethrow(String e) { super(e); }
+    public TestExceptionNoRethrow(String e, Throwable t) { super(e, t); }
+  }
+
+  public static class DoNothingHandler implements ExceptionHandler {
+    public void exceptionOccurred(
+        Executable m, long loc, Throwable exception, Executable catch_m, long catch_l) {
+      System.out.println("\tDoing nothing!");
+      return;
+    }
+  }
+
+  public static class ThrowCatchBase implements ExceptionHandler {
+    public void exceptionOccurred(
+        Executable m, long loc, Throwable exception, Executable catch_m, long catch_l) {
+      System.out.println("\tThrowing BaseTestException and catching it!");
+      try {
+        throw new BaseTestException("ThrowBaseHandler during throw from " + m + " @ line = " +
+            Breakpoint.locationToLine(m, loc), exception);
+      } catch (BaseTestException t) {
+        System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+        if (PRINT_FULL_EXCEPTION) {
+          t.printStackTrace(System.out);
+        }
+      }
+    }
+  }
+
+  public static void doThrow() {
+    throw new TestException("doThrow");
+  }
+
+  public static class DoThrowClass implements Runnable {
+    public void run() { doThrow(); }
+  }
+
+  public static void throwCatchBaseTestException() {
+    try {
+      throw new TestException("throwCatchBaseTestException");
+    } catch (BaseTestException t) {
+      System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+      if (PRINT_FULL_EXCEPTION) {
+        t.printStackTrace(System.out);
+      }
+    }
+  }
+
+  public static class DoThrowCatchBaseTestException implements Runnable {
+    public void run() { throwCatchBaseTestException(); }
+  }
+
+  public static void throwCatchTestException() {
+    try {
+      throw new TestException("throwCatchTestException");
+    } catch (TestException t) {
+      System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+      if (PRINT_FULL_EXCEPTION) {
+        t.printStackTrace(System.out);
+      }
+    }
+  }
+
+  public static class DoThrowCatchTestException implements Runnable {
+    public void run() { throwCatchTestException(); }
+  }
+
+  public static void throwCatchTestExceptionNoRethrow() {
+    try {
+      throw new TestException("throwCatchTestExceptionNoRethrow");
+    } catch (TestExceptionNoRethrow t) {
+      System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+      if (PRINT_FULL_EXCEPTION) {
+        t.printStackTrace(System.out);
+      }
+    }
+  }
+
+  public static class DoThrowCatchTestExceptionNoRethrow implements Runnable {
+    public void run() { throwCatchTestExceptionNoRethrow(); }
+  }
+
+  public static void run() throws Exception {
+    // Set up
+    Exceptions.setupExceptionTracing(
+        Test1928.class,
+        TestException.class,
+        Test1928.class.getDeclaredMethod(
+            "ExceptionEvent",
+            Thread.class,
+            Executable.class,
+            Long.TYPE,
+            Throwable.class,
+            Executable.class,
+            Long.TYPE),
+        null);
+    Exceptions.enableExceptionEvent(Thread.currentThread());
+
+    ExceptionHandler[] handlers = new ExceptionHandler[] {
+      new DoNothingHandler(),
+      new ThrowCatchBase(),
+    };
+
+    Runnable[] tests = new Runnable[] {
+      new DoThrowClass(),
+      new DoThrowCatchBaseTestException(),
+      new DoThrowCatchTestException(),
+      new DoThrowCatchTestExceptionNoRethrow(),
+    };
+
+    for (ExceptionHandler handler : handlers) {
+      for (Runnable test : tests) {
+        try {
+          HANDLER = handler;
+          System.out.printf("Test \"%s\": Running with handler \"%s\"\n",
+              test.getClass().getName(), handler.getClass().getName());
+          test.run();
+          System.out.printf("Test \"%s\": No error caught with handler \"%s\"\n",
+              test.getClass().getName(), handler.getClass().getName());
+        } catch (Throwable e) {
+          System.out.printf("Test \"%s\": Caught error %s:\"%s\" with handler \"%s\"\n",
+              test.getClass().getName(),
+              e.getClass().getName(),
+              e.getMessage(),
+              handler.getClass().getName());
+          if (PRINT_FULL_EXCEPTION) {
+            e.printStackTrace(System.out);
+          }
+        }
+        System.out.printf("Test \"%s\": Finished running with handler \"%s\"\n",
+            test.getClass().getName(), handler.getClass().getName());
+        HANDLER = null;
+      }
+    }
+    Exceptions.disableExceptionEvent(Thread.currentThread());
+  }
+}
diff --git a/test/1929-exception-catch-exception/expected.txt b/test/1929-exception-catch-exception/expected.txt
new file mode 100644
index 0000000..7c23a31
--- /dev/null
+++ b/test/1929-exception-catch-exception/expected.txt
@@ -0,0 +1,302 @@
+Test "art.Test1929$DoThrowClass": Running breakpoint with handler "art.Test1929$DoNothingHandler"
+main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: doThrow
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 283
+Test "art.Test1929$DoThrowClass": Caught error art.Test1929$TestException:"doThrow" with handler "art.Test1929$DoNothingHandler"
+Test "art.Test1929$DoThrowClass": Finished running with handler "art.Test1929$DoNothingHandler"
+Test "art.Test1929$DoThrowCatchBaseTestException": Running breakpoint with handler "art.Test1929$DoNothingHandler"
+main: public static void art.Test1929.throwCatchBaseTestException() @ line = 140 caught class art.Test1929$TestException: throwCatchBaseTestException
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929.throwCatchBaseTestException() @ line = 140
+		public void art.Test1929$DoThrowCatchBaseTestException.run() @ line = 149
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 280
+	Doing nothing!
+Caught art.Test1929$TestException: "throwCatchBaseTestException"
+Test "art.Test1929$DoThrowCatchBaseTestException": No error caught with handler "art.Test1929$DoNothingHandler"
+Test "art.Test1929$DoThrowCatchBaseTestException": Finished running with handler "art.Test1929$DoNothingHandler"
+Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Running breakpoint with handler "art.Test1929$DoNothingHandler"
+main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161
+		public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 197
+		public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 201
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 280
+Caught art.Test1929$TestException: "throwCatchBaseTestExceptionTwice"
+Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": No error caught with handler "art.Test1929$DoNothingHandler"
+Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Finished running with handler "art.Test1929$DoNothingHandler"
+Test "art.Test1929$DoThrowCatchTestException": Running breakpoint with handler "art.Test1929$DoNothingHandler"
+main: public static void art.Test1929.throwCatchTestException() @ line = 207 caught class art.Test1929$TestException: throwCatchTestException
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929.throwCatchTestException() @ line = 207
+		public void art.Test1929$DoThrowCatchTestException.run() @ line = 216
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 280
+	Doing nothing!
+Caught art.Test1929$TestException: "throwCatchTestException"
+Test "art.Test1929$DoThrowCatchTestException": No error caught with handler "art.Test1929$DoNothingHandler"
+Test "art.Test1929$DoThrowCatchTestException": Finished running with handler "art.Test1929$DoNothingHandler"
+Test "art.Test1929$DoThrowCatchTestExceptionTwice": Running breakpoint with handler "art.Test1929$DoNothingHandler"
+main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 caught class art.Test1929$TestException: throwCatchTestExceptionTwice
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179
+		public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 222
+		public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 226
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 280
+Caught art.Test1929$TestException: "throwCatchTestExceptionTwice"
+Test "art.Test1929$DoThrowCatchTestExceptionTwice": No error caught with handler "art.Test1929$DoNothingHandler"
+Test "art.Test1929$DoThrowCatchTestExceptionTwice": Finished running with handler "art.Test1929$DoNothingHandler"
+Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Running breakpoint with handler "art.Test1929$DoNothingHandler"
+main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 283
+Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1929$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1929$DoNothingHandler"
+Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1929$DoNothingHandler"
+Test "art.Test1929$DoThrowClass": Running breakpoint with handler "art.Test1929$ThrowCatchBase"
+main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: doThrow
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 283
+Test "art.Test1929$DoThrowClass": Caught error art.Test1929$TestException:"doThrow" with handler "art.Test1929$ThrowCatchBase"
+Test "art.Test1929$DoThrowClass": Finished running with handler "art.Test1929$ThrowCatchBase"
+Test "art.Test1929$DoThrowCatchBaseTestException": Running breakpoint with handler "art.Test1929$ThrowCatchBase"
+main: public static void art.Test1929.throwCatchBaseTestException() @ line = 140 caught class art.Test1929$TestException: throwCatchBaseTestException
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929.throwCatchBaseTestException() @ line = 140
+		public void art.Test1929$DoThrowCatchBaseTestException.run() @ line = 149
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 280
+	Throwing BaseTestException and catching it!
+Caught art.Test1929$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1929.throwCatchBaseTestException() @ line = 140"
+Caught art.Test1929$TestException: "throwCatchBaseTestException"
+Test "art.Test1929$DoThrowCatchBaseTestException": No error caught with handler "art.Test1929$ThrowCatchBase"
+Test "art.Test1929$DoThrowCatchBaseTestException": Finished running with handler "art.Test1929$ThrowCatchBase"
+Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowCatchBase"
+main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161
+		public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 197
+		public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 201
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 280
+Caught art.Test1929$TestException: "throwCatchBaseTestExceptionTwice"
+Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": No error caught with handler "art.Test1929$ThrowCatchBase"
+Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Finished running with handler "art.Test1929$ThrowCatchBase"
+Test "art.Test1929$DoThrowCatchTestException": Running breakpoint with handler "art.Test1929$ThrowCatchBase"
+main: public static void art.Test1929.throwCatchTestException() @ line = 207 caught class art.Test1929$TestException: throwCatchTestException
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929.throwCatchTestException() @ line = 207
+		public void art.Test1929$DoThrowCatchTestException.run() @ line = 216
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 280
+	Throwing BaseTestException and catching it!
+Caught art.Test1929$BaseTestException: "ThrowBaseHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 207"
+Caught art.Test1929$TestException: "throwCatchTestException"
+Test "art.Test1929$DoThrowCatchTestException": No error caught with handler "art.Test1929$ThrowCatchBase"
+Test "art.Test1929$DoThrowCatchTestException": Finished running with handler "art.Test1929$ThrowCatchBase"
+Test "art.Test1929$DoThrowCatchTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowCatchBase"
+main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 caught class art.Test1929$TestException: throwCatchTestExceptionTwice
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179
+		public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 222
+		public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 226
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 280
+Caught art.Test1929$TestException: "throwCatchTestExceptionTwice"
+Test "art.Test1929$DoThrowCatchTestExceptionTwice": No error caught with handler "art.Test1929$ThrowCatchBase"
+Test "art.Test1929$DoThrowCatchTestExceptionTwice": Finished running with handler "art.Test1929$ThrowCatchBase"
+Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Running breakpoint with handler "art.Test1929$ThrowCatchBase"
+main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 283
+Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1929$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1929$ThrowCatchBase"
+Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1929$ThrowCatchBase"
+Test "art.Test1929$DoThrowClass": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: doThrow
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 283
+Test "art.Test1929$DoThrowClass": Caught error art.Test1929$TestException:"doThrow" with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+Test "art.Test1929$DoThrowClass": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+Test "art.Test1929$DoThrowCatchBaseTestException": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+main: public static void art.Test1929.throwCatchBaseTestException() @ line = 140 caught class art.Test1929$TestException: throwCatchBaseTestException
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929.throwCatchBaseTestException() @ line = 140
+		public void art.Test1929$DoThrowCatchBaseTestException.run() @ line = 149
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 280
+	Throwing BaseTestException!
+Test "art.Test1929$DoThrowCatchBaseTestException": Caught error art.Test1929$BaseTestException:"ThrowBaseHandler during throw from public static void art.Test1929.throwCatchBaseTestException() @ line = 140" with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+Test "art.Test1929$DoThrowCatchBaseTestException": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161
+		public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 197
+		public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 201
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 280
+Caught art.Test1929$TestException: "throwCatchBaseTestExceptionTwice"
+Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": No error caught with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+Test "art.Test1929$DoThrowCatchTestException": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+main: public static void art.Test1929.throwCatchTestException() @ line = 207 caught class art.Test1929$TestException: throwCatchTestException
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929.throwCatchTestException() @ line = 207
+		public void art.Test1929$DoThrowCatchTestException.run() @ line = 216
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 280
+	Throwing BaseTestException!
+Test "art.Test1929$DoThrowCatchTestException": Caught error art.Test1929$BaseTestException:"ThrowBaseHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 207" with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+Test "art.Test1929$DoThrowCatchTestException": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+Test "art.Test1929$DoThrowCatchTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 caught class art.Test1929$TestException: throwCatchTestExceptionTwice
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179
+		public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 222
+		public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 226
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 280
+Caught art.Test1929$TestException: "throwCatchTestExceptionTwice"
+Test "art.Test1929$DoThrowCatchTestExceptionTwice": No error caught with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+Test "art.Test1929$DoThrowCatchTestExceptionTwice": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Running breakpoint with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 283
+Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1929$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1929$ThrowBaseTestExceptionHandler"
+Test "art.Test1929$DoThrowClass": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: doThrow
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 283
+Test "art.Test1929$DoThrowClass": Caught error art.Test1929$TestException:"doThrow" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+Test "art.Test1929$DoThrowClass": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+Test "art.Test1929$DoThrowCatchBaseTestException": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+main: public static void art.Test1929.throwCatchBaseTestException() @ line = 140 caught class art.Test1929$TestException: throwCatchBaseTestException
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929.throwCatchBaseTestException() @ line = 140
+		public void art.Test1929$DoThrowCatchBaseTestException.run() @ line = 149
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 280
+	Throwing TestExceptionNoRethrow!
+Test "art.Test1929$DoThrowCatchBaseTestException": Caught error art.Test1929$TestExceptionNoRethrow:"ThrowTestExceptionNoRethrowHandler during throw from public static void art.Test1929.throwCatchBaseTestException() @ line = 140" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+Test "art.Test1929$DoThrowCatchBaseTestException": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+main: public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161 caught class art.Test1929$TestException: throwCatchBaseTestExceptionTwice
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929$Impl.throwCatchBaseTestExceptionTwiceImpl() @ line = 161
+		public static void art.Test1929.throwCatchBaseTestExceptionTwice() @ line = 197
+		public void art.Test1929$DoThrowCatchBaseTestExceptionTwice.run() @ line = 201
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 280
+Caught art.Test1929$TestException: "throwCatchBaseTestExceptionTwice"
+Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": No error caught with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+Test "art.Test1929$DoThrowCatchBaseTestExceptionTwice": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+Test "art.Test1929$DoThrowCatchTestException": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+main: public static void art.Test1929.throwCatchTestException() @ line = 207 caught class art.Test1929$TestException: throwCatchTestException
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929.throwCatchTestException() @ line = 207
+		public void art.Test1929$DoThrowCatchTestException.run() @ line = 216
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 280
+	Throwing TestExceptionNoRethrow!
+Test "art.Test1929$DoThrowCatchTestException": Caught error art.Test1929$TestExceptionNoRethrow:"ThrowTestExceptionNoRethrowHandler during throw from public static void art.Test1929.throwCatchTestException() @ line = 207" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+Test "art.Test1929$DoThrowCatchTestException": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+Test "art.Test1929$DoThrowCatchTestExceptionTwice": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+main: public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179 caught class art.Test1929$TestException: throwCatchTestExceptionTwice
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929$Impl.throwCatchTestExceptionTwiceImpl() @ line = 179
+		public static void art.Test1929.throwCatchTestExceptionTwice() @ line = 222
+		public void art.Test1929$DoThrowCatchTestExceptionTwice.run() @ line = 226
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 280
+Caught art.Test1929$TestException: "throwCatchTestExceptionTwice"
+Test "art.Test1929$DoThrowCatchTestExceptionTwice": No error caught with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+Test "art.Test1929$DoThrowCatchTestExceptionTwice": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Running breakpoint with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+main: public static void art.Test1929.run() throws java.lang.Exception @ line = 283 caught class art.Test1929$TestException: throwCatchTestExceptionNoRethrow
+	Current Stack:
+		private static native art.StackTrace$StackFrameData[] art.StackTrace.nativeGetStackTrace(java.lang.Thread) @ line = -1
+		public static art.StackTrace$StackFrameData[] art.StackTrace.GetStackTrace(java.lang.Thread) @ line = 60
+		private static void art.Test1929.PrintStack() @ line = 52
+		public static void art.Test1929.ExceptionCatchEvent(java.lang.Thread,java.lang.reflect.Executable,long,java.lang.Throwable) @ line = 65
+		public static void art.Test1929.run() throws java.lang.Exception @ line = 283
+Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Caught error art.Test1929$TestException:"throwCatchTestExceptionNoRethrow" with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
+Test "art.Test1929$DoThrowCatchTestExceptionNoRethrow": Finished running with handler "art.Test1929$ThrowTestExceptionNoRethrowHandler"
diff --git a/test/1929-exception-catch-exception/info.txt b/test/1929-exception-catch-exception/info.txt
new file mode 100644
index 0000000..2d9b3a0
--- /dev/null
+++ b/test/1929-exception-catch-exception/info.txt
@@ -0,0 +1 @@
+Test JVMTI behavior when throwing exceptions during the Exception catch Event.
diff --git a/test/1929-exception-catch-exception/run b/test/1929-exception-catch-exception/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1929-exception-catch-exception/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1929-exception-catch-exception/smali/art/Test1929$Impl.smali b/test/1929-exception-catch-exception/smali/art/Test1929$Impl.smali
new file mode 100644
index 0000000..4edd56f
--- /dev/null
+++ b/test/1929-exception-catch-exception/smali/art/Test1929$Impl.smali
@@ -0,0 +1,363 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# The standard dx/jack/d8 all would leave the move-exception instructions outside of either catch
+# block. This is different from the RI which will leave the corresponding aload.
+#
+# See b/65203529 for more information.
+
+.class public final Lart/Test1929$Impl;
+.super Ljava/lang/Object;
+.source "Test1929.java"
+
+
+# annotations
+.annotation system Ldalvik/annotation/EnclosingClass;
+    value = Lart/Test1929;
+.end annotation
+
+.annotation system Ldalvik/annotation/InnerClass;
+    accessFlags = 0x19
+    name = "Impl"
+.end annotation
+
+
+# direct methods
+.method private constructor <init>()V
+    .registers 1
+
+    .prologue
+    .line 152
+    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+
+    return-void
+.end method
+
+.method public static throwCatchBaseTestExceptionTwiceImpl()V
+    .registers 4
+
+    .prologue
+    .line 156
+    :try_start_0
+    new-instance v1, Lart/Test1929$TestException;
+
+    const-string v2, "throwCatchBaseTestExceptionTwice"
+
+    invoke-direct {v1, v2}, Lart/Test1929$TestException;-><init>(Ljava/lang/String;)V
+
+    throw v1
+    :try_end_8
+    .catch Lart/Test1929$BaseTestException; {:try_start_0 .. :try_end_8} :catch_8
+
+    .line 157
+    :catch_8
+    # This try needs to include the move-exception
+    :try_start_9
+    move-exception v0
+
+    .line 158
+    .local v0, "t":Lart/Test1929$BaseTestException;
+    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    new-instance v2, Ljava/lang/StringBuilder;
+
+    invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V
+
+    const-string v3, "Caught "
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    invoke-virtual {v0}, Ljava/lang/Object;->getClass()Ljava/lang/Class;
+
+    move-result-object v3
+
+    invoke-virtual {v3}, Ljava/lang/Class;->getName()Ljava/lang/String;
+
+    move-result-object v3
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    const-string v3, ": \""
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    invoke-virtual {v0}, Lart/Test1929$BaseTestException;->getMessage()Ljava/lang/String;
+
+    move-result-object v3
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    const-string v3, "\""
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
+
+    move-result-object v2
+
+    invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+    .line 159
+    sget-boolean v1, Lart/Test1929;->PRINT_FULL_EXCEPTION:Z
+
+    if-eqz v1, :cond_46
+
+    .line 160
+    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    invoke-virtual {v0, v1}, Lart/Test1929$BaseTestException;->printStackTrace(Ljava/io/PrintStream;)V
+    :try_end_46
+    .catch Lart/Test1929$BaseTestException; {:try_start_9 .. :try_end_46} :catch_47
+
+    .line 169
+    :cond_46
+    :goto_46
+    return-void
+
+    .line 163
+    :catch_47
+    move-exception v0
+
+    .line 164
+    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    new-instance v2, Ljava/lang/StringBuilder;
+
+    invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V
+
+    const-string v3, "2nd Caught "
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    invoke-virtual {v0}, Ljava/lang/Object;->getClass()Ljava/lang/Class;
+
+    move-result-object v3
+
+    invoke-virtual {v3}, Ljava/lang/Class;->getName()Ljava/lang/String;
+
+    move-result-object v3
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    const-string v3, ": \""
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    invoke-virtual {v0}, Lart/Test1929$BaseTestException;->getMessage()Ljava/lang/String;
+
+    move-result-object v3
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    const-string v3, "\""
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
+
+    move-result-object v2
+
+    invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+    .line 165
+    sget-boolean v1, Lart/Test1929;->PRINT_FULL_EXCEPTION:Z
+
+    if-eqz v1, :cond_46
+
+    .line 166
+    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    invoke-virtual {v0, v1}, Lart/Test1929$BaseTestException;->printStackTrace(Ljava/io/PrintStream;)V
+
+    goto :goto_46
+.end method
+
+.method public static throwCatchTestExceptionTwiceImpl()V
+    .registers 4
+
+    .prologue
+    .line 174
+    :try_start_0
+    new-instance v1, Lart/Test1929$TestException;
+
+    const-string v2, "throwCatchTestExceptionTwice"
+
+    invoke-direct {v1, v2}, Lart/Test1929$TestException;-><init>(Ljava/lang/String;)V
+
+    throw v1
+    :try_end_8
+    .catch Lart/Test1929$TestException; {:try_start_0 .. :try_end_8} :catch_8
+
+    .line 175
+    :catch_8
+    # This try needs to include the move-exception
+    :try_start_9
+    move-exception v0
+
+    .line 176
+    .local v0, "t":Lart/Test1929$TestException;
+    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    new-instance v2, Ljava/lang/StringBuilder;
+
+    invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V
+
+    const-string v3, "Caught "
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    invoke-virtual {v0}, Ljava/lang/Object;->getClass()Ljava/lang/Class;
+
+    move-result-object v3
+
+    invoke-virtual {v3}, Ljava/lang/Class;->getName()Ljava/lang/String;
+
+    move-result-object v3
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    const-string v3, ": \""
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    invoke-virtual {v0}, Lart/Test1929$TestException;->getMessage()Ljava/lang/String;
+
+    move-result-object v3
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    const-string v3, "\""
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
+
+    move-result-object v2
+
+    invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+    .line 177
+    sget-boolean v1, Lart/Test1929;->PRINT_FULL_EXCEPTION:Z
+
+    if-eqz v1, :cond_46
+
+    .line 178
+    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    invoke-virtual {v0, v1}, Lart/Test1929$TestException;->printStackTrace(Ljava/io/PrintStream;)V
+    :try_end_46
+    .catch Lart/Test1929$TestException; {:try_start_9 .. :try_end_46} :catch_47
+
+    .line 187
+    :cond_46
+    :goto_46
+    return-void
+
+    .line 181
+    :catch_47
+    move-exception v0
+
+    .line 182
+    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    new-instance v2, Ljava/lang/StringBuilder;
+
+    invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V
+
+    const-string v3, "2nd Caught "
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    invoke-virtual {v0}, Ljava/lang/Object;->getClass()Ljava/lang/Class;
+
+    move-result-object v3
+
+    invoke-virtual {v3}, Ljava/lang/Class;->getName()Ljava/lang/String;
+
+    move-result-object v3
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    const-string v3, ": \""
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    invoke-virtual {v0}, Lart/Test1929$TestException;->getMessage()Ljava/lang/String;
+
+    move-result-object v3
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    const-string v3, "\""
+
+    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+
+    move-result-object v2
+
+    invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
+
+    move-result-object v2
+
+    invoke-virtual {v1, v2}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+    .line 183
+    sget-boolean v1, Lart/Test1929;->PRINT_FULL_EXCEPTION:Z
+
+    if-eqz v1, :cond_46
+
+    .line 184
+    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+
+    invoke-virtual {v0, v1}, Lart/Test1929$TestException;->printStackTrace(Ljava/io/PrintStream;)V
+
+    goto :goto_46
+.end method
diff --git a/test/1929-exception-catch-exception/src/Main.java b/test/1929-exception-catch-exception/src/Main.java
new file mode 100644
index 0000000..4651bac
--- /dev/null
+++ b/test/1929-exception-catch-exception/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1929.run();
+  }
+}
diff --git a/test/1929-exception-catch-exception/src/art/Breakpoint.java b/test/1929-exception-catch-exception/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1929-exception-catch-exception/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+  public static class Manager {
+    public static class BP {
+      public final Executable method;
+      public final long location;
+
+      public BP(Executable method) {
+        this(method, getStartLocation(method));
+      }
+
+      public BP(Executable method, long location) {
+        this.method = method;
+        this.location = location;
+      }
+
+      @Override
+      public boolean equals(Object other) {
+        return (other instanceof BP) &&
+            method.equals(((BP)other).method) &&
+            location == ((BP)other).location;
+      }
+
+      @Override
+      public String toString() {
+        return method.toString() + " @ " + getLine();
+      }
+
+      @Override
+      public int hashCode() {
+        return Objects.hash(method, location);
+      }
+
+      public int getLine() {
+        try {
+          LineNumber[] lines = getLineNumberTable(method);
+          int best = -1;
+          for (LineNumber l : lines) {
+            if (l.location > location) {
+              break;
+            } else {
+              best = l.line;
+            }
+          }
+          return best;
+        } catch (Exception e) {
+          return -1;
+        }
+      }
+    }
+
+    private Set<BP> breaks = new HashSet<>();
+
+    public void setBreakpoints(BP... bs) {
+      for (BP b : bs) {
+        if (breaks.add(b)) {
+          Breakpoint.setBreakpoint(b.method, b.location);
+        }
+      }
+    }
+    public void setBreakpoint(Executable method, long location) {
+      setBreakpoints(new BP(method, location));
+    }
+
+    public void clearBreakpoints(BP... bs) {
+      for (BP b : bs) {
+        if (breaks.remove(b)) {
+          Breakpoint.clearBreakpoint(b.method, b.location);
+        }
+      }
+    }
+    public void clearBreakpoint(Executable method, long location) {
+      clearBreakpoints(new BP(method, location));
+    }
+
+    public void clearAllBreakpoints() {
+      clearBreakpoints(breaks.toArray(new BP[0]));
+    }
+  }
+
+  public static void startBreakpointWatch(Class<?> methodClass,
+                                          Executable breakpointReached,
+                                          Thread thr) {
+    startBreakpointWatch(methodClass, breakpointReached, false, thr);
+  }
+
+  /**
+   * Enables the trapping of breakpoint events.
+   *
+   * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+   */
+  public static native void startBreakpointWatch(Class<?> methodClass,
+                                                 Executable breakpointReached,
+                                                 boolean allowRecursive,
+                                                 Thread thr);
+  public static native void stopBreakpointWatch(Thread thr);
+
+  public static final class LineNumber implements Comparable<LineNumber> {
+    public final long location;
+    public final int line;
+
+    private LineNumber(long loc, int line) {
+      this.location = loc;
+      this.line = line;
+    }
+
+    public boolean equals(Object other) {
+      return other instanceof LineNumber && ((LineNumber)other).line == line &&
+          ((LineNumber)other).location == location;
+    }
+
+    public int compareTo(LineNumber other) {
+      int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+      if (v != 0) {
+        return v;
+      } else {
+        return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+      }
+    }
+  }
+
+  public static native void setBreakpoint(Executable m, long loc);
+  public static void setBreakpoint(Executable m, LineNumber l) {
+    setBreakpoint(m, l.location);
+  }
+
+  public static native void clearBreakpoint(Executable m, long loc);
+  public static void clearBreakpoint(Executable m, LineNumber l) {
+    clearBreakpoint(m, l.location);
+  }
+
+  private static native Object[] getLineNumberTableNative(Executable m);
+  public static LineNumber[] getLineNumberTable(Executable m) {
+    Object[] nativeTable = getLineNumberTableNative(m);
+    long[] location = (long[])(nativeTable[0]);
+    int[] lines = (int[])(nativeTable[1]);
+    if (lines.length != location.length) {
+      throw new Error("Lines and locations have different lengths!");
+    }
+    LineNumber[] out = new LineNumber[lines.length];
+    for (int i = 0; i < lines.length; i++) {
+      out[i] = new LineNumber(location[i], lines[i]);
+    }
+    return out;
+  }
+
+  public static native long getStartLocation(Executable m);
+
+  public static int locationToLine(Executable m, long location) {
+    try {
+      Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+      int best = -1;
+      for (Breakpoint.LineNumber l : lines) {
+        if (l.location > location) {
+          break;
+        } else {
+          best = l.line;
+        }
+      }
+      return best;
+    } catch (Exception e) {
+      return -1;
+    }
+  }
+
+  public static long lineToLocation(Executable m, int line) throws Exception {
+    try {
+      Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+      for (Breakpoint.LineNumber l : lines) {
+        if (l.line == line) {
+          return l.location;
+        }
+      }
+      throw new Exception("Unable to find line " + line + " in " + m);
+    } catch (Exception e) {
+      throw new Exception("Unable to get line number info for " + m, e);
+    }
+  }
+}
+
diff --git a/test/1929-exception-catch-exception/src/art/Exceptions.java b/test/1929-exception-catch-exception/src/art/Exceptions.java
new file mode 100644
index 0000000..2c959ec
--- /dev/null
+++ b/test/1929-exception-catch-exception/src/art/Exceptions.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class Exceptions {
+  public static native void setupExceptionTracing(
+      Class<?> methodClass,
+      Class<?> exceptionClass,
+      Method exceptionEventMethod,
+      Method exceptionCaughtEventMethod);
+
+  public static native void enableExceptionCatchEvent(Thread thr);
+  public static native void enableExceptionEvent(Thread thr);
+  public static native void disableExceptionCatchEvent(Thread thr);
+  public static native void disableExceptionEvent(Thread thr);
+}
diff --git a/test/1929-exception-catch-exception/src/art/StackTrace.java b/test/1929-exception-catch-exception/src/art/StackTrace.java
new file mode 100644
index 0000000..b12c3df
--- /dev/null
+++ b/test/1929-exception-catch-exception/src/art/StackTrace.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+  public static class StackFrameData {
+    public final Thread thr;
+    public final Executable method;
+    public final long current_location;
+    public final int depth;
+
+    public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+      this.thr = thr;
+      this.method = e;
+      this.current_location = loc;
+      this.depth = depth;
+    }
+    @Override
+    public String toString() {
+      return String.format(
+          "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+          this.thr,
+          this.method,
+          this.current_location,
+          this.depth);
+    }
+  }
+
+  public static native int GetStackDepth(Thread thr);
+
+  private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+  public static StackFrameData[] GetStackTrace(Thread thr) {
+    // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+    // suspended. The spec says that not being suspended is fine but since we want this to be
+    // consistent we will suspend for the RI.
+    boolean suspend_thread =
+        !System.getProperty("java.vm.name").equals("Dalvik") &&
+        !thr.equals(Thread.currentThread());
+    if (suspend_thread) {
+      Suspension.suspend(thr);
+    }
+    StackFrameData[] out = nativeGetStackTrace(thr);
+    if (suspend_thread) {
+      Suspension.resume(thr);
+    }
+    return out;
+  }
+}
+
diff --git a/test/1929-exception-catch-exception/src/art/Suspension.java b/test/1929-exception-catch-exception/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1929-exception-catch-exception/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+  // Suspends a thread using jvmti.
+  public native static void suspend(Thread thr);
+
+  // Resumes a thread using jvmti.
+  public native static void resume(Thread thr);
+
+  public native static boolean isSuspended(Thread thr);
+
+  public native static int[] suspendList(Thread... threads);
+  public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1929-exception-catch-exception/src/art/Test1929.java b/test/1929-exception-catch-exception/src/art/Test1929.java
new file mode 100644
index 0000000..07d2087
--- /dev/null
+++ b/test/1929-exception-catch-exception/src/art/Test1929.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Objects;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+
+public class Test1929 {
+  public static boolean PRINT_FULL_EXCEPTION = false;
+  public static ExceptionHandler HANDLER = null;
+  public static Collection<Executable> TEST_METHODS;
+
+  public static void doNothing() {};
+  static {
+    try {
+      TEST_METHODS = Arrays.asList(
+        Test1929.class.getDeclaredMethod("doThrow"),
+        Test1929.class.getDeclaredMethod("throwCatchBaseTestException"),
+        Test1929.class.getDeclaredMethod("throwCatchBaseTestExceptionTwice"),
+        Test1929.class.getDeclaredMethod("throwCatchTestException"),
+        Test1929.class.getDeclaredMethod("throwCatchTestExceptionTwice"),
+        Test1929.class.getDeclaredMethod("throwCatchTestExceptionNoRethrow"));
+    } catch (Exception e) {
+      throw new Error("Unable to list test methods!", e);
+    }
+  }
+
+  public static interface ExceptionHandler {
+    public void exceptionOccurred(
+        Executable m, long loc, Throwable exception);
+  }
+
+  private static void PrintStack() {
+    System.out.println("\tCurrent Stack:");
+    for (StackTrace.StackFrameData e : StackTrace.GetStackTrace(Thread.currentThread())) {
+      if (Objects.equals(e.method.getDeclaringClass().getPackage(), Test1929.class.getPackage())) {
+        System.out.println("\t\t" + e.method + " @ line = " +
+            Breakpoint.locationToLine(e.method, e.current_location));
+      }
+    }
+  }
+
+  public static void ExceptionCatchEvent(
+      Thread thr, Executable method, long location, Throwable exception) {
+    System.out.println(thr.getName() + ": " + method + " @ line = " +
+        Breakpoint.locationToLine(method, location) + " caught " +
+        exception.getClass() + ": " + exception.getMessage());
+    PrintStack();
+    if (PRINT_FULL_EXCEPTION) {
+      System.out.print("exception is: ");
+      exception.printStackTrace(System.out);
+    }
+    if (HANDLER != null && TEST_METHODS.contains(method)) {
+      HANDLER.exceptionOccurred(method, location, exception);
+    }
+  }
+
+  public static class BaseTestException extends Error {
+    public BaseTestException(String e) { super(e); }
+    public BaseTestException(String e, Throwable t) { super(e, t); }
+  }
+  public static class TestException extends BaseTestException {
+    public TestException(String e) { super(e); }
+    public TestException(String e, Throwable t) { super(e, t); }
+  }
+
+  public static class TestExceptionNoRethrow extends TestException {
+    public TestExceptionNoRethrow(String e) { super(e); }
+    public TestExceptionNoRethrow(String e, Throwable t) { super(e, t); }
+  }
+
+  public static class DoNothingHandler implements ExceptionHandler {
+    public void exceptionOccurred(Executable m, long loc, Throwable exception) {
+      System.out.println("\tDoing nothing!");
+      return;
+    }
+  }
+
+  public static class ThrowCatchBase implements ExceptionHandler {
+    public void exceptionOccurred(Executable m, long loc, Throwable exception) {
+      System.out.println("\tThrowing BaseTestException and catching it!");
+      try {
+        throw new BaseTestException("ThrowBaseHandler during throw from " + m + " @ line = " +
+            Breakpoint.locationToLine(m, loc), exception);
+      } catch (BaseTestException t) {
+        System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+        if (PRINT_FULL_EXCEPTION) {
+          t.printStackTrace(System.out);
+        }
+      }
+    }
+  }
+  public static class ThrowBaseTestExceptionHandler implements ExceptionHandler {
+    public void exceptionOccurred(Executable m, long loc, Throwable exception) {
+      System.out.println("\tThrowing BaseTestException!");
+      throw new BaseTestException("ThrowBaseHandler during throw from " + m + " @ line = " +
+          Breakpoint.locationToLine(m, loc), exception);
+    }
+  }
+
+  public static class ThrowTestExceptionNoRethrowHandler implements ExceptionHandler {
+    public void exceptionOccurred(Executable m, long loc, Throwable exception) {
+      if (exception instanceof TestExceptionNoRethrow) {
+        System.out.println("\tInstance of TestExceptionNoRethrow was thrown. Not throwing again.");
+      } else {
+        System.out.println("\tThrowing TestExceptionNoRethrow!");
+        throw new TestExceptionNoRethrow("ThrowTestExceptionNoRethrowHandler during throw from " +
+            m + " @ line = " + Breakpoint.locationToLine(m, loc), exception);
+      }
+    }
+  }
+  public static void doThrow() {
+    throw new TestException("doThrow");
+  }
+
+  public static class DoThrowClass implements Runnable {
+    public void run() { doThrow(); }
+  }
+
+  public static void throwCatchBaseTestException() {
+    try {
+      throw new TestException("throwCatchBaseTestException");
+    } catch (BaseTestException t) {
+      System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+      if (PRINT_FULL_EXCEPTION) {
+        t.printStackTrace(System.out);
+      }
+    }
+  }
+
+  public static class DoThrowCatchBaseTestException implements Runnable {
+    public void run() { throwCatchBaseTestException(); }
+  }
+
+  // dx/d8/jack all do an optimization around catch blocks that (while legal) breaks assumptions
+  // this test relies on so we have the actual implementation be corrected smali. This does work
+  // for RI however.
+  public static final class Impl {
+    private Impl() {}
+    public static void throwCatchBaseTestExceptionTwiceImpl() {
+      try {
+        try {
+          throw new TestException("throwCatchBaseTestExceptionTwice");
+        } catch (BaseTestException t) {
+          System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+          if (PRINT_FULL_EXCEPTION) {
+            t.printStackTrace(System.out);
+          }
+        }
+      } catch (BaseTestException t) {
+        System.out.println("2nd Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+        if (PRINT_FULL_EXCEPTION) {
+          t.printStackTrace(System.out);
+        }
+      }
+    }
+
+    public static void throwCatchTestExceptionTwiceImpl() {
+      try {
+        try {
+          throw new TestException("throwCatchTestExceptionTwice");
+        } catch (TestException t) {
+          System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+          if (PRINT_FULL_EXCEPTION) {
+            t.printStackTrace(System.out);
+          }
+        }
+      } catch (TestException t) {
+        System.out.println("2nd Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+        if (PRINT_FULL_EXCEPTION) {
+          t.printStackTrace(System.out);
+        }
+      }
+    }
+  }
+
+  public static void throwCatchBaseTestExceptionTwice() {
+    // The implementation of this has to change depending upon the runtime slightly due to compiler
+    // optimizations present in DX/D8/Jack.
+    Impl.throwCatchBaseTestExceptionTwiceImpl();
+  }
+
+  public static class DoThrowCatchBaseTestExceptionTwice implements Runnable {
+    public void run() { throwCatchBaseTestExceptionTwice(); }
+  }
+
+  public static void throwCatchTestException() {
+    try {
+      throw new TestException("throwCatchTestException");
+    } catch (TestException t) {
+      System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+      if (PRINT_FULL_EXCEPTION) {
+        t.printStackTrace(System.out);
+      }
+    }
+  }
+
+  public static class DoThrowCatchTestException implements Runnable {
+    public void run() { throwCatchTestException(); }
+  }
+
+  public static void throwCatchTestExceptionTwice() {
+    // The implementation of this has to change depending upon the runtime slightly due to compiler
+    // optimizations present in DX/D8/Jack.
+    Impl.throwCatchTestExceptionTwiceImpl();
+  }
+
+  public static class DoThrowCatchTestExceptionTwice implements Runnable {
+    public void run() { throwCatchTestExceptionTwice(); }
+  }
+
+  public static void throwCatchTestExceptionNoRethrow() {
+    try {
+      throw new TestException("throwCatchTestExceptionNoRethrow");
+    } catch (TestExceptionNoRethrow t) {
+      System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+      if (PRINT_FULL_EXCEPTION) {
+        t.printStackTrace(System.out);
+      }
+    }
+  }
+
+  public static class DoThrowCatchTestExceptionNoRethrow implements Runnable {
+    public void run() { throwCatchTestExceptionNoRethrow(); }
+  }
+
+  public static void run() throws Exception {
+    // Set up breakpoints
+    Exceptions.setupExceptionTracing(
+        Test1929.class,
+        TestException.class,
+        null,
+        Test1929.class.getDeclaredMethod(
+            "ExceptionCatchEvent",
+            Thread.class,
+            Executable.class,
+            Long.TYPE,
+            Throwable.class));
+    Exceptions.enableExceptionCatchEvent(Thread.currentThread());
+
+    ExceptionHandler[] handlers = new ExceptionHandler[] {
+      new DoNothingHandler(),
+      new ThrowCatchBase(),
+      new ThrowBaseTestExceptionHandler(),
+      new ThrowTestExceptionNoRethrowHandler(),
+    };
+
+    Runnable[] tests = new Runnable[] {
+      new DoThrowClass(),
+      new DoThrowCatchBaseTestException(),
+      new DoThrowCatchBaseTestExceptionTwice(),
+      new DoThrowCatchTestException(),
+      new DoThrowCatchTestExceptionTwice(),
+      new DoThrowCatchTestExceptionNoRethrow(),
+    };
+
+    for (ExceptionHandler handler : handlers) {
+      for (Runnable test : tests) {
+        try {
+          HANDLER = handler;
+          System.out.printf("Test \"%s\": Running breakpoint with handler \"%s\"\n",
+              test.getClass().getName(), handler.getClass().getName());
+          test.run();
+          System.out.printf("Test \"%s\": No error caught with handler \"%s\"\n",
+              test.getClass().getName(), handler.getClass().getName());
+        } catch (Throwable e) {
+          System.out.printf("Test \"%s\": Caught error %s:\"%s\" with handler \"%s\"\n",
+              test.getClass().getName(),
+              e.getClass().getName(),
+              e.getMessage(),
+              handler.getClass().getName());
+          if (PRINT_FULL_EXCEPTION) {
+            e.printStackTrace(System.out);
+          }
+        }
+        System.out.printf("Test \"%s\": Finished running with handler \"%s\"\n",
+            test.getClass().getName(), handler.getClass().getName());
+        HANDLER = null;
+      }
+    }
+    Exceptions.disableExceptionCatchEvent(Thread.currentThread());
+  }
+}
diff --git a/test/1930-monitor-info/expected.txt b/test/1930-monitor-info/expected.txt
new file mode 100644
index 0000000..b43f1b2
--- /dev/null
+++ b/test/1930-monitor-info/expected.txt
@@ -0,0 +1,31 @@
+Running with single thread.
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Running with single thread in native.
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testSingleThread], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Lock twice
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwice], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwice], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwice], owner: main, entryCount: 2, waiters: [], notify_waiters: [] }
+Lock twice native
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNative], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNative], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNative], owner: main, entryCount: 2, waiters: [], notify_waiters: [] }
+Lock twice Java then native
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceJN], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceJN], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceJN], owner: main, entryCount: 2, waiters: [], notify_waiters: [] }
+Lock twice native then Java
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNJ], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Pre-lock[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNJ], owner: main, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockedTwiceNJ], owner: main, entryCount: 2, waiters: [], notify_waiters: [] }
+lock with wait
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockWait], owner: main, entryCount: 1, waiters: [Test1930 Thread - testLockWait], notify_waiters: [] }
+Thread[Test1930 Thread - testLockWait]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockWait], owner: Test1930 Thread - testLockWait, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testLockWait], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Wait for notify.
+Thread[Test1930 Thread - testLockWait]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: Test1930 Thread - testLockWait, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: main, entryCount: 1, waiters: [Test1930 Thread - testLockWait], notify_waiters: [Test1930 Thread - testLockWait] }
+Thread[Test1930 Thread - testLockWait]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: Test1930 Thread - testLockWait, entryCount: 1, waiters: [], notify_waiters: [] }
+Thread[main]: MonitorUsage{ monitor: NamedLock[Test1930 - testNotifyWait], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
diff --git a/test/1930-monitor-info/info.txt b/test/1930-monitor-info/info.txt
new file mode 100644
index 0000000..8e19edc
--- /dev/null
+++ b/test/1930-monitor-info/info.txt
@@ -0,0 +1,3 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that the GetObjectMonitorUsage function works correctly.
diff --git a/test/1930-monitor-info/monitor.cc b/test/1930-monitor-info/monitor.cc
new file mode 100644
index 0000000..7f97c05
--- /dev/null
+++ b/test/1930-monitor-info/monitor.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+
+#include <cstdio>
+#include <iostream>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "jni.h"
+#include "jvmti.h"
+
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test1930MonitorInfo {
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1930_executeLockedNative(JNIEnv* env,
+                                                                        jclass klass,
+                                                                        jobject run,
+                                                                        jobject l) {
+  ScopedLocalRef<jclass> runnable(env, env->FindClass("java/lang/Runnable"));
+  if (env->ExceptionCheck()) {
+    return;
+  }
+  jmethodID method = env->GetMethodID(runnable.get(), "run", "()V");
+
+  if (env->ExceptionCheck()) {
+    return;
+  }
+  jmethodID printMethod = env->GetStaticMethodID(klass, "printPreLock", "(Ljava/lang/Object;)V");
+  if (env->ExceptionCheck()) {
+    return;
+  }
+
+  env->CallStaticVoidMethod(klass, printMethod, l);
+  if (env->ExceptionCheck()) {
+    return;
+  }
+  if (env->MonitorEnter(l) != 0) {
+    return;
+  }
+  env->CallVoidMethod(run, method);
+  env->MonitorExit(l);
+}
+
+}  // namespace Test1930MonitorInfo
+}  // namespace art
diff --git a/test/1930-monitor-info/run b/test/1930-monitor-info/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/1930-monitor-info/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --jvmti
diff --git a/test/1930-monitor-info/src/Main.java b/test/1930-monitor-info/src/Main.java
new file mode 100644
index 0000000..3328461
--- /dev/null
+++ b/test/1930-monitor-info/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1930.run();
+  }
+}
diff --git a/test/1930-monitor-info/src/art/Monitors.java b/test/1930-monitor-info/src/art/Monitors.java
new file mode 100644
index 0000000..26f7718
--- /dev/null
+++ b/test/1930-monitor-info/src/art/Monitors.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+public class Monitors {
+  public static class NamedLock {
+    public final String name;
+    public NamedLock(String name) {
+      this.name = name;
+    }
+    public String toString() {
+      return String.format("NamedLock[%s]", name);
+    }
+  }
+
+  public static final class MonitorUsage {
+    public final Object monitor;
+    public final Thread owner;
+    public final int entryCount;
+    public final Thread[] waiters;
+    public final Thread[] notifyWaiters;
+
+    public MonitorUsage(
+        Object monitor,
+        Thread owner,
+        int entryCount,
+        Thread[] waiters,
+        Thread[] notifyWaiters) {
+      this.monitor = monitor;
+      this.entryCount = entryCount;
+      this.owner = owner;
+      this.waiters = waiters;
+      this.notifyWaiters = notifyWaiters;
+    }
+
+    private static String toNameList(Thread[] ts) {
+      return Arrays.toString(Arrays.stream(ts).map((Thread t) -> t.getName()).toArray());
+    }
+
+    public String toString() {
+      return String.format(
+          "MonitorUsage{ monitor: %s, owner: %s, entryCount: %d, waiters: %s, notify_waiters: %s }",
+          monitor,
+          (owner != null) ? owner.getName() : "<NULL>",
+          entryCount,
+          toNameList(waiters),
+          toNameList(notifyWaiters));
+    }
+  }
+
+  public static native MonitorUsage getObjectMonitorUsage(Object monitor);
+}
+
diff --git a/test/1930-monitor-info/src/art/Test1930.java b/test/1930-monitor-info/src/art/Test1930.java
new file mode 100644
index 0000000..a7fa1c7
--- /dev/null
+++ b/test/1930-monitor-info/src/art/Test1930.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+
+public class Test1930 {
+  public static final int NUM_RETRY = 100;
+  private static void testSingleThread() {
+    Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testSingleThread");
+    executeLocked(() -> { printMonitorUsage(lk); }, lk);
+  }
+  private static void testSingleThreadNative() {
+    Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testSingleThread");
+    executeLockedNative(() -> { printMonitorUsage(lk); }, lk);
+  }
+
+  private static void testLockedTwice() {
+    final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwice");
+    executeLocked(() -> { executeLocked(() -> { printMonitorUsage(lk); }, lk); }, lk);
+  }
+
+  private static void testLockedTwiceNJ() {
+    final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwiceNJ");
+    executeLockedNative(() -> { executeLockedNative(() -> { printMonitorUsage(lk); }, lk); }, lk);
+  }
+
+  private static void testLockedTwiceJN() {
+    final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwiceJN");
+    executeLockedNative(() -> { executeLockedNative(() -> { printMonitorUsage(lk); }, lk); }, lk);
+  }
+
+  private static void testLockedTwiceNative() {
+    final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockedTwiceNative");
+    executeLockedNative(() -> { executeLockedNative(() -> { printMonitorUsage(lk); }, lk); }, lk);
+  }
+
+  public final static class ThreadSignaler {
+    public volatile boolean signal = false;
+  }
+
+  private static void testLockWait() throws Exception {
+    final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testLockWait");
+    final Semaphore sem = new Semaphore(0);
+    final Thread t = new Thread(() -> {
+      sem.release();
+      synchronized (lk) {
+        printMonitorUsage(lk);
+      }
+    }, "Test1930 Thread - testLockWait");
+    synchronized (lk) {
+      t.start();
+      // Wait for the other thread to actually start.
+      sem.acquire();
+      // Wait for the other thread to go to sleep trying to get the mutex. This might take a (short)
+      // time since we try spinning first for better performance.
+      boolean found_wait = false;
+      for (long i = 0; i < NUM_RETRY; i++) {
+        if (Arrays.asList(Monitors.getObjectMonitorUsage(lk).waiters).contains(t)) {
+          found_wait = true;
+          break;
+        } else {
+          Thread.sleep(500);
+          Thread.yield();
+        }
+      }
+      if (!found_wait) {
+        System.out.println("other thread doesn't seem to be waiting.");
+      }
+      printMonitorUsage(lk);
+    }
+    t.join();
+    printMonitorUsage(lk);
+  }
+
+  private static void testNotifyWait() throws Exception {
+    final Monitors.NamedLock lk = new Monitors.NamedLock("Test1930 - testNotifyWait");
+    final Semaphore sem = new Semaphore(0);
+    Thread t = new Thread(() -> {
+      synchronized (lk) {
+        printMonitorUsage(lk);
+        sem.release();
+        try {
+          lk.wait();
+        } catch (Exception e) {
+          throw new Error("Error waiting!", e);
+        }
+        printMonitorUsage(lk);
+      }
+    }, "Test1930 Thread - testLockWait");
+    t.start();
+    sem.acquire();
+    synchronized (lk) {
+      printMonitorUsage(lk);
+      lk.notifyAll();
+    }
+    t.join();
+    printMonitorUsage(lk);
+  }
+
+  public static void run() throws Exception {
+    // Single threaded tests.
+    System.out.println("Running with single thread.");
+    testSingleThread();
+    System.out.println("Running with single thread in native.");
+    testSingleThreadNative();
+    System.out.println("Lock twice");
+    testLockedTwice();
+    System.out.println("Lock twice native");
+    testLockedTwiceNative();
+    System.out.println("Lock twice Java then native");
+    testLockedTwiceJN();
+    System.out.println("Lock twice native then Java");
+    testLockedTwiceNJ();
+
+    // Mutli threaded tests.
+    System.out.println("lock with wait");
+    testLockWait();
+    System.out.println("Wait for notify.");
+    testNotifyWait();
+  }
+
+  public static void printPreLock(Object lock) {
+    System.out.println(String.format("Pre-lock[%s]: %s",
+          Thread.currentThread().getName(), Monitors.getObjectMonitorUsage(lock)));
+  }
+
+  public static void executeLocked(Runnable r, Object lock) {
+    printPreLock(lock);
+    synchronized (lock) {
+      r.run();
+    }
+  }
+
+  public native static void executeLockedNative(Runnable r, Object m);
+  public static void printMonitorUsage(Object m) {
+    System.out.println(String.format("Thread[%s]: %s",
+          Thread.currentThread().getName(), Monitors.getObjectMonitorUsage(m)));
+  }
+}
diff --git a/test/441-checker-inliner/smali/Smali.smali b/test/441-checker-inliner/smali/Smali.smali
new file mode 100644
index 0000000..7e42755
--- /dev/null
+++ b/test/441-checker-inliner/smali/Smali.smali
@@ -0,0 +1,97 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LSmali;
+.super Ljava/lang/Object;
+.source "Smali.java"
+
+##  CHECK-START: int Smali.InlineWithControlFlow(boolean) inliner (before)
+##  CHECK-DAG:     <<Const1:i\d+>> IntConstant 1
+##  CHECK-DAG:     <<Const3:i\d+>> IntConstant 3
+##  CHECK-DAG:     <<Const5:i\d+>> IntConstant 5
+##  CHECK-DAG:     <<Add:i\d+>>    InvokeStaticOrDirect [<<Const1>>,<<Const3>>{{(,[ij]\d+)?}}]
+##  CHECK-DAG:     <<Sub:i\d+>>    InvokeStaticOrDirect [<<Const5>>,<<Const3>>{{(,[ij]\d+)?}}]
+##  CHECK-DAG:     <<Phi:i\d+>>    Phi [<<Add>>,<<Sub>>]
+##  CHECK-DAG:                     Return [<<Phi>>]
+
+##  CHECK-START: int Smali.InlineWithControlFlow(boolean) inliner (after)
+##  CHECK-DAG:     <<Const4:i\d+>> IntConstant 4
+##  CHECK-DAG:     <<Const2:i\d+>> IntConstant 2
+##  CHECK-DAG:     <<Phi:i\d+>>    Phi [<<Const4>>,<<Const2>>]
+##  CHECK-DAG:                     Return [<<Phi>>]
+.method public static InlineWithControlFlow(Z)I
+
+    # int x, const1, const3, const5;
+    # const1 = 1;
+    # const3 = 3;
+    # const5 = 5;
+    # if (cond) {
+    #   x = returnAdd(const1, const3);
+    # } else {
+    #   x = returnSub(const5, const3);
+    # }
+    # return x;
+
+    .registers 5
+    .param p0, "cond"    # Z
+
+    .prologue
+    const/4 v0, 0x1
+
+    .local v0, "const1":I
+    const/4 v1, 0x3
+
+    .local v1, "const3":I
+    const/4 v2, 0x5
+
+    .local v2, "const5":I
+    if-eqz p0, :cond_a
+
+    invoke-static {v0, v1}, LSmali;->returnAdd(II)I
+
+    move-result v3
+
+    .local v3, "x":I
+    :goto_9
+    return v3
+
+    .end local v3    # "x":I
+    :cond_a
+    invoke-static {v2, v1}, LSmali;->returnSub(II)I
+
+    move-result v3
+
+    .restart local v3    # "x":I
+    goto :goto_9
+.end method
+
+.method private static returnAdd(II)I
+    .registers 3
+    .param p0, "a"    # I
+    .param p1, "b"    # I
+
+    add-int v0, p0, p1
+
+    return v0
+.end method
+
+.method private static returnSub(II)I
+    .registers 3
+    .param p0, "a"    # I
+    .param p1, "b"    # I
+
+    sub-int v0, p0, p1
+
+    return v0
+.end method
diff --git a/test/441-checker-inliner/src/Main.java b/test/441-checker-inliner/src/Main.java
index 6d6a4f2..aff4a07 100644
--- a/test/441-checker-inliner/src/Main.java
+++ b/test/441-checker-inliner/src/Main.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+import java.lang.reflect.Method;
 
 public class Main {
 
@@ -124,32 +125,14 @@
     return incCounter();
   }
 
-  /// CHECK-START: int Main.InlineWithControlFlow(boolean) inliner (before)
-  /// CHECK-DAG:     <<Const1:i\d+>> IntConstant 1
-  /// CHECK-DAG:     <<Const3:i\d+>> IntConstant 3
-  /// CHECK-DAG:     <<Const5:i\d+>> IntConstant 5
-  /// CHECK-DAG:     <<Add:i\d+>>    InvokeStaticOrDirect [<<Const1>>,<<Const3>>{{(,[ij]\d+)?}}]
-  /// CHECK-DAG:     <<Sub:i\d+>>    InvokeStaticOrDirect [<<Const5>>,<<Const3>>{{(,[ij]\d+)?}}]
-  /// CHECK-DAG:     <<Phi:i\d+>>    Phi [<<Add>>,<<Sub>>]
-  /// CHECK-DAG:                     Return [<<Phi>>]
-
-  /// CHECK-START: int Main.InlineWithControlFlow(boolean) inliner (after)
-  /// CHECK-DAG:     <<Const4:i\d+>> IntConstant 4
-  /// CHECK-DAG:     <<Const2:i\d+>> IntConstant 2
-  /// CHECK-DAG:     <<Phi:i\d+>>    Phi [<<Const4>>,<<Const2>>]
-  /// CHECK-DAG:                     Return [<<Phi>>]
-
   public static int InlineWithControlFlow(boolean cond) {
-    int x, const1, const3, const5;
-    const1 = 1;
-    const3 = 3;
-    const5 = 5;
-    if (cond) {
-      x = returnAdd(const1, const3);
-    } else {
-      x = returnSub(const5, const3);
+    try {
+      Class<?> c = Class.forName("Smali");
+      Method m = c.getMethod("InlineWithControlFlow", boolean.class);
+      return (Integer) m.invoke(null, cond);
+    } catch (Throwable t) {
+      throw new RuntimeException(t);
     }
-    return x;
   }
 
   /// CHECK-START: int Main.returnAbs(int) intrinsics_recognition (before)
diff --git a/test/455-checker-gvn/smali/Smali.smali b/test/455-checker-gvn/smali/Smali.smali
new file mode 100644
index 0000000..fbeb4db
--- /dev/null
+++ b/test/455-checker-gvn/smali/Smali.smali
@@ -0,0 +1,42 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+.class public LSmali;
+.super Ljava/lang/Object;
+
+## CHECK-START: int Smali.foo(int, int) GVN (before)
+## CHECK: Add
+## CHECK: Add
+## CHECK: Add
+
+## CHECK-START: int Smali.foo(int, int) GVN (after)
+## CHECK: Add
+## CHECK: Add
+## CHECK-NOT: Add
+.method public static foo(II)I
+
+    # int sum1 = x + y;
+    # int sum2 = y + x;
+    # return sum1 + sum2;
+
+    .registers 5
+    .param p0, "x"    # I
+    .param p1, "y"    # I
+
+    add-int v0, p0, p1
+    add-int v1, p1, p0
+    add-int v2, v0, v1
+
+    return v2
+.end method
+
diff --git a/test/455-checker-gvn/src/Main.java b/test/455-checker-gvn/src/Main.java
index cea0959..6df57fd 100644
--- a/test/455-checker-gvn/src/Main.java
+++ b/test/455-checker-gvn/src/Main.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+import java.lang.reflect.Method;
 
 public class Main {
 
@@ -25,20 +26,14 @@
     System.out.println(directIntrinsic(-5));
   }
 
-  /// CHECK-START: int Main.foo(int, int) GVN (before)
-  /// CHECK: Add
-  /// CHECK: Add
-  /// CHECK: Add
-
-  /// CHECK-START: int Main.foo(int, int) GVN (after)
-  /// CHECK: Add
-  /// CHECK: Add
-  /// CHECK-NOT: Add
-
   public static int foo(int x, int y) {
-    int sum1 = x + y;
-    int sum2 = y + x;
-    return sum1 + sum2;
+   try {
+      Class<?> c = Class.forName("Smali");
+      Method m = c.getMethod("foo", int.class, int.class);
+      return (Integer) m.invoke(null, x, y);
+    } catch (Throwable t) {
+      throw new RuntimeException(t);
+    }
   }
 
   /// CHECK-START: int Main.mulAndIntrinsic() GVN (before)
diff --git a/test/480-checker-dead-blocks/smali/Smali.smali b/test/480-checker-dead-blocks/smali/Smali.smali
new file mode 100644
index 0000000..b93d5ac
--- /dev/null
+++ b/test/480-checker-dead-blocks/smali/Smali.smali
@@ -0,0 +1,122 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LSmali;
+.super Ljava/lang/Object;
+
+##  CHECK-START: int Smali.testTrueBranch(int, int) dead_code_elimination$after_inlining (before)
+##  CHECK-DAG:     <<ArgX:i\d+>>    ParameterValue
+##  CHECK-DAG:     <<ArgY:i\d+>>    ParameterValue
+##  CHECK-DAG:                      If
+##  CHECK-DAG:     <<Add:i\d+>>     Add [<<ArgX>>,<<ArgY>>]
+##  CHECK-DAG:     <<Sub:i\d+>>     Sub [<<ArgX>>,<<ArgY>>]
+##  CHECK-DAG:     <<Phi:i\d+>>     Phi [<<Add>>,<<Sub>>]
+##  CHECK-DAG:                      Return [<<Phi>>]
+
+##  CHECK-START: int Smali.testTrueBranch(int, int) dead_code_elimination$after_inlining (after)
+##  CHECK-DAG:     <<ArgX:i\d+>>    ParameterValue
+##  CHECK-DAG:     <<ArgY:i\d+>>    ParameterValue
+##  CHECK-DAG:     <<Add:i\d+>>     Add [<<ArgX>>,<<ArgY>>]
+##  CHECK-DAG:                      Return [<<Add>>]
+
+##  CHECK-START: int Smali.testTrueBranch(int, int) dead_code_elimination$after_inlining (after)
+##  CHECK-NOT:                      If
+##  CHECK-NOT:                      Sub
+##  CHECK-NOT:                      Phi
+.method public static testTrueBranch(II)I
+    # int z;
+    # if (inlineTrue()) {
+    #   z = x + y;
+    # } else {
+    #   z = x - y;
+    #   // Prevent HSelect simplification by having a branch with multiple instructions.
+    #   System.nanoTime();
+    # }
+    #return z;
+
+    .registers 4
+    .param p0, "x"    # I
+    .param p1, "y"    # I
+
+    invoke-static {}, LMain;->inlineTrue()Z
+
+    move-result v1
+
+    if-eqz v1, :cond_9
+
+    add-int v0, p0, p1
+
+    :goto_8
+    return v0
+
+    :cond_9
+    sub-int v0, p0, p1
+
+    invoke-static {}, Ljava/lang/System;->nanoTime()J
+
+    goto :goto_8
+.end method
+
+##  CHECK-START: int Smali.testFalseBranch(int, int) dead_code_elimination$after_inlining (before)
+##  CHECK-DAG:     <<ArgX:i\d+>>    ParameterValue
+##  CHECK-DAG:     <<ArgY:i\d+>>    ParameterValue
+##  CHECK-DAG:                      If
+##  CHECK-DAG:     <<Add:i\d+>>     Add [<<ArgX>>,<<ArgY>>]
+##  CHECK-DAG:     <<Sub:i\d+>>     Sub [<<ArgX>>,<<ArgY>>]
+##  CHECK-DAG:     <<Phi:i\d+>>     Phi [<<Add>>,<<Sub>>]
+##  CHECK-DAG:                      Return [<<Phi>>]
+
+##  CHECK-START: int Smali.testFalseBranch(int, int) dead_code_elimination$after_inlining (after)
+##  CHECK-DAG:     <<ArgX:i\d+>>    ParameterValue
+##  CHECK-DAG:     <<ArgY:i\d+>>    ParameterValue
+##  CHECK-DAG:     <<Sub:i\d+>>     Sub [<<ArgX>>,<<ArgY>>]
+##  CHECK-DAG:                      Return [<<Sub>>]
+
+##  CHECK-START: int Smali.testFalseBranch(int, int) dead_code_elimination$after_inlining (after)
+##  CHECK-NOT:                      If
+##  CHECK-NOT:                      Add
+##  CHECK-NOT:                      Phi
+.method public static testFalseBranch(II)I
+    # int z;
+    # if (inlineFalse()) {
+    #   z = x + y;
+    # } else {
+    #   z = x - y;
+    #   // Prevent HSelect simplification by having a branch with multiple instructions.
+    #   System.nanoTime();
+    # }
+    # return z;
+
+    .registers 4
+    .param p0, "x"    # I
+    .param p1, "y"    # I
+
+    invoke-static {}, LMain;->inlineFalse()Z
+
+    move-result v1
+
+    if-eqz v1, :cond_9
+
+    add-int v0, p0, p1
+
+    :goto_8
+    return v0
+
+    :cond_9
+    sub-int v0, p0, p1
+
+    invoke-static {}, Ljava/lang/System;->nanoTime()J
+
+    goto :goto_8
+.end method
diff --git a/test/480-checker-dead-blocks/src/Main.java b/test/480-checker-dead-blocks/src/Main.java
index 0ca822f..485efd9 100644
--- a/test/480-checker-dead-blocks/src/Main.java
+++ b/test/480-checker-dead-blocks/src/Main.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+import java.lang.reflect.Method;
 
 public class Main {
 
@@ -30,68 +31,24 @@
     return false;
   }
 
-  /// CHECK-START: int Main.testTrueBranch(int, int) dead_code_elimination$after_inlining (before)
-  /// CHECK-DAG:     <<ArgX:i\d+>>    ParameterValue
-  /// CHECK-DAG:     <<ArgY:i\d+>>    ParameterValue
-  /// CHECK-DAG:                      If
-  /// CHECK-DAG:     <<Add:i\d+>>     Add [<<ArgX>>,<<ArgY>>]
-  /// CHECK-DAG:     <<Sub:i\d+>>     Sub [<<ArgX>>,<<ArgY>>]
-  /// CHECK-DAG:     <<Phi:i\d+>>     Phi [<<Add>>,<<Sub>>]
-  /// CHECK-DAG:                      Return [<<Phi>>]
-
-  /// CHECK-START: int Main.testTrueBranch(int, int) dead_code_elimination$after_inlining (after)
-  /// CHECK-DAG:     <<ArgX:i\d+>>    ParameterValue
-  /// CHECK-DAG:     <<ArgY:i\d+>>    ParameterValue
-  /// CHECK-DAG:     <<Add:i\d+>>     Add [<<ArgX>>,<<ArgY>>]
-  /// CHECK-DAG:                      Return [<<Add>>]
-
-  /// CHECK-START: int Main.testTrueBranch(int, int) dead_code_elimination$after_inlining (after)
-  /// CHECK-NOT:                      If
-  /// CHECK-NOT:                      Sub
-  /// CHECK-NOT:                      Phi
-
   public static int testTrueBranch(int x, int y) {
-    int z;
-    if (inlineTrue()) {
-      z = x + y;
-    } else {
-      z = x - y;
-      // Prevent HSelect simplification by having a branch with multiple instructions.
-      System.nanoTime();
+      try {
+      Class<?> c = Class.forName("Smali");
+      Method m = c.getMethod("testTrueBranch", int.class, int.class);
+      return (Integer) m.invoke(null, x, y);
+    } catch (Throwable t) {
+      throw new RuntimeException(t);
     }
-    return z;
   }
 
-  /// CHECK-START: int Main.testFalseBranch(int, int) dead_code_elimination$after_inlining (before)
-  /// CHECK-DAG:     <<ArgX:i\d+>>    ParameterValue
-  /// CHECK-DAG:     <<ArgY:i\d+>>    ParameterValue
-  /// CHECK-DAG:                      If
-  /// CHECK-DAG:     <<Add:i\d+>>     Add [<<ArgX>>,<<ArgY>>]
-  /// CHECK-DAG:     <<Sub:i\d+>>     Sub [<<ArgX>>,<<ArgY>>]
-  /// CHECK-DAG:     <<Phi:i\d+>>     Phi [<<Add>>,<<Sub>>]
-  /// CHECK-DAG:                      Return [<<Phi>>]
-
-  /// CHECK-START: int Main.testFalseBranch(int, int) dead_code_elimination$after_inlining (after)
-  /// CHECK-DAG:     <<ArgX:i\d+>>    ParameterValue
-  /// CHECK-DAG:     <<ArgY:i\d+>>    ParameterValue
-  /// CHECK-DAG:     <<Sub:i\d+>>     Sub [<<ArgX>>,<<ArgY>>]
-  /// CHECK-DAG:                      Return [<<Sub>>]
-
-  /// CHECK-START: int Main.testFalseBranch(int, int) dead_code_elimination$after_inlining (after)
-  /// CHECK-NOT:                      If
-  /// CHECK-NOT:                      Add
-  /// CHECK-NOT:                      Phi
-
   public static int testFalseBranch(int x, int y) {
-    int z;
-    if (inlineFalse()) {
-      z = x + y;
-    } else {
-      z = x - y;
-      // Prevent HSelect simplification by having a branch with multiple instructions.
-      System.nanoTime();
+      try {
+      Class<?> c = Class.forName("Smali");
+      Method m = c.getMethod("testFalseBranch", int.class, int.class);
+      return (Integer) m.invoke(null, x, y);
+    } catch (Throwable t) {
+      throw new RuntimeException(t);
     }
-    return z;
   }
 
   /// CHECK-START: int Main.testRemoveLoop(int) dead_code_elimination$after_inlining (before)
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index 6632503..7ae873a 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -881,10 +881,10 @@
   /// CHECK: ArrayGet
   private static int testAllocationEliminationOfArray2() {
     // Cannot eliminate array allocation since array is accessed with non-constant
-    // index.
-    int[] array = new int[4];
-    array[2] = 4;
-    array[3] = 7;
+    // index (only 3 elements to prevent vectorization of the reduction).
+    int[] array = new int[3];
+    array[1] = 4;
+    array[2] = 7;
     int sum = 0;
     for (int e : array) {
       sum += e;
diff --git a/test/661-checker-simd-reduc/src/Main.java b/test/661-checker-simd-reduc/src/Main.java
index 741b5fa..8208a9e 100644
--- a/test/661-checker-simd-reduc/src/Main.java
+++ b/test/661-checker-simd-reduc/src/Main.java
@@ -51,6 +51,26 @@
     return sum;
   }
 
+  /// CHECK-START: int Main.reductionInt(int[]) loop_optimization (before)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
+  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                 loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get:i\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi2>>,<<Get>>]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]      loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: int Main.reductionInt(int[]) loop_optimization (after)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
+  /// CHECK-DAG: <<Cons4:i\d+>>  IntConstant 4                 loop:none
+  /// CHECK-DAG: <<Set:d\d+>>    VecSetScalars [<<Cons0>>]     loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>   Phi [<<Set>>,{{d\d+}}]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 VecAdd [<<Phi2>>,<<Load>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons4>>]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Red:d\d+>>    VecReduce [<<Phi2>>]          loop:none
+  /// CHECK-DAG: <<Extr:i\d+>>   VecExtractScalar [<<Red>>]    loop:none
   private static int reductionInt(int[] x) {
     int sum = 0;
     for (int i = 0; i < x.length; i++) {
@@ -59,6 +79,28 @@
     return sum;
   }
 
+  /// CHECK-START: long Main.reductionLong(long[]) loop_optimization (before)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
+  /// CHECK-DAG: <<Long0:j\d+>>  LongConstant 0                loop:none
+  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                 loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:j\d+>>   Phi [<<Long0>>,{{j\d+}}]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get:j\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi2>>,<<Get>>]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]      loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: long Main.reductionLong(long[]) loop_optimization (after)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
+  /// CHECK-DAG: <<Long0:j\d+>>  LongConstant 0                loop:none
+  /// CHECK-DAG: <<Cons2:i\d+>>  IntConstant 2                 loop:none
+  /// CHECK-DAG: <<Set:d\d+>>    VecSetScalars [<<Long0>>]     loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>   Phi [<<Set>>,{{d\d+}}]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 VecAdd [<<Phi2>>,<<Load>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons2>>]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Red:d\d+>>    VecReduce [<<Phi2>>]          loop:none
+  /// CHECK-DAG: <<Extr:j\d+>>   VecExtractScalar [<<Red>>]    loop:none
   private static long reductionLong(long[] x) {
     long sum = 0;
     for (int i = 0; i < x.length; i++) {
@@ -67,6 +109,90 @@
     return sum;
   }
 
+  private static byte reductionByteM1(byte[] x) {
+    byte sum = -1;
+    for (int i = 0; i < x.length; i++) {
+      sum += x[i];
+    }
+    return sum;
+  }
+
+  private static short reductionShortM1(short[] x) {
+    short sum = -1;
+    for (int i = 0; i < x.length; i++) {
+      sum += x[i];
+    }
+    return sum;
+  }
+
+  private static char reductionCharM1(char[] x) {
+    char sum = 0xffff;
+    for (int i = 0; i < x.length; i++) {
+      sum += x[i];
+    }
+    return sum;
+  }
+
+  /// CHECK-START: int Main.reductionIntM1(int[]) loop_optimization (before)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
+  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                 loop:none
+  /// CHECK-DAG: <<ConsM1:i\d+>> IntConstant -1                loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>   Phi [<<ConsM1>>,{{i\d+}}]     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get:i\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi2>>,<<Get>>]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]      loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: int Main.reductionIntM1(int[]) loop_optimization (after)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
+  /// CHECK-DAG: <<ConsM1:i\d+>> IntConstant -1                loop:none
+  /// CHECK-DAG: <<Cons4:i\d+>>  IntConstant 4                 loop:none
+  /// CHECK-DAG: <<Set:d\d+>>    VecSetScalars [<<ConsM1>>]    loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>   Phi [<<Set>>,{{d\d+}}]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 VecAdd [<<Phi2>>,<<Load>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons4>>]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Red:d\d+>>    VecReduce [<<Phi2>>]          loop:none
+  /// CHECK-DAG: <<Extr:i\d+>>   VecExtractScalar [<<Red>>]    loop:none
+  private static int reductionIntM1(int[] x) {
+    int sum = -1;
+    for (int i = 0; i < x.length; i++) {
+      sum += x[i];
+    }
+    return sum;
+  }
+
+  /// CHECK-START: long Main.reductionLongM1(long[]) loop_optimization (before)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
+  /// CHECK-DAG: <<LongM1:j\d+>> LongConstant -1               loop:none
+  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                 loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:j\d+>>   Phi [<<LongM1>>,{{j\d+}}]     loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get:j\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi2>>,<<Get>>]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]      loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: long Main.reductionLongM1(long[]) loop_optimization (after)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
+  /// CHECK-DAG: <<LongM1:j\d+>> LongConstant -1               loop:none
+  /// CHECK-DAG: <<Cons2:i\d+>>  IntConstant 2                 loop:none
+  /// CHECK-DAG: <<Set:d\d+>>    VecSetScalars [<<LongM1>>]    loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>   Phi [<<Set>>,{{d\d+}}]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 VecAdd [<<Phi2>>,<<Load>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons2>>]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Red:d\d+>>    VecReduce [<<Phi2>>]          loop:none
+  /// CHECK-DAG: <<Extr:j\d+>>   VecExtractScalar [<<Red>>]    loop:none
+  private static long reductionLongM1(long[] x) {
+    long sum = -1L;
+    for (int i = 0; i < x.length; i++) {
+      sum += x[i];
+    }
+    return sum;
+  }
+
   private static byte reductionMinusByte(byte[] x) {
     byte sum = 0;
     for (int i = 0; i < x.length; i++) {
@@ -91,6 +217,26 @@
     return sum;
   }
 
+  /// CHECK-START: int Main.reductionMinusInt(int[]) loop_optimization (before)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
+  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                 loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get:i\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Sub [<<Phi2>>,<<Get>>]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]      loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: int Main.reductionMinusInt(int[]) loop_optimization (after)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
+  /// CHECK-DAG: <<Cons4:i\d+>>  IntConstant 4                 loop:none
+  /// CHECK-DAG: <<Set:d\d+>>    VecSetScalars [<<Cons0>>]     loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>   Phi [<<Set>>,{{d\d+}}]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 VecSub [<<Phi2>>,<<Load>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons4>>]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Red:d\d+>>    VecReduce [<<Phi2>>]          loop:none
+  /// CHECK-DAG: <<Extr:i\d+>>   VecExtractScalar [<<Red>>]    loop:none
   private static int reductionMinusInt(int[] x) {
     int sum = 0;
     for (int i = 0; i < x.length; i++) {
@@ -99,6 +245,28 @@
     return sum;
   }
 
+  /// CHECK-START: long Main.reductionMinusLong(long[]) loop_optimization (before)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
+  /// CHECK-DAG: <<Long0:j\d+>>  LongConstant 0                loop:none
+  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                 loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:j\d+>>   Phi [<<Long0>>,{{j\d+}}]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get:j\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Sub [<<Phi2>>,<<Get>>]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]      loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: long Main.reductionMinusLong(long[]) loop_optimization (after)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
+  /// CHECK-DAG: <<Long0:j\d+>>  LongConstant 0                loop:none
+  /// CHECK-DAG: <<Cons2:i\d+>>  IntConstant 2                 loop:none
+  /// CHECK-DAG: <<Set:d\d+>>    VecSetScalars [<<Long0>>]     loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>   Phi [<<Set>>,{{d\d+}}]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 VecSub [<<Phi2>>,<<Load>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons2>>]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Red:d\d+>>    VecReduce [<<Phi2>>]          loop:none
+  /// CHECK-DAG: <<Extr:j\d+>>   VecExtractScalar [<<Red>>]    loop:none
   private static long reductionMinusLong(long[] x) {
     long sum = 0;
     for (int i = 0; i < x.length; i++) {
@@ -131,6 +299,28 @@
     return min;
   }
 
+  /// CHECK-START: int Main.reductionMinInt(int[]) loop_optimization (before)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
+  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                 loop:none
+  /// CHECK-DAG: <<ConsM:i\d+>>  IntConstant 2147483647        loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>   Phi [<<ConsM>>,{{i\d+}}]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get:i\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 InvokeStaticOrDirect [<<Phi2>>,<<Get>>] intrinsic:MathMinIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]      loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: int Main.reductionMinInt(int[]) loop_optimization (after)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
+  /// CHECK-DAG: <<ConsM:i\d+>>  IntConstant 2147483647        loop:none
+  /// CHECK-DAG: <<Cons4:i\d+>>  IntConstant 4                 loop:none
+  /// CHECK-DAG: <<Set:d\d+>>    VecSetScalars [<<ConsM>>]     loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>   Phi [<<Set>>,{{d\d+}}]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 VecMin [<<Phi2>>,<<Load>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons4>>]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Red:d\d+>>    VecReduce [<<Phi2>>]          loop:none
+  /// CHECK-DAG: <<Extr:i\d+>>   VecExtractScalar [<<Red>>]    loop:none
   private static int reductionMinInt(int[] x) {
     int min = Integer.MAX_VALUE;
     for (int i = 0; i < x.length; i++) {
@@ -171,6 +361,28 @@
     return max;
   }
 
+  /// CHECK-START: int Main.reductionMaxInt(int[]) loop_optimization (before)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
+  /// CHECK-DAG: <<Cons1:i\d+>>  IntConstant 1                 loop:none
+  /// CHECK-DAG: <<ConsM:i\d+>>  IntConstant -2147483648       loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:i\d+>>   Phi [<<ConsM>>,{{i\d+}}]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Get:i\d+>>    ArrayGet [{{l\d+}},<<Phi1>>]  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 InvokeStaticOrDirect [<<Phi2>>,<<Get>>] intrinsic:MathMaxIntInt loop:<<Loop>> outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons1>>]      loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: int Main.reductionMaxInt(int[]) loop_optimization (after)
+  /// CHECK-DAG: <<Cons0:i\d+>>  IntConstant 0                 loop:none
+  /// CHECK-DAG: <<ConsM:i\d+>>  IntConstant -2147483648       loop:none
+  /// CHECK-DAG: <<Cons4:i\d+>>  IntConstant 4                 loop:none
+  /// CHECK-DAG: <<Set:d\d+>>    VecSetScalars [<<ConsM>>]     loop:none
+  /// CHECK-DAG: <<Phi1:i\d+>>   Phi [<<Cons0>>,{{i\d+}}]      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: <<Phi2:d\d+>>   Phi [<<Set>>,{{d\d+}}]        loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Load:d\d+>>   VecLoad [{{l\d+}},<<Phi1>>]   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 VecMax [<<Phi2>>,<<Load>>]    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG:                 Add [<<Phi1>>,<<Cons4>>]      loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: <<Red:d\d+>>    VecReduce [<<Phi2>>]          loop:none
+  /// CHECK-DAG: <<Extr:i\d+>>   VecExtractScalar [<<Red>>]    loop:none
   private static int reductionMaxInt(int[] x) {
     int max = Integer.MIN_VALUE;
     for (int i = 0; i < x.length; i++) {
@@ -253,6 +465,11 @@
     expectEquals(38070, reductionChar(xc));
     expectEquals(365750, reductionInt(xi));
     expectEquals(365750L, reductionLong(xl));
+    expectEquals(-75, reductionByteM1(xb));
+    expectEquals(-27467, reductionShortM1(xs));
+    expectEquals(38069, reductionCharM1(xc));
+    expectEquals(365749, reductionIntM1(xi));
+    expectEquals(365749L, reductionLongM1(xl));
     expectEquals(74, reductionMinusByte(xb));
     expectEquals(27466, reductionMinusShort(xs));
     expectEquals(27466, reductionMinusChar(xc));
diff --git a/test/665-checker-simd-zero/expected.txt b/test/665-checker-simd-zero/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/665-checker-simd-zero/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/665-checker-simd-zero/info.txt b/test/665-checker-simd-zero/info.txt
new file mode 100644
index 0000000..55eca88
--- /dev/null
+++ b/test/665-checker-simd-zero/info.txt
@@ -0,0 +1 @@
+Functional tests on zero-out SIMD vectorization.
diff --git a/test/665-checker-simd-zero/src/Main.java b/test/665-checker-simd-zero/src/Main.java
new file mode 100644
index 0000000..66eea64
--- /dev/null
+++ b/test/665-checker-simd-zero/src/Main.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Tests for zero vectorization.
+ */
+public class Main {
+
+  /// CHECK-START: void Main.zeroz(boolean[]) loop_optimization (before)
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                        loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.zeroz(boolean[]) loop_optimization (after)
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                        loop:none
+  /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>]        loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>>      outer_loop:none
+  private static void zeroz(boolean[] x) {
+    for (int i = 0; i < x.length; i++) {
+      x[i] = false;
+    }
+  }
+
+  /// CHECK-START: void Main.zerob(byte[]) loop_optimization (before)
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                        loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.zerob(byte[]) loop_optimization (after)
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                        loop:none
+  /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>]        loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>>      outer_loop:none
+  private static void zerob(byte[] x) {
+    for (int i = 0; i < x.length; i++) {
+      x[i] = 0;
+    }
+  }
+
+  /// CHECK-START: void Main.zeroc(char[]) loop_optimization (before)
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                        loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.zeroc(char[]) loop_optimization (after)
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                        loop:none
+  /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>]        loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>>      outer_loop:none
+  private static void zeroc(char[] x) {
+    for (int i = 0; i < x.length; i++) {
+      x[i] = 0;
+    }
+  }
+
+  /// CHECK-START: void Main.zeros(short[]) loop_optimization (before)
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                        loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.zeros(short[]) loop_optimization (after)
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                        loop:none
+  /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>]        loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>>      outer_loop:none
+  private static void zeros(short[] x) {
+    for (int i = 0; i < x.length; i++) {
+      x[i] = 0;
+    }
+  }
+
+  /// CHECK-START: void Main.zeroi(int[]) loop_optimization (before)
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                        loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.zeroi(int[]) loop_optimization (after)
+  /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0                        loop:none
+  /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>]        loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>>      outer_loop:none
+  private static void zeroi(int[] x) {
+    for (int i = 0; i < x.length; i++) {
+      x[i] = 0;
+    }
+  }
+
+  /// CHECK-START: void Main.zerol(long[]) loop_optimization (before)
+  /// CHECK-DAG: <<Zero:j\d+>> LongConstant 0                       loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.zerol(long[]) loop_optimization (after)
+  /// CHECK-DAG: <<Zero:j\d+>> LongConstant 0                       loop:none
+  /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>]        loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>>      outer_loop:none
+  private static void zerol(long[] x) {
+    for (int i = 0; i < x.length; i++) {
+      x[i] = 0;
+    }
+  }
+
+  /// CHECK-START: void Main.zerof(float[]) loop_optimization (before)
+  /// CHECK-DAG: <<Zero:f\d+>> FloatConstant 0                      loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.zerof(float[]) loop_optimization (after)
+  /// CHECK-DAG: <<Zero:f\d+>> FloatConstant 0                      loop:none
+  /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>]        loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>>      outer_loop:none
+  private static void zerof(float[] x) {
+    for (int i = 0; i < x.length; i++) {
+      x[i] = 0;
+    }
+  }
+
+  /// CHECK-START: void Main.zerod(double[]) loop_optimization (before)
+  /// CHECK-DAG: <<Zero:d\d+>> DoubleConstant 0                     loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               ArraySet [{{l\d+}},<<Phi>>,<<Zero>>] loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.zerod(double[]) loop_optimization (after)
+  /// CHECK-DAG: <<Zero:d\d+>> DoubleConstant 0                     loop:none
+  /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Zero>>]        loop:none
+  /// CHECK-DAG: <<Phi:i\d+>>  Phi                                  loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG:               VecStore [{{l\d+}},<<Phi>>,<<Repl>>] loop:<<Loop>>      outer_loop:none
+  private static void zerod(double[] x) {
+    for (int i = 0; i < x.length; i++) {
+      x[i] = 0;
+    }
+  }
+
+  public static void main(String[] args) {
+    int total = 1111;
+
+    boolean[] xz = new boolean[total];
+    byte[]    xb = new byte[total];
+    char[]    xc = new char[total];
+    short[]   xs = new short[total];
+    int[]     xi = new int[total];
+    long[]    xl = new long[total];
+    float[]   xf = new float[total];
+    double[]  xd = new double[total];
+
+    for (int i = 0; i < total; i++) {
+      xz[i] = true;
+      xb[i] = 1;
+      xc[i] = 1;
+      xs[i] = 1;
+      xi[i] = 1;
+      xl[i] = 1;
+      xf[i] = 1;
+      xd[i] = 1;
+    }
+
+    for (int i = 0; i < total; i++) {
+      expectEquals(true, xz[i]);
+      expectEquals(1, xb[i]);
+      expectEquals(1, xc[i]);
+      expectEquals(1, xs[i]);
+      expectEquals(1, xi[i]);
+      expectEquals(1, xl[i]);
+      expectEquals(1, xf[i]);
+      expectEquals(1, xd[i]);
+    }
+
+    zeroz(xz);
+    zerob(xb);
+    zeroc(xc);
+    zeros(xs);
+    zeroi(xi);
+    zerol(xl);
+    zerof(xf);
+    zerod(xd);
+
+    for (int i = 0; i < total; i++) {
+      expectEquals(false, xz[i]);
+      expectEquals(0, xb[i]);
+      expectEquals(0, xc[i]);
+      expectEquals(0, xs[i]);
+      expectEquals(0, xi[i]);
+      expectEquals(0, xl[i]);
+      expectEquals(0, xf[i]);
+      expectEquals(0, xd[i]);
+    }
+
+    System.out.println("passed");
+  }
+
+  private static void expectEquals(boolean expected, boolean result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  private static void expectEquals(int expected, int result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  private static void expectEquals(long expected, long result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  private static void expectEquals(float expected, float result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+
+  private static void expectEquals(double expected, double result) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result);
+    }
+  }
+}
diff --git a/test/Android.bp b/test/Android.bp
index 2aed50c..2f23056 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -250,10 +250,12 @@
         "ti-agent/common_helper.cc",
         "ti-agent/frame_pop_helper.cc",
         "ti-agent/locals_helper.cc",
+        "ti-agent/monitors_helper.cc",
         "ti-agent/redefinition_helper.cc",
         "ti-agent/suspension_helper.cc",
         "ti-agent/stack_trace_helper.cc",
         "ti-agent/trace_helper.cc",
+        "ti-agent/exceptions_helper.cc",
         // This is the list of non-special OnLoad things and excludes BCI and anything that depends
         // on ART internals.
         "903-hello-tagging/tagging.cc",
@@ -298,6 +300,8 @@
         "1922-owned-monitors-info/owned_monitors.cc",
         "1924-frame-pop-toggle/frame_pop_toggle.cc",
         "1926-missed-frame-pop/frame_pop_missed.cc",
+        "1927-exception-event/exception_event.cc",
+        "1930-monitor-info/monitor.cc",
     ],
     shared_libs: [
         "libbase",
@@ -347,6 +351,7 @@
     ],
     shared_libs: [
         "libbase",
+        "slicer",
     ],
     header_libs: ["libopenjdkjvmti_headers"],
 }
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 90e2600..c16c487 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -422,10 +422,7 @@
   if [[ "$JVMTI_REDEFINE_STRESS" = "y" ]]; then
     # We really cannot do this on RI so don't both passing it in that case.
     if [[ "$USE_JVM" = "n" ]]; then
-      file_1=$(mktemp --tmpdir=${DEX_LOCATION})
-      file_2=$(mktemp --tmpdir=${DEX_LOCATION})
-      # TODO Remove need for DEXTER_BINARY!
-      agent_args="${agent_args},redefine,${DEXTER_BINARY},${file_1},${file_2}"
+      agent_args="${agent_args},redefine"
     fi
   fi
   if [[ "$JVMTI_FIELD_STRESS" = "y" ]]; then
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 09c3743..84758c9 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -267,6 +267,11 @@
         "variant": "npictest"
     },
     {
+        "tests": "639-checker-code-sinking",
+        "variant": "pictest",
+        "bug": "http://b/65366606"
+    },
+    {
         "tests": "055-enum-performance",
         "variant": "optimizing | regalloc_gc",
         "description": ["055: Exceeds run time limits due to heap poisoning ",
diff --git a/test/ti-agent/exceptions_helper.cc b/test/ti-agent/exceptions_helper.cc
new file mode 100644
index 0000000..74f7ecc
--- /dev/null
+++ b/test/ti-agent/exceptions_helper.cc
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common_helper.h"
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
+
+namespace art {
+
+namespace common_exceptions {
+
+struct ExceptionsData {
+  jclass test_klass;
+  jclass exception_klass;
+  jmethodID exception_event;
+  jmethodID exception_catch_event;
+};
+
+static void exceptionCB(jvmtiEnv* jvmti,
+                        JNIEnv* jnienv,
+                        jthread thread,
+                        jmethodID throw_method,
+                        jlocation throw_location,
+                        jobject throwable,
+                        jmethodID catch_method,
+                        jlocation catch_location) {
+  ExceptionsData* data = nullptr;
+  if (JvmtiErrorToException(jnienv, jvmti,
+                            jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+    return;
+  }
+  DCHECK(throwable != nullptr);
+  if (!jnienv->IsInstanceOf(throwable, data->exception_klass)) {
+    return;
+  }
+  jthrowable e = jnienv->ExceptionOccurred();
+  jnienv->ExceptionClear();
+  CHECK(data->exception_event != nullptr);
+  jobject throw_method_arg = GetJavaMethod(jvmti, jnienv, throw_method);
+  jobject catch_method_arg =
+      catch_method != nullptr ? GetJavaMethod(jvmti, jnienv, catch_method) : nullptr;
+  jnienv->CallStaticVoidMethod(data->test_klass,
+                               data->exception_event,
+                               thread,
+                               throw_method_arg,
+                               static_cast<jlong>(throw_location),
+                               throwable,
+                               catch_method_arg,
+                               static_cast<jlong>(catch_location));
+  jnienv->DeleteLocalRef(throw_method_arg);
+  if (catch_method_arg != nullptr) {
+    jnienv->DeleteLocalRef(catch_method_arg);
+  }
+  if (e != nullptr) {
+    jnienv->Throw(e);
+  }
+}
+
+
+static void exceptionCatchCB(jvmtiEnv* jvmti,
+                             JNIEnv* jnienv,
+                             jthread thread,
+                             jmethodID catch_method,
+                             jlocation catch_location,
+                             jobject throwable) {
+  ExceptionsData* data = nullptr;
+  if (JvmtiErrorToException(jnienv, jvmti,
+                            jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+    return;
+  }
+  if (!jnienv->IsSameObject(data->exception_klass, jnienv->GetObjectClass(throwable))) {
+    return;
+  }
+  jthrowable e = jnienv->ExceptionOccurred();
+  jnienv->ExceptionClear();
+  CHECK(data->exception_catch_event != nullptr);
+  jobject catch_method_arg = GetJavaMethod(jvmti, jnienv, catch_method);
+  jnienv->CallStaticVoidMethod(data->test_klass,
+                               data->exception_catch_event,
+                               thread,
+                               catch_method_arg,
+                               static_cast<jlong>(catch_location),
+                               throwable);
+  jnienv->DeleteLocalRef(catch_method_arg);
+  if (e != nullptr) {
+    jnienv->Throw(e);
+  }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Exceptions_setupExceptionTracing(
+    JNIEnv* env,
+    jclass exception ATTRIBUTE_UNUSED,
+    jclass klass,
+    jclass except,
+    jobject exception_event,
+    jobject exception_catch_event) {
+  ExceptionsData* data = nullptr;
+  if (JvmtiErrorToException(env,
+                            jvmti_env,
+                            jvmti_env->Allocate(sizeof(ExceptionsData),
+                                                reinterpret_cast<unsigned char**>(&data)))) {
+    return;
+  }
+  jvmtiCapabilities caps;
+  memset(&caps, 0, sizeof(caps));
+  caps.can_generate_exception_events = 1;
+  if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) {
+    return;
+  }
+  memset(data, 0, sizeof(ExceptionsData));
+  data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(klass));
+  data->exception_klass = reinterpret_cast<jclass>(env->NewGlobalRef(except));
+  data->exception_event =
+      exception_event != nullptr ?  env->FromReflectedMethod(exception_event) : nullptr;
+  data->exception_catch_event =
+      exception_catch_event != nullptr ? env->FromReflectedMethod(exception_catch_event) : nullptr;
+
+  ExceptionsData* old_data = nullptr;
+  if (JvmtiErrorToException(env, jvmti_env,
+                            jvmti_env->GetEnvironmentLocalStorage(
+                                reinterpret_cast<void**>(&old_data)))) {
+    return;
+  } else if (old_data != nullptr && old_data->test_klass != nullptr) {
+    ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+    env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
+    return;
+  }
+  if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
+    return;
+  }
+
+  jvmtiEventCallbacks cb;
+  memset(&cb, 0, sizeof(cb));
+  cb.Exception = exceptionCB;
+  cb.ExceptionCatch = exceptionCatchCB;
+  if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
+    return;
+  }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Exceptions_enableExceptionCatchEvent(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) {
+  JvmtiErrorToException(env,
+                        jvmti_env,
+                        jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+                                                            JVMTI_EVENT_EXCEPTION_CATCH,
+                                                            thr));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Exceptions_enableExceptionEvent(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) {
+  JvmtiErrorToException(env,
+                        jvmti_env,
+                        jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+                                                            JVMTI_EVENT_EXCEPTION,
+                                                            thr));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Exceptions_disableExceptionCatchEvent(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) {
+  JvmtiErrorToException(env,
+                        jvmti_env,
+                        jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+                                                            JVMTI_EVENT_EXCEPTION_CATCH,
+                                                            thr));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Exceptions_disableExceptionEvent(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thr) {
+  JvmtiErrorToException(env,
+                        jvmti_env,
+                        jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+                                                            JVMTI_EVENT_EXCEPTION,
+                                                            thr));
+}
+
+}  // namespace common_exceptions
+
+
+}  // namespace art
diff --git a/test/ti-agent/jvmti_helper.cc b/test/ti-agent/jvmti_helper.cc
index 7280102..c290e9b 100644
--- a/test/ti-agent/jvmti_helper.cc
+++ b/test/ti-agent/jvmti_helper.cc
@@ -50,7 +50,7 @@
     .can_get_synthetic_attribute                     = 1,
     .can_get_owned_monitor_info                      = 0,
     .can_get_current_contended_monitor               = 0,
-    .can_get_monitor_info                            = 0,
+    .can_get_monitor_info                            = 1,
     .can_pop_frame                                   = 0,
     .can_redefine_classes                            = 1,
     .can_signal_thread                               = 0,
diff --git a/test/ti-agent/monitors_helper.cc b/test/ti-agent/monitors_helper.cc
new file mode 100644
index 0000000..7c28ede
--- /dev/null
+++ b/test/ti-agent/monitors_helper.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "jni.h"
+#include "jvmti.h"
+#include <vector>
+#include "jvmti_helper.h"
+#include "jni_helper.h"
+#include "test_env.h"
+#include "scoped_local_ref.h"
+namespace art {
+namespace common_monitors {
+
+extern "C" JNIEXPORT jobject JNICALL Java_art_Monitors_getObjectMonitorUsage(
+    JNIEnv* env, jclass, jobject obj) {
+  ScopedLocalRef<jclass> klass(env, env->FindClass("art/Monitors$MonitorUsage"));
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  }
+  jmethodID constructor = env->GetMethodID(
+      klass.get(),
+      "<init>",
+      "(Ljava/lang/Object;Ljava/lang/Thread;I[Ljava/lang/Thread;[Ljava/lang/Thread;)V");
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  }
+  jvmtiMonitorUsage usage;
+  if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetObjectMonitorUsage(obj, &usage))) {
+    return nullptr;
+  }
+  jobjectArray wait = CreateObjectArray(env, usage.waiter_count, "java/lang/Thread",
+                                        [&](jint i) { return usage.waiters[i]; });
+  if (env->ExceptionCheck()) {
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.waiters));
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.notify_waiters));
+    return nullptr;
+  }
+  jobjectArray notify_wait = CreateObjectArray(env, usage.notify_waiter_count, "java/lang/Thread",
+                                               [&](jint i) { return usage.notify_waiters[i]; });
+  if (env->ExceptionCheck()) {
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.waiters));
+    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(usage.notify_waiters));
+    return nullptr;
+  }
+  return env->NewObject(klass.get(), constructor,
+                        obj, usage.owner, usage.entry_count, wait, notify_wait);
+}
+
+}  // namespace common_monitors
+}  // namespace art
diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc
index 5d7c2f3..6e29e36 100644
--- a/test/ti-stress/stress.cc
+++ b/test/ti-stress/stress.cc
@@ -28,15 +28,31 @@
 #include "jvmti.h"
 #include "utils.h"
 
+#pragma clang diagnostic push
+// slicer defines its own CHECK. b/65422458
+#pragma push_macro("CHECK")
+#undef CHECK
+
+// Slicer's headers have code that triggers these warnings. b/65298177
+#pragma clang diagnostic ignored "-Wunused-parameter"
+#pragma clang diagnostic ignored "-Wsign-compare"
+#include "code_ir.h"
+#include "control_flow_graph.h"
+#include "dex_ir.h"
+#include "dex_ir_builder.h"
+#include "instrumentation.h"
+#include "reader.h"
+#include "writer.h"
+
+#pragma pop_macro("CHECK")
+#pragma clang diagnostic pop
+
 namespace art {
 
 // Should we do a 'full_rewrite' with this test?
 static constexpr bool kDoFullRewrite = true;
 
 struct StressData {
-  std::string dexter_cmd;
-  std::string out_temp_dex;
-  std::string in_temp_dex;
   bool vm_class_loader_initialized;
   bool trace_stress;
   bool redefine_stress;
@@ -44,51 +60,60 @@
   bool step_stress;
 };
 
-static void WriteToFile(const std::string& fname, jint data_len, const unsigned char* data) {
-  std::ofstream file(fname, std::ios::binary | std::ios::out | std::ios::trunc);
-  file.write(reinterpret_cast<const char*>(data), data_len);
-  file.flush();
-}
-
-static bool ReadIntoBuffer(const std::string& fname, /*out*/std::vector<unsigned char>* data) {
-  std::ifstream file(fname, std::ios::binary | std::ios::in);
-  file.seekg(0, std::ios::end);
-  size_t len = file.tellg();
-  data->resize(len);
-  file.seekg(0);
-  file.read(reinterpret_cast<char*>(data->data()), len);
-  return len != 0;
-}
-
-// TODO rewrite later.
-static bool DoExtractClassFromData(StressData* data,
-                                   const std::string& class_name,
+static bool DoExtractClassFromData(jvmtiEnv* env,
+                                   const std::string& descriptor,
                                    jint in_len,
                                    const unsigned char* in_data,
-                                   /*out*/std::vector<unsigned char>* dex) {
-  // Write the dex file into a temporary file.
-  WriteToFile(data->in_temp_dex, in_len, in_data);
-  // Clear out file so even if something suppresses the exit value we will still detect dexter
-  // failure.
-  WriteToFile(data->out_temp_dex, 0, nullptr);
-  // Have dexter do the extraction.
-  std::vector<std::string> args;
-  args.push_back(data->dexter_cmd);
-  if (kDoFullRewrite) {
-    args.push_back("-x");
-    args.push_back("full_rewrite");
-  }
-  args.push_back("-e");
-  args.push_back(class_name);
-  args.push_back("-o");
-  args.push_back(data->out_temp_dex);
-  args.push_back(data->in_temp_dex);
-  std::string error;
-  if (ExecAndReturnCode(args, &error) != 0) {
-    LOG(ERROR) << "unable to execute dexter: " << error;
+                                   /*out*/jint* out_len,
+                                   /*out*/unsigned char** out_data) {
+  dex::Reader reader(in_data, in_len);
+  dex::u4 class_idx = reader.FindClassIndex(descriptor.c_str());
+  if (class_idx != dex::kNoIndex) {
+    reader.CreateClassIr(class_idx);
+  } else {
+    LOG(ERROR) << "ERROR: Can't find class " << descriptor;
     return false;
   }
-  return ReadIntoBuffer(data->out_temp_dex, dex);
+  auto dex_ir = reader.GetIr();
+
+  if (kDoFullRewrite) {
+    for (auto& ir_method : dex_ir->encoded_methods) {
+      if (ir_method->code != nullptr) {
+        lir::CodeIr code_ir(ir_method.get(), dex_ir);
+        lir::ControlFlowGraph cfg_compact(&code_ir, false);
+        lir::ControlFlowGraph cfg_verbose(&code_ir, true);
+        code_ir.Assemble();
+      }
+    }
+  }
+  dex::Writer writer(dex_ir);
+
+  struct Allocator : public dex::Writer::Allocator {
+    explicit Allocator(jvmtiEnv* jvmti_env) : jvmti_env_(jvmti_env) {}
+    virtual void* Allocate(size_t size) {
+      unsigned char* out = nullptr;
+      if (JVMTI_ERROR_NONE != jvmti_env_->Allocate(size, &out)) {
+        return nullptr;
+      } else {
+        return out;
+      }
+    }
+    virtual void Free(void* ptr) {
+      jvmti_env_->Deallocate(reinterpret_cast<unsigned char*>(ptr));
+    }
+   private:
+    jvmtiEnv* jvmti_env_;
+  };
+  Allocator alloc(env);
+  size_t res_len;
+  unsigned char* res = writer.CreateImage(&alloc, &res_len);
+  if (res != nullptr) {
+    *out_data = res;
+    *out_len = res_len;
+    return true;
+  } else {
+    return false;
+  }
 }
 
 class ScopedThreadInfo {
@@ -615,10 +640,10 @@
                                          jint* new_class_data_len,
                                          unsigned char** new_class_data) {
   std::vector<unsigned char> out;
-  std::string name_str(name);
-  // Make the jvmti semi-descriptor into the java style descriptor (though with $ for inner
-  // classes).
-  std::replace(name_str.begin(), name_str.end(), '/', '.');
+  // Make the jvmti semi-descriptor into the full descriptor.
+  std::string name_str("L");
+  name_str += name;
+  name_str += ";";
   StressData* data = nullptr;
   CHECK_EQ(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)),
            JVMTI_ERROR_NONE);
@@ -626,15 +651,11 @@
     LOG(WARNING) << "Ignoring load of class " << name << " because VMClassLoader is not yet "
                  << "initialized. Transforming this class could cause spurious test failures.";
     return;
-  } else if (DoExtractClassFromData(data, name_str, class_data_len, class_data, /*out*/ &out)) {
+  } else if (DoExtractClassFromData(jvmti, name_str, class_data_len, class_data,
+                                    /*out*/ new_class_data_len, /*out*/ new_class_data)) {
     LOG(INFO) << "Extracted class: " << name;
-    unsigned char* new_data;
-    CHECK_EQ(JVMTI_ERROR_NONE, jvmti->Allocate(out.size(), &new_data));
-    memcpy(new_data, out.data(), out.size());
-    *new_class_data_len = static_cast<jint>(out.size());
-    *new_class_data = new_data;
   } else {
-    std::cerr << "Unable to extract class " << name_str << std::endl;
+    std::cerr << "Unable to extract class " << name << std::endl;
     *new_class_data_len = 0;
     *new_class_data = nullptr;
   }
@@ -653,7 +674,7 @@
 }
 
 // Options are
-// jvmti-stress,[redefine,${DEXTER_BINARY},${TEMP_FILE_1},${TEMP_FILE_2},][trace,][field]
+// jvmti-stress,[redefine,][trace,][field]
 static void ReadOptions(StressData* data, char* options) {
   std::string ops(options);
   CHECK_EQ(GetOption(ops), "jvmti-stress") << "Options should start with jvmti-stress";
@@ -668,12 +689,6 @@
       data->field_stress = true;
     } else if (cur == "redefine") {
       data->redefine_stress = true;
-      ops = AdvanceOption(ops);
-      data->dexter_cmd = GetOption(ops);
-      ops = AdvanceOption(ops);
-      data->in_temp_dex = GetOption(ops);
-      ops = AdvanceOption(ops);
-      data->out_temp_dex = GetOption(ops);
     } else {
       LOG(FATAL) << "Unknown option: " << GetOption(ops);
     }
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index 5806b61..d27b8fc 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -11,9 +11,11 @@
   names: ["jsr166.CompletableFutureTest#testCompleteOnTimeout_completed",
           "libcore.icu.TransliteratorTest#testAll",
           "libcore.icu.RelativeDateTimeFormatterTest#test_bug25821045",
+          "libcore.icu.RelativeDateTimeFormatterTest#test_bug25883157",
           "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndTimeout",
           "libcore.java.lang.ref.ReferenceQueueTest#testRemoveWithDelayedResultAndNoTimeout",
           "libcore.java.util.TimeZoneTest#testSetDefaultDeadlock",
+          "libcore.javax.crypto.CipherBasicsTest#testBasicEncryption",
           "org.apache.harmony.tests.java.util.TimerTest#testThrowingTaskKillsTimerThread"]
 }
 ]