package org.graalvm.compiler.hotspot.test;
import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;
import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.STACK;
import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED;
import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED;
import java.util.function.Consumer;
import org.graalvm.compiler.code.CompilationResult;
import org.graalvm.compiler.core.common.LIRKind;
import org.graalvm.compiler.core.common.type.StampFactory;
import org.graalvm.compiler.core.test.GraalCompilerTest;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.DebugContext.Scope;
import org.graalvm.compiler.debug.DebugHandlersFactory;
import org.graalvm.compiler.graph.NodeClass;
import org.graalvm.compiler.hotspot.HotSpotCompiledCodeBuilder;
import org.graalvm.compiler.lir.FullInfopointOp;
import org.graalvm.compiler.lir.LIRFrameState;
import org.graalvm.compiler.lir.LIRInstruction;
import org.graalvm.compiler.lir.LIRInstructionClass;
import org.graalvm.compiler.lir.Variable;
import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
import org.graalvm.compiler.nodeinfo.NodeInfo;
import org.graalvm.compiler.nodes.DeoptimizingFixedWithNextNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.spi.LIRLowerable;
import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
import org.junit.Test;
import jdk.vm.ci.code.BytecodeFrame;
import jdk.vm.ci.code.CodeCacheProvider;
import jdk.vm.ci.code.VirtualObject;
import jdk.vm.ci.code.site.InfopointReason;
import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.hotspot.HotSpotCompiledCode;
import jdk.vm.ci.meta.AllocatableValue;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.JavaValue;
import jdk.vm.ci.meta.PlatformKind;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.meta.Value;
public class JVMCIInfopointErrorTest extends GraalCompilerTest {
private static class ValueDef extends LIRInstruction {
private static final LIRInstructionClass<ValueDef> TYPE = LIRInstructionClass.create(ValueDef.class);
@Def({REG, STACK}) AllocatableValue value;
ValueDef(AllocatableValue value) {
super(TYPE);
this.value = value;
}
@Override
public void emitCode(CompilationResultBuilder crb) {
}
}
private static class ValueUse extends LIRInstruction {
private static final LIRInstructionClass<ValueUse> TYPE = LIRInstructionClass.create(ValueUse.class);
@Use({REG, STACK}) AllocatableValue value;
ValueUse(AllocatableValue value) {
super(TYPE);
this.value = value;
}
@Override
public void emitCode(CompilationResultBuilder crb) {
}
}
@NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED)
private static class TestNode extends DeoptimizingFixedWithNextNode implements LIRLowerable {
private static final NodeClass<TestNode> TYPE = NodeClass.create(TestNode.class);
private final TestSpec spec;
protected TestNode(TestSpec spec) {
super(TYPE, StampFactory.forVoid());
this.spec = spec;
}
@Override
public boolean canDeoptimize() {
return true;
}
@Override
public void generate(NodeLIRBuilderTool gen) {
LIRGeneratorTool tool = gen.getLIRGeneratorTool();
LIRFrameState state = gen.state(this);
spec.spec(tool, state, st -> {
tool.append(new FullInfopointOp(st, InfopointReason.SAFEPOINT));
});
}
}
@FunctionalInterface
private interface TestSpec {
void spec(LIRGeneratorTool tool, LIRFrameState state, Consumer<LIRFrameState> safepoint);
}
public static void testMethod() {
}
private void test(TestSpec spec) {
test(getDebugContext(), spec);
}
private void test(DebugContext debug, TestSpec spec) {
ResolvedJavaMethod method = getResolvedJavaMethod("testMethod");
StructuredGraph graph = parseForCompile(method, debug);
TestNode test = graph.add(new TestNode(spec));
graph.addAfterFixed(graph.start(), test);
CompilationResult compResult = compile(method, graph);
CodeCacheProvider codeCache = getCodeCache();
HotSpotCompiledCode compiledCode = HotSpotCompiledCodeBuilder.createCompiledCode(codeCache, method, null, compResult, getInitialOptions());
codeCache.addCode(method, compiledCode, null, null);
}
@Test(expected = Error.class)
public void testInvalidShortOop() {
test((tool, state, safepoint) -> {
PlatformKind kind = tool.target().arch.getPlatformKind(JavaKind.Short);
LIRKind lirKind = LIRKind.reference(kind);
Variable var = tool.newVariable(lirKind);
tool.append(new ValueDef(var));
safepoint.accept(state);
tool.append(new ValueUse(var));
});
}
@Test(expected = Error.class)
public void testInvalidShortDerivedOop() {
test((tool, state, safepoint) -> {
Variable baseOop = tool.newVariable(LIRKind.fromJavaKind(tool.target().arch, JavaKind.Object));
tool.append(new ValueDef(baseOop));
PlatformKind kind = tool.target().arch.getPlatformKind(JavaKind.Short);
LIRKind lirKind = LIRKind.derivedReference(kind, baseOop, false);
Variable var = tool.newVariable(lirKind);
tool.append(new ValueDef(var));
safepoint.accept(state);
tool.append(new ValueUse(var));
});
}
private static LIRFrameState modifyTopFrame(LIRFrameState state, JavaValue[] values, JavaKind[] slotKinds, int locals, int stack, int locks) {
return modifyTopFrame(state, null, values, slotKinds, locals, stack, locks);
}
private static LIRFrameState modifyTopFrame(LIRFrameState state, VirtualObject[] vobj, JavaValue[] values, JavaKind[] slotKinds, int locals, int stack, int locks) {
BytecodeFrame top = state.topFrame;
top = new BytecodeFrame(top.caller(), top.getMethod(), top.getBCI(), top.rethrowException, top.duringCall, values, slotKinds, locals, stack, locks);
return new LIRFrameState(top, vobj, state.exceptionEdge);
}
@Test(expected = JVMCIError.class)
public void testUnexpectedScopeValuesLength() {
test((tool, state, safepoint) -> {
LIRFrameState newState = modifyTopFrame(state, new JavaValue[]{JavaConstant.FALSE}, new JavaKind[0], 0, 0, 0);
safepoint.accept(newState);
});
}
@Test(expected = JVMCIError.class)
public void testUnexpectedScopeSlotKindsLength() {
test((tool, state, safepoint) -> {
LIRFrameState newState = modifyTopFrame(state, new JavaValue[0], new JavaKind[]{JavaKind.Boolean}, 0, 0, 0);
safepoint.accept(newState);
});
}
@Test(expected = JVMCIError.class)
public void testWrongMonitorType() {
test((tool, state, safepoint) -> {
LIRFrameState newState = modifyTopFrame(state, new JavaValue[]{JavaConstant.INT_0}, new JavaKind[]{}, 0, 0, 1);
safepoint.accept(newState);
});
}
@Test(expected = JVMCIError.class)
public void testUnexpectedIllegalValue() {
test((tool, state, safepoint) -> {
LIRFrameState newState = modifyTopFrame(state, new JavaValue[]{Value.ILLEGAL}, new JavaKind[]{JavaKind.Int}, 1, 0, 0);
safepoint.accept(newState);
});
}
@Test(expected = JVMCIError.class)
public void testUnexpectedTypeInRegister() {
test((tool, state, safepoint) -> {
Variable var = tool.newVariable(LIRKind.fromJavaKind(tool.target().arch, JavaKind.Int));
tool.append(new ValueDef(var));
LIRFrameState newState = modifyTopFrame(state, new JavaValue[]{var}, new JavaKind[]{JavaKind.Illegal}, 1, 0, 0);
safepoint.accept(newState);
});
}
@Test(expected = JVMCIError.class)
public void testWrongConstantType() {
test((tool, state, safepoint) -> {
LIRFrameState newState = modifyTopFrame(state, new JavaValue[]{JavaConstant.INT_0}, new JavaKind[]{JavaKind.Object}, 1, 0, 0);
safepoint.accept(newState);
});
}
@Test(expected = JVMCIError.class)
public void testUnsupportedConstantType() {
test((tool, state, safepoint) -> {
LIRFrameState newState = modifyTopFrame(state, new JavaValue[]{JavaConstant.forShort((short) 0)}, new JavaKind[]{JavaKind.Short}, 1, 0, 0);
safepoint.accept(newState);
});
}
@Test(expected = JVMCIError.class)
public void testUnexpectedNull() {
test((tool, state, safepoint) -> {
LIRFrameState newState = modifyTopFrame(state, new JavaValue[]{JavaConstant.NULL_POINTER}, new JavaKind[]{JavaKind.Int}, 1, 0, 0);
safepoint.accept(newState);
});
}
@Test(expected = JVMCIError.class)
public void testUnexpectedObject() {
JavaValue wrapped = getSnippetReflection().forObject(this);
test((tool, state, safepoint) -> {
LIRFrameState newState = modifyTopFrame(state, new JavaValue[]{wrapped}, new JavaKind[]{JavaKind.Int}, 1, 0, 0);
safepoint.accept(newState);
});
}
private static class UnknownJavaValue implements JavaValue {
}
@SuppressWarnings("try")
@Test(expected = Error.class)
public void testUnknownJavaValue() {
DebugContext debug = DebugContext.create(getInitialOptions(), DebugHandlersFactory.LOADER);
try (Scope s = debug.disable()) {
test(debug, (tool, state, safepoint) -> {
LIRFrameState newState = modifyTopFrame(state, new JavaValue[]{new UnknownJavaValue()}, new JavaKind[]{JavaKind.Int}, 1, 0, 0);
safepoint.accept(newState);
});
}
}
@Test(expected = Error.class)
public void testMissingIllegalAfterDouble() {
test((tool, state, safepoint) -> {
LIRFrameState newState = modifyTopFrame(state, new JavaValue[]{JavaConstant.DOUBLE_0, JavaConstant.INT_0}, new JavaKind[]{JavaKind.Double, JavaKind.Int}, 2, 0, 0);
safepoint.accept(newState);
});
}
@Test(expected = JVMCIError.class)
public void testInvalidVirtualObjectId() {
ResolvedJavaType obj = getMetaAccess().lookupJavaType(Object.class);
test((tool, state, safepoint) -> {
VirtualObject o = VirtualObject.get(obj, 5);
o.setValues(new JavaValue[0], new JavaKind[0]);
safepoint.accept(new LIRFrameState(state.topFrame, new VirtualObject[]{o}, state.exceptionEdge));
});
}
@Test(expected = JVMCIError.class)
public void testDuplicateVirtualObject() {
ResolvedJavaType obj = getMetaAccess().lookupJavaType(Object.class);
test((tool, state, safepoint) -> {
VirtualObject o1 = VirtualObject.get(obj, 0);
o1.setValues(new JavaValue[0], new JavaKind[0]);
VirtualObject o2 = VirtualObject.get(obj, 0);
o2.setValues(new JavaValue[0], new JavaKind[0]);
safepoint.accept(new LIRFrameState(state.topFrame, new VirtualObject[]{o1, o2}, state.exceptionEdge));
});
}
@Test(expected = JVMCIError.class)
public void testUnexpectedVirtualObject() {
ResolvedJavaType obj = getMetaAccess().lookupJavaType(Object.class);
test((tool, state, safepoint) -> {
VirtualObject o = VirtualObject.get(obj, 0);
o.setValues(new JavaValue[0], new JavaKind[0]);
LIRFrameState newState = modifyTopFrame(state, new VirtualObject[]{o}, new JavaValue[]{o}, new JavaKind[]{JavaKind.Int}, 1, 0, 0);
safepoint.accept(newState);
});
}
@Test(expected = JVMCIError.class)
public void testUndefinedVirtualObject() {
ResolvedJavaType obj = getMetaAccess().lookupJavaType(Object.class);
test((tool, state, safepoint) -> {
VirtualObject o0 = VirtualObject.get(obj, 0);
o0.setValues(new JavaValue[0], new JavaKind[0]);
VirtualObject o1 = VirtualObject.get(obj, 1);
o1.setValues(new JavaValue[0], new JavaKind[0]);
LIRFrameState newState = modifyTopFrame(state, new VirtualObject[]{o0}, new JavaValue[]{o1}, new JavaKind[]{JavaKind.Object}, 1, 0, 0);
safepoint.accept(newState);
});
}
}