1 /*
2  * Copyright (c) 2018, 2019, 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 
26 package org.graalvm.compiler.hotspot.test;
27 
28 import java.lang.reflect.InvocationTargetException;
29 import java.math.BigInteger;
30 import java.util.Random;
31 
32 import org.graalvm.compiler.api.test.Graal;
33 import org.graalvm.compiler.core.test.GraalCompilerTest;
34 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
35 import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
36 import org.graalvm.compiler.runtime.RuntimeProvider;
37 
38 import org.junit.Test;
39 
40 import jdk.vm.ci.amd64.AMD64;
41 import jdk.vm.ci.code.InstalledCode;
42 import jdk.vm.ci.code.InvalidInstalledCodeException;
43 import jdk.vm.ci.meta.ResolvedJavaMethod;
44 
45 /*
46  * Indirectly test intrinsic/call substitutions for (innate) methods:
47  *
48  *      BigInteger.implMultiplyToLen
49  *      BigInteger.implMulAdd
50  *      BigInteger.implMontgomeryMultiply
51  *      BigInteger.implMontgomerySquare
52  *      BigInteger.implSquareToLen
53  *
54  * via BigInteger.multiply() and .modPow(). Note that the actual substitution
55  * is not tested per se (only execution based on admissible intrinsics).
56  *
57  */
58 public final class BigIntegerIntrinsicsTest extends GraalCompilerTest {
59 
60     static final int N = 100;
61 
62     @Test
testMultiplyToLen()63     public void testMultiplyToLen() throws ClassNotFoundException {
64 
65         // Intrinsic must be available.
66         org.junit.Assume.assumeTrue(config.useMultiplyToLenIntrinsic());
67         // Test case is (currently) AMD64 only.
68         org.junit.Assume.assumeTrue(getTarget().arch instanceof AMD64);
69 
70         Class<?> javaclass = Class.forName("java.math.BigInteger");
71 
72         TestIntrinsic tin = new TestIntrinsic("testMultiplyAux", javaclass,
73                         "multiply", BigInteger.class);
74 
75         for (int i = 0; i < N; i++) {
76 
77             BigInteger big1 = randomBig(i);
78             BigInteger big2 = randomBig(i);
79 
80             // Invoke BigInteger BigInteger.multiply(BigInteger)
81             BigInteger res1 = (BigInteger) tin.invokeJava(big1, big2);
82 
83             // Invoke BigInteger testMultiplyAux(BigInteger)
84             BigInteger res2 = (BigInteger) tin.invokeTest(big1, big2);
85 
86             assertDeepEquals(res1, res2);
87 
88             // Invoke BigInteger testMultiplyAux(BigInteger) through code handle.
89             BigInteger res3 = (BigInteger) tin.invokeCode(big1, big2);
90 
91             assertDeepEquals(res1, res3);
92         }
93     }
94 
95     @Test
testMulAdd()96     public void testMulAdd() throws ClassNotFoundException {
97 
98         // Intrinsic must be available.
99         org.junit.Assume.assumeTrue(config.useMulAddIntrinsic() ||
100                         config.useSquareToLenIntrinsic());
101         // Test case is (currently) AMD64 only.
102         org.junit.Assume.assumeTrue(getTarget().arch instanceof AMD64);
103 
104         Class<?> javaclass = Class.forName("java.math.BigInteger");
105 
106         TestIntrinsic tin = new TestIntrinsic("testMultiplyAux", javaclass,
107                         "multiply", BigInteger.class);
108 
109         for (int i = 0; i < N; i++) {
110 
111             BigInteger big1 = randomBig(i);
112 
113             // Invoke BigInteger BigInteger.multiply(BigInteger)
114             BigInteger res1 = (BigInteger) tin.invokeJava(big1, big1);
115 
116             // Invoke BigInteger testMultiplyAux(BigInteger)
117             BigInteger res2 = (BigInteger) tin.invokeTest(big1, big1);
118 
119             assertDeepEquals(res1, res2);
120 
121             // Invoke BigInteger testMultiplyAux(BigInteger) through code handle.
122             BigInteger res3 = (BigInteger) tin.invokeCode(big1, big1);
123 
124             assertDeepEquals(res1, res3);
125         }
126     }
127 
128     @Test
testMontgomery()129     public void testMontgomery() throws ClassNotFoundException {
130 
131         // Intrinsic must be available.
132         org.junit.Assume.assumeTrue(config.useMontgomeryMultiplyIntrinsic() ||
133                         config.useMontgomerySquareIntrinsic());
134         // Test case is (currently) AMD64 only.
135         org.junit.Assume.assumeTrue(getTarget().arch instanceof AMD64);
136 
137         Class<?> javaclass = Class.forName("java.math.BigInteger");
138 
139         TestIntrinsic tin = new TestIntrinsic("testMontgomeryAux", javaclass,
140                         "modPow", BigInteger.class, BigInteger.class);
141 
142         for (int i = 0; i < N; i++) {
143 
144             BigInteger big1 = randomBig(i);
145             BigInteger big2 = randomBig(i);
146 
147             // Invoke BigInteger BigInteger.modPow(BigExp, BigInteger)
148             BigInteger res1 = (BigInteger) tin.invokeJava(big1, bigTwo, big2);
149 
150             // Invoke BigInteger testMontgomeryAux(BigInteger, BigExp, BigInteger)
151             BigInteger res2 = (BigInteger) tin.invokeTest(big1, bigTwo, big2);
152 
153             assertDeepEquals(res1, res2);
154 
155             // Invoke BigInteger testMontgomeryAux(BigInteger, BigExp, BigInteger)
156             // through code handle.
157             BigInteger res3 = (BigInteger) tin.invokeCode(big1, bigTwo, big2);
158 
159             assertDeepEquals(res1, res3);
160         }
161     }
162 
testMultiplyAux(BigInteger a, BigInteger b)163     public static BigInteger testMultiplyAux(BigInteger a, BigInteger b) {
164         return a.multiply(b);
165     }
166 
testMontgomeryAux(BigInteger a, BigInteger exp, BigInteger b)167     public static BigInteger testMontgomeryAux(BigInteger a, BigInteger exp, BigInteger b) {
168         return a.modPow(exp, b);
169     }
170 
171     private class TestIntrinsic {
172 
TestIntrinsic(String testmname, Class<?> javaclass, String javamname, Class<?>... params)173         TestIntrinsic(String testmname, Class<?> javaclass, String javamname, Class<?>... params) {
174             javamethod = getResolvedJavaMethod(javaclass, javamname, params);
175             testmethod = getResolvedJavaMethod(testmname);
176 
177             assert javamethod != null;
178             assert testmethod != null;
179 
180             // Force the test method to be compiled.
181             testcode = getCode(testmethod);
182 
183             assert testcode != null;
184             assert testcode.isValid();
185         }
186 
invokeJava(BigInteger big, Object... args)187         Object invokeJava(BigInteger big, Object... args) {
188             return invokeSafe(javamethod, big, args);
189         }
190 
invokeTest(Object... args)191         Object invokeTest(Object... args) {
192             return invokeSafe(testmethod, null, args);
193         }
194 
invokeCode(Object... args)195         Object invokeCode(Object... args) {
196             try {
197                 return testcode.executeVarargs(args);
198             } catch (InvalidInstalledCodeException e) {
199                 // Ensure the installed code is valid, possibly recompiled.
200                 testcode = getCode(testmethod);
201 
202                 assert testcode != null;
203                 assert testcode.isValid();
204 
205                 return invokeCode(args);
206             }
207         }
208 
invokeSafe(ResolvedJavaMethod method, Object receiver, Object... args)209         private Object invokeSafe(ResolvedJavaMethod method, Object receiver, Object... args) {
210             try {
211                 return invoke(method, receiver, args);
212             } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException | InstantiationException e) {
213                 throw new RuntimeException(e);
214             }
215         }
216 
217         // Private data section:
218         private ResolvedJavaMethod javamethod;
219         private ResolvedJavaMethod testmethod;
220         private InstalledCode testcode;
221     }
222 
223     private static GraalHotSpotVMConfig config = ((HotSpotGraalRuntimeProvider) Graal.getRequiredCapability(RuntimeProvider.class)).getVMConfig();
224 
225     private static BigInteger bigTwo = BigInteger.valueOf(2);
226     private static Random rnd = new Random(17);
227 
randomBig(int i)228     private static BigInteger randomBig(int i) {
229         return new BigInteger(rnd.nextInt(4096) + i2sz(i), rnd);
230     }
231 
i2sz(int i)232     private static int i2sz(int i) {
233         return i * 3 + 1;
234     }
235 }
236