Merge "Merge profiles without needing to creating profile_compilation_info object"
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 115e722..6ee9cc6 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -2593,10 +2593,6 @@
 
   CopyReference(copy->GetDeclaringClassAddressWithoutBarrier(), orig->GetDeclaringClassUnchecked());
 
-  mirror::MethodDexCacheType* orig_resolved_methods =
-      orig->GetDexCacheResolvedMethods(target_ptr_size_);
-  copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods), target_ptr_size_);
-
   // OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to
   // oat_begin_
 
diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc
index 18ff1c9..4ca5afe 100644
--- a/compiler/linker/arm/relative_patcher_arm_base.cc
+++ b/compiler/linker/arm/relative_patcher_arm_base.cc
@@ -28,7 +28,7 @@
 class ArmBaseRelativePatcher::ThunkData {
  public:
   ThunkData(std::vector<uint8_t> code, uint32_t max_next_offset)
-      : code_(code),
+      : code_(std::move(code)),
         offsets_(),
         max_next_offset_(max_next_offset),
         pending_offset_(0u) {
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..57e7dc6 100644
--- a/compiler/optimizing/code_generator_vector_x86.cc
+++ b/compiler/optimizing/code_generator_vector_x86.cc
@@ -27,9 +27,97 @@
 
 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(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 +125,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 +139,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 +174,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 +195,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 +946,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 +1078,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 +1113,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..efab0db 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,88 @@
     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(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 +127,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 +157,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 +178,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 +929,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 +1051,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 +1086,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/emit_swap_mips_test.cc b/compiler/optimizing/emit_swap_mips_test.cc
index 0d4e1c5..fa3c4df 100644
--- a/compiler/optimizing/emit_swap_mips_test.cc
+++ b/compiler/optimizing/emit_swap_mips_test.cc
@@ -91,7 +91,9 @@
     return nullptr;
   }
 
-  void DriverWrapper(HParallelMove* move, std::string assembly_text, std::string test_name) {
+  void DriverWrapper(HParallelMove* move,
+                     const std::string& assembly_text,
+                     const std::string& test_name) {
     codegen_->GetMoveResolver()->EmitNativeCode(move);
     assembler_ = codegen_->GetAssembler();
     assembler_->FinalizeCode();
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index a20ec3c..3035e46 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -501,6 +501,20 @@
     StartAttributeStream("field_type") << iset->GetFieldType();
   }
 
+  void VisitStaticFieldGet(HStaticFieldGet* sget) OVERRIDE {
+    StartAttributeStream("field_name") <<
+        sget->GetFieldInfo().GetDexFile().PrettyField(sget->GetFieldInfo().GetFieldIndex(),
+                                                      /* with type */ false);
+    StartAttributeStream("field_type") << sget->GetFieldType();
+  }
+
+  void VisitStaticFieldSet(HStaticFieldSet* sset) OVERRIDE {
+    StartAttributeStream("field_name") <<
+        sset->GetFieldInfo().GetDexFile().PrettyField(sset->GetFieldInfo().GetFieldIndex(),
+                                                      /* with type */ false);
+    StartAttributeStream("field_type") << sset->GetFieldType();
+  }
+
   void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* field_access) OVERRIDE {
     StartAttributeStream("field_type") << field_access->GetFieldType();
   }
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index e5463bf..1ed1b75 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -566,14 +566,6 @@
   MathAbsFP(invoke->GetLocations(), /* is64bit */ false, GetVIXLAssembler());
 }
 
-static void CreateIntToInt(ArenaAllocator* arena, HInvoke* invoke) {
-  LocationSummary* locations = new (arena) LocationSummary(invoke,
-                                                           LocationSummary::kNoCall,
-                                                           kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
-}
-
 static void GenAbsInteger(LocationSummary* locations,
                           bool is64bit,
                           MacroAssembler* masm) {
@@ -588,7 +580,7 @@
 }
 
 void IntrinsicLocationsBuilderARM64::VisitMathAbsInt(HInvoke* invoke) {
-  CreateIntToInt(arena_, invoke);
+  CreateIntToIntLocations(arena_, invoke);
 }
 
 void IntrinsicCodeGeneratorARM64::VisitMathAbsInt(HInvoke* invoke) {
@@ -596,7 +588,7 @@
 }
 
 void IntrinsicLocationsBuilderARM64::VisitMathAbsLong(HInvoke* invoke) {
-  CreateIntToInt(arena_, invoke);
+  CreateIntToIntLocations(arena_, invoke);
 }
 
 void IntrinsicCodeGeneratorARM64::VisitMathAbsLong(HInvoke* invoke) {
@@ -1641,12 +1633,13 @@
   }
 
   // Assertions that must hold in order to compare strings 8 bytes at a time.
+  // Ok to do this because strings are zero-padded to kObjectAlignment.
   DCHECK_ALIGNED(value_offset, 8);
   static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded");
 
   if (const_string != nullptr &&
-      const_string_length < (is_compressed ? kShortConstStringEqualsCutoffInBytes
-                                           : kShortConstStringEqualsCutoffInBytes / 2u)) {
+      const_string_length <= (is_compressed ? kShortConstStringEqualsCutoffInBytes
+                                            : kShortConstStringEqualsCutoffInBytes / 2u)) {
     // Load and compare the contents. Though we know the contents of the short const string
     // at compile time, materializing constants may be more code than loading from memory.
     int32_t offset = value_offset;
@@ -1654,7 +1647,7 @@
         RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u);
     temp = temp.X();
     temp1 = temp1.X();
-    while (remaining_bytes > 8u) {
+    while (remaining_bytes > sizeof(uint64_t)) {
       Register temp2 = XRegisterFrom(locations->GetTemp(0));
       __ Ldp(temp, temp1, MemOperand(str.X(), offset));
       __ Ldp(temp2, out, MemOperand(arg.X(), offset));
@@ -1690,7 +1683,6 @@
     temp1 = temp1.X();
     Register temp2 = XRegisterFrom(locations->GetTemp(0));
     // Loop to compare strings 8 bytes at a time starting at the front of the string.
-    // Ok to do this because strings are zero-padded to kObjectAlignment.
     __ Bind(&loop);
     __ Ldr(out, MemOperand(str.X(), temp1));
     __ Ldr(temp2, MemOperand(arg.X(), temp1));
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 8b4044d..d2dc88a 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -331,6 +331,14 @@
   locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
 }
 
+static void CreateLongToLongLocationsWithOverlap(ArenaAllocator* arena, HInvoke* invoke) {
+  LocationSummary* locations = new (arena) LocationSummary(invoke,
+                                                           LocationSummary::kNoCall,
+                                                           kIntrinsified);
+  locations->SetInAt(0, Location::RequiresRegister());
+  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
 static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) {
   LocationSummary* locations = new (arena) LocationSummary(invoke,
                                                            LocationSummary::kNoCall,
@@ -375,11 +383,7 @@
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                           LocationSummary::kNoCall,
-                                                           kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+  CreateLongToLongLocationsWithOverlap(arena_, invoke);
 }
 
 void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* invoke) {
@@ -417,11 +421,7 @@
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kNoCall,
-                                                            kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+  CreateIntToIntLocations(arena_, invoke);
 }
 
 void IntrinsicCodeGeneratorARMVIXL::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
@@ -429,11 +429,7 @@
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kNoCall,
-                                                            kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+  CreateLongToLongLocationsWithOverlap(arena_, invoke);
 }
 
 void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
@@ -1713,6 +1709,22 @@
   }
 }
 
+// The cut off for unrolling the loop in String.equals() intrinsic for const strings.
+// The normal loop plus the pre-header is 9 instructions (18-26 bytes) without string compression
+// and 12 instructions (24-32 bytes) with string compression. We can compare up to 4 bytes in 4
+// instructions (LDR+LDR+CMP+BNE) and up to 8 bytes in 6 instructions (LDRD+LDRD+CMP+BNE+CMP+BNE).
+// Allow up to 12 instructions (32 bytes) for the unrolled loop.
+constexpr size_t kShortConstStringEqualsCutoffInBytes = 16;
+
+static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) {
+  if (candidate->IsLoadString()) {
+    HLoadString* load_string = candidate->AsLoadString();
+    const DexFile& dex_file = load_string->GetDexFile();
+    return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length);
+  }
+  return nullptr;
+}
+
 void IntrinsicLocationsBuilderARMVIXL::VisitStringEquals(HInvoke* invoke) {
   LocationSummary* locations = new (arena_) LocationSummary(invoke,
                                                             LocationSummary::kNoCall,
@@ -1720,12 +1732,29 @@
   InvokeRuntimeCallingConventionARMVIXL calling_convention;
   locations->SetInAt(0, Location::RequiresRegister());
   locations->SetInAt(1, Location::RequiresRegister());
+
   // Temporary registers to store lengths of strings and for calculations.
   // Using instruction cbz requires a low register, so explicitly set a temp to be R0.
   locations->AddTemp(LocationFrom(r0));
-  locations->AddTemp(Location::RequiresRegister());
-  locations->AddTemp(Location::RequiresRegister());
 
+  // For the generic implementation and for long const strings we need an extra temporary.
+  // We do not need it for short const strings, up to 4 bytes, see code generation below.
+  uint32_t const_string_length = 0u;
+  const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
+  if (const_string == nullptr) {
+    const_string = GetConstString(invoke->InputAt(1), &const_string_length);
+  }
+  bool is_compressed =
+      mirror::kUseStringCompression &&
+      const_string != nullptr &&
+      mirror::String::DexFileStringAllASCII(const_string, const_string_length);
+  if (const_string == nullptr || const_string_length > (is_compressed ? 4u : 2u)) {
+    locations->AddTemp(Location::RequiresRegister());
+  }
+
+  // TODO: If the String.equals() is used only for an immediately following HIf, we can
+  // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks.
+  // Then we shall need an extra temporary register instead of the output register.
   locations->SetOut(Location::RequiresRegister());
 }
 
@@ -1738,8 +1767,6 @@
   vixl32::Register out = OutputRegister(invoke);
 
   vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
-  vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
-  vixl32::Register temp2 = RegisterFrom(locations->GetTemp(2));
 
   vixl32::Label loop;
   vixl32::Label end;
@@ -1771,53 +1798,110 @@
     // Receiver must be a string object, so its class field is equal to all strings' class fields.
     // If the argument is a string object, its class field must be equal to receiver's class field.
     __ Ldr(temp, MemOperand(str, class_offset));
-    __ Ldr(temp1, MemOperand(arg, class_offset));
-    __ Cmp(temp, temp1);
+    __ Ldr(out, MemOperand(arg, class_offset));
+    __ Cmp(temp, out);
     __ B(ne, &return_false, /* far_target */ false);
   }
 
-  // Load `count` fields of this and argument strings.
-  __ Ldr(temp, MemOperand(str, count_offset));
-  __ Ldr(temp1, MemOperand(arg, count_offset));
-  // Check if `count` fields are equal, return false if they're not.
-  // Also compares the compression style, if differs return false.
-  __ Cmp(temp, temp1);
-  __ B(ne, &return_false, /* far_target */ false);
-  // Return true if both strings are empty. Even with string compression `count == 0` means empty.
-  static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
-                "Expecting 0=compressed, 1=uncompressed");
-  __ CompareAndBranchIfZero(temp, &return_true, /* far_target */ false);
+  // Check if one of the inputs is a const string. Do not special-case both strings
+  // being const, such cases should be handled by constant folding if needed.
+  uint32_t const_string_length = 0u;
+  const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length);
+  if (const_string == nullptr) {
+    const_string = GetConstString(invoke->InputAt(1), &const_string_length);
+    if (const_string != nullptr) {
+      std::swap(str, arg);  // Make sure the const string is in `str`.
+    }
+  }
+  bool is_compressed =
+      mirror::kUseStringCompression &&
+      const_string != nullptr &&
+      mirror::String::DexFileStringAllASCII(const_string, const_string_length);
+
+  if (const_string != nullptr) {
+    // Load `count` field of the argument string and check if it matches the const string.
+    // Also compares the compression style, if differs return false.
+    __ Ldr(temp, MemOperand(arg, count_offset));
+    __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed)));
+    __ B(ne, &return_false, /* far_target */ false);
+  } else {
+    // Load `count` fields of this and argument strings.
+    __ Ldr(temp, MemOperand(str, count_offset));
+    __ Ldr(out, MemOperand(arg, count_offset));
+    // Check if `count` fields are equal, return false if they're not.
+    // Also compares the compression style, if differs return false.
+    __ Cmp(temp, out);
+    __ B(ne, &return_false, /* far_target */ false);
+  }
 
   // Assertions that must hold in order to compare strings 4 bytes at a time.
+  // Ok to do this because strings are zero-padded to kObjectAlignment.
   DCHECK_ALIGNED(value_offset, 4);
   static_assert(IsAligned<4>(kObjectAlignment), "String data must be aligned for fast compare.");
 
-  if (mirror::kUseStringCompression) {
-    // For string compression, calculate the number of bytes to compare (not chars).
-    // This could in theory exceed INT32_MAX, so treat temp as unsigned.
-    __ Lsrs(temp, temp, 1u);                        // Extract length and check compression flag.
-    ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
-                           2 * kMaxInstructionSizeInBytes,
-                           CodeBufferCheckScope::kMaximumSize);
-    __ it(cs);                                      // If uncompressed,
-    __ add(cs, temp, temp, temp);                   //   double the byte count.
+  if (const_string != nullptr &&
+      const_string_length <= (is_compressed ? kShortConstStringEqualsCutoffInBytes
+                                            : kShortConstStringEqualsCutoffInBytes / 2u)) {
+    // Load and compare the contents. Though we know the contents of the short const string
+    // at compile time, materializing constants may be more code than loading from memory.
+    int32_t offset = value_offset;
+    size_t remaining_bytes =
+        RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 4u);
+    while (remaining_bytes > sizeof(uint32_t)) {
+      vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
+      UseScratchRegisterScope scratch_scope(assembler->GetVIXLAssembler());
+      vixl32::Register temp2 = scratch_scope.Acquire();
+      __ Ldrd(temp, temp1, MemOperand(str, offset));
+      __ Ldrd(temp2, out, MemOperand(arg, offset));
+      __ Cmp(temp, temp2);
+      __ B(ne, &return_false, /* far_label */ false);
+      __ Cmp(temp1, out);
+      __ B(ne, &return_false, /* far_label */ false);
+      offset += 2u * sizeof(uint32_t);
+      remaining_bytes -= 2u * sizeof(uint32_t);
+    }
+    if (remaining_bytes != 0u) {
+      __ Ldr(temp, MemOperand(str, offset));
+      __ Ldr(out, MemOperand(arg, offset));
+      __ Cmp(temp, out);
+      __ B(ne, &return_false, /* far_label */ false);
+    }
+  } else {
+    // Return true if both strings are empty. Even with string compression `count == 0` means empty.
+    static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
+                  "Expecting 0=compressed, 1=uncompressed");
+    __ CompareAndBranchIfZero(temp, &return_true, /* far_target */ false);
+
+    if (mirror::kUseStringCompression) {
+      // For string compression, calculate the number of bytes to compare (not chars).
+      // This could in theory exceed INT32_MAX, so treat temp as unsigned.
+      __ Lsrs(temp, temp, 1u);                        // Extract length and check compression flag.
+      ExactAssemblyScope aas(assembler->GetVIXLAssembler(),
+                             2 * kMaxInstructionSizeInBytes,
+                             CodeBufferCheckScope::kMaximumSize);
+      __ it(cs);                                      // If uncompressed,
+      __ add(cs, temp, temp, temp);                   //   double the byte count.
+    }
+
+    vixl32::Register temp1 = RegisterFrom(locations->GetTemp(1));
+    UseScratchRegisterScope scratch_scope(assembler->GetVIXLAssembler());
+    vixl32::Register temp2 = scratch_scope.Acquire();
+
+    // Store offset of string value in preparation for comparison loop.
+    __ Mov(temp1, value_offset);
+
+    // Loop to compare strings 4 bytes at a time starting at the front of the string.
+    __ Bind(&loop);
+    __ Ldr(out, MemOperand(str, temp1));
+    __ Ldr(temp2, MemOperand(arg, temp1));
+    __ Add(temp1, temp1, Operand::From(sizeof(uint32_t)));
+    __ Cmp(out, temp2);
+    __ B(ne, &return_false, /* far_target */ false);
+    // With string compression, we have compared 4 bytes, otherwise 2 chars.
+    __ Subs(temp, temp, mirror::kUseStringCompression ? 4 : 2);
+    __ B(hi, &loop, /* far_target */ false);
   }
 
-  // Store offset of string value in preparation for comparison loop.
-  __ Mov(temp1, value_offset);
-
-  // Loop to compare strings 4 bytes at a time starting at the front of the string.
-  // Ok to do this because strings are zero-padded to kObjectAlignment.
-  __ Bind(&loop);
-  __ Ldr(out, MemOperand(str, temp1));
-  __ Ldr(temp2, MemOperand(arg, temp1));
-  __ Add(temp1, temp1, Operand::From(sizeof(uint32_t)));
-  __ Cmp(out, temp2);
-  __ B(ne, &return_false, /* far_target */ false);
-  // With string compression, we have compared 4 bytes, otherwise 2 chars.
-  __ Subs(temp, temp, mirror::kUseStringCompression ? 4 : 2);
-  __ B(hi, &loop, /* far_target */ false);
-
   // Return true and exit the function.
   // If loop does not result in returning false, we return true.
   __ Bind(&return_true);
@@ -2723,11 +2807,7 @@
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitLongReverse(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kNoCall,
-                                                            kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+  CreateLongToLongLocationsWithOverlap(arena_, invoke);
 }
 
 void IntrinsicCodeGeneratorARMVIXL::VisitLongReverse(HInvoke* invoke) {
@@ -2753,11 +2833,7 @@
 }
 
 void IntrinsicLocationsBuilderARMVIXL::VisitLongReverseBytes(HInvoke* invoke) {
-  LocationSummary* locations = new (arena_) LocationSummary(invoke,
-                                                            LocationSummary::kNoCall,
-                                                            kIntrinsified);
-  locations->SetInAt(0, Location::RequiresRegister());
-  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+  CreateLongToLongLocationsWithOverlap(arena_, invoke);
 }
 
 void IntrinsicCodeGeneratorARMVIXL::VisitLongReverseBytes(HInvoke* invoke) {
@@ -2827,6 +2903,137 @@
   GenBitCount(invoke, Primitive::kPrimLong, GetAssembler());
 }
 
+static void GenHighestOneBit(HInvoke* invoke,
+                             Primitive::Type type,
+                             CodeGeneratorARMVIXL* codegen) {
+  DCHECK(Primitive::IsIntOrLongType(type));
+
+  ArmVIXLAssembler* assembler = codegen->GetAssembler();
+  UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
+  const vixl32::Register temp = temps.Acquire();
+
+  if (type == Primitive::kPrimLong) {
+    LocationSummary* locations = invoke->GetLocations();
+    Location in = locations->InAt(0);
+    Location out = locations->Out();
+
+    vixl32::Register in_reg_lo = LowRegisterFrom(in);
+    vixl32::Register in_reg_hi = HighRegisterFrom(in);
+    vixl32::Register out_reg_lo = LowRegisterFrom(out);
+    vixl32::Register out_reg_hi = HighRegisterFrom(out);
+
+    __ Mov(temp, 0x80000000);  // Modified immediate.
+    __ Clz(out_reg_lo, in_reg_lo);
+    __ Clz(out_reg_hi, in_reg_hi);
+    __ Lsr(out_reg_lo, temp, out_reg_lo);
+    __ Lsrs(out_reg_hi, temp, out_reg_hi);
+
+    // Discard result for lowest 32 bits if highest 32 bits are not zero.
+    // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
+    // we check that the output is in a low register, so that a 16-bit MOV
+    // encoding can be used. If output is in a high register, then we generate
+    // 4 more bytes of code to avoid a branch.
+    Operand mov_src(0);
+    if (!out_reg_lo.IsLow()) {
+      __ Mov(LeaveFlags, temp, 0);
+      mov_src = Operand(temp);
+    }
+    ExactAssemblyScope it_scope(codegen->GetVIXLAssembler(),
+                                  2 * vixl32::k16BitT32InstructionSizeInBytes,
+                                  CodeBufferCheckScope::kExactSize);
+    __ it(ne);
+    __ mov(ne, out_reg_lo, mov_src);
+  } else {
+    vixl32::Register out = OutputRegister(invoke);
+    vixl32::Register in = InputRegisterAt(invoke, 0);
+
+    __ Mov(temp, 0x80000000);  // Modified immediate.
+    __ Clz(out, in);
+    __ Lsr(out, temp, out);
+  }
+}
+
+void IntrinsicLocationsBuilderARMVIXL::VisitIntegerHighestOneBit(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARMVIXL::VisitIntegerHighestOneBit(HInvoke* invoke) {
+  GenHighestOneBit(invoke, Primitive::kPrimInt, codegen_);
+}
+
+void IntrinsicLocationsBuilderARMVIXL::VisitLongHighestOneBit(HInvoke* invoke) {
+  CreateLongToLongLocationsWithOverlap(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARMVIXL::VisitLongHighestOneBit(HInvoke* invoke) {
+  GenHighestOneBit(invoke, Primitive::kPrimLong, codegen_);
+}
+
+static void GenLowestOneBit(HInvoke* invoke,
+                            Primitive::Type type,
+                            CodeGeneratorARMVIXL* codegen) {
+  DCHECK(Primitive::IsIntOrLongType(type));
+
+  ArmVIXLAssembler* assembler = codegen->GetAssembler();
+  UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
+  const vixl32::Register temp = temps.Acquire();
+
+  if (type == Primitive::kPrimLong) {
+    LocationSummary* locations = invoke->GetLocations();
+    Location in = locations->InAt(0);
+    Location out = locations->Out();
+
+    vixl32::Register in_reg_lo = LowRegisterFrom(in);
+    vixl32::Register in_reg_hi = HighRegisterFrom(in);
+    vixl32::Register out_reg_lo = LowRegisterFrom(out);
+    vixl32::Register out_reg_hi = HighRegisterFrom(out);
+
+    __ Rsb(out_reg_hi, in_reg_hi, 0);
+    __ Rsb(out_reg_lo, in_reg_lo, 0);
+    __ And(out_reg_hi, out_reg_hi, in_reg_hi);
+    // The result of this operation is 0 iff in_reg_lo is 0
+    __ Ands(out_reg_lo, out_reg_lo, in_reg_lo);
+
+    // Discard result for highest 32 bits if lowest 32 bits are not zero.
+    // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
+    // we check that the output is in a low register, so that a 16-bit MOV
+    // encoding can be used. If output is in a high register, then we generate
+    // 4 more bytes of code to avoid a branch.
+    Operand mov_src(0);
+    if (!out_reg_lo.IsLow()) {
+      __ Mov(LeaveFlags, temp, 0);
+      mov_src = Operand(temp);
+    }
+    ExactAssemblyScope it_scope(codegen->GetVIXLAssembler(),
+                                  2 * vixl32::k16BitT32InstructionSizeInBytes,
+                                  CodeBufferCheckScope::kExactSize);
+    __ it(ne);
+    __ mov(ne, out_reg_hi, mov_src);
+  } else {
+    vixl32::Register out = OutputRegister(invoke);
+    vixl32::Register in = InputRegisterAt(invoke, 0);
+
+    __ Rsb(temp, in, 0);
+    __ And(out, temp, in);
+  }
+}
+
+void IntrinsicLocationsBuilderARMVIXL::VisitIntegerLowestOneBit(HInvoke* invoke) {
+  CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARMVIXL::VisitIntegerLowestOneBit(HInvoke* invoke) {
+  GenLowestOneBit(invoke, Primitive::kPrimInt, codegen_);
+}
+
+void IntrinsicLocationsBuilderARMVIXL::VisitLongLowestOneBit(HInvoke* invoke) {
+  CreateLongToLongLocationsWithOverlap(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARMVIXL::VisitLongLowestOneBit(HInvoke* invoke) {
+  GenLowestOneBit(invoke, Primitive::kPrimLong, codegen_);
+}
+
 void IntrinsicLocationsBuilderARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) {
   LocationSummary* locations = new (arena_) LocationSummary(invoke,
                                                             LocationSummary::kNoCall,
@@ -3124,10 +3331,6 @@
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, UnsafeCASLong)     // High register pressure.
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, SystemArrayCopyChar)
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(ARMVIXL, IntegerHighestOneBit)
-UNIMPLEMENTED_INTRINSIC(ARMVIXL, LongHighestOneBit)
-UNIMPLEMENTED_INTRINSIC(ARMVIXL, IntegerLowestOneBit)
-UNIMPLEMENTED_INTRINSIC(ARMVIXL, LongLowestOneBit)
 
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOf);
 UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h
index a2c1794..02bc254 100644
--- a/compiler/optimizing/load_store_analysis.h
+++ b/compiler/optimizing/load_store_analysis.h
@@ -461,49 +461,15 @@
     has_heap_stores_ = true;
   }
 
-  void VisitNewInstance(HNewInstance* new_instance) OVERRIDE {
-    // Any references appearing in the ref_info_array_ so far cannot alias with new_instance.
-    CreateReferenceInfoForReferenceType(new_instance);
-  }
-
-  void VisitNewArray(HNewArray* new_array) OVERRIDE {
-    // Any references appearing in the ref_info_array_ so far cannot alias with new_array.
-    CreateReferenceInfoForReferenceType(new_array);
-  }
-
-  void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* instruction) OVERRIDE {
-    CreateReferenceInfoForReferenceType(instruction);
-  }
-
-  void VisitInvokeVirtual(HInvokeVirtual* instruction) OVERRIDE {
-    CreateReferenceInfoForReferenceType(instruction);
-  }
-
-  void VisitInvokeInterface(HInvokeInterface* instruction) OVERRIDE {
-    CreateReferenceInfoForReferenceType(instruction);
-  }
-
-  void VisitInvokeUnresolved(HInvokeUnresolved* instruction) OVERRIDE {
-    CreateReferenceInfoForReferenceType(instruction);
-  }
-
-  void VisitInvokePolymorphic(HInvokePolymorphic* instruction) OVERRIDE {
-    CreateReferenceInfoForReferenceType(instruction);
-  }
-
-  void VisitLoadString(HLoadString* instruction) OVERRIDE {
-    CreateReferenceInfoForReferenceType(instruction);
-  }
-
-  void VisitPhi(HPhi* instruction) OVERRIDE {
-    CreateReferenceInfoForReferenceType(instruction);
-  }
-
-  void VisitParameterValue(HParameterValue* instruction) OVERRIDE {
-    CreateReferenceInfoForReferenceType(instruction);
-  }
-
-  void VisitSelect(HSelect* instruction) OVERRIDE {
+  void VisitInstruction(HInstruction* instruction) OVERRIDE {
+    // Any new-instance or new-array cannot alias with references that
+    // pre-exist the new-instance/new-array. We append entries into
+    // ref_info_array_ which keeps track of the order of creation
+    // of reference values since we visit the blocks in reverse post order.
+    //
+    // By default, VisitXXX() (including VisitPhi()) calls VisitInstruction(),
+    // unless VisitXXX() is overridden. VisitInstanceFieldGet() etc. above
+    // also call CreateReferenceInfoForReferenceType() explicitly.
     CreateReferenceInfoForReferenceType(instruction);
   }
 
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 027ba77..f8f4eb2 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;
@@ -334,7 +347,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 +402,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 +417,7 @@
     reductions_ = nullptr;
     vector_refs_ = nullptr;
     vector_map_ = nullptr;
+    vector_permanent_map_ = nullptr;
   }
 }
 
@@ -603,7 +621,6 @@
   // 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);
@@ -802,6 +819,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 +865,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 +885,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 +931,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 +1003,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 +1208,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 +1227,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 +1240,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 +1255,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 +1269,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 +1284,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 +1312,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 +1361,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 +1425,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 +1677,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;
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index 49be8a3..ba9126c 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -62,17 +62,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 +156,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 +257,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/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/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/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 46c5f58..04c2fcd 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -59,7 +59,7 @@
                          size_t class_frequency = 1) {
     size_t method_counter = 0;
     size_t class_counter = 0;
-    for (std::string dex : GetLibCoreDexFileNames()) {
+    for (const std::string& dex : GetLibCoreDexFileNames()) {
       std::vector<std::unique_ptr<const DexFile>> dex_files;
       std::string error_msg;
       CHECK(DexFile::Open(dex.c_str(), dex, /*verify_checksum*/ false, &error_msg, &dex_files))
@@ -158,9 +158,6 @@
       argv.push_back("--host");
     }
 
-    ScratchFile file;
-    const std::string image_prefix = file.GetFilename();
-
     argv.push_back("--image=" + image_file_name_prefix + ".art");
     argv.push_back("--oat-file=" + image_file_name_prefix + ".oat");
     argv.push_back("--oat-location=" + image_file_name_prefix + ".oat");
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index dbb9dfc..db70e78 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -89,13 +89,31 @@
     return status;
   }
 
-  void GenerateOdexForTest(const std::string& dex_location,
-                           const std::string& odex_location,
-                           CompilerFilter::Filter filter,
-                           const std::vector<std::string>& extra_args = {},
-                           bool expect_success = true,
-                           bool use_fd = false,
-                           std::function<void(const OatFile&)> check_oat = [](const OatFile&) {}) {
+  void GenerateOdexForTest(
+      const std::string& dex_location,
+      const std::string& odex_location,
+      CompilerFilter::Filter filter,
+      const std::vector<std::string>& extra_args = {},
+      bool expect_success = true,
+      bool use_fd = false) {
+    GenerateOdexForTest(dex_location,
+                        odex_location,
+                        filter,
+                        extra_args,
+                        expect_success,
+                        use_fd,
+                        [](const OatFile&) {});
+  }
+
+  template <typename T>
+  void GenerateOdexForTest(
+      const std::string& dex_location,
+      const std::string& odex_location,
+      CompilerFilter::Filter filter,
+      const std::vector<std::string>& extra_args,
+      bool expect_success,
+      bool use_fd,
+      T check_oat) {
     std::string error_msg;
     int status = GenerateOdexForTestWithStatus({dex_location},
                                                odex_location,
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index 1658967..336eb5f 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -380,7 +380,7 @@
   // Runs DexFileLayout test.
   bool DexFileLayoutExec(std::string* error_msg) {
     ScratchFile tmp_file;
-    std::string tmp_name = tmp_file.GetFilename();
+    const std::string& tmp_name = tmp_file.GetFilename();
     size_t tmp_last_slash = tmp_name.rfind('/');
     std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
 
@@ -415,7 +415,7 @@
   // for behavior consistency.
   bool DexFileLayoutFixedPointExec(std::string* error_msg) {
     ScratchFile tmp_file;
-    std::string tmp_name = tmp_file.GetFilename();
+    const std::string& tmp_name = tmp_file.GetFilename();
     size_t tmp_last_slash = tmp_name.rfind('/');
     std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
 
@@ -481,7 +481,7 @@
   // Runs UnreferencedCatchHandlerTest & Unreferenced0SizeCatchHandlerTest.
   bool UnreferencedCatchHandlerExec(std::string* error_msg, const char* filename) {
     ScratchFile tmp_file;
-    std::string tmp_name = tmp_file.GetFilename();
+    const std::string& tmp_name = tmp_file.GetFilename();
     size_t tmp_last_slash = tmp_name.rfind('/');
     std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
 
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index af77072..6c0d492 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -183,22 +183,27 @@
   }
 
   static jvmtiError GetOwnedMonitorInfo(jvmtiEnv* env,
-                                        jthread thread ATTRIBUTE_UNUSED,
-                                        jint* owned_monitor_count_ptr ATTRIBUTE_UNUSED,
-                                        jobject** owned_monitors_ptr ATTRIBUTE_UNUSED) {
+                                        jthread thread,
+                                        jint* owned_monitor_count_ptr,
+                                        jobject** owned_monitors_ptr) {
     ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_owned_monitor_info);
-    return ERR(NOT_IMPLEMENTED);
+    return StackUtil::GetOwnedMonitorInfo(env,
+                                          thread,
+                                          owned_monitor_count_ptr,
+                                          owned_monitors_ptr);
   }
 
-  static jvmtiError GetOwnedMonitorStackDepthInfo(
-      jvmtiEnv* env,
-      jthread thread ATTRIBUTE_UNUSED,
-      jint* monitor_info_count_ptr ATTRIBUTE_UNUSED,
-      jvmtiMonitorStackDepthInfo** monitor_info_ptr ATTRIBUTE_UNUSED) {
+  static jvmtiError GetOwnedMonitorStackDepthInfo(jvmtiEnv* env,
+                                                  jthread thread,
+                                                  jint* monitor_info_count_ptr,
+                                                  jvmtiMonitorStackDepthInfo** monitor_info_ptr) {
     ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_get_owned_monitor_stack_depth_info);
-    return ERR(NOT_IMPLEMENTED);
+    return StackUtil::GetOwnedMonitorStackDepthInfo(env,
+                                                    thread,
+                                                    monitor_info_count_ptr,
+                                                    monitor_info_ptr);
   }
 
   static jvmtiError GetCurrentContendedMonitor(jvmtiEnv* env,
@@ -313,12 +318,10 @@
     return StackUtil::GetFrameLocation(env, thread, depth, method_ptr, location_ptr);
   }
 
-  static jvmtiError NotifyFramePop(jvmtiEnv* env,
-                                   jthread thread ATTRIBUTE_UNUSED,
-                                   jint depth ATTRIBUTE_UNUSED) {
+  static jvmtiError NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth) {
     ENSURE_VALID_ENV(env);
     ENSURE_HAS_CAP(env, can_generate_frame_pop_events);
-    return ERR(NOT_IMPLEMENTED);
+    return StackUtil::NotifyFramePop(env, thread, depth);
   }
 
   static jvmtiError ForceEarlyReturnObject(jvmtiEnv* env,
diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
index 12f4cab..d74e25b 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -52,6 +52,7 @@
 namespace art {
 class ArtField;
 class ArtMethod;
+class ShadowFrame;
 }  // namespace art
 
 namespace openjdkjvmti {
@@ -81,6 +82,7 @@
 
   // Set of breakpoints is unique to each jvmtiEnv.
   std::unordered_set<Breakpoint> breakpoints;
+  std::unordered_set<const art::ShadowFrame*> notify_frames;
 
   ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler);
 
@@ -222,7 +224,7 @@
     .can_generate_field_access_events                = 1,
     .can_get_bytecodes                               = 1,
     .can_get_synthetic_attribute                     = 1,
-    .can_get_owned_monitor_info                      = 0,
+    .can_get_owned_monitor_info                      = 1,
     .can_get_current_contended_monitor               = 0,
     .can_get_monitor_info                            = 0,
     .can_pop_frame                                   = 0,
@@ -235,7 +237,7 @@
     .can_maintain_original_method_order              = 1,
     .can_generate_single_step_events                 = 1,
     .can_generate_exception_events                   = 0,
-    .can_generate_frame_pop_events                   = 0,
+    .can_generate_frame_pop_events                   = 1,
     .can_generate_breakpoint_events                  = 1,
     .can_suspend                                     = 1,
     .can_redefine_any_class                          = 0,
@@ -251,7 +253,7 @@
     .can_generate_garbage_collection_events          = 1,
     .can_generate_object_free_events                 = 1,
     .can_force_early_return                          = 0,
-    .can_get_owned_monitor_stack_depth_info          = 0,
+    .can_get_owned_monitor_stack_depth_info          = 1,
     .can_get_constant_pool                           = 0,
     .can_set_native_method_prefix                    = 0,
     .can_retransform_classes                         = 1,
diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h
index 32dba3e..31edc73 100644
--- a/openjdkjvmti/events-inl.h
+++ b/openjdkjvmti/events-inl.h
@@ -244,6 +244,35 @@
   }
 }
 
+// Need to give custom specializations for FramePop since it needs to filter out which particular
+// agents get the event. This specialization gets an extra argument so we can determine which (if
+// any) environments have the frame pop.
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kFramePop>(
+    art::Thread* thread,
+    JNIEnv* jnienv,
+    jthread jni_thread,
+    jmethodID jmethod,
+    jboolean is_exception,
+    const art::ShadowFrame* frame) const {
+  for (ArtJvmTiEnv* env : envs) {
+    // Search for the frame. Do this before checking if we need to send the event so that we don't
+    // have to deal with use-after-free or the frames being reallocated later.
+    if (env != nullptr && env->notify_frames.erase(frame) != 0) {
+      if (ShouldDispatch<ArtJvmtiEvent::kFramePop>(env, thread)) {
+        // We temporarily clear any pending exceptions so the event can call back into java code.
+        ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
+        jnienv->ExceptionClear();
+        auto callback = impl::GetCallback<ArtJvmtiEvent::kFramePop>(env);
+        (*callback)(env, jnienv, jni_thread, jmethod, is_exception);
+        if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
+          jnienv->Throw(thr.get());
+        }
+      }
+    }
+  }
+}
+
 // Need to give custom specializations for FieldAccess and FieldModification since they need to
 // filter out which particular fields agents want to get notified on.
 // TODO The spec allows us to do shortcuts like only allow one agent to ever set these watches. This
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index 73e6881..acef682 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -538,6 +538,20 @@
     }
   }
 
+  void WatchedFramePop(art::Thread* self, const art::ShadowFrame& frame)
+      REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE {
+    if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kFramePop)) {
+      art::JNIEnvExt* jnienv = self->GetJniEnv();
+      jboolean is_exception_pending = self->IsExceptionPending();
+      RunEventCallback<ArtJvmtiEvent::kFramePop>(
+          self,
+          jnienv,
+          art::jni::EncodeArtMethod(frame.GetMethod()),
+          is_exception_pending,
+          &frame);
+    }
+  }
+
   // Call-back when an exception is thrown.
   void ExceptionThrown(art::Thread* self ATTRIBUTE_UNUSED,
                        art::Handle<art::mirror::Throwable> exception_object ATTRIBUTE_UNUSED)
@@ -582,6 +596,8 @@
     case ArtJvmtiEvent::kBreakpoint:
     case ArtJvmtiEvent::kSingleStep:
       return art::instrumentation::Instrumentation::kDexPcMoved;
+    case ArtJvmtiEvent::kFramePop:
+      return art::instrumentation::Instrumentation::kWatchedFramePop;
     default:
       LOG(FATAL) << "Unknown event ";
       return 0;
@@ -648,6 +664,15 @@
       }
       return;
     }
+    // FramePop can never be disabled once it's been turned on since we would either need to deal
+    // with dangling pointers or have missed events.
+    case ArtJvmtiEvent::kFramePop:
+      if (!enable || (enable && frame_pop_enabled)) {
+        break;
+      } else {
+        SetupTraceListener(method_trace_listener_.get(), event, enable);
+        break;
+      }
     case ArtJvmtiEvent::kMethodEntry:
     case ArtJvmtiEvent::kMethodExit:
     case ArtJvmtiEvent::kFieldAccess:
diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h
index 3d05fa1..b49e80c 100644
--- a/openjdkjvmti/events.h
+++ b/openjdkjvmti/events.h
@@ -223,6 +223,11 @@
   std::unique_ptr<JvmtiAllocationListener> alloc_listener_;
   std::unique_ptr<JvmtiGcPauseListener> gc_pause_listener_;
   std::unique_ptr<JvmtiMethodTraceListener> method_trace_listener_;
+
+  // True if frame pop has ever been enabled. Since we store pointers to stack frames we need to
+  // continue to listen to this event even if it has been disabled.
+  // TODO We could remove the listeners once all jvmtiEnvs have drained their shadow-frame vectors.
+  bool frame_pop_enabled;
 };
 
 }  // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index 35f2f0c..f99b167 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -51,6 +51,7 @@
 #include "stack.h"
 #include "thread-current-inl.h"
 #include "thread_list.h"
+#include "ti_stack.h"
 #include "ti_thread.h"
 #include "ti_phase.h"
 
@@ -533,39 +534,6 @@
   return IsMethodT(env, m, test, is_synthetic_ptr);
 }
 
-struct FindFrameAtDepthVisitor : art::StackVisitor {
- public:
-  FindFrameAtDepthVisitor(art::Thread* target, art::Context* ctx, jint depth)
-      REQUIRES_SHARED(art::Locks::mutator_lock_)
-      : art::StackVisitor(target, ctx, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames),
-        found_frame_(false),
-        cnt_(0),
-        depth_(static_cast<size_t>(depth)) { }
-
-  bool FoundFrame() {
-    return found_frame_;
-  }
-
-  bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
-    if (GetMethod()->IsRuntimeMethod()) {
-      return true;
-    }
-    if (cnt_ == depth_) {
-      // We found our frame, exit.
-      found_frame_ = true;
-      return false;
-    } else {
-      cnt_++;
-      return true;
-    }
-  }
-
- private:
-  bool found_frame_;
-  size_t cnt_;
-  size_t depth_;
-};
-
 class CommonLocalVariableClosure : public art::Closure {
  public:
   CommonLocalVariableClosure(art::Thread* caller,
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index c679d73..1b4e910 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -1364,7 +1364,6 @@
 }
 
 void Redefiner::ClassRedefinition::UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
-                                                 art::ObjPtr<art::mirror::DexCache> new_dex_cache,
                                                  const art::DexFile::ClassDef& class_def) {
   art::ClassLinker* linker = driver_->runtime_->GetClassLinker();
   art::PointerSize image_pointer_size = linker->GetImagePointerSize();
@@ -1396,7 +1395,6 @@
     method.SetDexMethodIndex(dex_method_idx);
     linker->SetEntryPointsToInterpreter(&method);
     method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(class_def, dex_method_idx));
-    method.SetDexCacheResolvedMethods(new_dex_cache->GetResolvedMethods(), image_pointer_size);
     // Clear all the intrinsics related flags.
     method.ClearAccessFlags(art::kAccIntrinsic | (~art::kAccFlagsNotUsedByIntrinsic));
     // Notify the jit that this method is redefined.
@@ -1433,7 +1431,7 @@
     art::ObjPtr<art::mirror::Object> original_dex_file) {
   DCHECK_EQ(dex_file_->NumClassDefs(), 1u);
   const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(0);
-  UpdateMethods(mclass, new_dex_cache, class_def);
+  UpdateMethods(mclass, class_def);
   UpdateFields(mclass);
 
   // Update the class fields.
diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h
index 984f922..528563b 100644
--- a/openjdkjvmti/ti_redefine.h
+++ b/openjdkjvmti/ti_redefine.h
@@ -184,7 +184,6 @@
         REQUIRES(art::Locks::mutator_lock_);
 
     void UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
-                       art::ObjPtr<art::mirror::DexCache> new_dex_cache,
                        const art::DexFile::ClassDef& class_def)
         REQUIRES(art::Locks::mutator_lock_);
 
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
index ff2de8d..e165187 100644
--- a/openjdkjvmti/ti_stack.cc
+++ b/openjdkjvmti/ti_stack.cc
@@ -37,6 +37,7 @@
 #include <vector>
 
 #include "art_field-inl.h"
+#include "art_method-inl.h"
 #include "art_jvmti.h"
 #include "art_method-inl.h"
 #include "barrier.h"
@@ -45,6 +46,7 @@
 #include "base/mutex.h"
 #include "dex_file.h"
 #include "dex_file_annotations.h"
+#include "gc_root.h"
 #include "handle_scope-inl.h"
 #include "jni_env_ext.h"
 #include "jni_internal.h"
@@ -53,9 +55,11 @@
 #include "nativehelper/ScopedLocalRef.h"
 #include "scoped_thread_state_change-inl.h"
 #include "stack.h"
+#include "ti_thread.h"
 #include "thread-current-inl.h"
 #include "thread_list.h"
 #include "thread_pool.h"
+#include "ti_thread.h"
 #include "well_known_classes.h"
 
 namespace openjdkjvmti {
@@ -824,4 +828,239 @@
   return ERR(NONE);
 }
 
+struct MonitorVisitor : public art::StackVisitor, public art::SingleRootVisitor {
+  // We need a context because VisitLocks needs it retrieve the monitor objects.
+  explicit MonitorVisitor(art::Thread* thread)
+      REQUIRES_SHARED(art::Locks::mutator_lock_)
+      : art::StackVisitor(thread,
+                          art::Context::Create(),
+                          art::StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+        hs(art::Thread::Current()),
+        current_stack_depth(0) {}
+
+  ~MonitorVisitor() {
+    delete context_;
+  }
+
+  bool VisitFrame() OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
+    if (!GetMethod()->IsRuntimeMethod()) {
+      art::Monitor::VisitLocks(this, AppendOwnedMonitors, this);
+      ++current_stack_depth;
+    }
+    return true;
+  }
+
+  static void AppendOwnedMonitors(art::mirror::Object* owned_monitor, void* arg)
+      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
+    MonitorVisitor* visitor = reinterpret_cast<MonitorVisitor*>(arg);
+    art::ObjPtr<art::mirror::Object> mon(owned_monitor);
+    // Filter out duplicates.
+    for (const art::Handle<art::mirror::Object>& monitor : visitor->monitors) {
+      if (monitor.Get() == mon.Ptr()) {
+        return;
+      }
+    }
+    visitor->monitors.push_back(visitor->hs.NewHandle(mon));
+    visitor->stack_depths.push_back(visitor->current_stack_depth);
+  }
+
+  void VisitRoot(art::mirror::Object* obj, const art::RootInfo& info ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    for (const art::Handle<art::mirror::Object>& m : monitors) {
+      if (m.Get() == obj) {
+        return;
+      }
+    }
+    monitors.push_back(hs.NewHandle(obj));
+    stack_depths.push_back(-1);
+  }
+
+  art::VariableSizedHandleScope hs;
+  jint current_stack_depth;
+  std::vector<art::Handle<art::mirror::Object>> monitors;
+  std::vector<jint> stack_depths;
+};
+
+template<typename Fn>
+struct MonitorInfoClosure : public art::Closure {
+ public:
+  MonitorInfoClosure(art::ScopedObjectAccess& soa, Fn handle_results)
+      : soa_(soa), err_(OK), handle_results_(handle_results) {}
+
+  void Run(art::Thread* target) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
+    // Find the monitors on the stack.
+    MonitorVisitor visitor(target);
+    visitor.WalkStack(/* include_transitions */ false);
+    // Find any other monitors, including ones acquired in native code.
+    art::RootInfo root_info(art::kRootVMInternal);
+    target->GetJniEnv()->monitors.VisitRoots(&visitor, root_info);
+    err_ = handle_results_(soa_, visitor);
+  }
+
+  jvmtiError GetError() {
+    return err_;
+  }
+
+ private:
+  art::ScopedObjectAccess& soa_;
+  jvmtiError err_;
+  Fn handle_results_;
+};
+
+
+template <typename Fn>
+static jvmtiError GetOwnedMonitorInfoCommon(jthread thread, Fn handle_results) {
+  art::Thread* self = art::Thread::Current();
+  art::ScopedObjectAccess soa(self);
+  MonitorInfoClosure<Fn> closure(soa, handle_results);
+  bool called_method = false;
+  {
+    art::MutexLock mu(self, *art::Locks::thread_list_lock_);
+    art::Thread* target = ThreadUtil::GetNativeThread(thread, soa);
+    if (target == nullptr && thread == nullptr) {
+      return ERR(INVALID_THREAD);
+    }
+    if (target == nullptr) {
+      return ERR(THREAD_NOT_ALIVE);
+    }
+    if (target != self) {
+      called_method = true;
+      if (!target->RequestSynchronousCheckpoint(&closure)) {
+        return ERR(THREAD_NOT_ALIVE);
+      }
+    }
+  }
+  // Cannot call the closure on the current thread if we have thread_list_lock since we need to call
+  // into the verifier which can cause the current thread to suspend for gc. Suspending would be a
+  // bad thing to do if we hold the ThreadListLock. For other threads since we are running it on a
+  // checkpoint we are fine but if the thread is the current one we need to drop the mutex first.
+  if (!called_method) {
+    closure.Run(self);
+  }
+  return closure.GetError();
+}
+
+jvmtiError StackUtil::GetOwnedMonitorStackDepthInfo(jvmtiEnv* env,
+                                                    jthread thread,
+                                                    jint* info_cnt,
+                                                    jvmtiMonitorStackDepthInfo** info_ptr) {
+  if (info_cnt == nullptr || info_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+  auto handle_fun = [&] (art::ScopedObjectAccess& soa, MonitorVisitor& visitor)
+      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    auto nbytes = sizeof(jvmtiMonitorStackDepthInfo) * visitor.monitors.size();
+    jvmtiError err = env->Allocate(nbytes, reinterpret_cast<unsigned char**>(info_ptr));
+    if (err != OK) {
+      return err;
+    }
+    *info_cnt = visitor.monitors.size();
+    for (size_t i = 0; i < visitor.monitors.size(); i++) {
+      (*info_ptr)[i] = {
+        soa.Env()->AddLocalReference<jobject>(visitor.monitors[i].Get()),
+        visitor.stack_depths[i]
+      };
+    }
+    return OK;
+  };
+  return GetOwnedMonitorInfoCommon(thread, handle_fun);
+}
+
+jvmtiError StackUtil::GetOwnedMonitorInfo(jvmtiEnv* env,
+                                          jthread thread,
+                                          jint* owned_monitor_count_ptr,
+                                          jobject** owned_monitors_ptr) {
+  if (owned_monitors_ptr == nullptr || owned_monitors_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+  auto handle_fun = [&] (art::ScopedObjectAccess& soa, MonitorVisitor& visitor)
+      REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    auto nbytes = sizeof(jobject) * visitor.monitors.size();
+    jvmtiError err = env->Allocate(nbytes, reinterpret_cast<unsigned char**>(owned_monitors_ptr));
+    if (err != OK) {
+      return err;
+    }
+    *owned_monitor_count_ptr = visitor.monitors.size();
+    for (size_t i = 0; i < visitor.monitors.size(); i++) {
+      (*owned_monitors_ptr)[i] =
+          soa.Env()->AddLocalReference<jobject>(visitor.monitors[i].Get());
+    }
+    return OK;
+  };
+  return GetOwnedMonitorInfoCommon(thread, handle_fun);
+}
+
+jvmtiError StackUtil::NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth) {
+  if (depth < 0) {
+    return ERR(ILLEGAL_ARGUMENT);
+  }
+  ArtJvmTiEnv* tienv = ArtJvmTiEnv::AsArtJvmTiEnv(env);
+  art::Thread* self = art::Thread::Current();
+  art::Thread* target;
+  do {
+    ThreadUtil::SuspendCheck(self);
+    art::MutexLock ucsl_mu(self, *art::Locks::user_code_suspension_lock_);
+    // Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_ by a
+    // user-code suspension. We retry and do another SuspendCheck to clear this.
+    if (ThreadUtil::WouldSuspendForUserCodeLocked(self)) {
+      continue;
+    }
+    // From now on we know we cannot get suspended by user-code.
+    // NB This does a SuspendCheck (during thread state change) so we need to make sure we don't
+    // have the 'suspend_lock' locked here.
+    art::ScopedObjectAccess soa(self);
+    art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_);
+    target = ThreadUtil::GetNativeThread(thread, soa);
+    if (target == nullptr) {
+      return ERR(THREAD_NOT_ALIVE);
+    } else if (target != self) {
+      // TODO This is part of the spec but we could easily avoid needing to do it. We would just put
+      // all the logic into a sync-checkpoint.
+      art::MutexLock tscl_mu(self, *art::Locks::thread_suspend_count_lock_);
+      if (target->GetUserCodeSuspendCount() == 0) {
+        return ERR(THREAD_NOT_SUSPENDED);
+      }
+    }
+    // We hold the user_code_suspension_lock_ so the target thread is staying suspended until we are
+    // done (unless it's 'self' in which case we don't care since we aren't going to be returning).
+    // TODO We could implement this using a synchronous checkpoint and not bother with any of the
+    // suspension stuff. The spec does specifically say to return THREAD_NOT_SUSPENDED though.
+    // Find the requested stack frame.
+    std::unique_ptr<art::Context> context(art::Context::Create());
+    FindFrameAtDepthVisitor visitor(target, context.get(), depth);
+    visitor.WalkStack();
+    if (!visitor.FoundFrame()) {
+      return ERR(NO_MORE_FRAMES);
+    }
+    art::ArtMethod* method = visitor.GetMethod();
+    if (method->IsNative()) {
+      return ERR(OPAQUE_FRAME);
+    }
+    // From here we are sure to succeed.
+    bool needs_instrument = false;
+    // Get/create a shadow frame
+    art::ShadowFrame* shadow_frame = visitor.GetCurrentShadowFrame();
+    if (shadow_frame == nullptr) {
+      needs_instrument = true;
+      const size_t frame_id = visitor.GetFrameId();
+      const uint16_t num_regs = method->GetCodeItem()->registers_size_;
+      shadow_frame = target->FindOrCreateDebuggerShadowFrame(frame_id,
+                                                             num_regs,
+                                                             method,
+                                                             visitor.GetDexPc());
+    }
+    // Mark shadow frame as needs_notify_pop_
+    shadow_frame->SetNotifyPop(true);
+    tienv->notify_frames.insert(shadow_frame);
+    // Make sure can we will go to the interpreter and use the shadow frames.
+    if (needs_instrument) {
+      art::Runtime::Current()->GetInstrumentation()->InstrumentThreadStack(target);
+    }
+    return OK;
+  } while (true);
+}
+
 }  // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_stack.h b/openjdkjvmti/ti_stack.h
index 2e96b82..b41fa4b 100644
--- a/openjdkjvmti/ti_stack.h
+++ b/openjdkjvmti/ti_stack.h
@@ -35,7 +35,9 @@
 #include "jni.h"
 #include "jvmti.h"
 
+#include "art_method.h"
 #include "base/mutex.h"
+#include "stack.h"
 
 namespace openjdkjvmti {
 
@@ -67,6 +69,51 @@
                                              const jthread* thread_list,
                                              jint max_frame_count,
                                              jvmtiStackInfo** stack_info_ptr);
+
+  static jvmtiError GetOwnedMonitorStackDepthInfo(jvmtiEnv* env,
+                                                  jthread thread,
+                                                  jint* info_cnt_ptr,
+                                                  jvmtiMonitorStackDepthInfo** info_ptr);
+
+  static jvmtiError GetOwnedMonitorInfo(jvmtiEnv* env,
+                                        jthread thread,
+                                        jint* owned_monitor_count_ptr,
+                                        jobject** owned_monitors_ptr);
+
+  static jvmtiError NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth);
+};
+
+struct FindFrameAtDepthVisitor : art::StackVisitor {
+ public:
+  FindFrameAtDepthVisitor(art::Thread* target, art::Context* ctx, jint depth)
+      REQUIRES_SHARED(art::Locks::mutator_lock_)
+      : art::StackVisitor(target, ctx, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames),
+        found_frame_(false),
+        cnt_(0),
+        depth_(static_cast<size_t>(depth)) { }
+
+  bool FoundFrame() {
+    return found_frame_;
+  }
+
+  bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
+    if (GetMethod()->IsRuntimeMethod()) {
+      return true;
+    }
+    if (cnt_ == depth_) {
+      // We found our frame, exit.
+      found_frame_ = true;
+      return false;
+    } else {
+      cnt_++;
+      return true;
+    }
+  }
+
+ private:
+  bool found_frame_;
+  size_t cnt_;
+  size_t depth_;
 };
 
 }  // namespace openjdkjvmti
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index ed7623a..efa2969 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -686,8 +686,6 @@
   // Just update the entry points if it looks like we should.
   // TODO: sanity check all the pointers' values
   copy->SetDeclaringClass(RelocatedAddressOfPointer(object->GetDeclaringClass()));
-  copy->SetDexCacheResolvedMethods(
-      RelocatedAddressOfPointer(object->GetDexCacheResolvedMethods(pointer_size)), pointer_size);
   copy->SetEntryPointFromQuickCompiledCodePtrSize(RelocatedAddressOfPointer(
       object->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size)), pointer_size);
   // No special handling for IMT conflict table since all pointers are moved by the same offset.
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index c78d34e..8cbf8c3 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -207,7 +207,7 @@
     return ExecAndReturnCode(argv_str, &error);
   }
 
-  bool CreateProfile(std::string profile_file_contents,
+  bool CreateProfile(const std::string& profile_file_contents,
                      const std::string& filename,
                      const std::string& dex_location) {
     ScratchFile class_names_file;
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index cf2bfee..69c615d 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1596,10 +1596,21 @@
     .cfi_rel_offset r1, 0
     .cfi_rel_offset r2, 4
     ldr     r4, [sp, #(2 * 4)]  // Load referrer.
-    ubfx    r1, r12, #0, #METHOD_DEX_CACHE_HASH_BITS  // Calculate DexCache method slot index.
-    ldr     r4, [r4, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_32]   // Load dex cache methods array
-    add     r4, r4, r1, lsl #(POINTER_SIZE_SHIFT + 1)  // Load DexCache method slot address.
     ldr     r2, [r0, #ART_METHOD_JNI_OFFSET_32]  // Load ImtConflictTable
+    // Load the declaring class (without read barrier) and access flags (for obsolete method check).
+    // The obsolete flag is set with suspended threads, so we do not need an acquire operation here.
+#if ART_METHOD_ACCESS_FLAGS_OFFSET != ART_METHOD_DECLARING_CLASS_OFFSET + 4
+#error "Expecting declaring class and access flags to be consecutive for LDRD."
+#endif
+    ldrd    r0, r1, [r4, #ART_METHOD_DECLARING_CLASS_OFFSET]
+    // If the method is obsolete, just go through the dex cache miss slow path.
+    lsrs    r1, #(ACC_OBSOLETE_METHOD_SHIFT + 1)
+    bcs     .Limt_conflict_trampoline_dex_cache_miss
+    ldr     r4, [r0, #MIRROR_CLASS_DEX_CACHE_OFFSET]  // Load the DexCache (without read barrier).
+    UNPOISON_HEAP_REF r4
+    ubfx    r1, r12, #0, #METHOD_DEX_CACHE_HASH_BITS  // Calculate DexCache method slot index.
+    ldr     r4, [r4, #MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET]  // Load the resolved methods.
+    add     r4, r4, r1, lsl #(POINTER_SIZE_SHIFT + 1)  // Load DexCache method slot address.
 
 // FIXME: Configure the build to use the faster code when appropriate.
 //        Currently we fall back to the slower version.
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 3d8ca40..802cf5e 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -2060,8 +2060,18 @@
     .extern artLookupResolvedMethod
 ENTRY art_quick_imt_conflict_trampoline
     ldr xIP0, [sp, #0]  // Load referrer
+    // Load the declaring class (without read barrier) and access flags (for obsolete method check).
+    // The obsolete flag is set with suspended threads, so we do not need an acquire operation here.
+#if ART_METHOD_ACCESS_FLAGS_OFFSET != ART_METHOD_DECLARING_CLASS_OFFSET + 4
+#error "Expecting declaring class and access flags to be consecutive for LDP."
+#endif
+    ldp wIP0, w15, [xIP0, #ART_METHOD_DECLARING_CLASS_OFFSET]
+    // If the method is obsolete, just go through the dex cache miss slow path.
+    tbnz x15, #ACC_OBSOLETE_METHOD_SHIFT, .Limt_conflict_trampoline_dex_cache_miss
+    ldr wIP0, [xIP0, #MIRROR_CLASS_DEX_CACHE_OFFSET]  // Load the DexCache (without read barrier).
+    UNPOISON_HEAP_REF wIP0
     ubfx x15, xIP1, #0, #METHOD_DEX_CACHE_HASH_BITS  // Calculate DexCache method slot index.
-    ldr xIP0, [xIP0, #ART_METHOD_DEX_CACHE_METHODS_OFFSET_64]   // Load dex cache methods array
+    ldr xIP0, [xIP0, #MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET]  // Load the resolved methods.
     add xIP0, xIP0, x15, lsl #(POINTER_SIZE_SHIFT + 1)  // Load DexCache method slot address.
 
     // Relaxed atomic load x14:x15 from the dex cache slot.
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index bb82d58..b876353 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -2112,10 +2112,18 @@
     SETUP_SAVE_REFS_AND_ARGS_FRAME_REGISTERS_ONLY /* save_s4_thru_s8 */ 0
 
     lw      $t8, FRAME_SIZE_SAVE_REFS_AND_ARGS($sp)  # $t8 = referrer.
+    // If the method is obsolete, just go through the dex cache miss slow path.
+    // The obsolete flag is set with suspended threads, so we do not need an acquire operation here.
+    lw      $t9, ART_METHOD_ACCESS_FLAGS_OFFSET($t8)  # $t9 = access flags.
+    sll     $t9, $t9, 31 - ACC_OBSOLETE_METHOD_SHIFT  # Move obsolete method bit to sign bit.
+    bltz    $t9, .Limt_conflict_trampoline_dex_cache_miss
+    lw      $t8, ART_METHOD_DECLARING_CLASS_OFFSET($t8)  # $t8 = declaring class (no read barrier).
+    lw      $t8, MIRROR_CLASS_DEX_CACHE_OFFSET($t8)  # $t8 = dex cache (without read barrier).
+    UNPOISON_HEAP_REF $t8
     la      $t9, __atomic_load_8
     addiu   $sp, $sp, -ARG_SLOT_SIZE                # Reserve argument slots on the stack.
     .cfi_adjust_cfa_offset ARG_SLOT_SIZE
-    lw      $t8, ART_METHOD_DEX_CACHE_METHODS_OFFSET_32($t8)  # $t8 = dex cache methods array.
+    lw      $t8, MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET($t8)  # $t8 = dex cache methods array.
 
     move    $s2, $t7                                # $s2 = method index (callee-saved).
     lw      $s3, ART_METHOD_JNI_OFFSET_32($a0)      # $s3 = ImtConflictTable (callee-saved).
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 7350c85..eeaae3c 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -2024,8 +2024,16 @@
     SETUP_SAVE_REFS_AND_ARGS_FRAME_INTERNAL /* save_s4_thru_s8 */ 0
 
     ld      $t1, FRAME_SIZE_SAVE_REFS_AND_ARGS($sp)  # $t1 = referrer.
+    // If the method is obsolete, just go through the dex cache miss slow path.
+    // The obsolete flag is set with suspended threads, so we do not need an acquire operation here.
+    lw      $t9, ART_METHOD_ACCESS_FLAGS_OFFSET($t1)  # $t9 = access flags.
+    sll     $t9, $t9, 31 - ACC_OBSOLETE_METHOD_SHIFT  # Move obsolete method bit to sign bit.
+    bltzc   $t9, .Limt_conflict_trampoline_dex_cache_miss
+    lwu     $t1, ART_METHOD_DECLARING_CLASS_OFFSET($t1)  # $t1 = declaring class (no read barrier).
+    lwu     $t1, MIRROR_CLASS_DEX_CACHE_OFFSET($t1)  # $t1 = dex cache (without read barrier).
+    UNPOISON_HEAP_REF $t1
     dla     $t9, __atomic_load_16
-    ld      $t1, ART_METHOD_DEX_CACHE_METHODS_OFFSET_64($t1)  # $t1 = dex cache methods array.
+    ld      $t1, MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET($t1)  # $t1 = dex cache methods array.
 
     dext    $s2, $t0, 0, 32                         # $s2 = zero-extended method index
                                                     # (callee-saved).
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 4e5e93a..f08c7fe 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1787,7 +1787,14 @@
     PUSH ESI
     PUSH EDX
     movl 16(%esp), %edi         // Load referrer.
-    movl ART_METHOD_DEX_CACHE_METHODS_OFFSET_32(%edi), %edi   // Load dex cache methods array.
+    // If the method is obsolete, just go through the dex cache miss slow path.
+    // The obsolete flag is set with suspended threads, so we do not need an acquire operation here.
+    testl LITERAL(ACC_OBSOLETE_METHOD), ART_METHOD_ACCESS_FLAGS_OFFSET(%edi)
+    jnz .Limt_conflict_trampoline_dex_cache_miss
+    movl ART_METHOD_DECLARING_CLASS_OFFSET(%edi), %edi // Load declaring class (no read barrier).
+    movl MIRROR_CLASS_DEX_CACHE_OFFSET(%edi), %edi     // Load the DexCache (without read barrier).
+    UNPOISON_HEAP_REF edi
+    movl MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET(%edi), %edi  // Load the resolved methods.
     pushl ART_METHOD_JNI_OFFSET_32(%eax)  // Push ImtConflictTable.
     CFI_ADJUST_CFA_OFFSET(4)
     movd %xmm7, %eax            // Get target method index stored in xmm7.
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 73e8610..b70abaa 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1646,7 +1646,14 @@
     int3
 #else
     movq __SIZEOF_POINTER__(%rsp), %r10 // Load referrer.
-    movq ART_METHOD_DEX_CACHE_METHODS_OFFSET_64(%r10), %r10   // Load dex cache methods array.
+    // If the method is obsolete, just go through the dex cache miss slow path.
+    // The obsolete flag is set with suspended threads, so we do not need an acquire operation here.
+    testl LITERAL(ACC_OBSOLETE_METHOD), ART_METHOD_ACCESS_FLAGS_OFFSET(%r10)
+    jnz .Limt_conflict_trampoline_dex_cache_miss
+    movl ART_METHOD_DECLARING_CLASS_OFFSET(%r10), %r10d  // Load declaring class (no read barrier).
+    movl MIRROR_CLASS_DEX_CACHE_OFFSET(%r10), %r10d    // Load the DexCache (without read barrier).
+    UNPOISON_HEAP_REF r10d
+    movq MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET(%r10), %r10  // Load the resolved methods.
     mov %eax, %r11d  // Remember method index in R11.
     andl LITERAL(METHOD_DEX_CACHE_SIZE_MINUS_ONE), %eax  // Calculate DexCache method slot index.
     shll LITERAL(1), %eax       // Multiply by 2 as entries have size 2 * __SIZEOF_POINTER__.
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 11f8253..1588920 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -102,58 +102,6 @@
   return GetDexMethodIndexUnchecked();
 }
 
-inline mirror::MethodDexCacheType* ArtMethod::GetDexCacheResolvedMethods(PointerSize pointer_size) {
-  return GetNativePointer<mirror::MethodDexCacheType*>(DexCacheResolvedMethodsOffset(pointer_size),
-                                                       pointer_size);
-}
-
-inline ArtMethod* ArtMethod::GetDexCacheResolvedMethod(uint16_t method_index,
-                                                       PointerSize pointer_size) {
-  // NOTE: Unchecked, i.e. not throwing AIOOB. We don't even know the length here
-  // without accessing the DexCache and we don't want to do that in release build.
-  DCHECK_LT(method_index, GetInterfaceMethodIfProxy(pointer_size)->GetDexFile()->NumMethodIds());
-  uint32_t slot_idx = method_index % mirror::DexCache::kDexCacheMethodCacheSize;
-  DCHECK_LT(slot_idx, GetInterfaceMethodIfProxy(pointer_size)->GetDexCache()->NumResolvedMethods());
-  mirror::MethodDexCachePair pair = mirror::DexCache::GetNativePairPtrSize(
-      GetDexCacheResolvedMethods(pointer_size), slot_idx, pointer_size);
-  ArtMethod* method = pair.GetObjectForIndex(method_index);
-  if (LIKELY(method != nullptr)) {
-    auto* declaring_class = method->GetDeclaringClass();
-    if (LIKELY(declaring_class == nullptr || !declaring_class->IsErroneous())) {
-      return method;
-    }
-  }
-  return nullptr;
-}
-
-inline void ArtMethod::SetDexCacheResolvedMethod(uint16_t method_index,
-                                                 ArtMethod* new_method,
-                                                 PointerSize pointer_size) {
-  // NOTE: Unchecked, i.e. not throwing AIOOB. We don't even know the length here
-  // without accessing the DexCache and we don't want to do that in release build.
-  DCHECK_LT(method_index, GetInterfaceMethodIfProxy(pointer_size)->GetDexFile()->NumMethodIds());
-  DCHECK(new_method == nullptr || new_method->GetDeclaringClass() != nullptr);
-  uint32_t slot_idx = method_index % mirror::DexCache::kDexCacheMethodCacheSize;
-  DCHECK_LT(slot_idx, GetInterfaceMethodIfProxy(pointer_size)->GetDexCache()->NumResolvedMethods());
-  mirror::MethodDexCachePair pair(new_method, method_index);
-  mirror::DexCache::SetNativePairPtrSize(
-      GetDexCacheResolvedMethods(pointer_size), slot_idx, pair, pointer_size);
-}
-
-inline bool ArtMethod::HasDexCacheResolvedMethods(PointerSize pointer_size) {
-  return GetDexCacheResolvedMethods(pointer_size) != nullptr;
-}
-
-inline bool ArtMethod::HasSameDexCacheResolvedMethods(ArtMethod* other, PointerSize pointer_size) {
-  return GetDexCacheResolvedMethods(pointer_size) ==
-      other->GetDexCacheResolvedMethods(pointer_size);
-}
-
-inline bool ArtMethod::HasSameDexCacheResolvedMethods(mirror::MethodDexCacheType* other_cache,
-                                                      PointerSize pointer_size) {
-  return GetDexCacheResolvedMethods(pointer_size) == other_cache;
-}
-
 inline ObjPtr<mirror::Class> ArtMethod::LookupResolvedClassFromTypeIndex(dex::TypeIndex type_idx) {
   ObjPtr<mirror::DexCache> dex_cache = GetDexCache();
   ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
@@ -403,13 +351,6 @@
   return interface_method;
 }
 
-inline void ArtMethod::SetDexCacheResolvedMethods(mirror::MethodDexCacheType* new_dex_cache_methods,
-                                                  PointerSize pointer_size) {
-  SetNativePointer(DexCacheResolvedMethodsOffset(pointer_size),
-                   new_dex_cache_methods,
-                   pointer_size);
-}
-
 inline dex::TypeIndex ArtMethod::GetReturnTypeIndex() {
   DCHECK(!IsProxyMethod());
   const DexFile* dex_file = GetDexFile();
@@ -489,18 +430,12 @@
 }
 
 template <typename Visitor>
-inline void ArtMethod::UpdateObjectsForImageRelocation(const Visitor& visitor,
-                                                       PointerSize pointer_size) {
+inline void ArtMethod::UpdateObjectsForImageRelocation(const Visitor& visitor) {
   mirror::Class* old_class = GetDeclaringClassUnchecked<kWithoutReadBarrier>();
   mirror::Class* new_class = visitor(old_class);
   if (old_class != new_class) {
     SetDeclaringClass(new_class);
   }
-  mirror::MethodDexCacheType* old_methods = GetDexCacheResolvedMethods(pointer_size);
-  mirror::MethodDexCacheType* new_methods = visitor(old_methods);
-  if (old_methods != new_methods) {
-    SetDexCacheResolvedMethods(new_methods, pointer_size);
-  }
 }
 
 template <ReadBarrierOption kReadBarrierOption, typename Visitor>
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 64988f2..2d67761 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -137,6 +137,10 @@
     } while (!access_flags_.compare_exchange_weak(old_access_flags, new_access_flags));
   }
 
+  static MemberOffset AccessFlagsOffset() {
+    return MemberOffset(OFFSETOF_MEMBER(ArtMethod, access_flags_));
+  }
+
   // Approximate what kind of method call would be used for this method.
   InvokeType GetInvokeType() REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -356,26 +360,6 @@
     dex_method_index_ = new_idx;
   }
 
-  ALWAYS_INLINE mirror::MethodDexCacheType* GetDexCacheResolvedMethods(PointerSize pointer_size)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-  ALWAYS_INLINE ArtMethod* GetDexCacheResolvedMethod(uint16_t method_index,
-                                                     PointerSize pointer_size)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  ALWAYS_INLINE void SetDexCacheResolvedMethod(uint16_t method_index,
-                                               ArtMethod* new_method,
-                                               PointerSize pointer_size)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-  ALWAYS_INLINE void SetDexCacheResolvedMethods(mirror::MethodDexCacheType* new_dex_cache_methods,
-                                                PointerSize pointer_size)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-  bool HasDexCacheResolvedMethods(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_);
-  bool HasSameDexCacheResolvedMethods(ArtMethod* other, PointerSize pointer_size)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-  bool HasSameDexCacheResolvedMethods(mirror::MethodDexCacheType* other_cache,
-                                      PointerSize pointer_size)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Lookup the Class* from the type index into this method's dex cache.
   ObjPtr<mirror::Class> LookupResolvedClassFromTypeIndex(dex::TypeIndex type_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -427,12 +411,6 @@
 
   void UnregisterNative() REQUIRES_SHARED(Locks::mutator_lock_);
 
-  static MemberOffset DexCacheResolvedMethodsOffset(PointerSize pointer_size) {
-    return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
-        PtrSizedFields, dex_cache_resolved_methods_) / sizeof(void*)
-            * static_cast<size_t>(pointer_size));
-  }
-
   static MemberOffset DataOffset(PointerSize pointer_size) {
     return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
         PtrSizedFields, data_) / sizeof(void*) * static_cast<size_t>(pointer_size));
@@ -686,8 +664,7 @@
   // Update heap objects and non-entrypoint pointers by the passed in visitor for image relocation.
   // Does not use read barrier.
   template <typename Visitor>
-  ALWAYS_INLINE void UpdateObjectsForImageRelocation(const Visitor& visitor,
-                                                     PointerSize pointer_size)
+  ALWAYS_INLINE void UpdateObjectsForImageRelocation(const Visitor& visitor)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Update entry points by passing them through the visitor.
@@ -728,9 +705,6 @@
 
   // Must be the last fields in the method.
   struct PtrSizedFields {
-    // Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
-    mirror::MethodDexCacheType* dex_cache_resolved_methods_;
-
     // Depending on the method type, the data is
     //   - native method: pointer to the JNI function registered to this method
     //                    or a function to resolve the JNI function,
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 44c0661..f4830e2 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -141,7 +141,7 @@
 #define SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET (SHADOWFRAME_NUMBER_OF_VREGS_OFFSET + 10)
 ADD_TEST_EQ(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET,
             static_cast<int32_t>(art::ShadowFrame::HotnessCountdownOffset()))
-#define SHADOWFRAME_VREGS_OFFSET (SHADOWFRAME_NUMBER_OF_VREGS_OFFSET + 12)
+#define SHADOWFRAME_VREGS_OFFSET (SHADOWFRAME_NUMBER_OF_VREGS_OFFSET + 16)
 ADD_TEST_EQ(SHADOWFRAME_VREGS_OFFSET,
             static_cast<int32_t>(art::ShadowFrame::VRegsOffset()))
 
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 439ecaf..d6f0030 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -186,7 +186,8 @@
   // lookup in the context of the original method from where it steals the code.
   // However, we delay the GetInterfaceMethodIfProxy() until needed.
   DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor());
-  ArtMethod* resolved_method = referrer->GetDexCacheResolvedMethod(method_idx, image_pointer_size_);
+  ArtMethod* resolved_method = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedMethod(
+      method_idx, image_pointer_size_);
   if (resolved_method == nullptr) {
     return nullptr;
   }
@@ -226,7 +227,8 @@
   // However, we delay the GetInterfaceMethodIfProxy() until needed.
   DCHECK(!referrer->IsProxyMethod() || referrer->IsConstructor());
   Thread::PoisonObjectPointersIfDebug();
-  ArtMethod* resolved_method = referrer->GetDexCacheResolvedMethod(method_idx, image_pointer_size_);
+  ArtMethod* resolved_method = referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedMethod(
+      method_idx, image_pointer_size_);
   DCHECK(resolved_method == nullptr || !resolved_method->IsRuntimeMethod());
   if (UNLIKELY(resolved_method == nullptr)) {
     referrer = referrer->GetInterfaceMethodIfProxy(image_pointer_size_);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 051c0c2..1beb783 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1115,35 +1115,6 @@
   return true;
 }
 
-class FixupArtMethodArrayVisitor : public ArtMethodVisitor {
- public:
-  explicit FixupArtMethodArrayVisitor(const ImageHeader& header) : header_(header) {}
-
-  virtual void Visit(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
-    const bool is_copied = method->IsCopied();
-    mirror::MethodDexCacheType* resolved_methods =
-        method->GetDexCacheResolvedMethods(kRuntimePointerSize);
-    if (resolved_methods != nullptr) {
-      bool in_image_space = false;
-      if (kIsDebugBuild || is_copied) {
-        in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
-              reinterpret_cast<const uint8_t*>(resolved_methods) - header_.GetImageBegin());
-      }
-      // Must be in image space for non-miranda method.
-      DCHECK(is_copied || in_image_space)
-          << resolved_methods << " is not in image starting at "
-          << reinterpret_cast<void*>(header_.GetImageBegin());
-      if (!is_copied || in_image_space) {
-        method->SetDexCacheResolvedMethods(method->GetDexCache()->GetResolvedMethods(),
-                                           kRuntimePointerSize);
-      }
-    }
-  }
-
- private:
-  const ImageHeader& header_;
-};
-
 class VerifyDeclaringClassVisitor : public ArtMethodVisitor {
  public:
   VerifyDeclaringClassVisitor() REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_)
@@ -1492,12 +1463,6 @@
     FixupInternVisitor fixup_intern_visitor;
     bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_intern_visitor);
   }
-  if (*out_forward_dex_cache_array) {
-    ScopedTrace timing("Fixup ArtMethod dex cache arrays");
-    FixupArtMethodArrayVisitor visitor(header);
-    header.VisitPackedArtMethods(&visitor, space->Begin(), kRuntimePointerSize);
-    Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
-  }
   if (kVerifyArtMethodDeclaringClasses) {
     ScopedTrace timing("Verify declaring classes");
     ReaderMutexLock rmu(self, *Locks::heap_bitmap_lock_);
@@ -3444,8 +3409,6 @@
   dst->SetDeclaringClass(klass.Get());
   dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());
 
-  dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods(), image_pointer_size_);
-
   uint32_t access_flags = it.GetMethodAccessFlags();
 
   if (UNLIKELY(strcmp("finalize", method_name) == 0)) {
@@ -4729,7 +4692,6 @@
 
   // The proxy method doesn't have its own dex cache or dex file and so it steals those of its
   // interface prototype. The exception to this are Constructors and the Class of the Proxy itself.
-  CHECK(prototype->HasSameDexCacheResolvedMethods(method, image_pointer_size_));
   auto* np = method->GetInterfaceMethodIfProxy(image_pointer_size_);
   CHECK_EQ(prototype->GetDeclaringClass()->GetDexCache(), np->GetDexCache());
   CHECK_EQ(prototype->GetDexMethodIndex(), method->GetDexMethodIndex());
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 5e9707c..f887b8e 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -245,11 +245,6 @@
     EXPECT_TRUE(method->GetDeclaringClass() != nullptr);
     EXPECT_TRUE(method->GetName() != nullptr);
     EXPECT_TRUE(method->GetSignature() != Signature::NoSignature());
-
-    EXPECT_TRUE(method->HasDexCacheResolvedMethods(kRuntimePointerSize));
-    EXPECT_TRUE(method->HasSameDexCacheResolvedMethods(
-        method->GetDeclaringClass()->GetDexCache()->GetResolvedMethods(),
-        kRuntimePointerSize));
   }
 
   void AssertField(ObjPtr<mirror::Class> klass, ArtField* field)
diff --git a/runtime/compiler_filter_test.cc b/runtime/compiler_filter_test.cc
index 383f4e3..9b4f845 100644
--- a/runtime/compiler_filter_test.cc
+++ b/runtime/compiler_filter_test.cc
@@ -20,7 +20,7 @@
 
 namespace art {
 
-static void TestCompilerFilterName(CompilerFilter::Filter filter, std::string name) {
+static void TestCompilerFilterName(CompilerFilter::Filter filter, const std::string& name) {
   CompilerFilter::Filter parsed;
   EXPECT_TRUE(CompilerFilter::ParseCompilerFilter(name.c_str(), &parsed));
   EXPECT_EQ(filter, parsed);
@@ -28,7 +28,7 @@
   EXPECT_EQ(name, CompilerFilter::NameOfFilter(filter));
 }
 
-static void TestSafeModeFilter(CompilerFilter::Filter expected, std::string name) {
+static void TestSafeModeFilter(CompilerFilter::Filter expected, const std::string& name) {
   CompilerFilter::Filter parsed;
   EXPECT_TRUE(CompilerFilter::ParseCompilerFilter(name.c_str(), &parsed));
   EXPECT_EQ(expected, CompilerFilter::GetSafeModeFilterFrom(parsed));
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index be3e4f8..8253739 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -83,7 +83,7 @@
   ObjPtr<mirror::DexCache> dex_cache = caller->GetDexCache();
   const DexFile* dex_file = dex_cache->GetDexFile();
   const DexFile::MethodId& method_id = dex_file->GetMethodId(method_index);
-  ArtMethod* inlined_method = caller->GetDexCacheResolvedMethod(method_index, kRuntimePointerSize);
+  ArtMethod* inlined_method = dex_cache->GetResolvedMethod(method_index, kRuntimePointerSize);
   if (inlined_method != nullptr) {
     DCHECK(!inlined_method->IsRuntimeMethod());
     return inlined_method;
@@ -106,7 +106,7 @@
                << dex_file->GetMethodSignature(method_id) << " declared. "
                << "This must be due to duplicate classes or playing wrongly with class loaders";
   }
-  caller->SetDexCacheResolvedMethod(method_index, inlined_method, kRuntimePointerSize);
+  dex_cache->SetResolvedMethod(method_index, inlined_method, kRuntimePointerSize);
 
   return inlined_method;
 }
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index e08319d..c6abd28 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1258,7 +1258,7 @@
       // FindVirtualMethodFor... This is ok for FindDexMethodIndexInOtherDexFile that only cares
       // about the name and signature.
       uint32_t update_dex_cache_method_index = called->GetDexMethodIndex();
-      if (!called->HasSameDexCacheResolvedMethods(caller, kRuntimePointerSize)) {
+      if (called->GetDexFile() != caller->GetDexFile()) {
         // Calling from one dex file to another, need to compute the method index appropriate to
         // the caller's dex file. Since we get here only if the original called was a runtime
         // method, we've got the correct dex_file and a dex_method_idx from above.
@@ -1270,12 +1270,16 @@
             called->FindDexMethodIndexInOtherDexFile(*caller_dex_file,
                                                      caller_method_name_and_sig_index);
       }
-      if ((update_dex_cache_method_index != DexFile::kDexNoIndex) &&
-          (caller->GetDexCacheResolvedMethod(
-              update_dex_cache_method_index, kRuntimePointerSize) != called)) {
-        caller->SetDexCacheResolvedMethod(update_dex_cache_method_index,
-                                          called,
-                                          kRuntimePointerSize);
+      if (update_dex_cache_method_index != DexFile::kDexNoIndex) {
+        // Note: We do not need the read barrier for the dex cache as the SetResolvedMethod()
+        // operates on native (non-moveable) data and constants (num_resolved_methods_).
+        ObjPtr<mirror::DexCache> caller_dex_cache = caller->GetDexCache<kWithoutReadBarrier>();
+        if (caller_dex_cache->GetResolvedMethod(
+                update_dex_cache_method_index, kRuntimePointerSize) != called) {
+          caller_dex_cache->SetResolvedMethod(update_dex_cache_method_index,
+                                              called,
+                                              kRuntimePointerSize);
+        }
       }
     } else if (invoke_type == kStatic) {
       const auto called_dex_method_idx = called->GetDexMethodIndex();
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 14e017a..1a48b46 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1128,7 +1128,7 @@
         }
       } else {
         if (fixup_heap_objects_) {
-          method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this), pointer_size_);
+          method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this));
         }
         method->UpdateEntrypoints<kWithoutReadBarrier>(ForwardCodeAdapter(this), pointer_size_);
       }
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index 314c45e..071d1ae 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -48,6 +48,10 @@
 DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_CARD_TABLE_OFFSET), (static_cast<int32_t>(art::Thread:: CardTableOffset<art::kRuntimePointerSize>().Int32Value())))
 #define CODEITEM_INSNS_OFFSET 16
 DEFINE_CHECK_EQ(static_cast<int32_t>(CODEITEM_INSNS_OFFSET), (static_cast<int32_t>(__builtin_offsetof(art::DexFile::CodeItem, insns_))))
+#define MIRROR_CLASS_DEX_CACHE_OFFSET 16
+DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_CLASS_DEX_CACHE_OFFSET), (static_cast<int32_t>(art::mirror::Class:: DexCacheOffset().Int32Value())))
+#define MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET 48
+DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET), (static_cast<int32_t>(art::mirror::DexCache:: ResolvedMethodsOffset().Int32Value())))
 #define MIRROR_OBJECT_CLASS_OFFSET 0
 DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_OBJECT_CLASS_OFFSET), (static_cast<int32_t>(art::mirror::Object:: ClassOffset().Int32Value())))
 #define MIRROR_OBJECT_LOCK_WORD_OFFSET 4
@@ -60,20 +64,18 @@
 DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_INTERFACE), (static_cast<uint32_t>((art::kAccInterface))))
 #define ACCESS_FLAGS_CLASS_IS_FINALIZABLE_BIT 0x1f
 DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_FINALIZABLE_BIT), (static_cast<uint32_t>((art::MostSignificantBit(art::kAccClassIsFinalizable)))))
-#define ART_METHOD_DEX_CACHE_METHODS_OFFSET_32 20
-DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DEX_CACHE_METHODS_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: DexCacheResolvedMethodsOffset(art::PointerSize::k32).Int32Value())))
-#define ART_METHOD_DEX_CACHE_METHODS_OFFSET_64 24
-DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DEX_CACHE_METHODS_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: DexCacheResolvedMethodsOffset(art::PointerSize::k64).Int32Value())))
-#define ART_METHOD_JNI_OFFSET_32 24
+#define ART_METHOD_JNI_OFFSET_32 20
 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_JNI_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromJniOffset(art::PointerSize::k32).Int32Value())))
-#define ART_METHOD_JNI_OFFSET_64 32
+#define ART_METHOD_JNI_OFFSET_64 24
 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_JNI_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromJniOffset(art::PointerSize::k64).Int32Value())))
-#define ART_METHOD_QUICK_CODE_OFFSET_32 28
+#define ART_METHOD_QUICK_CODE_OFFSET_32 24
 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_QUICK_CODE_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k32).Int32Value())))
-#define ART_METHOD_QUICK_CODE_OFFSET_64 40
+#define ART_METHOD_QUICK_CODE_OFFSET_64 32
 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_QUICK_CODE_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k64).Int32Value())))
 #define ART_METHOD_DECLARING_CLASS_OFFSET 0
 DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DECLARING_CLASS_OFFSET), (static_cast<int32_t>(art::ArtMethod:: DeclaringClassOffset().Int32Value())))
+#define ART_METHOD_ACCESS_FLAGS_OFFSET 4
+DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_ACCESS_FLAGS_OFFSET), (static_cast<int32_t>(art::ArtMethod:: AccessFlagsOffset().Int32Value())))
 #define STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT 3
 DEFINE_CHECK_EQ(static_cast<int32_t>(STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT), (static_cast<int32_t>(art::WhichPowerOf2(sizeof(art::mirror::StringDexCachePair)))))
 #define STRING_DEX_CACHE_SIZE_MINUS_ONE 1023
@@ -126,6 +128,10 @@
 DEFINE_CHECK_EQ(static_cast<uint32_t>(OBJECT_ALIGNMENT_MASK_TOGGLED), (static_cast<uint32_t>(~static_cast<uint32_t>(art::kObjectAlignment - 1))))
 #define OBJECT_ALIGNMENT_MASK_TOGGLED64 0xfffffffffffffff8
 DEFINE_CHECK_EQ(static_cast<uint64_t>(OBJECT_ALIGNMENT_MASK_TOGGLED64), (static_cast<uint64_t>(~static_cast<uint64_t>(art::kObjectAlignment - 1))))
+#define ACC_OBSOLETE_METHOD 262144
+DEFINE_CHECK_EQ(static_cast<int32_t>(ACC_OBSOLETE_METHOD), (static_cast<int32_t>(art::kAccObsoleteMethod)))
+#define ACC_OBSOLETE_METHOD_SHIFT 18
+DEFINE_CHECK_EQ(static_cast<int32_t>(ACC_OBSOLETE_METHOD_SHIFT), (static_cast<int32_t>(art::WhichPowerOf2(art::kAccObsoleteMethod))))
 #define ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE 128
 DEFINE_CHECK_EQ(static_cast<int32_t>(ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE), (static_cast<int32_t>((art::gc::allocator::RosAlloc::kMaxThreadLocalBracketSize))))
 #define ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT 3
diff --git a/runtime/image.cc b/runtime/image.cc
index 950ac5d..1f7e0f3 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -26,7 +26,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '4', '6', '\0' };  // Hash-based methods array.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '4', '7', '\0' };  // Smaller ArtMethod.
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 758c9e4..05384b4 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -105,6 +105,7 @@
       have_field_read_listeners_(false),
       have_field_write_listeners_(false),
       have_exception_thrown_listeners_(false),
+      have_watched_frame_pop_listeners_(false),
       have_branch_listeners_(false),
       have_invoke_virtual_or_interface_listeners_(false),
       deoptimized_methods_lock_("deoptimized methods lock", kDeoptimizedMethodsLock),
@@ -504,6 +505,11 @@
                            exception_thrown_listeners_,
                            listener,
                            &have_exception_thrown_listeners_);
+  PotentiallyAddListenerTo(kWatchedFramePop,
+                           events,
+                           watched_frame_pop_listeners_,
+                           listener,
+                           &have_watched_frame_pop_listeners_);
   UpdateInterpreterHandlerTable();
 }
 
@@ -581,6 +587,11 @@
                                 exception_thrown_listeners_,
                                 listener,
                                 &have_exception_thrown_listeners_);
+  PotentiallyRemoveListenerFrom(kWatchedFramePop,
+                                events,
+                                watched_frame_pop_listeners_,
+                                listener,
+                                &have_watched_frame_pop_listeners_);
   UpdateInterpreterHandlerTable();
 }
 
@@ -1042,6 +1053,14 @@
   }
 }
 
+void Instrumentation::WatchedFramePopImpl(Thread* thread, const ShadowFrame& frame) const {
+  for (InstrumentationListener* listener : watched_frame_pop_listeners_) {
+    if (listener != nullptr) {
+      listener->WatchedFramePop(thread, frame);
+    }
+  }
+}
+
 void Instrumentation::FieldReadEventImpl(Thread* thread,
                                          ObjPtr<mirror::Object> this_object,
                                          ArtMethod* method,
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index f6efb5f..114db76 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -38,6 +38,7 @@
 class ArtMethod;
 template <typename T> class Handle;
 union JValue;
+class ShadowFrame;
 class Thread;
 
 namespace instrumentation {
@@ -143,6 +144,15 @@
                                         uint32_t dex_pc,
                                         ArtMethod* callee)
       REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+  // Call-back when a shadow_frame with the needs_notify_pop_ boolean set is popped off the stack by
+  // either return or exceptions. Normally instrumentation listeners should ensure that there are
+  // shadow-frames by deoptimizing stacks.
+  virtual void WatchedFramePop(Thread* thread ATTRIBUTE_UNUSED,
+                               const ShadowFrame& frame ATTRIBUTE_UNUSED)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    return;
+  }
 };
 
 // Instrumentation is a catch-all for when extra information is required from the runtime. The
@@ -161,6 +171,7 @@
     kExceptionThrown = 0x40,
     kBranch = 0x80,
     kInvokeVirtualOrInterface = 0x100,
+    kWatchedFramePop = 0x200,
   };
 
   enum class InstrumentationLevel {
@@ -334,11 +345,16 @@
     return have_invoke_virtual_or_interface_listeners_;
   }
 
+  bool HasWatchedFramePopListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
+    return have_watched_frame_pop_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_branch_listeners_ || have_invoke_virtual_or_interface_listeners_ ||
+        have_watched_frame_pop_listeners_;
   }
 
   // Any instrumentation *other* than what is needed for Jit profiling active?
@@ -346,7 +362,7 @@
     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_branch_listeners_ || have_watched_frame_pop_listeners_;
   }
 
   // Inform listeners that a method has been entered. A dex PC is provided as we may install
@@ -424,6 +440,14 @@
     }
   }
 
+  // Inform listeners that a branch has been taken (only supported by the interpreter).
+  void WatchedFramePopped(Thread* thread, const ShadowFrame& frame) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (UNLIKELY(HasWatchedFramePopListeners())) {
+      WatchedFramePopImpl(thread, frame);
+    }
+  }
+
   // Inform listeners that an exception was thrown.
   void ExceptionThrownEvent(Thread* thread, mirror::Throwable* exception_object) const
       REQUIRES_SHARED(Locks::mutator_lock_);
@@ -531,6 +555,8 @@
                                     uint32_t dex_pc,
                                     ArtMethod* callee) const
       REQUIRES_SHARED(Locks::mutator_lock_);
+  void WatchedFramePopImpl(Thread* thread, const ShadowFrame& frame) const
+      REQUIRES_SHARED(Locks::mutator_lock_);
   void FieldReadEventImpl(Thread* thread,
                           ObjPtr<mirror::Object> this_object,
                           ArtMethod* method,
@@ -602,6 +628,9 @@
   // Do we have any exception thrown listeners? Short-cut to avoid taking the instrumentation_lock_.
   bool have_exception_thrown_listeners_ GUARDED_BY(Locks::mutator_lock_);
 
+  // Do we have any frame pop listeners? Short-cut to avoid taking the instrumentation_lock_.
+  bool have_watched_frame_pop_listeners_ GUARDED_BY(Locks::mutator_lock_);
+
   // Do we have any branch listeners? Short-cut to avoid taking the instrumentation_lock_.
   bool have_branch_listeners_ GUARDED_BY(Locks::mutator_lock_);
 
@@ -633,6 +662,7 @@
   std::list<InstrumentationListener*> field_read_listeners_ GUARDED_BY(Locks::mutator_lock_);
   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_);
 
   // 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 19ce299..7390f4f 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -28,6 +28,7 @@
 #include "jvalue.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
+#include "interpreter/shadow_frame.h"
 #include "thread-inl.h"
 #include "thread_list.h"
 #include "well_known_classes.h"
@@ -48,7 +49,8 @@
       received_field_written_object_event(false),
       received_exception_thrown_event(false),
       received_branch_event(false),
-      received_invoke_virtual_or_interface_event(false) {}
+      received_invoke_virtual_or_interface_event(false),
+      received_watched_frame_pop(false) {}
 
   virtual ~TestInstrumentationListener() {}
 
@@ -146,6 +148,11 @@
     received_invoke_virtual_or_interface_event = true;
   }
 
+  void WatchedFramePop(Thread* thread ATTRIBUTE_UNUSED, const ShadowFrame& frame ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+    received_watched_frame_pop  = true;
+  }
+
   void Reset() {
     received_method_enter_event = false;
     received_method_exit_event = false;
@@ -158,6 +165,7 @@
     received_exception_thrown_event = false;
     received_branch_event = false;
     received_invoke_virtual_or_interface_event = false;
+    received_watched_frame_pop = false;
   }
 
   bool received_method_enter_event;
@@ -171,6 +179,7 @@
   bool received_exception_thrown_event;
   bool received_branch_event;
   bool received_invoke_virtual_or_interface_event;
+  bool received_watched_frame_pop;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(TestInstrumentationListener);
@@ -221,6 +230,7 @@
 
     mirror::Object* const event_obj = nullptr;
     const uint32_t event_dex_pc = 0;
+    ShadowFrameAllocaUniquePtr test_frame = CREATE_SHADOW_FRAME(0, nullptr, event_method, 0);
 
     // Check the listener is registered and is notified of the event.
     EXPECT_TRUE(HasEventListener(instr, instrumentation_event));
@@ -231,7 +241,8 @@
                 event_method,
                 event_obj,
                 event_field,
-                event_dex_pc);
+                event_dex_pc,
+                *test_frame);
     EXPECT_TRUE(DidListenerReceiveEvent(listener, instrumentation_event, with_object));
 
     listener.Reset();
@@ -250,7 +261,8 @@
                 event_method,
                 event_obj,
                 event_field,
-                event_dex_pc);
+                event_dex_pc,
+                *test_frame);
     EXPECT_FALSE(DidListenerReceiveEvent(listener, instrumentation_event, with_object));
   }
 
@@ -361,6 +373,8 @@
         return instr->HasBranchListeners();
       case instrumentation::Instrumentation::kInvokeVirtualOrInterface:
         return instr->HasInvokeVirtualOrInterfaceListeners();
+      case instrumentation::Instrumentation::kWatchedFramePop:
+        return instr->HasWatchedFramePopListeners();
       default:
         LOG(FATAL) << "Unknown instrumentation event " << event_type;
         UNREACHABLE();
@@ -373,7 +387,8 @@
                           ArtMethod* method,
                           mirror::Object* obj,
                           ArtField* field,
-                          uint32_t dex_pc)
+                          uint32_t dex_pc,
+                          const ShadowFrame& frame)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     switch (event_type) {
       case instrumentation::Instrumentation::kMethodEntered:
@@ -411,6 +426,9 @@
       case instrumentation::Instrumentation::kInvokeVirtualOrInterface:
         instr->InvokeVirtualOrInterface(self, obj, method, dex_pc, method);
         break;
+      case instrumentation::Instrumentation::kWatchedFramePop:
+        instr->WatchedFramePopped(self, frame);
+        break;
       default:
         LOG(FATAL) << "Unknown instrumentation event " << event_type;
         UNREACHABLE();
@@ -441,6 +459,8 @@
         return listener.received_branch_event;
       case instrumentation::Instrumentation::kInvokeVirtualOrInterface:
         return listener.received_invoke_virtual_or_interface_event;
+      case instrumentation::Instrumentation::kWatchedFramePop:
+        return listener.received_watched_frame_pop;
       default:
         LOG(FATAL) << "Unknown instrumentation event " << event_type;
         UNREACHABLE();
@@ -527,6 +547,10 @@
   TestEvent(instrumentation::Instrumentation::kFieldRead);
 }
 
+TEST_F(InstrumentationTest, WatchedFramePop) {
+  TestEvent(instrumentation::Instrumentation::kWatchedFramePop);
+}
+
 TEST_F(InstrumentationTest, FieldWriteObjectEvent) {
   ScopedObjectAccess soa(Thread::Current());
   jobject class_loader = LoadDex("Instrumentation");
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 8c825f3..ae461fd 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -431,6 +431,9 @@
   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);
+    }
     // Exception is not caught by the current method. We will unwind to the
     // caller. Notify any instrumentation listener.
     instrumentation->MethodUnwindEvent(self, shadow_frame.GetThisObject(),
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 0c5a45f..f352960 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -150,6 +150,37 @@
   }
 }
 
+static bool NeedsMethodExitEvent(const instrumentation::Instrumentation* ins)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return ins->HasMethodExitListeners() || ins->HasWatchedFramePopListeners();
+}
+
+// Sends the normal method exit event. Returns true if the events succeeded and false if there is a
+// pending exception.
+NO_INLINE static bool SendMethodExitEvents(Thread* self,
+                                           const instrumentation::Instrumentation* instrumentation,
+                                           const ShadowFrame& frame,
+                                           ObjPtr<mirror::Object> thiz,
+                                           ArtMethod* method,
+                                           uint32_t dex_pc,
+                                           const JValue& result)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  bool had_event = false;
+  if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+    had_event = true;
+    instrumentation->MethodExitEvent(self, thiz.Ptr(), method, dex_pc, result);
+  }
+  if (UNLIKELY(frame.NeedsNotifyPop() && instrumentation->HasWatchedFramePopListeners())) {
+    had_event = true;
+    instrumentation->WatchedFramePopped(self, frame);
+  }
+  if (UNLIKELY(had_event)) {
+    return !self->IsExceptionPending();
+  } else {
+    return true;
+  }
+}
+
 template<bool do_access_check, bool transaction_active>
 JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
                          ShadowFrame& shadow_frame, JValue result_register,
@@ -262,14 +293,15 @@
         JValue result;
         self->AllowThreadSuspension();
         HANDLE_MONITOR_CHECKS();
-        if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
-          instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
-                                           shadow_frame.GetMethod(), inst->GetDexPc(insns),
-                                           result);
-          if (UNLIKELY(self->IsExceptionPending())) {
-            // Don't send another method exit event.
-            HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
-          }
+        if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+                     !SendMethodExitEvents(self,
+                                           instrumentation,
+                                           shadow_frame,
+                                           shadow_frame.GetThisObject(code_item->ins_size_),
+                                           shadow_frame.GetMethod(),
+                                           inst->GetDexPc(insns),
+                                           result))) {
+          HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
         }
         if (interpret_one_instruction) {
           /* Signal mterp to return to caller */
@@ -283,14 +315,15 @@
         JValue result;
         self->AllowThreadSuspension();
         HANDLE_MONITOR_CHECKS();
-        if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
-          instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
-                                           shadow_frame.GetMethod(), inst->GetDexPc(insns),
-                                           result);
-          if (UNLIKELY(self->IsExceptionPending())) {
-            // Don't send another method exit event.
-            HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
-          }
+        if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+                     !SendMethodExitEvents(self,
+                                           instrumentation,
+                                           shadow_frame,
+                                           shadow_frame.GetThisObject(code_item->ins_size_),
+                                           shadow_frame.GetMethod(),
+                                           inst->GetDexPc(insns),
+                                           result))) {
+          HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
         }
         if (interpret_one_instruction) {
           /* Signal mterp to return to caller */
@@ -305,14 +338,15 @@
         result.SetI(shadow_frame.GetVReg(inst->VRegA_11x(inst_data)));
         self->AllowThreadSuspension();
         HANDLE_MONITOR_CHECKS();
-        if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
-          instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
-                                           shadow_frame.GetMethod(), inst->GetDexPc(insns),
-                                           result);
-          if (UNLIKELY(self->IsExceptionPending())) {
-            // Don't send another method exit event.
-            HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
-          }
+        if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+                     !SendMethodExitEvents(self,
+                                           instrumentation,
+                                           shadow_frame,
+                                           shadow_frame.GetThisObject(code_item->ins_size_),
+                                           shadow_frame.GetMethod(),
+                                           inst->GetDexPc(insns),
+                                           result))) {
+          HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
         }
         if (interpret_one_instruction) {
           /* Signal mterp to return to caller */
@@ -326,14 +360,15 @@
         result.SetJ(shadow_frame.GetVRegLong(inst->VRegA_11x(inst_data)));
         self->AllowThreadSuspension();
         HANDLE_MONITOR_CHECKS();
-        if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
-          instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
-                                           shadow_frame.GetMethod(), inst->GetDexPc(insns),
-                                           result);
-          if (UNLIKELY(self->IsExceptionPending())) {
-            // Don't send another method exit event.
-            HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
-          }
+        if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+                     !SendMethodExitEvents(self,
+                                           instrumentation,
+                                           shadow_frame,
+                                           shadow_frame.GetThisObject(code_item->ins_size_),
+                                           shadow_frame.GetMethod(),
+                                           inst->GetDexPc(insns),
+                                           result))) {
+          HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
         }
         if (interpret_one_instruction) {
           /* Signal mterp to return to caller */
@@ -367,17 +402,18 @@
           }
         }
         result.SetL(obj_result);
-        if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
-          instrumentation->MethodExitEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
-                                           shadow_frame.GetMethod(), inst->GetDexPc(insns),
-                                           result);
-          if (UNLIKELY(self->IsExceptionPending())) {
-            // Don't send another method exit event.
-            HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
-          }
-          // Re-load since it might have moved during the MethodExitEvent.
-          result.SetL(shadow_frame.GetVRegReference(ref_idx));
+        if (UNLIKELY(NeedsMethodExitEvent(instrumentation) &&
+                     !SendMethodExitEvents(self,
+                                           instrumentation,
+                                           shadow_frame,
+                                           shadow_frame.GetThisObject(code_item->ins_size_),
+                                           shadow_frame.GetMethod(),
+                                           inst->GetDexPc(insns),
+                                           result))) {
+          HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(nullptr);
         }
+        // Re-load since it might have moved during the MethodExitEvent.
+        result.SetL(shadow_frame.GetVRegReference(ref_idx));
         if (interpret_one_instruction) {
           /* Signal mterp to return to caller */
           shadow_frame.SetDexPC(DexFile::kDexNoIndex);
diff --git a/runtime/interpreter/shadow_frame.h b/runtime/interpreter/shadow_frame.h
index 05768cd..6903af2 100644
--- a/runtime/interpreter/shadow_frame.h
+++ b/runtime/interpreter/shadow_frame.h
@@ -361,6 +361,14 @@
     return result_register_;
   }
 
+  bool NeedsNotifyPop() const {
+    return needs_notify_pop_;
+  }
+
+  void SetNotifyPop(bool notify) {
+    needs_notify_pop_ = notify;
+  }
+
  private:
   ShadowFrame(uint32_t num_vregs, ShadowFrame* link, ArtMethod* method,
               uint32_t dex_pc, bool has_reference_array)
@@ -372,7 +380,8 @@
         number_of_vregs_(num_vregs),
         dex_pc_(dex_pc),
         cached_hotness_countdown_(0),
-        hotness_countdown_(0) {
+        hotness_countdown_(0),
+        needs_notify_pop_(0) {
     // TODO(iam): Remove this parameter, it's an an artifact of portable removal
     DCHECK(has_reference_array);
     if (has_reference_array) {
@@ -404,6 +413,9 @@
   uint32_t dex_pc_;
   int16_t cached_hotness_countdown_;
   int16_t hotness_countdown_;
+  // TODO Might be worth it to try to bit-pack this into some other field to reduce stack usage.
+  // NB alignment requires that this field takes 4 bytes. Only 1 bit is actually ever used.
+  bool needs_notify_pop_;
 
   // This is a two-part array:
   //  - [0..number_of_vregs) holds the raw virtual registers, and each element here is always 4
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index c79b5c9..d9cfa53 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -75,7 +75,7 @@
 static constexpr bool kPrintDlOpenErrorMessage = false;
 
 // If true, we advise the kernel about dex file mem map accesses.
-static constexpr bool kMadviseDexFileAccesses = false;
+static constexpr bool kMadviseDexFileAccesses = true;
 
 // Note for OatFileBase and descendents:
 //
@@ -1498,14 +1498,20 @@
 
 // Madvise the dex file based on the state we are moving to.
 void OatDexFile::MadviseDexFile(const DexFile& dex_file, MadviseState state) {
-  if (!kMadviseDexFileAccesses) {
+  const bool low_ram = Runtime::Current()->GetHeap()->IsLowMemoryMode();
+  // TODO: Also do madvise hints for non low ram devices.
+  if (!kMadviseDexFileAccesses || !low_ram) {
     return;
   }
   if (state == MadviseState::kMadviseStateAtLoad) {
-    // Default every dex file to MADV_RANDOM when its loaded by default.
-    MadviseLargestPageAlignedRegion(dex_file.Begin(),
-                                    dex_file.Begin() + dex_file.Size(),
-                                    MADV_RANDOM);
+    if (low_ram) {
+      // Default every dex file to MADV_RANDOM when its loaded by default for low ram devices.
+      // Other devices have enough page cache to get performance benefits from loading more pages
+      // into the page cache.
+      MadviseLargestPageAlignedRegion(dex_file.Begin(),
+                                      dex_file.Begin() + dex_file.Size(),
+                                      MADV_RANDOM);
+    }
   }
   const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
   if (oat_dex_file != nullptr) {
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index a4d6c3d..ac2ed9e 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -258,7 +258,7 @@
                                 /*out*/DexFile const** final_dex_file ATTRIBUTE_UNUSED,
                                 /*out*/DexFile::ClassDef const** final_class_def ATTRIBUTE_UNUSED)
         OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
-      std::string location(initial_dex_file.GetLocation());
+      const std::string& location = initial_dex_file.GetLocation();
       std::string event =
           std::string("PreDefine:") + descriptor + " <" +
           location.substr(location.rfind('/') + 1, location.size()) + ">";
diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc
index 82b9af3..6ff9666 100644
--- a/runtime/ti/agent.cc
+++ b/runtime/ti/agent.cc
@@ -123,7 +123,7 @@
   }
 }
 
-Agent::Agent(std::string arg)
+Agent::Agent(const std::string& arg)
     : dlopen_handle_(nullptr),
       onload_(nullptr),
       onattach_(nullptr),
diff --git a/runtime/ti/agent.h b/runtime/ti/agent.h
index f98e387..d6f1f2e 100644
--- a/runtime/ti/agent.h
+++ b/runtime/ti/agent.h
@@ -77,7 +77,7 @@
     return DoLoadHelper(true, call_res, error_msg);
   }
 
-  explicit Agent(std::string arg);
+  explicit Agent(const std::string& arg);
 
   Agent(const Agent& other);
   Agent& operator=(const Agent& other);
diff --git a/test/004-JniTest/build b/test/004-JniTest/build
index c8440fc..e563d73 100755
--- a/test/004-JniTest/build
+++ b/test/004-JniTest/build
@@ -38,4 +38,30 @@
 export -f javac_wrapper
 export JAVAC=javac_wrapper
 
+######################################################################
+
+# Use the original dx with no extra magic or pessimizing flags.
+# This ensures that any default optimizations that dx do would not break JNI.
+
+export ORIGINAL_DX="$DX"
+
+# Filter out --debug flag from dx.
+function dx_wrapper {
+  local args=("$@")
+  local args_filtered=()
+  for i in "${args[@]}"; do
+    case "$i" in
+      --debug)
+        ;;
+      *)
+        args_filtered+=("$i")
+        ;;
+    esac
+  done
+  "$ORIGINAL_DX" "${args_filtered[@]}"
+}
+
+export -f dx_wrapper
+export DX=dx_wrapper
+
 ./default-build "$@"
diff --git a/test/004-JniTest/expected.txt b/test/004-JniTest/expected.txt
index f7e404d..7e85ab1 100644
--- a/test/004-JniTest/expected.txt
+++ b/test/004-JniTest/expected.txt
@@ -58,3 +58,5 @@
 hi-lambda: λ
 hi-default δλ
 hi-default δλ
+Clinit Lookup: ClassWithoutClinit: <NSME Exception>
+Clinit Lookup: ClassWithClinit: Main$ClassWithClinit()(Class: class java.lang.reflect.Constructor)
diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc
index a34d633..bc5a0a6 100644
--- a/test/004-JniTest/jni_test.cc
+++ b/test/004-JniTest/jni_test.cc
@@ -776,6 +776,18 @@
   return a + b + c;
 }
 
+extern "C" JNIEXPORT jobject JNICALL Java_Main_lookupClinit(JNIEnv* env, jclass, jclass kls) {
+  jmethodID clinit_id = env->GetStaticMethodID(kls, "<clinit>", "()V");
+
+  if (clinit_id != nullptr) {
+    jobject obj = env->ToReflectedMethod(kls, clinit_id, /*isStatic*/ true);
+    CHECK(obj != nullptr);
+    return obj;
+  } else {
+    return nullptr;
+  }
+}
+
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_isSlowDebug(JNIEnv*, jclass) {
   // Return whether slow-debug is on. Only relevant for debug builds.
   if (kIsDebugBuild) {
diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java
index 0c4ed89..fe5f4e3 100644
--- a/test/004-JniTest/src/Main.java
+++ b/test/004-JniTest/src/Main.java
@@ -56,6 +56,8 @@
         registerNativesJniTest();
         testFastNativeMethods();
         testCriticalNativeMethods();
+
+        testClinitMethodLookup();
     }
 
     private static native boolean registerNativesJniTest();
@@ -314,6 +316,36 @@
     }
 
     private static native boolean isSlowDebug();
+
+    private static void testClinitMethodLookup() {
+      // Expect this to print <NSME Exception>
+      try {
+        System.out.println("Clinit Lookup: ClassWithoutClinit: " + methodString(lookupClinit(ClassWithoutClinit.class)));
+      } catch (NoSuchMethodError e) {
+        System.out.println("Clinit Lookup: ClassWithoutClinit: <NSME Exception>");
+      }
+      // Expect this to print <clinit>
+      try {
+        System.out.println("Clinit Lookup: ClassWithClinit: " + methodString(lookupClinit(ClassWithClinit.class)));
+      } catch (NoSuchMethodError e) {
+        System.out.println("Clinit Lookup: ClassWithClinit: <NSME Exception>");
+      }
+   }
+
+    private static String methodString(java.lang.reflect.Executable method) {
+      if (method == null) {
+        return "<<null>>";
+      } else {
+        return method.toString() + "(Class: " + method.getClass().toString() + ")";
+      }
+    }
+    private static native java.lang.reflect.Executable lookupClinit(Class kls);
+
+    private static class ClassWithoutClinit {
+    }
+    private static class ClassWithClinit {
+      static {}
+    }
 }
 
 @FunctionalInterface
diff --git a/test/021-string2/src/Main.java b/test/021-string2/src/Main.java
index 3b81d8e..c713aa4 100644
--- a/test/021-string2/src/Main.java
+++ b/test/021-string2/src/Main.java
@@ -556,12 +556,24 @@
         Assert.assertTrue($noinline$equalsConstString0(""));
         Assert.assertFalse($noinline$equalsConstString0("1"));
 
+        Assert.assertTrue($noinline$equalsConstString3("012"));
+        Assert.assertFalse($noinline$equalsConstString3("01"));
+        Assert.assertFalse($noinline$equalsConstString3("0123"));
+        Assert.assertFalse($noinline$equalsConstString3("01x"));
+        Assert.assertFalse($noinline$equalsConstString3("01\u0440"));
+
         Assert.assertTrue($noinline$equalsConstString7("0123456"));
         Assert.assertFalse($noinline$equalsConstString7("012345"));
         Assert.assertFalse($noinline$equalsConstString7("01234567"));
         Assert.assertFalse($noinline$equalsConstString7("012345x"));
         Assert.assertFalse($noinline$equalsConstString7("012345\u0440"));
 
+        Assert.assertTrue($noinline$equalsConstString12("012345678901"));
+        Assert.assertFalse($noinline$equalsConstString12("01234567890"));
+        Assert.assertFalse($noinline$equalsConstString12("0123456789012"));
+        Assert.assertFalse($noinline$equalsConstString12("01234567890x"));
+        Assert.assertFalse($noinline$equalsConstString12("01234567890\u0440"));
+
         Assert.assertTrue($noinline$equalsConstString14("01234567890123"));
         Assert.assertFalse($noinline$equalsConstString14("0123456789012"));
         Assert.assertFalse($noinline$equalsConstString14("012345678901234"));
@@ -587,12 +599,24 @@
         Assert.assertFalse(
             $noinline$equalsConstString35("0123456789012345678901234567890123\u0440"));
 
+        Assert.assertTrue($noinline$equalsConstNonAsciiString3("\u044012"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString3("\u04401"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString3("\u0440123"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString3("\u04401x"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString3("012"));
+
         Assert.assertTrue($noinline$equalsConstNonAsciiString7("\u0440123456"));
         Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u044012345"));
         Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u04401234567"));
         Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u044012345x"));
         Assert.assertFalse($noinline$equalsConstNonAsciiString7("0123456"));
 
+        Assert.assertTrue($noinline$equalsConstNonAsciiString12("\u044012345678901"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString12("\u04401234567890"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString12("\u0440123456789012"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString12("\u04401234567890x"));
+        Assert.assertFalse($noinline$equalsConstNonAsciiString12("012345678901"));
+
         Assert.assertTrue($noinline$equalsConstNonAsciiString14("\u04401234567890123"));
         Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u0440123456789012"));
         Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u044012345678901234"));
@@ -631,12 +655,24 @@
         Assert.assertTrue($noinline$constString0Equals(""));
         Assert.assertFalse($noinline$constString0Equals("1"));
 
+        Assert.assertTrue($noinline$constString3Equals("012"));
+        Assert.assertFalse($noinline$constString3Equals("01"));
+        Assert.assertFalse($noinline$constString3Equals("0123"));
+        Assert.assertFalse($noinline$constString3Equals("01x"));
+        Assert.assertFalse($noinline$constString3Equals("01\u0440"));
+
         Assert.assertTrue($noinline$constString7Equals("0123456"));
         Assert.assertFalse($noinline$constString7Equals("012345"));
         Assert.assertFalse($noinline$constString7Equals("01234567"));
         Assert.assertFalse($noinline$constString7Equals("012345x"));
         Assert.assertFalse($noinline$constString7Equals("012345\u0440"));
 
+        Assert.assertTrue($noinline$constString12Equals("012345678901"));
+        Assert.assertFalse($noinline$constString12Equals("01234567890"));
+        Assert.assertFalse($noinline$constString12Equals("0123456789012"));
+        Assert.assertFalse($noinline$constString12Equals("01234567890x"));
+        Assert.assertFalse($noinline$constString12Equals("01234567890\u0440"));
+
         Assert.assertTrue($noinline$constString14Equals("01234567890123"));
         Assert.assertFalse($noinline$constString14Equals("0123456789012"));
         Assert.assertFalse($noinline$constString14Equals("012345678901234"));
@@ -662,12 +698,24 @@
         Assert.assertFalse(
             $noinline$constString35Equals("0123456789012345678901234567890123\u0040"));
 
+        Assert.assertTrue($noinline$constNonAsciiString3Equals("\u044012"));
+        Assert.assertFalse($noinline$constNonAsciiString3Equals("\u04401"));
+        Assert.assertFalse($noinline$constNonAsciiString3Equals("\u0440123"));
+        Assert.assertFalse($noinline$constNonAsciiString3Equals("\u04401x"));
+        Assert.assertFalse($noinline$constNonAsciiString3Equals("0123456"));
+
         Assert.assertTrue($noinline$constNonAsciiString7Equals("\u0440123456"));
         Assert.assertFalse($noinline$constNonAsciiString7Equals("\u044012345"));
         Assert.assertFalse($noinline$constNonAsciiString7Equals("\u04401234567"));
         Assert.assertFalse($noinline$constNonAsciiString7Equals("\u044012345x"));
         Assert.assertFalse($noinline$constNonAsciiString7Equals("0123456"));
 
+        Assert.assertTrue($noinline$constNonAsciiString12Equals("\u044012345678901"));
+        Assert.assertFalse($noinline$constNonAsciiString12Equals("\u04401234567890"));
+        Assert.assertFalse($noinline$constNonAsciiString12Equals("\u0440123456789012"));
+        Assert.assertFalse($noinline$constNonAsciiString12Equals("\u04401234567890x"));
+        Assert.assertFalse($noinline$constNonAsciiString12Equals("012345678901"));
+
         Assert.assertTrue($noinline$constNonAsciiString14Equals("\u04401234567890123"));
         Assert.assertFalse($noinline$constNonAsciiString14Equals("\u0440123456789012"));
         Assert.assertFalse($noinline$constNonAsciiString14Equals("\u044012345678901234"));
@@ -708,134 +756,138 @@
     }
 
     public static boolean $noinline$equalsConstString0(String s) {
-        if (doThrow) { throw new Error(); }
         return s.equals("");
     }
 
+    public static boolean $noinline$equalsConstString3(String s) {
+        return s.equals("012");
+    }
+
     public static boolean $noinline$equalsConstString7(String s) {
-        if (doThrow) { throw new Error(); }
         return s.equals("0123456");
     }
 
+    public static boolean $noinline$equalsConstString12(String s) {
+        return s.equals("012345678901");
+    }
+
     public static boolean $noinline$equalsConstString14(String s) {
-        if (doThrow) { throw new Error(); }
         return s.equals("01234567890123");
     }
 
     public static boolean $noinline$equalsConstString24(String s) {
-        if (doThrow) { throw new Error(); }
         return s.equals("012345678901234567890123");
     }
 
     public static boolean $noinline$equalsConstString29(String s) {
-        if (doThrow) { throw new Error(); }
         return s.equals("01234567890123456789012345678");
     }
 
     public static boolean $noinline$equalsConstString35(String s) {
-        if (doThrow) { throw new Error(); }
         return s.equals("01234567890123456789012345678901234");
     }
 
+    public static boolean $noinline$equalsConstNonAsciiString3(String s) {
+        return s.equals("\u044012");
+    }
+
     public static boolean $noinline$equalsConstNonAsciiString7(String s) {
-        if (doThrow) { throw new Error(); }
         return s.equals("\u0440123456");
     }
 
+    public static boolean $noinline$equalsConstNonAsciiString12(String s) {
+        return s.equals("\u044012345678901");
+    }
+
     public static boolean $noinline$equalsConstNonAsciiString14(String s) {
-        if (doThrow) { throw new Error(); }
         return s.equals("\u04401234567890123");
     }
 
     public static boolean $noinline$equalsConstNonAsciiString24(String s) {
-        if (doThrow) { throw new Error(); }
         return s.equals("\u044012345678901234567890123");
     }
 
     public static boolean $noinline$equalsConstNonAsciiString29(String s) {
-        if (doThrow) { throw new Error(); }
         return s.equals("\u04401234567890123456789012345678");
     }
 
     public static boolean $noinline$equalsConstNonAsciiString35(String s) {
-        if (doThrow) { throw new Error(); }
         return s.equals("\u04401234567890123456789012345678901234");
     }
 
     public static boolean $noinline$constString0Equals(String s) {
-        if (doThrow) { throw new Error(); }
         return s.equals("");
     }
 
+    public static boolean $noinline$constString3Equals(String s) {
+        return "012".equals(s);
+    }
+
     public static boolean $noinline$constString7Equals(String s) {
-        if (doThrow) { throw new Error(); }
         return "0123456".equals(s);
     }
 
+    public static boolean $noinline$constString12Equals(String s) {
+        return "012345678901".equals(s);
+    }
+
     public static boolean $noinline$constString14Equals(String s) {
-        if (doThrow) { throw new Error(); }
         return "01234567890123".equals(s);
     }
 
     public static boolean $noinline$constString24Equals(String s) {
-        if (doThrow) { throw new Error(); }
         return "012345678901234567890123".equals(s);
     }
 
     public static boolean $noinline$constString29Equals(String s) {
-        if (doThrow) { throw new Error(); }
         return "01234567890123456789012345678".equals(s);
     }
 
     public static boolean $noinline$constString35Equals(String s) {
-        if (doThrow) { throw new Error(); }
         return "01234567890123456789012345678901234".equals(s);
     }
 
+    public static boolean $noinline$constNonAsciiString3Equals(String s) {
+        return "\u044012".equals(s);
+    }
+
     public static boolean $noinline$constNonAsciiString7Equals(String s) {
-        if (doThrow) { throw new Error(); }
         return "\u0440123456".equals(s);
     }
 
+    public static boolean $noinline$constNonAsciiString12Equals(String s) {
+        return "\u044012345678901".equals(s);
+    }
+
     public static boolean $noinline$constNonAsciiString14Equals(String s) {
-        if (doThrow) { throw new Error(); }
         return "\u04401234567890123".equals(s);
     }
 
     public static boolean $noinline$constNonAsciiString24Equals(String s) {
-        if (doThrow) { throw new Error(); }
         return "\u044012345678901234567890123".equals(s);
     }
 
     public static boolean $noinline$constNonAsciiString29Equals(String s) {
-        if (doThrow) { throw new Error(); }
         return "\u04401234567890123456789012345678".equals(s);
     }
 
     public static boolean $noinline$constNonAsciiString35Equals(String s) {
-        if (doThrow) { throw new Error(); }
         return "\u04401234567890123456789012345678901234".equals(s);
     }
 
     public static int $noinline$compareTo(String lhs, String rhs) {
-        if (doThrow) { throw new Error(); }
         return lhs.compareTo(rhs);
     }
 
     public static boolean $noinline$equals(String lhs, String rhs) {
-        if (doThrow) { throw new Error(); }
         return lhs.equals(rhs);
     }
 
     public static int $noinline$indexOf(String lhs, int ch) {
-        if (doThrow) { throw new Error(); }
         return lhs.indexOf(ch);
     }
 
     public static int $noinline$indexOf(String lhs, int ch, int fromIndex) {
-        if (doThrow) { throw new Error(); }
         return lhs.indexOf(ch, fromIndex);
     }
-
-    public static boolean doThrow = false;
 }
diff --git a/test/1922-owned-monitors-info/expected.txt b/test/1922-owned-monitors-info/expected.txt
new file mode 100644
index 0000000..f66d1d5
--- /dev/null
+++ b/test/1922-owned-monitors-info/expected.txt
@@ -0,0 +1,678 @@
+owner-monitors, This thread
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockOther]
+Owned monitors: [NamedLock("Lock 1")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Current thread test: owned-monitor")]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [class art.Test1922$Target]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Current thread test: owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockSync, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Current thread test: owned-monitor")]
+owner-monitors, no suspend, Other thread
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockOther]
+Owned monitors: [NamedLock("Lock 1")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor")]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [class art.Test1922$Target]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockSync, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: false): owned-monitor")]
+owner-monitors, suspend, Other thread
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockOther]
+Owned monitors: [NamedLock("Lock 1")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 1"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 1"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 2"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [NamedLock("Lock 3")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [NamedLock("Lock 3"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [NamedLock("Lock 3"), Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor")]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [class art.Test1922$Target]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor"), class art.Test1922$Target]
+Running: [class art.Test1922$CallLockSync, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [Target("Other thread test (suspend: true): owned-monitor")]
+owner-monitors-stack-depth, This thread
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockOther]
+Owned monitors: [{ depth: 2, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 2, monitor: "NamedLock("Lock 2")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 2, monitor: "NamedLock("Lock 2")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 2, monitor: "NamedLock("Lock 2")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }, { depth: 6, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockSync, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Current thread test: owned-stack-depth")" }]
+owner-monitors-stack-depth, no suspend, other thread
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockOther]
+Owned monitors: [{ depth: 1, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 1, monitor: "NamedLock("Lock 2")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 3, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 1, monitor: "class art.Test1922$Target" }, { depth: 3, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 1, monitor: "NamedLock("Lock 2")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 3, monitor: "NamedLock("Lock 2")" }, { depth: 3, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 3, monitor: "NamedLock("Lock 2")" }, { depth: 3, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 1, monitor: "class art.Test1922$Target" }, { depth: 3, monitor: "NamedLock("Lock 2")" }, { depth: 3, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "NamedLock("Lock 2")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "class art.Test1922$Target" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 1, monitor: "class art.Test1922$Target" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 1, monitor: "class art.Test1922$Target" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "class art.Test1922$Target" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 1, monitor: "NamedLock("Lock 2")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 3, monitor: "NamedLock("Lock 2")" }, { depth: 3, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 3, monitor: "NamedLock("Lock 2")" }, { depth: 3, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 1, monitor: "class art.Test1922$Target" }, { depth: 3, monitor: "NamedLock("Lock 2")" }, { depth: 3, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 5, monitor: "NamedLock("Lock 2")" }, { depth: 5, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 5, monitor: "NamedLock("Lock 2")" }, { depth: 5, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "class art.Test1922$Target" }, { depth: 5, monitor: "NamedLock("Lock 2")" }, { depth: 5, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 5, monitor: "NamedLock("Lock 2")" }, { depth: 5, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 1, monitor: "class art.Test1922$Target" }, { depth: 5, monitor: "NamedLock("Lock 2")" }, { depth: 5, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 1, monitor: "class art.Test1922$Target" }, { depth: 5, monitor: "NamedLock("Lock 2")" }, { depth: 5, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "class art.Test1922$Target" }, { depth: 5, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 1, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 1, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 1, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 1, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 3, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }, { depth: 5, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockSync, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 1, monitor: "Target("Other thread test (suspend: false): owned-stack-depth")" }]
+owner-monitors-stack-depth, suspend, other thread
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockOther]
+Owned monitors: [{ depth: 2, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 2, monitor: "NamedLock("Lock 2")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockOther, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 2, monitor: "NamedLock("Lock 2")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockOther, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 1")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra]
+Owned monitors: [{ depth: 2, monitor: "NamedLock("Lock 2")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 4, monitor: "NamedLock("Lock 2")" }, { depth: 4, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 2")" }, { depth: 6, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }, { depth: 6, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockExtra, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "NamedLock("Lock 2")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: -1, monitor: "NamedLock("Lock 3")" }, { depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: -1, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockThisNative, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockClass]
+Owned monitors: [{ depth: 2, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockClass, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 4, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockClass, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }, { depth: 6, monitor: "class art.Test1922$Target" }]
+Running: [class art.Test1922$CallLockSync, class art.Test1922$CallLockSync, class art.Test1922$CallLockSync]
+Owned monitors: [{ depth: 2, monitor: "Target("Other thread test (suspend: true): owned-stack-depth")" }]
diff --git a/test/1922-owned-monitors-info/info.txt b/test/1922-owned-monitors-info/info.txt
new file mode 100644
index 0000000..c8c9189
--- /dev/null
+++ b/test/1922-owned-monitors-info/info.txt
@@ -0,0 +1,3 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that the GetBytecodes function works as expected.
diff --git a/test/1922-owned-monitors-info/owned_monitors.cc b/test/1922-owned-monitors-info/owned_monitors.cc
new file mode 100644
index 0000000..66a8368
--- /dev/null
+++ b/test/1922-owned-monitors-info/owned_monitors.cc
@@ -0,0 +1,144 @@
+/*
+ * 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 Test1922OwnedMonitors {
+
+static bool doMonitorEnter(JNIEnv* env, jobject target) {
+  return env->MonitorEnter(target) != 0;
+}
+static bool doMonitorExit(JNIEnv* env, jobject target) {
+  return env->MonitorExit(target) != 0;
+}
+
+static bool doCallRunnable(JNIEnv* env, jobject next) {
+  ScopedLocalRef<jclass> run_class(env, env->FindClass("java/lang/Runnable"));
+  if (run_class.get() == nullptr) {
+    return true;
+  }
+  jmethodID run = env->GetMethodID(run_class.get(), "run", "()V");
+  if (env->ExceptionCheck()) {
+    return true;
+  }
+  env->CallVoidMethod(next, run);
+  return env->ExceptionCheck();
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1922_00024Target_lockThisNative(
+    JNIEnv* env, jobject thiz, jobject next) {
+  if (doMonitorEnter(env, thiz)) {
+    return;
+  }
+  if (doCallRunnable(env, next)) {
+    return;
+  }
+  if (doMonitorExit(env, thiz)) {
+    return;
+  }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1922_00024Target_lockNative(
+    JNIEnv* env, jobject thiz ATTRIBUTE_UNUSED, jobject mon, jobject next) {
+  if (doMonitorEnter(env, mon)) {
+    return;
+  }
+  if (doCallRunnable(env, next)) {
+    return;
+  }
+  if (doMonitorExit(env, mon)) {
+    return;
+  }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1922_setupTest(JNIEnv* env, jclass) {
+  jvmtiCapabilities caps;
+  memset(&caps, 0, sizeof(caps));
+  caps.can_get_owned_monitor_info = 1;
+  caps.can_get_owned_monitor_stack_depth_info = 1;
+  JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps));
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test1922_getOwnedMonitorStackDepthInfo(
+    JNIEnv* env, jclass, jthread thread) {
+  jint len = 0;
+  jvmtiMonitorStackDepthInfo* monitors = nullptr;
+  if (JvmtiErrorToException(
+      env, jvmti_env, jvmti_env->GetOwnedMonitorStackDepthInfo(thread, &len, &monitors))) {
+    return nullptr;
+  }
+  ScopedLocalRef<jclass> ret_class(env, env->FindClass("art/Test1922$MonitorStackDepthInfo"));
+  if (ret_class.get() == nullptr) {
+    // CNFE should be pending.
+    return nullptr;
+  }
+  jmethodID constructor = env->GetMethodID(ret_class.get(), "<init>", "(ILjava/lang/Object;)V");
+  if (env->ExceptionCheck()) {
+    return nullptr;
+  }
+  return CreateObjectArray(env, len, "art/Test1922$MonitorStackDepthInfo",
+                           [&](jint i) {
+                             return env->NewObject(ret_class.get(),
+                                                   constructor,
+                                                   monitors[i].stack_depth,
+                                                   monitors[i].monitor);
+                           });
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Test1922_getOwnedMonitors(JNIEnv* env,
+                                                                             jclass,
+                                                                             jthread thread) {
+  jint len = 0;
+  jobject* arr = nullptr;
+  if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetOwnedMonitorInfo(thread, &len, &arr))) {
+    return nullptr;
+  }
+  ScopedLocalRef<jclass> obj_class(env, env->FindClass("java/lang/Object"));
+  if (obj_class.get() == nullptr) {
+    // CNFE should be pending.
+    return nullptr;
+  }
+  jobjectArray ret = env->NewObjectArray(len, obj_class.get(), nullptr);
+  if (ret == nullptr) {
+    return nullptr;
+  }
+  for (jint i = 0; i < len; i++) {
+    env->SetObjectArrayElement(ret, i, arr[i]);
+    if (env->ExceptionCheck()) {
+      return nullptr;
+    }
+  }
+  return ret;
+}
+
+}  // namespace Test1922OwnedMonitors
+}  // namespace art
diff --git a/test/1922-owned-monitors-info/run b/test/1922-owned-monitors-info/run
new file mode 100755
index 0000000..e92b873
--- /dev/null
+++ b/test/1922-owned-monitors-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/1922-owned-monitors-info/src/Main.java b/test/1922-owned-monitors-info/src/Main.java
new file mode 100644
index 0000000..2bd272f
--- /dev/null
+++ b/test/1922-owned-monitors-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.Test1922.run();
+  }
+}
diff --git a/test/1922-owned-monitors-info/src/art/Suspension.java b/test/1922-owned-monitors-info/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1922-owned-monitors-info/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/1922-owned-monitors-info/src/art/Test1922.java b/test/1922-owned-monitors-info/src/art/Test1922.java
new file mode 100644
index 0000000..82f1a6c
--- /dev/null
+++ b/test/1922-owned-monitors-info/src/art/Test1922.java
@@ -0,0 +1,357 @@
+/*
+ * 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.List;
+import java.util.ListIterator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class Test1922 {
+  // Set to true to run with all combinations of locks. This isn't really needed for the test to be
+  // useful and fully representative.
+  public final static boolean ALL_COMBOS = false;
+
+  // A runnable that lets us know when a different thread is paused.
+  public static class ThreadPauser implements Runnable {
+    private boolean suspend;
+    private volatile Thread paused_thread;
+    public ThreadPauser(boolean suspend) {
+      paused_thread = null;
+      this.suspend = suspend;
+    }
+    public ThreadPauser() {
+      paused_thread = null;
+      this.suspend = false;
+    }
+
+    @Override
+    public void run() {
+      this.paused_thread = Thread.currentThread();
+      if (suspend) {
+        Suspension.suspend(paused_thread);
+      }
+      while (this.paused_thread != null) {}
+    }
+
+    public void waitForOtherThreadToPause() {
+      while (this.paused_thread == null) {}
+      if (suspend) {
+        while (!Suspension.isSuspended(this.paused_thread)) {}
+      }
+    }
+
+    public void wakeupOtherThread() {
+      if (this.paused_thread == null) {
+        throw new Error("Other thread is not paused!");
+      }
+      if (suspend) {
+        Suspension.resume(this.paused_thread);
+        while (Suspension.isSuspended(this.paused_thread)) {}
+      }
+      this.paused_thread = null;
+    }
+  }
+
+  // A class with a number of monitor operations in its methods.
+  public static class Target {
+    public String name;
+    public Target(String name) { this.name = name; }
+    public String toString() { return "Target(\"" + name + "\")"; }
+
+    // synchronize on Target.class
+    public void lockClass(Runnable safepoint) {
+      synchronized (this.getClass()) {
+        safepoint.run();
+      }
+    }
+
+    // use java synchronized method.
+    public synchronized void lockSync(Runnable safepoint) {
+      safepoint.run();
+    }
+
+    // use java synchronized method and synchronize on another object.
+    public synchronized void lockExtra(Object l, Runnable safepoint) {
+      synchronized (l) {
+        safepoint.run();
+      }
+    }
+
+    // monitor enter the object 'l' in native code.
+    public native void lockNative(Object l, Runnable safepoint);
+
+    // monitor enter 'this' in native code.
+    public native void lockThisNative(Runnable safepoint);
+
+    // synchronize on 'l'
+    public void lockOther(Object l, Runnable safepoint) {
+      synchronized (l) {
+        safepoint.run();
+      }
+    }
+
+    // Don't do anything. Just call the next method.
+    public void callSafepoint(Runnable safepoint) {
+      safepoint.run();
+    }
+  }
+
+  // A lock with a toString.
+  public static class NamedLock {
+    public String name;
+    public NamedLock(String name) { this.name = name; }
+    public String toString() { return "NamedLock(\"" + name + "\")"; }
+  }
+
+  private static Object[] sortByString(Object[] arr) {
+    Arrays.sort(arr, (a, b) -> a.toString().compareTo(b.toString()));
+    return arr;
+  }
+
+  public static class PrintOwnedMonitorsStackDepthRunnable implements Runnable {
+    public final Thread target;
+    public PrintOwnedMonitorsStackDepthRunnable(Thread target) {
+      this.target = target;
+    }
+    public void run() {
+      System.out.println("Owned monitors: " +
+          Arrays.toString(sortByString(getOwnedMonitorStackDepthInfo(target))));
+    }
+  }
+
+  public static class PrintOwnedMonitorsRunnable implements Runnable {
+    public final Thread target;
+    public PrintOwnedMonitorsRunnable(Thread target) {
+      this.target = target;
+    }
+    public void run() {
+      System.out.println("Owned monitors: " +
+          Arrays.toString(sortByString(getOwnedMonitors(target))));
+    }
+  }
+
+  public static void run() throws Exception {
+    setupTest();
+
+    System.out.println("owner-monitors, This thread");
+    runTestsCurrentThread("owned-monitor",
+        new PrintOwnedMonitorsRunnable(Thread.currentThread()));
+
+    System.out.println("owner-monitors, no suspend, Other thread");
+    runTestsOtherThread("owned-monitor", false,
+        (t) -> { new PrintOwnedMonitorsRunnable(t).run(); });
+
+    System.out.println("owner-monitors, suspend, Other thread");
+    runTestsOtherThread("owned-monitor", true,
+        (t) -> { new PrintOwnedMonitorsRunnable(t).run(); });
+
+    System.out.println("owner-monitors-stack-depth, This thread");
+    runTestsCurrentThread("owned-stack-depth",
+        new PrintOwnedMonitorsStackDepthRunnable(Thread.currentThread()));
+
+    System.out.println("owner-monitors-stack-depth, no suspend, other thread");
+    runTestsOtherThread("owned-stack-depth", false,
+        (t) -> { new PrintOwnedMonitorsStackDepthRunnable(t).run(); });
+
+    System.out.println("owner-monitors-stack-depth, suspend, other thread");
+    runTestsOtherThread("owned-stack-depth", true,
+        (t) -> { new PrintOwnedMonitorsStackDepthRunnable(t).run(); });
+  }
+
+  public static void runTestsOtherThread(String name, boolean suspend, Consumer<Thread> printer) {
+    final Target t = new Target("Other thread test (suspend: " + suspend + "): " + name);
+    final NamedLock l1 = new NamedLock("Lock 1");
+    final NamedLock l2 = new NamedLock("Lock 2");
+    final NamedLock l3 = new NamedLock("Lock 3");
+
+    List<Function<Runnable, Runnable>> MkSafepoints = Arrays.asList(
+      (r) -> { return new CallLockOther(t, l1, r); },
+      (r) -> { return new CallLockExtra(t, l2, r); },
+      (r) -> { return new CallLockNative(t, l3, r); },
+      (r) -> { return new CallLockThisNative(t, r); },
+      (r) -> { return new CallLockClass(t, r); },
+      (r) -> { return new CallLockSync(t, r); },
+      (r) -> { return new CallSafepoint(t, r); }
+    );
+    // Use ListIterators so we can have elements in the test multiple times.
+    ListIterator<Function<Runnable, Runnable>> li1 = MkSafepoints.listIterator();
+    for (Function<Runnable, Runnable> r1 = li1.next(); li1.hasNext(); r1 = li1.next()) {
+      ListIterator<Function<Runnable, Runnable>> li2 =
+          MkSafepoints.listIterator(ALL_COMBOS ? 0 : li1.previousIndex());
+      for (Function<Runnable, Runnable> r2 = li2.next(); li2.hasNext(); r2 = li2.next()) {
+        ListIterator<Function<Runnable, Runnable>> li3 =
+            MkSafepoints.listIterator(ALL_COMBOS ? 0 : li2.previousIndex());
+        for (Function<Runnable, Runnable> r3 = li3.next(); li3.hasNext(); r3 = li3.next()) {
+          System.out.println("Running: " + Arrays.toString(
+              new Object[] {
+                r1.apply(null).getClass(),
+                r2.apply(null).getClass(),
+                r3.apply(null).getClass(),
+              }));
+          try {
+            final ThreadPauser pause = new ThreadPauser(suspend);
+            final Thread thr = new Thread(r1.apply(r2.apply(r3.apply(pause))));
+            thr.start();
+            pause.waitForOtherThreadToPause();
+            printer.accept(thr);
+            pause.wakeupOtherThread();
+            thr.join();
+          } catch (Exception e) {
+            throw new Error("Exception in test." , e);
+          }
+        }
+      }
+    }
+  }
+  public static void runTestsCurrentThread(String name, Runnable printer) {
+    final Target t = new Target("Current thread test: " + name);
+    final NamedLock l1 = new NamedLock("Lock 1");
+    final NamedLock l2 = new NamedLock("Lock 2");
+    final NamedLock l3 = new NamedLock("Lock 3");
+
+    List<Function<Runnable, Runnable>> MkSafepoints = Arrays.asList(
+      (r) -> { return new CallLockOther(t, l1, r); },
+      (r) -> { return new CallLockExtra(t, l2, r); },
+      (r) -> { return new CallLockNative(t, l3, r); },
+      (r) -> { return new CallLockThisNative(t, r); },
+      (r) -> { return new CallLockClass(t, r); },
+      (r) -> { return new CallLockSync(t, r); },
+      (r) -> { return new CallSafepoint(t, r); }
+    );
+    ListIterator<Function<Runnable, Runnable>> li1 = MkSafepoints.listIterator();
+    for (Function<Runnable, Runnable> r1 = li1.next(); li1.hasNext(); r1 = li1.next()) {
+      ListIterator<Function<Runnable, Runnable>> li2 =
+          MkSafepoints.listIterator(ALL_COMBOS ? 0 : li1.previousIndex());
+      for (Function<Runnable, Runnable> r2 = li2.next(); li2.hasNext(); r2 = li2.next()) {
+        ListIterator<Function<Runnable, Runnable>> li3 =
+            MkSafepoints.listIterator(ALL_COMBOS ? 0 : li2.previousIndex());
+        for (Function<Runnable, Runnable> r3 = li3.next(); li3.hasNext(); r3 = li3.next()) {
+          System.out.println("Running: " + Arrays.toString(
+              new Object[] {
+                r1.apply(null).getClass(),
+                r2.apply(null).getClass(),
+                r3.apply(null).getClass(),
+              }));
+          r1.apply(r2.apply(r3.apply(printer))).run();
+        }
+      }
+    }
+  }
+
+  public static native void setupTest();
+  public static native Object[] getOwnedMonitors(Thread thr);
+  public static native MonitorStackDepthInfo[] getOwnedMonitorStackDepthInfo(Thread thr);
+  public static class MonitorStackDepthInfo {
+    public final int depth;
+    public final Object monitor;
+    public MonitorStackDepthInfo(int depth, Object monitor) {
+      this.depth = depth;
+      this.monitor = monitor;
+    }
+    public String toString() {
+      return "{ depth: " + depth + ", monitor: \"" + monitor.toString() + "\" }";
+    }
+  }
+
+  // We want to avoid synthetic methods that would mess up our stack-depths so we make everything
+  // explicit here.
+  public static class CallSafepoint implements Runnable {
+    public final Target target;
+    public final Runnable safepoint;
+    public CallSafepoint(Target target, Runnable safepoint) {
+      this.target = target;
+      this.safepoint = safepoint;
+    }
+    public void run() {
+      target.callSafepoint(safepoint);
+    }
+  }
+  public static class CallLockOther implements Runnable {
+    public final Target target;
+    public final Object l;
+    public final Runnable safepoint;
+    public CallLockOther(Target target, Object l, Runnable safepoint) {
+      this.target = target;
+      this.l = l;
+      this.safepoint = safepoint;
+    }
+    public void run() {
+      target.lockOther(l, safepoint);
+    }
+  }
+  public static class CallLockExtra implements Runnable {
+    public final Target target;
+    public final Object l;
+    public final Runnable safepoint;
+    public CallLockExtra(Target target, Object l, Runnable safepoint) {
+      this.target = target;
+      this.l = l;
+      this.safepoint = safepoint;
+    }
+    public void run() {
+      target.lockExtra(l, safepoint);
+    }
+  }
+  public static class CallLockThisNative implements Runnable {
+    public final Target target;
+    public final Runnable safepoint;
+    public CallLockThisNative(Target target, Runnable safepoint) {
+      this.target = target;
+      this.safepoint = safepoint;
+    }
+    public void run() {
+      target.lockThisNative(safepoint);
+    }
+  }
+  public static class CallLockNative implements Runnable {
+    public final Target target;
+    public final Object l;
+    public final Runnable safepoint;
+    public CallLockNative(Target target, Object l, Runnable safepoint) {
+      this.target = target;
+      this.l = l;
+      this.safepoint = safepoint;
+    }
+    public void run() {
+      target.lockNative(l, safepoint);
+    }
+  }
+  public static class CallLockClass implements Runnable {
+    public final Target target;
+    public final Runnable safepoint;
+    public CallLockClass(Target target, Runnable safepoint) {
+      this.target = target;
+      this.safepoint = safepoint;
+    }
+    public void run() {
+      target.lockClass(safepoint);
+    }
+  }
+  public static class CallLockSync implements Runnable {
+    public final Target target;
+    public final Runnable safepoint;
+    public CallLockSync(Target target, Runnable safepoint) {
+      this.target = target;
+      this.safepoint = safepoint;
+    }
+    public void run() {
+      target.lockSync(safepoint);
+    }
+  }
+}
diff --git a/test/1923-frame-pop/expected.txt b/test/1923-frame-pop/expected.txt
new file mode 100644
index 0000000..7bc001d
--- /dev/null
+++ b/test/1923-frame-pop/expected.txt
@@ -0,0 +1,8 @@
+public static void art.Test1923.recurTimesA(int,java.lang.Runnable) pop. Line=44 exception:false
+Ran recurTimes(10) without errors!
+public static void art.Test1923.recurTimesF(int,java.lang.Runnable) pop. Line=84 exception:false
+Ran recurTimes(10) without errors!
+public static void art.Test1923.recurTimesK(int,java.lang.Runnable) pop. Line=124 exception:false
+Ran recurTimes(10) without errors!
+public static void art.Test1923.recurTimesF(int,java.lang.Runnable) pop. Line=83 exception:true
+Caught exception art.Test1923$RecursionError: Unable recur further. Still 90 outstanding! while running recurTimes(100)
diff --git a/test/1923-frame-pop/info.txt b/test/1923-frame-pop/info.txt
new file mode 100644
index 0000000..b4984d9
--- /dev/null
+++ b/test/1923-frame-pop/info.txt
@@ -0,0 +1,3 @@
+Tests notify frame pop JVMTI functionality.
+
+This tests the normal use case.
diff --git a/test/1923-frame-pop/run b/test/1923-frame-pop/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1923-frame-pop/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/1923-frame-pop/src/Main.java b/test/1923-frame-pop/src/Main.java
new file mode 100644
index 0000000..8881483
--- /dev/null
+++ b/test/1923-frame-pop/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.Test1923.run();
+  }
+}
diff --git a/test/1923-frame-pop/src/art/Breakpoint.java b/test/1923-frame-pop/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1923-frame-pop/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/1923-frame-pop/src/art/FramePop.java b/test/1923-frame-pop/src/art/FramePop.java
new file mode 100644
index 0000000..86bf226
--- /dev/null
+++ b/test/1923-frame-pop/src/art/FramePop.java
@@ -0,0 +1,25 @@
+/*
+ * 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.Method;
+
+public class FramePop {
+  public static native void enableFramePopEvent(Class klass, Method method, Thread thr)
+      throws Exception;
+  public static native void notifyFramePop(Thread target, int depth) throws Exception;
+}
diff --git a/test/1923-frame-pop/src/art/Locals.java b/test/1923-frame-pop/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1923-frame-pop/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * 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.Objects;
+
+public class Locals {
+  public static native void EnableLocalVariableAccess();
+
+  public static class VariableDescription {
+    public final long start_location;
+    public final int length;
+    public final String name;
+    public final String signature;
+    public final String generic_signature;
+    public final int slot;
+
+    public VariableDescription(
+        long start, int length, String name, String sig, String gen_sig, int slot) {
+      this.start_location = start;
+      this.length = length;
+      this.name = name;
+      this.signature = sig;
+      this.generic_signature = gen_sig;
+      this.slot = slot;
+    }
+
+    @Override
+    public String toString() {
+      return String.format(
+          "VariableDescription { " +
+            "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+          "}",
+          this.signature,
+          this.name,
+          this.generic_signature,
+          this.slot,
+          this.start_location,
+          this.length);
+    }
+    public boolean equals(Object other) {
+      if (!(other instanceof VariableDescription)) {
+        return false;
+      } else {
+        VariableDescription v = (VariableDescription)other;
+        return Objects.equals(v.signature, signature) &&
+            Objects.equals(v.name, name) &&
+            Objects.equals(v.generic_signature, generic_signature) &&
+            v.slot == slot &&
+            v.start_location == start_location &&
+            v.length == length;
+      }
+    }
+    public int hashCode() {
+      return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+          this.start_location, this.length);
+    }
+  }
+
+  public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+  public static VariableDescription GetVariableAtLine(
+      Executable e, String name, String sig, int line) throws Exception {
+    return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+  }
+
+  public static VariableDescription GetVariableAtLocation(
+      Executable e, String name, String sig, long loc) {
+    VariableDescription[] vars = GetLocalVariableTable(e);
+    for (VariableDescription var : vars) {
+      if (var.start_location <= loc &&
+          var.length + var.start_location > loc &&
+          var.name.equals(name) &&
+          var.signature.equals(sig)) {
+        return var;
+      }
+    }
+    throw new Error(
+        "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+  }
+
+  public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+  public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+  public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+  public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+  public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+  public static native Object GetLocalInstance(Thread thr, int depth);
+
+  public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+  }
+  public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+  }
+  public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+  }
+  public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+  }
+  public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+  public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+  public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+  public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+  public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1923-frame-pop/src/art/StackTrace.java b/test/1923-frame-pop/src/art/StackTrace.java
new file mode 100644
index 0000000..2ea2f20
--- /dev/null
+++ b/test/1923-frame-pop/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * 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()) &&
+        !Suspension.isSuspended(thr);
+    if (suspend_thread) {
+      Suspension.suspend(thr);
+    }
+    StackFrameData[] out = nativeGetStackTrace(thr);
+    if (suspend_thread) {
+      Suspension.resume(thr);
+    }
+    return out;
+  }
+}
+
diff --git a/test/1923-frame-pop/src/art/Suspension.java b/test/1923-frame-pop/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1923-frame-pop/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/1923-frame-pop/src/art/Test1923.java b/test/1923-frame-pop/src/art/Test1923.java
new file mode 100644
index 0000000..b5265b7
--- /dev/null
+++ b/test/1923-frame-pop/src/art/Test1923.java
@@ -0,0 +1,209 @@
+/*
+ * 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.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.function.IntUnaryOperator;
+import java.util.function.Function;
+
+public class Test1923 {
+  public static void handleFramePop(Executable m, boolean exception, long location) {
+    System.out.println(
+        m + " pop. Line=" + Breakpoint.locationToLine(m, location) + " exception:" + exception);
+  }
+
+  public static void recurTimesA(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesB(times - 1, safepoint);
+  }
+
+  public static void recurTimesB(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesC(times - 1, safepoint);
+  }
+
+  public static void recurTimesC(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesD(times - 1, safepoint);
+  }
+
+  public static void recurTimesD(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesE(times - 1, safepoint);
+  }
+
+  public static void recurTimesE(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesF(times - 1, safepoint);
+  }
+
+  public static void recurTimesF(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesG(times - 1, safepoint);
+  }
+
+  public static void recurTimesG(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesH(times - 1, safepoint);
+  }
+
+  public static void recurTimesH(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesI(times - 1, safepoint);
+  }
+
+  public static void recurTimesI(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesJ(times - 1, safepoint);
+  }
+
+  public static void recurTimesJ(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesK(times - 1, safepoint);
+  }
+
+  public static class RecursionError extends Error {
+    public RecursionError(String s) { super(s); }
+  }
+  public static void recurTimesK(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    safepoint.run();
+    throw new RecursionError("Unable recur further. Still " + times + " outstanding!");
+  }
+
+  public static class ThreadPauser implements Runnable {
+    public final Semaphore sem_wakeup_main;
+    public final Semaphore sem_wait;
+
+    public ThreadPauser() {
+      sem_wakeup_main = new Semaphore(0);
+      sem_wait = new Semaphore(0);
+    }
+
+    public void run() {
+      try {
+        sem_wakeup_main.release();
+        sem_wait.acquire();
+      } catch (Exception e) {
+        throw new Error("Error with semaphores!", e);
+      }
+    }
+
+    public void waitForOtherThreadToPause() throws Exception {
+      sem_wakeup_main.acquire();
+    }
+
+    public void wakeupOtherThread() throws Exception {
+      sem_wait.release();
+    }
+  }
+
+  public static void doRecurTestWith(final int times, int watch_frame) throws Exception {
+    final String target_method_name_start = "recurTimes";
+    final ThreadPauser safepoint = new ThreadPauser();
+    Thread target = new Thread(() -> {
+      try {
+        recurTimesA(times, safepoint);
+        System.out.println("Ran recurTimes(" + times + ") without errors!");
+      } catch (RecursionError e) {
+        System.out.println("Caught exception " + e + " while running recurTimes(" + times + ")");
+      }
+    });
+    target.start();
+    safepoint.waitForOtherThreadToPause();
+    Suspension.suspend(target);
+    // Safe block
+    int cnt = 0;
+    StackTrace.StackFrameData target_frame = null;
+    for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(target)) {
+      if (frame.method.getName().startsWith(target_method_name_start)) {
+        if (times - cnt == watch_frame) {
+          target_frame = frame;
+          break;
+        } else {
+          cnt++;
+        }
+      }
+    }
+    if (target_frame != null) {
+      FramePop.notifyFramePop(target, target_frame.depth);
+    } else {
+      System.out.println(
+          "Unable to find stack frame for " + watch_frame + " depth of "
+          + target_method_name_start);
+    }
+    Suspension.resume(target);
+    safepoint.wakeupOtherThread();
+    target.join();
+  }
+
+  public static void run() throws Exception {
+    // TODO Investigate what thread argument means for FramePop event enable.
+    // Listen for events on all threads.
+    FramePop.enableFramePopEvent(
+        Test1923.class,
+        Test1923.class.getDeclaredMethod(
+            "handleFramePop", Executable.class, Boolean.TYPE, Long.TYPE),
+        null);
+    doRecurTestWith(10, 0);
+    doRecurTestWith(10, 5);
+    doRecurTestWith(10, 10);
+    doRecurTestWith(100, 95);
+  }
+}
diff --git a/test/1923-frame-pop/src/art/Trace.java b/test/1923-frame-pop/src/art/Trace.java
new file mode 100644
index 0000000..ba3d397
--- /dev/null
+++ b/test/1923-frame-pop/src/art/Trace.java
@@ -0,0 +1,56 @@
+/*
+ * 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 Trace {
+  public static native void enableTracing(Class<?> methodClass,
+                                          Method entryMethod,
+                                          Method exitMethod,
+                                          Method fieldAccess,
+                                          Method fieldModify,
+                                          Method singleStep,
+                                          Thread thr);
+  public static native void disableTracing(Thread thr);
+
+  public static void enableFieldTracing(Class<?> methodClass,
+                                        Method fieldAccess,
+                                        Method fieldModify,
+                                        Thread thr) {
+    enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
+  }
+
+  public static void enableMethodTracing(Class<?> methodClass,
+                                         Method entryMethod,
+                                         Method exitMethod,
+                                         Thread thr) {
+    enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+  }
+
+  public static void enableSingleStepTracing(Class<?> methodClass,
+                                             Method singleStep,
+                                             Thread thr) {
+    enableTracing(methodClass, null, null, null, null, singleStep, thr);
+  }
+
+  public static native void watchFieldAccess(Field f);
+  public static native void watchFieldModification(Field f);
+  public static native void watchAllFieldAccesses();
+  public static native void watchAllFieldModifications();
+}
diff --git a/test/1924-frame-pop-toggle/expected.txt b/test/1924-frame-pop-toggle/expected.txt
new file mode 100644
index 0000000..64ca9db
--- /dev/null
+++ b/test/1924-frame-pop-toggle/expected.txt
@@ -0,0 +1,8 @@
+public static void art.Test1924.recurTimesA(int,java.lang.Runnable) pop. Line=44 exception:false
+Ran recurTimes(10) without errors!
+public static void art.Test1924.recurTimesF(int,java.lang.Runnable) pop. Line=84 exception:false
+Ran recurTimes(10) without errors!
+public static void art.Test1924.recurTimesK(int,java.lang.Runnable) pop. Line=124 exception:false
+Ran recurTimes(10) without errors!
+public static void art.Test1924.recurTimesF(int,java.lang.Runnable) pop. Line=83 exception:true
+Caught exception art.Test1924$RecursionError: Unable recur further. Still 90 outstanding! while running recurTimes(100)
diff --git a/test/1924-frame-pop-toggle/frame_pop_toggle.cc b/test/1924-frame-pop-toggle/frame_pop_toggle.cc
new file mode 100644
index 0000000..1fb7a1f
--- /dev/null
+++ b/test/1924-frame-pop-toggle/frame_pop_toggle.cc
@@ -0,0 +1,51 @@
+/*
+ * 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 Test1924FramePop {
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1924_toggleFramePop(
+    JNIEnv* env, jclass, jthread thr) {
+  JvmtiErrorToException(env,
+                        jvmti_env,
+                        jvmti_env->SetEventNotificationMode(
+                            JVMTI_DISABLE, JVMTI_EVENT_FRAME_POP, thr));
+  JvmtiErrorToException(env,
+                        jvmti_env,
+                        jvmti_env->SetEventNotificationMode(
+                            JVMTI_ENABLE, JVMTI_EVENT_FRAME_POP, thr));
+}
+
+}  // namespace Test1924FramePop
+}  // namespace art
+
diff --git a/test/1924-frame-pop-toggle/info.txt b/test/1924-frame-pop-toggle/info.txt
new file mode 100644
index 0000000..1f7480f
--- /dev/null
+++ b/test/1924-frame-pop-toggle/info.txt
@@ -0,0 +1,3 @@
+Tests notify frame pop JVMTI functionality.
+
+This tests toggling frame pop off and on.
diff --git a/test/1924-frame-pop-toggle/run b/test/1924-frame-pop-toggle/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1924-frame-pop-toggle/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/1924-frame-pop-toggle/src/Main.java b/test/1924-frame-pop-toggle/src/Main.java
new file mode 100644
index 0000000..f48fc2d
--- /dev/null
+++ b/test/1924-frame-pop-toggle/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.Test1924.run();
+  }
+}
diff --git a/test/1924-frame-pop-toggle/src/art/Breakpoint.java b/test/1924-frame-pop-toggle/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1924-frame-pop-toggle/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/1924-frame-pop-toggle/src/art/FramePop.java b/test/1924-frame-pop-toggle/src/art/FramePop.java
new file mode 100644
index 0000000..86bf226
--- /dev/null
+++ b/test/1924-frame-pop-toggle/src/art/FramePop.java
@@ -0,0 +1,25 @@
+/*
+ * 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.Method;
+
+public class FramePop {
+  public static native void enableFramePopEvent(Class klass, Method method, Thread thr)
+      throws Exception;
+  public static native void notifyFramePop(Thread target, int depth) throws Exception;
+}
diff --git a/test/1924-frame-pop-toggle/src/art/Locals.java b/test/1924-frame-pop-toggle/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1924-frame-pop-toggle/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * 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.Objects;
+
+public class Locals {
+  public static native void EnableLocalVariableAccess();
+
+  public static class VariableDescription {
+    public final long start_location;
+    public final int length;
+    public final String name;
+    public final String signature;
+    public final String generic_signature;
+    public final int slot;
+
+    public VariableDescription(
+        long start, int length, String name, String sig, String gen_sig, int slot) {
+      this.start_location = start;
+      this.length = length;
+      this.name = name;
+      this.signature = sig;
+      this.generic_signature = gen_sig;
+      this.slot = slot;
+    }
+
+    @Override
+    public String toString() {
+      return String.format(
+          "VariableDescription { " +
+            "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+          "}",
+          this.signature,
+          this.name,
+          this.generic_signature,
+          this.slot,
+          this.start_location,
+          this.length);
+    }
+    public boolean equals(Object other) {
+      if (!(other instanceof VariableDescription)) {
+        return false;
+      } else {
+        VariableDescription v = (VariableDescription)other;
+        return Objects.equals(v.signature, signature) &&
+            Objects.equals(v.name, name) &&
+            Objects.equals(v.generic_signature, generic_signature) &&
+            v.slot == slot &&
+            v.start_location == start_location &&
+            v.length == length;
+      }
+    }
+    public int hashCode() {
+      return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+          this.start_location, this.length);
+    }
+  }
+
+  public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+  public static VariableDescription GetVariableAtLine(
+      Executable e, String name, String sig, int line) throws Exception {
+    return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+  }
+
+  public static VariableDescription GetVariableAtLocation(
+      Executable e, String name, String sig, long loc) {
+    VariableDescription[] vars = GetLocalVariableTable(e);
+    for (VariableDescription var : vars) {
+      if (var.start_location <= loc &&
+          var.length + var.start_location > loc &&
+          var.name.equals(name) &&
+          var.signature.equals(sig)) {
+        return var;
+      }
+    }
+    throw new Error(
+        "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+  }
+
+  public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+  public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+  public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+  public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+  public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+  public static native Object GetLocalInstance(Thread thr, int depth);
+
+  public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+  }
+  public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+  }
+  public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+  }
+  public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+  }
+  public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+  public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+  public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+  public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+  public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1924-frame-pop-toggle/src/art/StackTrace.java b/test/1924-frame-pop-toggle/src/art/StackTrace.java
new file mode 100644
index 0000000..2ea2f20
--- /dev/null
+++ b/test/1924-frame-pop-toggle/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * 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()) &&
+        !Suspension.isSuspended(thr);
+    if (suspend_thread) {
+      Suspension.suspend(thr);
+    }
+    StackFrameData[] out = nativeGetStackTrace(thr);
+    if (suspend_thread) {
+      Suspension.resume(thr);
+    }
+    return out;
+  }
+}
+
diff --git a/test/1924-frame-pop-toggle/src/art/Suspension.java b/test/1924-frame-pop-toggle/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1924-frame-pop-toggle/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/1924-frame-pop-toggle/src/art/Test1924.java b/test/1924-frame-pop-toggle/src/art/Test1924.java
new file mode 100644
index 0000000..0ffbfc6
--- /dev/null
+++ b/test/1924-frame-pop-toggle/src/art/Test1924.java
@@ -0,0 +1,212 @@
+/*
+ * 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.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.function.IntUnaryOperator;
+import java.util.function.Function;
+
+public class Test1924 {
+  public static void handleFramePop(Executable m, boolean exception, long location) {
+    System.out.println(
+        m + " pop. Line=" + Breakpoint.locationToLine(m, location) + " exception:" + exception);
+  }
+
+  public static void recurTimesA(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesB(times - 1, safepoint);
+  }
+
+  public static void recurTimesB(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesC(times - 1, safepoint);
+  }
+
+  public static void recurTimesC(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesD(times - 1, safepoint);
+  }
+
+  public static void recurTimesD(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesE(times - 1, safepoint);
+  }
+
+  public static void recurTimesE(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesF(times - 1, safepoint);
+  }
+
+  public static void recurTimesF(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesG(times - 1, safepoint);
+  }
+
+  public static void recurTimesG(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesH(times - 1, safepoint);
+  }
+
+  public static void recurTimesH(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesI(times - 1, safepoint);
+  }
+
+  public static void recurTimesI(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesJ(times - 1, safepoint);
+  }
+
+  public static void recurTimesJ(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesK(times - 1, safepoint);
+  }
+
+  public static class RecursionError extends Error {
+    public RecursionError(String s) { super(s); }
+  }
+  public static void recurTimesK(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    safepoint.run();
+    throw new RecursionError("Unable recur further. Still " + times + " outstanding!");
+  }
+
+  public static class ThreadPauser implements Runnable {
+    public final Semaphore sem_wakeup_main;
+    public final Semaphore sem_wait;
+
+    public ThreadPauser() {
+      sem_wakeup_main = new Semaphore(0);
+      sem_wait = new Semaphore(0);
+    }
+
+    public void run() {
+      try {
+        sem_wakeup_main.release();
+        sem_wait.acquire();
+      } catch (Exception e) {
+        throw new Error("Error with semaphores!", e);
+      }
+    }
+
+    public void waitForOtherThreadToPause() throws Exception {
+      sem_wakeup_main.acquire();
+    }
+
+    public void wakeupOtherThread() throws Exception {
+      sem_wait.release();
+    }
+  }
+
+  public static void doRecurTestWith(final int times, int watch_frame) throws Exception {
+    final String target_method_name_start = "recurTimes";
+    final ThreadPauser safepoint = new ThreadPauser();
+    Thread target = new Thread(() -> {
+      try {
+        recurTimesA(times, safepoint);
+        System.out.println("Ran recurTimes(" + times + ") without errors!");
+      } catch (RecursionError e) {
+        System.out.println("Caught exception " + e + " while running recurTimes(" + times + ")");
+      }
+    });
+    target.start();
+    safepoint.waitForOtherThreadToPause();
+    Suspension.suspend(target);
+    // Safe block
+    int cnt = 0;
+    StackTrace.StackFrameData target_frame = null;
+    for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(target)) {
+      if (frame.method.getName().startsWith(target_method_name_start)) {
+        if (times - cnt == watch_frame) {
+          target_frame = frame;
+          break;
+        } else {
+          cnt++;
+        }
+      }
+    }
+    if (target_frame != null) {
+      FramePop.notifyFramePop(target, target_frame.depth);
+    } else {
+      System.out.println(
+          "Unable to find stack frame for " + watch_frame + " depth of "
+          + target_method_name_start);
+    }
+    Suspension.resume(target);
+    toggleFramePop(null);
+    safepoint.wakeupOtherThread();
+    target.join();
+  }
+
+  public static void run() throws Exception {
+    // TODO Investigate what thread argument means for FramePop event enable.
+    // Listen for events on all threads.
+    FramePop.enableFramePopEvent(
+        Test1924.class,
+        Test1924.class.getDeclaredMethod(
+            "handleFramePop", Executable.class, Boolean.TYPE, Long.TYPE),
+        null);
+    doRecurTestWith(10, 0);
+    doRecurTestWith(10, 5);
+    doRecurTestWith(10, 10);
+    doRecurTestWith(100, 95);
+  }
+
+  public static native void toggleFramePop(Thread thr);
+}
diff --git a/test/1924-frame-pop-toggle/src/art/Trace.java b/test/1924-frame-pop-toggle/src/art/Trace.java
new file mode 100644
index 0000000..ba3d397
--- /dev/null
+++ b/test/1924-frame-pop-toggle/src/art/Trace.java
@@ -0,0 +1,56 @@
+/*
+ * 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 Trace {
+  public static native void enableTracing(Class<?> methodClass,
+                                          Method entryMethod,
+                                          Method exitMethod,
+                                          Method fieldAccess,
+                                          Method fieldModify,
+                                          Method singleStep,
+                                          Thread thr);
+  public static native void disableTracing(Thread thr);
+
+  public static void enableFieldTracing(Class<?> methodClass,
+                                        Method fieldAccess,
+                                        Method fieldModify,
+                                        Thread thr) {
+    enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
+  }
+
+  public static void enableMethodTracing(Class<?> methodClass,
+                                         Method entryMethod,
+                                         Method exitMethod,
+                                         Thread thr) {
+    enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+  }
+
+  public static void enableSingleStepTracing(Class<?> methodClass,
+                                             Method singleStep,
+                                             Thread thr) {
+    enableTracing(methodClass, null, null, null, null, singleStep, thr);
+  }
+
+  public static native void watchFieldAccess(Field f);
+  public static native void watchFieldModification(Field f);
+  public static native void watchAllFieldAccesses();
+  public static native void watchAllFieldModifications();
+}
diff --git a/test/1925-self-frame-pop/expected.txt b/test/1925-self-frame-pop/expected.txt
new file mode 100644
index 0000000..f154205
--- /dev/null
+++ b/test/1925-self-frame-pop/expected.txt
@@ -0,0 +1,4 @@
+public static void art.Test1925.recurTimesE(int,java.lang.Runnable) pop. Line=76 exception:false
+Ran recurTimes(10) without errors!
+public static void art.Test1925.recurTimesE(int,java.lang.Runnable) pop. Line=75 exception:true
+Caught exception art.Test1925$RecursionError: Unable recur further. Still 90 outstanding! while running recurTimes(100)
diff --git a/test/1925-self-frame-pop/info.txt b/test/1925-self-frame-pop/info.txt
new file mode 100644
index 0000000..ccca498
--- /dev/null
+++ b/test/1925-self-frame-pop/info.txt
@@ -0,0 +1,3 @@
+Tests notify frame pop JVMTI functionality.
+
+This tests setting frame-pop on the current thread
diff --git a/test/1925-self-frame-pop/run b/test/1925-self-frame-pop/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1925-self-frame-pop/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/1925-self-frame-pop/src/Main.java b/test/1925-self-frame-pop/src/Main.java
new file mode 100644
index 0000000..2761aef
--- /dev/null
+++ b/test/1925-self-frame-pop/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.Test1925.run();
+  }
+}
diff --git a/test/1925-self-frame-pop/src/art/Breakpoint.java b/test/1925-self-frame-pop/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1925-self-frame-pop/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/1925-self-frame-pop/src/art/FramePop.java b/test/1925-self-frame-pop/src/art/FramePop.java
new file mode 100644
index 0000000..86bf226
--- /dev/null
+++ b/test/1925-self-frame-pop/src/art/FramePop.java
@@ -0,0 +1,25 @@
+/*
+ * 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.Method;
+
+public class FramePop {
+  public static native void enableFramePopEvent(Class klass, Method method, Thread thr)
+      throws Exception;
+  public static native void notifyFramePop(Thread target, int depth) throws Exception;
+}
diff --git a/test/1925-self-frame-pop/src/art/Locals.java b/test/1925-self-frame-pop/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1925-self-frame-pop/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * 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.Objects;
+
+public class Locals {
+  public static native void EnableLocalVariableAccess();
+
+  public static class VariableDescription {
+    public final long start_location;
+    public final int length;
+    public final String name;
+    public final String signature;
+    public final String generic_signature;
+    public final int slot;
+
+    public VariableDescription(
+        long start, int length, String name, String sig, String gen_sig, int slot) {
+      this.start_location = start;
+      this.length = length;
+      this.name = name;
+      this.signature = sig;
+      this.generic_signature = gen_sig;
+      this.slot = slot;
+    }
+
+    @Override
+    public String toString() {
+      return String.format(
+          "VariableDescription { " +
+            "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+          "}",
+          this.signature,
+          this.name,
+          this.generic_signature,
+          this.slot,
+          this.start_location,
+          this.length);
+    }
+    public boolean equals(Object other) {
+      if (!(other instanceof VariableDescription)) {
+        return false;
+      } else {
+        VariableDescription v = (VariableDescription)other;
+        return Objects.equals(v.signature, signature) &&
+            Objects.equals(v.name, name) &&
+            Objects.equals(v.generic_signature, generic_signature) &&
+            v.slot == slot &&
+            v.start_location == start_location &&
+            v.length == length;
+      }
+    }
+    public int hashCode() {
+      return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+          this.start_location, this.length);
+    }
+  }
+
+  public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+  public static VariableDescription GetVariableAtLine(
+      Executable e, String name, String sig, int line) throws Exception {
+    return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+  }
+
+  public static VariableDescription GetVariableAtLocation(
+      Executable e, String name, String sig, long loc) {
+    VariableDescription[] vars = GetLocalVariableTable(e);
+    for (VariableDescription var : vars) {
+      if (var.start_location <= loc &&
+          var.length + var.start_location > loc &&
+          var.name.equals(name) &&
+          var.signature.equals(sig)) {
+        return var;
+      }
+    }
+    throw new Error(
+        "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+  }
+
+  public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+  public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+  public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+  public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+  public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+  public static native Object GetLocalInstance(Thread thr, int depth);
+
+  public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+  }
+  public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+  }
+  public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+  }
+  public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+  }
+  public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+  public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+  public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+  public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+  public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1925-self-frame-pop/src/art/StackTrace.java b/test/1925-self-frame-pop/src/art/StackTrace.java
new file mode 100644
index 0000000..2ea2f20
--- /dev/null
+++ b/test/1925-self-frame-pop/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * 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()) &&
+        !Suspension.isSuspended(thr);
+    if (suspend_thread) {
+      Suspension.suspend(thr);
+    }
+    StackFrameData[] out = nativeGetStackTrace(thr);
+    if (suspend_thread) {
+      Suspension.resume(thr);
+    }
+    return out;
+  }
+}
+
diff --git a/test/1925-self-frame-pop/src/art/Suspension.java b/test/1925-self-frame-pop/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1925-self-frame-pop/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/1925-self-frame-pop/src/art/Test1925.java b/test/1925-self-frame-pop/src/art/Test1925.java
new file mode 100644
index 0000000..b413d06
--- /dev/null
+++ b/test/1925-self-frame-pop/src/art/Test1925.java
@@ -0,0 +1,168 @@
+/*
+ * 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.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.function.IntUnaryOperator;
+import java.util.function.Function;
+
+public class Test1925 {
+  public static void handleFramePop(Executable m, boolean exception, long location) {
+    System.out.println(
+        m + " pop. Line=" + Breakpoint.locationToLine(m, location) + " exception:" + exception);
+  }
+
+  public static void recurTimesA(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesB(times - 1, safepoint);
+  }
+
+  public static void recurTimesB(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesC(times - 1, safepoint);
+  }
+
+  public static void recurTimesC(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesD(times - 1, safepoint);
+  }
+
+  public static void recurTimesD(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesE(times - 1, safepoint);
+  }
+
+  public static void recurTimesE(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesF(times - 1, safepoint);
+  }
+
+  public static void recurTimesF(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesG(times - 1, safepoint);
+  }
+
+  public static void recurTimesG(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesH(times - 1, safepoint);
+  }
+
+  public static void recurTimesH(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesI(times - 1, safepoint);
+  }
+
+  public static void recurTimesI(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesJ(times - 1, safepoint);
+  }
+
+  public static void recurTimesJ(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesK(times - 1, safepoint);
+  }
+
+  public static class RecursionError extends Error {
+    public RecursionError(String s) { super(s); }
+  }
+  public static void recurTimesK(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    safepoint.run();
+    throw new RecursionError("Unable recur further. Still " + times + " outstanding!");
+  }
+
+  public static void doRecurTestWith(final int times, int watch_frame) throws Exception {
+    final String target_method_name_start = "recurTimes";
+    final Runnable safepoint = () -> {
+      StackTrace.StackFrameData target_frame = null;
+      int cnt = 0;
+      for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(Thread.currentThread())) {
+        if (frame.method.getName().startsWith(target_method_name_start)) {
+          if (times - cnt == watch_frame) {
+            target_frame = frame;
+            break;
+          } else {
+            cnt++;
+          }
+        }
+      }
+      try {
+        FramePop.notifyFramePop(null, target_frame.depth);
+      } catch (Exception e) {
+        throw new Error("Unexpected error in notifyFramePop!", e);
+      }
+    };
+    try {
+      recurTimesA(times, safepoint);
+      System.out.println("Ran recurTimes(" + times + ") without errors!");
+    } catch (Throwable e) {
+      System.out.println("Caught exception " + e + " while running recurTimes(" + times + ")");
+    }
+  }
+
+  public static void run() throws Exception {
+    FramePop.enableFramePopEvent(
+        Test1925.class,
+        Test1925.class.getDeclaredMethod(
+            "handleFramePop", Executable.class, Boolean.TYPE, Long.TYPE),
+        null);
+    doRecurTestWith(10, 5);
+    doRecurTestWith(100, 95);
+  }
+}
diff --git a/test/1925-self-frame-pop/src/art/Trace.java b/test/1925-self-frame-pop/src/art/Trace.java
new file mode 100644
index 0000000..ba3d397
--- /dev/null
+++ b/test/1925-self-frame-pop/src/art/Trace.java
@@ -0,0 +1,56 @@
+/*
+ * 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 Trace {
+  public static native void enableTracing(Class<?> methodClass,
+                                          Method entryMethod,
+                                          Method exitMethod,
+                                          Method fieldAccess,
+                                          Method fieldModify,
+                                          Method singleStep,
+                                          Thread thr);
+  public static native void disableTracing(Thread thr);
+
+  public static void enableFieldTracing(Class<?> methodClass,
+                                        Method fieldAccess,
+                                        Method fieldModify,
+                                        Thread thr) {
+    enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
+  }
+
+  public static void enableMethodTracing(Class<?> methodClass,
+                                         Method entryMethod,
+                                         Method exitMethod,
+                                         Thread thr) {
+    enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+  }
+
+  public static void enableSingleStepTracing(Class<?> methodClass,
+                                             Method singleStep,
+                                             Thread thr) {
+    enableTracing(methodClass, null, null, null, null, singleStep, thr);
+  }
+
+  public static native void watchFieldAccess(Field f);
+  public static native void watchFieldModification(Field f);
+  public static native void watchAllFieldAccesses();
+  public static native void watchAllFieldModifications();
+}
diff --git a/test/1926-missed-frame-pop/expected.txt b/test/1926-missed-frame-pop/expected.txt
new file mode 100644
index 0000000..20e723f
--- /dev/null
+++ b/test/1926-missed-frame-pop/expected.txt
@@ -0,0 +1,9 @@
+Ran recurTimes(10) without errors after disabling frame pop event!
+renabling frame pop event with similar stack.
+Ran recurTimes(10) without errors!
+Ran recurTimes(10) without errors after disabling frame pop event!
+renabling frame pop event with similar stack.
+Ran recurTimes(10) without errors!
+Ran recurTimes(10) without errors after disabling frame pop event!
+renabling frame pop event with similar stack.
+Ran recurTimes(10) without errors!
diff --git a/test/1926-missed-frame-pop/frame_pop_missed.cc b/test/1926-missed-frame-pop/frame_pop_missed.cc
new file mode 100644
index 0000000..c5104de
--- /dev/null
+++ b/test/1926-missed-frame-pop/frame_pop_missed.cc
@@ -0,0 +1,55 @@
+/*
+ * 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 Test1926FramePopMissed {
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1926_disableFramePop(
+    JNIEnv* env, jclass, jthread thr) {
+  JvmtiErrorToException(env,
+                        jvmti_env,
+                        jvmti_env->SetEventNotificationMode(
+                            JVMTI_DISABLE, JVMTI_EVENT_FRAME_POP, thr));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test1926_reenableFramePop(
+    JNIEnv* env, jclass, jthread thr) {
+  JvmtiErrorToException(env,
+                        jvmti_env,
+                        jvmti_env->SetEventNotificationMode(
+                            JVMTI_ENABLE, JVMTI_EVENT_FRAME_POP, thr));
+}
+
+}  // namespace Test1926FramePopMissed
+}  // namespace art
+
diff --git a/test/1926-missed-frame-pop/info.txt b/test/1926-missed-frame-pop/info.txt
new file mode 100644
index 0000000..b4984d9
--- /dev/null
+++ b/test/1926-missed-frame-pop/info.txt
@@ -0,0 +1,3 @@
+Tests notify frame pop JVMTI functionality.
+
+This tests the normal use case.
diff --git a/test/1926-missed-frame-pop/run b/test/1926-missed-frame-pop/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1926-missed-frame-pop/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/1926-missed-frame-pop/src/Main.java b/test/1926-missed-frame-pop/src/Main.java
new file mode 100644
index 0000000..f924079
--- /dev/null
+++ b/test/1926-missed-frame-pop/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.Test1926.run();
+  }
+}
diff --git a/test/1926-missed-frame-pop/src/art/Breakpoint.java b/test/1926-missed-frame-pop/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1926-missed-frame-pop/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/1926-missed-frame-pop/src/art/FramePop.java b/test/1926-missed-frame-pop/src/art/FramePop.java
new file mode 100644
index 0000000..86bf226
--- /dev/null
+++ b/test/1926-missed-frame-pop/src/art/FramePop.java
@@ -0,0 +1,25 @@
+/*
+ * 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.Method;
+
+public class FramePop {
+  public static native void enableFramePopEvent(Class klass, Method method, Thread thr)
+      throws Exception;
+  public static native void notifyFramePop(Thread target, int depth) throws Exception;
+}
diff --git a/test/1926-missed-frame-pop/src/art/Locals.java b/test/1926-missed-frame-pop/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1926-missed-frame-pop/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * 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.Objects;
+
+public class Locals {
+  public static native void EnableLocalVariableAccess();
+
+  public static class VariableDescription {
+    public final long start_location;
+    public final int length;
+    public final String name;
+    public final String signature;
+    public final String generic_signature;
+    public final int slot;
+
+    public VariableDescription(
+        long start, int length, String name, String sig, String gen_sig, int slot) {
+      this.start_location = start;
+      this.length = length;
+      this.name = name;
+      this.signature = sig;
+      this.generic_signature = gen_sig;
+      this.slot = slot;
+    }
+
+    @Override
+    public String toString() {
+      return String.format(
+          "VariableDescription { " +
+            "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+          "}",
+          this.signature,
+          this.name,
+          this.generic_signature,
+          this.slot,
+          this.start_location,
+          this.length);
+    }
+    public boolean equals(Object other) {
+      if (!(other instanceof VariableDescription)) {
+        return false;
+      } else {
+        VariableDescription v = (VariableDescription)other;
+        return Objects.equals(v.signature, signature) &&
+            Objects.equals(v.name, name) &&
+            Objects.equals(v.generic_signature, generic_signature) &&
+            v.slot == slot &&
+            v.start_location == start_location &&
+            v.length == length;
+      }
+    }
+    public int hashCode() {
+      return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+          this.start_location, this.length);
+    }
+  }
+
+  public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+  public static VariableDescription GetVariableAtLine(
+      Executable e, String name, String sig, int line) throws Exception {
+    return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+  }
+
+  public static VariableDescription GetVariableAtLocation(
+      Executable e, String name, String sig, long loc) {
+    VariableDescription[] vars = GetLocalVariableTable(e);
+    for (VariableDescription var : vars) {
+      if (var.start_location <= loc &&
+          var.length + var.start_location > loc &&
+          var.name.equals(name) &&
+          var.signature.equals(sig)) {
+        return var;
+      }
+    }
+    throw new Error(
+        "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+  }
+
+  public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+  public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+  public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+  public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+  public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+  public static native Object GetLocalInstance(Thread thr, int depth);
+
+  public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+  }
+  public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+  }
+  public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+  }
+  public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+  }
+  public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+  public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+  public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+  public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+  public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1926-missed-frame-pop/src/art/StackTrace.java b/test/1926-missed-frame-pop/src/art/StackTrace.java
new file mode 100644
index 0000000..2ea2f20
--- /dev/null
+++ b/test/1926-missed-frame-pop/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * 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()) &&
+        !Suspension.isSuspended(thr);
+    if (suspend_thread) {
+      Suspension.suspend(thr);
+    }
+    StackFrameData[] out = nativeGetStackTrace(thr);
+    if (suspend_thread) {
+      Suspension.resume(thr);
+    }
+    return out;
+  }
+}
+
diff --git a/test/1926-missed-frame-pop/src/art/Suspension.java b/test/1926-missed-frame-pop/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1926-missed-frame-pop/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/1926-missed-frame-pop/src/art/Test1926.java b/test/1926-missed-frame-pop/src/art/Test1926.java
new file mode 100644
index 0000000..cb21072
--- /dev/null
+++ b/test/1926-missed-frame-pop/src/art/Test1926.java
@@ -0,0 +1,222 @@
+/*
+ * 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.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.function.IntUnaryOperator;
+import java.util.function.Function;
+
+public class Test1926 {
+  public static void handleFramePop(Executable m, boolean exception, long location) {
+    System.out.println(
+        m + " pop. Line=" + Breakpoint.locationToLine(m, location) + " exception:" + exception);
+  }
+
+  public static void recurTimesA(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesB(times - 1, safepoint);
+  }
+
+  public static void recurTimesB(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesC(times - 1, safepoint);
+  }
+
+  public static void recurTimesC(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesD(times - 1, safepoint);
+  }
+
+  public static void recurTimesD(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesE(times - 1, safepoint);
+  }
+
+  public static void recurTimesE(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesF(times - 1, safepoint);
+  }
+
+  public static void recurTimesF(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesG(times - 1, safepoint);
+  }
+
+  public static void recurTimesG(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesH(times - 1, safepoint);
+  }
+
+  public static void recurTimesH(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesI(times - 1, safepoint);
+  }
+
+  public static void recurTimesI(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesJ(times - 1, safepoint);
+  }
+
+  public static void recurTimesJ(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesK(times - 1, safepoint);
+  }
+
+  public static void recurTimesK(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    recurTimesL(times - 1, safepoint);
+  }
+
+  public static class RecursionError extends Error {
+    public RecursionError(String s) { super(s); }
+  }
+
+  public static void recurTimesL(int times, Runnable safepoint) {
+    if (times == 0) {
+      safepoint.run();
+      return;
+    }
+    safepoint.run();
+    throw new RecursionError("Unable recur further. Still " + times + " outstanding!");
+  }
+
+  public static class ThreadPauser implements Runnable {
+    public final Semaphore sem_wakeup_main;
+    public final Semaphore sem_wait;
+
+    public ThreadPauser() {
+      sem_wakeup_main = new Semaphore(0);
+      sem_wait = new Semaphore(0);
+    }
+
+    public void run() {
+      try {
+        sem_wakeup_main.release();
+        sem_wait.acquire();
+      } catch (Exception e) {
+        throw new Error("Error with semaphores!", e);
+      }
+    }
+
+    public void waitForOtherThreadToPause() throws Exception {
+      sem_wakeup_main.acquire();
+    }
+
+    public void wakeupOtherThread() throws Exception {
+      sem_wait.release();
+    }
+  }
+
+  public static void doRecurTestWith(final int times, int watch_frame) throws Exception {
+    final String target_method_name_start = "recurTimes";
+    final ThreadPauser safepoint = new ThreadPauser();
+    Thread target = new Thread(() -> {
+      recurTimesA(times, () -> {
+        safepoint.run();
+        disableFramePop(null);
+      });
+      System.out.println("Ran recurTimes(" + times + ") without errors after disabling " +
+          "frame pop event!");
+      System.out.println("renabling frame pop event with similar stack.");
+      recurTimesB(times, () -> { reenableFramePop(null); });
+      System.out.println("Ran recurTimes(" + times + ") without errors!");
+    });
+    target.start();
+    safepoint.waitForOtherThreadToPause();
+    Suspension.suspend(target);
+    // Safe block
+    int cnt = 0;
+    StackTrace.StackFrameData target_frame = null;
+    for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(target)) {
+      if (frame.method.getName().startsWith(target_method_name_start)) {
+        if (times - cnt == watch_frame) {
+          target_frame = frame;
+          break;
+        } else {
+          cnt++;
+        }
+      }
+    }
+    if (target_frame != null) {
+      FramePop.notifyFramePop(target, target_frame.depth);
+    } else {
+      System.out.println(
+          "Unable to find stack frame for " + watch_frame + " depth of "
+          + target_method_name_start);
+    }
+    Suspension.resume(target);
+    safepoint.wakeupOtherThread();
+    target.join();
+  }
+
+  public static void run() throws Exception {
+    // Listen for events on all threads.
+    FramePop.enableFramePopEvent(
+        Test1926.class,
+        Test1926.class.getDeclaredMethod(
+            "handleFramePop", Executable.class, Boolean.TYPE, Long.TYPE),
+        null);
+    doRecurTestWith(10, 0);
+    doRecurTestWith(10, 5);
+    doRecurTestWith(10, 10);
+  }
+
+  public static native void disableFramePop(Thread thr);
+  public static native void reenableFramePop(Thread thr);
+}
diff --git a/test/1926-missed-frame-pop/src/art/Trace.java b/test/1926-missed-frame-pop/src/art/Trace.java
new file mode 100644
index 0000000..ba3d397
--- /dev/null
+++ b/test/1926-missed-frame-pop/src/art/Trace.java
@@ -0,0 +1,56 @@
+/*
+ * 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 Trace {
+  public static native void enableTracing(Class<?> methodClass,
+                                          Method entryMethod,
+                                          Method exitMethod,
+                                          Method fieldAccess,
+                                          Method fieldModify,
+                                          Method singleStep,
+                                          Thread thr);
+  public static native void disableTracing(Thread thr);
+
+  public static void enableFieldTracing(Class<?> methodClass,
+                                        Method fieldAccess,
+                                        Method fieldModify,
+                                        Thread thr) {
+    enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
+  }
+
+  public static void enableMethodTracing(Class<?> methodClass,
+                                         Method entryMethod,
+                                         Method exitMethod,
+                                         Thread thr) {
+    enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+  }
+
+  public static void enableSingleStepTracing(Class<?> methodClass,
+                                             Method singleStep,
+                                             Thread thr) {
+    enableTracing(methodClass, null, null, null, null, singleStep, thr);
+  }
+
+  public static native void watchFieldAccess(Field f);
+  public static native void watchFieldModification(Field f);
+  public static native void watchAllFieldAccesses();
+  public static native void watchAllFieldModifications();
+}
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/Android.bp b/test/Android.bp
index 4d82cbb..2aed50c 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -248,6 +248,7 @@
         "ti-agent/test_env.cc",
         "ti-agent/breakpoint_helper.cc",
         "ti-agent/common_helper.cc",
+        "ti-agent/frame_pop_helper.cc",
         "ti-agent/locals_helper.cc",
         "ti-agent/redefinition_helper.cc",
         "ti-agent/suspension_helper.cc",
@@ -294,6 +295,9 @@
         "1919-vminit-thread-start-timing/vminit.cc",
         "1920-suspend-native-monitor/native_suspend_monitor.cc",
         "1921-suspend-native-recursive-monitor/native_suspend_recursive_monitor.cc",
+        "1922-owned-monitors-info/owned_monitors.cc",
+        "1924-frame-pop-toggle/frame_pop_toggle.cc",
+        "1926-missed-frame-pop/frame_pop_missed.cc",
     ],
     shared_libs: [
         "libbase",
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 20cfc34..df7544e 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -681,6 +681,16 @@
         "env_vars": {"SANITIZE_HOST": "address"}
     },
     {
+        "tests": [
+            "1923-frame-pop",
+            "1924-frame-pop-toggle"
+        ],
+        "description": "ASAN seems to make these tests overflow their stacks.",
+        "variant": "64",
+        "bug": "b/65189092",
+        "env_vars": {"SANITIZE_HOST": "address"}
+    },
+    {
         "tests": ["988-method-trace"],
         "variant": "redefine-stress | jvmti-stress",
         "description": "Test disabled due to redefine-stress disabling intrinsics which changes the trace output slightly."
diff --git a/test/run-test b/test/run-test
index e6196a0..9996986 100755
--- a/test/run-test
+++ b/test/run-test
@@ -407,6 +407,10 @@
     elif [ "x$1" = "x--random-profile" ]; then
         run_args="${run_args} --random-profile"
         shift
+    elif [ "x$1" = "x--dex2oat-jobs" ]; then
+        shift
+        run_args="${run_args} -Xcompiler-option -j$1"
+        shift
     elif expr "x$1" : "x--" >/dev/null 2>&1; then
         echo "unknown $0 option: $1" 1>&2
         usage="yes"
@@ -702,6 +706,7 @@
         echo "    --bisection-search    Perform bisection bug search."
         echo "    --vdex                Test using vdex as in input to dex2oat. Only works with --prebuild."
         echo "    --suspend-timeout     Change thread suspend timeout ms (default 500000)."
+        echo "    --dex2oat-jobs        Number of dex2oat jobs."
     ) 1>&2  # Direct to stderr so usage is not printed if --quiet is set.
     exit 1
 fi
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 68e1856..f425097 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -127,6 +127,7 @@
 gdb = False
 gdb_arg = ''
 stop_testrunner = False
+dex2oat_jobs = -1   # -1 corresponds to default threads for dex2oat
 
 def gather_test_info():
   """The method gathers test information about the test to be run which includes
@@ -341,6 +342,9 @@
     if gdb_arg:
       options_all += ' --gdb-arg ' + gdb_arg
 
+  if dex2oat_jobs != -1:
+    options_all += ' --dex2oat-jobs ' + str(dex2oat_jobs)
+
   config = itertools.product(tests, TARGET_TYPES, RUN_TYPES, PREBUILD_TYPES,
                              COMPILER_TYPES, RELOCATE_TYPES, TRACE_TYPES,
                              GC_TYPES, JNI_TYPES, IMAGE_TYPES, PICTEST_TYPES,
@@ -860,6 +864,7 @@
   global gdb
   global gdb_arg
   global timeout
+  global dex2oat_jobs
 
   parser = argparse.ArgumentParser(description="Runs all or a subset of the ART test suite.")
   parser.add_argument('-t', '--test', dest='test', help='name of the test')
@@ -887,6 +892,8 @@
   parser.set_defaults(build = env.ART_TEST_RUN_TEST_BUILD)
   parser.add_argument('--gdb', action='store_true', dest='gdb')
   parser.add_argument('--gdb-arg', dest='gdb_arg')
+  parser.add_argument('--dex2oat-jobs', type=int, dest='dex2oat_jobs',
+                      help='Number of dex2oat jobs')
 
   options = vars(parser.parse_args())
   if options['build_target']:
@@ -987,6 +994,8 @@
     if options['gdb_arg']:
       gdb_arg = options['gdb_arg']
   timeout = options['timeout']
+  if options['dex2oat_jobs']:
+    dex2oat_jobs = options['dex2oat_jobs']
 
   return test
 
diff --git a/test/ti-agent/frame_pop_helper.cc b/test/ti-agent/frame_pop_helper.cc
new file mode 100644
index 0000000..4571032
--- /dev/null
+++ b/test/ti-agent/frame_pop_helper.cc
@@ -0,0 +1,130 @@
+/*
+ * 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_frame_pop {
+
+struct FramePopData {
+  jclass test_klass;
+  jmethodID pop_method;
+};
+
+static void framePopCB(jvmtiEnv* jvmti,
+                       JNIEnv* jnienv,
+                       jthread thr,
+                       jmethodID method ATTRIBUTE_UNUSED,
+                       jboolean was_popped_by_exception) {
+  FramePopData* data = nullptr;
+  if (JvmtiErrorToException(jnienv, jvmti,
+                            jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+    return;
+  }
+  jlong location;
+  jmethodID frame_method;
+  if (JvmtiErrorToException(jnienv,
+                            jvmti,
+                            jvmti->GetFrameLocation(thr, 0, &frame_method, &location))) {
+    return;
+  }
+  CHECK(data->pop_method != nullptr);
+  jobject method_arg = GetJavaMethod(jvmti, jnienv, frame_method);
+  jnienv->CallStaticVoidMethod(data->test_klass,
+                               data->pop_method,
+                               method_arg,
+                               was_popped_by_exception,
+                               location);
+  jnienv->DeleteLocalRef(method_arg);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_FramePop_enableFramePopEvent(
+    JNIEnv* env, jclass, jclass klass, jobject notify_method, jthread thr) {
+  FramePopData* data = nullptr;
+  if (JvmtiErrorToException(env,
+                            jvmti_env,
+                            jvmti_env->Allocate(sizeof(FramePopData),
+                                                reinterpret_cast<unsigned char**>(&data)))) {
+    return;
+  }
+  memset(data, 0, sizeof(FramePopData));
+  data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(klass));
+  data->pop_method = env->FromReflectedMethod(notify_method);
+  if (env->ExceptionCheck()) {
+    return;
+  }
+  void* old_data = nullptr;
+  if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
+    return;
+  } else if (old_data != 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;
+  }
+  jvmtiCapabilities caps;
+  memset(&caps, 0, sizeof(caps));
+  caps.can_generate_frame_pop_events = 1;
+  if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) {
+    return;
+  }
+  jvmtiEventCallbacks cb;
+  memset(&cb, 0, sizeof(cb));
+  cb.FramePop = framePopCB;
+  if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
+    return;
+  }
+  JvmtiErrorToException(env,
+                        jvmti_env,
+                        jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+                                                            JVMTI_EVENT_FRAME_POP,
+                                                            thr));
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_art_FramePop_makeJvmtiEnvForFramePop(JNIEnv* env, jclass) {
+  JavaVM* vm;
+  jvmtiEnv* out_jvmti_env = nullptr;
+  if (env->GetJavaVM(&vm) != JNI_OK ||
+      vm->GetEnv(reinterpret_cast<void**>(&out_jvmti_env), JVMTI_VERSION_1_0) != JNI_OK) {
+    ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+    if (rt_exception.get() == nullptr) {
+      // CNFE should be pending.
+      return 0L;
+    }
+    env->ThrowNew(rt_exception.get(), "Unable to create new jvmti_env");
+    return 0L;
+  }
+  SetAllCapabilities(out_jvmti_env);
+  return static_cast<jlong>(reinterpret_cast<intptr_t>(out_jvmti_env));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_FramePop_notifyFramePop(
+    JNIEnv* env, jclass, jthread thr, jint depth) {
+  JvmtiErrorToException(env, jvmti_env, jvmti_env->NotifyFramePop(thr, depth));
+}
+
+}  // namespace common_frame_pop
+}  // namespace art
+
diff --git a/tools/cpp-define-generator/constant_globals.def b/tools/cpp-define-generator/constant_globals.def
index dbaf33c..5018f52 100644
--- a/tools/cpp-define-generator/constant_globals.def
+++ b/tools/cpp-define-generator/constant_globals.def
@@ -19,6 +19,7 @@
 #if defined(DEFINE_INCLUDE_DEPENDENCIES)
 #include <atomic>            // std::memory_order_relaxed
 #include "globals.h"         // art::kObjectAlignment
+#include "modifiers.h"
 #endif
 
 DEFINE_EXPR(STD_MEMORY_ORDER_RELAXED, int32_t, std::memory_order_relaxed)
@@ -30,5 +31,8 @@
 DEFINE_OBJECT_EXPR(ALIGNMENT_MASK_TOGGLED, uint32_t, ~static_cast<uint32_t>(art::kObjectAlignment - 1))
 DEFINE_OBJECT_EXPR(ALIGNMENT_MASK_TOGGLED64, uint64_t, ~static_cast<uint64_t>(art::kObjectAlignment - 1))
 
+DEFINE_EXPR(ACC_OBSOLETE_METHOD,           int32_t,  art::kAccObsoleteMethod)
+DEFINE_EXPR(ACC_OBSOLETE_METHOD_SHIFT,     int32_t,  art::WhichPowerOf2(art::kAccObsoleteMethod))
+
 #undef DEFINE_OBJECT_EXPR
 
diff --git a/tools/cpp-define-generator/offset_dexcache.def b/tools/cpp-define-generator/offset_art_method.def
similarity index 95%
rename from tools/cpp-define-generator/offset_dexcache.def
rename to tools/cpp-define-generator/offset_art_method.def
index 43f9434..e6a0907 100644
--- a/tools/cpp-define-generator/offset_dexcache.def
+++ b/tools/cpp-define-generator/offset_art_method.def
@@ -33,10 +33,10 @@
   DEFINE_EXPR(DECLARING_CLASS_ ## field_name ## _OFFSET, int32_t, art::mirror::Class::method_name##Offset().Int32Value())
 
 //                         New macro suffix          Method Name (of the Offset method)
-DEFINE_ART_METHOD_OFFSET_SIZED(DEX_CACHE_METHODS,    DexCacheResolvedMethods)
 DEFINE_ART_METHOD_OFFSET_SIZED(JNI,                  EntryPointFromJni)
 DEFINE_ART_METHOD_OFFSET_SIZED(QUICK_CODE,           EntryPointFromQuickCompiledCode)
 DEFINE_ART_METHOD_OFFSET(DECLARING_CLASS,            DeclaringClass)
+DEFINE_ART_METHOD_OFFSET(ACCESS_FLAGS,               AccessFlags)
 
 #undef DEFINE_ART_METHOD_OFFSET
 #undef DEFINE_ART_METHOD_OFFSET_32
diff --git a/tools/cpp-define-generator/offset_mirror_class.def b/tools/cpp-define-generator/offset_mirror_class.def
new file mode 100644
index 0000000..9b7bfce
--- /dev/null
+++ b/tools/cpp-define-generator/offset_mirror_class.def
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+// Offsets within java.lang.Class (mirror::Class).
+
+#if defined(DEFINE_INCLUDE_DEPENDENCIES)
+#include "mirror/class.h"         // art::mirror::Object
+#endif
+
+#include "common.def"        // DEFINE_OFFSET_EXPR
+
+#define DEFINE_MIRROR_CLASS_OFFSET(field_name, method_name) \
+  DEFINE_OFFSET_EXPR(MIRROR_CLASS, field_name, int32_t, art::mirror::Class::method_name##Offset().Int32Value())
+
+//                         New macro suffix             Method Name (of the Offset method)
+DEFINE_MIRROR_CLASS_OFFSET(DEX_CACHE,                   DexCache)
+
+#undef DEFINE_MIRROR_CLASS_OFFSET
+#include "common_undef.def"  // undef DEFINE_OFFSET_EXPR
diff --git a/tools/cpp-define-generator/offset_mirror_dex_cache.def b/tools/cpp-define-generator/offset_mirror_dex_cache.def
new file mode 100644
index 0000000..8f008bb
--- /dev/null
+++ b/tools/cpp-define-generator/offset_mirror_dex_cache.def
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+// Offsets within java.lang.DexCache (mirror::DexCache).
+
+#if defined(DEFINE_INCLUDE_DEPENDENCIES)
+#include "mirror/class.h"         // art::mirror::Object
+#endif
+
+#include "common.def"        // DEFINE_OFFSET_EXPR
+
+#define DEFINE_MIRROR_DEX_CACHE_OFFSET(field_name, method_name) \
+  DEFINE_OFFSET_EXPR(MIRROR_DEX_CACHE, field_name, int32_t, art::mirror::DexCache::method_name##Offset().Int32Value())
+
+//                             New macro suffix         Method Name (of the Offset method)
+DEFINE_MIRROR_DEX_CACHE_OFFSET(RESOLVED_METHODS,        ResolvedMethods)
+
+#undef DEFINE_MIRROR_CLASS_OFFSET
+#include "common_undef.def"  // undef DEFINE_OFFSET_EXPR
diff --git a/tools/cpp-define-generator/offsets_all.def b/tools/cpp-define-generator/offsets_all.def
index b8947de..c2e8c97 100644
--- a/tools/cpp-define-generator/offsets_all.def
+++ b/tools/cpp-define-generator/offsets_all.def
@@ -42,12 +42,13 @@
 // #include "offset_shadow_frame.def"
 #include "offset_codeitem.def"
 // TODO: MIRROR_OBJECT_HEADER_SIZE (depends on #ifdef read barrier)
-// TODO: MIRROR_CLASS offsets (see above)
+#include "offset_mirror_class.def"
+#include "offset_mirror_dex_cache.def"
 #include "offset_mirror_object.def"
 #include "constant_class.def"
 // TODO: MIRROR_*_ARRAY offsets (depends on header size)
 // TODO: MIRROR_STRING offsets (depends on header size)
-#include "offset_dexcache.def"
+#include "offset_art_method.def"
 #include "constant_dexcache.def"
 #include "constant_card_table.def"
 #include "constant_heap.def"
diff --git a/tools/jfuzz/README.md b/tools/jfuzz/README.md
index c87e714..10d175b 100644
--- a/tools/jfuzz/README.md
+++ b/tools/jfuzz/README.md
@@ -48,6 +48,7 @@
                           [--report_script=SCRIPT]
                           [--jfuzz_arg=ARG]
                           [--true_divergence]
+                          [--use_dx]
 
 where
 
@@ -63,6 +64,7 @@
     --report_script   : path to script called for each divergence
     --jfuzz_arg       : argument for jfuzz
     --true_divergence : don't bisect timeout divergences
+    --use_dx          : use dx (rather than jack)
 
 How to start JFuzz nightly testing
 ==================================
@@ -83,12 +85,14 @@
                           [--num_tests=NUM_TESTS]
                           [--num_inputs=NUM_INPUTS]
                           [--device=DEVICE]
+                          [--use_dx]
 
 where
 
     --num_tests : number of tests to run (10000 by default)
     --num_inputs: number of JFuzz programs to generate
     --device    : target device serial number (passed to adb -s)
+    --use_dx    : use dx (rather than jack)
 
 Background
 ==========
diff --git a/tools/jfuzz/run_dex_fuzz_test.py b/tools/jfuzz/run_dex_fuzz_test.py
index c1d2e4f..ca0aec0 100755
--- a/tools/jfuzz/run_dex_fuzz_test.py
+++ b/tools/jfuzz/run_dex_fuzz_test.py
@@ -19,6 +19,7 @@
 import shutil
 import sys
 
+from glob import glob
 from subprocess import call
 from tempfile import mkdtemp
 
@@ -40,13 +41,14 @@
 class DexFuzzTester(object):
   """Tester that feeds JFuzz programs into DexFuzz testing."""
 
-  def  __init__(self, num_tests, num_inputs, device):
+  def  __init__(self, num_tests, num_inputs, device, use_dx):
     """Constructor for the tester.
 
     Args:
       num_tests: int, number of tests to run
       num_inputs: int, number of JFuzz programs to generate
       device: string, target device serial number (or None)
+      use_dx: boolean, if True use dx rather than jack
     """
     self._num_tests = num_tests
     self._num_inputs = num_inputs
@@ -56,6 +58,7 @@
     self._dexfuzz_dir = None
     self._inputs_dir = None
     self._dexfuzz_env = None
+    self._use_dx = use_dx
 
   def __enter__(self):
     """On entry, enters new temp directory after saving current directory.
@@ -100,6 +103,35 @@
     self.GenerateJFuzzPrograms()
     self.RunDexFuzz()
 
+  def CompileOnHost(self):
+    """Compiles Test.java into classes.dex using either javac/dx or jack.
+
+    Raises:
+      FatalError: error when compilation fails
+    """
+    if self._use_dx:
+      if RunCommand(['javac', 'Test.java'],
+                    out=None, err='jerr.txt', timeout=30) != RetCode.SUCCESS:
+        print('Unexpected error while running javac')
+        raise FatalError('Unexpected error while running javac')
+      cfiles = glob('*.class')
+      if RunCommand(['dx', '--dex', '--output=classes.dex'] + cfiles,
+                    out=None, err='dxerr.txt', timeout=30) != RetCode.SUCCESS:
+        print('Unexpected error while running dx')
+        raise FatalError('Unexpected error while running dx')
+      # Cleanup on success (nothing to see).
+      for cfile in cfiles:
+        os.unlink(cfile)
+      os.unlink('jerr.txt')
+      os.unlink('dxerr.txt')
+    else:
+      jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java']
+      if RunCommand(['jack'] + jack_args, out=None, err='jackerr.txt',
+                    timeout=30) != RetCode.SUCCESS:
+        print('Unexpected error while running Jack')
+        raise FatalError('Unexpected error while running Jack')
+      # Cleanup on success (nothing to see).
+      os.unlink('jackerr.txt')
 
   def GenerateJFuzzPrograms(self):
     """Generates JFuzz programs.
@@ -109,17 +141,12 @@
     """
     os.chdir(self._inputs_dir)
     for i in range(1, self._num_inputs + 1):
-      jack_args = ['-cp', GetJackClassPath(), '--output-dex', '.', 'Test.java']
       if RunCommand(['jfuzz'], out='Test.java', err=None) != RetCode.SUCCESS:
         print('Unexpected error while running JFuzz')
         raise FatalError('Unexpected error while running JFuzz')
-      if RunCommand(['jack'] + jack_args, out=None, err='jackerr.txt',
-                    timeout=30) != RetCode.SUCCESS:
-        print('Unexpected error while running Jack')
-        raise FatalError('Unexpected error while running Jack')
+      self.CompileOnHost()
       shutil.move('Test.java', '../Test' + str(i) + '.java')
       shutil.move('classes.dex', 'classes' + str(i) + '.dex')
-    os.unlink('jackerr.txt')
 
   def RunDexFuzz(self):
     """Starts the DexFuzz testing."""
@@ -152,10 +179,12 @@
                       type=int, help='number of tests to run')
   parser.add_argument('--num_inputs', default=10,
                       type=int, help='number of JFuzz program to generate')
+  parser.add_argument('--use_dx', default=False, action='store_true',
+                      help='use dx (rather than jack)')
   parser.add_argument('--device', help='target device serial number')
   args = parser.parse_args()
   # Run the DexFuzz tester.
-  with DexFuzzTester(args.num_tests, args.num_inputs, args.device) as fuzzer:
+  with DexFuzzTester(args.num_tests, args.num_inputs, args.device, args.use_dx) as fuzzer:
     fuzzer.Run()
 
 if __name__ == '__main__':
diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py
index 58bc737..dac1c79 100755
--- a/tools/jfuzz/run_jfuzz_test.py
+++ b/tools/jfuzz/run_jfuzz_test.py
@@ -606,7 +606,7 @@
   parser.add_argument('--true_divergence', default=False, action='store_true',
                       help='don\'t bisect timeout divergences')
   parser.add_argument('--use_dx', default=False, action='store_true',
-                      help='use old-style dx (rather than jack)')
+                      help='use dx (rather than jack)')
   args = parser.parse_args()
   if args.mode1 == args.mode2:
     raise FatalError('Identical execution modes given')