[constant-folding] More indy experiments

Vicente Romero vicente.romero at oracle.com
Wed May 10 15:49:31 UTC 2017


Hi Tagir,

Thanks for the test and the bug report. Did you signed the OCA?

On 05/10/2017 05:55 AM, Maurizio Cimadamore wrote:
> 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.

I have just pushed: 
http://hg.openjdk.java.net/amber/amber/langtools/rev/31adff18583d, which 
takes care of issuing a compiler error if an empty string is passed as 
the value of the invocationName argument for invokedynamic(). I'm also 
investigating the reason for 3)

>
> 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

Thanks,
Vicente

>
>
> 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