Skip to content

Commit a10a287

Browse files
committed
[GR-35830] Improve AndNode canconicalization
PullRequest: graal/10962
2 parents 61b6238 + 4d88224 commit a10a287

File tree

2 files changed

+287
-0
lines changed

2 files changed

+287
-0
lines changed
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package org.graalvm.compiler.core.test;
26+
27+
import org.graalvm.compiler.nodes.StructuredGraph;
28+
import org.graalvm.compiler.nodes.calc.OrNode;
29+
import org.graalvm.compiler.nodes.calc.ShiftNode;
30+
import org.junit.Test;
31+
32+
public class MaskingOptimizationTest extends GraalCompilerTest {
33+
34+
int shiftAndMask1(int x) {
35+
return (x << 2) & 3; // reduces to return 0
36+
}
37+
38+
@Test
39+
public void test1() {
40+
test("shiftAndMask1", 42);
41+
}
42+
43+
long shiftAndMask2(long x) {
44+
return (x << 2) & 3; // reduces to return 0
45+
}
46+
47+
@Test
48+
public void test2() {
49+
test("shiftAndMask2", 42L);
50+
}
51+
52+
int shiftAndMask3(int x, int y) {
53+
return (y + (x << 2)) & 3; // reduces to return y & 3
54+
}
55+
56+
@Test
57+
public void test3() {
58+
test("shiftAndMask3", 42, 35);
59+
}
60+
61+
long shiftAndMask4(long x, long y) {
62+
return (y + (x << 2)) & 3; // reduces to return y & 3
63+
}
64+
65+
@Test
66+
public void test4() {
67+
test("shiftAndMask4", 42L, 35L);
68+
}
69+
70+
int shiftAndMask5(int x, int y) {
71+
return ((y << 2) + (x << 2)) & 3; // reduces to return 0
72+
}
73+
74+
@Test
75+
public void test5() {
76+
test("shiftAndMask5", 42, 35);
77+
}
78+
79+
long shiftAndMask6(long x, long y) {
80+
return ((y << 2) + (x << 2)) & 3; // reduces to return 0
81+
}
82+
83+
@Test
84+
public void test6() {
85+
test("shiftAndMask6", 42L, 35L);
86+
}
87+
88+
long shiftAndMask7(int x) {
89+
return ((long) (x << 2)) & 3; // reduces to return 0
90+
}
91+
92+
@Test
93+
public void test7() {
94+
test("shiftAndMask7", 42);
95+
}
96+
97+
long shiftAndMask8(int x, int y) {
98+
return (y + (x << 2)) & 3; // reduces to y & 3
99+
}
100+
101+
@Test
102+
public void test8() {
103+
test("shiftAndMask8", 42, 35);
104+
}
105+
106+
long shiftAndMask9(int x, int y) {
107+
return (((long) (y << 2)) + ((long) (x << 2))) & 3; // reduces to return 0
108+
}
109+
110+
@Test
111+
public void test9() {
112+
test("shiftAndMask9", 42, 35);
113+
}
114+
115+
boolean shiftAndMaskCompare1(int x) {
116+
return ((x << 2) & 3) == 0; // reduces to return true
117+
}
118+
119+
@Test
120+
public void test10() {
121+
test("shiftAndMaskCompare1", 42);
122+
}
123+
124+
boolean shiftAndMaskCompare2(long x) {
125+
return ((x << 2) & 3) == 0; // reduces to return true
126+
}
127+
128+
@Test
129+
public void test11() {
130+
test("shiftAndMaskCompare2", 42L);
131+
}
132+
133+
boolean shiftAndMaskCompare3(int x, int y) {
134+
return ((y + (x << 2)) & 3) == 0; // reduces to return y & 3 == 0
135+
}
136+
137+
@Test
138+
public void test12() {
139+
test("shiftAndMaskCompare3", 42, 35);
140+
}
141+
142+
boolean shiftAndMaskCompare4(long x, long y) {
143+
return ((y + (x << 2)) & 3) == 0; // reduces to return y & 3 == 0
144+
}
145+
146+
@Test
147+
public void test13() {
148+
test("shiftAndMaskCompare4", 42L, 35L);
149+
}
150+
151+
boolean shiftAndMaskCompare5(int x, int y) {
152+
return (((y << 2) + (x << 2)) & 3) == 0; // reduces to return true
153+
}
154+
155+
@Test
156+
public void test14() {
157+
test("shiftAndMaskCompare5", 42, 35);
158+
}
159+
160+
boolean shiftAndMaskCompare6(long x, long y) {
161+
return (((y << 2) + (x << 2)) & 3) == 0; // reduces to return true
162+
}
163+
164+
@Test
165+
public void test15() {
166+
test("shiftAndMaskCompare6", 42L, 35L);
167+
}
168+
169+
boolean shiftAndMaskCompare7(int x) {
170+
return (((long) (x << 2)) & 3) == 0; // reduces to return true
171+
}
172+
173+
@Test
174+
public void test16() {
175+
test("shiftAndMaskCompare7", 42);
176+
}
177+
178+
boolean shiftAndMaskCompare8(int x, int y) {
179+
return ((y + (x << 2)) & 3) == 0; // reduces to y & 3 == 0
180+
}
181+
182+
@Test
183+
public void test17() {
184+
test("shiftAndMaskCompare8", 42, 35);
185+
}
186+
187+
boolean shiftAndMaskCompare9(int x, int y) {
188+
return ((((long) (y << 2)) + ((long) (x << 2))) & 3) == 0; // reduces to return true
189+
}
190+
191+
@Test
192+
public void test18() {
193+
test("shiftAndMaskCompare9", 42, 35);
194+
}
195+
196+
int orAndMask1(int x) {
197+
return (x | 4) & 3; // reduces to return x & 3
198+
}
199+
200+
@Test
201+
public void test19() {
202+
test("orAndMask1", 42);
203+
}
204+
205+
long orAndMask2(long x) {
206+
return (x | 4L) & 3L; // reduces to return x & 3
207+
}
208+
209+
@Test
210+
public void test20() {
211+
test("orAndMask2", 42L);
212+
}
213+
214+
@Override
215+
protected void checkMidTierGraph(StructuredGraph graph) {
216+
super.checkMidTierGraph(graph);
217+
String methodName = graph.asJavaMethod().getName();
218+
if (methodName.startsWith("shift") && !graph.getNodes().filter(n -> n instanceof ShiftNode).isEmpty()) {
219+
throw new AssertionError("Expected shift nodes to be removed by canonicalization");
220+
} else if (methodName.startsWith("or") && !graph.getNodes().filter(n -> n instanceof OrNode).isEmpty()) {
221+
throw new AssertionError("Expected or nodes to be removed by canonicalization");
222+
}
223+
}
224+
}

compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AndNode.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,61 @@ public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode for
7878
return canonical(this, getOp(forX, forY), forX, forY, view);
7979
}
8080

81+
/**
82+
* Given a value which is an input to an {@code AndNode} and an IntegerStamp for the other input
83+
* determine if the input can be simplified by folding away an {@code AddNode} or an
84+
* {@code OrNode}.
85+
*
86+
* @param usingAndInput the {@code AndNode} {@code ValueNode} input
87+
* @param usingAndOtherStamp the stamp of the other input
88+
* @return A {@code ValueNode} to use as a replacement input in place of the
89+
* {@code usingAndInput} input, null otherwise
90+
*/
91+
private static ValueNode eliminateRedundantBinaryArithmeticOp(ValueNode usingAndInput, IntegerStamp usingAndOtherStamp) {
92+
if (usingAndOtherStamp.isUnrestricted()) {
93+
return null;
94+
}
95+
if (!(usingAndInput instanceof BinaryArithmeticNode<?>)) {
96+
return null;
97+
}
98+
BinaryArithmeticNode<?> opNode = (BinaryArithmeticNode<?>) usingAndInput;
99+
ValueNode opX = opNode.getX();
100+
ValueNode opY = opNode.getY();
101+
IntegerStamp stampX = (IntegerStamp) opX.stamp(NodeView.DEFAULT);
102+
IntegerStamp stampY = (IntegerStamp) opY.stamp(NodeView.DEFAULT);
103+
if (usingAndInput instanceof OrNode) {
104+
// An OrNode strictly adds information to a bit pattern - it cannot remove set bits.
105+
// We can fold an operand away when that operand does not contribute any bits to the
106+
// masked result - check must be set against masked maybe set bits
107+
if (!stampY.isUnrestricted() && (stampY.upMask() & usingAndOtherStamp.upMask()) == 0) {
108+
return opX;
109+
}
110+
if (!stampX.isUnrestricted() && (stampX.upMask() & usingAndOtherStamp.upMask()) == 0) {
111+
return opY;
112+
}
113+
} else if (usingAndInput instanceof AddNode) {
114+
// like an OrNode above, an AddNode is adding information in a fixed set of
115+
// bit positions, modulo carrying across columns. So if we know:
116+
// 1) the operand has ones only where we know zeros must be in the and result
117+
// 2) bit carrys can't mess up the pattern (eg bits are packed at the bottom)
118+
// then we can simply fold the add away because it adds no information
119+
long mightBeOne = usingAndOtherStamp.downMask();
120+
if (!usingAndOtherStamp.isPositive() || Long.numberOfLeadingZeros(mightBeOne) + Long.highestOneBit(mightBeOne) != 64) {
121+
return null;
122+
}
123+
124+
if (mightBeOne != 0) {
125+
if (!stampY.isUnrestricted() && (stampY.upMask() & mightBeOne) == 0) {
126+
return opX;
127+
}
128+
if (!stampX.isUnrestricted() && (stampX.upMask() & mightBeOne) == 0) {
129+
return opY;
130+
}
131+
}
132+
}
133+
return null;
134+
}
135+
81136
private static ValueNode canonical(AndNode self, BinaryOp<And> op, ValueNode forX, ValueNode forY, NodeView view) {
82137
if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
83138
return forX;
@@ -96,6 +151,14 @@ private static ValueNode canonical(AndNode self, BinaryOp<And> op, ValueNode for
96151
} else if (((~yStamp.downMask()) & xStamp.upMask()) == 0) {
97152
return forX;
98153
}
154+
ValueNode newLHS = eliminateRedundantBinaryArithmeticOp(forX, yStamp);
155+
if (newLHS != null) {
156+
return new AndNode(newLHS, forY);
157+
}
158+
ValueNode newRHS = eliminateRedundantBinaryArithmeticOp(forY, xStamp);
159+
if (newRHS != null) {
160+
return new AndNode(forX, newRHS);
161+
}
99162
}
100163

101164
if (forY.isConstant()) {

0 commit comments

Comments
 (0)