Skip to content
Open
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
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2026, 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
Expand Down Expand Up @@ -30,6 +30,7 @@
import jdk.graal.compiler.truffle.test.CachedLibraryCompilationTestFactory.GuardNodeGen;
import jdk.graal.compiler.truffle.test.CachedLibraryCompilationTestFactory.NoGuardNodeGen;
import jdk.graal.compiler.truffle.test.CachedLibraryCompilationTestFactory.VarArgsLibraryNodeGen;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import org.graalvm.polyglot.Context;
import org.junit.After;
import org.junit.Assert;
Expand All @@ -42,6 +43,8 @@
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.LibraryFactory;
import com.oracle.truffle.api.nodes.EncapsulatingNodeReference;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;

Expand All @@ -54,7 +57,7 @@ public class CachedLibraryCompilationTest extends PartialEvaluationTest {
public void setup() {
cleanup();
context = Context.newBuilder().allowExperimentalOptions(true).option("engine.CompilationFailureAction", "Throw").option("engine.BackgroundCompilation", "false").option(
"compiler.TreatPerformanceWarningsAsErrors", "all").build();
"compiler.TreatPerformanceWarningsAsErrors", "all").option("compiler.InliningPolicy", "Default").build();
context.enter();
}

Expand All @@ -66,6 +69,47 @@ public void tearDown() {
}
}

/*
* Depending on runtime/compiler initialization order, the uncached interop path may retain one
* helper-only call in the PE graph. Count the semantically relevant dispatch/boundary invokes
* and ignore that optional helper call.
*/
private static long countRelevantMethodCalls(StructuredGraph graph) {
long optionalHelperCalls = 0;
long relevantCalls = 0;
for (MethodCallTargetNode callTargetNode : graph.getNodes(MethodCallTargetNode.TYPE)) {
if (isOptionalHelperCall(callTargetNode)) {
optionalHelperCalls++;
} else {
relevantCalls++;
}
}
Assert.assertTrue("Unexpected optional helper calls: " + describeMethodCalls(graph), optionalHelperCalls <= 1);
return relevantCalls;
}

private static boolean isOptionalHelperCall(MethodCallTargetNode callTargetNode) {
ResolvedJavaMethod targetMethod = callTargetNode.targetMethod();
String declaringClassName = targetMethod.getDeclaringClass().toJavaName();
String methodName = targetMethod.getName();
return declaringClassName.equals(EncapsulatingNodeReference.class.getName()) && methodName.equals("getThreadLocal") ||
declaringClassName.equals(LibraryFactory.class.getName()) && methodName.equals("initializeUncached");
}

private static String describeMethodCalls(StructuredGraph graph) {
StringBuilder builder = new StringBuilder();
boolean first = true;
for (MethodCallTargetNode callTargetNode : graph.getNodes(MethodCallTargetNode.TYPE)) {
if (!first) {
builder.append(", ");
}
ResolvedJavaMethod targetMethod = callTargetNode.targetMethod();
builder.append(targetMethod.getDeclaringClass().toJavaName()).append('#').append(targetMethod.getName());
first = false;
}
return builder.toString();
}

abstract static class GuardNode extends Node {

static int LIMIT = 0;
Expand Down Expand Up @@ -95,7 +139,7 @@ public Object execute(VirtualFrame frame) {
return null;
}
});
Assert.assertEquals(3, graph.getNodes(MethodCallTargetNode.TYPE).count());
Assert.assertTrue(describeMethodCalls(graph), countRelevantMethodCalls(graph) == 2);
}

abstract static class NoGuardNode extends Node {
Expand Down Expand Up @@ -128,7 +172,7 @@ public Object execute(VirtualFrame frame) {
};

StructuredGraph graph = partialEval(testRoot);
Assert.assertEquals(1, graph.getNodes(MethodCallTargetNode.TYPE).count());
Assert.assertTrue(describeMethodCalls(graph), countRelevantMethodCalls(graph) == 1);
}

abstract static class FrameNode extends Node {
Expand Down Expand Up @@ -162,7 +206,7 @@ public Object execute(VirtualFrame frame) {
};

StructuredGraph graph = partialEval(testRoot);
Assert.assertEquals(1, graph.getNodes(MethodCallTargetNode.TYPE).count());
Assert.assertTrue(describeMethodCalls(graph), countRelevantMethodCalls(graph) == 1);
}

@Test
Expand All @@ -177,7 +221,7 @@ public Object execute(VirtualFrame frame) {
};

StructuredGraph graph = partialEval(testRoot);
Assert.assertEquals(4, graph.getNodes(MethodCallTargetNode.TYPE).count());
Assert.assertTrue(describeMethodCalls(graph), countRelevantMethodCalls(graph) == 3);
}

abstract static class VarArgsLibraryNode extends Node {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
package com.oracle.truffle.api.library.test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;

import org.junit.Test;
Expand Down Expand Up @@ -78,6 +79,7 @@
import com.oracle.truffle.api.library.test.CachedLibraryTestFactory.ReplaceCachedLibraryTestNodeGen;
import com.oracle.truffle.api.library.test.CachedLibraryTestFactory.SimpleDispatchedNodeGen;
import com.oracle.truffle.api.library.test.CachedLibraryTestFactory.SimpleNodeGen;
import com.oracle.truffle.api.library.test.CachedLibraryTestFactory.TestBoundaryAndVirtualFrame3NodeGen;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.test.AbstractLibraryTest;
import com.oracle.truffle.api.test.ExpectError;
Expand Down Expand Up @@ -684,6 +686,26 @@ String doDefault(Object arg, @CachedLibrary("arg") SomethingLibrary interop) {
}
}

@Test
public void testBoundaryAndVirtualFrameNull() {
var node = adopt(TestBoundaryAndVirtualFrame3NodeGen.create());
assertNull(node.execute(null, new Something()));
assertNull(node.execute(null, new Something()));
assertNull(node.execute(null, new Something()));
assertNull(node.execute(null, new Something()));
}

public abstract static class TestBoundaryAndVirtualFrame3 extends Node {

public abstract Object execute(VirtualFrame frame, Object arg);

@SuppressWarnings("unused")
@Specialization(limit = "1")
String doDefault(VirtualFrame frame, Object arg, @CachedLibrary("arg") SomethingLibrary interop) {
return null;
}
}

public abstract static class TestBoundaryAndFrame extends Node {

public abstract Object execute(Frame frame, Object arg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,11 @@ public SpecializationData getUncachedSpecialization() {
}

public boolean hasFrameParameter() {
for (Parameter p : getSignatureParameters()) {
/*
* Frames are optional DSL parameters and therefore are not part of the signature parameter
* list.
*/
for (Parameter p : getParameters()) {
if (ElementUtils.typeEquals(p.getType(), types.VirtualFrame) || ElementUtils.typeEquals(p.getType(), types.Frame)) {
return true;
}
Expand Down