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
  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
  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
 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 
 163     public static BigInteger testMultiplyAux(BigInteger a, BigInteger b) {
 164         return a.multiply(b);
 165     }
 166 
 167     public static BigInteger testMontgomeryAux(BigInteger a, BigInteger exp, BigInteger b) {
 168         return a.modPow(exp, b);
 169     }
 170 
 171     private class TestIntrinsic {
 172 
 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 
 187         Object invokeJava(BigInteger big, Object... args) {
 188             return invokeSafe(javamethod, big, args);
 189         }
 190 
 191         Object invokeTest(Object... args) {
 192             return invokeSafe(testmethod, null, args);
 193         }
 194 
 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 
 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 
 228     private static BigInteger randomBig(int i) {
 229         return new BigInteger(rnd.nextInt(4096) + i2sz(i), rnd);
 230     }
 231 
 232     private static int i2sz(int i) {
 233         return i * 3 + 1;
 234     }
 235 }