Skip to content

[GR-65477] Avoid use of scratch register in ZGC CAS #11339

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package jdk.graal.compiler.hotspot.test;

import java.io.IOException;

import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;

import jdk.graal.compiler.core.test.GraalCompilerTest;
import jdk.graal.compiler.core.test.SubprocessTest;
import jdk.graal.compiler.replacements.test.UnsafeSubstitutionsTest;
import jdk.graal.compiler.test.SubprocessUtil;
import jdk.vm.ci.aarch64.AArch64;

public class UnsafeSubstitutionsWithoutLSETest extends SubprocessTest {
@Before
public void before() {
Assume.assumeTrue("AArch64-specific test", getArchitecture() instanceof AArch64);
}

public void testWithoutLSE(Class<? extends GraalCompilerTest> testClass) {
Runnable nopRunnable = () -> {
/*
* The runnable is only relevant when running a test in the same class as the parent
* process.
*/
};
SubprocessUtil.Subprocess subprocess = null;
try {
subprocess = launchSubprocess(testClass, ALL_TESTS, nopRunnable, "-XX:-UseLSE");
} catch (IOException | InterruptedException e) {
Assert.fail("subprocess exception: " + e);
}
Assert.assertEquals("subprocess exit code", 0, subprocess.exitCode);
}

@Test
public void unsafeSubstitutions() {
testWithoutLSE(UnsafeSubstitutionsTest.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ static void emitStoreBarrier(CompilationResultBuilder crb,
LIRInstruction op,
GraalHotSpotVMConfig config,
AArch64Address address,
Register result,
Register tmp,
StoreKind storeKind,
ForeignCallLinkage callTarget,
LIRFrameState state) {
Expand All @@ -130,9 +130,9 @@ static void emitStoreBarrier(CompilationResultBuilder crb,
Register rscratch1 = sc1.getRegister();
Register rscratch2 = sc2.getRegister();

Assembler.guaranteeDifferentRegisters(address.getBase(), result, rscratch1);
Assembler.guaranteeDifferentRegisters(address.getOffset(), result, rscratch1);
Assembler.guaranteeDifferentRegisters(result, rscratch1);
Assembler.guaranteeDifferentRegisters(address.getBase(), tmp, rscratch1);
Assembler.guaranteeDifferentRegisters(address.getOffset(), tmp, rscratch1);
Assembler.guaranteeDifferentRegisters(tmp, rscratch1);

if (storeKind == StoreKind.Atomic) {
if (state != null) {
Expand Down Expand Up @@ -182,7 +182,7 @@ static void emitStoreBarrier(CompilationResultBuilder crb,
storeBarrierMedium(crb, masm, config,
address,
rscratch2,
result,
tmp,
rscratch1,
storeKind,
continuation,
Expand Down Expand Up @@ -423,10 +423,11 @@ public void emitCompareAndSwapOp(LIRGeneratorTool tool,
AllocatableValue allocatableNewValue,
BarrierType barrierType) {
ForeignCallLinkage callTarget = getWriteBarrierStub(barrierType, StoreKind.Atomic);
AllocatableValue temp = tool.newVariable(tool.toRegisterKind(LIRKind.value(memKind)));
AllocatableValue tmp = tool.newVariable(tool.toRegisterKind(LIRKind.value(memKind)));
AllocatableValue tmp2 = tool.newVariable(tool.toRegisterKind(LIRKind.value(memKind)));
tool.getResult().getFrameMapBuilder().callsMethod(callTarget.getOutgoingCallingConvention());
tool.append(new AArch64HotSpotZCompareAndSwapOp(isLogicVariant, memKind, memoryOrder, isLogicVariant, result,
allocatableExpectedValue, allocatableNewValue, tool.asAllocatable(address), config, callTarget, temp));
allocatableExpectedValue, allocatableNewValue, tool.asAllocatable(address), config, callTarget, tmp, tmp2));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,37 +51,46 @@ public final class AArch64HotSpotZCompareAndSwapOp extends AArch64AtomicMove.Com
private final boolean isLogic;
private final GraalHotSpotVMConfig config;
private final ForeignCallLinkage callTarget;
@Temp protected AllocatableValue tmp;
@Temp private AllocatableValue tmp;
@Temp private AllocatableValue tmp2;

public AArch64HotSpotZCompareAndSwapOp(boolean isLogic, AArch64Kind accessKind, MemoryOrderMode memoryOrder, boolean setConditionFlags, AllocatableValue result, Value expectedValue,
public AArch64HotSpotZCompareAndSwapOp(boolean isLogic,
AArch64Kind accessKind,
MemoryOrderMode memoryOrder,
boolean setConditionFlags,
AllocatableValue result,
Value expectedValue,
AllocatableValue newValue,
AllocatableValue addressValue, GraalHotSpotVMConfig config, ForeignCallLinkage callTarget, AllocatableValue tmp) {
AllocatableValue addressValue,
GraalHotSpotVMConfig config,
ForeignCallLinkage callTarget,
AllocatableValue tmp, AllocatableValue tmp2) {
super(TYPE, accessKind, memoryOrder, setConditionFlags, result, expectedValue, newValue, addressValue);
this.isLogic = isLogic;
this.config = config;
this.callTarget = callTarget;
this.tmp = tmp;
this.tmp2 = tmp2;
}

@Override
public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) {
// Use ANY_SIZE here because the store barrier performs 16 and 64 access for atomics.
AArch64Address location = AArch64Address.createBaseRegisterOnlyAddress(AArch64Address.ANY_SIZE, asRegister(addressValue));
AArch64HotSpotZBarrierSetLIRGenerator.emitStoreBarrier(crb, masm, this, config, location, asRegister(tmp), ZWriteBarrierSetLIRGeneratorTool.StoreKind.Atomic,
Register tmpRegister = asRegister(tmp);
AArch64HotSpotZBarrierSetLIRGenerator.emitStoreBarrier(crb, masm, this, config, location, tmpRegister, ZWriteBarrierSetLIRGeneratorTool.StoreKind.Atomic,
callTarget, null);
try (AArch64MacroAssembler.ScratchRegister sc1 = masm.getScratchRegister()) {
Register rscratch1 = sc1.getRegister();
// Color newValue and expectedValue into a temporary registers
AArch64HotSpotZBarrierSetLIRGenerator.zColor(crb, masm, config, asRegister(tmp), asRegister(newValue));
AArch64HotSpotZBarrierSetLIRGenerator.zColor(crb, masm, config, rscratch1, asRegister(expectedValue));
Register address = asRegister(addressValue);
// Produce the colored result into a temporary register
Register result = asRegister(resultValue);
Register expected = rscratch1;
emitCompareAndSwap(masm, accessKind, address, result, expected, asRegister(tmp), memoryOrder, setConditionFlags);
if (!isLogic) {
AArch64HotSpotZBarrierSetLIRGenerator.zUncolor(masm, config, asRegister(resultValue));
}
Register tmp2Register = asRegister(tmp2);
// Color newValue and expectedValue into a temporary registers
AArch64HotSpotZBarrierSetLIRGenerator.zColor(crb, masm, config, tmpRegister, asRegister(newValue));
AArch64HotSpotZBarrierSetLIRGenerator.zColor(crb, masm, config, tmp2Register, asRegister(expectedValue));
Register address = asRegister(addressValue);
// Produce the colored result into a temporary register
Register result = asRegister(resultValue);
Register expected = tmp2Register;
emitCompareAndSwap(masm, accessKind, address, result, expected, tmpRegister, memoryOrder, setConditionFlags);
if (!isLogic) {
AArch64HotSpotZBarrierSetLIRGenerator.zUncolor(masm, config, asRegister(resultValue));
}
}
}