1 /*
   2  * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved.
   4  * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
   5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   6  *
   7  * This code is free software; you can redistribute it and/or modify it
   8  * under the terms of the GNU General Public License version 2 only, as
   9  * published by the Free Software Foundation.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  *
  25  */
  26 
  27 #include "code/aotCodeCache.hpp"
  28 #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"
  29 #include "gc/shenandoah/mode/shenandoahMode.hpp"
  30 #include "gc/shenandoah/shenandoahBarrierSet.hpp"
  31 #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp"
  32 #include "gc/shenandoah/shenandoahHeap.inline.hpp"
  33 #include "gc/shenandoah/shenandoahHeapRegion.hpp"
  34 #include "gc/shenandoah/shenandoahRuntime.hpp"
  35 #include "gc/shenandoah/shenandoahThreadLocalData.hpp"
  36 #include "interpreter/interpreter.hpp"
  37 #include "runtime/javaThread.hpp"
  38 #include "runtime/sharedRuntime.hpp"
  39 #include "utilities/macros.hpp"
  40 #ifdef COMPILER1
  41 #include "c1/c1_LIRAssembler.hpp"
  42 #include "c1/c1_MacroAssembler.hpp"
  43 #include "gc/shenandoah/c1/shenandoahBarrierSetC1.hpp"
  44 #endif
  45 #ifdef COMPILER2
  46 #include "gc/shenandoah/c2/shenandoahBarrierSetC2.hpp"
  47 #endif
  48 
  49 #define __ masm->
  50 
  51 void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
  52                                                        Register src, Register dst, Register count) {
  53 
  54   bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0;
  55 
  56   if (is_reference_type(type)) {
  57     if (ShenandoahCardBarrier) {
  58       bool checkcast = (decorators & ARRAYCOPY_CHECKCAST) != 0;
  59       bool disjoint = (decorators & ARRAYCOPY_DISJOINT) != 0;
  60       bool obj_int = (type == T_OBJECT) && UseCompressedOops;
  61 
  62       // We need to save the original element count because the array copy stub
  63       // will destroy the value and we need it for the card marking barrier.
  64       if (!checkcast) {
  65         if (!obj_int) {
  66           // Save count for barrier
  67           __ movptr(r11, count);
  68         } else if (disjoint) {
  69           // Save dst in r11 in the disjoint case
  70           __ movq(r11, dst);
  71         }
  72       }
  73     }
  74 
  75     if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahLoadRefBarrier) {
  76       Register thread = r15_thread;
  77       assert_different_registers(src, dst, count, thread);
  78 
  79       Label L_done;
  80       // Short-circuit if count == 0.
  81       __ testptr(count, count);
  82       __ jcc(Assembler::zero, L_done);
  83 
  84       // Avoid runtime call when not active.
  85       Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
  86       int flags;
  87       if (ShenandoahSATBBarrier && dest_uninitialized) {
  88         flags = ShenandoahHeap::HAS_FORWARDED;
  89       } else {
  90         flags = ShenandoahHeap::HAS_FORWARDED | ShenandoahHeap::MARKING;
  91       }
  92       __ testb(gc_state, flags);
  93       __ jcc(Assembler::zero, L_done);
  94 
  95       __ push_call_clobbered_registers(/* save_fpu = */ false);
  96       // If arguments are not in proper places, shuffle them.
  97       // Doing this via the stack is the most straight-forward way to avoid
  98       // accidentally smashing any register.
  99       if (c_rarg0 != src || c_rarg1 != dst || c_rarg2 != count) {
 100         __ push(src);
 101         __ push(dst);
 102         __ push(count);
 103         __ pop(c_rarg2);
 104         __ pop(c_rarg1);
 105         __ pop(c_rarg0);
 106       }
 107       address target = nullptr;
 108       if (UseCompressedOops) {
 109         target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop);
 110       } else {
 111         target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop);
 112       }
 113       __ call_VM_leaf(target, 3);
 114 
 115       __ pop_call_clobbered_registers(/* restore_fpu = */ false);
 116 
 117       __ bind(L_done);
 118     }
 119   }
 120 
 121 }
 122 
 123 void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
 124                                                        Register src, Register dst, Register count) {
 125 
 126   if (ShenandoahCardBarrier && is_reference_type(type)) {
 127     bool checkcast = (decorators & ARRAYCOPY_CHECKCAST) != 0;
 128     bool disjoint = (decorators & ARRAYCOPY_DISJOINT) != 0;
 129     bool obj_int = (type == T_OBJECT) && UseCompressedOops;
 130     Register tmp = rax;
 131 
 132     if (!checkcast) {
 133       if (!obj_int) {
 134         // Save count for barrier
 135         count = r11;
 136       } else if (disjoint) {
 137         // Use the saved dst in the disjoint case
 138         dst = r11;
 139       }
 140     } else {
 141       tmp = rscratch1;
 142     }
 143     gen_write_ref_array_post_barrier(masm, decorators, dst, count, tmp);
 144   }
 145 }
 146 
 147 void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm,
 148                                                  Register obj,
 149                                                  Register pre_val,
 150                                                  Register tmp) {
 151   assert(ShenandoahSATBBarrier, "Should be checked by caller");
 152   const Register thread = r15_thread;
 153 
 154   Label done;
 155   Label runtime;
 156 
 157   assert(pre_val != noreg, "check this code");
 158   assert_different_registers(obj, pre_val, tmp);
 159 
 160   Address index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()));
 161   Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()));
 162 
 163   Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
 164   __ testb(gc_state, ShenandoahHeap::MARKING);
 165   __ jcc(Assembler::zero, done);
 166 
 167   // Do we need to load the previous value?
 168   if (obj != noreg) {
 169     if (UseCompressedOops) {
 170       __ movl(pre_val, Address(obj, 0));
 171       __ decode_heap_oop(pre_val);
 172     } else {
 173       __ movq(pre_val, Address(obj, 0));
 174     }
 175   }
 176 
 177   // Is the previous value null?
 178   __ cmpptr(pre_val, NULL_WORD);
 179   __ jcc(Assembler::equal, done);
 180 
 181   // Can we store original value in the thread's buffer?
 182   // Is index == 0?
 183   // (The index field is typed as size_t.)
 184 
 185   __ movptr(tmp, index);                   // tmp := *index_adr
 186   __ cmpptr(tmp, 0);                       // tmp == 0?
 187   __ jcc(Assembler::equal, runtime);       // If yes, goto runtime
 188 
 189   __ subptr(tmp, wordSize);                // tmp := tmp - wordSize
 190   __ movptr(index, tmp);                   // *index_adr := tmp
 191   __ addptr(tmp, buffer);                  // tmp := tmp + *buffer_adr
 192 
 193   // Record the previous value
 194   __ movptr(Address(tmp, 0), pre_val);
 195   __ jmp(done);
 196 
 197   __ bind(runtime);
 198 
 199   // Slow-path call.
 200   // Some paths can be reached from the c2i adapter with live fp arguments in registers.
 201   __ enter();
 202   __ push_call_clobbered_registers(/* save_fpu = */ true);
 203 
 204   assert(thread != c_rarg0, "smashed arg");
 205   if (c_rarg0 != pre_val) {
 206     __ mov(c_rarg0, pre_val);
 207   }
 208 
 209   // Calling with super_call_VM_leaf with c_rarg0 bypasses interpreter checks and avoids any moves.
 210   __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), c_rarg0);
 211 
 212   __ pop_call_clobbered_registers(/* restore_fpu = */ true);
 213   __ leave();
 214 
 215   __ bind(done);
 216 }
 217 
 218 void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, Register dst, Address src, DecoratorSet decorators) {
 219   assert(ShenandoahLoadRefBarrier, "Should be enabled");
 220 
 221   bool is_strong  = ShenandoahBarrierSet::is_strong_access(decorators);
 222   bool is_weak    = ShenandoahBarrierSet::is_weak_access(decorators);
 223   bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators);
 224   bool is_native  = ShenandoahBarrierSet::is_native_access(decorators);
 225   bool is_narrow  = UseCompressedOops && !is_native;
 226 
 227   Label heap_stable, not_cset;
 228 
 229   __ block_comment("load_reference_barrier { ");
 230 
 231   // Check if GC is active
 232   Register thread = r15_thread;
 233 
 234   Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
 235   int flags = ShenandoahHeap::HAS_FORWARDED;
 236   if (!is_strong) {
 237     flags |= ShenandoahHeap::WEAK_ROOTS;
 238   }
 239   __ testb(gc_state, flags);
 240   __ jcc(Assembler::zero, heap_stable);
 241 
 242   Register tmp1 = noreg, tmp2 = noreg;
 243   if (is_strong) {
 244     // Test for object in cset
 245     // Allocate temporary registers
 246     for (int i = 0; i < Register::available_gp_registers(); i++) {
 247       Register r = as_Register(i);
 248       if (r != rsp && r != rbp && r != rcx && r != dst && r != src.base() && r != src.index() ) {
 249         if (tmp1 == noreg) {
 250           tmp1 = r;
 251         } else {
 252           tmp2 = r;
 253           break;
 254         }
 255       }
 256     }
 257     assert(tmp1 != noreg, "tmp1 allocated");
 258     assert(tmp2 != noreg, "tmp2 allocated");
 259     assert_different_registers(tmp1, tmp2, src.base(), src.index());
 260     assert_different_registers(tmp1, tmp2, dst);
 261 
 262     __ push(tmp1);
 263     __ push(tmp2);
 264 
 265     // Optimized cset-test
 266     __ movptr(tmp1, dst);
 267 #if INCLUDE_CDS
 268     if (AOTCodeCache::is_on_for_dump()) {
 269       assert_different_registers(tmp1, tmp2, rcx);
 270       __ lea(tmp2, ExternalAddress(AOTRuntimeConstants::grain_shift_address()));
 271       __ push(rcx);
 272       __ movb(rcx, Address(tmp2));
 273       __ shrptr(tmp1);
 274       __ pop(rcx);
 275       __ lea(tmp2, ExternalAddress(AOTRuntimeConstants::cset_base_address()));
 276       __ movptr(tmp2, Address(tmp2));
 277     } else
 278 #endif
 279     {
 280       __ shrptr(tmp1, ShenandoahHeapRegion::region_size_bytes_shift_jint());
 281       __ movptr(tmp2, (intptr_t) ShenandoahHeap::in_cset_fast_test_addr());
 282     }
 283     __ movbool(tmp1, Address(tmp1, tmp2, Address::times_1));
 284     __ testbool(tmp1);
 285     __ jcc(Assembler::zero, not_cset);
 286   }
 287 
 288   // Slow-path call.
 289   // Save registers that can be clobbered by call.
 290   // Some paths can be reached from the c2i adapter with live fp arguments in registers.
 291   __ enter();
 292   if (dst != rax) {
 293     __ push(rax);
 294   }
 295   __ push_call_clobbered_registers_except(rax, /* save_fpu = */ true);
 296 
 297   // Shuffle registers such that dst is in c_rarg0 and addr in c_rarg1.
 298   if (dst == c_rarg1) {
 299     __ lea(c_rarg0, src);
 300     __ xchgptr(c_rarg1, c_rarg0);
 301   } else {
 302     __ lea(c_rarg1, src);
 303     __ movptr(c_rarg0, dst);
 304   }
 305 
 306   address target = nullptr;
 307   if (is_strong) {
 308     if (is_narrow) {
 309       target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow);
 310     } else {
 311       target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong);
 312     }
 313   } else if (is_weak) {
 314     if (is_narrow) {
 315       target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow);
 316     } else {
 317       target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak);
 318     }
 319   } else {
 320     assert(is_phantom, "only remaining strength");
 321     assert(!is_narrow, "phantom access cannot be narrow");
 322     target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom);
 323   }
 324 
 325   // Calling with super_call_VM_leaf with c_rarg0/1 bypasses interpreter checks and avoids any moves.
 326   __ super_call_VM_leaf(target, c_rarg0, c_rarg1);
 327   __ pop_call_clobbered_registers_except(rax, /* restore_fpu = */ true);
 328   if (dst != rax) {
 329     __ movptr(dst, rax);
 330     __ pop(rax);
 331   }
 332   __ leave();
 333 
 334   __ bind(not_cset);
 335 
 336   if  (is_strong) {
 337     __ pop(tmp2);
 338     __ pop(tmp1);
 339   }
 340 
 341   __ bind(heap_stable);
 342 
 343   __ block_comment("} load_reference_barrier");
 344 }
 345 
 346 //
 347 // Arguments:
 348 //
 349 // Inputs:
 350 //   src:        oop location, might be clobbered
 351 //   tmp1:       scratch register, might not be valid.
 352 //
 353 // Output:
 354 //   dst:        oop loaded from src location
 355 //
 356 // Kill:
 357 //   tmp1 (if it is valid)
 358 //
 359 void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
 360              Register dst, Address src, Register tmp1) {
 361   // 1: non-reference load, no additional barrier is needed
 362   if (!is_reference_type(type)) {
 363     BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1);
 364     return;
 365   }
 366 
 367   assert((decorators & ON_UNKNOWN_OOP_REF) == 0, "Not expected");
 368 
 369   // 2: load a reference from src location and apply LRB if needed
 370   if (ShenandoahBarrierSet::need_load_reference_barrier(decorators, type)) {
 371     Register result_dst = dst;
 372     bool use_tmp1_for_dst = false;
 373 
 374     // Preserve src location for LRB
 375     if (dst == src.base() || dst == src.index()) {
 376     // Use tmp1 for dst if possible, as it is not used in BarrierAssembler::load_at()
 377       if (tmp1->is_valid() && tmp1 != src.base() && tmp1 != src.index()) {
 378         dst = tmp1;
 379         use_tmp1_for_dst = true;
 380       } else {
 381         dst = rdi;
 382         __ push(dst);
 383       }
 384       assert_different_registers(dst, src.base(), src.index());
 385     }
 386 
 387     BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1);
 388 
 389     load_reference_barrier(masm, dst, src, decorators);
 390 
 391     // Move loaded oop to final destination
 392     if (dst != result_dst) {
 393       __ movptr(result_dst, dst);
 394 
 395       if (!use_tmp1_for_dst) {
 396         __ pop(dst);
 397       }
 398 
 399       dst = result_dst;
 400     }
 401   } else {
 402     BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1);
 403   }
 404 
 405   // 3: apply keep-alive barrier if needed
 406   if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) {
 407     satb_barrier(masm /* masm */,
 408                  noreg /* obj */,
 409                  dst /* pre_val */,
 410                  tmp1 /* tmp */);
 411   }
 412 }
 413 
 414 void ShenandoahBarrierSetAssembler::card_barrier(MacroAssembler* masm, Register obj, Register tmp) {
 415   assert(ShenandoahCardBarrier, "Should have been checked by caller");
 416   assert_different_registers(obj, tmp);
 417 
 418   // Does a store check for the oop in register obj. The content of
 419   // register obj is destroyed afterwards.
 420   __ shrptr(obj, CardTable::card_shift());
 421 
 422   Address curr_ct_holder_addr(r15_thread, in_bytes(ShenandoahThreadLocalData::card_table_offset()));
 423   __ movptr(tmp, curr_ct_holder_addr);
 424   Address card_addr(tmp, obj, Address::times_1);
 425 
 426   int dirty = CardTable::dirty_card_val();
 427   if (UseCondCardMark) {
 428     Label L_already_dirty;
 429     __ cmpb(card_addr, dirty);
 430     __ jccb(Assembler::equal, L_already_dirty);
 431     __ movb(card_addr, dirty);
 432     __ bind(L_already_dirty);
 433   } else {
 434     __ movb(card_addr, dirty);
 435   }
 436 }
 437 
 438 void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
 439               Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) {
 440 
 441   // 1: non-reference types require no barriers
 442   if (!is_reference_type(type)) {
 443     BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3);
 444     return;
 445   }
 446 
 447   // Flatten object address right away for simplicity: likely needed by barriers
 448   assert_different_registers(val, tmp1, tmp2, tmp3, r15_thread);
 449   if (dst.index() == noreg && dst.disp() == 0) {
 450     if (dst.base() != tmp1) {
 451       __ movptr(tmp1, dst.base());
 452     }
 453   } else {
 454     __ lea(tmp1, dst);
 455   }
 456 
 457   // 2: pre-barrier: SATB needs the previous value
 458   if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) {
 459     satb_barrier(masm,
 460                  tmp1 /* obj */,
 461                  tmp2 /* pre_val */,
 462                  tmp3 /* tmp */);
 463   }
 464 
 465   // Store!
 466   BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg);
 467 
 468   // 3: post-barrier: card barrier needs store address
 469   bool storing_non_null = (val != noreg);
 470   if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) {
 471     card_barrier(masm, tmp1, tmp2);
 472   }
 473 }
 474 
 475 void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env,
 476                                                                   Register obj, Register tmp, Label& slowpath) {
 477   Label done;
 478   // Resolve jobject
 479   BarrierSetAssembler::try_resolve_jobject_in_native(masm, jni_env, obj, tmp, slowpath);
 480 
 481   // Check for null.
 482   __ testptr(obj, obj);
 483   __ jcc(Assembler::zero, done);
 484 
 485   Address gc_state(jni_env, ShenandoahThreadLocalData::gc_state_offset() - JavaThread::jni_environment_offset());
 486   __ testb(gc_state, ShenandoahHeap::EVACUATION);
 487   __ jccb(Assembler::notZero, slowpath);
 488   __ bind(done);
 489 }
 490 
 491 void ShenandoahBarrierSetAssembler::try_peek_weak_handle_in_nmethod(MacroAssembler* masm, Register weak_handle, Register obj, Label& slowpath) {
 492   Label done;
 493 
 494   // Peek weak handle using the standard implementation.
 495   BarrierSetAssembler::try_peek_weak_handle_in_nmethod(masm, weak_handle, obj, slowpath);
 496 
 497   // Check if the reference is null, and if it is, take the fast path.
 498   __ testptr(obj, obj);
 499   __ jcc(Assembler::zero, done);
 500 
 501   Address gc_state(r15_thread, ShenandoahThreadLocalData::gc_state_offset());
 502 
 503   // Check if the heap is under weak-reference/roots processing, in
 504   // which case we need to take the slow path.
 505   __ testb(gc_state, ShenandoahHeap::WEAK_ROOTS);
 506   __ jcc(Assembler::notZero, slowpath);
 507   __ bind(done);
 508 }
 509 
 510 void ShenandoahBarrierSetAssembler::check_oop(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2, Label& L_error) {
 511   assert_different_registers(obj, tmp1, tmp2);
 512   // Check if the oop is in the right area of memory
 513   __ movptr(tmp1, obj);
 514 #if INCLUDE_CDS
 515   if (AOTCodeCache::is_on_for_dump()) {
 516     __ lea(tmp2, ExternalAddress(AOTRuntimeConstants::verify_oop_mask_address()));
 517     __ movptr(tmp2, Address(tmp2));
 518     __ andptr(tmp1, tmp2);
 519     __ lea(tmp2, ExternalAddress(AOTRuntimeConstants::verify_oop_bits_address()));
 520     __ movptr(tmp2, Address(tmp2));
 521   } else
 522 #endif
 523   {
 524     __ movptr(tmp2, (intptr_t) Universe::verify_oop_mask());
 525     __ andptr(tmp1, tmp2);
 526     __ movptr(tmp2, (intptr_t) Universe::verify_oop_bits());
 527   }
 528   __ cmpptr(tmp1, tmp2);
 529   __ jcc(Assembler::notZero, L_error);
 530 
 531   // This routine is sometimes called before applying GC barriers.
 532   // With +COH, loading the klass may end up loading forwarding pointer instead.
 533   Label L_skip;
 534   if (UseCompactObjectHeaders) {
 535     Address gc_state(r15_thread, ShenandoahThreadLocalData::gc_state_offset());
 536     __ testb(gc_state, ShenandoahHeap::HAS_FORWARDED);
 537     __ jcc(Assembler::notZero, L_skip);
 538   }
 539 
 540   // Make sure klass is 'reasonable', which is not zero.
 541   __ load_narrow_klass(tmp1, obj);
 542   __ testl(tmp1, tmp1);
 543   __ jcc(Assembler::zero, L_error);
 544 
 545   __ bind(L_skip);
 546 }
 547 
 548 #ifdef PRODUCT
 549 #define BLOCK_COMMENT(str) /* nothing */
 550 #else
 551 #define BLOCK_COMMENT(str) __ block_comment(str)
 552 #endif
 553 
 554 #define BIND(label) bind(label); BLOCK_COMMENT(#label ":")
 555 
 556 #define TIMES_OOP (UseCompressedOops ? Address::times_4 : Address::times_8)
 557 
 558 void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators,
 559                                                                      Register addr, Register count,
 560                                                                      Register tmp) {
 561   assert(ShenandoahCardBarrier, "Should have been checked by caller");
 562 
 563   Label L_loop, L_done;
 564   const Register end = count;
 565   assert_different_registers(addr, end);
 566 
 567   // Zero count? Nothing to do.
 568   __ testl(count, count);
 569   __ jccb(Assembler::zero, L_done);
 570 
 571   const Register thread = r15_thread;
 572   Address curr_ct_holder_addr(thread, in_bytes(ShenandoahThreadLocalData::card_table_offset()));
 573   __ movptr(tmp, curr_ct_holder_addr);
 574 
 575   __ leaq(end, Address(addr, count, TIMES_OOP, 0));  // end == addr+count*oop_size
 576   __ subptr(end, BytesPerHeapOop); // end - 1 to make inclusive
 577   __ shrptr(addr, CardTable::card_shift());
 578   __ shrptr(end, CardTable::card_shift());
 579   __ subptr(end, addr); // end --> cards count
 580 
 581   __ addptr(addr, tmp);
 582 
 583   __ BIND(L_loop);
 584   __ movb(Address(addr, count, Address::times_1), 0);
 585   __ decrement(count);
 586   __ jccb(Assembler::greaterEqual, L_loop);
 587 
 588   __ BIND(L_done);
 589 }
 590 
 591 #undef __
 592 
 593 #ifdef COMPILER1
 594 
 595 #define __ ce->masm()->
 596 
 597 void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_stub(LIR_Assembler* ce, ShenandoahKeepaliveBarrierStub* stub) {
 598   __ bind(*stub->entry());
 599 
 600   ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1();
 601 
 602   Register obj = stub->obj()->as_register();
 603 
 604   if (stub->do_load()) {
 605     ce->mem2reg(stub->addr(), stub->obj(), T_OBJECT, lir_patch_none, nullptr, /* wide = */ false);
 606   }
 607   __ cmpptr(obj, NULL_WORD);
 608   __ jcc(Assembler::equal, *stub->continuation());
 609 
 610   ce->store_parameter(obj, 0);
 611   __ call(RuntimeAddress(bs->keepalive_barrier_stub()));
 612   __ jmp(*stub->continuation());
 613 }
 614 
 615 void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub) {
 616   __ bind(*stub->entry());
 617 
 618   ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1();
 619 
 620   Register obj = stub->obj()->as_register();
 621   Register addr = stub->addr()->as_pointer_register();
 622   Register slow_result = stub->slow_result()->as_register();
 623   assert_different_registers(obj, addr, slow_result);
 624   assert(slow_result == rax, "C1 must know about our slow call result register");
 625 
 626   ce->store_parameter(obj, 0);
 627   ce->store_parameter(addr, 1);
 628   __ call(RuntimeAddress(bs->load_reference_barrier_stub(stub->decorators())));
 629   if (obj != slow_result) {
 630     __ mov(obj, slow_result);
 631   }
 632 
 633   __ jmp(*stub->continuation());
 634 }
 635 
 636 #undef __
 637 
 638 #define __ sasm->
 639 
 640 void ShenandoahBarrierSetAssembler::keepalive_barrier_c1_runtime_stub(StubAssembler* sasm) {
 641   __ prologue("shenandoah_keepalive_barrier", false);
 642   const Register tmp_obj = rax;
 643   const Register tmp = rdx;
 644   __ push(tmp);
 645   __ push(tmp_obj);
 646   __ load_parameter(0, tmp_obj);
 647   satb_barrier(sasm, noreg, tmp_obj, tmp);
 648   __ pop(tmp_obj);
 649   __ pop(tmp);
 650   __ epilogue();
 651 }
 652 
 653 void ShenandoahBarrierSetAssembler::load_reference_barrier_c1_runtime_stub(StubAssembler* sasm, DecoratorSet decorators) {
 654   __ prologue("shenandoah_load_reference_barrier", false);
 655   const Register tmp_obj = rax;
 656   const Register tmp_addr = rdx;
 657   __ push(tmp_addr);
 658   __ load_parameter(0, tmp_obj);
 659   __ load_parameter(1, tmp_addr);
 660   load_reference_barrier(sasm, tmp_obj, Address(tmp_addr, 0), decorators);
 661   __ pop(tmp_addr);
 662   __ epilogue();
 663 }
 664 
 665 #undef __
 666 
 667 #endif // COMPILER1
 668 
 669 #ifdef COMPILER2
 670 
 671 #undef __
 672 #define __ masm->
 673 
 674 void ShenandoahBarrierSetAssembler::load_c2(const MachNode* node, MacroAssembler* masm, Register dst, Address src, bool narrow) {
 675   // Do the actual load. This load is the candidate for implicit null check, and MUST come first.
 676   if (narrow) {
 677     __ movl(dst, src);
 678   } else {
 679     __ movq(dst, src);
 680   }
 681 
 682   ShenandoahBarrierStubC2::load_post(masm, node, dst, src, noreg, noreg, narrow);
 683 }
 684 
 685 void ShenandoahBarrierSetAssembler::store_c2(const MachNode* node, MacroAssembler* masm,
 686                                              Address dst, bool dst_narrow,
 687                                              Register src, bool src_narrow,
 688                                              Register tmp) {
 689 
 690   ShenandoahBarrierStubC2::store_pre(masm, node, dst, tmp, noreg, noreg, dst_narrow);
 691 
 692   // Need to encode into tmp, because we cannot clobber src.
 693   if (dst_narrow && !src_narrow) {
 694     __ movq(tmp, src);
 695     if ((node->barrier_data() & ShenandoahBitNotNull) == 0) {
 696       __ encode_heap_oop(tmp);
 697     } else {
 698       __ encode_heap_oop_not_null(tmp);
 699     }
 700     src = tmp;
 701   }
 702 
 703   // Do the actual store
 704   if (dst_narrow) {
 705     __ movl(dst, src);
 706   } else {
 707     __ movq(dst, src);
 708   }
 709 
 710   ShenandoahBarrierStubC2::store_post(masm, node, dst, tmp, noreg);
 711 }
 712 
 713 void ShenandoahBarrierSetAssembler::compare_and_set_c2(const MachNode* node, MacroAssembler* masm,
 714                                                        Register res, Address addr,
 715                                                        Register oldval, Register newval, Register tmp,
 716                                                        bool narrow) {
 717 
 718   assert(oldval == rax, "must be in rax for implicit use in cmpxchg");
 719 
 720   // Oldval and newval cannot be clobbered by aliasing with tmp.
 721   assert_different_registers(oldval, tmp);
 722   assert_different_registers(newval, tmp);
 723 
 724   ShenandoahBarrierStubC2::load_store_pre(masm, node, addr, tmp, noreg, noreg, narrow);
 725 
 726   // CAS!
 727   __ lock();
 728   if (narrow) {
 729     __ cmpxchgl(newval, addr);
 730   } else {
 731     __ cmpxchgptr(newval, addr);
 732   }
 733 
 734   // If we need a boolean result out of CAS, set the flag appropriately and promote the result.
 735   if (res != noreg) {
 736     __ setcc(Assembler::equal, res);
 737   }
 738 
 739   ShenandoahBarrierStubC2::load_store_post(masm, node, addr, tmp, noreg);
 740 }
 741 
 742 void ShenandoahBarrierSetAssembler::get_and_set_c2(const MachNode* node, MacroAssembler* masm, Register newval, Address addr, Register tmp, bool narrow) {
 743   assert_different_registers(newval, tmp);
 744 
 745   ShenandoahBarrierStubC2::load_store_pre(masm, node, addr, tmp, noreg, noreg, narrow);
 746 
 747   if (narrow) {
 748     __ xchgl(newval, addr);
 749   } else {
 750     __ xchgq(newval, addr);
 751   }
 752 
 753   ShenandoahBarrierStubC2::load_store_post(masm, node, addr, tmp, noreg);
 754 }
 755 
 756 #undef __
 757 #define __ masm.
 758 
 759 void ShenandoahBarrierStubC2::cardtable(MacroAssembler& masm, Address addr, Register tmp1, Register tmp2) {
 760   Assembler::InlineSkippedInstructionsCounter skip_counter(&masm);
 761 
 762   __ lea(tmp1, addr);
 763   __ shrptr(tmp1, CardTable::card_shift());
 764   __ addptr(tmp1, Address(r15_thread, in_bytes(ShenandoahThreadLocalData::card_table_offset())));
 765   Address card_address(tmp1, 0);
 766 
 767   assert(CardTable::dirty_card_val() == 0, "Encoding assumption");
 768   Label L_done;
 769   if (UseCondCardMark) {
 770     __ cmpb(card_address, 0);
 771     __ jccb(Assembler::equal, L_done);
 772   }
 773   if (UseCompressedOops && CompressedOops::base() == nullptr) {
 774     __ movb(card_address, r12);
 775   } else {
 776     __ movb(card_address, 0);
 777   }
 778   __ bind(L_done);
 779 }
 780 
 781 void ShenandoahBarrierStubC2::enter_if_gc_state(MacroAssembler& masm, const char test_state, Register tmp) {
 782   Assembler::InlineSkippedInstructionsCounter skip_counter(&masm);
 783 
 784   Address gc_state_fast(r15_thread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(test_state)));
 785   __ cmpb(gc_state_fast, 0);
 786   __ jcc(Assembler::notEqual, *entry());
 787   __ bind(*continuation());
 788 }
 789 
 790 void ShenandoahBarrierStubC2::emit_code(MacroAssembler& masm) {
 791   Assembler::InlineSkippedInstructionsCounter skip_counter(&masm);
 792   assert(_needs_keep_alive_barrier || _needs_load_ref_barrier, "Why are you here?");
 793 
 794   // On x86, there is a significant penalty with unaligned branch target, for example
 795   // when the target instruction straggles the fetch line. It makes (performance) sense
 796   // to spend some code size to align the target better.
 797   __ align(16);
 798   __ bind(*entry());
 799 
 800   // If we need to load ourselves, do it here.
 801   if (_do_load) {
 802     if (_narrow) {
 803       __ movl(_obj, _addr);
 804     } else {
 805       __ movq(_obj, _addr);
 806     }
 807   }
 808 
 809   // If the object is null, there is no point in applying barriers.
 810   maybe_far_jump_if_zero(masm, _obj);
 811 
 812   // We need to make sure that loads done by callers survive across slow-path calls.
 813   // For self-loads, we need to care about the case when both KA and LRB are enabled (rare).
 814   bool needs_both_barriers = _needs_keep_alive_barrier && _needs_load_ref_barrier;
 815   if (!_do_load || needs_both_barriers) {
 816     preserve(_obj);
 817   }
 818 
 819   // Go for barriers. Barriers can return straight to continuation, as long
 820   // as another barrier is not needed.
 821   if (needs_both_barriers) {
 822     keepalive(masm, nullptr);
 823     lrb(masm);
 824   } else if (_needs_keep_alive_barrier) {
 825     keepalive(masm, continuation());
 826   } else if (_needs_load_ref_barrier) {
 827     lrb(masm);
 828   } else {
 829     ShouldNotReachHere();
 830   }
 831 }
 832 
 833 void ShenandoahBarrierStubC2::keepalive(MacroAssembler& masm, Label* L_done) {
 834   Address gc_state_fast(r15_thread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(ShenandoahHeap::MARKING)));
 835   Address index(r15_thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()));
 836   Address buffer(r15_thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()));
 837 
 838   Label L_through, L_pop_and_slow;
 839 
 840   // If another barrier is enabled as well, do a runtime check for a specific barrier.
 841   if (_needs_load_ref_barrier) {
 842     assert(L_done == nullptr, "L_done is always null when _needs_load_ref_barrier is true");
 843     __ cmpb(gc_state_fast, 0);
 844     __ jcc(Assembler::equal, L_through);
 845   }
 846 
 847   // Need temp to work, allocate one now.
 848   bool tmp_live;
 849   Register tmp = select_temp_register(tmp_live);
 850   if (tmp_live) {
 851     __ push(tmp);
 852   }
 853 
 854   // Fast-path: put object into buffer.
 855   // If buffer is already full, go slow.
 856   __ movptr(tmp, index);
 857   __ subptr(tmp, wordSize);
 858   // VerifyOops adds code for decoded oop and needs long jump here
 859   __ jcc(Assembler::below, L_pop_and_slow);
 860   __ movptr(index, tmp);
 861   __ addptr(tmp, buffer);
 862 
 863   // Store the object in queue.
 864   // If object is narrow, we need to decode it before inserting.
 865   // We can skip the re-encoding if we know that object is not preserved.
 866   if (_narrow) {
 867     __ decode_heap_oop_not_null(_obj);
 868   }
 869   __ movptr(Address(tmp, 0), _obj);
 870   if (_narrow && is_preserved(_obj)) {
 871     __ encode_heap_oop_not_null(_obj);
 872   }
 873 
 874   // Fast-path exits here.
 875   if (tmp_live) {
 876     __ pop(tmp);
 877   }
 878 
 879   if (L_done != nullptr) {
 880     __ jmp(*L_done);
 881   } else {
 882     __ jmp(L_through);
 883   }
 884 
 885   // Slow-path: call runtime to handle.
 886   // Need to pop tmp immediately for stack to remain aligned.
 887   __ bind(L_pop_and_slow);
 888   if (tmp_live) {
 889     __ pop(tmp);
 890   }
 891   {
 892     SaveLiveRegisters slr(&masm, this);
 893 
 894     // Shuffle in the arguments. The end result should be:
 895     //   c_rarg0 <-- obj
 896     if (c_rarg0 != _obj) {
 897       __ mov(c_rarg0, _obj);
 898     }
 899 
 900     // Go to runtime and handle the rest there.
 901     // Use rax as scratch, as it will be saved if live.
 902     __ call(RuntimeAddress(keepalive_runtime_entry_addr()), rax);
 903   }
 904   if (L_done != nullptr) {
 905     __ jmp(*L_done);
 906   } else {
 907     __ bind(L_through);
 908   }
 909 }
 910 
 911 void ShenandoahBarrierStubC2::lrb(MacroAssembler& masm) {
 912   Label L_pop_and_slow, L_slow;
 913 
 914   // If another barrier is enabled as well, do a runtime check for a specific barrier.
 915   if (_needs_keep_alive_barrier) {
 916     char state_to_check = ShenandoahHeap::HAS_FORWARDED | (_needs_load_ref_weak_barrier ? ShenandoahHeap::WEAK_ROOTS : 0);
 917     Address gc_state_fast(r15_thread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(state_to_check)));
 918     __ cmpb(gc_state_fast, 0);
 919     __ jcc(Assembler::equal, *continuation());
 920   }
 921 
 922   // If weak references are being processed, weak/phantom loads need to go slow,
 923   // regardless of their cset status.
 924   if (_needs_load_ref_weak_barrier) {
 925     Address gc_state_fast(r15_thread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_array_offset(ShenandoahHeap::WEAK_ROOTS)));
 926     __ cmpb(gc_state_fast, 0);
 927     __ jccb(Assembler::notEqual, L_slow);
 928   }
 929 
 930   bool is_aot = AOTCodeCache::is_on_for_dump();
 931 
 932   // Need temp to work, allocate one now.
 933   bool tmp_live;
 934   Register tmp = select_temp_register(tmp_live, /* skip_reg1 = */ is_aot ? rcx : noreg);
 935   if (tmp_live) {
 936     __ push(tmp);
 937   }
 938 
 939   // Compute the cset bitmap index
 940   if (_narrow) {
 941     __ decode_heap_oop_not_null(tmp, _obj);
 942   } else {
 943     __ movptr(tmp, _obj);
 944   }
 945 
 946   Address cset_addr_arg;
 947   intptr_t cset_addr = reinterpret_cast<intptr_t>(ShenandoahHeap::in_cset_fast_test_addr());
 948   if (!is_aot && cset_addr < INT32_MAX) {
 949     // Cset bitmap is at easily encodeable address. Just use it as displacement.
 950     __ shrptr(tmp, ShenandoahHeapRegion::region_size_bytes_shift_jint());
 951     cset_addr_arg = Address(tmp, checked_cast<int>(cset_addr));
 952   } else {
 953     bool tmp2_live;
 954     Register tmp2 = select_temp_register(tmp2_live, /* skip_reg1 = */ tmp, /* skip_reg2 = */ is_aot ? rcx : noreg);
 955     if (tmp2_live) {
 956       __ push(tmp2);
 957     }
 958     if (is_aot) {
 959       // Generating AOT code, pull the cset bitmap and region shift from AOT table.
 960       assert_different_registers(tmp, tmp2, rcx);
 961       __ push(rcx);
 962       __ lea(rcx, ExternalAddress(AOTRuntimeConstants::grain_shift_address()));
 963       __ movl(rcx, Address(rcx));
 964       __ shrptr(tmp);
 965       __ pop(rcx);
 966       __ lea(tmp2, ExternalAddress(AOTRuntimeConstants::cset_base_address()));
 967       __ addptr(tmp, Address(tmp2));
 968     } else {
 969       // Cset bitmap is far away. Add its address fully.
 970       __ shrptr(tmp, ShenandoahHeapRegion::region_size_bytes_shift_jint());
 971       __ movptr(tmp2, cset_addr);
 972       __ addptr(tmp, tmp2);
 973     }
 974     if (tmp2_live) {
 975       __ pop(tmp2);
 976     }
 977     cset_addr_arg = Address(tmp, 0);
 978   }
 979 
 980   // Cset-check. Fall-through to slow if in collection set.
 981   __ cmpb(cset_addr_arg, 0);
 982   if (tmp_live) {
 983     __ jccb(Assembler::notEqual, L_pop_and_slow);
 984     __ pop(tmp);
 985     __ jmp(*continuation());
 986   } else {
 987     // Nothing else to do, jump back
 988     __ jcc(Assembler::equal, *continuation());
 989   }
 990 
 991   // Slow path
 992   __ bind(L_pop_and_slow);
 993   // Need to pop tmp immediately for stack to remain aligned.
 994   if (tmp_live) {
 995     __ pop(tmp);
 996   }
 997   __ bind(L_slow);
 998 
 999   // Obj is the result, need to temporarily stop preserving it.
1000   bool is_obj_preserved = is_preserved(_obj);
1001   if (is_obj_preserved) {
1002     dont_preserve(_obj);
1003   }
1004   {
1005     SaveLiveRegisters slr(&masm, this);
1006 
1007     assert_different_registers(rax, c_rarg0, c_rarg1);
1008 
1009     // Shuffle in the arguments. The end result should be:
1010     //   c_rarg0 <-- obj
1011     //   c_rarg1 <-- lea(addr)
1012     if (_obj == c_rarg0) {
1013       __ lea(c_rarg1, _addr);
1014     } else if (_obj == c_rarg1) {
1015       // Set up arguments in reverse, and then flip them
1016       __ lea(c_rarg0, _addr);
1017       __ xchgptr(c_rarg0, c_rarg1);
1018     } else {
1019       assert_different_registers(_obj, c_rarg0, c_rarg1);
1020       __ lea(c_rarg1, _addr);
1021       __ movptr(c_rarg0, _obj);
1022     }
1023 
1024     // Go to runtime and handle the rest there.
1025     // Use rax as scratch, as it will be clobbered by result anyway.
1026     __ call(RuntimeAddress(lrb_runtime_entry_addr()), rax);
1027 
1028     // Save the result where needed.
1029     if (_narrow) {
1030       __ movl(_obj, rax);
1031     } else if (_obj != rax) {
1032       __ movptr(_obj, rax);
1033     }
1034   }
1035   if (is_obj_preserved) {
1036     preserve(_obj);
1037   }
1038 
1039   __ jmp(*continuation());
1040 }
1041 
1042 int ShenandoahBarrierStubC2::available_gp_registers() {
1043   return Register::available_gp_registers();
1044 }
1045 
1046 bool ShenandoahBarrierStubC2::is_special_register(Register r) {
1047   return r == rsp || r == rbp || r == r12_heapbase || r == r15_thread;
1048 }
1049 
1050 void ShenandoahBarrierStubC2::post_init() {
1051   // Do nothing.
1052 }
1053 
1054 void ShenandoahBarrierStubC2::maybe_far_jump_if_zero(MacroAssembler& masm, Register reg) {
1055   if (_narrow) {
1056     __ testl(reg, reg);
1057   } else {
1058     __ testq(reg, reg);
1059   }
1060   __ jcc(Assembler::zero, *continuation());
1061 }
1062 
1063 #endif // COMPILER2