1 /*
  2  * Copyright (c) 2018, 2020, Red Hat, Inc. All rights reserved.
  3  * Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. All rights reserved.
  4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  5  *
  6  * This code is free software; you can redistribute it and/or modify it
  7  * under the terms of the GNU General Public License version 2 only, as
  8  * published by the Free Software Foundation.
  9  *
 10  * This code is distributed in the hope that it will be useful, but WITHOUT
 11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 13  * version 2 for more details (a copy is included in the LICENSE file that
 14  * accompanied this code).
 15  *
 16  * You should have received a copy of the GNU General Public License version
 17  * 2 along with this work; if not, write to the Free Software Foundation,
 18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 19  *
 20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 21  * or visit www.oracle.com if you need additional information or have any
 22  * questions.
 23  *
 24  */
 25 
 26 #include "precompiled.hpp"
 27 #include "gc/shenandoah/shenandoahBarrierSet.hpp"
 28 #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp"
 29 #include "gc/shenandoah/shenandoahForwarding.hpp"
 30 #include "gc/shenandoah/shenandoahHeap.inline.hpp"
 31 #include "gc/shenandoah/shenandoahHeapRegion.hpp"
 32 #include "gc/shenandoah/shenandoahRuntime.hpp"
 33 #include "gc/shenandoah/shenandoahThreadLocalData.hpp"
 34 #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"
 35 #include "interpreter/interpreter.hpp"
 36 #include "interpreter/interp_masm.hpp"
 37 #include "runtime/sharedRuntime.hpp"
 38 #include "runtime/thread.hpp"
 39 #ifdef COMPILER1
 40 #include "c1/c1_LIRAssembler.hpp"
 41 #include "c1/c1_MacroAssembler.hpp"
 42 #include "gc/shenandoah/c1/shenandoahBarrierSetC1.hpp"
 43 #endif
 44 
 45 #define __ masm->
 46 
 47 void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop,
 48                                                        Register src, Register dst, Register count, RegSet saved_regs) {
 49   if (is_oop) {
 50     bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0;
 51     if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahIUBarrier || ShenandoahLoadRefBarrier) {
 52 
 53       Label done;
 54 
 55       // Avoid calling runtime if count == 0
 56       __ beqz(count, done);
 57 
 58       // Is GC active?
 59       Address gc_state(xthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
 60       assert_different_registers(src, dst, count, t0);
 61 
 62       __ lbu(t0, gc_state);
 63       if (ShenandoahSATBBarrier && dest_uninitialized) {
 64         __ andi(t0, t0, ShenandoahHeap::HAS_FORWARDED);
 65         __ beqz(t0, done);
 66       } else {
 67         __ andi(t0, t0, ShenandoahHeap::HAS_FORWARDED | ShenandoahHeap::MARKING);
 68         __ beqz(t0, done);
 69       }
 70 
 71       __ push_reg(saved_regs, sp);
 72       if (UseCompressedOops) {
 73         __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop_entry),
 74                         src, dst, count);
 75       } else {
 76         __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop_entry), src, dst, count);
 77       }
 78       __ pop_reg(saved_regs, sp);
 79       __ bind(done);
 80     }
 81   }
 82 }
 83 
 84 void ShenandoahBarrierSetAssembler::shenandoah_write_barrier_pre(MacroAssembler* masm,
 85                                                                  Register obj,
 86                                                                  Register pre_val,
 87                                                                  Register thread,
 88                                                                  Register tmp,
 89                                                                  bool tosca_live,
 90                                                                  bool expand_call) {
 91   if (ShenandoahSATBBarrier) {
 92     satb_write_barrier_pre(masm, obj, pre_val, thread, tmp, tosca_live, expand_call);
 93   }
 94 }
 95 
 96 void ShenandoahBarrierSetAssembler::satb_write_barrier_pre(MacroAssembler* masm,
 97                                                            Register obj,
 98                                                            Register pre_val,
 99                                                            Register thread,
100                                                            Register tmp,
101                                                            bool tosca_live,
102                                                            bool expand_call) {
103   // If expand_call is true then we expand the call_VM_leaf macro
104   // directly to skip generating the check by
105   // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp.
106   assert(thread == xthread, "must be");
107 
108   Label done;
109   Label runtime;
110 
111   assert_different_registers(obj, pre_val, tmp, t0);
112   assert(pre_val != noreg &&  tmp != noreg, "expecting a register");
113 
114   Address in_progress(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_active_offset()));
115   Address index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()));
116   Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()));
117 
118   // Is marking active?
119   if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) {
120     __ lwu(tmp, in_progress);
121   } else {
122     assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption");
123     __ lbu(tmp, in_progress);
124   }
125   __ beqz(tmp, done);
126 
127   // Do we need to load the previous value?
128   if (obj != noreg) {
129     __ load_heap_oop(pre_val, Address(obj, 0), noreg, noreg, AS_RAW);
130   }
131 
132   // Is the previous value null?
133   __ beqz(pre_val, done);
134 
135   // Can we store original value in the thread's buffer?
136   // Is index == 0?
137   // (The index field is typed as size_t.)
138   __ ld(tmp, index);                        // tmp := *index_adr
139   __ beqz(tmp, runtime);                    // tmp == 0? If yes, goto runtime
140 
141   __ sub(tmp, tmp, wordSize);               // tmp := tmp - wordSize
142   __ sd(tmp, index);                        // *index_adr := tmp
143   __ ld(t0, buffer);
144   __ add(tmp, tmp, t0);                     // tmp := tmp + *buffer_adr
145 
146   // Record the previous value
147   __ sd(pre_val, Address(tmp, 0));
148   __ j(done);
149 
150   __ bind(runtime);
151   // save the live input values
152   RegSet saved = RegSet::of(pre_val);
153   if (tosca_live) saved += RegSet::of(x10);
154   if (obj != noreg) saved += RegSet::of(obj);
155 
156   __ push_reg(saved, sp);
157 
158   // Calling the runtime using the regular call_VM_leaf mechanism generates
159   // code (generated by InterpreterMacroAssember::call_VM_leaf_base)
160   // that checks that the *(rfp+frame::interpreter_frame_last_sp) == NULL.
161   //
162   // If we care generating the pre-barrier without a frame (e.g. in the
163   // intrinsified Reference.get() routine) then ebp might be pointing to
164   // the caller frame and so this check will most likely fail at runtime.
165   //
166   // Expanding the call directly bypasses the generation of the check.
167   // So when we do not have have a full interpreter frame on the stack
168   // expand_call should be passed true.
169   if (expand_call) {
170     assert(pre_val != c_rarg1, "smashed arg");
171     __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), pre_val, thread);
172   } else {
173     __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), pre_val, thread);
174   }
175 
176   __ pop_reg(saved, sp);
177 
178   __ bind(done);
179 }
180 
181 void ShenandoahBarrierSetAssembler::resolve_forward_pointer(MacroAssembler* masm, Register dst, Register tmp) {
182   assert(ShenandoahLoadRefBarrier || ShenandoahCASBarrier, "Should be enabled");
183 
184   Label is_null;
185   __ beqz(dst, is_null);
186   resolve_forward_pointer_not_null(masm, dst, tmp);
187   __ bind(is_null);
188 }
189 
190 // IMPORTANT: This must preserve all registers, even t0 and t1, except those explicitely
191 // passed in.
192 void ShenandoahBarrierSetAssembler::resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp) {
193   assert(ShenandoahLoadRefBarrier || ShenandoahCASBarrier, "Should be enabled");
194   // The below loads the mark word, checks if the lowest two bits are
195   // set, and if so, clear the lowest two bits and copy the result
196   // to dst. Otherwise it leaves dst alone.
197   // Implementing this is surprisingly awkward. I do it here by:
198   // - Inverting the mark word
199   // - Test lowest two bits == 0
200   // - If so, set the lowest two bits
201   // - Invert the result back, and copy to dst
202   RegSet saved_regs = RegSet::of(t2);
203   bool borrow_reg = (tmp == noreg);
204   if (borrow_reg) {
205     // No free registers available. Make one useful.
206     tmp = t0;
207     if (tmp == dst) {
208       tmp = t1;
209     }
210     saved_regs += RegSet::of(tmp);
211   }
212 
213   assert_different_registers(tmp, dst, t2);
214   __ push_reg(saved_regs, sp);
215 
216   Label done;
217   __ ld(tmp, Address(dst, oopDesc::mark_offset_in_bytes()));
218   __ xori(tmp, tmp, -1); // eon with 0 is equivalent to XOR with -1
219   __ andi(t2, tmp, markWord::lock_mask_in_place);
220   __ bnez(t2, done);
221   __ ori(tmp, tmp, markWord::marked_value);
222   __ xori(dst, tmp, -1); // eon with 0 is equivalent to XOR with -1
223   __ bind(done);
224 
225   __ pop_reg(saved_regs, sp);
226 }
227 
228 void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm,
229                                                            Register dst,
230                                                            Address load_addr,
231                                                            DecoratorSet decorators) {
232   assert(ShenandoahLoadRefBarrier, "Should be enabled");
233   assert(dst != t1 && load_addr.base() != t1, "need t1");
234   assert_different_registers(load_addr.base(), t0, t1);
235 
236   bool is_strong  = ShenandoahBarrierSet::is_strong_access(decorators);
237   bool is_weak    = ShenandoahBarrierSet::is_weak_access(decorators);
238   bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators);
239   bool is_native  = ShenandoahBarrierSet::is_native_access(decorators);
240   bool is_narrow  = UseCompressedOops && !is_native;
241 
242   Label heap_stable, not_cset;
243   __ enter();
244   Address gc_state(xthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
245   __ lbu(t1, gc_state);
246 
247   // Check for heap stability
248   if (is_strong) {
249     __ andi(t1, t1, ShenandoahHeap::HAS_FORWARDED);
250     __ beqz(t1, heap_stable);
251   } else {
252     Label lrb;
253     __ andi(t0, t1, ShenandoahHeap::WEAK_ROOTS);
254     __ bnez(t0, lrb);
255     __ andi(t0, t1, ShenandoahHeap::HAS_FORWARDED);
256     __ beqz(t0, heap_stable);
257     __ bind(lrb);
258   }
259 
260   // use x11 for load address
261   Register result_dst = dst;
262   if (dst == x11) {
263     __ mv(t1, dst);
264     dst = t1;
265   }
266 
267   // Save x10 and x11, unless it is an output register
268   RegSet saved_regs = RegSet::of(x10, x11) - result_dst;
269   __ push_reg(saved_regs, sp);
270   __ la(x11, load_addr);
271   __ mv(x10, dst);
272 
273   // Test for in-cset
274   if (is_strong) {
275     __ li(t1, (uint64_t)ShenandoahHeap::in_cset_fast_test_addr());
276     __ srli(t0, x10, ShenandoahHeapRegion::region_size_bytes_shift_jint());
277     __ add(t1, t1, t0);
278     __ lbu(t1, Address(t1));
279     __ andi(t0, t1, 1);
280     __ beqz(t0, not_cset);
281   }
282 
283   __ push_call_clobbered_registers();
284   if (is_strong) {
285     if (is_narrow) {
286       __ li(lr, (int64_t)(uintptr_t)ShenandoahRuntime::load_reference_barrier_strong_narrow);
287     } else {
288       __ li(lr, (int64_t)(uintptr_t)ShenandoahRuntime::load_reference_barrier_strong);
289     }
290   } else if (is_weak) {
291     if (is_narrow) {
292       __ li(lr, (int64_t)(uintptr_t)ShenandoahRuntime::load_reference_barrier_weak_narrow);
293     } else {
294       __ li(lr, (int64_t)(uintptr_t)ShenandoahRuntime::load_reference_barrier_weak);
295     }
296   } else {
297     assert(is_phantom, "only remaining strength");
298     assert(!is_narrow, "phantom access cannot be narrow");
299     __ li(lr, (int64_t)(uintptr_t)ShenandoahRuntime::load_reference_barrier_weak);
300   }
301   __ jalr(lr);
302   __ mv(t0, x10);
303   __ pop_call_clobbered_registers();
304   __ mv(x10, t0);
305   __ bind(not_cset);
306   __ mv(result_dst, x10);
307   __ pop_reg(saved_regs, sp);
308 
309   __ bind(heap_stable);
310   __ leave();
311 }
312 
313 void ShenandoahBarrierSetAssembler::iu_barrier(MacroAssembler* masm, Register dst, Register tmp) {
314   if (ShenandoahIUBarrier) {
315     __ push_call_clobbered_registers();
316 
317     satb_write_barrier_pre(masm, noreg, dst, xthread, tmp, true, false);
318 
319     __ pop_call_clobbered_registers();
320   }
321 }
322 
323 //
324 // Arguments:
325 //
326 // Inputs:
327 //   src:        oop location to load from, might be clobbered
328 //
329 // Output:
330 //   dst:        oop loaded from src location
331 //
332 // Kill:
333 //   x30 (tmp reg)
334 //
335 // Alias:
336 //   dst: x30 (might use x30 as temporary output register to avoid clobbering src)
337 //
338 void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm,
339                                             DecoratorSet decorators,
340                                             BasicType type,
341                                             Register dst,
342                                             Address src,
343                                             Register tmp1,
344                                             Register tmp_thread) {
345   // 1: non-reference load, no additional barrier is needed
346   if (!is_reference_type(type)) {
347     BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp_thread);
348     return;
349   }
350 
351   // 2: load a reference from src location and apply LRB if needed
352   if (ShenandoahBarrierSet::need_load_reference_barrier(decorators, type)) {
353     Register result_dst = dst;
354 
355     // Preserve src location for LRB
356     RegSet saved_regs;
357     if (dst == src.base()) {
358       dst = (src.base() == x28) ? x29 : x28;
359       saved_regs = RegSet::of(dst);
360       __ push_reg(saved_regs, sp);
361     }
362     assert_different_registers(dst, src.base());
363 
364     BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp_thread);
365 
366     load_reference_barrier(masm, dst, src, decorators);
367 
368     if (dst != result_dst) {
369       __ mv(result_dst, dst);
370       dst = result_dst;
371     }
372 
373     if (saved_regs.bits() != 0) {
374       __ pop_reg(saved_regs, sp);
375     }
376   } else {
377     BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp_thread);
378   }
379 
380   // 3: apply keep-alive barrier if needed
381   if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) {
382     __ enter();
383     __ push_call_clobbered_registers();
384     satb_write_barrier_pre(masm /* masm */,
385                            noreg /* obj */,
386                            dst /* pre_val */,
387                            xthread /* thread */,
388                            tmp1 /* tmp */,
389                            true /* tosca_live */,
390                            true /* expand_call */);
391     __ pop_call_clobbered_registers();
392     __ leave();
393   }
394 }
395 
396 void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
397                                              Address dst, Register val, Register tmp1, Register tmp2) {
398   bool on_oop = is_reference_type(type);
399   if (!on_oop) {
400     BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2);
401     return;
402   }
403 
404   // flatten object address if needed
405   if (dst.offset() == 0) {
406     if (dst.base() != x13) {
407       __ mv(x13, dst.base());
408     }
409   } else {
410     __ la(x13, dst);
411   }
412 
413   shenandoah_write_barrier_pre(masm,
414                                x13 /* obj */,
415                                tmp2 /* pre_val */,
416                                xthread /* thread */,
417                                tmp1  /* tmp */,
418                                val != noreg /* tosca_live */,
419                                false /* expand_call */);
420 
421   if (val == noreg) {
422     BarrierSetAssembler::store_at(masm, decorators, type, Address(x13, 0), noreg, noreg, noreg);
423   } else {
424     iu_barrier(masm, val, tmp1);
425     // G1 barrier needs uncompressed oop for region cross check.
426     Register new_val = val;
427     if (UseCompressedOops) {
428       new_val = t1;
429       __ mv(new_val, val);
430     }
431     BarrierSetAssembler::store_at(masm, decorators, type, Address(x13, 0), val, noreg, noreg);
432   }
433 }
434 
435 void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env,
436                                                                   Register obj, Register tmp, Label& slowpath) {
437   Label done;
438   // Resolve jobject
439   BarrierSetAssembler::try_resolve_jobject_in_native(masm, jni_env, obj, tmp, slowpath);
440 
441   // Check for null.
442   __ beqz(obj, done);
443 
444   assert(obj != t1, "need t1");
445   Address gc_state(jni_env, ShenandoahThreadLocalData::gc_state_offset() - JavaThread::jni_environment_offset());
446   __ lbu(t1, gc_state);
447 
448   // Check for heap in evacuation phase
449   __ andi(t0, t1, ShenandoahHeap::EVACUATION);
450   __ bnez(t0, slowpath);
451 
452   __ bind(done);
453 }
454 
455 // Special Shenandoah CAS implementation that handles false negatives due
456 // to concurrent evacuation.  The service is more complex than a
457 // traditional CAS operation because the CAS operation is intended to
458 // succeed if the reference at addr exactly matches expected or if the
459 // reference at addr holds a pointer to a from-space object that has
460 // been relocated to the location named by expected.  There are two
461 // races that must be addressed:
462 //  a) A parallel thread may mutate the contents of addr so that it points
463 //     to a different object.  In this case, the CAS operation should fail.
464 //  b) A parallel thread may heal the contents of addr, replacing a
465 //     from-space pointer held in addr with the to-space pointer
466 //     representing the new location of the object.
467 // Upon entry to cmpxchg_oop, it is assured that new_val equals NULL
468 // or it refers to an object that is not being evacuated out of
469 // from-space, or it refers to the to-space version of an object that
470 // is being evacuated out of from-space.
471 //
472 // By default the value held in the result register following execution
473 // of the generated code sequence is 0 to indicate failure of CAS,
474 // non-zero to indicate success. If is_cae, the result is the value most
475 // recently fetched from addr rather than a boolean success indicator.
476 //
477 // Clobbers t0, t1
478 void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler* masm,
479                                                 Register addr,
480                                                 Register expected,
481                                                 Register new_val,
482                                                 Assembler::Aqrl acquire,
483                                                 Assembler::Aqrl release,
484                                                 bool is_cae,
485                                                 Register result) {
486   bool is_narrow = UseCompressedOops;
487   Assembler::operand_size size = is_narrow ? Assembler::uint32 : Assembler::int64;
488 
489   assert_different_registers(addr, expected, t0, t1);
490   assert_different_registers(addr, new_val, t0, t1);
491 
492   Label retry, success, fail, done;
493 
494   __ bind(retry);
495 
496   // Step1: Try to CAS.
497   __ cmpxchg(addr, expected, new_val, size, acquire, release, /* result */ t1);
498 
499   // If success, then we are done.
500   __ beq(expected, t1, success);
501 
502   // Step2: CAS failed, check the forwared pointer.
503   __ mv(t0, t1);
504 
505   if (is_narrow) {
506     __ decode_heap_oop(t0, t0);
507   }
508   resolve_forward_pointer(masm, t0);
509 
510   __ encode_heap_oop(t0, t0);
511 
512   // Report failure when the forwarded oop was not expected.
513   __ bne(t0, expected, fail);
514 
515   // Step 3: CAS again using the forwarded oop.
516   __ cmpxchg(addr, t1, new_val, size, acquire, release, /* result */ t0);
517 
518   // Retry when failed.
519   __ bne(t0, t1, retry);
520 
521   __ bind(success);
522   if (is_cae) {
523     __ mv(result, expected);
524   } else {
525     __ addi(result, zr, 1);
526   }
527   __ j(done);
528 
529   __ bind(fail);
530   if (is_cae) {
531     __ mv(result, t0);
532   } else {
533     __ mv(result, zr);
534   }
535 
536   __ bind(done);
537 }
538 
539 #undef __
540 
541 #ifdef COMPILER1
542 
543 #define __ ce->masm()->
544 
545 void ShenandoahBarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub) {
546   ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1();
547   // At this point we know that marking is in progress.
548   // If do_load() is true then we have to emit the
549   // load of the previous value; otherwise it has already
550   // been loaded into _pre_val.
551   __ bind(*stub->entry());
552 
553   assert(stub->pre_val()->is_register(), "Precondition.");
554 
555   Register pre_val_reg = stub->pre_val()->as_register();
556 
557   if (stub->do_load()) {
558     ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false /* wide */);
559   }
560   __ beqz(pre_val_reg, *stub->continuation(), /* is_far */ true);
561   ce->store_parameter(stub->pre_val()->as_register(), 0);
562   __ far_call(RuntimeAddress(bs->pre_barrier_c1_runtime_code_blob()->code_begin()));
563   __ j(*stub->continuation());
564 }
565 
566 void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assembler* ce,
567                                                                     ShenandoahLoadReferenceBarrierStub* stub) {
568   ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1();
569   __ bind(*stub->entry());
570 
571   DecoratorSet decorators = stub->decorators();
572   bool is_strong  = ShenandoahBarrierSet::is_strong_access(decorators);
573   bool is_weak    = ShenandoahBarrierSet::is_weak_access(decorators);
574   bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators);
575   bool is_native  = ShenandoahBarrierSet::is_native_access(decorators);
576 
577   Register obj = stub->obj()->as_register();
578   Register res = stub->result()->as_register();
579   Register addr = stub->addr()->as_pointer_register();
580   Register tmp1 = stub->tmp1()->as_register();
581   Register tmp2 = stub->tmp2()->as_register();
582 
583   assert(res == x10, "result must arrive in x10");
584   assert_different_registers(tmp1, tmp2, t0);
585 
586   if (res != obj) {
587     __ mv(res, obj);
588   }
589 
590   if (is_strong) {
591     // Check for object in cset.
592     __ mv(tmp2, ShenandoahHeap::in_cset_fast_test_addr());
593     __ srli(tmp1, res, ShenandoahHeapRegion::region_size_bytes_shift_jint());
594     __ add(tmp2, tmp2, tmp1);
595     __ lbu(tmp2, Address(tmp2));
596     __ beqz(tmp2, *stub->continuation(), true /* is_far */);
597   }
598 
599   ce->store_parameter(res, 0);
600   ce->store_parameter(addr, 1);
601 
602   if (is_strong) {
603     if (is_native) {
604       __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin()));
605     } else {
606       __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_rt_code_blob()->code_begin()));
607     }
608   } else if (is_weak) {
609     __ far_call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin()));
610   } else {
611     assert(is_phantom, "only remaining strength");
612     __ far_call(RuntimeAddress(bs->load_reference_barrier_phantom_rt_code_blob()->code_begin()));
613   }
614 
615   __ j(*stub->continuation());
616 }
617 
618 #undef __
619 
620 #define __ sasm->
621 
622 void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm) {
623   __ prologue("shenandoah_pre_barrier", false);
624 
625   // arg0 : previous value of memory
626 
627   BarrierSet* bs = BarrierSet::barrier_set();
628 
629   const Register pre_val = x10;
630   const Register thread = xthread;
631   const Register tmp = t0;
632 
633   Address queue_index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()));
634   Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()));
635 
636   Label done;
637   Label runtime;
638 
639   // Is marking still active?
640   Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
641   __ lb(tmp, gc_state);
642   __ andi(tmp, tmp, ShenandoahHeap::MARKING);
643   __ beqz(tmp, done);
644 
645   // Can we store original value in the thread's buffer?
646   __ ld(tmp, queue_index);
647   __ beqz(tmp, runtime);
648 
649   __ sub(tmp, tmp, wordSize);
650   __ sd(tmp, queue_index);
651   __ ld(t1, buffer);
652   __ add(tmp, tmp, t1);
653   __ load_parameter(0, t1);
654   __ sd(t1, Address(tmp, 0));
655   __ j(done);
656 
657   __ bind(runtime);
658   __ push_call_clobbered_registers();
659   __ load_parameter(0, pre_val);
660   __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), pre_val, thread);
661   __ pop_call_clobbered_registers();
662   __ bind(done);
663 
664   __ epilogue();
665 }
666 
667 void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm,
668                                                                                     DecoratorSet decorators) {
669   __ prologue("shenandoah_load_reference_barrier", false);
670   // arg0 : object to be resolved
671 
672   __ push_call_clobbered_registers();
673   __ load_parameter(0, x10);
674   __ load_parameter(1, x11);
675 
676   bool is_strong  = ShenandoahBarrierSet::is_strong_access(decorators);
677   bool is_weak    = ShenandoahBarrierSet::is_weak_access(decorators);
678   bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators);
679   bool is_native  = ShenandoahBarrierSet::is_native_access(decorators);
680   if (is_strong) {
681     if (is_native) {
682       __ li(lr, (int64_t)(uintptr_t)ShenandoahRuntime::load_reference_barrier_strong);
683     } else {
684       if (UseCompressedOops) {
685         __ li(lr, (int64_t)(uintptr_t)ShenandoahRuntime::load_reference_barrier_strong_narrow);
686       } else {
687         __ li(lr, (int64_t)(uintptr_t)ShenandoahRuntime::load_reference_barrier_strong);
688       }
689     }
690   } else if (is_weak) {
691     assert(!is_native, "weak must not be called off-heap");
692     if (UseCompressedOops) {
693       __ li(lr, (int64_t)(uintptr_t)ShenandoahRuntime::load_reference_barrier_weak_narrow);
694     } else {
695       __ li(lr, (int64_t)(uintptr_t)ShenandoahRuntime::load_reference_barrier_weak);
696     }
697   } else {
698     assert(is_phantom, "only remaining strength");
699     assert(is_native, "phantom must only be called off-heap");
700     __ li(lr, (int64_t)(uintptr_t)ShenandoahRuntime::load_reference_barrier_phantom);
701   }
702   __ jalr(lr);
703   __ mv(t0, x10);
704   __ pop_call_clobbered_registers();
705   __ mv(x10, t0);
706 
707   __ epilogue();
708 }
709 
710 #undef __
711 
712 #endif // COMPILER1