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