Paul,

Thanks for the feedback! See my answers inline.

Updated webrev:
http://cr.openjdk.java.net/~vlivanov/8027827/final/webrev.01/

I finally figured out how to make caching work. This webrev contains these changes.

I changed LF representation a bit and added 2 auxiliary method handles - argument boxing and wrapping into Object[] and result unboxing. These operations depend on actual type and can't be shared among arbitrary combinators with the same basic type. They are used only during LF interpretation and are completely ignored in compiled LFs.

On 2/20/14 7:51 PM, Paul Sandoz wrote:
Hi Vladimir,

I know just enough about this area to be dangerous....


src/share/classes/java/lang/invoke/BoundMethodHandle.java

  865     private static final SpeciesData[] SPECIES_DATA_CACHE = new 
SpeciesData[4];


Only 3 elements are stored in the above array?
Fixed.




src/share/classes/java/lang/invoke/MethodHandleImpl.java

  634         // t_{i+2}:L=ValueConversions.unbox(t_{i+1}) OR 
ValueConversions.identity(t_{i+1})
  635         if (type.returnType().isPrimitive()) {
  636             names[UNBOX_RESULT] = new 
Name(ValueConversions.unbox(type.returnType()),
  637                                               names[TRY_CATCH]);
  638         } else {
  639             names[UNBOX_RESULT] = new Name(ValueConversions.identity(),
  640                                               names[TRY_CATCH]);
  641         }


You could create the form without the identity transform for the non-primitive 
case?

   final int UNBOX_RESULT = type.returnType().isPrimitive() ? nameCursor++ : 0;
...

   if (UNBOX_RESULT > 0) { ...
       names[UNBOX_RESULT] = new 
Name(ValueConversions.unbox(type.returnType()), names[TRY_CATCH]);
   }
I can, but it complicates matching and compiling the pattern in InvokerBytecodeGenerator. I decided to keep the shape uniform for all cases.



  699         try {
  700             GUARD_WITH_CATCH
  701                     = IMPL_LOOKUP.findStatic(MethodHandleImpl.class, 
"guardWithCatch",
  702                     MethodType.methodType(Object.class, 
MethodHandle.class, Class.class, MethodHandle.class, Object[].class));
  703         } catch (ReflectiveOperationException ex) {
  704             throw new RuntimeException(ex);
  705         }
  706         return GUARD_WITH_CATCH;


Should #704 be:

   throw newInternalError(ex);

?
Fixed. Also updated other places where RuntimeException was thrown.



test/java/lang/invoke/MethodHandles/TestCatchException.java

  100         Object o = new Object();
  101         Object[] obj1 = new Object[] { "str" };
  102
  103         Object r1 = target.invokeExact(o, o, o, o, o, o, o, o, obj1);
  104         Object r2 = gwc.invokeExact(o, o, o, o, o, o, o, o, obj1);
  105         assertTrue(r1 != null);
  106         assertTrue(r1.equals(r2));

To be on the safe side should probably assert as follows:

   assertEquals(r1, obj);
   assertEquals(r2, obj);
Fixed.

Best regards,
Vladimir Ivanov

Paul.


On Feb 19, 2014, at 10:46 PM, Vladimir Ivanov <[email protected]> 
wrote:

http://cr.openjdk.java.net/~vlivanov/8027827/final/webrev.00
https://jbs.oracle.com/bugs/browse/JDK-8027827
354 lines changed: 193 ins; 91 del; 70 mod

OVERVIEW

MethodHandles.catchException combinator implementation is based on generic 
invokers (MethodHandleImpl$GuardWithCatch.invoke_*). It is significantly slower 
than a Java equivalent (try-catch).

Future Nashorn performance improvements require catchException combinator speed 
to be on par with try-catch in Java.

So, it should be represented in a more efficient form.

I chose the following lambda form representation:

  t_{i}:L=ValueConversions.array(a1:L,...,a_{k}:L);
t_{i+1}:L=MethodHandleImpl.guardWithCatch(t_{p1}, t_{p2}, t_{p3}, t_{i}:L);
t_{i+2}:I=ValueConversions.unbox(t7:L);
     OR :L=ValueConversions.identity(t_{n+1})
     OR :V=ValueConversions.ignore(t_{n+1})

where:
  a1, ..., a_{k} - arguments
  t_{p1}, t_{p2}, t_{p3} - target method handle, exception class, catcher 
method handle respectively; passed as bounded parameters;

During lambda form compilation it is converted into bytecode equivalent of the 
following Java code:
  try {
      return target.invokeBasic(...);
  } catch(Throwable e) {
      if (!exClass.isInstance(e)) throw e;
      return catcher.invokeBasic(e, ...);
  }

There's a set of microbenchmarks (attached to the bug) I wrote to verify 
performance characteristics of new implementation.

FURTHER WORK

What is missing is lambda form caching. The plan is to have a single lambda 
form per basic type, but it needs more work - current representation is 
suitable for sharing on bytecode level, but lambda form interpretation doesn't 
work well (arguments boxing + result unboxing are problematic).

TESTING

Tests: microbenchmarks, jdk/java/lang/invoke/, nashorn with optimistic types 
(unit tests, octane).

Tested in 2 modes:
  * COMPILE_THRESHOLD=30
  * COMPILE_THRESHOLD=0 -Xverify:all

OTHER

1) Update of cached name and member in LF compilation loop (see 
InvokerBytecodeGenerator.generateCustomizedCodeBytes) fixes a bug during 
compilation of selectAlternative when running with COMPILE_THRESHOLD=0.

2) As part of this change, I fix existing bug [1], so I add regression test for 
it.

Thanks!

Best regards,
Vladimir Ivanov

[1] https://bugs.openjdk.java.net/browse/JDK-8034120

Reply via email to