1 /*
2 * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
3 * Copyright (c) 2018, 2020, Red Hat, Inc. All rights reserved.
4 * Copyright (c) 2020, 2021, Huawei Technologies Co., Ltd. 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 "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"
28 #include "gc/shenandoah/mode/shenandoahMode.hpp"
29 #include "gc/shenandoah/shenandoahBarrierSet.hpp"
30 #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp"
31 #include "gc/shenandoah/shenandoahForwarding.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/interp_masm.hpp"
37 #include "interpreter/interpreter.hpp"
38 #include "runtime/javaThread.hpp"
39 #include "runtime/sharedRuntime.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 #include "opto/output.hpp"
48 #include "utilities/population_count.hpp"
49 #include "utilities/powerOfTwo.hpp"
50 #endif
51
52 #define __ masm->
53
54 void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop,
55 Register src, Register dst, Register count, RegSet saved_regs) {
56 if (is_oop) {
57 bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0;
58 if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahLoadRefBarrier) {
59
60 Label done;
61
62 // Avoid calling runtime if count == 0
63 __ beqz(count, done);
64
65 // Is GC active?
66 Address gc_state(xthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
67 assert_different_registers(src, dst, count, t0);
68
69 __ lbu(t0, gc_state);
70 if (ShenandoahSATBBarrier && dest_uninitialized) {
71 __ test_bit(t0, t0, ShenandoahHeap::HAS_FORWARDED_BITPOS);
72 __ beqz(t0, done);
73 } else {
74 __ andi(t0, t0, ShenandoahHeap::HAS_FORWARDED | ShenandoahHeap::MARKING);
75 __ beqz(t0, done);
76 }
77
78 __ push_reg(saved_regs, sp);
79 if (UseCompressedOops) {
80 __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_narrow_oop),
81 src, dst, count);
82 } else {
83 __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::arraycopy_barrier_oop), src, dst, count);
84 }
85 __ pop_reg(saved_regs, sp);
86 __ bind(done);
87 }
88 }
89 }
90
91 void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, DecoratorSet decorators, bool is_oop,
92 Register start, Register count, Register tmp) {
93 if (ShenandoahCardBarrier && is_oop) {
94 gen_write_ref_array_post_barrier(masm, decorators, start, count, tmp);
95 }
96 }
97
98 void ShenandoahBarrierSetAssembler::satb_barrier(MacroAssembler* masm,
99 Register obj,
100 Register pre_val,
101 Register thread,
102 Register tmp1,
103 Register tmp2,
104 bool tosca_live,
105 bool expand_call) {
106 assert(ShenandoahSATBBarrier, "Should be checked by caller");
107
108 // If expand_call is true then we expand the call_VM_leaf macro
109 // directly to skip generating the check by
110 // InterpreterMacroAssembler::call_VM_leaf_base that checks _last_sp.
111 assert(thread == xthread, "must be");
112
113 Label done;
114 Label runtime;
115
116 assert_different_registers(obj, pre_val, tmp1, tmp2);
117 assert(pre_val != noreg && tmp1 != noreg && tmp2 != noreg, "expecting a register");
118
119 Address index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()));
120 Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()));
121
122 // Is marking active?
123 Address gc_state(xthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
124 __ lbu(t1, gc_state);
125 __ test_bit(t1, t1, ShenandoahHeap::MARKING_BITPOS);
126 __ beqz(t1, done);
127
128 // Do we need to load the previous value?
129 if (obj != noreg) {
130 __ load_heap_oop(pre_val, Address(obj, 0), noreg, noreg, AS_RAW);
131 }
132
133 // Is the previous value null?
134 __ beqz(pre_val, done);
135
136 // Can we store original value in the thread's buffer?
137 // Is index == 0?
138 // (The index field is typed as size_t.)
139 __ ld(tmp1, index); // tmp := *index_adr
140 __ beqz(tmp1, runtime); // tmp == 0? If yes, goto runtime
141
142 __ subi(tmp1, tmp1, wordSize); // tmp := tmp - wordSize
143 __ sd(tmp1, index); // *index_adr := tmp
144 __ ld(tmp2, buffer);
145 __ add(tmp1, tmp1, tmp2); // tmp := tmp + *buffer_adr
146
147 // Record the previous value
148 __ sd(pre_val, Address(tmp1, 0));
149 __ j(done);
150
151 __ bind(runtime);
152 // save the live input values
153 RegSet saved = RegSet::of(pre_val);
154 if (tosca_live) saved += RegSet::of(x10);
155 if (obj != noreg) saved += RegSet::of(obj);
156
157 __ push_reg(saved, sp);
158
159 // Calling the runtime using the regular call_VM_leaf mechanism generates
160 // code (generated by InterpreterMacroAssember::call_VM_leaf_base)
161 // that checks that the *(rfp+frame::interpreter_frame_last_sp) is null.
162 //
163 // If we care generating the pre-barrier without a frame (e.g. in the
164 // intrinsified Reference.get() routine) then ebp might be pointing to
165 // the caller frame and so this check will most likely fail at runtime.
166 //
167 // Expanding the call directly bypasses the generation of the check.
168 // So when we do not have have a full interpreter frame on the stack
169 // expand_call should be passed true.
170 if (expand_call) {
171 assert(pre_val != c_rarg1, "smashed arg");
172 __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val);
173 } else {
174 __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val);
175 }
176
177 __ pop_reg(saved, sp);
178
179 __ bind(done);
180 }
181
182 void ShenandoahBarrierSetAssembler::resolve_forward_pointer(MacroAssembler* masm, Register dst, Register tmp) {
183 assert(ShenandoahLoadRefBarrier || ShenandoahCASBarrier, "Should be enabled");
184
185 Label is_null;
186 __ beqz(dst, is_null);
187 resolve_forward_pointer_not_null(masm, dst, tmp);
188 __ bind(is_null);
189 }
190
191 // IMPORTANT: This must preserve all registers, even t0 and t1, except those explicitly
192 // passed in.
193 void ShenandoahBarrierSetAssembler::resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst, Register tmp) {
194 assert(ShenandoahLoadRefBarrier || ShenandoahCASBarrier, "Should be enabled");
195 // The below loads the mark word, checks if the lowest two bits are
196 // set, and if so, clear the lowest two bits and copy the result
197 // to dst. Otherwise it leaves dst alone.
198 // Implementing this is surprisingly awkward. I do it here by:
199 // - Inverting the mark word
200 // - Test lowest two bits == 0
201 // - If so, set the lowest two bits
202 // - Invert the result back, and copy to dst
203 RegSet saved_regs = RegSet::of(t2);
204 bool borrow_reg = (tmp == noreg);
205 if (borrow_reg) {
206 // No free registers available. Make one useful.
207 tmp = t0;
208 if (tmp == dst) {
209 tmp = t1;
210 }
211 saved_regs += RegSet::of(tmp);
212 }
213
214 assert_different_registers(tmp, dst, t2);
215 __ push_reg(saved_regs, sp);
216
217 Label done;
218 __ ld(tmp, Address(dst, oopDesc::mark_offset_in_bytes()));
219 __ xori(tmp, tmp, -1); // eon with 0 is equivalent to XOR with -1
220 __ andi(t2, tmp, markWord::lock_mask_in_place);
221 __ bnez(t2, done);
222 __ ori(tmp, tmp, markWord::marked_value);
223 __ xori(dst, tmp, -1); // eon with 0 is equivalent to XOR with -1
224 __ bind(done);
225
226 __ pop_reg(saved_regs, sp);
227 }
228
229 void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm,
230 Register dst,
231 Address load_addr,
232 DecoratorSet decorators) {
233 assert(ShenandoahLoadRefBarrier, "Should be enabled");
234 assert(dst != t1 && load_addr.base() != t1, "need t1");
235 assert_different_registers(load_addr.base(), t0, t1);
236
237 bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators);
238 bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators);
239 bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators);
240 bool is_native = ShenandoahBarrierSet::is_native_access(decorators);
241 bool is_narrow = UseCompressedOops && !is_native;
242
243 Label heap_stable, not_cset;
244 __ enter();
245 Address gc_state(xthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
246 __ lbu(t1, gc_state);
247
248 // Check for heap stability
249 if (is_strong) {
250 __ test_bit(t1, t1, ShenandoahHeap::HAS_FORWARDED_BITPOS);
251 __ beqz(t1, heap_stable);
252 } else {
253 Label lrb;
254 __ test_bit(t0, t1, ShenandoahHeap::WEAK_ROOTS_BITPOS);
255 __ bnez(t0, lrb);
256 __ test_bit(t0, t1, ShenandoahHeap::HAS_FORWARDED_BITPOS);
257 __ beqz(t0, heap_stable);
258 __ bind(lrb);
259 }
260
261 // use x11 for load address
262 Register result_dst = dst;
263 if (dst == x11) {
264 __ mv(t1, dst);
265 dst = t1;
266 }
267
268 // Save x10 and x11, unless it is an output register
269 RegSet saved_regs = RegSet::of(x10, x11) - result_dst;
270 __ push_reg(saved_regs, sp);
271 __ la(x11, load_addr);
272 __ mv(x10, dst);
273
274 // Test for in-cset
275 if (is_strong) {
276 __ mv(t1, ShenandoahHeap::in_cset_fast_test_addr());
277 __ srli(t0, x10, ShenandoahHeapRegion::region_size_bytes_shift_jint());
278 __ add(t1, t1, t0);
279 __ lbu(t1, Address(t1));
280 __ test_bit(t0, t1, 0);
281 __ beqz(t0, not_cset);
282 }
283
284 __ push_call_clobbered_registers();
285 address target = nullptr;
286 if (is_strong) {
287 if (is_narrow) {
288 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow);
289 } else {
290 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong);
291 }
292 } else if (is_weak) {
293 if (is_narrow) {
294 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow);
295 } else {
296 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak);
297 }
298 } else {
299 assert(is_phantom, "only remaining strength");
300 assert(!is_narrow, "phantom access cannot be narrow");
301 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak);
302 }
303 __ rt_call(target);
304 __ mv(t0, x10);
305 __ pop_call_clobbered_registers();
306 __ mv(x10, t0);
307 __ bind(not_cset);
308 __ mv(result_dst, x10);
309 __ pop_reg(saved_regs, sp);
310
311 __ bind(heap_stable);
312 __ leave();
313 }
314
315 //
316 // Arguments:
317 //
318 // Inputs:
319 // src: oop location to load from, might be clobbered
320 //
321 // Output:
322 // dst: oop loaded from src location
323 //
324 // Kill:
325 // x30 (tmp reg)
326 //
327 // Alias:
328 // dst: x30 (might use x30 as temporary output register to avoid clobbering src)
329 //
330 void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm,
331 DecoratorSet decorators,
332 BasicType type,
333 Register dst,
334 Address src,
335 Register tmp1,
336 Register tmp2) {
337 // 1: non-reference load, no additional barrier is needed
338 if (!is_reference_type(type)) {
339 BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp2);
340 return;
341 }
342
343 // 2: load a reference from src location and apply LRB if needed
344 if (ShenandoahBarrierSet::need_load_reference_barrier(decorators, type)) {
345 Register result_dst = dst;
346
347 // Preserve src location for LRB
348 RegSet saved_regs;
349 if (dst == src.base()) {
350 dst = (src.base() == x28) ? x29 : x28;
351 saved_regs = RegSet::of(dst);
352 __ push_reg(saved_regs, sp);
353 }
354 assert_different_registers(dst, src.base());
355
356 BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp2);
357
358 load_reference_barrier(masm, dst, src, decorators);
359
360 if (dst != result_dst) {
361 __ mv(result_dst, dst);
362 dst = result_dst;
363 }
364
365 if (saved_regs.bits() != 0) {
366 __ pop_reg(saved_regs, sp);
367 }
368 } else {
369 BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp2);
370 }
371
372 // 3: apply keep-alive barrier if needed
373 if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) {
374 __ enter();
375 __ push_call_clobbered_registers();
376 satb_barrier(masm /* masm */,
377 noreg /* obj */,
378 dst /* pre_val */,
379 xthread /* thread */,
380 tmp1 /* tmp1 */,
381 tmp2 /* tmp2 */,
382 true /* tosca_live */,
383 true /* expand_call */);
384 __ pop_call_clobbered_registers();
385 __ leave();
386 }
387 }
388
389 void ShenandoahBarrierSetAssembler::card_barrier(MacroAssembler* masm, Register obj) {
390 assert(ShenandoahCardBarrier, "Should have been checked by caller");
391
392 __ srli(obj, obj, CardTable::card_shift());
393
394 assert(CardTable::dirty_card_val() == 0, "must be");
395
396 Address curr_ct_holder_addr(xthread, in_bytes(ShenandoahThreadLocalData::card_table_offset()));
397 __ ld(t1, curr_ct_holder_addr);
398 __ add(t1, obj, t1);
399
400 if (UseCondCardMark) {
401 Label L_already_dirty;
402 __ lbu(t0, Address(t1));
403 __ beqz(t0, L_already_dirty);
404 __ sb(zr, Address(t1));
405 __ bind(L_already_dirty);
406 } else {
407 __ sb(zr, Address(t1));
408 }
409 }
410
411 void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
412 Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) {
413 // 1: non-reference types require no barriers
414 if (!is_reference_type(type)) {
415 BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3);
416 return;
417 }
418
419 // Flatten object address right away for simplicity: likely needed by barriers
420 if (dst.offset() == 0) {
421 if (dst.base() != tmp3) {
422 __ mv(tmp3, dst.base());
423 }
424 } else {
425 __ la(tmp3, dst);
426 }
427
428 bool storing_non_null = (val != noreg);
429
430 // 2: pre-barrier: SATB needs the previous value
431 if (ShenandoahBarrierSet::need_satb_barrier(decorators, type)) {
432 satb_barrier(masm,
433 tmp3 /* obj */,
434 tmp2 /* pre_val */,
435 xthread /* thread */,
436 tmp1 /* tmp */,
437 t0 /* tmp2 */,
438 storing_non_null /* tosca_live */,
439 false /* expand_call */);
440 }
441
442 // Store!
443 BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), val, noreg, noreg, noreg);
444
445 // 3: post-barrier: card barrier needs store address
446 if (ShenandoahBarrierSet::need_card_barrier(decorators, type) && storing_non_null) {
447 card_barrier(masm, tmp3);
448 }
449 }
450
451 void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env,
452 Register obj, Register tmp, Label& slowpath) {
453 Label done;
454 // Resolve jobject
455 BarrierSetAssembler::try_resolve_jobject_in_native(masm, jni_env, obj, tmp, slowpath);
456
457 // Check for null.
458 __ beqz(obj, done);
459
460 assert(obj != t1, "need t1");
461 Address gc_state(jni_env, ShenandoahThreadLocalData::gc_state_offset() - JavaThread::jni_environment_offset());
462 __ lbu(t1, gc_state);
463
464 // Check for heap in evacuation phase
465 __ test_bit(t0, t1, ShenandoahHeap::EVACUATION_BITPOS);
466 __ bnez(t0, slowpath);
467
468 __ bind(done);
469 }
470
471 #ifdef COMPILER2
472 void ShenandoahBarrierSetAssembler::try_resolve_weak_handle_in_c2(MacroAssembler *masm, Register obj,
473 Register tmp, Label& slow_path) {
474 assert_different_registers(obj, tmp);
475
476 Label done;
477
478 // Resolve weak handle using the standard implementation.
479 BarrierSetAssembler::try_resolve_weak_handle_in_c2(masm, obj, tmp, slow_path);
480
481 // Check if the reference is null, and if it is, take the fast path.
482 __ beqz(obj, done);
483
484 Address gc_state(xthread, ShenandoahThreadLocalData::gc_state_offset());
485 __ lbu(tmp, gc_state);
486
487 // Check if the heap is under weak-reference/roots processing, in
488 // which case we need to take the slow path.
489 __ test_bit(tmp, tmp, ShenandoahHeap::WEAK_ROOTS_BITPOS);
490 __ bnez(tmp, slow_path);
491 __ bind(done);
492 }
493 #endif
494
495 // Special Shenandoah CAS implementation that handles false negatives due
496 // to concurrent evacuation. The service is more complex than a
497 // traditional CAS operation because the CAS operation is intended to
498 // succeed if the reference at addr exactly matches expected or if the
499 // reference at addr holds a pointer to a from-space object that has
500 // been relocated to the location named by expected. There are two
501 // races that must be addressed:
502 // a) A parallel thread may mutate the contents of addr so that it points
503 // to a different object. In this case, the CAS operation should fail.
504 // b) A parallel thread may heal the contents of addr, replacing a
505 // from-space pointer held in addr with the to-space pointer
506 // representing the new location of the object.
507 // Upon entry to cmpxchg_oop, it is assured that new_val equals null
508 // or it refers to an object that is not being evacuated out of
509 // from-space, or it refers to the to-space version of an object that
510 // is being evacuated out of from-space.
511 //
512 // By default the value held in the result register following execution
513 // of the generated code sequence is 0 to indicate failure of CAS,
514 // non-zero to indicate success. If is_cae, the result is the value most
515 // recently fetched from addr rather than a boolean success indicator.
516 //
517 // Clobbers t0, t1
518 void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler* masm,
519 Register addr,
520 Register expected,
521 Register new_val,
522 Assembler::Aqrl acquire,
523 Assembler::Aqrl release,
524 bool is_cae,
525 Register result) {
526 bool is_narrow = UseCompressedOops;
527 Assembler::operand_size size = is_narrow ? Assembler::uint32 : Assembler::int64;
528
529 assert_different_registers(addr, expected, t0, t1);
530 assert_different_registers(addr, new_val, t0, t1);
531
532 Label retry, success, fail, done;
533
534 __ bind(retry);
535
536 // Step1: Try to CAS.
537 __ cmpxchg(addr, expected, new_val, size, acquire, release, /* result */ t1);
538
539 // If success, then we are done.
540 __ beq(expected, t1, success);
541
542 // Step2: CAS failed, check the forwarded pointer.
543 __ mv(t0, t1);
544
545 if (is_narrow) {
546 __ decode_heap_oop(t0, t0);
547 }
548 resolve_forward_pointer(masm, t0);
549
550 __ encode_heap_oop(t0, t0);
551
552 // Report failure when the forwarded oop was not expected.
553 __ bne(t0, expected, fail);
554
555 // Step 3: CAS again using the forwarded oop.
556 __ cmpxchg(addr, t1, new_val, size, acquire, release, /* result */ t0);
557
558 // Retry when failed.
559 __ bne(t0, t1, retry);
560
561 __ bind(success);
562 if (is_cae) {
563 __ mv(result, expected);
564 } else {
565 __ mv(result, 1);
566 }
567 __ j(done);
568
569 __ bind(fail);
570 if (is_cae) {
571 __ mv(result, t0);
572 } else {
573 __ mv(result, zr);
574 }
575
576 __ bind(done);
577 }
578
579 void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators,
580 Register start, Register count, Register tmp) {
581 assert(ShenandoahCardBarrier, "Did you mean to enable ShenandoahCardBarrier?");
582
583 Label L_loop, L_done;
584 const Register end = count;
585
586 // Zero count? Nothing to do.
587 __ beqz(count, L_done);
588
589 // end = start + count << LogBytesPerHeapOop
590 // last element address to make inclusive
591 __ shadd(end, count, start, tmp, LogBytesPerHeapOop);
592 __ subi(end, end, BytesPerHeapOop);
593 __ srli(start, start, CardTable::card_shift());
594 __ srli(end, end, CardTable::card_shift());
595
596 // number of bytes to copy
597 __ sub(count, end, start);
598
599 Address curr_ct_holder_addr(xthread, in_bytes(ShenandoahThreadLocalData::card_table_offset()));
600 __ ld(tmp, curr_ct_holder_addr);
601 __ add(start, start, tmp);
602
603 __ bind(L_loop);
604 __ add(tmp, start, count);
605 __ sb(zr, Address(tmp));
606 __ subi(count, count, 1);
607 __ bgez(count, L_loop);
608 __ bind(L_done);
609 }
610
611 #undef __
612
613 #ifdef COMPILER1
614
615 #define __ ce->masm()->
616
617 void ShenandoahBarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub) {
618 ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1();
619 // At this point we know that marking is in progress.
620 // If do_load() is true then we have to emit the
621 // load of the previous value; otherwise it has already
622 // been loaded into _pre_val.
623 __ bind(*stub->entry());
624
625 assert(stub->pre_val()->is_register(), "Precondition.");
626
627 Register pre_val_reg = stub->pre_val()->as_register();
628
629 if (stub->do_load()) {
630 ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false /* wide */);
631 }
632 __ beqz(pre_val_reg, *stub->continuation(), /* is_far */ true);
633 ce->store_parameter(stub->pre_val()->as_register(), 0);
634 __ far_call(RuntimeAddress(bs->pre_barrier_c1_runtime_code_blob()->code_begin()));
635 __ j(*stub->continuation());
636 }
637
638 void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assembler* ce,
639 ShenandoahLoadReferenceBarrierStub* stub) {
640 ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1();
641 __ bind(*stub->entry());
642
643 DecoratorSet decorators = stub->decorators();
644 bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators);
645 bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators);
646 bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators);
647 bool is_native = ShenandoahBarrierSet::is_native_access(decorators);
648
649 Register obj = stub->obj()->as_register();
650 Register res = stub->result()->as_register();
651 Register addr = stub->addr()->as_pointer_register();
652 Register tmp1 = stub->tmp1()->as_register();
653 Register tmp2 = stub->tmp2()->as_register();
654
655 assert(res == x10, "result must arrive in x10");
656 assert_different_registers(tmp1, tmp2, t0);
657
658 if (res != obj) {
659 __ mv(res, obj);
660 }
661
662 if (is_strong) {
663 // Check for object in cset.
664 __ mv(tmp2, ShenandoahHeap::in_cset_fast_test_addr());
665 __ srli(tmp1, res, ShenandoahHeapRegion::region_size_bytes_shift_jint());
666 __ add(tmp2, tmp2, tmp1);
667 __ lbu(tmp2, Address(tmp2));
668 __ beqz(tmp2, *stub->continuation(), true /* is_far */);
669 }
670
671 ce->store_parameter(res, 0);
672 ce->store_parameter(addr, 1);
673
674 if (is_strong) {
675 if (is_native) {
676 __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin()));
677 } else {
678 __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_rt_code_blob()->code_begin()));
679 }
680 } else if (is_weak) {
681 __ far_call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin()));
682 } else {
683 assert(is_phantom, "only remaining strength");
684 __ far_call(RuntimeAddress(bs->load_reference_barrier_phantom_rt_code_blob()->code_begin()));
685 }
686
687 __ j(*stub->continuation());
688 }
689
690 #undef __
691
692 #define __ sasm->
693
694 void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm) {
695 __ prologue("shenandoah_pre_barrier", false);
696
697 // arg0 : previous value of memory
698
699 BarrierSet* bs = BarrierSet::barrier_set();
700
701 const Register pre_val = x10;
702 const Register thread = xthread;
703 const Register tmp = t0;
704
705 Address queue_index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()));
706 Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()));
707
708 Label done;
709 Label runtime;
710
711 // Is marking still active?
712 Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
713 __ lb(tmp, gc_state);
714 __ test_bit(tmp, tmp, ShenandoahHeap::MARKING_BITPOS);
715 __ beqz(tmp, done);
716
717 // Can we store original value in the thread's buffer?
718 __ ld(tmp, queue_index);
719 __ beqz(tmp, runtime);
720
721 __ subi(tmp, tmp, wordSize);
722 __ sd(tmp, queue_index);
723 __ ld(t1, buffer);
724 __ add(tmp, tmp, t1);
725 __ load_parameter(0, t1);
726 __ sd(t1, Address(tmp, 0));
727 __ j(done);
728
729 __ bind(runtime);
730 __ push_call_clobbered_registers();
731 __ load_parameter(0, pre_val);
732 __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), pre_val);
733 __ pop_call_clobbered_registers();
734 __ bind(done);
735
736 __ epilogue();
737 }
738
739 void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm,
740 DecoratorSet decorators) {
741 __ prologue("shenandoah_load_reference_barrier", false);
742 // arg0 : object to be resolved
743
744 __ push_call_clobbered_registers();
745 __ load_parameter(0, x10);
746 __ load_parameter(1, x11);
747
748 bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators);
749 bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators);
750 bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators);
751 bool is_native = ShenandoahBarrierSet::is_native_access(decorators);
752 address target = nullptr;
753 if (is_strong) {
754 if (is_native) {
755 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong);
756 } else {
757 if (UseCompressedOops) {
758 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow);
759 } else {
760 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong);
761 }
762 }
763 } else if (is_weak) {
764 assert(!is_native, "weak must not be called off-heap");
765 if (UseCompressedOops) {
766 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow);
767 } else {
768 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak);
769 }
770 } else {
771 assert(is_phantom, "only remaining strength");
772 assert(is_native, "phantom must only be called off-heap");
773 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom);
774 }
775 __ rt_call(target);
776 __ mv(t0, x10);
777 __ pop_call_clobbered_registers();
778 __ mv(x10, t0);
779
780 __ epilogue();
781 }
782
783 #undef __
784
785 #endif // COMPILER1
786
787 #ifdef COMPILER2
788
789 #define __ masm->
790
791 static bool needs_acquiring_load_reserved(const MachNode* n) {
792 assert(n->is_CAS(true), "expecting a compare and swap");
793 if (n->is_CAS(false)) {
794 assert(n->has_trailing_membar(), "expected trailing membar");
795 } else {
796 return n->has_trailing_membar();
797 }
798 return true;
799 }
800
801 void ShenandoahBarrierStubC2::gc_state_check_c2(MacroAssembler* masm,
802 Register gcstate,
803 const unsigned char test_state,
804 ShenandoahBarrierStubC2* slow_stub) {
805 if (ShenandoahGCStateCheckRemove) {
806 // Unrealistic: remove all barrier fastpath checks.
807 } else if (ShenandoahGCStateCheckHotpatch) {
808 __ nop();
809 } else {
810 int bit_to_check = ShenandoahThreadLocalData::gc_state_to_fast_bit(test_state);
811 Address gc_state_fast(xthread, in_bytes(ShenandoahThreadLocalData::gc_state_fast_offset()));
812 __ lbu(gcstate, gc_state_fast);
813 __ test_bit(gcstate, gcstate, bit_to_check);
814 __ bnez(gcstate, *slow_stub->entry());
815
816 // Fast path falls through here when the barrier is not needed.
817 __ bind(*slow_stub->continuation());
818 }
819 }
820
821 void ShenandoahBarrierSetAssembler::compare_and_set_c2(const MachNode* node, MacroAssembler* masm, Register res, Register addr,
822 Register oldval, Register newval, Register tmp, bool exchange, bool maybe_null, bool narrow, bool weak) {
823 const Assembler::Aqrl acquire = needs_acquiring_load_reserved(node) ? Assembler::aq : Assembler::relaxed;
824 const Assembler::Aqrl release = Assembler::rl;
825
826 // Pre-barrier covers several things:
827 // a. Avoids false positives from CAS encountering to-space memory values.
828 // b. Satisfies the need for LRB for the CAE result.
829 // c. Records old value for the sake of SATB.
830 //
831 // (a) and (b) are covered because load barrier does memory location fixup.
832 // (c) is covered by KA on the current memory value.
833 if (ShenandoahBarrierStubC2::needs_slow_barrier(node)) {
834 ShenandoahBarrierStubC2* const stub = ShenandoahBarrierStubC2::create(node, tmp, Address(addr, 0), narrow, /* do_load: */ true);
835 char check = 0;
836 check |= ShenandoahBarrierStubC2::needs_keep_alive_barrier(node) ? ShenandoahHeap::MARKING : 0;
837 check |= ShenandoahBarrierStubC2::needs_load_ref_barrier(node) ? ShenandoahHeap::HAS_FORWARDED : 0;
838 assert(!ShenandoahBarrierStubC2::needs_load_ref_barrier_weak(node), "Not supported for CAS/CAE");
839 ShenandoahBarrierStubC2::gc_state_check_c2(masm, t0, check, stub);
840 }
841
842 // Existing RISCV cmpxchg_oop already handles Shenandoah forwarded-value retry logic.
843 // It returns:
844 // - boolean 0/1 for CAS (!exchange)
845 // - loaded/current value for CAE (exchange)
846 ShenandoahBarrierSet::assembler()->cmpxchg_oop(masm, addr, oldval, newval, acquire, release, exchange /* is_cae */, res);
847
848 // Post-barrier deals with card updates.
849 card_barrier_c2(node, masm, Address(addr, 0));
850 }
851
852 void ShenandoahBarrierSetAssembler::get_and_set_c2(const MachNode* node, MacroAssembler* masm, Register preval,
853 Register newval, Register addr, Register tmp) {
854 const bool acquire = needs_acquiring_load_reserved(node);
855 const bool narrow = node->bottom_type()->isa_narrowoop();
856
857 // Pre-barrier covers several things:
858 // a. Satisfies the need for LRB for the GAS result.
859 // b. Records old value for the sake of SATB.
860 //
861 // (a) is covered because load barrier does memory location fixup.
862 // (b) is covered by KA on the current memory value.
863 if (ShenandoahBarrierStubC2::needs_slow_barrier(node)) {
864 ShenandoahBarrierStubC2* const stub = ShenandoahBarrierStubC2::create(node, tmp, Address(addr, 0), narrow, /* do_load: */ true);
865 char check = 0;
866 check |= ShenandoahBarrierStubC2::needs_keep_alive_barrier(node) ? ShenandoahHeap::MARKING : 0;
867 check |= ShenandoahBarrierStubC2::needs_load_ref_barrier(node) ? ShenandoahHeap::HAS_FORWARDED : 0;
868 assert(!ShenandoahBarrierStubC2::needs_load_ref_barrier_weak(node), "Not supported for GAS");
869 ShenandoahBarrierStubC2::gc_state_check_c2(masm, t0, check, stub);
870 }
871
872 if (narrow) {
873 if (acquire) {
874 __ atomic_xchgalw(preval, newval, addr);
875 } else {
876 __ atomic_xchgw(preval, newval, addr);
877 }
878 } else {
879 if (acquire) {
880 __ atomic_xchgal(preval, newval, addr);
881 } else {
882 __ atomic_xchg(preval, newval, addr);
883 }
884 }
885
886 // Post-barrier deals with card updates.
887 card_barrier_c2(node, masm, Address(addr, 0));
888 }
889
890 void ShenandoahBarrierSetAssembler::store_c2(const MachNode* node, MacroAssembler* masm, Address dst, bool dst_narrow,
891 Register src, bool src_narrow, Register tmp) {
892
893 // Pre-barrier: SATB / keep-alive on current value in memory.
894 if (ShenandoahBarrierStubC2::needs_slow_barrier(node)) {
895 assert(!ShenandoahBarrierStubC2::needs_load_ref_barrier(node), "Should not be required for stores");
896 ShenandoahBarrierStubC2* const stub = ShenandoahBarrierStubC2::create(node, tmp, dst, dst_narrow, /* do_load: */ true);
897 ShenandoahBarrierStubC2::gc_state_check_c2(masm, t0, ShenandoahHeap::MARKING, stub);
898 }
899
900 // Do the actual store
901 const bool is_volatile = node->has_trailing_membar();
902 if (dst_narrow) {
903 Register actual_src = src;
904 if (!src_narrow) {
905 assert(tmp != noreg, "need temp register");
906 __ mv(tmp, src);
907 if (ShenandoahBarrierStubC2::src_not_null(node)) {
908 __ encode_heap_oop_not_null(tmp, tmp);
909 } else {
910 __ encode_heap_oop(tmp, tmp);
911 }
912 actual_src = tmp;
913 }
914
915 if (is_volatile) {
916 __ membar(MacroAssembler::StoreStore | MacroAssembler::LoadStore);
917 __ sw(actual_src, dst);
918 } else {
919 __ sw(actual_src, dst);
920 }
921 } else {
922 if (is_volatile) {
923 __ membar(MacroAssembler::StoreStore | MacroAssembler::LoadStore);
924 __ sd(src, dst);
925 } else {
926 __ sd(src, dst);
927 }
928 }
929
930 // Post-barrier: card updates.
931 card_barrier_c2(node, masm, dst);
932 }
933
934 void ShenandoahBarrierSetAssembler::load_c2(const MachNode* node, MacroAssembler* masm, Register dst, Address src) {
935 const bool acquire = node->memory_order() == MemNode::MemOrd::acquire;
936 const bool narrow = node->bottom_type()->isa_narrowoop();
937
938 // Do the actual load. This load is the candidate for implicit null check, and MUST come first.
939 if (narrow) {
940 __ lwu(dst, src);
941 if (acquire) {
942 __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore);
943 }
944 } else {
945 __ ld(dst, src);
946 if (acquire) {
947 __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore);
948 }
949 }
950
951 // Post-barrier: LRB / KA / weak-root processing.
952 if (ShenandoahBarrierStubC2::needs_slow_barrier(node)) {
953 ShenandoahBarrierStubC2* const stub = ShenandoahBarrierStubC2::create(node, dst, src, narrow, /* do_load: */ false);
954 char check = 0;
955 check |= ShenandoahBarrierStubC2::needs_keep_alive_barrier(node) ? ShenandoahHeap::MARKING : 0;
956 check |= ShenandoahBarrierStubC2::needs_load_ref_barrier(node) ? ShenandoahHeap::HAS_FORWARDED : 0;
957 check |= ShenandoahBarrierStubC2::needs_load_ref_barrier_weak(node) ? ShenandoahHeap::WEAK_ROOTS : 0;
958 ShenandoahBarrierStubC2::gc_state_check_c2(masm, t0, check, stub);
959 }
960 }
961
962 void ShenandoahBarrierSetAssembler::card_barrier_c2(const MachNode* node, MacroAssembler* masm, Address address) {
963 if (ShenandoahSkipBarriers || (node->barrier_data() & ShenandoahBitCardMark) == 0) {
964 return;
965 }
966
967 assert(CardTable::dirty_card_val() == 0, "must be");
968 Assembler::InlineSkippedInstructionsCounter skip_counter(masm);
969
970 // t1 = effective address
971 __ la(t1, address);
972
973 // t1 = card index
974 __ srli(t1, t1, CardTable::card_shift());
975
976 // t0 = card table base
977 Address curr_ct_holder_addr(xthread, in_bytes(ShenandoahThreadLocalData::card_table_offset()));
978 __ ld(t0, curr_ct_holder_addr);
979
980 // t1 = &card_table[card_index]
981 __ add(t1, t1, t0);
982
983 if (UseCondCardMark) {
984 Label already_dirty;
985 __ lbu(t0, Address(t1));
986 __ beqz(t0, already_dirty);
987 __ sb(zr, Address(t1));
988 __ bind(already_dirty);
989 } else {
990 __ sb(zr, Address(t1));
991 }
992 }
993
994 #undef __
995 #define __ masm.
996
997 void ShenandoahBarrierStubC2::emit_code(MacroAssembler& masm) {
998 assert(_needs_keep_alive_barrier || _needs_load_ref_barrier, "Why are you here?");
999
1000 // Stub entry
1001 __ bind(*BarrierStubC2::entry());
1002
1003 // If needed, perform the load here so stub logic sees the current oop value.
1004 if (_do_load) {
1005 __ load_heap_oop(_obj, _addr, noreg, noreg, AS_RAW);
1006 } else if (_narrow) {
1007 // Decode narrow oop before barrier processing.
1008 if (_maybe_null) {
1009 __ decode_heap_oop(_obj, _obj);
1010 } else {
1011 __ decode_heap_oop_not_null(_obj, _obj);
1012 }
1013 }
1014
1015 if (_do_load || _maybe_null) {
1016 __ beqz(_obj, *continuation());
1017 }
1018
1019 keepalive(&masm, _obj, t0, t1);
1020
1021 lrb(&masm, _obj, _addr, noreg);
1022
1023 // If object is narrow, we need to encode it before exiting.
1024 // For encoding, dst can only turn null if we are dealing with weak loads.
1025 // Otherwise, we have already null-checked. We can skip all this if we performed
1026 // the load ourselves, which means the value is not used by caller.
1027 if (_narrow && !_do_load) {
1028 if (_needs_load_ref_weak_barrier) {
1029 __ encode_heap_oop(_obj, _obj);
1030 } else {
1031 __ encode_heap_oop_not_null(_obj, _obj);
1032 }
1033 }
1034
1035 // Go back to fast path
1036 __ j(*continuation());
1037 }
1038
1039 #undef __
1040 #define __ masm->
1041
1042 void ShenandoahBarrierStubC2::keepalive(MacroAssembler* masm, Register obj, Register tmp1, Register tmp2) {
1043 Address index(xthread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()));
1044 Address buffer(xthread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()));
1045 Label L_runtime;
1046 Label L_done;
1047
1048 // The node doesn't even need keepalive barrier, just don't check anything else
1049 if (!_needs_keep_alive_barrier) {
1050 return;
1051 }
1052
1053 // If both LRB and KeepAlive barriers are required (rare), do a runtime check
1054 // for enabled barrier.
1055 if (_needs_load_ref_barrier) {
1056 Address gcs_addr(xthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
1057 __ lbu(t0, gcs_addr);
1058 __ test_bit(t0, t0, ShenandoahHeap::MARKING_BITPOS);
1059 __ beqz(t0, L_done);
1060 }
1061
1062 // If the queue is full, go to runtime.
1063 __ ld(tmp1, index);
1064 __ beqz(tmp1, L_runtime);
1065
1066 // Push into SATB queue.
1067 __ subi(tmp1, tmp1, wordSize);
1068 __ sd(tmp1, index);
1069 __ ld(tmp2, buffer);
1070 __ add(tmp1, tmp1, tmp2);
1071 __ sd(obj, Address(tmp1, 0));
1072 __ j(L_done);
1073
1074 // Runtime call
1075 __ bind(L_runtime);
1076
1077 preserve(obj);
1078 {
1079 SaveLiveRegisters save_registers(masm, this);
1080 __ mv(c_rarg0, obj);
1081 __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_barrier_pre), c_rarg0);
1082 }
1083
1084 __ bind(L_done);
1085 }
1086
1087 void ShenandoahBarrierStubC2::lrb(MacroAssembler* masm, Register obj, Address addr, Register tmp) {
1088 Label L_done;
1089
1090 // The node doesn't even need LRB barrier, just don't check anything else
1091 if (!_needs_load_ref_barrier) {
1092 return;
1093 }
1094
1095 if ((_node->barrier_data() & ShenandoahBitStrong) != 0) {
1096 // If both LRB and KeepAlive barriers are required (rare), do a runtime
1097 // check for enabled barrier.
1098 if (_needs_keep_alive_barrier) {
1099 Address gcs_addr(xthread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
1100 __ lbu(t0, gcs_addr);
1101
1102 if (_needs_load_ref_weak_barrier) {
1103 __ srli(t1, t0, ShenandoahHeap::WEAK_ROOTS_BITPOS);
1104 __ orr(t0, t0, t1);
1105 }
1106
1107 __ test_bit(t0, t0, ShenandoahHeap::HAS_FORWARDED_BITPOS);
1108 __ beqz(t0, L_done);
1109 }
1110
1111 // Weak/phantom loads always need to go to runtime. For strong refs we
1112 // check if the object in cset, if they are not, then we are done with LRB.
1113 __ mv(t1, ShenandoahHeap::in_cset_fast_test_addr());
1114 __ srli(t0, obj, ShenandoahHeapRegion::region_size_bytes_shift_jint());
1115 __ add(t1, t1, t0);
1116 __ lbu(t1, Address(t1));
1117 __ beqz(t1, L_done);
1118 }
1119
1120 dont_preserve(obj);
1121 {
1122 SaveLiveRegisters save_registers(masm, this);
1123
1124 // Runtime call wants:
1125 // c_rarg0 <- obj
1126 // c_rarg1 <- lea(addr)
1127 if (c_rarg0 == obj) {
1128 __ la(c_rarg1, addr);
1129 } else if (c_rarg1 == obj) {
1130 // Set up arguments in reverse, and then flip them
1131 __ la(c_rarg0, addr);
1132 // flip them
1133 __ mv(t0, c_rarg0);
1134 __ mv(c_rarg0, c_rarg1);
1135 __ mv(c_rarg1, t0);
1136 } else {
1137 assert_different_registers(c_rarg1, obj);
1138 __ la(c_rarg1, addr);
1139 __ mv(c_rarg0, obj);
1140 }
1141
1142 // Get address of runtime LRB entry and call it
1143 __ rt_call(lrb_runtime_entry_addr());
1144
1145 // If we loaded the object in the stub it means we don't need to return it
1146 // to fastpath, so no need to make this mov.
1147 if (!_do_load) {
1148 __ mv(obj, x10);
1149 }
1150 }
1151
1152 __ bind(L_done);
1153 }
1154
1155 Label* ShenandoahBarrierStubC2::entry() {
1156 return BarrierStubC2::entry();
1157 }
1158
1159 ShenandoahBarrierStubC2::ShenandoahBarrierStubC2(const MachNode* node, Register obj, Address addr, bool narrow, bool do_load, int offset) : BarrierStubC2(node),
1160 _obj(obj),
1161 _addr(addr),
1162 _do_load(do_load),
1163 _narrow(narrow),
1164 _maybe_null(!src_not_null(node)),
1165 _needs_load_ref_barrier(needs_load_ref_barrier(node)),
1166 _needs_load_ref_weak_barrier(needs_load_ref_barrier_weak(node)),
1167 _needs_keep_alive_barrier(needs_keep_alive_barrier(node)),
1168 _fastpath_branch_offset(),
1169 _test_and_branch_reachable(),
1170 _skip_trampoline(),
1171 _test_and_branch_reachable_entry() {
1172
1173 ShenandoahBarrierStubC2(node, obj, addr, narrow, do_load);
1174 }
1175 #endif // COMPILER2