Skip to content

Commit 2105987

Browse files
committed
Ensure invocation plugin has the correct return type.
1 parent 39d29e6 commit 2105987

File tree

3 files changed

+49
-15
lines changed

3 files changed

+49
-15
lines changed

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/BytecodeParser.java

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,6 @@
238238
import static jdk.graal.compiler.core.common.GraalOptions.StrictDeoptInsertionChecks;
239239
import static jdk.graal.compiler.core.common.GraalOptions.TraceInlining;
240240
import static jdk.graal.compiler.core.common.type.StampFactory.objectNonNull;
241-
import static jdk.graal.compiler.debug.GraalError.guarantee;
242241
import static jdk.graal.compiler.debug.GraalError.shouldNotReachHereUnexpectedValue;
243242
import static jdk.graal.compiler.java.BytecodeParserOptions.InlinePartialIntrinsicExitDuringParsing;
244243
import static jdk.graal.compiler.java.BytecodeParserOptions.TraceBytecodeParserLevel;
@@ -2313,10 +2312,10 @@ static String pluginErrorMessage(InvocationPlugin plugin, String format, Object.
23132312
}
23142313

23152314
/**
2316-
* Contains all the assertion checking logic around the application of an
2317-
* {@link InvocationPlugin}. This class is only loaded when assertions are enabled.
2315+
* Checks stack and nodes remain consistent before and after the application of an
2316+
* {@link InvocationPlugin}.
23182317
*/
2319-
class InvocationPluginAssertions {
2318+
class InvocationPluginChecks {
23202319
final InvocationPlugin plugin;
23212320
final ValueNode[] args;
23222321
final ResolvedJavaMethod targetMethod;
@@ -2325,8 +2324,7 @@ class InvocationPluginAssertions {
23252324
final int nodeCount;
23262325
final Mark mark;
23272326

2328-
InvocationPluginAssertions(InvocationPlugin plugin, ValueNode[] args, ResolvedJavaMethod targetMethod, JavaKind resultType) {
2329-
guarantee(Assertions.assertionsEnabled(), "%s should only be loaded and instantiated if assertions are enabled", getClass().getSimpleName());
2327+
InvocationPluginChecks(InvocationPlugin plugin, ValueNode[] args, ResolvedJavaMethod targetMethod, JavaKind resultType) {
23302328
this.plugin = plugin;
23312329
this.targetMethod = targetMethod;
23322330
this.args = args;
@@ -2340,17 +2338,31 @@ String error(String format, Object... a) {
23402338
return pluginErrorMessage(plugin, format, a);
23412339
}
23422340

2343-
boolean check(boolean pluginResult) {
2341+
boolean checkStackConsistency(boolean pluginResult) {
23442342
if (pluginResult) {
2343+
int expectedStackSize = beforeStackSize + resultType.getSlotCount();
2344+
JavaKind peekKind = frameState.peekKind();
2345+
23452346
/*
23462347
* If lastInstr is null, even if this method has a non-void return type, the method
23472348
* doesn't return a value, it probably throws an exception.
2349+
*
2350+
* We also generate various invocation plugins that return VM specific data
2351+
* structure types, e.g., KlassPointer and Word. The former will be treated as
2352+
* JavaKind.Illegal and the latter as JavaKind.Long. These plugins will break the
2353+
* result type check, and can be filtered out with InvocationPlugin.isGraalOnly().
23482354
*/
2349-
int expectedStackSize = beforeStackSize + resultType.getSlotCount();
2350-
assert lastInstr == null || plugin.isDecorator() || expectedStackSize == frameState.stackSize() : error("plugin manipulated the stack incorrectly: expected=%d, actual=%d",
2351-
expectedStackSize,
2352-
frameState.stackSize());
2355+
if (lastInstr != null && !plugin.isDecorator() && (expectedStackSize != frameState.stackSize() ||
2356+
(!plugin.isGraalOnly() && resultType != JavaKind.Void && resultType.getStackKind() != peekKind.getStackKind()))) {
2357+
throw new GraalError(error("plugin manipulated the stack incorrectly: expected=%d, actual=%d, resultType=%s stackKind=%s",
2358+
expectedStackSize, frameState.stackSize(), resultType.getJavaName(), peekKind.getJavaName()));
2359+
}
2360+
}
2361+
return true;
2362+
}
23532363

2364+
boolean checkNodeConsistency(boolean pluginResult) {
2365+
if (pluginResult) {
23542366
NodeIterable<Node> newNodes = graph.getNewNodes(mark);
23552367
for (Node n : newNodes) {
23562368
if (n instanceof StateSplit) {
@@ -2473,7 +2485,7 @@ protected boolean applyInvocationPlugin(InvokeKind invokeKind, ValueNode[] args,
24732485
InvocationPluginReceiver pluginReceiver = invocationPluginReceiver.init(targetMethod, args);
24742486
assert invokeKind.isDirect() : "Cannot apply invocation plugin on an indirect call site.";
24752487

2476-
InvocationPluginAssertions assertions = Assertions.assertionsEnabled() ? new InvocationPluginAssertions(plugin, args, targetMethod, resultType) : null;
2488+
InvocationPluginChecks checks = new InvocationPluginChecks(plugin, args, targetMethod, resultType);
24772489
boolean needsReceiverNullCheck = !(plugin instanceof GeneratedInvocationPlugin) && !targetMethod.isStatic() && args[0].getStackKind() == JavaKind.Object;
24782490
try (DebugCloseable context = openNodeContext(targetMethod); InvocationPluginScope pluginScope = new InvocationPluginScope(invokeKind, args, targetMethod, resultType, plugin)) {
24792491
Mark mark = graph.getMark();
@@ -2485,10 +2497,14 @@ protected boolean applyInvocationPlugin(InvokeKind invokeKind, ValueNode[] args,
24852497
targetMethod.format("%H.%n(%p)"),
24862498
args[0]));
24872499
}
2488-
assert assertions.check(true);
2500+
// Check stack consistency even without assert, to prevent silent invalid
2501+
// intrinsification. This may happen when JDK changes the return type of
2502+
// an intrinsic candidate.
2503+
checks.checkStackConsistency(true);
2504+
assert checks.checkNodeConsistency(true);
24892505
return true;
24902506
} else {
2491-
assert assertions.check(false);
2507+
assert checks.checkNodeConsistency(false);
24922508
}
24932509
}
24942510
return false;

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/java/FrameStateBuilder.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,17 @@ public ValueNode peekObject() {
958958
return x;
959959
}
960960

961+
public JavaKind peekKind() {
962+
if (stackSize == 0) {
963+
return JavaKind.Void;
964+
}
965+
ValueNode result = stack[stackSize - 1];
966+
if (result == TWO_SLOT_MARKER) {
967+
result = stack[stackSize - 2];
968+
}
969+
return result.getStackKind();
970+
}
971+
961972
/**
962973
* Pop the specified number of slots off of this stack and return them as an array of
963974
* instructions.

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/graphbuilderconf/InvocationPlugin.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ public boolean defaultHandler(@SuppressWarnings("unused") GraphBuilderContext b,
381381

382382
public String getSourceLocation() {
383383
Class<?> c = getClass();
384-
for (Method m : c.getDeclaredMethods()) {
384+
for (Method m : c.getMethods()) {
385385
if (m.getName().equals("apply") || m.getName().equals("defaultHandler")) {
386386
return String.format("%s.%s()", m.getDeclaringClass().getName(), m.getName());
387387
}
@@ -498,6 +498,13 @@ public RequiredInvocationPlugin(String name, Type... argumentTypes) {
498498
public final boolean canBeDisabled() {
499499
return false;
500500
}
501+
502+
@Override
503+
public boolean isGraalOnly() {
504+
// We treat all required invocation plugins as Graal only. This will skip the return
505+
// type check in BytecodeParser.
506+
return true;
507+
}
501508
}
502509

503510
public abstract static class RequiredInlineOnlyInvocationPlugin extends RequiredInvocationPlugin {

0 commit comments

Comments
 (0)