Path: blob/master/test/hotspot/jtreg/compiler/intrinsics/bigInteger/MontgomeryMultiplyTest.java
41153 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* Copyright (c) 2015, Red Hat Inc. 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 it6* under the terms of the GNU General Public License version 2 only, as7* published by the Free Software Foundation.8*9* This code is distributed in the hope that it will be useful, but WITHOUT10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License12* version 2 for more details (a copy is included in the LICENSE file that13* accompanied this code).14*15* You should have received a copy of the GNU General Public License version16* 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 USA20* or visit www.oracle.com if you need additional information or have any21* questions.22*/2324/**25* @test26* @bug 8130150 8131779 813990727* @summary Verify that the Montgomery multiply and square intrinsic works and correctly checks their arguments.28* @requires vm.flavor == "server" & !vm.emulatedClient29* @modules java.base/jdk.internal.misc:open30* @modules java.base/java.math:open31* @library /test/lib /32*33* @build sun.hotspot.WhiteBox34* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox35* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI36* compiler.intrinsics.bigInteger.MontgomeryMultiplyTest37*/3839package compiler.intrinsics.bigInteger;4041import jdk.test.lib.Platform;42import sun.hotspot.WhiteBox;43import compiler.whitebox.CompilerWhiteBoxTest;4445import java.lang.invoke.MethodHandle;46import java.lang.invoke.MethodHandles;47import java.lang.reflect.Constructor;48import java.lang.reflect.Executable;49import java.lang.reflect.Field;50import java.lang.reflect.Method;51import java.math.BigInteger;52import java.util.Arrays;53import java.util.Random;5455public class MontgomeryMultiplyTest {5657private static final WhiteBox wb = WhiteBox.getWhiteBox();5859static final MethodHandles.Lookup lookup = MethodHandles.lookup();6061static final MethodHandle montgomeryMultiplyHandle, montgomerySquareHandle;62static final MethodHandle bigIntegerConstructorHandle;63static final Field bigIntegerMagField;6465static {66// Use reflection to gain access to the methods we want to test.67try {68Method m = BigInteger.class.getDeclaredMethod("montgomeryMultiply",69/*a*/int[].class, /*b*/int[].class, /*n*/int[].class, /*len*/int.class,70/*inv*/long.class, /*product*/int[].class);71m.setAccessible(true);72montgomeryMultiplyHandle = lookup.unreflect(m);7374m = BigInteger.class.getDeclaredMethod("montgomerySquare",75/*a*/int[].class, /*n*/int[].class, /*len*/int.class,76/*inv*/long.class, /*product*/int[].class);77m.setAccessible(true);78montgomerySquareHandle = lookup.unreflect(m);7980Constructor c81= BigInteger.class.getDeclaredConstructor(int.class, int[].class);82c.setAccessible(true);83bigIntegerConstructorHandle = lookup.unreflectConstructor(c);8485bigIntegerMagField = BigInteger.class.getDeclaredField("mag");86bigIntegerMagField.setAccessible(true);8788} catch (Throwable ex) {89throw new RuntimeException(ex);90}91}9293/* Obtain executable for the intrinsics tested. Depending on the94* value of 'isMultiply', the executable corresponding to either95* implMontgomerMultiply or implMontgomerySqure is returned. */96static Executable getExecutable(boolean isMultiply) throws RuntimeException {97try {98Class aClass = Class.forName("java.math.BigInteger");99Method aMethod;100if (isMultiply) {101aMethod = aClass.getDeclaredMethod("implMontgomeryMultiply",102int[].class,103int[].class,104int[].class,105int.class,106long.class,107int[].class);108} else {109aMethod = aClass.getDeclaredMethod("implMontgomerySquare",110int[].class,111int[].class,112int.class,113long.class,114int[].class);115}116return aMethod;117} catch (NoSuchMethodException e) {118throw new RuntimeException("Test bug, method is unavailable. " + e);119} catch (ClassNotFoundException e) {120throw new RuntimeException("Test bug, class is unavailable. " + e);121}122}123124// Invoke either BigInteger.montgomeryMultiply or BigInteger.montgomerySquare.125int[] montgomeryMultiply(int[] a, int[] b, int[] n, int len, long inv,126int[] product) throws Throwable {127int[] result =128(a == b) ? (int[]) montgomerySquareHandle.invokeExact(a, n, len, inv, product)129: (int[]) montgomeryMultiplyHandle.invokeExact(a, b, n, len, inv, product);130return Arrays.copyOf(result, len);131}132133// Invoke the private constructor BigInteger(int[]).134BigInteger newBigInteger(int[] val) throws Throwable {135return (BigInteger) bigIntegerConstructorHandle.invokeExact(1, val);136}137138// Get the private field BigInteger.mag139int[] mag(BigInteger n) {140try {141return (int[]) bigIntegerMagField.get(n);142} catch (Exception ex) {143throw new RuntimeException(ex);144}145}146147// Montgomery multiplication148// Calculate a * b * r^-1 mod n)149//150// R is a power of the word size151// N' = R^-1 mod N152//153// T := ab154// m := (T mod R)N' mod R [so 0 <= m < R]155// t := (T + mN)/R156// if t >= N then return t - N else return t157//158BigInteger montgomeryMultiply(BigInteger a, BigInteger b, BigInteger N,159int len, BigInteger n_prime)160throws Throwable {161BigInteger T = a.multiply(b);162BigInteger R = BigInteger.ONE.shiftLeft(len*32);163BigInteger mask = R.subtract(BigInteger.ONE);164BigInteger m = (T.and(mask)).multiply(n_prime);165m = m.and(mask); // i.e. m.mod(R)166T = T.add(m.multiply(N));167T = T.shiftRight(len*32); // i.e. T.divide(R)168if (T.compareTo(N) > 0) {169T = T.subtract(N);170}171return T;172}173174// Call the Montgomery multiply intrinsic.175BigInteger montgomeryMultiply(int[] a_words, int[] b_words, int[] n_words,176int len, BigInteger inv)177throws Throwable {178BigInteger t = montgomeryMultiply(179newBigInteger(a_words),180newBigInteger(b_words),181newBigInteger(n_words),182len, inv);183return t;184}185186// Check that the Montgomery multiply intrinsic returns the same187// result as the longhand calculation.188void check(int[] a_words, int[] b_words, int[] n_words, int len, BigInteger inv)189throws Throwable {190BigInteger n = newBigInteger(n_words);191BigInteger slow = montgomeryMultiply(a_words, b_words, n_words, len, inv);192BigInteger fast193= newBigInteger(montgomeryMultiply194(a_words, b_words, n_words, len, inv.longValue(), null));195// The intrinsic may not return the same value as the longhand196// calculation but they must have the same residue mod N.197if (!slow.mod(n).equals(fast.mod(n))) {198throw new RuntimeException();199}200}201202Random rnd = new Random(42);203204// Return a random value of length <= bits in an array of even length205int[] random_val(int bits) {206int len = (bits+63)/64; // i.e. length in longs207int[] val = new int[len*2];208for (int i = 0; i < val.length; i++)209val[i] = rnd.nextInt();210int leadingZeros = 64 - (bits & 64);211if (leadingZeros >= 32) {212val[0] = 0;213val[1] &= ~(-1l << (leadingZeros & 31));214} else {215val[0] &= ~(-1l << leadingZeros);216}217return val;218}219220void testOneLength(int lenInBits, int lenInInts) throws Throwable {221BigInteger mod = new BigInteger(lenInBits, 2, rnd);222BigInteger r = BigInteger.ONE.shiftLeft(lenInInts * 32);223BigInteger n_prime = mod.modInverse(r).negate();224225// Make n.length even, padding with a zero if necessary226int[] n = mag(mod);227if (n.length < lenInInts) {228int[] x = new int[lenInInts];229System.arraycopy(n, 0, x, lenInInts-n.length, n.length);230n = x;231}232233for (int i = 0; i < 10000; i++) {234// multiply235check(random_val(lenInBits), random_val(lenInBits), n, lenInInts, n_prime);236// square237int[] tmp = random_val(lenInBits);238check(tmp, tmp, n, lenInInts, n_prime);239}240}241242// Test the Montgomery multiply intrinsic with a bunch of random243// values of varying lengths. Do this for long enough that the244// caller of the intrinsic is C2-compiled.245void testResultValues() throws Throwable {246// Test a couple of interesting edge cases.247testOneLength(1024, 32);248testOneLength(1025, 34);249for (int j = 10; j > 0; j--) {250// Construct a random prime whose length in words is even251int lenInBits = rnd.nextInt(2048) + 64;252int lenInInts = (lenInBits + 63)/64*2;253testOneLength(lenInBits, lenInInts);254}255}256257// Range checks258void testOneMontgomeryMultiplyCheck(int[] a, int[] b, int[] n, int len, long inv,259int[] product, Class klass) {260try {261montgomeryMultiply(a, b, n, len, inv, product);262} catch (Throwable ex) {263if (klass.isAssignableFrom(ex.getClass()))264return;265throw new RuntimeException(klass + " expected, " + ex + " was thrown");266}267throw new RuntimeException(klass + " expected, was not thrown");268}269270void testOneMontgomeryMultiplyCheck(int[] a, int[] b, BigInteger n, int len, BigInteger inv,271Class klass) {272testOneMontgomeryMultiplyCheck(a, b, mag(n), len, inv.longValue(), null, klass);273}274275void testOneMontgomeryMultiplyCheck(int[] a, int[] b, BigInteger n, int len, BigInteger inv,276int[] product, Class klass) {277testOneMontgomeryMultiplyCheck(a, b, mag(n), len, inv.longValue(), product, klass);278}279280void testMontgomeryMultiplyChecks() {281int[] blah = random_val(40);282int[] small = random_val(39);283BigInteger mod = new BigInteger(40*32 , 2, rnd);284BigInteger r = BigInteger.ONE.shiftLeft(40*32);285BigInteger n_prime = mod.modInverse(r).negate();286287// Length out of range: square288testOneMontgomeryMultiplyCheck(blah, blah, mod, 41, n_prime, IllegalArgumentException.class);289testOneMontgomeryMultiplyCheck(blah, blah, mod, 0, n_prime, IllegalArgumentException.class);290testOneMontgomeryMultiplyCheck(blah, blah, mod, -1, n_prime, IllegalArgumentException.class);291// As above, but for multiply292testOneMontgomeryMultiplyCheck(blah, blah.clone(), mod, 41, n_prime, IllegalArgumentException.class);293testOneMontgomeryMultiplyCheck(blah, blah.clone(), mod, 0, n_prime, IllegalArgumentException.class);294testOneMontgomeryMultiplyCheck(blah, blah.clone(), mod, 0, n_prime, IllegalArgumentException.class);295296// Length odd297testOneMontgomeryMultiplyCheck(small, small, mod, 39, n_prime, IllegalArgumentException.class);298testOneMontgomeryMultiplyCheck(small, small, mod, 0, n_prime, IllegalArgumentException.class);299testOneMontgomeryMultiplyCheck(small, small, mod, -1, n_prime, IllegalArgumentException.class);300// As above, but for multiply301testOneMontgomeryMultiplyCheck(small, small.clone(), mod, 39, n_prime, IllegalArgumentException.class);302testOneMontgomeryMultiplyCheck(small, small.clone(), mod, 0, n_prime, IllegalArgumentException.class);303testOneMontgomeryMultiplyCheck(small, small.clone(), mod, -1, n_prime, IllegalArgumentException.class);304305// array too small306testOneMontgomeryMultiplyCheck(blah, blah, mod, 40, n_prime, small, IllegalArgumentException.class);307testOneMontgomeryMultiplyCheck(blah, blah.clone(), mod, 40, n_prime, small, IllegalArgumentException.class);308testOneMontgomeryMultiplyCheck(small, blah, mod, 40, n_prime, blah, IllegalArgumentException.class);309testOneMontgomeryMultiplyCheck(blah, small, mod, 40, n_prime, blah, IllegalArgumentException.class);310testOneMontgomeryMultiplyCheck(blah, blah, mod, 40, n_prime, small, IllegalArgumentException.class);311testOneMontgomeryMultiplyCheck(small, small, mod, 40, n_prime, blah, IllegalArgumentException.class);312}313314public static void main(String args[]) {315if (!Platform.isServer() || Platform.isEmulatedClient()) {316throw new Error("TESTBUG: Not server mode");317}318if (wb.isIntrinsicAvailable(getExecutable(true), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) &&319wb.isIntrinsicAvailable(getExecutable(false), CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION)) {320try {321new MontgomeryMultiplyTest().testMontgomeryMultiplyChecks();322new MontgomeryMultiplyTest().testResultValues();323} catch (Throwable ex) {324throw new RuntimeException(ex);325}326}327}328}329330331