package org.graalvm.compiler.replacements.test;
import java.util.Objects;
import org.graalvm.compiler.api.directives.GraalDirectives;
import org.graalvm.compiler.debug.DebugContext;
import org.graalvm.compiler.debug.DebugContext.Scope;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration;
import org.graalvm.compiler.replacements.Snippets;
import org.graalvm.compiler.replacements.classfile.ClassfileBytecodeProvider;
import org.graalvm.compiler.word.Word;
import org.graalvm.compiler.word.WordCastNode;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import jdk.vm.ci.meta.ResolvedJavaMethod;
public class DerivedOopTest extends ReplacementsTest implements Snippets {
private static class Pointers {
public long basePointer;
public long internalPointer;
public long delta() {
return internalPointer - basePointer;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Pointers)) {
return false;
}
Pointers other = (Pointers) obj;
return this.delta() == other.delta();
}
@Override
public int hashCode() {
return (int) delta();
}
}
private static class Result {
public Pointers beforeGC;
public Pointers afterGC;
Result() {
beforeGC = new Pointers();
afterGC = new Pointers();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((afterGC == null) ? 0 : afterGC.hashCode());
result = prime * result + ((beforeGC == null) ? 0 : beforeGC.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Result)) {
return false;
}
Result other = (Result) obj;
return Objects.equals(this.beforeGC, other.beforeGC) && Objects.equals(this.afterGC, other.afterGC);
}
}
@Test
public void testFieldOffset() {
for (int i = 0; i < 4; i++) {
Result r = new Result();
test("fieldOffsetSnippet", r, 16L);
Assert.assertEquals(r.beforeGC.delta(), r.afterGC.delta());
}
}
static long getRawPointer(Object obj) {
return obj.hashCode();
}
static long getRawPointerIntrinsic(Object obj) {
return Word.objectToTrackedPointer(obj).rawValue();
}
public static Result fieldOffsetSnippet(Result obj, long offset) {
long internalPointer = getRawPointer(obj) + offset;
GraalDirectives.blackhole(internalPointer);
obj.beforeGC.basePointer = getRawPointer(obj);
obj.beforeGC.internalPointer = internalPointer;
System.gc();
obj.afterGC.basePointer = getRawPointer(obj);
obj.afterGC.internalPointer = internalPointer;
return obj;
}
@Rule public final ExpectedException thrown = ExpectedException.none();
private static final String UNKNOWN_REFERENCE_AT_SAFEPOINT_MSG = "should not reach here: unknown reference alive across safepoint";
@Test
@SuppressWarnings("try")
public void testFieldOffsetMergeNonLiveBasePointer() {
thrown.expect(GraalError.class);
thrown.expectMessage(UNKNOWN_REFERENCE_AT_SAFEPOINT_MSG);
DebugContext debug = getDebugContext();
try (Scope s = debug.disable()) {
for (int i = 0; i < 4; i++) {
Result r = new Result();
test("fieldOffsetMergeSnippet01", r, 8L, 16L);
Assert.assertEquals(r.beforeGC.delta(), r.afterGC.delta());
}
}
}
@Test
public void testFieldOffsetMergeNonLiveBasePointerNotAccrossSafepoint() {
for (int i = 0; i < 4; i++) {
Result r = new Result();
test("fieldOffsetMergeSnippet02", r, 8L, 16L);
}
}
@Test
@SuppressWarnings("try")
public void testFieldOffsetMergeLiveBasePointer() {
thrown.expect(GraalError.class);
thrown.expectMessage(UNKNOWN_REFERENCE_AT_SAFEPOINT_MSG);
DebugContext debug = getDebugContext();
try (Scope s = debug.disable()) {
for (int i = 0; i < 4; i++) {
Result r = new Result();
test("fieldOffsetMergeSnippet03", r, new Result(), new Result(), 8L, 16L);
Assert.assertEquals(r.beforeGC.delta(), r.afterGC.delta());
}
}
}
public static boolean SideEffectB;
public static long SideEffect1 = 16;
public static long SideEffect2 = 16;
public static Object o1 = new Result();
public static Object o2 = o1;
public static Result fieldOffsetMergeSnippet01(Result objResult, long offsetA, long offsetB) {
long internalPointer;
if (SideEffectB) {
internalPointer = getRawPointer(o1) + offsetA;
SideEffect1 = internalPointer;
} else {
internalPointer = getRawPointer(o2) + offsetB;
SideEffect2 = internalPointer;
}
GraalDirectives.controlFlowAnchor();
GraalDirectives.blackhole(internalPointer);
objResult.beforeGC.basePointer = getRawPointer(objResult);
objResult.beforeGC.internalPointer = internalPointer;
System.gc();
objResult.afterGC.basePointer = getRawPointer(objResult);
objResult.afterGC.internalPointer = internalPointer;
return objResult;
}
public static Result fieldOffsetMergeSnippet02(Result objResult, long offsetA, long offsetB) {
long internalPointer;
if (SideEffectB) {
internalPointer = getRawPointer(o1) + offsetA;
SideEffect1 = internalPointer;
} else {
internalPointer = getRawPointer(o2) + offsetB;
SideEffect2 = internalPointer;
}
GraalDirectives.controlFlowAnchor();
GraalDirectives.blackhole(internalPointer);
objResult.beforeGC.basePointer = getRawPointer(objResult);
objResult.beforeGC.internalPointer = internalPointer;
objResult.afterGC.basePointer = getRawPointer(objResult);
objResult.afterGC.internalPointer = internalPointer;
return objResult;
}
public static Result fieldOffsetMergeSnippet03(Result objResult, Result a, Result b, long offsetA, long offsetB) {
long internalPointer;
if (SideEffectB) {
internalPointer = getRawPointer(a) + offsetA;
SideEffect1 = internalPointer;
} else {
internalPointer = getRawPointer(b) + offsetB;
SideEffect2 = internalPointer;
}
GraalDirectives.controlFlowAnchor();
GraalDirectives.blackhole(internalPointer);
objResult.beforeGC.basePointer = getRawPointer(objResult);
objResult.beforeGC.internalPointer = internalPointer;
System.gc();
objResult.afterGC.basePointer = getRawPointer(objResult);
objResult.afterGC.internalPointer = internalPointer;
return objResult;
}
@Override
protected void registerInvocationPlugins(InvocationPlugins invocationPlugins) {
Registration r = new Registration(invocationPlugins, DerivedOopTest.class);
ClassfileBytecodeProvider bytecodeProvider = getSystemClassLoaderBytecodeProvider();
ResolvedJavaMethod intrinsic = getResolvedJavaMethod("getRawPointerIntrinsic");
r.register1("getRawPointer", Object.class, new InvocationPlugin() {
@Override
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
return b.intrinsify(bytecodeProvider, targetMethod, intrinsic, receiver, new ValueNode[]{arg});
}
});
super.registerInvocationPlugins(invocationPlugins);
}
@Override
protected boolean checkHighTierGraph(StructuredGraph graph) {
assert graph.getNodes().filter(WordCastNode.class).count() > 0 : "DerivedOopTest.toLong should be intrinsified";
return super.checkHighTierGraph(graph);
}
}