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 
  46 public class BitOpNodesTest extends GraalCompilerTest {
  47 
  48     private static final int INT_CONSTANT_1 = 0x80100010;
  49     private static final int INT_CONSTANT_2 = 0x00011110;
  50     private static final int INT_CONSTANT_3 = 0x00000000;
  51 
  52     private static final long LONG_CONSTANT_1 = 0x8000000000100010L;
  53     private static final long LONG_CONSTANT_2 = 0x0000000000011110L;
  54     private static final long LONG_CONSTANT_3 = 0x0000000000000000L;
  55 
  56     public static long dummyField;
  57 
  58     /*
  59      * Tests for BitCountNode canonicalizations.
  60      */
  61 
  62     /**
  63      * Determines if the current VM context supports intrinsics for the {@code bitCount} methods in
  64      * {@link Integer} and {@link Long}.
  65      */
  66     public static boolean isBitCountIntrinsicSupported(Architecture arch) {
  67         if (arch instanceof AMD64) {
  68             AMD64 amd64 = (AMD64) arch;
  69             return amd64.getFeatures().contains(AMD64.CPUFeature.POPCNT);
  70         } else {
  71             // Even though there are AArch64 intrinsics for bitCount, they do
  72             // not use BitCountNode.
  73             return false;
  74         }
  75     }
  76 
  77     /**
  78      * Determines if the current VM context supports intrinsics for the {@code numberOfLeadingZeros}
  79      * methods in {@link Integer} and {@link Long}.
  80      */
  81     public static boolean isNumberLeadingZerosIntrinsicSupported(Architecture arch) {
  82         if (arch instanceof AMD64) {
  83             AMD64 amd64 = (AMD64) arch;
  84             return amd64.getFeatures().contains(AMD64.CPUFeature.LZCNT) && amd64.getFlags().contains(AMD64.Flag.UseCountLeadingZerosInstruction);
  85         } else {
  86             return arch instanceof AArch64;
  87         }
  88     }
  89 
  90     /**
  91      * Determines if the current VM context supports intrinsics for the
  92      * {@code numberOfTrailingZeros} methods in {@link Integer} and {@link Long}.
  93      */
  94     public static boolean isNumberTrailingZerosIntrinsicSupported(Architecture arch) {
  95         if (arch instanceof AMD64) {
  96             AMD64 amd64 = (AMD64) arch;
  97             return amd64.getFeatures().contains(AMD64.CPUFeature.BMI1) && amd64.getFlags().contains(AMD64.Flag.UseCountTrailingZerosInstruction);
  98         } else {
  99             return arch instanceof AArch64;
 100         }
 101     }
 102 
 103     public static int bitCountIntConstantSnippet() {
 104         return Integer.bitCount(INT_CONSTANT_1) + Integer.bitCount(INT_CONSTANT_2) + Integer.bitCount(INT_CONSTANT_3);
 105     }
 106 
 107     @Test
 108     public void testBitCountIntConstant() {
 109         ValueNode result = parseAndInline("bitCountIntConstantSnippet");
 110         Assert.assertEquals(7, result.asJavaConstant().asInt());
 111     }
 112 
 113     public static int bitCountLongConstantSnippet() {
 114         return Long.bitCount(LONG_CONSTANT_1) + Long.bitCount(LONG_CONSTANT_2) + Long.bitCount(LONG_CONSTANT_3);
 115     }
 116 
 117     public static int bitCountIntSnippet(int v) {
 118         return Integer.bitCount(v & 0xFFFFFF | 0xFF);
 119     }
 120 
 121     @Test
 122     public void testBitCountInt() {
 123         Assume.assumeTrue(isBitCountIntrinsicSupported(getBackend().getTarget().arch));
 124         ValueNode result = parseAndInline("bitCountIntSnippet");
 125         Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 8, 24), result.stamp(NodeView.DEFAULT));
 126     }
 127 
 128     public static int bitCountIntEmptySnippet(int v) {
 129         return Integer.bitCount(v & 0xFFFFFF);
 130     }
 131 
 132     @Test
 133     public void testBitCountIntEmpty() {
 134         Assume.assumeTrue(isBitCountIntrinsicSupported(getBackend().getTarget().arch));
 135         ValueNode result = parseAndInline("bitCountIntEmptySnippet");
 136         Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 0, 24), result.stamp(NodeView.DEFAULT));
 137     }
 138 
 139     @Test
 140     public void testBitCountLongConstant() {
 141         ValueNode result = parseAndInline("bitCountLongConstantSnippet");
 142         Assert.assertEquals(7, result.asJavaConstant().asInt());
 143     }
 144 
 145     public static int bitCountLongSnippet(long v) {
 146         return Long.bitCount(v & 0xFFFFFFFFFFL | 0xFFL);
 147     }
 148 
 149     @Test
 150     public void testBitCountLong() {
 151         Assume.assumeTrue(isBitCountIntrinsicSupported(getBackend().getTarget().arch));
 152         ValueNode result = parseAndInline("bitCountLongSnippet");
 153         Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 8, 40), result.stamp(NodeView.DEFAULT));
 154     }
 155 
 156     public static int bitCountLongEmptySnippet(long v) {
 157         return Long.bitCount(v & 0xFFFFFFFFFFL);
 158     }
 159 
 160     @Test
 161     public void testBitCountLongEmpty() {
 162         Assume.assumeTrue(isBitCountIntrinsicSupported(getBackend().getTarget().arch));
 163         ValueNode result = parseAndInline("bitCountLongEmptySnippet");
 164         Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 0, 40), result.stamp(NodeView.DEFAULT));
 165     }
 166 
 167     /*
 168      * Tests for BitScanForwardNode
 169      */
 170 
 171     public static int scanForwardIntConstantSnippet() {
 172         return Integer.numberOfTrailingZeros(INT_CONSTANT_1) + Integer.numberOfTrailingZeros(INT_CONSTANT_2) + Integer.numberOfTrailingZeros(INT_CONSTANT_3);
 173     }
 174 
 175     @Test
 176     public void testScanForwardIntConstant() {
 177         ValueNode result = parseAndInline("scanForwardIntConstantSnippet");
 178         Assert.assertEquals(40, result.asJavaConstant().asInt());
 179     }
 180 
 181     public static int scanForwardIntSnippet(int v) {
 182         return Integer.numberOfTrailingZeros(v & 0xFFF0 | 0xFF00);
 183     }
 184 
 185     @Test
 186     public void testScanForwardInt() {
 187         ValueNode result = parseAndInline("scanForwardIntSnippet");
 188         Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 4, 8), result.stamp(NodeView.DEFAULT));
 189     }
 190 
 191     public static int scanForwardLongConstantSnippet() {
 192         return Long.numberOfTrailingZeros(LONG_CONSTANT_1) + Long.numberOfTrailingZeros(LONG_CONSTANT_2) + Long.numberOfTrailingZeros(LONG_CONSTANT_3);
 193     }
 194 
 195     @Test
 196     public void testScanForwardLongConstant() {
 197         ValueNode result = parseAndInline("scanForwardLongConstantSnippet");
 198         Assert.assertEquals(72, result.asJavaConstant().asInt());
 199     }
 200 
 201     public static int scanForwardLongSnippet(long v) {
 202         return Long.numberOfTrailingZeros(v & 0xFFFF000000L | 0xFF00000000L);
 203     }
 204 
 205     @Test
 206     public void testScanForwardLong() {
 207         ValueNode result = parseAndInline("scanForwardLongSnippet");
 208         Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 32), result.stamp(NodeView.DEFAULT));
 209     }
 210 
 211     public static int scanForwardLongEmptySnippet(long v) {
 212         int result = Long.numberOfTrailingZeros(v & 0xFFFF000000L);
 213         dummyField = result;
 214         return result;
 215     }
 216 
 217     @Test
 218     public void testScanForwardLongEmpty() {
 219         ValueNode result = parseAndInline("scanForwardLongEmptySnippet");
 220         Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 64), result.stamp(NodeView.DEFAULT));
 221     }
 222 
 223     /*
 224      * Tests for BitScanReverseNode
 225      */
 226 
 227     public static int scanReverseIntConstantSnippet() {
 228         return Integer.numberOfLeadingZeros(INT_CONSTANT_1) + Integer.numberOfLeadingZeros(INT_CONSTANT_2) + Integer.numberOfLeadingZeros(INT_CONSTANT_3);
 229     }
 230 
 231     @Test
 232     public void testScanReverseIntConstant() {
 233         ValueNode result = parseAndInline("scanReverseIntConstantSnippet");
 234         Assert.assertEquals(47, result.asJavaConstant().asInt());
 235     }
 236 
 237     public static int scanReverseIntSnippet(int v) {
 238         return Integer.numberOfLeadingZeros(v & 0xFFF0 | 0xFF0);
 239     }
 240 
 241     @Test
 242     public void testScanReverseInt() {
 243         /* This test isn't valid unless the BitScanReverseNode intrinsic is used. */
 244         ValueNode result = parseAndInline("scanReverseIntSnippet", BitScanReverseNode.class);
 245         if (result != null) {
 246             Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 16, 20), result.stamp(NodeView.DEFAULT));
 247         }
 248     }
 249 
 250     public static int scanReverseLongConstantSnippet() {
 251         return Long.numberOfLeadingZeros(LONG_CONSTANT_1) + Long.numberOfLeadingZeros(LONG_CONSTANT_2) + Long.numberOfLeadingZeros(LONG_CONSTANT_3);
 252     }
 253 
 254     @Test
 255     public void testScanReverseLongConstant() {
 256         ValueNode result = parseAndInline("scanReverseLongConstantSnippet");
 257         Assert.assertEquals(111, result.asJavaConstant().asInt());
 258     }
 259 
 260     public static int scanReverseLongSnippet(long v) {
 261         int result = Long.numberOfLeadingZeros(v & 0xFFF0);
 262         dummyField = result;
 263         return result;
 264     }
 265 
 266     @Test
 267     public void testScanReverseLong() {
 268         /* This test isn't valid unless the BitScanReverseNode intrinsic is used. */
 269         ValueNode result = parseAndInline("scanReverseLongSnippet", BitScanReverseNode.class);
 270         if (result != null) {
 271             Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 48, 64), result.stamp(NodeView.DEFAULT));
 272         }
 273     }
 274 
 275     public static int scanReverseLongEmptySnippet(long v) {
 276         int result = Long.numberOfLeadingZeros(v & 0xFFFF000000L);
 277         dummyField = result;
 278         return result;
 279     }
 280 
 281     @Test
 282     public void testScanReverseLongEmpty() {
 283         /* This test isn't valid unless the BitScanReverseNode intrinsic is used. */
 284         ValueNode result = parseAndInline("scanReverseLongEmptySnippet", BitScanReverseNode.class);
 285         if (result != null) {
 286             Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 64), result.stamp(NodeView.DEFAULT));
 287         }
 288     }
 289 
 290     private ValueNode parseAndInline(String name) {
 291         return parseAndInline(name, null);
 292     }
 293 
 294     /**
 295      * Parse and optimize {@code name}. If {@code expectedClass} is non-null and a node of that type
 296      * isn't found simply return null. Otherwise return the node returned by the graph.
 297      *
 298      * @param name
 299      * @param expectedClass
 300      * @return the returned value or null if {@code expectedClass} is not found in the graph.
 301      */
 302     private ValueNode parseAndInline(String name, Class<? extends ValueNode> expectedClass) {
 303         StructuredGraph graph = parseEager(name, AllowAssumptions.YES);
 304         HighTierContext context = getDefaultHighTierContext();
 305         CanonicalizerPhase canonicalizer = createCanonicalizerPhase();
 306         canonicalizer.apply(graph, context);
 307         createInliningPhase(canonicalizer).apply(graph, context);
 308         canonicalizer.apply(graph, context);
 309         Assert.assertEquals(1, graph.getNodes(ReturnNode.TYPE).count());
 310         if (expectedClass != null) {
 311             if (graph.getNodes().filter(expectedClass).count() == 0) {
 312                 return null;
 313             }
 314         }
 315         return graph.getNodes(ReturnNode.TYPE).first().result();
 316     }
 317 }