Skip to content

Commit 606f080

Browse files
committed
Use the equals method to compare speculations.
1 parent 120abbf commit 606f080

File tree

3 files changed

+65
-12
lines changed

3 files changed

+65
-12
lines changed

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/CheckGraalInvariants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@
116116
import jdk.vm.ci.meta.ResolvedJavaField;
117117
import jdk.vm.ci.meta.ResolvedJavaMethod;
118118
import jdk.vm.ci.meta.ResolvedJavaType;
119+
import jdk.vm.ci.meta.SpeculationLog;
119120
import jdk.vm.ci.meta.Value;
120121

121122
/**
@@ -324,6 +325,7 @@ public static void runTest(InvariantsTool tool) {
324325
verifiers.add(new VerifyUsageWithEquals(LIRKind.class));
325326
verifiers.add(new VerifyUsageWithEquals(ArithmeticOpTable.class));
326327
verifiers.add(new VerifyUsageWithEquals(ArithmeticOpTable.Op.class));
328+
verifiers.add(new VerifyUsageWithEquals(SpeculationLog.Speculation.class, SpeculationLog.NO_SPECULATION));
327329

328330
verifiers.add(new VerifySharedConstantEmptyArray());
329331
verifiers.add(new VerifyDebugUsage());

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/VerifyUsageWithEquals.java

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -24,6 +24,9 @@
2424
*/
2525
package jdk.graal.compiler.core.test;
2626

27+
import java.util.Objects;
28+
29+
import jdk.graal.compiler.api.replacements.SnippetReflectionProvider;
2730
import jdk.graal.compiler.core.common.type.ObjectStamp;
2831
import jdk.graal.compiler.nodes.Invoke;
2932
import jdk.graal.compiler.nodes.NodeView;
@@ -36,7 +39,8 @@
3639
import jdk.graal.compiler.nodes.spi.UncheckedInterfaceProvider;
3740
import jdk.graal.compiler.nodes.type.StampTool;
3841
import jdk.graal.compiler.phases.VerifyPhase;
39-
42+
import jdk.vm.ci.meta.ConstantReflectionProvider;
43+
import jdk.vm.ci.meta.JavaConstant;
4044
import jdk.vm.ci.meta.JavaField;
4145
import jdk.vm.ci.meta.JavaKind;
4246
import jdk.vm.ci.meta.JavaMethod;
@@ -49,7 +53,8 @@
4953
/**
5054
* For certain types, object identity should not be used for object equality check. This phase
5155
* checks the correct usage of the given type. Equality checks with == or != (except null checks)
52-
* results in an {@link AssertionError}.
56+
* results in an {@link AssertionError}. Optionally, a singleton value with which == and != checks
57+
* are allowed as an exception may be provided.
5358
*/
5459
public class VerifyUsageWithEquals extends VerifyPhase<CoreProviders> {
5560

@@ -63,9 +68,34 @@ public boolean checkContract() {
6368
*/
6469
private final Class<?> restrictedClass;
6570

71+
/**
72+
* The value besides {@code null} for which equality checks with == or != are allowed.
73+
*/
74+
private final Object safeSingletonValue;
75+
6676
public VerifyUsageWithEquals(Class<?> restrictedClass) {
77+
checkRestrictedClass(restrictedClass);
78+
this.restrictedClass = restrictedClass;
79+
this.safeSingletonValue = null;
80+
}
81+
82+
/**
83+
* Constructs a verifier to check that object identity is not used for equality checks except
84+
* with {@code null} and the given singleton value.
85+
*
86+
* @param restrictedClass the class for which equality checks are restricted
87+
* @param singletonValue the non-null value for which equality checks with == or != are allowed
88+
* as an exception
89+
*/
90+
public <T> VerifyUsageWithEquals(Class<T> restrictedClass, T singletonValue) {
91+
checkRestrictedClass(restrictedClass);
6792
this.restrictedClass = restrictedClass;
68-
assert !restrictedClass.isInterface() || isTrustedInterface(restrictedClass);
93+
Objects.requireNonNull(singletonValue);
94+
this.safeSingletonValue = singletonValue;
95+
}
96+
97+
private static void checkRestrictedClass(Class<?> restrictedClass) {
98+
assert !restrictedClass.isInterface() || isTrustedInterface(restrictedClass) : "the restricted class must not be an untrusted interface";
6999
}
70100

71101
private static final Class<?>[] trustedInterfaceTypes = {JavaType.class, JavaField.class, JavaMethod.class};
@@ -104,10 +134,28 @@ private boolean isAssignableToRestrictedType(ValueNode node, MetaAccessProvider
104134
return false;
105135
}
106136

137+
private boolean isSafeConstant(ValueNode node, ConstantReflectionProvider constantReflection, SnippetReflectionProvider snippetReflection) {
138+
return isNullConstant(node) || isSafeSingletonValue(node, constantReflection, snippetReflection);
139+
}
140+
107141
private static boolean isNullConstant(ValueNode node) {
108142
return node.isConstant() && node.isNullConstant();
109143
}
110144

145+
private boolean isSafeSingletonValue(ValueNode node, ConstantReflectionProvider constantReflection, SnippetReflectionProvider snippetReflection) {
146+
if (safeSingletonValue == null) {
147+
return false;
148+
}
149+
JavaConstant javaConstant = node.asJavaConstant();
150+
if (node instanceof LoadFieldNode loadField && loadField.isStatic() && loadField.field().isFinal()) {
151+
javaConstant = constantReflection.readFieldValue(loadField.field(), null);
152+
}
153+
if (javaConstant == null) {
154+
return false;
155+
}
156+
return safeSingletonValue == snippetReflection.asObject(Object.class, javaConstant);
157+
}
158+
111159
private static boolean isEqualsMethod(ResolvedJavaMethod method) {
112160
if (method.getName().equals("equals")) {
113161
Signature sig = method.getSignature();
@@ -129,12 +177,14 @@ private static boolean isThisParameter(ValueNode node) {
129177
}
130178

131179
/**
132-
* Checks whether the type of {@code x} is assignable to the restricted type and that {@code y}
133-
* is not a null constant.
180+
* Checks whether the type of {@code x} or {@code y} is assignable to the restricted type and
181+
* that {@code x} and {@code y} are not safe constants.
134182
*/
135-
private boolean isIllegalUsage(ResolvedJavaMethod method, ValueNode x, ValueNode y, MetaAccessProvider metaAccess) {
136-
if (isAssignableToRestrictedType(x, metaAccess) && !isNullConstant(y)) {
137-
if (isEqualsMethod(method) && isThisParameter(x) || isThisParameter(y)) {
183+
private boolean isIllegalUsage(ResolvedJavaMethod method, ValueNode x, ValueNode y, CoreProviders context) {
184+
if ((isAssignableToRestrictedType(x, context.getMetaAccess()) || isAssignableToRestrictedType(y, context.getMetaAccess())) &&
185+
!isSafeConstant(x, context.getConstantReflection(), context.getSnippetReflection()) &&
186+
!isSafeConstant(y, context.getConstantReflection(), context.getSnippetReflection())) {
187+
if (isEqualsMethod(method) && (isThisParameter(x) || isThisParameter(y))) {
138188
return false;
139189
}
140190
return true;
@@ -151,7 +201,7 @@ protected void verify(StructuredGraph graph, CoreProviders context) {
151201

152202
if (restrictedType.isAssignableFrom(method.getDeclaringClass())) {
153203
// Allow violation in methods of the restricted type itself and its subclasses.
154-
} else if (isIllegalUsage(method, cn.getX(), cn.getY(), context.getMetaAccess()) || isIllegalUsage(method, cn.getY(), cn.getX(), context.getMetaAccess())) {
204+
} else if (isIllegalUsage(method, cn.getX(), cn.getY(), context)) {
155205
throw new VerificationError("Verification of " + restrictedClass.getName() + " usage failed: Comparing " + cn.getX() + " and " + cn.getY() + " in " + method +
156206
" must use .equals() for object equality, not '==' or '!='");
157207
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/phases/common/ConditionalEliminationPhase.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.ArrayDeque;
3030
import java.util.Deque;
3131
import java.util.List;
32+
import java.util.Objects;
3233

3334
import org.graalvm.collections.EconomicMap;
3435
import org.graalvm.collections.EconomicSet;
@@ -355,7 +356,7 @@ public HIRBlock enter(HIRBlock b) {
355356
Speculation speculation = trueGuard.getSpeculation();
356357
if (speculation == null) {
357358
speculation = falseGuard.getSpeculation();
358-
} else if (falseGuard.getSpeculation() != null && falseGuard.getSpeculation() != speculation) {
359+
} else if (falseGuard.getSpeculation() != null && !falseGuard.getSpeculation().equals(speculation)) {
359360
// Cannot optimize due to different speculations.
360361
continue;
361362
}
@@ -1107,7 +1108,7 @@ private boolean canScheduleAbove(Node n, Node target, ValueNode knownToBeAbove)
11071108

11081109
protected boolean foldGuard(DeoptimizingGuard thisGuard, DeoptimizingGuard otherGuard, boolean outcome, Stamp guardedValueStamp, ConditionalEliminationUtil.GuardRewirer rewireGuardFunction) {
11091110
DeoptimizationAction action = mergeActions(otherGuard.getAction(), thisGuard.getAction());
1110-
if (action != null && otherGuard.getSpeculation() == thisGuard.getSpeculation()) {
1111+
if (action != null && Objects.equals(otherGuard.getSpeculation(), thisGuard.getSpeculation())) {
11111112
LogicNode condition = (LogicNode) thisGuard.getCondition().copyWithInputs();
11121113
/*
11131114
* We have ...; guard(C1); guard(C2);...

0 commit comments

Comments
 (0)