1 /*
   2  * Copyright (c) 2012, 2014, 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 package org.graalvm.compiler.replacements.test;
  24 
  25 import java.util.HashMap;
  26 
  27 import org.junit.Test;
  28 
  29 import org.graalvm.compiler.api.replacements.MethodSubstitution;
  30 import org.graalvm.compiler.nodes.IfNode;
  31 import org.graalvm.compiler.nodes.StructuredGraph;
  32 import org.graalvm.compiler.nodes.calc.AbsNode;
  33 import org.graalvm.compiler.nodes.calc.ReinterpretNode;
  34 import org.graalvm.compiler.replacements.nodes.BitCountNode;
  35 import org.graalvm.compiler.replacements.nodes.BitScanForwardNode;
  36 import org.graalvm.compiler.replacements.nodes.BitScanReverseNode;
  37 import org.graalvm.compiler.replacements.nodes.ReverseBytesNode;
  38 
  39 import jdk.vm.ci.code.InstalledCode;
  40 import jdk.vm.ci.meta.ResolvedJavaMethod;
  41 
  42 /**
  43  * Tests the VM independent {@link MethodSubstitution}s.
  44  */
  45 public class StandardMethodSubstitutionsTest extends MethodSubstitutionTest {
  46 
  47     @Test
  48     public void testMathSubstitutions() {
  49         assertInGraph(assertNotInGraph(testGraph("mathAbs"), IfNode.class), AbsNode.class);     // Java
  50         double value = 34567.891D;
  51         testGraph("mathCos");
  52         testGraph("mathLog");
  53         testGraph("mathLog10");
  54         testGraph("mathSin");
  55         testGraph("mathSqrt");
  56         testGraph("mathTan");
  57         testGraph("mathAll");
  58 
  59         test("mathCos", value);
  60         test("mathLog", value);
  61         test("mathLog10", value);
  62         test("mathSin", value);
  63         test("mathSqrt", value);
  64         test("mathTan", value);
  65         test("mathAll", value);
  66     }
  67 
  68     @Test
  69     public void testMathPow() {
  70         double a = 34567.891D;
  71         double b = 4.6D;
  72         test("mathPow", a, b);
  73 
  74         // Test the values directly handled by the substitution
  75 
  76         // If the second argument is positive or negative zero, then the result is 1.0.
  77         test("mathPow", a, 0.0D);
  78         test("mathPow", a, -0.0D);
  79         // If the second argument is 1.0, then the result is the same as the first argument.
  80         test("mathPow", a, 1.0D);
  81         // If the second argument is NaN, then the result is NaN.
  82         test("mathPow", a, Double.NaN);
  83         // If the first argument is NaN and the second argument is nonzero, then the result is NaN.
  84         test("mathPow", Double.NaN, b);
  85         test("mathPow", Double.NaN, 0.0D);
  86         // x**-1 = 1/x
  87         test("mathPow", a, -1.0D);
  88         // x**2 = x*x
  89         test("mathPow", a, 2.0D);
  90         // x**0.5 = sqrt(x)
  91         test("mathPow", a, 0.5D);
  92     }
  93 
  94     public static double mathPow(double a, double b) {
  95         return mathPow0(a, b);
  96     }
  97 
  98     public static double mathPow0(double a, double b) {
  99         return Math.pow(a, b);
 100     }
 101 
 102     public static double mathAbs(double value) {
 103         return Math.abs(value);
 104     }
 105 
 106     public static double mathSqrt(double value) {
 107         return Math.sqrt(value);
 108     }
 109 
 110     public static double mathLog(double value) {
 111         return Math.log(value);
 112     }
 113 
 114     public static double mathLog10(double value) {
 115         return Math.log10(value);
 116     }
 117 
 118     public static double mathSin(double value) {
 119         return Math.sin(value);
 120     }
 121 
 122     public static double mathCos(double value) {
 123         return Math.cos(value);
 124     }
 125 
 126     public static double mathTan(double value) {
 127         return Math.tan(value);
 128     }
 129 
 130     public static double mathAll(double value) {
 131         return Math.sqrt(value) + Math.log(value) + Math.log10(value) + Math.sin(value) + Math.cos(value) + Math.tan(value);
 132     }
 133 
 134     public void testSubstitution(String testMethodName, Class<?> intrinsicClass, Class<?> holder, String methodName, boolean optional, Object... args) {
 135         ResolvedJavaMethod realJavaMethod = getResolvedJavaMethod(holder, methodName);
 136         ResolvedJavaMethod testJavaMethod = getResolvedJavaMethod(testMethodName);
 137         StructuredGraph graph = testGraph(testMethodName);
 138 
 139         // Check to see if the resulting graph contains the expected node
 140         StructuredGraph replacement = getReplacements().getSubstitution(realJavaMethod, -1);
 141         if (replacement == null && !optional) {
 142             assertInGraph(graph, intrinsicClass);
 143         }
 144 
 145         for (Object l : args) {
 146             // Force compilation
 147             InstalledCode code = getCode(testJavaMethod);
 148             assert optional || code != null;
 149             // Verify that the original method and the substitution produce the same value
 150             Object expected = invokeSafe(realJavaMethod, null, l);
 151             assertDeepEquals(expected, invokeSafe(testJavaMethod, null, l));
 152             // Verify that the generated code and the original produce the same value
 153             assertDeepEquals(expected, executeVarargsSafe(code, l));
 154         }
 155     }
 156 
 157     @Test
 158     public void testCharSubstitutions() {
 159         Object[] args = new Character[]{Character.MIN_VALUE, (char) -1, (char) 0, (char) 1, Character.MAX_VALUE};
 160 
 161         testSubstitution("charReverseBytes", ReverseBytesNode.class, Character.class, "reverseBytes", false, args);
 162     }
 163 
 164     public static char charReverseBytes(char value) {
 165         return Character.reverseBytes(value);
 166     }
 167 
 168     @Test
 169     public void testCharSubstitutionsNarrowing() {
 170         Object[] args = new Integer[]{(int) Character.MIN_VALUE, -1, 0, 1, (int) Character.MAX_VALUE};
 171 
 172         for (Object arg : args) {
 173             test("charReverseBytesNarrowing", arg);
 174         }
 175     }
 176 
 177     public static char charReverseBytesNarrowing(int value) {
 178         return Character.reverseBytes((char) value);
 179     }
 180 
 181     @Test
 182     public void testShortSubstitutions() {
 183         Object[] args = new Short[]{Short.MIN_VALUE, -1, 0, 1, Short.MAX_VALUE};
 184 
 185         testSubstitution("shortReverseBytes", ReverseBytesNode.class, Short.class, "reverseBytes", false, args);
 186     }
 187 
 188     public static short shortReverseBytes(short value) {
 189         return Short.reverseBytes(value);
 190     }
 191 
 192     @Test
 193     public void testShortSubstitutionsNarrowing() {
 194         Object[] args = new Integer[]{(int) Short.MIN_VALUE, -1, 0, 1, (int) Short.MAX_VALUE};
 195 
 196         for (Object arg : args) {
 197             test("shortReverseBytesNarrowing", arg);
 198         }
 199     }
 200 
 201     public static short shortReverseBytesNarrowing(int value) {
 202         return Short.reverseBytes((short) value);
 203     }
 204 
 205     @Test
 206     public void testIntegerSubstitutions() {
 207         Object[] args = new Object[]{Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE};
 208 
 209         testSubstitution("integerReverseBytes", ReverseBytesNode.class, Integer.class, "reverseBytes", false, args);
 210         testSubstitution("integerNumberOfLeadingZeros", BitScanReverseNode.class, Integer.class, "numberOfLeadingZeros", true, args);
 211         testSubstitution("integerNumberOfTrailingZeros", BitScanForwardNode.class, Integer.class, "numberOfTrailingZeros", false, args);
 212         testSubstitution("integerBitCount", BitCountNode.class, Integer.class, "bitCount", true, args);
 213     }
 214 
 215     public static int integerReverseBytes(int value) {
 216         return Integer.reverseBytes(value);
 217     }
 218 
 219     public static int integerNumberOfLeadingZeros(int value) {
 220         return Integer.numberOfLeadingZeros(value);
 221     }
 222 
 223     public static int integerNumberOfTrailingZeros(int value) {
 224         return Integer.numberOfTrailingZeros(value);
 225     }
 226 
 227     public static int integerBitCount(int value) {
 228         return Integer.bitCount(value);
 229     }
 230 
 231     @Test
 232     public void testLongSubstitutions() {
 233         Object[] args = new Object[]{Long.MIN_VALUE, -1L, 0L, 1L, Long.MAX_VALUE};
 234 
 235         testSubstitution("longReverseBytes", ReverseBytesNode.class, Long.class, "reverseBytes", false, args);
 236         testSubstitution("longNumberOfLeadingZeros", BitScanReverseNode.class, Long.class, "numberOfLeadingZeros", true, args);
 237         testSubstitution("longNumberOfTrailingZeros", BitScanForwardNode.class, Long.class, "numberOfTrailingZeros", false, args);
 238         testSubstitution("longBitCount", BitCountNode.class, Long.class, "bitCount", true, args);
 239     }
 240 
 241     public static long longReverseBytes(long value) {
 242         return Long.reverseBytes(value);
 243     }
 244 
 245     public static int longNumberOfLeadingZeros(long value) {
 246         return Long.numberOfLeadingZeros(value);
 247     }
 248 
 249     public static int longNumberOfTrailingZeros(long value) {
 250         return Long.numberOfTrailingZeros(value);
 251     }
 252 
 253     public static int longBitCount(long value) {
 254         return Long.bitCount(value);
 255     }
 256 
 257     @Test
 258     public void testFloatSubstitutions() {
 259         assertInGraph(testGraph("floatToIntBits"), ReinterpretNode.class); // Java
 260         testGraph("intBitsToFloat");
 261     }
 262 
 263     public static int floatToIntBits(float value) {
 264         return Float.floatToIntBits(value);
 265     }
 266 
 267     public static float intBitsToFloat(int value) {
 268         return Float.intBitsToFloat(value);
 269     }
 270 
 271     @Test
 272     public void testDoubleSubstitutions() {
 273         assertInGraph(testGraph("doubleToLongBits"), ReinterpretNode.class); // Java
 274         testGraph("longBitsToDouble");
 275     }
 276 
 277     public static long doubleToLongBits(double value) {
 278         return Double.doubleToLongBits(value);
 279     }
 280 
 281     public static double longBitsToDouble(long value) {
 282         return Double.longBitsToDouble(value);
 283     }
 284 
 285     public static boolean isInstance(Class<?> clazz, Object object) {
 286         return clazz.isInstance(object);
 287     }
 288 
 289     public static boolean isInstance2(boolean cond, Object object) {
 290         Class<?> clazz;
 291         if (cond) {
 292             clazz = String.class;
 293         } else {
 294             clazz = java.util.HashMap.class;
 295         }
 296         return clazz.isInstance(object);
 297     }
 298 
 299     public static boolean isAssignableFrom(Class<?> clazz, Class<?> other) {
 300         return clazz.isAssignableFrom(other);
 301     }
 302 
 303     @Test
 304     public void testClassSubstitutions() {
 305         testGraph("isInstance");
 306         testGraph("isInstance2");
 307         testGraph("isAssignableFrom");
 308         for (Class<?> c : new Class<?>[]{getClass(), Cloneable.class, int[].class, String[][].class}) {
 309             for (Object o : new Object[]{this, new int[5], new String[2][], new Object()}) {
 310                 test("isInstance", c, o);
 311                 test("isAssignableFrom", c, o.getClass());
 312             }
 313         }
 314 
 315         test("isInstance2", true, null);
 316         test("isInstance2", false, null);
 317         test("isInstance2", true, "string");
 318         test("isInstance2", false, "string");
 319         test("isInstance2", true, new HashMap<>());
 320         test("isInstance2", false, new HashMap<>());
 321     }
 322 }