8329982: compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/SimpleDebugInfoTest.java failed assert(oopDesc::is_oop_or_null(val)) failed: bad oop found

Reviewed-by: never
This commit is contained in:
Doug Simon 2024-05-03 19:51:37 +00:00
parent c1a164528a
commit b20fa7b48b
14 changed files with 422 additions and 327 deletions

@ -65,16 +65,10 @@ bool BarrierSetNMethod::supports_entry_barrier(nmethod* nm) {
return false;
}
if (nm->is_native_method() || nm->is_compiled_by_c2() || nm->is_compiled_by_c1()) {
if (nm->is_native_method() || nm->is_compiled_by_c2() || nm->is_compiled_by_c1() || nm->is_compiled_by_jvmci()) {
return true;
}
#if INCLUDE_JVMCI
if (nm->is_compiled_by_jvmci() && nm->jvmci_nmethod_data()->has_entry_barrier()) {
return true;
}
#endif
return false;
}

@ -771,11 +771,8 @@ JVMCI::CodeInstallResult CodeInstaller::install(JVMCICompiler* compiler,
JVMCI_THROW_MSG_(IllegalArgumentException, "InstalledCode object must be a HotSpotNmethod when installing a HotSpotCompiledNmethod", JVMCI::ok);
}
// We would like to be strict about the nmethod entry barrier but there are various test
// configurations which generate assembly without being a full compiler. So for now we enforce
// that JIT compiled methods must have an nmethod barrier.
bool install_default = JVMCIENV->get_HotSpotNmethod_isDefault(installed_code) != 0;
if (_nmethod_entry_patch_offset == -1 && install_default) {
// Enforce that compiled methods have an nmethod barrier.
if (_nmethod_entry_patch_offset == -1) {
JVMCI_THROW_MSG_(IllegalArgumentException, "nmethod entry barrier is missing", JVMCI::ok);
}
@ -816,14 +813,12 @@ JVMCI::CodeInstallResult CodeInstaller::install(JVMCICompiler* compiler,
DirectivesStack::release(directive);
}
if (_nmethod_entry_patch_offset != -1) {
BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod();
BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod();
// an empty error buffer for use by the verify_barrier code
err_msg msg("");
if (!bs_nm->verify_barrier(nm, msg)) {
JVMCI_THROW_MSG_(IllegalArgumentException, err_msg("nmethod entry barrier is malformed: %s", msg.buffer()), JVMCI::ok);
}
// an empty error buffer for use by the verify_barrier code
err_msg msg("");
if (!bs_nm->verify_barrier(nm, msg)) {
JVMCI_THROW_MSG_(IllegalArgumentException, err_msg("nmethod entry barrier is malformed: %s", msg.buffer()), JVMCI::ok);
}
}
}

@ -787,6 +787,7 @@ void JVMCINMethodData::initialize(int nmethod_mirror_index,
{
_failed_speculations = failed_speculations;
_nmethod_mirror_index = nmethod_mirror_index;
guarantee(nmethod_entry_patch_offset != -1, "missing entry barrier");
_nmethod_entry_patch_offset = nmethod_entry_patch_offset;
if (nmethod_mirror_name != nullptr) {
_has_name = true;

@ -54,10 +54,8 @@ class JVMCINMethodData : public ResourceObj {
// This is -1 if there is no mirror in the oops table.
int _nmethod_mirror_index;
// This is the offset of the patchable part of the nmethod entry barrier sequence. The meaning is
// somewhat platform dependent as the way patching is done varies by architecture. Older JVMCI
// based compilers didn't emit the entry barrier so having a positive value for this offset
// confirms that the installed code supports the entry barrier.
// This is the offset of the patchable part of the nmethod entry barrier sequence. The meaning is
// somewhat platform dependent as the way patching is done varies by architecture.
int _nmethod_entry_patch_offset;
// Address of the failed speculations list to which a speculation
@ -129,12 +127,7 @@ public:
// Sets the mirror in nm's oops table.
void set_nmethod_mirror(nmethod* nm, oop mirror);
bool has_entry_barrier() {
return _nmethod_entry_patch_offset != -1;
}
int nmethod_entry_patch_offset() {
guarantee(_nmethod_entry_patch_offset != -1, "missing entry barrier");
return _nmethod_entry_patch_offset;
}
};

@ -1,108 +0,0 @@
/*
* Copyright (c) 2015, 2021, 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.
*/
/*
* @test
* @bug 8136421
* @requires vm.jvmci
* @library /test/lib /
* @library ../common/patches
* @modules java.base/jdk.internal.misc
* @modules java.base/jdk.internal.org.objectweb.asm
* java.base/jdk.internal.org.objectweb.asm.tree
* jdk.internal.vm.ci/jdk.vm.ci.hotspot
* jdk.internal.vm.ci/jdk.vm.ci.code
* jdk.internal.vm.ci/jdk.vm.ci.code.site
* jdk.internal.vm.ci/jdk.vm.ci.meta
* jdk.internal.vm.ci/jdk.vm.ci.runtime
*
* @build jdk.internal.vm.ci/jdk.vm.ci.hotspot.CompilerToVMHelper
* jdk.test.whitebox.WhiteBox jdk.test.whitebox.parser.DiagnosticCommand
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* jdk.test.whitebox.parser.DiagnosticCommand
* @run junit/othervm -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI
* compiler.jvmci.compilerToVM.InvalidateInstalledCodeTest
*/
package compiler.jvmci.compilerToVM;
import compiler.jvmci.common.CodeInstallerTest;
import compiler.jvmci.common.CTVMUtilities;
import jdk.test.lib.Asserts;
import jdk.test.lib.Utils;
import jdk.vm.ci.code.InstalledCode;
import jdk.vm.ci.code.site.Site;
import jdk.vm.ci.code.site.DataPatch;
import jdk.vm.ci.hotspot.CompilerToVMHelper;
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
import jdk.vm.ci.hotspot.HotSpotCompiledCode.Comment;
import jdk.vm.ci.hotspot.HotSpotNmethod;
import jdk.vm.ci.meta.Assumptions.Assumption;
import java.util.List;
import org.junit.Test;
public class InvalidateInstalledCodeTest extends CodeInstallerTest {
@Test
public void testInvalidation() {
List<CompileCodeTestCase> testCases
= CompileCodeTestCase.generate(/* bci = */ 0);
testCases.addAll(CompileCodeTestCase.generate(/* bci = */ -1));
testCases.forEach(t -> check(t));
checkNull();
}
private void checkNull() {
Utils.runAndCheckException(
() -> CompilerToVMHelper.invalidateHotSpotNmethod(null, true),
NullPointerException.class);
}
private void check(CompileCodeTestCase testCase) {
HotSpotResolvedJavaMethod javaMethod = CTVMUtilities.getResolvedMethod(testCase.executable);
HotSpotNmethod nmethod = (HotSpotNmethod) installEmptyCode(new Site[0], new Assumption[0],
new Comment[0], 8, new DataPatch[0], null);
Asserts.assertTrue(nmethod.isValid(), testCase + " : code is invalid even before invalidation");
Asserts.assertTrue(nmethod.isValid(), testCase + " : code is not valid, i = " + nmethod);
Asserts.assertTrue(nmethod.isAlive(), testCase + " : code is not alive, i = " + nmethod);
Asserts.assertNotEquals(nmethod.getStart(), 0L);
// Make nmethod non-entrant but still alive
CompilerToVMHelper.invalidateHotSpotNmethod(nmethod, false);
Asserts.assertFalse(nmethod.isValid(), testCase + " : code is valid, i = " + nmethod);
Asserts.assertTrue(nmethod.isAlive(), testCase + " : code is not alive, i = " + nmethod);
Asserts.assertEquals(nmethod.getStart(), 0L);
// Deoptimize the nmethod and cut the link to it from the HotSpotNmethod
CompilerToVMHelper.invalidateHotSpotNmethod(nmethod, true);
Asserts.assertFalse(nmethod.isValid(), testCase + " : code is valid, i = " + nmethod);
Asserts.assertFalse(nmethod.isAlive(), testCase + " : code is alive, i = " + nmethod);
Asserts.assertEquals(nmethod.getStart(), 0L);
}
}

@ -1,2 +0,0 @@
compiler.jvmci.events.JvmciNotifyInstallEventTest
compiler.jvmci.common.JVMCIHelpers

@ -1,179 +0,0 @@
/*
* Copyright (c) 2015, 2019, 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.
*/
/*
* @test
* @bug 8136421
* @requires vm.jvmci & !vm.graal.enabled & vm.compMode == "Xmixed"
* @library / /test/lib
* @library ../common/patches
* @modules java.base/jdk.internal.misc
* @modules java.base/jdk.internal.org.objectweb.asm
* java.base/jdk.internal.org.objectweb.asm.tree
* jdk.internal.vm.ci/jdk.vm.ci.hotspot
* jdk.internal.vm.ci/jdk.vm.ci.code
* jdk.internal.vm.ci/jdk.vm.ci.code.site
* jdk.internal.vm.ci/jdk.vm.ci.meta
* jdk.internal.vm.ci/jdk.vm.ci.runtime
* jdk.internal.vm.ci/jdk.vm.ci.services
*
* @build jdk.internal.vm.ci/jdk.vm.ci.hotspot.CompilerToVMHelper
* @build compiler.jvmci.common.JVMCIHelpers
* @run driver jdk.test.lib.FileInstaller ./JvmciNotifyInstallEventTest.config
* ./META-INF/services/jdk.vm.ci.services.JVMCIServiceLocator
* @run driver jdk.test.lib.helpers.ClassFileInstaller
* compiler.jvmci.common.JVMCIHelpers$EmptyHotspotCompiler
* compiler.jvmci.common.JVMCIHelpers$EmptyCompilerFactory
* compiler.jvmci.common.JVMCIHelpers$EmptyCompilationRequestResult
* compiler.jvmci.common.JVMCIHelpers$EmptyVMEventListener
* @run main/othervm -XX:+UnlockExperimentalVMOptions
* -Djvmci.Compiler=EmptyCompiler -Xbootclasspath/a:.
* -XX:+UseJVMCICompiler -XX:-BootstrapJVMCI
* -XX:-UseJVMCINativeLibrary -XX:JVMCITraceLevel=1
* -Dtest.jvmci.forceRuntimeStubAllocFail=test_stub_that_fails_to_be_allocated
* compiler.jvmci.events.JvmciNotifyInstallEventTest
* @run main/othervm -XX:+UnlockExperimentalVMOptions
* -Djvmci.Compiler=EmptyCompiler -Xbootclasspath/a:.
* -XX:+UseJVMCICompiler -XX:-BootstrapJVMCI -XX:JVMCINMethodSizeLimit=0
* -XX:-UseJVMCINativeLibrary
* compiler.jvmci.events.JvmciNotifyInstallEventTest
*/
package compiler.jvmci.events;
import compiler.jvmci.common.CTVMUtilities;
import compiler.jvmci.common.testcases.SimpleClass;
import jdk.test.lib.Asserts;
import jdk.test.lib.Platform;
import jdk.test.lib.Utils;
import jdk.vm.ci.services.JVMCIServiceLocator;
import jdk.vm.ci.code.BailoutException;
import jdk.vm.ci.code.CompiledCode;
import jdk.vm.ci.code.InstalledCode;
import jdk.vm.ci.code.site.DataPatch;
import jdk.vm.ci.code.site.Site;
import jdk.vm.ci.hotspot.HotSpotCodeCacheProvider;
import jdk.vm.ci.hotspot.HotSpotCompiledCode;
import jdk.vm.ci.hotspot.HotSpotCompiledCode.Comment;
import jdk.vm.ci.hotspot.HotSpotCompiledNmethod;
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
import jdk.vm.ci.hotspot.HotSpotVMEventListener;
import jdk.vm.ci.meta.Assumptions.Assumption;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import java.lang.reflect.Method;
public class JvmciNotifyInstallEventTest extends JVMCIServiceLocator implements HotSpotVMEventListener {
private static final String METHOD_NAME = "testMethod";
private static volatile int gotInstallNotification = 0;
public static void main(String args[]) {
new JvmciNotifyInstallEventTest().runTest();
}
@Override
public <S> S getProvider(Class<S> service) {
if (service == HotSpotVMEventListener.class) {
return service.cast(this);
}
return null;
}
private void runTest() {
if (gotInstallNotification != 0) {
throw new Error("Got install notification before test actions");
}
HotSpotCodeCacheProvider codeCache;
try {
codeCache = (HotSpotCodeCacheProvider) HotSpotJVMCIRuntime.runtime()
.getHostJVMCIBackend().getCodeCache();
} catch (InternalError ie) {
// passed
return;
}
Method testMethod;
try {
testMethod = SimpleClass.class.getDeclaredMethod(METHOD_NAME);
} catch (NoSuchMethodException e) {
throw new Error("TEST BUG: Can't find " + METHOD_NAME, e);
}
HotSpotResolvedJavaMethod method = CTVMUtilities
.getResolvedMethod(SimpleClass.class, testMethod);
int dataSectionAlignment = 8; // CodeBuffer::SECT_CONSTS code section alignment
HotSpotCompiledCode compiledCode = new HotSpotCompiledNmethod(METHOD_NAME,
new byte[0], 0, new Site[0], new Assumption[0],
new ResolvedJavaMethod[]{method}, new Comment[0], new byte[0],
dataSectionAlignment, new DataPatch[0], false, 0, null,
method, 0, 1, 0L, false);
codeCache.installCode(method, compiledCode, /* installedCode = */ null,
/* speculationLog = */ null, /* isDefault = */ false);
Asserts.assertEQ(gotInstallNotification, 1,
"Got unexpected event count after 1st install attempt");
// since "empty" compilation result is ok, a second attempt should be ok
codeCache.installCode(method, compiledCode, /* installedCode = */ null,
/* speculationLog = */ null, /* isDefault = */ false);
Asserts.assertEQ(gotInstallNotification, 2,
"Got unexpected event count after 2nd install attempt");
// and an incorrect cases
Utils.runAndCheckException(() -> {
codeCache.installCode(method, null, null, null, true);
}, NullPointerException.class);
Asserts.assertEQ(gotInstallNotification, 2,
"Got unexpected event count after 3rd install attempt");
Utils.runAndCheckException(() -> {
codeCache.installCode(null, null, null, null, true);
}, NullPointerException.class);
Asserts.assertEQ(gotInstallNotification, 2,
"Got unexpected event count after 4th install attempt");
String stubToFail = System.getProperty("test.jvmci.forceRuntimeStubAllocFail");
if (Platform.isDebugBuild() && stubToFail != null) {
HotSpotCompiledCode stub = new HotSpotCompiledCode(stubToFail,
/* targetCode */ new byte[0],
/* targetCodeSize */ 0,
/* sites */ new Site[0],
/* assumptions */ new Assumption[0],
/* methods */ new ResolvedJavaMethod[0],
/* comments */ new Comment[0],
/* dataSection */ new byte[0],
dataSectionAlignment,
/* dataSectionPatches */ new DataPatch[0],
/* isImmutablePIC */ false,
/* totalFrameSize */ 0,
/* deoptRescueSlot */ null);
try {
codeCache.installCode(null, stub, null, null, true);
throw new AssertionError("Didn't get expected " + BailoutException.class.getName());
} catch (BailoutException e) {
Asserts.assertEQ(e.getMessage(), "Error installing " + stubToFail + ": code cache is full");
}
}
}
@Override
public void notifyInstall(HotSpotCodeCacheProvider hotSpotCodeCacheProvider,
InstalledCode installedCode, CompiledCode compiledCode) {
gotInstallNotification++;
}
}

@ -35,6 +35,7 @@ import jdk.vm.ci.code.test.riscv64.RISCV64TestAssembler;
import jdk.vm.ci.hotspot.HotSpotCodeCacheProvider;
import jdk.vm.ci.hotspot.HotSpotCompiledCode;
import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
import jdk.vm.ci.hotspot.HotSpotNmethod;
import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
import jdk.vm.ci.meta.ConstantReflectionProvider;
import jdk.vm.ci.meta.MetaAccessProvider;
@ -95,7 +96,7 @@ public class CodeInstallationTest {
}
}
protected void test(TestCompiler compiler, Method method, Object... args) {
protected HotSpotNmethod test(TestCompiler compiler, Method method, Object... args) {
try {
HotSpotResolvedJavaMethod resolvedMethod = (HotSpotResolvedJavaMethod) metaAccess.lookupJavaMethod(method);
TestAssembler asm = createAssembler();
@ -115,9 +116,9 @@ public class CodeInstallationTest {
Object expected = method.invoke(null, args);
Object actual = installed.executeVarargs(args);
Assert.assertEquals(expected, actual);
return (HotSpotNmethod) installed;
} catch (Exception e) {
e.printStackTrace();
Assert.fail(e.toString());
throw new AssertionError(e);
}
}
}

@ -0,0 +1,86 @@
/*
* Copyright (c) 2024, 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.
*/
/*
* @test
* @requires vm.jvmci & !vm.graal.enabled & vm.compMode == "Xmixed"
* @library / /test/lib
* @modules jdk.internal.vm.ci/jdk.vm.ci.hotspot
* jdk.internal.vm.ci/jdk.vm.ci.code
* jdk.internal.vm.ci/jdk.vm.ci.code.site
* jdk.internal.vm.ci/jdk.vm.ci.meta
* jdk.internal.vm.ci/jdk.vm.ci.runtime
*
* @run main/othervm -XX:+UnlockExperimentalVMOptions
* -Xbootclasspath/a:.
* -XX:+EnableJVMCI -XX:JVMCITraceLevel=1
* -Dtest.jvmci.forceRuntimeStubAllocFail=test_stub_that_fails_to_be_allocated
* jdk.vm.ci.code.test.RuntimeStubAllocFailTest
*/
package jdk.vm.ci.code.test;
import jdk.test.lib.Asserts;
import jdk.test.lib.Platform;
import jdk.vm.ci.code.BailoutException;
import jdk.vm.ci.code.site.DataPatch;
import jdk.vm.ci.code.site.Site;
import jdk.vm.ci.hotspot.HotSpotCodeCacheProvider;
import jdk.vm.ci.hotspot.HotSpotCompiledCode;
import jdk.vm.ci.hotspot.HotSpotCompiledCode.Comment;
import jdk.vm.ci.meta.Assumptions.Assumption;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.runtime.JVMCI;
import jdk.vm.ci.runtime.JVMCIRuntime;
import jdk.vm.ci.runtime.JVMCIBackend;
public class RuntimeStubAllocFailTest {
public static void main(String args[]) {
JVMCIBackend backend = JVMCI.getRuntime().getHostJVMCIBackend();
HotSpotCodeCacheProvider codeCache = (HotSpotCodeCacheProvider) backend.getCodeCache();
int dataSectionAlignment = 8; // CodeBuffer::SECT_CONSTS code section alignment
String stubToFail = System.getProperty("test.jvmci.forceRuntimeStubAllocFail");
if (Platform.isDebugBuild() && stubToFail != null) {
HotSpotCompiledCode stub = new HotSpotCompiledCode(stubToFail,
/* targetCode */ new byte[0],
/* targetCodeSize */ 0,
/* sites */ new Site[0],
/* assumptions */ new Assumption[0],
/* methods */ new ResolvedJavaMethod[0],
/* comments */ new Comment[0],
/* dataSection */ new byte[0],
dataSectionAlignment,
/* dataSectionPatches */ new DataPatch[0],
/* isImmutablePIC */ false,
/* totalFrameSize */ 0,
/* deoptRescueSlot */ null);
try {
codeCache.installCode(null, stub, null, null, true);
throw new AssertionError("Didn't get expected " + BailoutException.class.getName());
} catch (BailoutException e) {
Asserts.assertEQ(e.getMessage(), "Error installing " + stubToFail + ": code cache is full");
}
}
}
}

@ -25,7 +25,7 @@
* @test
* @requires vm.jvmci
* @requires vm.simpleArch == "x64" | vm.simpleArch == "aarch64" | vm.simpleArch == "riscv64"
* @library /
* @library /test/lib /
* @modules jdk.internal.vm.ci/jdk.vm.ci.hotspot
* jdk.internal.vm.ci/jdk.vm.ci.meta
* jdk.internal.vm.ci/jdk.vm.ci.code
@ -39,8 +39,10 @@
*/
package jdk.vm.ci.code.test;
import jdk.test.lib.Asserts;
import jdk.vm.ci.code.Register;
import jdk.vm.ci.hotspot.HotSpotNmethod;
import org.junit.Test;
/**
@ -61,6 +63,23 @@ public class SimpleCodeInstallationTest extends CodeInstallationTest {
@Test
public void test() {
test(SimpleCodeInstallationTest::compileAdd, getMethod("add", int.class, int.class), 5, 7);
HotSpotNmethod nmethod = test(SimpleCodeInstallationTest::compileAdd, getMethod("add", int.class, int.class), 5, 7);
// Test code invalidation
Asserts.assertTrue(nmethod.isValid(), "code is not valid, i = " + nmethod);
Asserts.assertTrue(nmethod.isAlive(), "code is not alive, i = " + nmethod);
Asserts.assertNotEquals(nmethod.getStart(), 0L);
// Make nmethod non-entrant but still alive
nmethod.invalidate(false);
Asserts.assertFalse(nmethod.isValid(), "code is valid, i = " + nmethod);
Asserts.assertTrue(nmethod.isAlive(), "code is not alive, i = " + nmethod);
Asserts.assertEquals(nmethod.getStart(), 0L);
// Deoptimize the nmethod and cut the link to it from the HotSpotNmethod
nmethod.invalidate(true);
Asserts.assertFalse(nmethod.isValid(), "code is valid, i = " + nmethod);
Asserts.assertFalse(nmethod.isAlive(), "code is alive, i = " + nmethod);
Asserts.assertEquals(nmethod.getStart(), 0L);
}
}

@ -243,6 +243,27 @@ public abstract class TestAssembler {
this.curStackSlot = initialFrameSize;
}
public class Bookmark implements AutoCloseable {
private final int registerMark = nextRegister;
private final int codePos = code.position();
private final int dataPos = data.position();
@Override
public void close() {
nextRegister = registerMark;
code.data.position(codePos);
data.data.position(dataPos);
}
}
/**
* Enters a scope in which the current register, code and data emitting state
* is restored upon leaving the scope.
*/
public Bookmark bookmark() {
return new Bookmark();
}
public ValueKind<?> getValueKind(JavaKind kind) {
return new TestValueKind(codeCache.getTarget().arch.getPlatformKind(kind));
}
@ -296,6 +317,18 @@ public abstract class TestAssembler {
dataPatches.add(new DataPatch(data.position(), ref));
}
/**
* Emits the 32 bit constant `c` into the data section.
*/
public DataSectionReference emitDataItem(int c) {
DataSectionReference ref = new DataSectionReference();
ref.setOffset(data.position());
recordDataPatchInCode(ref);
data.emitInt(c);
return ref;
}
public DataSectionReference emitDataItem(HotSpotConstant c) {
DataSectionReference ref = new DataSectionReference();
ref.setOffset(data.position());
@ -321,6 +354,50 @@ public abstract class TestAssembler {
finishedDataPatches, false, frameSize, deoptRescue, method, -1, id, 0L, false);
}
/**
* @param n Number of bits that should be set to 1. Must be between 0 and 32 (inclusive).
* @return A number with n bits set to 1.
*/
public static int getNbitNumberInt(int n) {
assert n >= 0 && n <= 32 : "0 <= n <= 32; instead: " + n;
if (n < 32) {
return (1 << n) - 1;
} else {
return 0xFFFFFFFF;
}
}
public static boolean isSignedNbit(int n, int value) {
assert n > 0 && n < 32 : n;
int min = -(1 << (n - 1));
int max = (1 << (n - 1)) - 1;
return value >= min && value <= max;
}
public static boolean isUnsignedNbit(int n, int value) {
assert n > 0 && n < 32 : n;
return 32 - Integer.numberOfLeadingZeros(value) <= n;
}
/**
* Determines if `x` is in the range of signed byte values.
*/
public static boolean isByte(int x) {
return (byte) x == x;
}
/**
* Determines if `l` is in the range of signed int values.
*/
public static boolean isInt(long l) {
return (int) l == l;
}
public static void check(boolean condition, String errorMessage, Object... args) {
if (!condition) {
throw new AssertionError(errorMessage.formatted(args));
}
}
protected static class Buffer {
private ByteBuffer data = ByteBuffer.allocate(32).order(ByteOrder.nativeOrder());

@ -32,6 +32,7 @@ public class TestHotSpotVMConfig extends HotSpotVMConfigAccess {
public TestHotSpotVMConfig(HotSpotVMConfigStore config, Architecture arch) {
super(config);
ropProtection = (arch instanceof AArch64) ? getFieldValue("VM_Version::_rop_protection", Boolean.class) : false;
nmethodEntryBarrierConcurrentPatch = initNmethodEntryBarrierConcurrentPatch(arch);
}
public final boolean useCompressedOops = getFlag("UseCompressedOops", Boolean.class);
@ -47,10 +48,37 @@ public class TestHotSpotVMConfig extends HotSpotVMConfigAccess {
// Checkstyle: stop
public final int MARKID_DEOPT_HANDLER_ENTRY = getConstant("CodeInstaller::DEOPT_HANDLER_ENTRY", Integer.class);
public final int MARKID_FRAME_COMPLETE = getConstant("CodeInstaller::FRAME_COMPLETE", Integer.class);
public final int MARKID_ENTRY_BARRIER_PATCH = getConstant("CodeInstaller::ENTRY_BARRIER_PATCH", Integer.class);
public final long handleDeoptStub = getFieldValue("CompilerToVM::Data::SharedRuntime_deopt_blob_unpack", Long.class, "address");
public final int maxOopMapStackOffset = getFieldValue("CompilerToVM::Data::_max_oop_map_stack_offset", Integer.class, "int");
public final int heapWordSize = getConstant("HeapWordSize", Integer.class);
public final boolean ropProtection;
private Boolean initNmethodEntryBarrierConcurrentPatch(Architecture arch) {
Boolean patchConcurrent = null;
if (arch instanceof AArch64 && nmethodEntryBarrier != 0) {
Integer patchingType = getFieldValue("CompilerToVM::Data::BarrierSetAssembler_nmethod_patching_type", Integer.class, "int");
if (patchingType != null) {
// There currently only 2 variants in use that differ only by the presence of a
// dmb instruction
int stw = getConstant("NMethodPatchingType::stw_instruction_and_data_patch", Integer.class);
int conc = getConstant("NMethodPatchingType::conc_data_patch", Integer.class);
if (patchingType == stw) {
patchConcurrent = false;
} else if (patchingType == conc) {
patchConcurrent = true;
} else {
throw new IllegalArgumentException("unsupported barrier sequence " + patchingType);
}
}
}
return patchConcurrent;
}
public final int threadDisarmedOffset = getFieldValue("CompilerToVM::Data::thread_disarmed_guard_value_offset", Integer.class, "int");
public final long nmethodEntryBarrier = getFieldValue("CompilerToVM::Data::nmethod_entry_barrier", Long.class, "address");
public final Boolean nmethodEntryBarrierConcurrentPatch;
}

@ -46,8 +46,69 @@ import jdk.vm.ci.meta.VMConstant;
public class AArch64TestAssembler extends TestAssembler {
private static final Register scratchRegister = AArch64.rscratch1;
private static final Register scratchRegister2 = AArch64.rscratch2;
private static final Register doubleScratch = AArch64.v9;
/**
* Condition Flags for branches. See C1.2.4
*/
public enum ConditionFlag {
// Integer | Floating-point meanings
/** Equal | Equal. */
EQ(0x0),
/** Not Equal | Not equal or unordered. */
NE(0x1),
/** Unsigned Higher or Same | Greater than, equal or unordered. */
HS(0x2),
/** Unsigned lower | less than. */
LO(0x3),
/** Minus (negative) | less than. */
MI(0x4),
/** Plus (positive or zero) | greater than, equal or unordered. */
PL(0x5),
/** Overflow set | unordered. */
VS(0x6),
/** Overflow clear | ordered. */
VC(0x7),
/** Unsigned higher | greater than or unordered. */
HI(0x8),
/** Unsigned lower or same | less than or equal. */
LS(0x9),
/** Signed greater than or equal | greater than or equal. */
GE(0xA),
/** Signed less than | less than or unordered. */
LT(0xB),
/** Signed greater than | greater than. */
GT(0xC),
/** Signed less than or equal | less than, equal or unordered. */
LE(0xD),
/** Always | always. */
AL(0xE),
/** Always | always (identical to AL, just to have valid 0b1111 encoding). */
NV(0xF);
public final int encoding;
ConditionFlag(int encoding) {
this.encoding = encoding;
}
}
public AArch64TestAssembler(CodeCacheProvider codeCache, TestHotSpotVMConfig config) {
super(codeCache, config,
16 /* initialFrameSize */, 16 /* stackAlignment */,
@ -215,6 +276,22 @@ public class AArch64TestAssembler extends TestAssembler {
| f(0, 4, 0));
}
/**
* C6.2.25 Branch conditionally.
*
* @param condition may not be null.
* @param imm21 Signed 21-bit offset, has to be 4-byte aligned.
*/
protected void emitBranch(ConditionFlag condition, int imm21) {
// B.cond
check(isSignedNbit(21, imm21) && (imm21 & 0b11) == 0,
"0x%x must be a 21-bit signed number and 4-byte aligned", imm21);
int imm19 = (imm21 & getNbitNumberInt(21)) >> 2;
code.emitInt(f(0b001010100, 31, 24)
| f(imm19, 23, 4)
| f(condition.encoding, 3, 0));
}
private void emitFmov(Register Rd, AArch64Kind kind, Register Rn) {
// FMOV (general)
int ftype = 0, sf = 0;
@ -261,9 +338,25 @@ public class AArch64TestAssembler extends TestAssembler {
code.emitInt(0xa9bf7bfd); // stp x29, x30, [sp, #-16]!
code.emitInt(0x910003fd); // mov x29, sp
emitNMethodEntryBarrier();
setDeoptRescueSlot(newStackSlot(AArch64Kind.QWORD));
}
private void emitNMethodEntryBarrier() {
recordMark(config.MARKID_ENTRY_BARRIER_PATCH);
DataSectionReference ref = emitDataItem(0);
emitLoadPointer(scratchRegister, AArch64Kind.DWORD, ref);
if (config.nmethodEntryBarrierConcurrentPatch) {
code.emitInt(0xd50339bf); // dmb ishld
}
Register thread = AArch64.r28;
emitLoadPointer(scratchRegister2, AArch64Kind.DWORD, thread, config.threadDisarmedOffset);
code.emitInt(0x6b09011f); // cmp w8, w9
emitBranch(ConditionFlag.EQ, 8); // jump over slow path, runtime call
emitCall(config.nmethodEntryBarrier);
}
@Override
public void emitEpilogue() {
recordMark(config.MARKID_DEOPT_HANDLER_ENTRY);
@ -361,8 +454,11 @@ public class AArch64TestAssembler extends TestAssembler {
@Override
public Register emitLoadPointer(Register b, int offset) {
Register ret = newRegister();
emitLoadRegister(ret, AArch64Kind.QWORD, b, offset);
return emitLoadPointer(newRegister(), AArch64Kind.QWORD, b, offset);
}
public Register emitLoadPointer(Register ret, AArch64Kind kind, Register b, int offset) {
emitLoadRegister(ret, kind, b, offset);
return ret;
}
@ -377,10 +473,13 @@ public class AArch64TestAssembler extends TestAssembler {
@Override
public Register emitLoadPointer(DataSectionReference ref) {
return emitLoadPointer(newRegister(), AArch64Kind.QWORD, ref);
}
public Register emitLoadPointer(Register ret, AArch64Kind kind, DataSectionReference ref) {
recordDataPatchInCode(ref);
Register ret = newRegister();
emitLoadRegister(ret, AArch64Kind.QWORD, 0xdead);
emitLoadRegister(ret, kind, 0xdead);
return ret;
}

@ -49,6 +49,40 @@ public class AMD64TestAssembler extends TestAssembler {
private static final Register scratchRegister = AMD64.r12;
private static final Register doubleScratch = AMD64.xmm15;
/**
* The x86 condition codes used for conditional jumps/moves.
*/
public enum ConditionFlag {
Zero(0x4, "|zero|"),
NotZero(0x5, "|nzero|"),
Equal(0x4, "="),
NotEqual(0x5, "!="),
Less(0xc, "<"),
LessEqual(0xe, "<="),
Greater(0xf, ">"),
GreaterEqual(0xd, ">="),
Below(0x2, "|<|"),
BelowEqual(0x6, "|<=|"),
Above(0x7, "|>|"),
AboveEqual(0x3, "|>=|"),
Overflow(0x0, "|of|"),
NoOverflow(0x1, "|nof|"),
CarrySet(0x2, "|carry|"),
CarryClear(0x3, "|ncarry|"),
Negative(0x8, "|neg|"),
Positive(0x9, "|pos|"),
Parity(0xa, "|par|"),
NoParity(0xb, "|npar|");
public final int value;
public final String operator;
ConditionFlag(int value, String operator) {
this.value = value;
this.operator = operator;
}
}
public AMD64TestAssembler(CodeCacheProvider codeCache, TestHotSpotVMConfig config) {
super(codeCache, config, 16, 16, AMD64Kind.DWORD, AMD64.rax, AMD64.rcx, AMD64.rdi, AMD64.r8, AMD64.r9, AMD64.r10);
}
@ -63,6 +97,62 @@ public class AMD64TestAssembler extends TestAssembler {
code.emitByte(0x00);
}
/**
* Emit the expected patchable code sequence for the nmethod entry barrier. The int sized
* payload must be naturally aligned so it can be patched atomically.
*/
private void emitNMethodEntryCompare(int displacement) {
// cmp dword ptr [r15 + <displacement>], 0x00000000
// 41 81 7f <db> 00 00 00 00
code.emitByte(0x41);
code.emitByte(0x81);
code.emitByte(0x7f);
check(isByte(displacement), "expected byte sized displacement: 0x%x", displacement);
code.emitByte(displacement & 0xff);
check(code.position() % 4 == 0, "must be aligned");
code.emitInt(0);
}
/**
* Emits a long (i.e. 6 byte) format conditional branch.
*
* @param offset the offset of the branch target wrt the start of the branch instruction
*/
private void emitBranch(ConditionFlag condition, int offset) {
final int longSize = 6;
int disp32 = offset - longSize;
// 0000 1111 1000 tttn #32-bit disp
check(isInt(disp32), "must be 32bit disp: %d", disp32);
code.emitByte(0x0F);
code.emitByte(0x80 | condition.value);
code.emitInt(disp32);
}
public void emitAlign(int modulus) {
while (code.position() % modulus != 0) {
code.emitByte(0x90);
}
}
private void emitNMethodEntryBarrier() {
// The following code sequence must be emitted in exactly this fashion as HotSpot
// will check that the barrier is the expected code sequence.
emitAlign(4);
recordMark(config.MARKID_FRAME_COMPLETE);
recordMark(config.MARKID_ENTRY_BARRIER_PATCH);
emitNMethodEntryCompare(config.threadDisarmedOffset);
int branchOffset;
try (Bookmark bm = bookmark()) {
int pos = code.position();
emitBranch(ConditionFlag.Equal, 0);
emitCall(config.nmethodEntryBarrier);
branchOffset = code.position() - pos;
}
emitBranch(ConditionFlag.Equal, branchOffset);
emitCall(config.nmethodEntryBarrier);
}
@Override
public void emitPrologue() {
// WARNING: Initial instruction MUST be 5 bytes or longer so that
@ -71,6 +161,7 @@ public class AMD64TestAssembler extends TestAssembler {
emitFatNop();
code.emitByte(0x50 | AMD64.rbp.encoding); // PUSH rbp
emitMove(true, AMD64.rbp, AMD64.rsp); // MOV rbp, rsp
emitNMethodEntryBarrier();
setDeoptRescueSlot(newStackSlot(AMD64Kind.QWORD));
}
@ -414,7 +505,7 @@ public class AMD64TestAssembler extends TestAssembler {
@Override
public void emitCall(long addr) {
Register target = emitLoadLong(addr);
Register target = emitLoadLong(AMD64.rax, addr);
code.emitByte(0xFF); // CALL r/m64
int enc = target.encoding;
if (enc >= 8) {