1 /* 2 * Copyright (c) 2012, 2020, 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 */ 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 */ 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 */ 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 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 109 public void testBitCountIntConstant() { 110 ValueNode result = parseAndInline("bitCountIntConstantSnippet"); 111 Assert.assertEquals(7, result.asJavaConstant().asInt()); 112 } 113 114 public static int bitCountLongConstantSnippet() { 115 return Long.bitCount(LONG_CONSTANT_1) + Long.bitCount(LONG_CONSTANT_2) + Long.bitCount(LONG_CONSTANT_3); 116 } 117 118 public static int bitCountIntSnippet(int v) { 119 return Integer.bitCount(v & 0xFFFFFF | 0xFF); 120 } 121 122 @Test 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 129 public static int bitCountIntEmptySnippet(int v) { 130 return Integer.bitCount(v & 0xFFFFFF); 131 } 132 133 @Test 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 141 public void testBitCountLongConstant() { 142 ValueNode result = parseAndInline("bitCountLongConstantSnippet"); 143 Assert.assertEquals(7, result.asJavaConstant().asInt()); 144 } 145 146 public static int bitCountLongSnippet(long v) { 147 return Long.bitCount(v & 0xFFFFFFFFFFL | 0xFFL); 148 } 149 150 @Test 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 157 public static int bitCountLongEmptySnippet(long v) { 158 return Long.bitCount(v & 0xFFFFFFFFFFL); 159 } 160 161 @Test 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 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 177 public void testScanForwardIntConstant() { 178 ValueNode result = parseAndInline("scanForwardIntConstantSnippet"); 179 Assert.assertEquals(40, result.asJavaConstant().asInt()); 180 } 181 182 public static int scanForwardIntSnippet(int v) { 183 return Integer.numberOfTrailingZeros(v & 0xFFF0 | 0xFF00); 184 } 185 186 @Test 187 public void testScanForwardInt() { 188 ValueNode result = parseAndInline("scanForwardIntSnippet"); 189 Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 4, 8), result.stamp(NodeView.DEFAULT)); 190 } 191 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 197 public void testScanForwardLongConstant() { 198 ValueNode result = parseAndInline("scanForwardLongConstantSnippet"); 199 Assert.assertEquals(72, result.asJavaConstant().asInt()); 200 } 201 202 public static int scanForwardLongSnippet(long v) { 203 return Long.numberOfTrailingZeros(v & 0xFFFF000000L | 0xFF00000000L); 204 } 205 206 @Test 207 public void testScanForwardLong() { 208 ValueNode result = parseAndInline("scanForwardLongSnippet"); 209 Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 32), result.stamp(NodeView.DEFAULT)); 210 } 211 212 public static int scanForwardLongEmptySnippet(long v) { 213 int result = Long.numberOfTrailingZeros(v & 0xFFFF000000L); 214 dummyField = result; 215 return result; 216 } 217 218 @Test 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 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 233 public void testScanReverseIntConstant() { 234 ValueNode result = parseAndInline("scanReverseIntConstantSnippet"); 235 Assert.assertEquals(47, result.asJavaConstant().asInt()); 236 } 237 238 public static int scanReverseIntSnippet(int v) { 239 return Integer.numberOfLeadingZeros(v & 0xFFF0 | 0xFF0); 240 } 241 242 @Test 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 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 256 public void testScanReverseLongConstant() { 257 ValueNode result = parseAndInline("scanReverseLongConstantSnippet"); 258 Assert.assertEquals(111, result.asJavaConstant().asInt()); 259 } 260 261 public static int scanReverseLongSnippet(long v) { 262 int result = Long.numberOfLeadingZeros(v & 0xFFF0); 263 dummyField = result; 264 return result; 265 } 266 267 @Test 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 276 public static int scanReverseLongEmptySnippet(long v) { 277 int result = Long.numberOfLeadingZeros(v & 0xFFFF000000L); 278 dummyField = result; 279 return result; 280 } 281 282 @Test 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 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 */ 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 }