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) || 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 __ 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_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, 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 in_progress(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_active_offset()));
116 Address index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()));
117 Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()));
118
119 // Is marking active?
120 if (in_bytes(SATBMarkQueue::byte_width_of_active()) == 4) {
121 __ lwu(tmp1, in_progress);
122 } else {
123 assert(in_bytes(SATBMarkQueue::byte_width_of_active()) == 1, "Assumption");
124 __ lbu(tmp1, in_progress);
125 }
126 __ beqz(tmp1, 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 __ sub(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_ref_field_pre_entry), pre_val, thread);
173 } else {
174 __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), pre_val, thread);
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 __ 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 void ShenandoahBarrierSetAssembler::iu_barrier(MacroAssembler* masm, Register dst, Register tmp) {
316 if (ShenandoahIUBarrier) {
317 __ push_call_clobbered_registers();
318
319 satb_write_barrier_pre(masm, noreg, dst, xthread, tmp, t0, true, false);
320
321 __ pop_call_clobbered_registers();
322 }
323 }
324
325 //
326 // Arguments:
327 //
328 // Inputs:
329 // src: oop location to load from, might be clobbered
330 //
331 // Output:
332 // dst: oop loaded from src location
333 //
334 // Kill:
335 // x30 (tmp reg)
336 //
337 // Alias:
338 // dst: x30 (might use x30 as temporary output register to avoid clobbering src)
339 //
340 void ShenandoahBarrierSetAssembler::load_at(MacroAssembler* masm,
341 DecoratorSet decorators,
342 BasicType type,
343 Register dst,
344 Address src,
345 Register tmp1,
346 Register tmp2) {
347 // 1: non-reference load, no additional barrier is needed
348 if (!is_reference_type(type)) {
349 BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp2);
350 return;
351 }
352
353 // 2: load a reference from src location and apply LRB if needed
354 if (ShenandoahBarrierSet::need_load_reference_barrier(decorators, type)) {
355 Register result_dst = dst;
356
357 // Preserve src location for LRB
358 RegSet saved_regs;
359 if (dst == src.base()) {
360 dst = (src.base() == x28) ? x29 : x28;
361 saved_regs = RegSet::of(dst);
362 __ push_reg(saved_regs, sp);
363 }
364 assert_different_registers(dst, src.base());
365
366 BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp2);
367
368 load_reference_barrier(masm, dst, src, decorators);
369
370 if (dst != result_dst) {
371 __ mv(result_dst, dst);
372 dst = result_dst;
373 }
374
375 if (saved_regs.bits() != 0) {
376 __ pop_reg(saved_regs, sp);
377 }
378 } else {
379 BarrierSetAssembler::load_at(masm, decorators, type, dst, src, tmp1, tmp2);
380 }
381
382 // 3: apply keep-alive barrier if needed
383 if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) {
384 __ enter();
385 __ push_call_clobbered_registers();
386 satb_write_barrier_pre(masm /* masm */,
387 noreg /* obj */,
388 dst /* pre_val */,
389 xthread /* thread */,
390 tmp1 /* tmp1 */,
391 tmp2 /* tmp2 */,
392 true /* tosca_live */,
393 true /* expand_call */);
394 __ pop_call_clobbered_registers();
395 __ leave();
396 }
397 }
398
399 void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
400 Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) {
401 bool on_oop = is_reference_type(type);
402 if (!on_oop) {
403 BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3);
404 return;
405 }
406
407 // flatten object address if needed
408 if (dst.offset() == 0) {
409 if (dst.base() != tmp3) {
410 __ mv(tmp3, dst.base());
411 }
412 } else {
413 __ la(tmp3, dst);
414 }
415
416 shenandoah_write_barrier_pre(masm,
417 tmp3 /* obj */,
418 tmp2 /* pre_val */,
419 xthread /* thread */,
420 tmp1 /* tmp */,
421 val != noreg /* tosca_live */,
422 false /* expand_call */);
423
424 if (val == noreg) {
425 BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), noreg, noreg, noreg, noreg);
426 } else {
427 iu_barrier(masm, val, tmp1);
428 // G1 barrier needs uncompressed oop for region cross check.
429 Register new_val = val;
430 if (UseCompressedOops) {
431 new_val = t1;
432 __ mv(new_val, val);
433 }
434 BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), val, noreg, noreg, noreg);
435 }
436 }
437
438 void ShenandoahBarrierSetAssembler::try_resolve_jobject_in_native(MacroAssembler* masm, Register jni_env,
439 Register obj, Register tmp, Label& slowpath) {
440 Label done;
441 // Resolve jobject
442 BarrierSetAssembler::try_resolve_jobject_in_native(masm, jni_env, obj, tmp, slowpath);
443
444 // Check for null.
445 __ beqz(obj, done);
446
447 assert(obj != t1, "need t1");
448 Address gc_state(jni_env, ShenandoahThreadLocalData::gc_state_offset() - JavaThread::jni_environment_offset());
449 __ lbu(t1, gc_state);
450
451 // Check for heap in evacuation phase
452 __ test_bit(t0, t1, ShenandoahHeap::EVACUATION_BITPOS);
453 __ bnez(t0, slowpath);
454
455 __ bind(done);
456 }
457
458 // Special Shenandoah CAS implementation that handles false negatives due
459 // to concurrent evacuation. The service is more complex than a
460 // traditional CAS operation because the CAS operation is intended to
461 // succeed if the reference at addr exactly matches expected or if the
462 // reference at addr holds a pointer to a from-space object that has
463 // been relocated to the location named by expected. There are two
464 // races that must be addressed:
465 // a) A parallel thread may mutate the contents of addr so that it points
466 // to a different object. In this case, the CAS operation should fail.
467 // b) A parallel thread may heal the contents of addr, replacing a
468 // from-space pointer held in addr with the to-space pointer
469 // representing the new location of the object.
470 // Upon entry to cmpxchg_oop, it is assured that new_val equals null
471 // or it refers to an object that is not being evacuated out of
472 // from-space, or it refers to the to-space version of an object that
473 // is being evacuated out of from-space.
474 //
475 // By default the value held in the result register following execution
476 // of the generated code sequence is 0 to indicate failure of CAS,
477 // non-zero to indicate success. If is_cae, the result is the value most
478 // recently fetched from addr rather than a boolean success indicator.
479 //
480 // Clobbers t0, t1
481 void ShenandoahBarrierSetAssembler::cmpxchg_oop(MacroAssembler* masm,
482 Register addr,
483 Register expected,
484 Register new_val,
485 Assembler::Aqrl acquire,
486 Assembler::Aqrl release,
487 bool is_cae,
488 Register result) {
489 bool is_narrow = UseCompressedOops;
490 Assembler::operand_size size = is_narrow ? Assembler::uint32 : Assembler::int64;
491
492 assert_different_registers(addr, expected, t0, t1);
493 assert_different_registers(addr, new_val, t0, t1);
494
495 Label retry, success, fail, done;
496
497 __ bind(retry);
498
499 // Step1: Try to CAS.
500 __ cmpxchg(addr, expected, new_val, size, acquire, release, /* result */ t1);
501
502 // If success, then we are done.
503 __ beq(expected, t1, success);
504
505 // Step2: CAS failed, check the forwarded pointer.
506 __ mv(t0, t1);
507
508 if (is_narrow) {
509 __ decode_heap_oop(t0, t0);
510 }
511 resolve_forward_pointer(masm, t0);
512
513 __ encode_heap_oop(t0, t0);
514
515 // Report failure when the forwarded oop was not expected.
516 __ bne(t0, expected, fail);
517
518 // Step 3: CAS again using the forwarded oop.
519 __ cmpxchg(addr, t1, new_val, size, acquire, release, /* result */ t0);
520
521 // Retry when failed.
522 __ bne(t0, t1, retry);
523
524 __ bind(success);
525 if (is_cae) {
526 __ mv(result, expected);
527 } else {
528 __ mv(result, 1);
529 }
530 __ j(done);
531
532 __ bind(fail);
533 if (is_cae) {
534 __ mv(result, t0);
535 } else {
536 __ mv(result, zr);
537 }
538
539 __ bind(done);
540 }
541
542 #undef __
543
544 #ifdef COMPILER1
545
546 #define __ ce->masm()->
547
548 void ShenandoahBarrierSetAssembler::gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub) {
549 ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1();
550 // At this point we know that marking is in progress.
551 // If do_load() is true then we have to emit the
552 // load of the previous value; otherwise it has already
553 // been loaded into _pre_val.
554 __ bind(*stub->entry());
555
556 assert(stub->pre_val()->is_register(), "Precondition.");
557
558 Register pre_val_reg = stub->pre_val()->as_register();
559
560 if (stub->do_load()) {
561 ce->mem2reg(stub->addr(), stub->pre_val(), T_OBJECT, stub->patch_code(), stub->info(), false /* wide */);
562 }
563 __ beqz(pre_val_reg, *stub->continuation(), /* is_far */ true);
564 ce->store_parameter(stub->pre_val()->as_register(), 0);
565 __ far_call(RuntimeAddress(bs->pre_barrier_c1_runtime_code_blob()->code_begin()));
566 __ j(*stub->continuation());
567 }
568
569 void ShenandoahBarrierSetAssembler::gen_load_reference_barrier_stub(LIR_Assembler* ce,
570 ShenandoahLoadReferenceBarrierStub* stub) {
571 ShenandoahBarrierSetC1* bs = (ShenandoahBarrierSetC1*)BarrierSet::barrier_set()->barrier_set_c1();
572 __ bind(*stub->entry());
573
574 DecoratorSet decorators = stub->decorators();
575 bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators);
576 bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators);
577 bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators);
578 bool is_native = ShenandoahBarrierSet::is_native_access(decorators);
579
580 Register obj = stub->obj()->as_register();
581 Register res = stub->result()->as_register();
582 Register addr = stub->addr()->as_pointer_register();
583 Register tmp1 = stub->tmp1()->as_register();
584 Register tmp2 = stub->tmp2()->as_register();
585
586 assert(res == x10, "result must arrive in x10");
587 assert_different_registers(tmp1, tmp2, t0);
588
589 if (res != obj) {
590 __ mv(res, obj);
591 }
592
593 if (is_strong) {
594 // Check for object in cset.
595 __ mv(tmp2, ShenandoahHeap::in_cset_fast_test_addr());
596 __ srli(tmp1, res, ShenandoahHeapRegion::region_size_bytes_shift_jint());
597 __ add(tmp2, tmp2, tmp1);
598 __ lbu(tmp2, Address(tmp2));
599 __ beqz(tmp2, *stub->continuation(), true /* is_far */);
600 }
601
602 ce->store_parameter(res, 0);
603 ce->store_parameter(addr, 1);
604
605 if (is_strong) {
606 if (is_native) {
607 __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_native_rt_code_blob()->code_begin()));
608 } else {
609 __ far_call(RuntimeAddress(bs->load_reference_barrier_strong_rt_code_blob()->code_begin()));
610 }
611 } else if (is_weak) {
612 __ far_call(RuntimeAddress(bs->load_reference_barrier_weak_rt_code_blob()->code_begin()));
613 } else {
614 assert(is_phantom, "only remaining strength");
615 __ far_call(RuntimeAddress(bs->load_reference_barrier_phantom_rt_code_blob()->code_begin()));
616 }
617
618 __ j(*stub->continuation());
619 }
620
621 #undef __
622
623 #define __ sasm->
624
625 void ShenandoahBarrierSetAssembler::generate_c1_pre_barrier_runtime_stub(StubAssembler* sasm) {
626 __ prologue("shenandoah_pre_barrier", false);
627
628 // arg0 : previous value of memory
629
630 BarrierSet* bs = BarrierSet::barrier_set();
631
632 const Register pre_val = x10;
633 const Register thread = xthread;
634 const Register tmp = t0;
635
636 Address queue_index(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()));
637 Address buffer(thread, in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()));
638
639 Label done;
640 Label runtime;
641
642 // Is marking still active?
643 Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
644 __ lb(tmp, gc_state);
645 __ test_bit(tmp, tmp, ShenandoahHeap::MARKING_BITPOS);
646 __ beqz(tmp, done);
647
648 // Can we store original value in the thread's buffer?
649 __ ld(tmp, queue_index);
650 __ beqz(tmp, runtime);
651
652 __ sub(tmp, tmp, wordSize);
653 __ sd(tmp, queue_index);
654 __ ld(t1, buffer);
655 __ add(tmp, tmp, t1);
656 __ load_parameter(0, t1);
657 __ sd(t1, Address(tmp, 0));
658 __ j(done);
659
660 __ bind(runtime);
661 __ push_call_clobbered_registers();
662 __ load_parameter(0, pre_val);
663 __ call_VM_leaf(CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), pre_val, thread);
664 __ pop_call_clobbered_registers();
665 __ bind(done);
666
667 __ epilogue();
668 }
669
670 void ShenandoahBarrierSetAssembler::generate_c1_load_reference_barrier_runtime_stub(StubAssembler* sasm,
671 DecoratorSet decorators) {
672 __ prologue("shenandoah_load_reference_barrier", false);
673 // arg0 : object to be resolved
674
675 __ push_call_clobbered_registers();
676 __ load_parameter(0, x10);
677 __ load_parameter(1, x11);
678
679 bool is_strong = ShenandoahBarrierSet::is_strong_access(decorators);
680 bool is_weak = ShenandoahBarrierSet::is_weak_access(decorators);
681 bool is_phantom = ShenandoahBarrierSet::is_phantom_access(decorators);
682 bool is_native = ShenandoahBarrierSet::is_native_access(decorators);
683 address target = nullptr;
684 if (is_strong) {
685 if (is_native) {
686 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong);
687 } else {
688 if (UseCompressedOops) {
689 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong_narrow);
690 } else {
691 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_strong);
692 }
693 }
694 } else if (is_weak) {
695 assert(!is_native, "weak must not be called off-heap");
696 if (UseCompressedOops) {
697 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak_narrow);
698 } else {
699 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_weak);
700 }
701 } else {
702 assert(is_phantom, "only remaining strength");
703 assert(is_native, "phantom must only be called off-heap");
704 target = CAST_FROM_FN_PTR(address, ShenandoahRuntime::load_reference_barrier_phantom);
705 }
706 __ call(target);
707 __ mv(t0, x10);
708 __ pop_call_clobbered_registers();
709 __ mv(x10, t0);
710
711 __ epilogue();
712 }
713
714 #undef __
715
716 #endif // COMPILER1