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 */
23package org.graalvm.compiler.replacements.test;
24
25import java.util.HashMap;
26
27import org.junit.Test;
28
29import org.graalvm.compiler.api.replacements.MethodSubstitution;
30import org.graalvm.compiler.nodes.IfNode;
31import org.graalvm.compiler.nodes.StructuredGraph;
32import org.graalvm.compiler.nodes.calc.AbsNode;
33import org.graalvm.compiler.nodes.calc.ReinterpretNode;
34import org.graalvm.compiler.replacements.nodes.BitCountNode;
35import org.graalvm.compiler.replacements.nodes.BitScanForwardNode;
36import org.graalvm.compiler.replacements.nodes.BitScanReverseNode;
37import org.graalvm.compiler.replacements.nodes.ReverseBytesNode;
38
39import jdk.vm.ci.code.InstalledCode;
40import jdk.vm.ci.meta.ResolvedJavaMethod;
41
42/**
43 * Tests the VM independent {@link MethodSubstitution}s.
44 */
45public 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}
323