1 /*
2  * Copyright (c) 2012, 2019, 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.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 
25 package org.graalvm.compiler.replacements.test;
26 
27 import org.graalvm.compiler.core.common.type.StampFactory;
28 import org.graalvm.compiler.core.test.GraalCompilerTest;
29 import org.graalvm.compiler.nodes.NodeView;
30 import org.graalvm.compiler.nodes.ReturnNode;
31 import org.graalvm.compiler.nodes.StructuredGraph;
32 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
33 import org.graalvm.compiler.nodes.ValueNode;
34 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
35 import org.graalvm.compiler.phases.tiers.HighTierContext;
36 import org.graalvm.compiler.replacements.nodes.BitScanReverseNode;
37 import org.junit.Assert;
38 import org.junit.Assume;
39 import org.junit.Test;
40 
41 import jdk.vm.ci.aarch64.AArch64;
42 import jdk.vm.ci.amd64.AMD64;
43 import jdk.vm.ci.code.Architecture;
44 import jdk.vm.ci.meta.JavaKind;
45 import jdk.vm.ci.sparc.SPARC;
46 
47 public class BitOpNodesTest extends GraalCompilerTest {
48 
49     private static final int INT_CONSTANT_1 = 0x80100010;
50     private static final int INT_CONSTANT_2 = 0x00011110;
51     private static final int INT_CONSTANT_3 = 0x00000000;
52 
53     private static final long LONG_CONSTANT_1 = 0x8000000000100010L;
54     private static final long LONG_CONSTANT_2 = 0x0000000000011110L;
55     private static final long LONG_CONSTANT_3 = 0x0000000000000000L;
56 
57     public static long dummyField;
58 
59     /*
60      * Tests for BitCountNode canonicalizations.
61      */
62 
63     /**
64      * Determines if the current VM context supports intrinsics for the {@code bitCount} methods in
65      * {@link Integer} and {@link Long}.
66      */
isBitCountIntrinsicSupported(Architecture arch)67     public static boolean isBitCountIntrinsicSupported(Architecture arch) {
68         if (arch instanceof AMD64) {
69             AMD64 amd64 = (AMD64) arch;
70             return amd64.getFeatures().contains(AMD64.CPUFeature.POPCNT);
71         } else {
72             // Even though there are AArch64 intrinsics for bitCount, they do
73             // not use BitCountNode.
74             return arch instanceof SPARC;
75         }
76     }
77 
78     /**
79      * Determines if the current VM context supports intrinsics for the {@code numberOfLeadingZeros}
80      * methods in {@link Integer} and {@link Long}.
81      */
isNumberLeadingZerosIntrinsicSupported(Architecture arch)82     public static boolean isNumberLeadingZerosIntrinsicSupported(Architecture arch) {
83         if (arch instanceof AMD64) {
84             AMD64 amd64 = (AMD64) arch;
85             return amd64.getFeatures().contains(AMD64.CPUFeature.LZCNT) && amd64.getFlags().contains(AMD64.Flag.UseCountLeadingZerosInstruction);
86         } else {
87             return arch instanceof SPARC || arch instanceof AArch64;
88         }
89     }
90 
91     /**
92      * Determines if the current VM context supports intrinsics for the
93      * {@code numberOfTrailingZeros} methods in {@link Integer} and {@link Long}.
94      */
isNumberTrailingZerosIntrinsicSupported(Architecture arch)95     public static boolean isNumberTrailingZerosIntrinsicSupported(Architecture arch) {
96         if (arch instanceof AMD64) {
97             AMD64 amd64 = (AMD64) arch;
98             return amd64.getFeatures().contains(AMD64.CPUFeature.BMI1) && amd64.getFlags().contains(AMD64.Flag.UseCountTrailingZerosInstruction);
99         } else {
100             return arch instanceof SPARC || arch instanceof AArch64;
101         }
102     }
103 
bitCountIntConstantSnippet()104     public static int bitCountIntConstantSnippet() {
105         return Integer.bitCount(INT_CONSTANT_1) + Integer.bitCount(INT_CONSTANT_2) + Integer.bitCount(INT_CONSTANT_3);
106     }
107 
108     @Test
testBitCountIntConstant()109     public void testBitCountIntConstant() {
110         ValueNode result = parseAndInline("bitCountIntConstantSnippet");
111         Assert.assertEquals(7, result.asJavaConstant().asInt());
112     }
113 
bitCountLongConstantSnippet()114     public static int bitCountLongConstantSnippet() {
115         return Long.bitCount(LONG_CONSTANT_1) + Long.bitCount(LONG_CONSTANT_2) + Long.bitCount(LONG_CONSTANT_3);
116     }
117 
bitCountIntSnippet(int v)118     public static int bitCountIntSnippet(int v) {
119         return Integer.bitCount(v & 0xFFFFFF | 0xFF);
120     }
121 
122     @Test
testBitCountInt()123     public void testBitCountInt() {
124         Assume.assumeTrue(isBitCountIntrinsicSupported(getBackend().getTarget().arch));
125         ValueNode result = parseAndInline("bitCountIntSnippet");
126         Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 8, 24), result.stamp(NodeView.DEFAULT));
127     }
128 
bitCountIntEmptySnippet(int v)129     public static int bitCountIntEmptySnippet(int v) {
130         return Integer.bitCount(v & 0xFFFFFF);
131     }
132 
133     @Test
testBitCountIntEmpty()134     public void testBitCountIntEmpty() {
135         Assume.assumeTrue(isBitCountIntrinsicSupported(getBackend().getTarget().arch));
136         ValueNode result = parseAndInline("bitCountIntEmptySnippet");
137         Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 0, 24), result.stamp(NodeView.DEFAULT));
138     }
139 
140     @Test
testBitCountLongConstant()141     public void testBitCountLongConstant() {
142         ValueNode result = parseAndInline("bitCountLongConstantSnippet");
143         Assert.assertEquals(7, result.asJavaConstant().asInt());
144     }
145 
bitCountLongSnippet(long v)146     public static int bitCountLongSnippet(long v) {
147         return Long.bitCount(v & 0xFFFFFFFFFFL | 0xFFL);
148     }
149 
150     @Test
testBitCountLong()151     public void testBitCountLong() {
152         Assume.assumeTrue(isBitCountIntrinsicSupported(getBackend().getTarget().arch));
153         ValueNode result = parseAndInline("bitCountLongSnippet");
154         Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 8, 40), result.stamp(NodeView.DEFAULT));
155     }
156 
bitCountLongEmptySnippet(long v)157     public static int bitCountLongEmptySnippet(long v) {
158         return Long.bitCount(v & 0xFFFFFFFFFFL);
159     }
160 
161     @Test
testBitCountLongEmpty()162     public void testBitCountLongEmpty() {
163         Assume.assumeTrue(isBitCountIntrinsicSupported(getBackend().getTarget().arch));
164         ValueNode result = parseAndInline("bitCountLongEmptySnippet");
165         Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 0, 40), result.stamp(NodeView.DEFAULT));
166     }
167 
168     /*
169      * Tests for BitScanForwardNode
170      */
171 
scanForwardIntConstantSnippet()172     public static int scanForwardIntConstantSnippet() {
173         return Integer.numberOfTrailingZeros(INT_CONSTANT_1) + Integer.numberOfTrailingZeros(INT_CONSTANT_2) + Integer.numberOfTrailingZeros(INT_CONSTANT_3);
174     }
175 
176     @Test
testScanForwardIntConstant()177     public void testScanForwardIntConstant() {
178         ValueNode result = parseAndInline("scanForwardIntConstantSnippet");
179         Assert.assertEquals(40, result.asJavaConstant().asInt());
180     }
181 
scanForwardIntSnippet(int v)182     public static int scanForwardIntSnippet(int v) {
183         return Integer.numberOfTrailingZeros(v & 0xFFF0 | 0xFF00);
184     }
185 
186     @Test
testScanForwardInt()187     public void testScanForwardInt() {
188         ValueNode result = parseAndInline("scanForwardIntSnippet");
189         Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 4, 8), result.stamp(NodeView.DEFAULT));
190     }
191 
scanForwardLongConstantSnippet()192     public static int scanForwardLongConstantSnippet() {
193         return Long.numberOfTrailingZeros(LONG_CONSTANT_1) + Long.numberOfTrailingZeros(LONG_CONSTANT_2) + Long.numberOfTrailingZeros(LONG_CONSTANT_3);
194     }
195 
196     @Test
testScanForwardLongConstant()197     public void testScanForwardLongConstant() {
198         ValueNode result = parseAndInline("scanForwardLongConstantSnippet");
199         Assert.assertEquals(72, result.asJavaConstant().asInt());
200     }
201 
scanForwardLongSnippet(long v)202     public static int scanForwardLongSnippet(long v) {
203         return Long.numberOfTrailingZeros(v & 0xFFFF000000L | 0xFF00000000L);
204     }
205 
206     @Test
testScanForwardLong()207     public void testScanForwardLong() {
208         ValueNode result = parseAndInline("scanForwardLongSnippet");
209         Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 32), result.stamp(NodeView.DEFAULT));
210     }
211 
scanForwardLongEmptySnippet(long v)212     public static int scanForwardLongEmptySnippet(long v) {
213         int result = Long.numberOfTrailingZeros(v & 0xFFFF000000L);
214         dummyField = result;
215         return result;
216     }
217 
218     @Test
testScanForwardLongEmpty()219     public void testScanForwardLongEmpty() {
220         ValueNode result = parseAndInline("scanForwardLongEmptySnippet");
221         Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 64), result.stamp(NodeView.DEFAULT));
222     }
223 
224     /*
225      * Tests for BitScanReverseNode
226      */
227 
scanReverseIntConstantSnippet()228     public static int scanReverseIntConstantSnippet() {
229         return Integer.numberOfLeadingZeros(INT_CONSTANT_1) + Integer.numberOfLeadingZeros(INT_CONSTANT_2) + Integer.numberOfLeadingZeros(INT_CONSTANT_3);
230     }
231 
232     @Test
testScanReverseIntConstant()233     public void testScanReverseIntConstant() {
234         ValueNode result = parseAndInline("scanReverseIntConstantSnippet");
235         Assert.assertEquals(47, result.asJavaConstant().asInt());
236     }
237 
scanReverseIntSnippet(int v)238     public static int scanReverseIntSnippet(int v) {
239         return Integer.numberOfLeadingZeros(v & 0xFFF0 | 0xFF0);
240     }
241 
242     @Test
testScanReverseInt()243     public void testScanReverseInt() {
244         /* This test isn't valid unless the BitScanReverseNode intrinsic is used. */
245         ValueNode result = parseAndInline("scanReverseIntSnippet", BitScanReverseNode.class);
246         if (result != null) {
247             Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 16, 20), result.stamp(NodeView.DEFAULT));
248         }
249     }
250 
scanReverseLongConstantSnippet()251     public static int scanReverseLongConstantSnippet() {
252         return Long.numberOfLeadingZeros(LONG_CONSTANT_1) + Long.numberOfLeadingZeros(LONG_CONSTANT_2) + Long.numberOfLeadingZeros(LONG_CONSTANT_3);
253     }
254 
255     @Test
testScanReverseLongConstant()256     public void testScanReverseLongConstant() {
257         ValueNode result = parseAndInline("scanReverseLongConstantSnippet");
258         Assert.assertEquals(111, result.asJavaConstant().asInt());
259     }
260 
scanReverseLongSnippet(long v)261     public static int scanReverseLongSnippet(long v) {
262         int result = Long.numberOfLeadingZeros(v & 0xFFF0);
263         dummyField = result;
264         return result;
265     }
266 
267     @Test
testScanReverseLong()268     public void testScanReverseLong() {
269         /* This test isn't valid unless the BitScanReverseNode intrinsic is used. */
270         ValueNode result = parseAndInline("scanReverseLongSnippet", BitScanReverseNode.class);
271         if (result != null) {
272             Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 48, 64), result.stamp(NodeView.DEFAULT));
273         }
274     }
275 
scanReverseLongEmptySnippet(long v)276     public static int scanReverseLongEmptySnippet(long v) {
277         int result = Long.numberOfLeadingZeros(v & 0xFFFF000000L);
278         dummyField = result;
279         return result;
280     }
281 
282     @Test
testScanReverseLongEmpty()283     public void testScanReverseLongEmpty() {
284         /* This test isn't valid unless the BitScanReverseNode intrinsic is used. */
285         ValueNode result = parseAndInline("scanReverseLongEmptySnippet", BitScanReverseNode.class);
286         if (result != null) {
287             Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 64), result.stamp(NodeView.DEFAULT));
288         }
289     }
290 
parseAndInline(String name)291     private ValueNode parseAndInline(String name) {
292         return parseAndInline(name, null);
293     }
294 
295     /**
296      * Parse and optimize {@code name}. If {@code expectedClass} is non-null and a node of that type
297      * isn't found simply return null. Otherwise return the node returned by the graph.
298      *
299      * @param name
300      * @param expectedClass
301      * @return the returned value or null if {@code expectedClass} is not found in the graph.
302      */
parseAndInline(String name, Class<? extends ValueNode> expectedClass)303     private ValueNode parseAndInline(String name, Class<? extends ValueNode> expectedClass) {
304         StructuredGraph graph = parseEager(name, AllowAssumptions.YES);
305         HighTierContext context = getDefaultHighTierContext();
306         CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
307         canonicalizer.apply(graph, context);
308         createInliningPhase(canonicalizer).apply(graph, context);
309         canonicalizer.apply(graph, context);
310         Assert.assertEquals(1, graph.getNodes(ReturnNode.TYPE).count());
311         if (expectedClass != null) {
312             if (graph.getNodes().filter(expectedClass).count() == 0) {
313                 return null;
314             }
315         }
316         return graph.getNodes(ReturnNode.TYPE).first().result();
317     }
318 }
319