001/*
002 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.replacements.test;
024
025import java.util.function.*;
026
027import jdk.internal.jvmci.meta.*;
028
029import org.junit.*;
030
031import com.oracle.graal.api.replacements.*;
032import com.oracle.graal.api.runtime.*;
033import com.oracle.graal.compiler.test.*;
034import com.oracle.graal.graph.Node.ConstantNodeParameter;
035import com.oracle.graal.graph.Node.NodeIntrinsic;
036import com.oracle.graal.nodes.*;
037import com.oracle.graal.nodes.spi.*;
038import com.oracle.graal.runtime.*;
039
040/**
041 * Tests for expected behavior when parsing snippets and intrinsics.
042 */
043public class ReplacementsParseTest extends GraalCompilerTest {
044
045    private static final Object THROW_EXCEPTION_MARKER = new Object() {
046        @Override
047        public String toString() {
048            return "THROW_EXCEPTION_MARKER";
049        }
050    };
051
052    static class TestMethods {
053        static double next(double v) {
054            return Math.nextAfter(v, 1.0);
055        }
056
057        static double next2(double v) {
058            return Math.nextAfter(v, 1.0);
059        }
060
061        static double nextAfter(double x, double d) {
062            return Math.nextAfter(x, d);
063        }
064
065        static String stringize(Object obj) {
066            String res = String.valueOf(obj);
067            if (res.equals(THROW_EXCEPTION_MARKER.toString())) {
068                // Tests exception throwing from partial intrinsification
069                throw new RuntimeException("ex: " + obj);
070            }
071            return res;
072        }
073
074        static String identity(String s) {
075            return s;
076        }
077    }
078
079    @ClassSubstitution(TestMethods.class)
080    static class TestMethodsSubstitutions {
081
082        @MethodSubstitution(isStatic = true)
083        static double nextAfter(double x, double d) {
084            double xx = (x == -0.0 ? 0.0 : x);
085            return Math.nextAfter(xx, d);
086        }
087
088        /**
089         * Tests partial intrinsification.
090         */
091        @MethodSubstitution
092        static String stringize(Object obj) {
093            if (obj != null && obj.getClass() == String.class) {
094                return asNonNullString(obj);
095            } else {
096                // A recursive call denotes exiting/deoptimizing
097                // out of the partial intrinsification to the
098                // slow/uncommon case.
099                return stringize(obj);
100            }
101        }
102
103        public static String asNonNullString(Object object) {
104            return asNonNullStringIntrinsic(object, String.class, true, true);
105        }
106
107        @NodeIntrinsic(PiNode.class)
108        private static native String asNonNullStringIntrinsic(Object object, @ConstantNodeParameter Class<?> toType, @ConstantNodeParameter boolean exactType, @ConstantNodeParameter boolean nonNull);
109
110        /**
111         * Tests that non-capturing lambdas are folded away.
112         */
113        @MethodSubstitution
114        static String identity(String value) {
115            return apply(s -> s, value);
116        }
117
118        private static String apply(Function<String, String> f, String value) {
119            return f.apply(value);
120        }
121    }
122
123    private static boolean substitutionsInstalled;
124
125    public ReplacementsParseTest() {
126        if (!substitutionsInstalled) {
127            Replacements replacements = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend().getProviders().getReplacements();
128            replacements.registerSubstitutions(TestMethods.class, TestMethodsSubstitutions.class);
129            substitutionsInstalled = true;
130        }
131    }
132
133    /**
134     * Ensure that calling the original method from the substitution binds correctly.
135     */
136    @Test
137    public void test1() {
138        test("test1Snippet", 1.0);
139    }
140
141    public double test1Snippet(double d) {
142        return TestMethods.next(d);
143    }
144
145    /**
146     * Ensure that calling the substitution method binds to the original method properly.
147     */
148    @Test
149    public void test2() {
150        test("test2Snippet", 1.0);
151    }
152
153    public double test2Snippet(double d) {
154        return TestMethods.next2(d);
155    }
156
157    /**
158     * Ensure that substitution methods with assertions in them don't complain when the exception
159     * constructor is deleted.
160     */
161
162    @Test
163    public void testNextAfter() {
164        double[] inArray = new double[1024];
165        double[] outArray = new double[1024];
166        for (int i = 0; i < inArray.length; i++) {
167            inArray[i] = -0.0;
168        }
169        test("doNextAfter", inArray, outArray);
170    }
171
172    public void doNextAfter(double[] outArray, double[] inArray) {
173        for (int i = 0; i < inArray.length; i++) {
174            double direction = (i & 1) == 0 ? Double.POSITIVE_INFINITY : -Double.NEGATIVE_INFINITY;
175            outArray[i] = TestMethods.nextAfter(inArray[i], direction);
176        }
177    }
178
179    @Test
180    public void testCallStringize() {
181        test("callStringize", "a string");
182        test("callStringize", THROW_EXCEPTION_MARKER);
183        test("callStringize", Boolean.TRUE);
184    }
185
186    public static Object callStringize(Object obj) {
187        return TestMethods.stringize(obj);
188    }
189
190    @Test
191    public void testRootCompileStringize() {
192        ResolvedJavaMethod method = getResolvedJavaMethod(TestMethods.class, "stringize");
193        test(method, null, "a string");
194        test(method, null, Boolean.TRUE);
195        test(method, null, THROW_EXCEPTION_MARKER);
196    }
197
198    @Test
199    public void testLambda() {
200        test("callLambda", (String) null);
201        test("callLambda", "a string");
202    }
203
204    public static String callLambda(String value) {
205        return TestMethods.identity(value);
206    }
207}