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