[constant-folding] More indy experiments

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Wed May 10 09:55:07 UTC 2017


Hi Tagir,
thanks for testing the prototype.

As you noticed, class literal support has been dropped; we're discussing 
the pros and cons of this internally, we will let you know the outcome 
of that discussion soon.

As for your issue, (1) is clearly a bug - albeit not sure if it's in the 
compiler or the BSM runtime - Vicente please take a look.

As for (2) - the extra boxing you are observing is caused by the fact 
that Intrinsic.invokedynamic is not (yet) a polymorphic signature method 
(note that the polysig annotation is mentioned in the JEP). When it will 
be, boxing will disappear. But I think that if we go down the polysig 
path, then an explicit cast for the return type would be necessary (as 
it is for i.e. MethodHandle.invokeExact).

Maurizio


On 10/05/17 07:33, Tagir Valeev wrote:
> Hello!
>
> I tried to make more sophisticated example with the latest code.
> Unfortunately seems that using Class as polyexpression is not supported
> anymore which adds more boilerplate to the code. Nevertheless here's my
> attempt and several issues I've found.
>
> The problem: make a dynamic callsite which multiplies two longs and returns
> BigInteger. At first it should optimistically think that multiplication of
> two longs will not overflow. However if it overflows once at given call
> site, it switches to the slower implementation.
>
> Here's the code (written by me, can be used under OCA terms if you wish to
> use it as testcase, etc.):
>
> import java.lang.invoke.*;
> import java.math.BigInteger;
> import java.lang.invoke.Constables.*;
>
> public class IndyTest {
> // library code starts
>    static class MultiplyCallSite extends MutableCallSite {
>      private static final MethodTypeConstant TYPE = MethodTypeConstant.of(
>        ClassConstant.of("Ljava/math/BigInteger;"), ClassConstant.of("J"),
> ClassConstant.of("J"));
>      private static final ClassConstant ME =
> ClassConstant.of("LIndyTest$MultiplyCallSite;");
>
>      private static final MethodHandle FAST =
>        Intrinsics.ldc(MethodHandleConstant.ofVirtual(ME, "fast", TYPE));
>      private static final MethodHandle SLOW =
>        Intrinsics.ldc(MethodHandleConstant.ofStatic(ME, "slow", TYPE));
>
>      MultiplyCallSite(MethodType type) {
>        super(type);
>        setTarget(FAST.bindTo(this).asType(type));
>      }
>
>      BigInteger fast(long a, long b) {
>        try {
>          return BigInteger.valueOf(Math.multiplyExact(a, b));
>        } catch (ArithmeticException ex) {
>          // switch to slower implementation
>          setTarget(SLOW.asType(type()));
>          return slow(a, b);
>        }
>      }
>
>      static BigInteger slow(long a, long b) {
>        return BigInteger.valueOf(a).multiply(BigInteger.valueOf(b));
>      }
>    }
>
>    public static final BootstrapSpecifier MULT =
> BootstrapSpecifier.of(MethodHandleConstant.ofStatic(
>      ClassConstant.of("LIndyTest;"), "multiplyFactory",
>
> MethodTypeConstant.of("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;")));
>
>    public static CallSite multiplyFactory(MethodHandles.Lookup lookup,
> String name, MethodType type) {
>      return new MultiplyCallSite(type);
>    }
> // library code ends
>
>    public static BigInteger multiplyIndy(long bigNum, long smallNum) {
>      try {
>        return ((BigInteger) Intrinsics.invokedynamic(MULT, " ", bigNum,
> bigNum))
>            .add((BigInteger) Intrinsics.invokedynamic(MULT, " ", smallNum,
> smallNum));
>      } catch (Throwable throwable) {
>        throw new InternalError(throwable);
>      }
>    }
>
>    public static void main(String[] args) {
>      System.out.println(multiplyIndy(5000000000L, 6));
>    }
> }
>
> ---
> This indeed works. A few problems though:
>
> 1). I don't need the name parameter of the factory method. When I pass
> there an empty string (not single space like in the code above), I have an
> incorrect bytecode:
>
> Error: LinkageError occurred while loading main class IndyTest
> java.lang.ClassFormatError: Illegal zero length constant pool entry at 91
> in class IndyTest
>
> I don't understand exactly what happens. Probably it's incorrectly encoded
> in constant pool or BSM does not allow empty string as name argument.
> Nevertheless it should either work or end up with a compilation error, not
> runtime error.
>
> Also I think that name is not always useful and it would be fine to be able
> to omit it (or hide inside BootstrapSpecifier like it was done before).
>
> 2). It boxes arguments unnecessarily. Here's start of multiplyIndy method
> bytecode:
>
> 0: lload_0
> 1: invokestatic  #4                  // Method
> java/lang/Long.valueOf:(J)Ljava/lang/Long;
> 4: lload_0
> 5: invokestatic  #4                  // Method
> java/lang/Long.valueOf:(J)Ljava/lang/Long;
> 8: invokedynamic #5,  0              // InvokeDynamic
> #0:"":(Ljava/lang/Long;Ljava/lang/Long;)Ljava/math/BigInteger;
>
> I'd expect to see here
>
> 0: lload_0
> 1: lload_0
> 2: invokedynamic #5,  0              // InvokeDynamic
> #0:"":(JJ)Ljava/math/BigInteger;
>
> 3). It does not merge equal BSM attributes. Resulting class has two of them:
>
> BootstrapMethods:
>    0: #61 REF_invokeStatic
> IndyTest.multiplyFactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
>      Method arguments:
>    1: #61 REF_invokeStatic
> IndyTest.multiplyFactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
>      Method arguments:
>
> I would expect to have only one.
>
> A side thought:
>
> Probably it would be good to bind BootstrapSpecifier to the return type to
> omit explicit casts of return value. The signature of invokedynamic would
> be then:
>
> static <T> T invokedynamic(BootstrapSpecifier<T> bsm, ...) {..}
>
> This way I would be able to write:
>
> // in library code
> public static final BootstrapSpecifier<BigInteger> MULT =
> BootstrapSpecifier.of(...);
>
> // in user code:
> return Intrinsics.invokedynamic(MULT, " ", bigNum, bigNum)
>            .add(Intrinsics.invokedynamic(MULT, " ", smallNum, smallNum));
>
> So this would be a library responsibility to specify an expected return
> type which would reduce possible errors in user code. Of course
> BootstrapSpecifier<?> could be used if specific return type is not
> applicable for some reason.
>
> Thank you for the great work!
> With best regards,
> Tagir Valeev.



More information about the amber-dev mailing list