< prev index next >

src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp

Print this page
@@ -28,14 +28,16 @@
  #include "gc/shenandoah/mode/shenandoahMode.hpp"
  #include "gc/shenandoah/shenandoahBarrierSet.hpp"
  #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp"
  #include "gc/shenandoah/shenandoahHeap.inline.hpp"
  #include "gc/shenandoah/shenandoahHeapRegion.hpp"
+ #include "gc/shenandoah/shenandoahNMethod.inline.hpp"
  #include "gc/shenandoah/shenandoahRuntime.hpp"
  #include "gc/shenandoah/shenandoahThreadLocalData.hpp"
  #include "interpreter/interp_masm.hpp"
  #include "interpreter/interpreter.hpp"
+ #include "nativeInst_aarch64.hpp"
  #include "runtime/javaThread.hpp"
  #include "runtime/sharedRuntime.hpp"
  #ifdef COMPILER1
  #include "c1/c1_LIRAssembler.hpp"
  #include "c1/c1_MacroAssembler.hpp"

@@ -664,34 +666,99 @@
    } else {
      __ strb(zr, Address(tmp2));
    }
  }
  
+ void ShenandoahBarrierStubC2::patchable_jump_if_gc_state(MacroAssembler& masm, const char test_state, Label* L_target) {
+   // Emit the unconditional branch in the first version of the method.
+   // Let the rest of runtime figure out how to manage it.
+   __ relocate(patchable_barrier_Relocation::spec(ShenandoahNMethod::encode_to_reloc(test_state, false)));
+   __ b(*L_target);
+ }
+ 
+ void ShenandoahBarrierStubC2::patchable_jump_if_not_gc_state(MacroAssembler& masm, const char test_state, Label* L_target) {
+   // Emit the unconditional branch in the first version of the method.
+   // Let the rest of runtime figure out how to manage it.
+   __ relocate(patchable_barrier_Relocation::spec(ShenandoahNMethod::encode_to_reloc(test_state, true)));
+   __ b(*L_target);
+ }
+ 
  void ShenandoahBarrierStubC2::enter_if_gc_state(MacroAssembler& masm, const char test_state, Register tmp) {
    Assembler::InlineSkippedInstructionsCounter skip_counter(&masm);
    PhaseOutput* const output = Compile::current()->output();
-   Address gc_state_fast(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(test_state)));
  
-   // We piggyback on scratch_emit_size mode to compute the slowpath stub size.
-   // We'll use that information to decide whether we need a far jump to the
-   // stub entry point or not. In scratch_emit_size mode we don't bind entry()
-   // because otherwise it will be rebound when we later emit the instructions
-   // for real.
-   if (_needs_far_jump) {
-     __ ldrb(tmp, gc_state_fast);
-     __ cbz(tmp, *continuation());
-     __ b(output->in_scratch_emit_size() ? *continuation() : *entry());
+   if (output->in_scratch_emit_size()) {
+     // We piggyback on scratch_emit_size mode to compute the slowpath stub size.
+     // We'll use that information to decide whether we need a far jump to the
+     // stub entry point or not. In scratch_emit_size mode we don't bind entry()
+     // because otherwise it will be rebound when we later emit the instructions
+     // for real.
+     __ nop();
    } else {
-     __ ldrb(tmp, gc_state_fast);
-     __ cbnz(tmp, output->in_scratch_emit_size() ? *continuation() : *entry());
+     patchable_jump_if_gc_state(masm, test_state, entry());
    }
  
    // This is were the slowpath stub will return to or the code above will
    // jump to if the checks are false
    __ bind(*continuation());
  }
  
+ address ShenandoahBarrierSetAssembler::parse_stub_address(address pc) {
+   NativeInstruction* ni = nativeInstruction_at(pc);
+   assert(ni->is_jump(), "Initial code version: GC barrier fastpath must be a jump");
+   NativeJump* jmp = nativeJump_at(pc);
+   return jmp->jump_destination();
+ }
+ 
+ static bool is_patchable_nop(address pc) {
+   if (*(pc + 0) != 0x1F) return false;
+   if (*(pc + 1) != 0x20) return false;
+   if (*(pc + 2) != 0x03) return false;
+   if (*(pc + 3) != 0xD5) return false;
+   return true;
+ }
+ 
+ static void insert_patchable_nop(address pc) {
+   *reinterpret_cast<int32_t*>(pc) = 0xD503201F;
+ }
+ 
+ static void insert_patchable_jump(address code_pos, address entry) {
+   intptr_t disp = (intptr_t)entry - ((intptr_t)code_pos);
+   int64_t imm26 = disp >> 2;
+   guarantee(Assembler::is_simm(imm26, 26), "maximum offset is 128MiB, requested %ld", imm26);
+ 
+   uint32_t new_val = 0x14000000 | (imm26 & 0x03FFFFFF);
+   AtomicAccess::store((uint32_t*)code_pos, new_val);
+ }
+ 
+ static void check_at(bool cond, address pc, const char* msg) {
+   assert(cond, "%s: at PC " PTR_FORMAT ": %02x%02x%02x%02x",
+          msg, p2i(pc), *(pc + 0), *(pc + 1), *(pc + 2), *(pc + 3));
+ }
+ 
+ bool ShenandoahBarrierSetAssembler::patch_branch_to_nop(address pc) {
+   NativeInstruction* ni = nativeInstruction_at(pc);
+   bool patching = ni->is_jump();
+   if (patching) {
+     insert_patchable_nop(pc);
+   }
+   check_at(is_patchable_nop(pc), pc, "Should be nop");
+   return patching;
+ }
+ 
+ bool ShenandoahBarrierSetAssembler::patch_nop_to_branch(address pc, address stub_addr) {
+   bool patching = is_patchable_nop(pc);
+   if (patching) {
+     insert_patchable_jump(pc, stub_addr);
+   }
+ 
+   NativeInstruction* ni = nativeInstruction_at(pc);
+   check_at(ni->is_jump(), pc, "Should be jump");
+   check_at(nativeJump_at(pc)->jump_destination() == stub_addr, pc, "Jump should be to the same address");
+   return patching;
+ }
+ 
  void ShenandoahBarrierStubC2::emit_code(MacroAssembler& masm) {
    Assembler::InlineSkippedInstructionsCounter skip_counter(&masm);
    assert(_needs_keep_alive_barrier || _needs_load_ref_barrier, "Why are you here?");
    PhaseOutput* const output = Compile::current()->output();
  

@@ -758,20 +825,19 @@
      __ cbz(reg, *continuation());
    }
  }
  
  void ShenandoahBarrierStubC2::keepalive(MacroAssembler& masm, Label* L_done) {
-   Address gcstate(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(ShenandoahHeap::MARKING)));
    Address index(rthread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()));
    Address buffer(rthread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()));
    Label L_through, L_slowpath;
  
-   // If another barrier is enabled as well, do a runtime check for a specific barrier.
+   // If another barrier is enabled as well, do a check for a specific barrier.
    if (_needs_load_ref_barrier) {
-     assert(L_done == nullptr, "L_done is always null when _needs_load_ref_barrier is true");
-     __ ldrb(_tmp1, gcstate);
-     __ cbz(_tmp1, L_through);
+     assert(L_done == nullptr, "Should be");
+     char state_to_check = ShenandoahHeap::MARKING;
+     patchable_jump_if_not_gc_state(masm, state_to_check, &L_through);
    }
  
    // Fast-path: put object into buffer.
    // If buffer is already full, go slow.
    __ ldr(_tmp1, index);

@@ -817,24 +883,21 @@
  }
  
  void ShenandoahBarrierStubC2::lrb(MacroAssembler& masm) {
    Label L_slow;
  
-   // If another barrier is enabled as well, do a runtime check for a specific barrier.
+   // If another barrier is enabled as well, do a check for a specific barrier.
    if (_needs_keep_alive_barrier) {
      char state_to_check = ShenandoahHeap::HAS_FORWARDED | (_needs_load_ref_weak_barrier ? ShenandoahHeap::WEAK_ROOTS : 0);
-     Address gc_state_fast(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(state_to_check)));
-     __ ldrb(_tmp1, gc_state_fast);
-     maybe_far_jump_if_zero(masm, _tmp1);
+     patchable_jump_if_not_gc_state(masm, state_to_check, continuation());
    }
  
    // If weak references are being processed, weak/phantom loads need to go slow,
    // regardless of their cset status.
    if (_needs_load_ref_weak_barrier) {
-     Address gc_state_fast(rthread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(ShenandoahHeap::WEAK_ROOTS)));
-     __ ldrb(_tmp1, gc_state_fast);
-     __ cbnz(_tmp1, L_slow);
+     char state_to_check = ShenandoahHeap::WEAK_ROOTS;
+     patchable_jump_if_gc_state(masm, state_to_check, &L_slow);
    }
  
    // Cset-check. Fall-through to slow if in collection set.
    bool is_aot = AOTCodeCache::is_on_for_dump();
    if (!is_aot) {
< prev index next >