| /* | |
| * Copyright (c) 2018, 2025, Oracle and/or its affiliates. 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 | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| * | |
| */ | |
| #include "gc/shared/barrierSet.hpp" | |
| #include "gc/shared/barrierSetAssembler.hpp" | |
| #include "gc/shared/barrierSetNMethod.hpp" | |
| #include "gc/shared/collectedHeap.hpp" | |
| #include "memory/universe.hpp" | |
| #include "runtime/javaThread.hpp" | |
| #include "runtime/stubRoutines.hpp" | |
| #ifdef COMPILER2 | |
| #include "gc/shared/c2/barrierSetC2.hpp" | |
| #endif // COMPILER2 | |
| #define __ masm-> | |
| void BarrierSetAssembler::load_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, | |
| Register dst, Address src, Register tmp1, Register tmp2, Register tmp3) { | |
| bool in_heap = (decorators & IN_HEAP) != 0; | |
| bool in_native = (decorators & IN_NATIVE) != 0; | |
| switch (type) { | |
| case T_OBJECT: | |
| case T_ARRAY: { | |
| if (in_heap) { | |
| { | |
| __ ldr(dst, src); | |
| } | |
| } else { | |
| assert(in_native, "why else?"); | |
| __ ldr(dst, src); | |
| } | |
| break; | |
| } | |
| case T_BOOLEAN: __ ldrb (dst, src); break; | |
| case T_BYTE: __ ldrsb (dst, src); break; | |
| case T_CHAR: __ ldrh (dst, src); break; | |
| case T_SHORT: __ ldrsh (dst, src); break; | |
| case T_INT: __ ldr_s32 (dst, src); break; | |
| case T_ADDRESS: __ ldr (dst, src); break; | |
| case T_LONG: | |
| assert(dst == noreg, "only to ltos"); | |
| __ add (src.index(), src.index(), src.base()); | |
| __ ldmia (src.index(), RegisterSet(R0_tos_lo) | RegisterSet(R1_tos_hi)); | |
| break; | |
| #ifdef __SOFTFP__ | |
| case T_FLOAT: | |
| assert(dst == noreg, "only to ftos"); | |
| __ ldr (R0_tos, src); | |
| break; | |
| case T_DOUBLE: | |
| assert(dst == noreg, "only to dtos"); | |
| __ add (src.index(), src.index(), src.base()); | |
| __ ldmia (src.index(), RegisterSet(R0_tos_lo) | RegisterSet(R1_tos_hi)); | |
| break; | |
| #else | |
| case T_FLOAT: | |
| assert(dst == noreg, "only to ftos"); | |
| __ add(src.index(), src.index(), src.base()); | |
| __ ldr_float (S0_tos, src.index()); | |
| break; | |
| case T_DOUBLE: | |
| assert(dst == noreg, "only to dtos"); | |
| __ add (src.index(), src.index(), src.base()); | |
| __ ldr_double (D0_tos, src.index()); | |
| break; | |
| #endif | |
| default: Unimplemented(); | |
| } | |
| } | |
| void BarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type, | |
| Address obj, Register val, Register tmp1, Register tmp2, Register tmp3, bool is_null) { | |
| bool in_heap = (decorators & IN_HEAP) != 0; | |
| bool in_native = (decorators & IN_NATIVE) != 0; | |
| switch (type) { | |
| case T_OBJECT: | |
| case T_ARRAY: { | |
| if (in_heap) { | |
| { | |
| __ str(val, obj); | |
| } | |
| } else { | |
| assert(in_native, "why else?"); | |
| __ str(val, obj); | |
| } | |
| break; | |
| } | |
| case T_BOOLEAN: | |
| __ and_32(val, val, 1); | |
| __ strb(val, obj); | |
| break; | |
| case T_BYTE: __ strb (val, obj); break; | |
| case T_CHAR: __ strh (val, obj); break; | |
| case T_SHORT: __ strh (val, obj); break; | |
| case T_INT: __ str (val, obj); break; | |
| case T_ADDRESS: __ str (val, obj); break; | |
| case T_LONG: | |
| assert(val == noreg, "only tos"); | |
| __ add (obj.index(), obj.index(), obj.base()); | |
| __ stmia (obj.index(), RegisterSet(R0_tos_lo) | RegisterSet(R1_tos_hi)); | |
| break; | |
| #ifdef __SOFTFP__ | |
| case T_FLOAT: | |
| assert(val == noreg, "only tos"); | |
| __ str (R0_tos, obj); | |
| break; | |
| case T_DOUBLE: | |
| assert(val == noreg, "only tos"); | |
| __ add (obj.index(), obj.index(), obj.base()); | |
| __ stmia (obj.index(), RegisterSet(R0_tos_lo) | RegisterSet(R1_tos_hi)); | |
| break; | |
| #else | |
| case T_FLOAT: | |
| assert(val == noreg, "only tos"); | |
| __ add (obj.index(), obj.index(), obj.base()); | |
| __ str_float (S0_tos, obj.index()); | |
| break; | |
| case T_DOUBLE: | |
| assert(val == noreg, "only tos"); | |
| __ add (obj.index(), obj.index(), obj.base()); | |
| __ str_double (D0_tos, obj.index()); | |
| break; | |
| #endif | |
| default: Unimplemented(); | |
| } | |
| } | |
| // Puts address of allocated object into register `obj` and end of allocated object into register `obj_end`. | |
| void BarrierSetAssembler::tlab_allocate(MacroAssembler* masm, Register obj, Register obj_end, Register tmp1, | |
| RegisterOrConstant size_expression, Label& slow_case) { | |
| const Register tlab_end = tmp1; | |
| assert_different_registers(obj, obj_end, tlab_end); | |
| __ ldr(obj, Address(Rthread, JavaThread::tlab_top_offset())); | |
| __ ldr(tlab_end, Address(Rthread, JavaThread::tlab_end_offset())); | |
| __ add_rc(obj_end, obj, size_expression); | |
| __ cmp(obj_end, tlab_end); | |
| __ b(slow_case, hi); | |
| __ str(obj_end, Address(Rthread, JavaThread::tlab_top_offset())); | |
| } | |
| void BarrierSetAssembler::nmethod_entry_barrier(MacroAssembler* masm) { | |
| BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod(); | |
| Register tmp0 = Rtemp; | |
| Register tmp1 = R5; // must be callee-save register | |
| // The are no GCs that require memory barrier on arm32 now | |
| #ifdef ASSERT | |
| NMethodPatchingType patching_type = nmethod_patching_type(); | |
| assert(patching_type == NMethodPatchingType::stw_instruction_and_data_patch, "Unsupported patching type"); | |
| #endif | |
| Label skip, guard; | |
| Address thread_disarmed_addr(Rthread, in_bytes(bs_nm->thread_disarmed_guard_value_offset())); | |
| __ block_comment("nmethod_barrier begin"); | |
| __ ldr_label(tmp0, guard); | |
| // No memory barrier here | |
| __ ldr(tmp1, thread_disarmed_addr); | |
| __ cmp(tmp0, tmp1); | |
| __ b(skip, eq); | |
| __ mov_address(tmp0, StubRoutines::method_entry_barrier()); | |
| __ call(tmp0); | |
| __ b(skip); | |
| __ bind(guard); | |
| // nmethod guard value. Skipped over in common case. | |
| // | |
| // Put a debug value to make any offsets skew | |
| // clearly visible in coredump | |
| __ emit_int32(0xDEADBEAF); | |
| __ bind(skip); | |
| __ block_comment("nmethod_barrier end"); | |
| } | |
| #ifdef COMPILER2 | |
| OptoReg::Name BarrierSetAssembler::refine_register(const Node* node, OptoReg::Name opto_reg) { | |
| if (!OptoReg::is_reg(opto_reg)) { | |
| return OptoReg::Bad; | |
| } | |
| const VMReg vm_reg = OptoReg::as_VMReg(opto_reg); | |
| if (!vm_reg->is_valid()){ | |
| // skip APSR and FPSCR | |
| return OptoReg::Bad; | |
| } | |
| return opto_reg; | |
| } | |
| void SaveLiveRegisters::initialize(BarrierStubC2* stub) { | |
| // Record registers that needs to be saved/restored | |
| RegMaskIterator rmi(stub->preserve_set()); | |
| while (rmi.has_next()) { | |
| const OptoReg::Name opto_reg = rmi.next(); | |
| if (OptoReg::is_reg(opto_reg)) { | |
| const VMReg vm_reg = OptoReg::as_VMReg(opto_reg); | |
| if (vm_reg->is_Register()) { | |
| gp_regs += RegSet::of(vm_reg->as_Register()); | |
| } else if (vm_reg->is_FloatRegister()) { | |
| fp_regs += FloatRegSet::of(vm_reg->as_FloatRegister()); | |
| } else { | |
| fatal("Unknown register type"); | |
| } | |
| } | |
| } | |
| // Remove C-ABI SOE registers that will be updated | |
| gp_regs -= RegSet::range(R4, R11) + RegSet::of(R13, R15); | |
| // Remove C-ABI SOE fp registers | |
| fp_regs -= FloatRegSet::range(S16, S31); | |
| } | |
| SaveLiveRegisters::SaveLiveRegisters(MacroAssembler* masm, BarrierStubC2* stub) | |
| : masm(masm), | |
| gp_regs(), | |
| fp_regs() { | |
| // Figure out what registers to save/restore | |
| initialize(stub); | |
| // Save registers | |
| if (gp_regs.size() > 0) __ push(RegisterSet::from(gp_regs)); | |
| if (fp_regs.size() > 0) __ fpush(FloatRegisterSet::from(fp_regs)); | |
| } | |
| SaveLiveRegisters::~SaveLiveRegisters() { | |
| // Restore registers | |
| if (fp_regs.size() > 0) __ fpop(FloatRegisterSet::from(fp_regs)); | |
| if (gp_regs.size() > 0) __ pop(RegisterSet::from(gp_regs)); | |
| } | |
| #endif // COMPILER2 |