1 /*
2  * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
3  * Copyright (c) 2015, Red Hat Inc. All rights reserved.
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 only, as
8  * published by the Free Software Foundation.
9  *
10  * This code is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13  * version 2 for more details (a copy is included in the LICENSE file that
14  * accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License version
17  * 2 along with this work; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19  *
20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21  * or visit www.oracle.com if you need additional information or have any
22  * questions.
23  */
24 
25 /**
26  * @test
27  * @bug 8130150 8131779 8139907
28  * @summary Verify that the Montgomery multiply and square intrinsic works and correctly checks their arguments.
29  * @requires vm.flavor == "server" & !vm.emulatedClient
30  * @modules java.base/jdk.internal.misc:open
31  * @modules java.base/java.math:open
32  * @library /test/lib /
33  *
34  * @build sun.hotspot.WhiteBox
35  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
36  *                                sun.hotspot.WhiteBox$WhiteBoxPermission
37  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
38  *      compiler.intrinsics.bigInteger.MontgomeryMultiplyTest
39  */
40 
41 package compiler.intrinsics.bigInteger;
42 
43 import jdk.test.lib.Platform;
44 import sun.hotspot.WhiteBox;
45 import compiler.whitebox.CompilerWhiteBoxTest;
46 
47 import java.lang.invoke.MethodHandle;
48 import java.lang.invoke.MethodHandles;
49 import java.lang.reflect.Constructor;
50 import java.lang.reflect.Executable;
51 import java.lang.reflect.Field;
52 import java.lang.reflect.Method;
53 import java.math.BigInteger;
54 import java.util.Arrays;
55 import java.util.Random;
56 
57 public class MontgomeryMultiplyTest {
58 
59     private static final WhiteBox wb = WhiteBox.getWhiteBox();
60 
61     static final MethodHandles.Lookup lookup = MethodHandles.lookup();
62 
63     static final MethodHandle montgomeryMultiplyHandle, montgomerySquareHandle;
64     static final MethodHandle bigIntegerConstructorHandle;
65     static final Field bigIntegerMagField;
66 
67     static {
68        // Use reflection to gain access to the methods we want to test.
69         try {
70             Method m = BigInteger.class.getDeclaredMethod("montgomeryMultiply",
71                 /*a*/int[].class, /*b*/int[].class, /*n*/int[].class, /*len*/int.class,
72                 /*inv*/long.class, /*product*/int[].class);
73             m.setAccessible(true);
74             montgomeryMultiplyHandle = lookup.unreflect(m);
75 
76             m = BigInteger.class.getDeclaredMethod("montgomerySquare",
77                 /*a*/int[].class, /*n*/int[].class, /*len*/int.class,
78                 /*inv*/long.class, /*product*/int[].class);
79             m.setAccessible(true);
80             montgomerySquareHandle = lookup.unreflect(m);
81 
82             Constructor c
83                 = BigInteger.class.getDeclaredConstructor(int.class, int[].class);
84             c.setAccessible(true);
85             bigIntegerConstructorHandle = lookup.unreflectConstructor(c);
86 
87             bigIntegerMagField = BigInteger.class.getDeclaredField("mag");
88             bigIntegerMagField.setAccessible(true);
89 
90         } catch (Throwable ex) {
91             throw new RuntimeException(ex);
92         }
93     }
94 
95     /* Obtain executable for the intrinsics tested. Depending on the
96      * value of 'isMultiply', the executable corresponding to either
97      * implMontgomerMultiply or implMontgomerySqure is returned. */
getExecutable(boolean isMultiply)98     static Executable getExecutable(boolean isMultiply) throws RuntimeException {
99         try {
100             Class aClass = Class.forName("java.math.BigInteger");
101             Method aMethod;
102             if (isMultiply) {
103                 aMethod = aClass.getDeclaredMethod("implMontgomeryMultiply",
104                                                    int[].class,
105                                                    int[].class,
106                                                    int[].class,
107                                                    int.class,
108                                                    long.class,
109                                                    int[].class);
110             } else {
111                 aMethod = aClass.getDeclaredMethod("implMontgomerySquare",
112                                                    int[].class,
113                                                    int[].class,
114                                                    int.class,
115                                                    long.class,
116                                                    int[].class);
117             }
118             return aMethod;
119         } catch (NoSuchMethodException e) {
120             throw new RuntimeException("Test bug, method is unavailable. " + e);
121         } catch (ClassNotFoundException e) {
122             throw new RuntimeException("Test bug, class is unavailable. " + e);
123         }
124     }
125 
126     // Invoke either BigInteger.montgomeryMultiply or BigInteger.montgomerySquare.
montgomeryMultiply(int[] a, int[] b, int[] n, int len, long inv, int[] product)127     int[] montgomeryMultiply(int[] a, int[] b, int[] n, int len, long inv,
128                              int[] product) throws Throwable {
129         int[] result =
130             (a == b) ? (int[]) montgomerySquareHandle.invokeExact(a, n, len, inv, product)
131                      : (int[]) montgomeryMultiplyHandle.invokeExact(a, b, n, len, inv, product);
132         return Arrays.copyOf(result, len);
133     }
134 
135     // Invoke the private constructor BigInteger(int[]).
newBigInteger(int[] val)136     BigInteger newBigInteger(int[] val) throws Throwable {
137         return (BigInteger) bigIntegerConstructorHandle.invokeExact(1, val);
138     }
139 
140     // Get the private field BigInteger.mag
mag(BigInteger n)141     int[] mag(BigInteger n) {
142         try {
143             return (int[]) bigIntegerMagField.get(n);
144         } catch (Exception ex) {
145             throw new RuntimeException(ex);
146         }
147     }
148 
149     // Montgomery multiplication
150     // Calculate a * b * r^-1 mod n)
151     //
152     // R is a power of the word size
153     // N' = R^-1 mod N
154     //
155     // T := ab
156     // m := (T mod R)N' mod R [so 0 <= m < R]
157     // t := (T + mN)/R
158     // if t >= N then return t - N else return t
159     //
montgomeryMultiply(BigInteger a, BigInteger b, BigInteger N, int len, BigInteger n_prime)160     BigInteger montgomeryMultiply(BigInteger a, BigInteger b, BigInteger N,
161             int len, BigInteger n_prime)
162             throws Throwable {
163         BigInteger T = a.multiply(b);
164         BigInteger R = BigInteger.ONE.shiftLeft(len*32);
165         BigInteger mask = R.subtract(BigInteger.ONE);
166         BigInteger m = (T.and(mask)).multiply(n_prime);
167         m = m.and(mask); // i.e. m.mod(R)
168         T = T.add(m.multiply(N));
169         T = T.shiftRight(len*32); // i.e. T.divide(R)
170         if (T.compareTo(N) > 0) {
171             T = T.subtract(N);
172         }
173         return T;
174     }
175 
176     // Call the Montgomery multiply intrinsic.
montgomeryMultiply(int[] a_words, int[] b_words, int[] n_words, int len, BigInteger inv)177     BigInteger montgomeryMultiply(int[] a_words, int[] b_words, int[] n_words,
178             int len, BigInteger inv)
179             throws Throwable {
180         BigInteger t = montgomeryMultiply(
181                 newBigInteger(a_words),
182                 newBigInteger(b_words),
183                 newBigInteger(n_words),
184                 len, inv);
185         return t;
186     }
187 
188     // Check that the Montgomery multiply intrinsic returns the same
189     // result as the longhand calculation.
check(int[] a_words, int[] b_words, int[] n_words, int len, BigInteger inv)190     void check(int[] a_words, int[] b_words, int[] n_words, int len, BigInteger inv)
191             throws Throwable {
192         BigInteger n = newBigInteger(n_words);
193         BigInteger slow = montgomeryMultiply(a_words, b_words, n_words, len, inv);
194         BigInteger fast
195             = newBigInteger(montgomeryMultiply
196                             (a_words, b_words, n_words, len, inv.longValue(), null));
197         // The intrinsic may not return the same value as the longhand
198         // calculation but they must have the same residue mod N.
199         if (!slow.mod(n).equals(fast.mod(n))) {
200             throw new RuntimeException();
201         }
202     }
203 
204     Random rnd = new Random(0);
205 
206     // Return a random value of length <= bits in an array of even length
random_val(int bits)207     int[] random_val(int bits) {
208         int len = (bits+63)/64;  // i.e. length in longs
209         int[] val = new int[len*2];
210         for (int i = 0; i < val.length; i++)
211             val[i] = rnd.nextInt();
212         int leadingZeros = 64 - (bits & 64);
213         if (leadingZeros >= 32) {
214             val[0] = 0;
215             val[1] &= ~(-1l << (leadingZeros & 31));
216         } else {
217             val[0] &= ~(-1l << leadingZeros);
218         }
219         return val;
220     }
221 
testOneLength(int lenInBits, int lenInInts)222     void testOneLength(int lenInBits, int lenInInts) throws Throwable {
223         BigInteger mod = new BigInteger(lenInBits, 2, rnd);
224         BigInteger r = BigInteger.ONE.shiftLeft(lenInInts * 32);
225         BigInteger n_prime = mod.modInverse(r).negate();
226 
227         // Make n.length even, padding with a zero if necessary
228         int[] n = mag(mod);
229         if (n.length < lenInInts) {
230             int[] x = new int[lenInInts];
231             System.arraycopy(n, 0, x, lenInInts-n.length, n.length);
232             n = x;
233         }
234 
235         for (int i = 0; i < 10000; i++) {
236             // multiply
237             check(random_val(lenInBits), random_val(lenInBits), n, lenInInts, n_prime);
238             // square
239             int[] tmp = random_val(lenInBits);
240             check(tmp, tmp, n, lenInInts, n_prime);
241         }
242     }
243 
244     // Test the Montgomery multiply intrinsic with a bunch of random
245     // values of varying lengths.  Do this for long enough that the
246     // caller of the intrinsic is C2-compiled.
testResultValues()247     void testResultValues() throws Throwable {
248         // Test a couple of interesting edge cases.
249         testOneLength(1024, 32);
250         testOneLength(1025, 34);
251         for (int j = 10; j > 0; j--) {
252             // Construct a random prime whose length in words is even
253             int lenInBits = rnd.nextInt(2048) + 64;
254             int lenInInts = (lenInBits + 63)/64*2;
255             testOneLength(lenInBits, lenInInts);
256         }
257     }
258 
259     // Range checks
testOneMontgomeryMultiplyCheck(int[] a, int[] b, int[] n, int len, long inv, int[] product, Class klass)260     void testOneMontgomeryMultiplyCheck(int[] a, int[] b, int[] n, int len, long inv,
261                                         int[] product, Class klass) {
262         try {
263             montgomeryMultiply(a, b, n, len, inv, product);
264         } catch (Throwable ex) {
265             if (klass.isAssignableFrom(ex.getClass()))
266                 return;
267             throw new RuntimeException(klass + " expected, " + ex + " was thrown");
268         }
269         throw new RuntimeException(klass + " expected, was not thrown");
270     }
271 
testOneMontgomeryMultiplyCheck(int[] a, int[] b, BigInteger n, int len, BigInteger inv, Class klass)272     void testOneMontgomeryMultiplyCheck(int[] a, int[] b, BigInteger n, int len, BigInteger inv,
273             Class klass) {
274         testOneMontgomeryMultiplyCheck(a, b, mag(n), len, inv.longValue(), null, klass);
275     }
276 
testOneMontgomeryMultiplyCheck(int[] a, int[] b, BigInteger n, int len, BigInteger inv, int[] product, Class klass)277     void testOneMontgomeryMultiplyCheck(int[] a, int[] b, BigInteger n, int len, BigInteger inv,
278             int[] product, Class klass) {
279         testOneMontgomeryMultiplyCheck(a, b, mag(n), len, inv.longValue(), product, klass);
280     }
281 
testMontgomeryMultiplyChecks()282     void testMontgomeryMultiplyChecks() {
283         int[] blah = random_val(40);
284         int[] small = random_val(39);
285         BigInteger mod = new BigInteger(40*32 , 2, rnd);
286         BigInteger r = BigInteger.ONE.shiftLeft(40*32);
287         BigInteger n_prime = mod.modInverse(r).negate();
288 
289         // Length out of range: square
290         testOneMontgomeryMultiplyCheck(blah, blah, mod, 41, n_prime, IllegalArgumentException.class);
291         testOneMontgomeryMultiplyCheck(blah, blah, mod, 0, n_prime, IllegalArgumentException.class);
292         testOneMontgomeryMultiplyCheck(blah, blah, mod, -1, n_prime, IllegalArgumentException.class);
293         // As above, but for multiply
294         testOneMontgomeryMultiplyCheck(blah, blah.clone(), mod, 41, n_prime, IllegalArgumentException.class);
295         testOneMontgomeryMultiplyCheck(blah, blah.clone(), mod, 0, n_prime, IllegalArgumentException.class);
296         testOneMontgomeryMultiplyCheck(blah, blah.clone(), mod, 0, n_prime, IllegalArgumentException.class);
297 
298         // Length odd
299         testOneMontgomeryMultiplyCheck(small, small, mod, 39, n_prime, IllegalArgumentException.class);
300         testOneMontgomeryMultiplyCheck(small, small, mod, 0, n_prime, IllegalArgumentException.class);
301         testOneMontgomeryMultiplyCheck(small, small, mod, -1, n_prime, IllegalArgumentException.class);
302         // As above, but for multiply
303         testOneMontgomeryMultiplyCheck(small, small.clone(), mod, 39, n_prime, IllegalArgumentException.class);
304         testOneMontgomeryMultiplyCheck(small, small.clone(), mod, 0, n_prime, IllegalArgumentException.class);
305         testOneMontgomeryMultiplyCheck(small, small.clone(), mod, -1, n_prime, IllegalArgumentException.class);
306 
307         // array too small
308         testOneMontgomeryMultiplyCheck(blah, blah, mod, 40, n_prime, small, IllegalArgumentException.class);
309         testOneMontgomeryMultiplyCheck(blah, blah.clone(), mod, 40, n_prime, small, IllegalArgumentException.class);
310         testOneMontgomeryMultiplyCheck(small, blah, mod, 40, n_prime, blah, IllegalArgumentException.class);
311         testOneMontgomeryMultiplyCheck(blah, small, mod, 40, n_prime, blah, IllegalArgumentException.class);
312         testOneMontgomeryMultiplyCheck(blah, blah, mod, 40, n_prime, small, IllegalArgumentException.class);
313         testOneMontgomeryMultiplyCheck(small, small, mod, 40, n_prime, blah, IllegalArgumentException.class);
314     }
315 
main(String args[])316     public static void main(String args[]) {
317         if (!Platform.isServer() || Platform.isEmulatedClient()) {
318             throw new Error("TESTBUG: Not server mode");
319         }
320         if (wb.isIntrinsicAvailable(getExecutable(true), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) &&
321                 wb.isIntrinsicAvailable(getExecutable(false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION)) {
322             try {
323                 new MontgomeryMultiplyTest().testMontgomeryMultiplyChecks();
324                 new MontgomeryMultiplyTest().testResultValues();
325             } catch (Throwable ex) {
326                 throw new RuntimeException(ex);
327             }
328         }
329     }
330 }
331