diff a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp --- a/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/g1/g1BarrierSetAssembler_riscv.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2024, Huawei Technologies Co., Ltd. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as @@ -136,10 +136,11 @@ __ bind(done); } static void generate_queue_test_and_insertion(MacroAssembler* masm, ByteSize index_offset, ByteSize buffer_offset, Label& runtime, const Register thread, const Register value, const Register tmp1, const Register tmp2) { + assert_different_registers(value, tmp1, tmp2); // Can we store a value in the given thread's buffer? // (The index field is typed as size_t.) __ ld(tmp1, Address(thread, in_bytes(index_offset))); // tmp1 := *(index address) __ beqz(tmp1, runtime); // jump to runtime if index == 0 (full buffer) // The buffer is not full, store value into it. @@ -174,11 +175,11 @@ // Do we need to load the previous value? if (obj != noreg) { __ load_heap_oop(pre_val, Address(obj, 0), noreg, noreg, AS_RAW); } // Is the previous value null? - __ beqz(pre_val, done, true); + __ beqz(pre_val, done, /* is_far */ true); generate_queue_test_and_insertion(masm, G1ThreadLocalData::satb_mark_queue_index_offset(), G1ThreadLocalData::satb_mark_queue_buffer_offset(), runtime, thread, pre_val, tmp1, tmp2); @@ -212,10 +213,22 @@ __ bind(runtime); __ push_call_clobbered_registers(); + // Calling the runtime using the regular call_VM_leaf mechanism generates + // code (generated by InterpreterMacroAssember::call_VM_leaf_base) + // that checks that the *(fp+frame::interpreter_frame_last_sp) == nullptr. + // + // If we care generating the pre-barrier without a frame (e.g. in the + // intrinsified Reference.get() routine) then fp might be pointing to + // the caller frame and so this check will most likely fail at runtime. + // + // Expanding the call directly bypasses the generation of the check. + // So when we do not have have a full interpreter frame on the stack + // expand_call should be passed true. + if (expand_call) { assert(pre_val != c_rarg1, "smashed arg"); __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry), pre_val, thread); } else { __ call_VM_leaf(CAST_FROM_FN_PTR(address, G1BarrierSetRuntime::write_ref_field_pre_entry), pre_val, thread); @@ -299,11 +312,11 @@ stub->initialize_registers(obj, pre_val, thread, tmp1, tmp2); generate_pre_barrier_fast_path(masm, thread, tmp1); // If marking is active (*(mark queue active address) != 0), jump to stub (slow path) - __ bnez(tmp1, *stub->entry(), true); + __ bnez(tmp1, *stub->entry(), /* is_far */ true); __ bind(*stub->continuation()); } void G1BarrierSetAssembler::generate_c2_pre_barrier_stub(MacroAssembler* masm, @@ -362,44 +375,60 @@ } } void G1BarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) { + + bool in_heap = (decorators & IN_HEAP) != 0; + bool as_normal = (decorators & AS_NORMAL) != 0; + bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0; + + bool needs_pre_barrier = as_normal && !dest_uninitialized; + bool needs_post_barrier = (val != noreg && in_heap); + + assert_different_registers(val, tmp1, tmp2, tmp3); + // flatten object address if needed if (dst.offset() == 0) { if (dst.base() != tmp3) { __ mv(tmp3, dst.base()); } } else { __ la(tmp3, dst); } - g1_write_barrier_pre(masm, - tmp3 /* obj */, - tmp2 /* pre_val */, - xthread /* thread */, - tmp1 /* tmp1 */, - t1 /* tmp2 */, - val != noreg /* tosca_live */, - false /* expand_call */); + if (needs_pre_barrier) { + g1_write_barrier_pre(masm, + tmp3 /* obj */, + tmp2 /* pre_val */, + xthread /* thread */, + tmp1 /* tmp1 */, + t1 /* tmp2 */, + val != noreg /* tosca_live */, + false /* expand_call */); + } if (val == noreg) { BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), noreg, noreg, noreg, noreg); } else { // G1 barrier needs uncompressed oop for region cross check. Register new_val = val; - if (UseCompressedOops) { - new_val = t1; - __ mv(new_val, val); + if (needs_post_barrier) { + if (UseCompressedOops) { + new_val = t1; + __ mv(new_val, val); + } } BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), val, noreg, noreg, noreg); - g1_write_barrier_post(masm, - tmp3 /* store_adr */, - new_val /* new_val */, - xthread /* thread */, - tmp1 /* tmp1 */, - tmp2 /* tmp2 */); + if (needs_post_barrier) { + g1_write_barrier_post(masm, + tmp3 /* store_adr */, + new_val /* new_val */, + xthread /* thread */, + tmp1 /* tmp1 */, + tmp2 /* tmp2 */); + } } } #ifdef COMPILER1