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