1 /* 2 * Copyright (c) 2014, 2015, 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.function.Function; 26 27 import org.junit.Test; 28 29 import org.graalvm.compiler.api.replacements.ClassSubstitution; 30 import org.graalvm.compiler.api.replacements.MethodSubstitution; 31 import org.graalvm.compiler.bytecode.BytecodeProvider; 32 import org.graalvm.compiler.core.test.GraalCompilerTest; 33 import org.graalvm.compiler.graph.Node.ConstantNodeParameter; 34 import org.graalvm.compiler.graph.Node.NodeIntrinsic; 35 import org.graalvm.compiler.nodes.PiNode; 36 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; 37 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; 38 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins; 39 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Registration; 40 41 import jdk.vm.ci.meta.ResolvedJavaMethod; 42 43 /** 44 * Tests for expected behavior when parsing snippets and intrinsics. 45 */ 46 public class ReplacementsParseTest extends GraalCompilerTest { 47 48 @Override 49 protected Plugins getDefaultGraphBuilderPlugins() { 50 Plugins ret = super.getDefaultGraphBuilderPlugins(); 51 // manually register generated factory, jvmci service providers don't work from unit tests 52 new PluginFactory_ReplacementsParseTest().registerPlugins(ret.getInvocationPlugins(), null); 53 return ret; 54 } 55 56 private static final Object THROW_EXCEPTION_MARKER = new Object() { 57 @Override 58 public String toString() { 59 return "THROW_EXCEPTION_MARKER"; 60 } 61 }; 62 63 static class TestMethods { 64 static double next(double v) { 65 return Math.nextAfter(v, 1.0); 66 } 67 68 static double next2(double v) { 69 return Math.nextAfter(v, 1.0); 70 } 71 72 static double nextAfter(double x, double d) { 73 return Math.nextAfter(x, d); 74 } 75 76 static String stringize(Object obj) { 77 String res = String.valueOf(obj); 78 if (res.equals(THROW_EXCEPTION_MARKER.toString())) { 79 // Tests exception throwing from partial intrinsification 80 throw new RuntimeException("ex: " + obj); 81 } 82 return res; 83 } 84 85 static String identity(String s) { 86 return s; 87 } 88 } 89 90 @ClassSubstitution(TestMethods.class) 91 static class TestMethodsSubstitutions { 92 93 @MethodSubstitution(isStatic = true) 94 static double nextAfter(double x, double d) { 95 double xx = (x == -0.0 ? 0.0 : x); 96 return Math.nextAfter(xx, d); 97 } 98 99 /** 100 * Tests partial intrinsification. 101 */ 102 @MethodSubstitution 103 static String stringize(Object obj) { 104 if (obj != null && obj.getClass() == String.class) { 105 return asNonNullString(obj); 106 } else { 107 // A recursive call denotes exiting/deoptimizing 108 // out of the partial intrinsification to the 109 // slow/uncommon case. 110 return stringize(obj); 111 } 112 } 113 114 public static String asNonNullString(Object object) { 115 return asNonNullStringIntrinsic(object, String.class, true, true); 116 } 117 118 @NodeIntrinsic(PiNode.class) 119 private static native String asNonNullStringIntrinsic(Object object, @ConstantNodeParameter Class<?> toType, @ConstantNodeParameter boolean exactType, @ConstantNodeParameter boolean nonNull); 120 121 /** 122 * Tests that non-capturing lambdas are folded away. 123 */ 124 @MethodSubstitution 125 static String identity(String value) { 126 return apply(s -> s, value); 127 } 128 129 private static String apply(Function<String, String> f, String value) { 130 return f.apply(value); 131 } 132 } 133 134 @Override 135 protected GraphBuilderConfiguration editGraphBuilderConfiguration(GraphBuilderConfiguration conf) { 136 InvocationPlugins invocationPlugins = conf.getPlugins().getInvocationPlugins(); 137 BytecodeProvider replacementBytecodeProvider = getReplacements().getReplacementBytecodeProvider(); 138 Registration r = new Registration(invocationPlugins, TestMethods.class, replacementBytecodeProvider); 139 r.registerMethodSubstitution(TestMethodsSubstitutions.class, "nextAfter", double.class, double.class); 140 r.registerMethodSubstitution(TestMethodsSubstitutions.class, "stringize", Object.class); 141 if (replacementBytecodeProvider.supportsInvokedynamic()) { 142 r.registerMethodSubstitution(TestMethodsSubstitutions.class, "identity", String.class); 143 } 144 return super.editGraphBuilderConfiguration(conf); 145 } 146 147 /** 148 * Ensure that calling the original method from the substitution binds correctly. 149 */ 150 @Test 151 public void test1() { 152 test("test1Snippet", 1.0); 153 } 154 155 public double test1Snippet(double d) { 156 return TestMethods.next(d); 157 } 158 159 /** 160 * Ensure that calling the substitution method binds to the original method properly. 161 */ 162 @Test 163 public void test2() { 164 test("test2Snippet", 1.0); 165 } 166 167 public double test2Snippet(double d) { 168 return TestMethods.next2(d); 169 } 170 171 /** 172 * Ensure that substitution methods with assertions in them don't complain when the exception 173 * constructor is deleted. 174 */ 175 176 @Test 177 public void testNextAfter() { 178 double[] inArray = new double[1024]; 179 double[] outArray = new double[1024]; 180 for (int i = 0; i < inArray.length; i++) { 181 inArray[i] = -0.0; 182 } 183 test("doNextAfter", inArray, outArray); 184 } 185 186 public void doNextAfter(double[] outArray, double[] inArray) { 187 for (int i = 0; i < inArray.length; i++) { 188 double direction = (i & 1) == 0 ? Double.POSITIVE_INFINITY : -Double.NEGATIVE_INFINITY; 189 outArray[i] = TestMethods.nextAfter(inArray[i], direction); 190 } 191 } 192 193 @Test 194 public void testCallStringize() { 195 test("callStringize", "a string"); 196 test("callStringize", THROW_EXCEPTION_MARKER); 197 test("callStringize", Boolean.TRUE); 198 } 199 200 public static Object callStringize(Object obj) { 201 return TestMethods.stringize(obj); 202 } 203 204 @Test 205 public void testRootCompileStringize() { 206 ResolvedJavaMethod method = getResolvedJavaMethod(TestMethods.class, "stringize"); 207 test(method, null, "a string"); 208 test(method, null, Boolean.TRUE); 209 test(method, null, THROW_EXCEPTION_MARKER); 210 } 211 212 @Test 213 public void testLambda() { 214 test("callLambda", (String) null); 215 test("callLambda", "a string"); 216 } 217 218 public static String callLambda(String value) { 219 return TestMethods.identity(value); 220 } 221 }