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