OSDN Git Service

Revert "Revert "x86/x86-64: Avoid temporary for read barrier field load.""
authorVladimir Marko <vmarko@google.com>
Wed, 24 Aug 2016 08:30:46 +0000 (08:30 +0000)
committerVladimir Marko <vmarko@google.com>
Wed, 24 Aug 2016 12:20:32 +0000 (13:20 +0100)
Fixed the fault handler recognizing the TEST instruction and
fault address within the lock word. Added tests to 439-npe.

Bug: 29966877
Bug: 12687968
Test: Tested with ART_USE_READ_BARRIER=true on host.
Test: Tested with ART_USE_READ_BARRIER=true ART_HEAP_POISONING=true on host.

This reverts commit ccf15bca330f9a23337b1a4b5850f7fcc6c1bf15.

Change-Id: I8990def5f719c9205bf6e5fdba32027fa82bec50

19 files changed:
compiler/optimizing/code_generator_arm.cc
compiler/optimizing/code_generator_arm64.cc
compiler/optimizing/code_generator_x86.cc
compiler/optimizing/code_generator_x86.h
compiler/optimizing/code_generator_x86_64.cc
compiler/optimizing/code_generator_x86_64.h
compiler/optimizing/intrinsics_x86.cc
compiler/optimizing/intrinsics_x86_64.cc
compiler/utils/x86/assembler_x86.cc
compiler/utils/x86/assembler_x86.h
compiler/utils/x86/assembler_x86_test.cc
compiler/utils/x86_64/assembler_x86_64.cc
compiler/utils/x86_64/assembler_x86_64.h
compiler/utils/x86_64/assembler_x86_64_test.cc
runtime/arch/x86/fault_handler_x86.cc
runtime/common_throws.cc
test/439-npe/expected.txt
test/439-npe/src/Main.java
test/537-checker-arraycopy/src/Main.java

index 404f044..6d9c55c 100644 (file)
@@ -6377,7 +6377,7 @@ void InstructionCodeGeneratorARM::GenerateGcRootFieldLoad(HInstruction* instruct
                     "art::mirror::CompressedReference<mirror::Object> and int32_t "
                     "have different sizes.");
 
-      // Slow path used to mark the GC root `root`.
+      // Slow path marking the GC root `root`.
       SlowPathCode* slow_path =
           new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(instruction, root);
       codegen_->AddSlowPath(slow_path);
@@ -6518,7 +6518,7 @@ void CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* i
   // Object* ref = ref_addr->AsMirrorPtr()
   __ MaybeUnpoisonHeapReference(ref_reg);
 
-  // Slow path used to mark the object `ref` when it is gray.
+  // Slow path marking the object `ref` when it is gray.
   SlowPathCode* slow_path =
       new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM(instruction, ref);
   AddSlowPath(slow_path);
index 122c174..cc8985d 100644 (file)
@@ -5041,7 +5041,7 @@ void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad(HInstruction* instru
                     "art::mirror::CompressedReference<mirror::Object> and int32_t "
                     "have different sizes.");
 
-      // Slow path used to mark the GC root `root`.
+      // Slow path marking the GC root `root`.
       SlowPathCodeARM64* slow_path =
           new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM64(instruction, root);
       codegen_->AddSlowPath(slow_path);
@@ -5239,7 +5239,7 @@ void CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction*
   // Object* ref = ref_addr->AsMirrorPtr()
   GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
 
-  // Slow path used to mark the object `ref` when it is gray.
+  // Slow path marking the object `ref` when it is gray.
   SlowPathCodeARM64* slow_path =
       new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathARM64(instruction, ref);
   AddSlowPath(slow_path);
index 7aca16f..f50eb5c 100644 (file)
@@ -445,8 +445,8 @@ class ArraySetSlowPathX86 : public SlowPathCode {
 // Slow path marking an object during a read barrier.
 class ReadBarrierMarkSlowPathX86 : public SlowPathCode {
  public:
-  ReadBarrierMarkSlowPathX86(HInstruction* instruction, Location obj)
-      : SlowPathCode(instruction), obj_(obj) {
+  ReadBarrierMarkSlowPathX86(HInstruction* instruction, Location obj, bool unpoison)
+      : SlowPathCode(instruction), obj_(obj), unpoison_(unpoison) {
     DCHECK(kEmitCompilerReadBarrier);
   }
 
@@ -470,6 +470,10 @@ class ReadBarrierMarkSlowPathX86 : public SlowPathCode {
         << instruction_->DebugName();
 
     __ Bind(GetEntryLabel());
+    if (unpoison_) {
+      // Object* ref = ref_addr->AsMirrorPtr()
+      __ MaybeUnpoisonHeapReference(reg);
+    }
     // No need to save live registers; it's taken care of by the
     // entrypoint. Also, there is no need to update the stack mask,
     // as this runtime call will not trigger a garbage collection.
@@ -499,6 +503,7 @@ class ReadBarrierMarkSlowPathX86 : public SlowPathCode {
 
  private:
   const Location obj_;
+  const bool unpoison_;
 
   DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathX86);
 };
@@ -4631,10 +4636,6 @@ void LocationsBuilderX86::HandleFieldGet(HInstruction* instruction, const FieldI
     // load the temp into the XMM and then copy the XMM into the
     // output, 32 bits at a time).
     locations->AddTemp(Location::RequiresFpuRegister());
-  } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
-    // We need a temporary register for the read barrier marking slow
-    // path in CodeGeneratorX86::GenerateFieldLoadWithBakerReadBarrier.
-    locations->AddTemp(Location::RequiresRegister());
   }
 }
 
@@ -4678,11 +4679,10 @@ void InstructionCodeGeneratorX86::HandleFieldGet(HInstruction* instruction,
     case Primitive::kPrimNot: {
       // /* HeapReference<Object> */ out = *(base + offset)
       if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
-        Location temp_loc = locations->GetTemp(0);
         // Note that a potential implicit null check is handled in this
         // CodeGeneratorX86::GenerateFieldLoadWithBakerReadBarrier call.
         codegen_->GenerateFieldLoadWithBakerReadBarrier(
-            instruction, out, base, offset, temp_loc, /* needs_null_check */ true);
+            instruction, out, base, offset, /* needs_null_check */ true);
         if (is_volatile) {
           codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
         }
@@ -5093,11 +5093,6 @@ void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) {
             Location::kOutputOverlap :
             Location::kNoOutputOverlap);
   }
-  // We need a temporary register for the read barrier marking slow
-  // path in CodeGeneratorX86::GenerateArrayLoadWithBakerReadBarrier.
-  if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
-    locations->AddTemp(Location::RequiresRegister());
-  }
 }
 
 void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
@@ -5172,11 +5167,10 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
       // /* HeapReference<Object> */ out =
       //     *(obj + data_offset + index * sizeof(HeapReference<Object>))
       if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
-        Location temp = locations->GetTemp(0);
         // Note that a potential implicit null check is handled in this
         // CodeGeneratorX86::GenerateArrayLoadWithBakerReadBarrier call.
         codegen_->GenerateArrayLoadWithBakerReadBarrier(
-            instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ true);
+            instruction, out_loc, obj, data_offset, index, /* needs_null_check */ true);
       } else {
         Register out = out_loc.AsRegister<Register>();
         if (index.IsConstant()) {
@@ -6281,8 +6275,8 @@ void InstructionCodeGeneratorX86::VisitThrow(HThrow* instruction) {
 
 static bool TypeCheckNeedsATemporary(TypeCheckKind type_check_kind) {
   return kEmitCompilerReadBarrier &&
-      (kUseBakerReadBarrier ||
-       type_check_kind == TypeCheckKind::kAbstractClassCheck ||
+      !kUseBakerReadBarrier &&
+      (type_check_kind == TypeCheckKind::kAbstractClassCheck ||
        type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
        type_check_kind == TypeCheckKind::kArrayObjectCheck);
 }
@@ -6343,7 +6337,7 @@ void InstructionCodeGeneratorX86::VisitInstanceOf(HInstanceOf* instruction) {
   }
 
   // /* HeapReference<Class> */ out = obj->klass_
-  GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc);
+  GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset);
 
   switch (type_check_kind) {
     case TypeCheckKind::kExactCheck: {
@@ -6565,7 +6559,7 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) {
   }
 
   // /* HeapReference<Class> */ temp = obj->klass_
-  GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+  GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset);
 
   switch (type_check_kind) {
     case TypeCheckKind::kExactCheck:
@@ -6601,8 +6595,7 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) {
       // going into the slow path, as it has been overwritten in the
       // meantime.
       // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(
-          instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+      GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset);
       __ jmp(type_check_slow_path->GetEntryLabel());
 
       __ Bind(&compare_classes);
@@ -6641,8 +6634,7 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) {
       // going into the slow path, as it has been overwritten in the
       // meantime.
       // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(
-          instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+      GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset);
       __ jmp(type_check_slow_path->GetEntryLabel());
       break;
     }
@@ -6674,8 +6666,7 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) {
       // going into the slow path, as it has been overwritten in the
       // meantime.
       // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(
-          instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+      GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset);
       __ jmp(type_check_slow_path->GetEntryLabel());
 
       __ Bind(&check_non_primitive_component_type);
@@ -6683,8 +6674,7 @@ void InstructionCodeGeneratorX86::VisitCheckCast(HCheckCast* instruction) {
       __ j(kEqual, &done);
       // Same comment as above regarding `temp` and the slow path.
       // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(
-          instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+      GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset);
       __ jmp(type_check_slow_path->GetEntryLabel());
       break;
     }
@@ -6875,17 +6865,17 @@ void InstructionCodeGeneratorX86::GenerateReferenceLoadOneRegister(HInstruction*
                                                                    Location maybe_temp) {
   Register out_reg = out.AsRegister<Register>();
   if (kEmitCompilerReadBarrier) {
-    DCHECK(maybe_temp.IsRegister()) << maybe_temp;
     if (kUseBakerReadBarrier) {
       // Load with fast path based Baker's read barrier.
       // /* HeapReference<Object> */ out = *(out + offset)
       codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false);
+          instruction, out, out_reg, offset, /* needs_null_check */ false);
     } else {
       // Load with slow path based read barrier.
       // Save the value of `out` into `maybe_temp` before overwriting it
       // in the following move operation, as we will need it for the
       // read barrier below.
+      DCHECK(maybe_temp.IsRegister()) << maybe_temp;
       __ movl(maybe_temp.AsRegister<Register>(), out_reg);
       // /* HeapReference<Object> */ out = *(out + offset)
       __ movl(out_reg, Address(out_reg, offset));
@@ -6902,17 +6892,15 @@ void InstructionCodeGeneratorX86::GenerateReferenceLoadOneRegister(HInstruction*
 void InstructionCodeGeneratorX86::GenerateReferenceLoadTwoRegisters(HInstruction* instruction,
                                                                     Location out,
                                                                     Location obj,
-                                                                    uint32_t offset,
-                                                                    Location maybe_temp) {
+                                                                    uint32_t offset) {
   Register out_reg = out.AsRegister<Register>();
   Register obj_reg = obj.AsRegister<Register>();
   if (kEmitCompilerReadBarrier) {
     if (kUseBakerReadBarrier) {
-      DCHECK(maybe_temp.IsRegister()) << maybe_temp;
       // Load with fast path based Baker's read barrier.
       // /* HeapReference<Object> */ out = *(obj + offset)
       codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false);
+          instruction, out, obj_reg, offset, /* needs_null_check */ false);
     } else {
       // Load with slow path based read barrier.
       // /* HeapReference<Object> */ out = *(obj + offset)
@@ -6955,9 +6943,9 @@ void InstructionCodeGeneratorX86::GenerateGcRootFieldLoad(HInstruction* instruct
                     "art::mirror::CompressedReference<mirror::Object> and int32_t "
                     "have different sizes.");
 
-      // Slow path used to mark the GC root `root`.
-      SlowPathCode* slow_path =
-          new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathX86(instruction, root);
+      // Slow path marking the GC root `root`.
+      SlowPathCode* slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathX86(
+          instruction, root, /* unpoison */ false);
       codegen_->AddSlowPath(slow_path);
 
       __ fs()->cmpl(Address::Absolute(Thread::IsGcMarkingOffset<kX86PointerSize>().Int32Value()),
@@ -6991,14 +6979,13 @@ void CodeGeneratorX86::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instr
                                                              Location ref,
                                                              Register obj,
                                                              uint32_t offset,
-                                                             Location temp,
                                                              bool needs_null_check) {
   DCHECK(kEmitCompilerReadBarrier);
   DCHECK(kUseBakerReadBarrier);
 
   // /* HeapReference<Object> */ ref = *(obj + offset)
   Address src(obj, offset);
-  GenerateReferenceLoadWithBakerReadBarrier(instruction, ref, obj, src, temp, needs_null_check);
+  GenerateReferenceLoadWithBakerReadBarrier(instruction, ref, obj, src, needs_null_check);
 }
 
 void CodeGeneratorX86::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -7006,7 +6993,6 @@ void CodeGeneratorX86::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instr
                                                              Register obj,
                                                              uint32_t data_offset,
                                                              Location index,
-                                                             Location temp,
                                                              bool needs_null_check) {
   DCHECK(kEmitCompilerReadBarrier);
   DCHECK(kUseBakerReadBarrier);
@@ -7019,14 +7005,13 @@ void CodeGeneratorX86::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instr
   Address src = index.IsConstant() ?
       Address(obj, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset) :
       Address(obj, index.AsRegister<Register>(), TIMES_4, data_offset);
-  GenerateReferenceLoadWithBakerReadBarrier(instruction, ref, obj, src, temp, needs_null_check);
+  GenerateReferenceLoadWithBakerReadBarrier(instruction, ref, obj, src, needs_null_check);
 }
 
 void CodeGeneratorX86::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
                                                                  Location ref,
                                                                  Register obj,
                                                                  const Address& src,
-                                                                 Location temp,
                                                                  bool needs_null_check) {
   DCHECK(kEmitCompilerReadBarrier);
   DCHECK(kUseBakerReadBarrier);
@@ -7056,17 +7041,23 @@ void CodeGeneratorX86::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* i
   //   performance reasons.
 
   Register ref_reg = ref.AsRegister<Register>();
-  Register temp_reg = temp.AsRegister<Register>();
   uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
 
-  // /* int32_t */ monitor = obj->monitor_
-  __ movl(temp_reg, Address(obj, monitor_offset));
+  // Given the numeric representation, it's enough to check the low bit of the rb_state.
+  static_assert(ReadBarrier::white_ptr_ == 0, "Expecting white to have value 0");
+  static_assert(ReadBarrier::gray_ptr_ == 1, "Expecting gray to have value 1");
+  static_assert(ReadBarrier::black_ptr_ == 2, "Expecting black to have value 2");
+  constexpr uint32_t gray_byte_position = LockWord::kReadBarrierStateShift / kBitsPerByte;
+  constexpr uint32_t gray_bit_position = LockWord::kReadBarrierStateShift % kBitsPerByte;
+  constexpr int32_t test_value = static_cast<int8_t>(1 << gray_bit_position);
+
+  // if (rb_state == ReadBarrier::gray_ptr_)
+  //   ref = ReadBarrier::Mark(ref);
+  // At this point, just do the "if" and make sure that flags are preserved until the branch.
+  __ testb(Address(obj, monitor_offset + gray_byte_position), Immediate(test_value));
   if (needs_null_check) {
     MaybeRecordImplicitNullCheck(instruction);
   }
-  // /* LockWord */ lock_word = LockWord(monitor)
-  static_assert(sizeof(LockWord) == sizeof(int32_t),
-                "art::LockWord and int32_t have different sizes.");
 
   // Load fence to prevent load-load reordering.
   // Note that this is a no-op, thanks to the x86 memory model.
@@ -7074,25 +7065,20 @@ void CodeGeneratorX86::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* i
 
   // The actual reference load.
   // /* HeapReference<Object> */ ref = *src
-  __ movl(ref_reg, src);
+  __ movl(ref_reg, src);  // Flags are unaffected.
+
+  // Note: Reference unpoisoning modifies the flags, so we need to delay it after the branch.
+  // Slow path marking the object `ref` when it is gray.
+  SlowPathCode* slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathX86(
+      instruction, ref, /* unpoison */ true);
+  AddSlowPath(slow_path);
+
+  // We have done the "if" of the gray bit check above, now branch based on the flags.
+  __ j(kNotZero, slow_path->GetEntryLabel());
 
   // Object* ref = ref_addr->AsMirrorPtr()
   __ MaybeUnpoisonHeapReference(ref_reg);
 
-  // Slow path used to mark the object `ref` when it is gray.
-  SlowPathCode* slow_path =
-      new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathX86(instruction, ref);
-  AddSlowPath(slow_path);
-
-  // if (rb_state == ReadBarrier::gray_ptr_)
-  //   ref = ReadBarrier::Mark(ref);
-  // Given the numeric representation, it's enough to check the low bit of the
-  // rb_state. We do that by shifting the bit out of the lock word with SHR.
-  static_assert(ReadBarrier::white_ptr_ == 0, "Expecting white to have value 0");
-  static_assert(ReadBarrier::gray_ptr_ == 1, "Expecting gray to have value 1");
-  static_assert(ReadBarrier::black_ptr_ == 2, "Expecting black to have value 2");
-  __ shrl(temp_reg, Immediate(LockWord::kReadBarrierStateShift + 1));
-  __ j(kCarrySet, slow_path->GetEntryLabel());
   __ Bind(slow_path->GetExitLabel());
 }
 
index 894f2e8..c644e40 100644 (file)
@@ -254,8 +254,7 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator {
   void GenerateReferenceLoadTwoRegisters(HInstruction* instruction,
                                          Location out,
                                          Location obj,
-                                         uint32_t offset,
-                                         Location maybe_temp);
+                                         uint32_t offset);
   // Generate a GC root reference load:
   //
   //   root <- *address
@@ -487,7 +486,6 @@ class CodeGeneratorX86 : public CodeGenerator {
                                              Location ref,
                                              Register obj,
                                              uint32_t offset,
-                                             Location temp,
                                              bool needs_null_check);
   // Fast path implementation of ReadBarrier::Barrier for a heap
   // reference array load when Baker's read barriers are used.
@@ -496,7 +494,6 @@ class CodeGeneratorX86 : public CodeGenerator {
                                              Register obj,
                                              uint32_t data_offset,
                                              Location index,
-                                             Location temp,
                                              bool needs_null_check);
   // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier
   // and GenerateArrayLoadWithBakerReadBarrier.
@@ -504,7 +501,6 @@ class CodeGeneratorX86 : public CodeGenerator {
                                                  Location ref,
                                                  Register obj,
                                                  const Address& src,
-                                                 Location temp,
                                                  bool needs_null_check);
 
   // Generate a read barrier for a heap reference within `instruction`
index 0c55ae4..ec37e5d 100644 (file)
@@ -466,8 +466,8 @@ class ArraySetSlowPathX86_64 : public SlowPathCode {
 // Slow path marking an object during a read barrier.
 class ReadBarrierMarkSlowPathX86_64 : public SlowPathCode {
  public:
-  ReadBarrierMarkSlowPathX86_64(HInstruction* instruction, Location obj)
-      : SlowPathCode(instruction), obj_(obj) {
+  ReadBarrierMarkSlowPathX86_64(HInstruction* instruction, Location obj, bool unpoison)
+      : SlowPathCode(instruction), obj_(obj), unpoison_(unpoison) {
     DCHECK(kEmitCompilerReadBarrier);
   }
 
@@ -491,6 +491,10 @@ class ReadBarrierMarkSlowPathX86_64 : public SlowPathCode {
         << instruction_->DebugName();
 
     __ Bind(GetEntryLabel());
+    if (unpoison_) {
+      // Object* ref = ref_addr->AsMirrorPtr()
+      __ MaybeUnpoisonHeapReference(obj_.AsRegister<CpuRegister>());
+    }
     // No need to save live registers; it's taken care of by the
     // entrypoint. Also, there is no need to update the stack mask,
     // as this runtime call will not trigger a garbage collection.
@@ -520,6 +524,7 @@ class ReadBarrierMarkSlowPathX86_64 : public SlowPathCode {
 
  private:
   const Location obj_;
+  const bool unpoison_;
 
   DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathX86_64);
 };
@@ -4152,11 +4157,6 @@ void LocationsBuilderX86_64::HandleFieldGet(HInstruction* instruction) {
         Location::RequiresRegister(),
         object_field_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
   }
-  if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
-    // We need a temporary register for the read barrier marking slow
-    // path in CodeGeneratorX86_64::GenerateFieldLoadWithBakerReadBarrier.
-    locations->AddTemp(Location::RequiresRegister());
-  }
 }
 
 void InstructionCodeGeneratorX86_64::HandleFieldGet(HInstruction* instruction,
@@ -4200,11 +4200,10 @@ void InstructionCodeGeneratorX86_64::HandleFieldGet(HInstruction* instruction,
     case Primitive::kPrimNot: {
       // /* HeapReference<Object> */ out = *(base + offset)
       if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
-        Location temp_loc = locations->GetTemp(0);
         // Note that a potential implicit null check is handled in this
         // CodeGeneratorX86::GenerateFieldLoadWithBakerReadBarrier call.
         codegen_->GenerateFieldLoadWithBakerReadBarrier(
-            instruction, out, base, offset, temp_loc, /* needs_null_check */ true);
+            instruction, out, base, offset, /* needs_null_check */ true);
         if (is_volatile) {
           codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
         }
@@ -4588,11 +4587,6 @@ void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) {
         Location::RequiresRegister(),
         object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
   }
-  // We need a temporary register for the read barrier marking slow
-  // path in CodeGeneratorX86_64::GenerateArrayLoadWithBakerReadBarrier.
-  if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
-    locations->AddTemp(Location::RequiresRegister());
-  }
 }
 
 void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
@@ -4667,11 +4661,10 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
       // /* HeapReference<Object> */ out =
       //     *(obj + data_offset + index * sizeof(HeapReference<Object>))
       if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
-        Location temp = locations->GetTemp(0);
         // Note that a potential implicit null check is handled in this
         // CodeGeneratorX86::GenerateArrayLoadWithBakerReadBarrier call.
         codegen_->GenerateArrayLoadWithBakerReadBarrier(
-            instruction, out_loc, obj, data_offset, index, temp, /* needs_null_check */ true);
+            instruction, out_loc, obj, data_offset, index, /* needs_null_check */ true);
       } else {
         CpuRegister out = out_loc.AsRegister<CpuRegister>();
         if (index.IsConstant()) {
@@ -5687,8 +5680,8 @@ void InstructionCodeGeneratorX86_64::VisitThrow(HThrow* instruction) {
 
 static bool TypeCheckNeedsATemporary(TypeCheckKind type_check_kind) {
   return kEmitCompilerReadBarrier &&
-      (kUseBakerReadBarrier ||
-       type_check_kind == TypeCheckKind::kAbstractClassCheck ||
+      !kUseBakerReadBarrier &&
+      (type_check_kind == TypeCheckKind::kAbstractClassCheck ||
        type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
        type_check_kind == TypeCheckKind::kArrayObjectCheck);
 }
@@ -5749,7 +5742,7 @@ void InstructionCodeGeneratorX86_64::VisitInstanceOf(HInstanceOf* instruction) {
   }
 
   // /* HeapReference<Class> */ out = obj->klass_
-  GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset, maybe_temp_loc);
+  GenerateReferenceLoadTwoRegisters(instruction, out_loc, obj_loc, class_offset);
 
   switch (type_check_kind) {
     case TypeCheckKind::kExactCheck: {
@@ -5979,8 +5972,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
       }
 
       // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(
-          instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+      GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset);
 
       if (cls.IsRegister()) {
         __ cmpl(temp, cls.AsRegister<CpuRegister>());
@@ -6004,8 +5996,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
       }
 
       // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(
-          instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+      GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset);
 
       // If the class is abstract, we eagerly fetch the super class of the
       // object to avoid doing a comparison we know will fail.
@@ -6025,8 +6016,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
       // going into the slow path, as it has been overwritten in the
       // meantime.
       // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(
-          instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+      GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset);
       __ jmp(type_check_slow_path->GetEntryLabel());
 
       __ Bind(&compare_classes);
@@ -6050,8 +6040,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
       }
 
       // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(
-          instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+      GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset);
 
       // Walk over the class hierarchy to find a match.
       NearLabel loop;
@@ -6077,8 +6066,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
       // going into the slow path, as it has been overwritten in the
       // meantime.
       // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(
-          instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+      GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset);
       __ jmp(type_check_slow_path->GetEntryLabel());
       __ Bind(&done);
       break;
@@ -6097,8 +6085,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
       }
 
       // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(
-          instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+      GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset);
 
       // Do an exact check.
       NearLabel check_non_primitive_component_type;
@@ -6126,8 +6113,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
       // going into the slow path, as it has been overwritten in the
       // meantime.
       // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(
-          instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+      GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset);
       __ jmp(type_check_slow_path->GetEntryLabel());
 
       __ Bind(&check_non_primitive_component_type);
@@ -6135,8 +6121,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
       __ j(kEqual, &done);
       // Same comment as above regarding `temp` and the slow path.
       // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(
-          instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+      GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset);
       __ jmp(type_check_slow_path->GetEntryLabel());
       __ Bind(&done);
       break;
@@ -6152,8 +6137,7 @@ void InstructionCodeGeneratorX86_64::VisitCheckCast(HCheckCast* instruction) {
       }
 
       // /* HeapReference<Class> */ temp = obj->klass_
-      GenerateReferenceLoadTwoRegisters(
-          instruction, temp_loc, obj_loc, class_offset, maybe_temp2_loc);
+      GenerateReferenceLoadTwoRegisters(instruction, temp_loc, obj_loc, class_offset);
 
       // We always go into the type check slow path for the unresolved
       // and interface check cases.
@@ -6321,17 +6305,17 @@ void InstructionCodeGeneratorX86_64::GenerateReferenceLoadOneRegister(HInstructi
                                                                       Location maybe_temp) {
   CpuRegister out_reg = out.AsRegister<CpuRegister>();
   if (kEmitCompilerReadBarrier) {
-    DCHECK(maybe_temp.IsRegister()) << maybe_temp;
     if (kUseBakerReadBarrier) {
       // Load with fast path based Baker's read barrier.
       // /* HeapReference<Object> */ out = *(out + offset)
       codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          instruction, out, out_reg, offset, maybe_temp, /* needs_null_check */ false);
+          instruction, out, out_reg, offset, /* needs_null_check */ false);
     } else {
       // Load with slow path based read barrier.
       // Save the value of `out` into `maybe_temp` before overwriting it
       // in the following move operation, as we will need it for the
       // read barrier below.
+      DCHECK(maybe_temp.IsRegister()) << maybe_temp;
       __ movl(maybe_temp.AsRegister<CpuRegister>(), out_reg);
       // /* HeapReference<Object> */ out = *(out + offset)
       __ movl(out_reg, Address(out_reg, offset));
@@ -6348,17 +6332,15 @@ void InstructionCodeGeneratorX86_64::GenerateReferenceLoadOneRegister(HInstructi
 void InstructionCodeGeneratorX86_64::GenerateReferenceLoadTwoRegisters(HInstruction* instruction,
                                                                        Location out,
                                                                        Location obj,
-                                                                       uint32_t offset,
-                                                                       Location maybe_temp) {
+                                                                       uint32_t offset) {
   CpuRegister out_reg = out.AsRegister<CpuRegister>();
   CpuRegister obj_reg = obj.AsRegister<CpuRegister>();
   if (kEmitCompilerReadBarrier) {
     if (kUseBakerReadBarrier) {
-      DCHECK(maybe_temp.IsRegister()) << maybe_temp;
       // Load with fast path based Baker's read barrier.
       // /* HeapReference<Object> */ out = *(obj + offset)
       codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check */ false);
+          instruction, out, obj_reg, offset, /* needs_null_check */ false);
     } else {
       // Load with slow path based read barrier.
       // /* HeapReference<Object> */ out = *(obj + offset)
@@ -6401,9 +6383,9 @@ void InstructionCodeGeneratorX86_64::GenerateGcRootFieldLoad(HInstruction* instr
                     "art::mirror::CompressedReference<mirror::Object> and int32_t "
                     "have different sizes.");
 
-      // Slow path used to mark the GC root `root`.
-      SlowPathCode* slow_path =
-          new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathX86_64(instruction, root);
+      // Slow path marking the GC root `root`.
+      SlowPathCode* slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathX86_64(
+          instruction, root, /* unpoison */ false);
       codegen_->AddSlowPath(slow_path);
 
       __ gs()->cmpl(Address::Absolute(Thread::IsGcMarkingOffset<kX86_64PointerSize>().Int32Value(),
@@ -6438,14 +6420,13 @@ void CodeGeneratorX86_64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* in
                                                                 Location ref,
                                                                 CpuRegister obj,
                                                                 uint32_t offset,
-                                                                Location temp,
                                                                 bool needs_null_check) {
   DCHECK(kEmitCompilerReadBarrier);
   DCHECK(kUseBakerReadBarrier);
 
   // /* HeapReference<Object> */ ref = *(obj + offset)
   Address src(obj, offset);
-  GenerateReferenceLoadWithBakerReadBarrier(instruction, ref, obj, src, temp, needs_null_check);
+  GenerateReferenceLoadWithBakerReadBarrier(instruction, ref, obj, src, needs_null_check);
 }
 
 void CodeGeneratorX86_64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -6453,7 +6434,6 @@ void CodeGeneratorX86_64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* in
                                                                 CpuRegister obj,
                                                                 uint32_t data_offset,
                                                                 Location index,
-                                                                Location temp,
                                                                 bool needs_null_check) {
   DCHECK(kEmitCompilerReadBarrier);
   DCHECK(kUseBakerReadBarrier);
@@ -6466,14 +6446,13 @@ void CodeGeneratorX86_64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* in
   Address src = index.IsConstant() ?
       Address(obj, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset) :
       Address(obj, index.AsRegister<CpuRegister>(), TIMES_4, data_offset);
-  GenerateReferenceLoadWithBakerReadBarrier(instruction, ref, obj, src, temp, needs_null_check);
+  GenerateReferenceLoadWithBakerReadBarrier(instruction, ref, obj, src, needs_null_check);
 }
 
 void CodeGeneratorX86_64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
                                                                     Location ref,
                                                                     CpuRegister obj,
                                                                     const Address& src,
-                                                                    Location temp,
                                                                     bool needs_null_check) {
   DCHECK(kEmitCompilerReadBarrier);
   DCHECK(kUseBakerReadBarrier);
@@ -6503,17 +6482,23 @@ void CodeGeneratorX86_64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction
   //   performance reasons.
 
   CpuRegister ref_reg = ref.AsRegister<CpuRegister>();
-  CpuRegister temp_reg = temp.AsRegister<CpuRegister>();
   uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
 
-  // /* int32_t */ monitor = obj->monitor_
-  __ movl(temp_reg, Address(obj, monitor_offset));
+  // Given the numeric representation, it's enough to check the low bit of the rb_state.
+  static_assert(ReadBarrier::white_ptr_ == 0, "Expecting white to have value 0");
+  static_assert(ReadBarrier::gray_ptr_ == 1, "Expecting gray to have value 1");
+  static_assert(ReadBarrier::black_ptr_ == 2, "Expecting black to have value 2");
+  constexpr uint32_t gray_byte_position = LockWord::kReadBarrierStateShift / kBitsPerByte;
+  constexpr uint32_t gray_bit_position = LockWord::kReadBarrierStateShift % kBitsPerByte;
+  constexpr int32_t test_value = static_cast<int8_t>(1 << gray_bit_position);
+
+  // if (rb_state == ReadBarrier::gray_ptr_)
+  //   ref = ReadBarrier::Mark(ref);
+  // At this point, just do the "if" and make sure that flags are preserved until the branch.
+  __ testb(Address(obj, monitor_offset + gray_byte_position), Immediate(test_value));
   if (needs_null_check) {
     MaybeRecordImplicitNullCheck(instruction);
   }
-  // /* LockWord */ lock_word = LockWord(monitor)
-  static_assert(sizeof(LockWord) == sizeof(int32_t),
-                "art::LockWord and int32_t have different sizes.");
 
   // Load fence to prevent load-load reordering.
   // Note that this is a no-op, thanks to the x86-64 memory model.
@@ -6521,25 +6506,20 @@ void CodeGeneratorX86_64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction
 
   // The actual reference load.
   // /* HeapReference<Object> */ ref = *src
-  __ movl(ref_reg, src);
+  __ movl(ref_reg, src);  // Flags are unaffected.
+
+  // Note: Reference unpoisoning modifies the flags, so we need to delay it after the branch.
+  // Slow path marking the object `ref` when it is gray.
+  SlowPathCode* slow_path = new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathX86_64(
+      instruction, ref, /* unpoison */ true);
+  AddSlowPath(slow_path);
+
+  // We have done the "if" of the gray bit check above, now branch based on the flags.
+  __ j(kNotZero, slow_path->GetEntryLabel());
 
   // Object* ref = ref_addr->AsMirrorPtr()
   __ MaybeUnpoisonHeapReference(ref_reg);
 
-  // Slow path used to mark the object `ref` when it is gray.
-  SlowPathCode* slow_path =
-      new (GetGraph()->GetArena()) ReadBarrierMarkSlowPathX86_64(instruction, ref);
-  AddSlowPath(slow_path);
-
-  // if (rb_state == ReadBarrier::gray_ptr_)
-  //   ref = ReadBarrier::Mark(ref);
-  // Given the numeric representation, it's enough to check the low bit of the
-  // rb_state. We do that by shifting the bit out of the lock word with SHR.
-  static_assert(ReadBarrier::white_ptr_ == 0, "Expecting white to have value 0");
-  static_assert(ReadBarrier::gray_ptr_ == 1, "Expecting gray to have value 1");
-  static_assert(ReadBarrier::black_ptr_ == 2, "Expecting black to have value 2");
-  __ shrl(temp_reg, Immediate(LockWord::kReadBarrierStateShift + 1));
-  __ j(kCarrySet, slow_path->GetEntryLabel());
   __ Bind(slow_path->GetExitLabel());
 }
 
index 4e0e34c..44844ac 100644 (file)
@@ -248,8 +248,7 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator {
   void GenerateReferenceLoadTwoRegisters(HInstruction* instruction,
                                          Location out,
                                          Location obj,
-                                         uint32_t offset,
-                                         Location maybe_temp);
+                                         uint32_t offset);
   // Generate a GC root reference load:
   //
   //   root <- *address
@@ -427,7 +426,6 @@ class CodeGeneratorX86_64 : public CodeGenerator {
                                              Location ref,
                                              CpuRegister obj,
                                              uint32_t offset,
-                                             Location temp,
                                              bool needs_null_check);
   // Fast path implementation of ReadBarrier::Barrier for a heap
   // reference array load when Baker's read barriers are used.
@@ -436,7 +434,6 @@ class CodeGeneratorX86_64 : public CodeGenerator {
                                              CpuRegister obj,
                                              uint32_t data_offset,
                                              Location index,
-                                             Location temp,
                                              bool needs_null_check);
   // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier
   // and GenerateArrayLoadWithBakerReadBarrier.
@@ -444,7 +441,6 @@ class CodeGeneratorX86_64 : public CodeGenerator {
                                                  Location ref,
                                                  CpuRegister obj,
                                                  const Address& src,
-                                                 Location temp,
                                                  bool needs_null_check);
 
   // Generate a read barrier for a heap reference within `instruction`
index 49d6c19..cf4a040 100644 (file)
@@ -1934,10 +1934,9 @@ static void GenUnsafeGet(HInvoke* invoke,
       Register output = output_loc.AsRegister<Register>();
       if (kEmitCompilerReadBarrier) {
         if (kUseBakerReadBarrier) {
-          Location temp = locations->GetTemp(0);
           Address src(base, offset, ScaleFactor::TIMES_1, 0);
           codegen->GenerateReferenceLoadWithBakerReadBarrier(
-              invoke, output_loc, base, src, temp, /* needs_null_check */ false);
+              invoke, output_loc, base, src, /* needs_null_check */ false);
         } else {
           __ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0));
           codegen->GenerateReadBarrierSlow(
@@ -2000,11 +1999,6 @@ static void CreateIntIntIntToIntLocations(ArenaAllocator* arena,
     locations->SetOut(Location::RequiresRegister(),
                       can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
   }
-  if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
-    // We need a temporary register for the read barrier marking slow
-    // path in InstructionCodeGeneratorX86::GenerateReferenceLoadWithBakerReadBarrier.
-    locations->AddTemp(Location::RequiresRegister());
-  }
 }
 
 void IntrinsicLocationsBuilderX86::VisitUnsafeGet(HInvoke* invoke) {
@@ -2933,11 +2927,11 @@ void IntrinsicCodeGeneratorX86::VisitSystemArrayCopy(HInvoke* invoke) {
       if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
         // /* HeapReference<Class> */ temp1 = src->klass_
         codegen_->GenerateFieldLoadWithBakerReadBarrier(
-            invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
+            invoke, temp1_loc, src, class_offset, /* needs_null_check */ false);
         // Bail out if the source is not a non primitive array.
         // /* HeapReference<Class> */ temp1 = temp1->component_type_
         codegen_->GenerateFieldLoadWithBakerReadBarrier(
-            invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
+            invoke, temp1_loc, temp1, component_offset, /* needs_null_check */ false);
         __ testl(temp1, temp1);
         __ j(kEqual, intrinsic_slow_path->GetEntryLabel());
         // If heap poisoning is enabled, `temp1` has been unpoisoned
@@ -2970,7 +2964,7 @@ void IntrinsicCodeGeneratorX86::VisitSystemArrayCopy(HInvoke* invoke) {
 
       // /* HeapReference<Class> */ temp1 = dest->klass_
       codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          invoke, temp1_loc, dest, class_offset, temp2_loc, /* needs_null_check */ false);
+          invoke, temp1_loc, dest, class_offset, /* needs_null_check */ false);
 
       if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
         // Bail out if the destination is not a non primitive array.
@@ -2982,7 +2976,7 @@ void IntrinsicCodeGeneratorX86::VisitSystemArrayCopy(HInvoke* invoke) {
         // temporaries such a `temp1`.
         // /* HeapReference<Class> */ temp2 = temp1->component_type_
         codegen_->GenerateFieldLoadWithBakerReadBarrier(
-            invoke, temp2_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false);
+            invoke, temp2_loc, temp1, component_offset, /* needs_null_check */ false);
         __ testl(temp2, temp2);
         __ j(kEqual, intrinsic_slow_path->GetEntryLabel());
         // If heap poisoning is enabled, `temp2` has been unpoisoned
@@ -2995,7 +2989,7 @@ void IntrinsicCodeGeneratorX86::VisitSystemArrayCopy(HInvoke* invoke) {
       // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
       // /* HeapReference<Class> */ temp2 = src->klass_
       codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          invoke, temp2_loc, src, class_offset, temp3_loc, /* needs_null_check */ false);
+          invoke, temp2_loc, src, class_offset, /* needs_null_check */ false);
       // Note: if heap poisoning is on, we are comparing two unpoisoned references here.
       __ cmpl(temp1, temp2);
 
@@ -3004,7 +2998,7 @@ void IntrinsicCodeGeneratorX86::VisitSystemArrayCopy(HInvoke* invoke) {
         __ j(kEqual, &do_copy);
         // /* HeapReference<Class> */ temp1 = temp1->component_type_
         codegen_->GenerateFieldLoadWithBakerReadBarrier(
-            invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
+            invoke, temp1_loc, temp1, component_offset, /* needs_null_check */ false);
         // We do not need to emit a read barrier for the following
         // heap reference load, as `temp1` is only used in a
         // comparison with null below, and this reference is not
@@ -3058,10 +3052,10 @@ void IntrinsicCodeGeneratorX86::VisitSystemArrayCopy(HInvoke* invoke) {
     if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
       // /* HeapReference<Class> */ temp1 = src->klass_
       codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check */ false);
+          invoke, temp1_loc, src, class_offset, /* needs_null_check */ false);
       // /* HeapReference<Class> */ temp1 = temp1->component_type_
       codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          invoke, temp1_loc, temp1, component_offset, temp2_loc, /* needs_null_check */ false);
+          invoke, temp1_loc, temp1, component_offset, /* needs_null_check */ false);
       __ testl(temp1, temp1);
       __ j(kEqual, intrinsic_slow_path->GetEntryLabel());
       // If heap poisoning is enabled, `temp1` has been unpoisoned
@@ -3139,11 +3133,18 @@ void IntrinsicCodeGeneratorX86::VisitSystemArrayCopy(HInvoke* invoke) {
     __ cmpl(temp1, temp3);
     __ j(kEqual, &done);
 
-    // /* int32_t */ monitor = src->monitor_
-    __ movl(temp2, Address(src, monitor_offset));
-    // /* LockWord */ lock_word = LockWord(monitor)
-    static_assert(sizeof(LockWord) == sizeof(int32_t),
-                  "art::LockWord and int32_t have different sizes.");
+    // Given the numeric representation, it's enough to check the low bit of the rb_state.
+    static_assert(ReadBarrier::white_ptr_ == 0, "Expecting white to have value 0");
+    static_assert(ReadBarrier::gray_ptr_ == 1, "Expecting gray to have value 1");
+    static_assert(ReadBarrier::black_ptr_ == 2, "Expecting black to have value 2");
+    constexpr uint32_t gray_byte_position = LockWord::kReadBarrierStateShift / kBitsPerByte;
+    constexpr uint32_t gray_bit_position = LockWord::kReadBarrierStateShift % kBitsPerByte;
+    constexpr int32_t test_value = static_cast<int8_t>(1 << gray_bit_position);
+
+    // if (rb_state == ReadBarrier::gray_ptr_)
+    //   goto slow_path;
+    // At this point, just do the "if" and make sure that flags are preserved until the branch.
+    __ testb(Address(src, monitor_offset + gray_byte_position), Immediate(test_value));
 
     // Load fence to prevent load-load reordering.
     // Note that this is a no-op, thanks to the x86 memory model.
@@ -3154,13 +3155,8 @@ void IntrinsicCodeGeneratorX86::VisitSystemArrayCopy(HInvoke* invoke) {
         new (GetAllocator()) ReadBarrierSystemArrayCopySlowPathX86(invoke);
     codegen_->AddSlowPath(read_barrier_slow_path);
 
-    // Given the numeric representation, it's enough to check the low bit of the
-    // rb_state. We do that by shifting the bit out of the lock word with SHR.
-    static_assert(ReadBarrier::white_ptr_ == 0, "Expecting white to have value 0");
-    static_assert(ReadBarrier::gray_ptr_ == 1, "Expecting gray to have value 1");
-    static_assert(ReadBarrier::black_ptr_ == 2, "Expecting black to have value 2");
-    __ shrl(temp2, Immediate(LockWord::kReadBarrierStateShift + 1));
-    __ j(kCarrySet, read_barrier_slow_path->GetEntryLabel());
+    // We have done the "if" of the gray bit check above, now branch based on the flags.
+    __ j(kNotZero, read_barrier_slow_path->GetEntryLabel());
 
     // Fast-path copy.
 
index 311e1cd..a4ee546 100644 (file)
@@ -1241,7 +1241,7 @@ void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopy(HInvoke* invoke) {
     if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
       // /* HeapReference<Class> */ temp1 = dest->klass_
       codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          invoke, temp1_loc, dest, class_offset, temp3_loc, /* needs_null_check */ false);
+          invoke, temp1_loc, dest, class_offset, /* needs_null_check */ false);
       // Register `temp1` is not trashed by the read barrier emitted
       // by GenerateFieldLoadWithBakerReadBarrier below, as that
       // method produces a call to a ReadBarrierMarkRegX entry point,
@@ -1249,7 +1249,7 @@ void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopy(HInvoke* invoke) {
       // temporaries such a `temp1`.
       // /* HeapReference<Class> */ temp2 = src->klass_
       codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          invoke, temp2_loc, src, class_offset, temp3_loc, /* needs_null_check */ false);
+          invoke, temp2_loc, src, class_offset, /* needs_null_check */ false);
       // If heap poisoning is enabled, `temp1` and `temp2` have been
       // unpoisoned by the the previous calls to
       // GenerateFieldLoadWithBakerReadBarrier.
@@ -1273,7 +1273,7 @@ void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopy(HInvoke* invoke) {
       if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
         // /* HeapReference<Class> */ TMP = temp1->component_type_
         codegen_->GenerateFieldLoadWithBakerReadBarrier(
-            invoke, TMP_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false);
+            invoke, TMP_loc, temp1, component_offset, /* needs_null_check */ false);
         __ testl(CpuRegister(TMP), CpuRegister(TMP));
         __ j(kEqual, intrinsic_slow_path->GetEntryLabel());
         // If heap poisoning is enabled, `TMP` has been unpoisoned by
@@ -1296,7 +1296,7 @@ void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopy(HInvoke* invoke) {
         // read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
         // /* HeapReference<Class> */ TMP = temp2->component_type_
         codegen_->GenerateFieldLoadWithBakerReadBarrier(
-            invoke, TMP_loc, temp2, component_offset, temp3_loc, /* needs_null_check */ false);
+            invoke, TMP_loc, temp2, component_offset, /* needs_null_check */ false);
         __ testl(CpuRegister(TMP), CpuRegister(TMP));
         __ j(kEqual, intrinsic_slow_path->GetEntryLabel());
         // If heap poisoning is enabled, `TMP` has been unpoisoned by
@@ -1320,7 +1320,7 @@ void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopy(HInvoke* invoke) {
       if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
         // /* HeapReference<Class> */ temp1 = temp1->component_type_
         codegen_->GenerateFieldLoadWithBakerReadBarrier(
-            invoke, temp1_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false);
+            invoke, temp1_loc, temp1, component_offset, /* needs_null_check */ false);
         // We do not need to emit a read barrier for the following
         // heap reference load, as `temp1` is only used in a
         // comparison with null below, and this reference is not
@@ -1348,10 +1348,10 @@ void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopy(HInvoke* invoke) {
     if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
       // /* HeapReference<Class> */ temp1 = src->klass_
       codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          invoke, temp1_loc, src, class_offset, temp3_loc, /* needs_null_check */ false);
+          invoke, temp1_loc, src, class_offset, /* needs_null_check */ false);
       // /* HeapReference<Class> */ TMP = temp1->component_type_
       codegen_->GenerateFieldLoadWithBakerReadBarrier(
-          invoke, TMP_loc, temp1, component_offset, temp3_loc, /* needs_null_check */ false);
+          invoke, TMP_loc, temp1, component_offset, /* needs_null_check */ false);
       __ testl(CpuRegister(TMP), CpuRegister(TMP));
       __ j(kEqual, intrinsic_slow_path->GetEntryLabel());
     } else {
@@ -1421,11 +1421,18 @@ void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopy(HInvoke* invoke) {
     __ cmpl(temp1, temp3);
     __ j(kEqual, &done);
 
-    // /* int32_t */ monitor = src->monitor_
-    __ movl(CpuRegister(TMP), Address(src, monitor_offset));
-    // /* LockWord */ lock_word = LockWord(monitor)
-    static_assert(sizeof(LockWord) == sizeof(int32_t),
-                  "art::LockWord and int32_t have different sizes.");
+    // Given the numeric representation, it's enough to check the low bit of the rb_state.
+    static_assert(ReadBarrier::white_ptr_ == 0, "Expecting white to have value 0");
+    static_assert(ReadBarrier::gray_ptr_ == 1, "Expecting gray to have value 1");
+    static_assert(ReadBarrier::black_ptr_ == 2, "Expecting black to have value 2");
+    constexpr uint32_t gray_byte_position = LockWord::kReadBarrierStateShift / kBitsPerByte;
+    constexpr uint32_t gray_bit_position = LockWord::kReadBarrierStateShift % kBitsPerByte;
+    constexpr int32_t test_value = static_cast<int8_t>(1 << gray_bit_position);
+
+    // if (rb_state == ReadBarrier::gray_ptr_)
+    //   goto slow_path;
+    // At this point, just do the "if" and make sure that flags are preserved until the branch.
+    __ testb(Address(src, monitor_offset + gray_byte_position), Immediate(test_value));
 
     // Load fence to prevent load-load reordering.
     // Note that this is a no-op, thanks to the x86-64 memory model.
@@ -1436,13 +1443,8 @@ void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopy(HInvoke* invoke) {
         new (GetAllocator()) ReadBarrierSystemArrayCopySlowPathX86_64(invoke);
     codegen_->AddSlowPath(read_barrier_slow_path);
 
-    // Given the numeric representation, it's enough to check the low bit of the
-    // rb_state. We do that by shifting the bit out of the lock word with SHR.
-    static_assert(ReadBarrier::white_ptr_ == 0, "Expecting white to have value 0");
-    static_assert(ReadBarrier::gray_ptr_ == 1, "Expecting gray to have value 1");
-    static_assert(ReadBarrier::black_ptr_ == 2, "Expecting black to have value 2");
-    __ shrl(CpuRegister(TMP), Immediate(LockWord::kReadBarrierStateShift + 1));
-    __ j(kCarrySet, read_barrier_slow_path->GetEntryLabel());
+    // We have done the "if" of the gray bit check above, now branch based on the flags.
+    __ j(kNotZero, read_barrier_slow_path->GetEntryLabel());
 
     // Fast-path copy.
     // Iterate over the arrays and do a raw copy of the objects. We don't need to
@@ -2087,10 +2089,9 @@ static void GenUnsafeGet(HInvoke* invoke,
     case Primitive::kPrimNot: {
       if (kEmitCompilerReadBarrier) {
         if (kUseBakerReadBarrier) {
-          Location temp = locations->GetTemp(0);
           Address src(base, offset, ScaleFactor::TIMES_1, 0);
           codegen->GenerateReferenceLoadWithBakerReadBarrier(
-              invoke, output_loc, base, src, temp, /* needs_null_check */ false);
+              invoke, output_loc, base, src, /* needs_null_check */ false);
         } else {
           __ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0));
           codegen->GenerateReadBarrierSlow(
@@ -2113,9 +2114,7 @@ static void GenUnsafeGet(HInvoke* invoke,
   }
 }
 
-static void CreateIntIntIntToIntLocations(ArenaAllocator* arena,
-                                          HInvoke* invoke,
-                                          Primitive::Type type) {
+static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) {
   bool can_call = kEmitCompilerReadBarrier &&
       (invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject ||
        invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile);
@@ -2129,30 +2128,25 @@ static void CreateIntIntIntToIntLocations(ArenaAllocator* arena,
   locations->SetInAt(2, Location::RequiresRegister());
   locations->SetOut(Location::RequiresRegister(),
                     can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
-  if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
-    // We need a temporary register for the read barrier marking slow
-    // path in InstructionCodeGeneratorX86_64::GenerateReferenceLoadWithBakerReadBarrier.
-    locations->AddTemp(Location::RequiresRegister());
-  }
 }
 
 void IntrinsicLocationsBuilderX86_64::VisitUnsafeGet(HInvoke* invoke) {
-  CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
+  CreateIntIntIntToIntLocations(arena_, invoke);
 }
 void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetVolatile(HInvoke* invoke) {
-  CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimInt);
+  CreateIntIntIntToIntLocations(arena_, invoke);
 }
 void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetLong(HInvoke* invoke) {
-  CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
+  CreateIntIntIntToIntLocations(arena_, invoke);
 }
 void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetLongVolatile(HInvoke* invoke) {
-  CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimLong);
+  CreateIntIntIntToIntLocations(arena_, invoke);
 }
 void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetObject(HInvoke* invoke) {
-  CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
+  CreateIntIntIntToIntLocations(arena_, invoke);
 }
 void IntrinsicLocationsBuilderX86_64::VisitUnsafeGetObjectVolatile(HInvoke* invoke) {
-  CreateIntIntIntToIntLocations(arena_, invoke, Primitive::kPrimNot);
+  CreateIntIntIntToIntLocations(arena_, invoke);
 }
 
 
index f1a9915..f2ef41f 100644 (file)
@@ -1148,6 +1148,23 @@ void X86Assembler::testl(Register reg, const Immediate& immediate) {
 }
 
 
+void X86Assembler::testb(const Address& dst, const Immediate& imm) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xF6);
+  EmitOperand(EAX, dst);
+  CHECK(imm.is_int8());
+  EmitUint8(imm.value() & 0xFF);
+}
+
+
+void X86Assembler::testl(const Address& dst, const Immediate& imm) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0xF7);
+  EmitOperand(0, dst);
+  EmitImmediate(imm);
+}
+
+
 void X86Assembler::andl(Register dst, Register src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x23);
index 63aa4a4..2ddcd76 100644 (file)
@@ -496,6 +496,9 @@ class X86Assembler FINAL : public Assembler {
   void testl(Register reg, const Immediate& imm);
   void testl(Register reg1, const Address& address);
 
+  void testb(const Address& dst, const Immediate& imm);
+  void testl(const Address& dst, const Immediate& imm);
+
   void andl(Register dst, const Immediate& imm);
   void andl(Register dst, Register src);
   void andl(Register dst, const Address& address);
index 307e034..61d70d7 100644 (file)
@@ -375,6 +375,42 @@ TEST_F(AssemblerX86Test, CmovlAddress) {
   DriverStr(expected, "cmovl_address");
 }
 
+TEST_F(AssemblerX86Test, TestbAddressImmediate) {
+  GetAssembler()->testb(
+      x86::Address(x86::Register(x86::EDI), x86::Register(x86::EBX), x86::TIMES_4, 12),
+      x86::Immediate(1));
+  GetAssembler()->testb(
+      x86::Address(x86::Register(x86::ESP), FrameOffset(7)),
+      x86::Immediate(-128));
+  GetAssembler()->testb(
+      x86::Address(x86::Register(x86::EBX), MemberOffset(130)),
+      x86::Immediate(127));
+  const char* expected =
+      "testb $1, 0xc(%EDI,%EBX,4)\n"
+      "testb $-128, 0x7(%ESP)\n"
+      "testb $127, 0x82(%EBX)\n";
+
+  DriverStr(expected, "TestbAddressImmediate");
+}
+
+TEST_F(AssemblerX86Test, TestlAddressImmediate) {
+  GetAssembler()->testl(
+      x86::Address(x86::Register(x86::EDI), x86::Register(x86::EBX), x86::TIMES_4, 12),
+      x86::Immediate(1));
+  GetAssembler()->testl(
+      x86::Address(x86::Register(x86::ESP), FrameOffset(7)),
+      x86::Immediate(-100000));
+  GetAssembler()->testl(
+      x86::Address(x86::Register(x86::EBX), MemberOffset(130)),
+      x86::Immediate(77777777));
+  const char* expected =
+      "testl $1, 0xc(%EDI,%EBX,4)\n"
+      "testl $-100000, 0x7(%ESP)\n"
+      "testl $77777777, 0x82(%EBX)\n";
+
+  DriverStr(expected, "TestlAddressImmediate");
+}
+
 /////////////////
 // Near labels //
 /////////////////
index ddc8244..1f73aa7 100644 (file)
@@ -1389,6 +1389,25 @@ void X86_64Assembler::testq(CpuRegister reg, const Address& address) {
 }
 
 
+void X86_64Assembler::testb(const Address& dst, const Immediate& imm) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitOptionalRex32(dst);
+  EmitUint8(0xF6);
+  EmitOperand(Register::RAX, dst);
+  CHECK(imm.is_int8());
+  EmitUint8(imm.value() & 0xFF);
+}
+
+
+void X86_64Assembler::testl(const Address& dst, const Immediate& imm) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitOptionalRex32(dst);
+  EmitUint8(0xF7);
+  EmitOperand(0, dst);
+  EmitImmediate(imm);
+}
+
+
 void X86_64Assembler::andl(CpuRegister dst, CpuRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitOptionalRex32(dst, src);
index a4166f9..3a4bfca 100644 (file)
@@ -528,6 +528,9 @@ class X86_64Assembler FINAL : public Assembler {
   void testq(CpuRegister reg1, CpuRegister reg2);
   void testq(CpuRegister reg, const Address& address);
 
+  void testb(const Address& address, const Immediate& imm);
+  void testl(const Address& address, const Immediate& imm);
+
   void andl(CpuRegister dst, const Immediate& imm);
   void andl(CpuRegister dst, CpuRegister src);
   void andl(CpuRegister reg, const Address& address);
index 36c966b..48a1876 100644 (file)
@@ -1526,6 +1526,48 @@ TEST_F(AssemblerX86_64Test, Cmpb) {
   DriverStr(expected, "cmpb");
 }
 
+TEST_F(AssemblerX86_64Test, TestbAddressImmediate) {
+  GetAssembler()->testb(
+      x86_64::Address(x86_64::CpuRegister(x86_64::RDI),
+                      x86_64::CpuRegister(x86_64::RBX),
+                      x86_64::TIMES_4,
+                      12),
+      x86_64::Immediate(1));
+  GetAssembler()->testb(
+      x86_64::Address(x86_64::CpuRegister(x86_64::RSP), FrameOffset(7)),
+      x86_64::Immediate(-128));
+  GetAssembler()->testb(
+      x86_64::Address(x86_64::CpuRegister(x86_64::RBX), MemberOffset(130)),
+      x86_64::Immediate(127));
+  const char* expected =
+      "testb $1, 0xc(%RDI,%RBX,4)\n"
+      "testb $-128, 0x7(%RSP)\n"
+      "testb $127, 0x82(%RBX)\n";
+
+  DriverStr(expected, "TestbAddressImmediate");
+}
+
+TEST_F(AssemblerX86_64Test, TestlAddressImmediate) {
+  GetAssembler()->testl(
+      x86_64::Address(x86_64::CpuRegister(x86_64::RDI),
+                      x86_64::CpuRegister(x86_64::RBX),
+                      x86_64::TIMES_4,
+                      12),
+      x86_64::Immediate(1));
+  GetAssembler()->testl(
+      x86_64::Address(x86_64::CpuRegister(x86_64::RSP), FrameOffset(7)),
+      x86_64::Immediate(-100000));
+  GetAssembler()->testl(
+      x86_64::Address(x86_64::CpuRegister(x86_64::RBX), MemberOffset(130)),
+      x86_64::Immediate(77777777));
+  const char* expected =
+      "testl $1, 0xc(%RDI,%RBX,4)\n"
+      "testl $-100000, 0x7(%RSP)\n"
+      "testl $77777777, 0x82(%RBX)\n";
+
+  DriverStr(expected, "TestlAddressImmediate");
+}
+
 class JNIMacroAssemblerX86_64Test : public JNIMacroAssemblerTest<x86_64::X86_64JNIMacroAssembler> {
  public:
   using Base = JNIMacroAssemblerTest<x86_64::X86_64JNIMacroAssembler>;
index 3efeb40..c7af249 100644 (file)
@@ -191,6 +191,27 @@ static uint32_t GetInstructionSize(const uint8_t* pc) {
         immediate_size = operand_size_prefix ? 2 : 4;
         break;
 
+      case 0xf6:
+      case 0xf7:
+        modrm = *pc++;
+        has_modrm = true;
+        switch ((modrm >> 3) & 7) {  // Extract "reg/opcode" from "modr/m".
+          case 0:  // test
+            immediate_size = (opcode == 0xf6) ? 1 : (operand_size_prefix ? 2 : 4);
+            break;
+          case 2:  // not
+          case 3:  // neg
+          case 4:  // mul
+          case 5:  // imul
+          case 6:  // div
+          case 7:  // idiv
+            break;
+          default:
+            unhandled_instruction = true;
+            break;
+        }
+        break;
+
       default:
         unhandled_instruction = true;
         break;
index 99732c6..e1da23c 100644 (file)
@@ -402,6 +402,16 @@ void ThrowNullPointerExceptionForMethodAccess(ArtMethod* method,
                                                dex_file, type);
 }
 
+static bool IsValidReadBarrierImplicitCheck(uintptr_t addr) {
+  DCHECK(kEmitCompilerReadBarrier);
+  uint32_t monitor_offset = mirror::Object::MonitorOffset().Uint32Value();
+  if (kUseBakerReadBarrier && (kRuntimeISA == kX86 || kRuntimeISA == kX86_64)) {
+    constexpr uint32_t gray_byte_position = LockWord::kReadBarrierStateShift / kBitsPerByte;
+    monitor_offset += gray_byte_position;
+  }
+  return addr == monitor_offset;
+}
+
 static bool IsValidImplicitCheck(uintptr_t addr, ArtMethod* method, const Instruction& instr)
     SHARED_REQUIRES(Locks::mutator_lock_) {
   if (!CanDoImplicitNullCheckOn(addr)) {
@@ -424,9 +434,13 @@ static bool IsValidImplicitCheck(uintptr_t addr, ArtMethod* method, const Instru
       return true;
     }
 
+    case Instruction::IGET_OBJECT:
+      if (kEmitCompilerReadBarrier && IsValidReadBarrierImplicitCheck(addr)) {
+        return true;
+      }
+      FALLTHROUGH_INTENDED;
     case Instruction::IGET:
     case Instruction::IGET_WIDE:
-    case Instruction::IGET_OBJECT:
     case Instruction::IGET_BOOLEAN:
     case Instruction::IGET_BYTE:
     case Instruction::IGET_CHAR:
@@ -440,18 +454,20 @@ static bool IsValidImplicitCheck(uintptr_t addr, ArtMethod* method, const Instru
     case Instruction::IPUT_SHORT: {
       ArtField* field =
           Runtime::Current()->GetClassLinker()->ResolveField(instr.VRegC_22c(), method, false);
-      return (addr == 0) ||
-          (addr == field->GetOffset().Uint32Value()) ||
-          (kEmitCompilerReadBarrier && (addr == mirror::Object::MonitorOffset().Uint32Value()));
+      return (addr == 0) || (addr == field->GetOffset().Uint32Value());
     }
 
+    case Instruction::IGET_OBJECT_QUICK:
+      if (kEmitCompilerReadBarrier && IsValidReadBarrierImplicitCheck(addr)) {
+        return true;
+      }
+      FALLTHROUGH_INTENDED;
     case Instruction::IGET_QUICK:
     case Instruction::IGET_BOOLEAN_QUICK:
     case Instruction::IGET_BYTE_QUICK:
     case Instruction::IGET_CHAR_QUICK:
     case Instruction::IGET_SHORT_QUICK:
     case Instruction::IGET_WIDE_QUICK:
-    case Instruction::IGET_OBJECT_QUICK:
     case Instruction::IPUT_QUICK:
     case Instruction::IPUT_BOOLEAN_QUICK:
     case Instruction::IPUT_BYTE_QUICK:
@@ -459,14 +475,16 @@ static bool IsValidImplicitCheck(uintptr_t addr, ArtMethod* method, const Instru
     case Instruction::IPUT_SHORT_QUICK:
     case Instruction::IPUT_WIDE_QUICK:
     case Instruction::IPUT_OBJECT_QUICK: {
-      return (addr == 0u) ||
-          (addr == instr.VRegC_22c()) ||
-          (kEmitCompilerReadBarrier && (addr == mirror::Object::MonitorOffset().Uint32Value()));
+      return (addr == 0u) || (addr == instr.VRegC_22c());
     }
 
+    case Instruction::AGET_OBJECT:
+      if (kEmitCompilerReadBarrier && IsValidReadBarrierImplicitCheck(addr)) {
+        return true;
+      }
+      FALLTHROUGH_INTENDED;
     case Instruction::AGET:
     case Instruction::AGET_WIDE:
-    case Instruction::AGET_OBJECT:
     case Instruction::AGET_BOOLEAN:
     case Instruction::AGET_BYTE:
     case Instruction::AGET_CHAR:
@@ -482,9 +500,7 @@ static bool IsValidImplicitCheck(uintptr_t addr, ArtMethod* method, const Instru
     case Instruction::ARRAY_LENGTH: {
       // The length access should crash. We currently do not do implicit checks on
       // the array access itself.
-      return (addr == 0u) ||
-          (addr == mirror::Array::LengthOffset().Uint32Value()) ||
-          (kEmitCompilerReadBarrier && (addr == mirror::Object::MonitorOffset().Uint32Value()));
+      return (addr == 0u) || (addr == mirror::Array::LengthOffset().Uint32Value());
     }
 
     default: {
index 271d40d..34855ee 100644 (file)
@@ -1,18 +1,54 @@
-$opt$setObjectField
-$opt$setIntField
-$opt$setFloatField
-$opt$setLongField
-$opt$setDoubleField
-$opt$setByteField
-$opt$setBooleanField
-$opt$setCharField
-$opt$setShortField
-$opt$getObjectField
-$opt$getIntField
-$opt$getFloatField
-$opt$getLongField
-$opt$getDoubleField
-$opt$getByteField
-$opt$getBooleanField
-$opt$getCharField
-$opt$getShortField
+$opt$noinline$setObjectField
+$opt$noinline$setIntField
+$opt$noinline$setFloatField
+$opt$noinline$setLongField
+$opt$noinline$setDoubleField
+$opt$noinline$setByteField
+$opt$noinline$setBooleanField
+$opt$noinline$setCharField
+$opt$noinline$setShortField
+$opt$noinline$getObjectField
+$opt$noinline$getIntField
+$opt$noinline$getFloatField
+$opt$noinline$getLongField
+$opt$noinline$getDoubleField
+$opt$noinline$getByteField
+$opt$noinline$getBooleanField
+$opt$noinline$getCharField
+$opt$noinline$getShortField
+$opt$noinline$setVolatileObjectField
+$opt$noinline$setVolatileIntField
+$opt$noinline$setVolatileFloatField
+$opt$noinline$setVolatileLongField
+$opt$noinline$setVolatileDoubleField
+$opt$noinline$setVolatileByteField
+$opt$noinline$setVolatileBooleanField
+$opt$noinline$setVolatileCharField
+$opt$noinline$setVolatileShortField
+$opt$noinline$getVolatileObjectField
+$opt$noinline$getVolatileIntField
+$opt$noinline$getVolatileFloatField
+$opt$noinline$getVolatileLongField
+$opt$noinline$getVolatileDoubleField
+$opt$noinline$getVolatileByteField
+$opt$noinline$getVolatileBooleanField
+$opt$noinline$getVolatileCharField
+$opt$noinline$getVolatileShortField
+$opt$noinline$setObjectElement
+$opt$noinline$setIntElement
+$opt$noinline$setFloatElement
+$opt$noinline$setLongElement
+$opt$noinline$setDoubleElement
+$opt$noinline$setByteElement
+$opt$noinline$setBooleanElement
+$opt$noinline$setCharElement
+$opt$noinline$setShortElement
+$opt$noinline$getObjectElement
+$opt$noinline$getIntElement
+$opt$noinline$getFloatElement
+$opt$noinline$getLongElement
+$opt$noinline$getDoubleElement
+$opt$noinline$getByteElement
+$opt$noinline$getBooleanElement
+$opt$noinline$getCharElement
+$opt$noinline$getShortElement
index 40c2645..8f66da0 100644 (file)
  */
 
 public class Main {
+  public static boolean doThrow = false;
 
-  private volatile Object objectField;
-  private volatile int intField;
-  private volatile float floatField;
-  private volatile long longField;
-  private volatile double doubleField;
-  private volatile byte byteField;
-  private volatile boolean booleanField;
-  private volatile char charField;
-  private volatile short shortField;
-
-  public static void $opt$setObjectField(Main m) {
+  private Object objectField;
+  private int intField;
+  private float floatField;
+  private long longField;
+  private double doubleField;
+  private byte byteField;
+  private boolean booleanField;
+  private char charField;
+  private short shortField;
+
+  private volatile Object volatileObjectField;
+  private volatile int volatileIntField;
+  private volatile float volatileFloatField;
+  private volatile long volatileLongField;
+  private volatile double volatileDoubleField;
+  private volatile byte volatileByteField;
+  private volatile boolean volatileBooleanField;
+  private volatile char volatileCharField;
+  private volatile short volatileShortField;
+
+  public static void $opt$noinline$setObjectField(Main m) {
+    if (doThrow) { throw new Error(); }
     m.objectField = null;
   }
 
-  public static void $opt$setIntField(Main m) {
+  public static void $opt$noinline$setIntField(Main m) {
+    if (doThrow) { throw new Error(); }
     m.intField = 0;
   }
 
-  public static void $opt$setFloatField(Main m) {
+  public static void $opt$noinline$setFloatField(Main m) {
+    if (doThrow) { throw new Error(); }
     m.floatField = 0;
   }
 
-  public static void $opt$setLongField(Main m) {
+  public static void $opt$noinline$setLongField(Main m) {
+    if (doThrow) { throw new Error(); }
     m.longField = 0;
   }
 
-  public static void $opt$setDoubleField(Main m) {
+  public static void $opt$noinline$setDoubleField(Main m) {
+    if (doThrow) { throw new Error(); }
     m.doubleField = 0;
   }
 
-  public static void $opt$setByteField(Main m) {
+  public static void $opt$noinline$setByteField(Main m) {
+    if (doThrow) { throw new Error(); }
     m.byteField = 0;
   }
 
-  public static void $opt$setBooleanField(Main m) {
+  public static void $opt$noinline$setBooleanField(Main m) {
+    if (doThrow) { throw new Error(); }
     m.booleanField = false;
   }
 
-  public static void $opt$setCharField(Main m) {
+  public static void $opt$noinline$setCharField(Main m) {
+    if (doThrow) { throw new Error(); }
     m.charField = 0;
   }
 
-  public static void $opt$setShortField(Main m) {
+  public static void $opt$noinline$setShortField(Main m) {
+    if (doThrow) { throw new Error(); }
     m.shortField = 0;
   }
 
-  public static Object $opt$getObjectField(Main m) {
+  public static Object $opt$noinline$getObjectField(Main m) {
+    if (doThrow) { throw new Error(); }
     return m.objectField;
   }
 
-  public static int $opt$getIntField(Main m) {
+  public static int $opt$noinline$getIntField(Main m) {
+    if (doThrow) { throw new Error(); }
     return m.intField;
   }
 
-  public static float $opt$getFloatField(Main m) {
+  public static float $opt$noinline$getFloatField(Main m) {
+    if (doThrow) { throw new Error(); }
     return m.floatField;
   }
 
-  public static long $opt$getLongField(Main m) {
+  public static long $opt$noinline$getLongField(Main m) {
+    if (doThrow) { throw new Error(); }
     return m.longField;
   }
 
-  public static double $opt$getDoubleField(Main m) {
+  public static double $opt$noinline$getDoubleField(Main m) {
+    if (doThrow) { throw new Error(); }
     return m.doubleField;
   }
 
-  public static byte $opt$getByteField(Main m) {
+  public static byte $opt$noinline$getByteField(Main m) {
+    if (doThrow) { throw new Error(); }
     return m.byteField;
   }
 
-  public static boolean $opt$getBooleanField(Main m) {
+  public static boolean $opt$noinline$getBooleanField(Main m) {
+    if (doThrow) { throw new Error(); }
     return m.booleanField;
   }
 
-  public static char $opt$getCharField(Main m) {
+  public static char $opt$noinline$getCharField(Main m) {
+    if (doThrow) { throw new Error(); }
     return m.charField;
   }
 
-  public static short $opt$getShortField(Main m) {
+  public static short $opt$noinline$getShortField(Main m) {
+    if (doThrow) { throw new Error(); }
     return m.shortField;
   }
 
+  public static void $opt$noinline$setVolatileObjectField(Main m) {
+    if (doThrow) { throw new Error(); }
+    m.volatileObjectField = null;
+  }
+
+  public static void $opt$noinline$setVolatileIntField(Main m) {
+    if (doThrow) { throw new Error(); }
+    m.volatileIntField = 0;
+  }
+
+  public static void $opt$noinline$setVolatileFloatField(Main m) {
+    if (doThrow) { throw new Error(); }
+    m.volatileFloatField = 0;
+  }
+
+  public static void $opt$noinline$setVolatileLongField(Main m) {
+    if (doThrow) { throw new Error(); }
+    m.volatileLongField = 0;
+  }
+
+  public static void $opt$noinline$setVolatileDoubleField(Main m) {
+    if (doThrow) { throw new Error(); }
+    m.volatileDoubleField = 0;
+  }
+
+  public static void $opt$noinline$setVolatileByteField(Main m) {
+    if (doThrow) { throw new Error(); }
+    m.volatileByteField = 0;
+  }
+
+  public static void $opt$noinline$setVolatileBooleanField(Main m) {
+    if (doThrow) { throw new Error(); }
+    m.volatileBooleanField = false;
+  }
+
+  public static void $opt$noinline$setVolatileCharField(Main m) {
+    if (doThrow) { throw new Error(); }
+    m.volatileCharField = 0;
+  }
+
+  public static void $opt$noinline$setVolatileShortField(Main m) {
+    if (doThrow) { throw new Error(); }
+    m.volatileShortField = 0;
+  }
+
+  public static Object $opt$noinline$getVolatileObjectField(Main m) {
+    if (doThrow) { throw new Error(); }
+    return m.volatileObjectField;
+  }
+
+  public static int $opt$noinline$getVolatileIntField(Main m) {
+    if (doThrow) { throw new Error(); }
+    return m.volatileIntField;
+  }
+
+  public static float $opt$noinline$getVolatileFloatField(Main m) {
+    if (doThrow) { throw new Error(); }
+    return m.volatileFloatField;
+  }
+
+  public static long $opt$noinline$getVolatileLongField(Main m) {
+    if (doThrow) { throw new Error(); }
+    return m.volatileLongField;
+  }
+
+  public static double $opt$noinline$getVolatileDoubleField(Main m) {
+    if (doThrow) { throw new Error(); }
+    return m.volatileDoubleField;
+  }
+
+  public static byte $opt$noinline$getVolatileByteField(Main m) {
+    if (doThrow) { throw new Error(); }
+    return m.volatileByteField;
+  }
+
+  public static boolean $opt$noinline$getVolatileBooleanField(Main m) {
+    if (doThrow) { throw new Error(); }
+    return m.volatileBooleanField;
+  }
+
+  public static char $opt$noinline$getVolatileCharField(Main m) {
+    if (doThrow) { throw new Error(); }
+    return m.volatileCharField;
+  }
+
+  public static short $opt$noinline$getVolatileShortField(Main m) {
+    if (doThrow) { throw new Error(); }
+    return m.volatileShortField;
+  }
+
+  public static void $opt$noinline$setObjectElement(Object[] a) {
+    if (doThrow) { throw new Error(); }
+    a[0] = null;
+  }
+
+  public static void $opt$noinline$setIntElement(int[] a) {
+    if (doThrow) { throw new Error(); }
+    a[0] = 0;
+  }
+
+  public static void $opt$noinline$setFloatElement(float[] a) {
+    if (doThrow) { throw new Error(); }
+    a[0] = 0;
+  }
+
+  public static void $opt$noinline$setLongElement(long[] a) {
+    if (doThrow) { throw new Error(); }
+    a[0] = 0;
+  }
+
+  public static void $opt$noinline$setDoubleElement(double[] a) {
+    if (doThrow) { throw new Error(); }
+    a[0] = 0;
+  }
+
+  public static void $opt$noinline$setByteElement(byte[] a) {
+    if (doThrow) { throw new Error(); }
+    a[0] = 0;
+  }
+
+  public static void $opt$noinline$setBooleanElement(boolean[] a) {
+    if (doThrow) { throw new Error(); }
+    a[0] = false;
+  }
+
+  public static void $opt$noinline$setCharElement(char[] a) {
+    if (doThrow) { throw new Error(); }
+    a[0] = 0;
+  }
+
+  public static void $opt$noinline$setShortElement(short[] a) {
+    if (doThrow) { throw new Error(); }
+    a[0] = 0;
+  }
+
+  public static Object $opt$noinline$getObjectElement(Object[] a) {
+    if (doThrow) { throw new Error(); }
+    return a[0];
+  }
+
+  public static int $opt$noinline$getIntElement(int[] a) {
+    if (doThrow) { throw new Error(); }
+    return a[0];
+  }
+
+  public static float $opt$noinline$getFloatElement(float[] a) {
+    if (doThrow) { throw new Error(); }
+    return a[0];
+  }
+
+  public static long $opt$noinline$getLongElement(long[] a) {
+    if (doThrow) { throw new Error(); }
+    return a[0];
+  }
+
+  public static double $opt$noinline$getDoubleElement(double[] a) {
+    if (doThrow) { throw new Error(); }
+    return a[0];
+  }
+
+  public static byte $opt$noinline$getByteElement(byte[] a) {
+    if (doThrow) { throw new Error(); }
+    return a[0];
+  }
+
+  public static boolean $opt$noinline$getBooleanElement(boolean[] a) {
+    if (doThrow) { throw new Error(); }
+    return a[0];
+  }
+
+  public static char $opt$noinline$getCharElement(char[] a) {
+    if (doThrow) { throw new Error(); }
+    return a[0];
+  }
+
+  public static short $opt$noinline$getShortElement(short[] a) {
+    if (doThrow) { throw new Error(); }
+    return a[0];
+  }
+
   public static void main(String[] args) {
-    int methodLine = 30;
-    int thisLine = 103;
+    int methodLine = 42;
+    int thisLine = 312;
+    try {
+      $opt$noinline$setObjectField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 2, methodLine, "$opt$noinline$setObjectField");
+    }
+    try {
+      $opt$noinline$setIntField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setIntField");
+    }
+    try {
+      $opt$noinline$setFloatField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setFloatField");
+    }
+    try {
+      $opt$noinline$setLongField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setLongField");
+    }
+    try {
+      $opt$noinline$setDoubleField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setDoubleField");
+    }
+    try {
+      $opt$noinline$setByteField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setByteField");
+    }
+    try {
+      $opt$noinline$setBooleanField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setBooleanField");
+    }
+    try {
+      $opt$noinline$setCharField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setCharField");
+    }
+    try {
+      $opt$noinline$setShortField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setShortField");
+    }
+    try {
+      $opt$noinline$getObjectField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getObjectField");
+    }
+    try {
+      $opt$noinline$getIntField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getIntField");
+    }
+    try {
+      $opt$noinline$getFloatField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getFloatField");
+    }
+    try {
+      $opt$noinline$getLongField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getLongField");
+    }
+    try {
+      $opt$noinline$getDoubleField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getDoubleField");
+    }
+    try {
+      $opt$noinline$getByteField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getByteField");
+    }
+    try {
+      $opt$noinline$getBooleanField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getBooleanField");
+    }
+    try {
+      $opt$noinline$getCharField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getCharField");
+    }
+    try {
+      $opt$noinline$getShortField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getShortField");
+    }
+    try {
+      $opt$noinline$setVolatileObjectField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setVolatileObjectField");
+    }
+    try {
+      $opt$noinline$setVolatileIntField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setVolatileIntField");
+    }
+    try {
+      $opt$noinline$setVolatileFloatField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setVolatileFloatField");
+    }
+    try {
+      $opt$noinline$setVolatileLongField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setVolatileLongField");
+    }
+    try {
+      $opt$noinline$setVolatileDoubleField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setVolatileDoubleField");
+    }
+    try {
+      $opt$noinline$setVolatileByteField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setVolatileByteField");
+    }
+    try {
+      $opt$noinline$setVolatileBooleanField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setVolatileBooleanField");
+    }
+    try {
+      $opt$noinline$setVolatileCharField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setVolatileCharField");
+    }
+    try {
+      $opt$noinline$setVolatileShortField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setVolatileShortField");
+    }
+    try {
+      $opt$noinline$getVolatileObjectField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getVolatileObjectField");
+    }
+    try {
+      $opt$noinline$getVolatileIntField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getVolatileIntField");
+    }
+    try {
+      $opt$noinline$getVolatileFloatField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getVolatileFloatField");
+    }
+    try {
+      $opt$noinline$getVolatileLongField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getVolatileLongField");
+    }
+    try {
+      $opt$noinline$getVolatileDoubleField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getVolatileDoubleField");
+    }
+    try {
+      $opt$noinline$getVolatileByteField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getVolatileByteField");
+    }
+    try {
+      $opt$noinline$getVolatileBooleanField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getVolatileBooleanField");
+    }
+    try {
+      $opt$noinline$getVolatileCharField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getVolatileCharField");
+    }
+    try {
+      $opt$noinline$getVolatileShortField(null);
+      throw new RuntimeException("Failed to throw NullPointerException.");
+    } catch (NullPointerException npe) {
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getVolatileShortField");
+    }
     try {
-      $opt$setObjectField(null);
+      $opt$noinline$setObjectElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 2, methodLine, "$opt$setObjectField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setObjectElement");
     }
     try {
-      $opt$setIntField(null);
+      $opt$noinline$setIntElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$setIntField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setIntElement");
     }
     try {
-      $opt$setFloatField(null);
+      $opt$noinline$setFloatElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$setFloatField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setFloatElement");
     }
     try {
-      $opt$setLongField(null);
+      $opt$noinline$setLongElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$setLongField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setLongElement");
     }
     try {
-      $opt$setDoubleField(null);
+      $opt$noinline$setDoubleElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$setDoubleField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setDoubleElement");
     }
     try {
-      $opt$setByteField(null);
+      $opt$noinline$setByteElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$setByteField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setByteElement");
     }
     try {
-      $opt$setBooleanField(null);
+      $opt$noinline$setBooleanElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$setBooleanField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setBooleanElement");
     }
     try {
-      $opt$setCharField(null);
+      $opt$noinline$setCharElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$setCharField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setCharElement");
     }
     try {
-      $opt$setShortField(null);
+      $opt$noinline$setShortElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$setShortField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$setShortElement");
     }
     try {
-      $opt$getObjectField(null);
+      $opt$noinline$getObjectElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$getObjectField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getObjectElement");
     }
     try {
-      $opt$getIntField(null);
+      $opt$noinline$getIntElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$getIntField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getIntElement");
     }
     try {
-      $opt$getFloatField(null);
+      $opt$noinline$getFloatElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$getFloatField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getFloatElement");
     }
     try {
-      $opt$getLongField(null);
+      $opt$noinline$getLongElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$getLongField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getLongElement");
     }
     try {
-      $opt$getDoubleField(null);
+      $opt$noinline$getDoubleElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$getDoubleField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getDoubleElement");
     }
     try {
-      $opt$getByteField(null);
+      $opt$noinline$getByteElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$getByteField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getByteElement");
     }
     try {
-      $opt$getBooleanField(null);
+      $opt$noinline$getBooleanElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$getBooleanField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getBooleanElement");
     }
     try {
-      $opt$getCharField(null);
+      $opt$noinline$getCharElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$getCharField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getCharElement");
     }
     try {
-      $opt$getShortField(null);
+      $opt$noinline$getShortElement(null);
       throw new RuntimeException("Failed to throw NullPointerException.");
     } catch (NullPointerException npe) {
-      check(npe, thisLine += 6, methodLine += 4, "$opt$getShortField");
+      check(npe, thisLine += 6, methodLine += 5, "$opt$noinline$getShortElement");
     }
   }
 
index 7c124ca..95a11ca 100644 (file)
@@ -51,10 +51,10 @@ public class Main {
 
   /// CHECK-START-X86_64: void Main.arraycopy() disassembly (after)
   /// CHECK:          InvokeStaticOrDirect intrinsic:SystemArrayCopy
-  /// CHECK-NOT:      test
+  /// CHECK-NOT:      test {{^[^\[].*}}, {{^[^\[].*}}
   /// CHECK-NOT:      call
   /// CHECK:          ReturnVoid
-  // Checks that the call is intrinsified and that there is no test instruction
+  // Checks that the call is intrinsified and that there is no register test instruction
   // when we know the source and destination are not null.
   public static void arraycopy() {
     Object[] obj = new Object[4];