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 }