Skip to content

Commit cc12aed

Browse files
committed
[GR-54986] Canonicalize Pi with constant from Phi.
PullRequest: graal/18170
2 parents 882ba24 + 6b4b631 commit cc12aed

File tree

1 file changed

+76
-3
lines changed
  • compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes

1 file changed

+76
-3
lines changed

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/PiNode.java

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 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
@@ -244,8 +244,16 @@ public void virtualize(VirtualizerTool tool) {
244244
}
245245
}
246246

247-
@SuppressFBWarnings(value = {"NP"}, justification = "We null check it before")
248247
public static ValueNode canonical(ValueNode object, Stamp piStamp, GuardingNode guard, ValueNode self) {
248+
/*
249+
* If canonicalization is not explicitly triggered, we need to be conservative and assume
250+
* that the graph is still building up and some usages/inputs of/to nodes are still missing.
251+
*/
252+
return canonical(object, piStamp, guard, self, false);
253+
}
254+
255+
@SuppressFBWarnings(value = {"NP"}, justification = "We null check it before")
256+
public static ValueNode canonical(ValueNode object, Stamp piStamp, GuardingNode guard, ValueNode self, boolean allUsagesAvailable) {
249257
GraalError.guarantee(piStamp != null && object != null, "Invariant piStamp=%s object=%s guard=%s self=%s", piStamp, object, guard, self);
250258

251259
// Use most up to date stamp.
@@ -256,6 +264,56 @@ public static ValueNode canonical(ValueNode object, Stamp piStamp, GuardingNode
256264
return object;
257265
}
258266

267+
/*
268+
* Extracts a constant node if (1) this pi node's stamp is a non-null pointer stamp, (2)
269+
* 'object' is phi node that has only the same constant node and else just null constants as
270+
* inputs, and (3) if the constant's stamp equals this pi node's stamp.
271+
*
272+
* @formatter:off
273+
* ...
274+
* |
275+
* Merge
276+
* |
277+
* | C(object) null null C(object) (...)
278+
* | | | | | |
279+
* | | | | | |
280+
* | |-------|-----+-----|-------|
281+
* | |
282+
* |----------------- phi
283+
* |
284+
* piWithNonNullStamp
285+
* |
286+
* ...
287+
* @formatter:on
288+
*
289+
* Another important condition is that the phi node already has all inputs. During graph
290+
* decoding, it may happen that inputs are added later for loop phis. Therefore, either
291+
* 'allUsagesAvailable' is given or the phi node is not a loop phi.
292+
*
293+
* This canonicalization case has some overlap with other optimizations. In particular,
294+
* aggressive path duplication may get to a very similar result. Also, 'IfNode.splitIfAtPhi'
295+
* does a similar optimization but with different limitations. This should be considered
296+
* when touching them. One advantage of this canonicalization is that it happens immediately
297+
* when creating PiNodes.
298+
*/
299+
if (StampFactory.objectNonNull().equals(piStamp) && object instanceof PhiNode phiNode && (allUsagesAvailable || !phiNode.isLoopPhi())) {
300+
ValueNode constantValue = null;
301+
for (ValueNode phiValue : phiNode.values()) {
302+
if (constantValue == null && phiValue.isConstant() && !phiValue.isNullConstant() && checkPhiValueStamp(piStamp, computedStamp, phiValue.stamp(NodeView.DEFAULT))) {
303+
constantValue = phiValue;
304+
} else if (!phiValue.isNullConstant() && (constantValue == null || !isSameConstant(constantValue, phiValue))) {
305+
// reset value to indicate that condition is violated
306+
constantValue = null;
307+
break;
308+
}
309+
}
310+
if (constantValue != null) {
311+
assert constantValue.isConstant() && !constantValue.isNullConstant() && checkPhiValueStamp(piStamp, computedStamp,
312+
constantValue.stamp(NodeView.DEFAULT)) : Assertions.errorMessageContext("pi", self, "phi", phiNode, "constant", constantValue);
313+
return constantValue;
314+
}
315+
}
316+
259317
if (guard == null) {
260318
// Try to merge the pi node with a load node.
261319
if (object instanceof ReadNode && !object.hasMoreThanOneUsage()) {
@@ -295,9 +353,24 @@ public static ValueNode canonical(ValueNode object, Stamp piStamp, GuardingNode
295353
return null;
296354
}
297355

356+
/**
357+
* Pi-phi canonicalization is only allowed if the stamp of the phi: (1) is an object stamp
358+
* (because PiNodes on primitive values are flaky), (2) the computed stamp is equal to the phi
359+
* value's stamp, and (3) the stamp of the PiNode does not add any information.
360+
*/
361+
private static boolean checkPhiValueStamp(Stamp piStamp, Stamp computedStamp, Stamp phiValueStamp) {
362+
return phiValueStamp.isObjectStamp() && computedStamp.equals(phiValueStamp) && piStamp.join(phiValueStamp).equals(phiValueStamp);
363+
}
364+
365+
private static boolean isSameConstant(ValueNode referenceValue, ValueNode phiValue) {
366+
assert referenceValue.isConstant() && !referenceValue.isNullConstant() : "reference value must be constant but not the null constant";
367+
assert referenceValue.asConstant() != null : "constant must not be null";
368+
return phiValue.isConstant() && referenceValue.asConstant().equals(phiValue.asConstant());
369+
}
370+
298371
@Override
299372
public Node canonical(CanonicalizerTool tool) {
300-
Node value = canonical(object(), piStamp(), getGuard(), this);
373+
Node value = canonical(object(), piStamp(), getGuard(), this, tool.allUsagesAvailable());
301374
if (value != null) {
302375
return value;
303376
}

0 commit comments

Comments
 (0)