changeset 14963:b7da29e8b7f1

8143211: provide bytecode intrinsics for loop and try/finally executors Reviewed-by: psandoz, redestad, vlivanov
author mhaupt
date Mon, 04 Jul 2016 10:08:18 +0200
parents ef8766bfb88d
children 167660ada3b1
files src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java src/java.base/share/classes/java/lang/invoke/LambdaForm.java src/java.base/share/classes/java/lang/invoke/LambdaFormBuffer.java src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java src/java.base/share/classes/java/lang/invoke/MemberName.java src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java src/java.base/share/classes/java/lang/invoke/MethodHandles.java src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java test/java/lang/invoke/MethodHandlesTest.java
diffstat 10 files changed, 843 insertions(+), 297 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java	Mon Jul 04 13:00:15 2016 +0900
+++ b/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java	Mon Jul 04 10:08:18 2016 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,17 +26,16 @@
 package java.lang.invoke;
 
 import jdk.internal.loader.BootLoader;
-import jdk.internal.vm.annotation.Stable;
 import jdk.internal.org.objectweb.asm.ClassWriter;
 import jdk.internal.org.objectweb.asm.FieldVisitor;
 import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.vm.annotation.Stable;
 import sun.invoke.util.ValueConversions;
 import sun.invoke.util.Wrapper;
 
 import java.lang.invoke.LambdaForm.NamedFunction;
 import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.reflect.Field;
-import java.util.Arrays;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -168,12 +167,16 @@
     }
 
     @Override
-    final Object internalValues() {
-        Object[] boundValues = new Object[speciesData().fieldCount()];
-        for (int i = 0; i < boundValues.length; ++i) {
-            boundValues[i] = arg(i);
+    final String internalValues() {
+        int count = speciesData().fieldCount();
+        if (count == 1) {
+            return "[" + arg(0) + "]";
         }
-        return Arrays.asList(boundValues);
+        StringBuilder sb = new StringBuilder("[");
+        for (int i = 0; i < count; ++i) {
+            sb.append("\n  ").append(i).append(": ( ").append(arg(i)).append(" )");
+        }
+        return sb.append("\n]").toString();
     }
 
     /*non-public*/ final Object arg(int i) {
@@ -869,7 +872,7 @@
      */
     static final SpeciesData SPECIES_DATA = SpeciesData.EMPTY;
 
-    private static final SpeciesData[] SPECIES_DATA_CACHE = new SpeciesData[5];
+    private static final SpeciesData[] SPECIES_DATA_CACHE = new SpeciesData[6];
     private static SpeciesData checkCache(int size, String types) {
         int idx = size - 1;
         SpeciesData data = SPECIES_DATA_CACHE[idx];
@@ -877,9 +880,10 @@
         SPECIES_DATA_CACHE[idx] = data = getSpeciesData(types);
         return data;
     }
-    static SpeciesData speciesData_L()     { return checkCache(1, "L"); }
-    static SpeciesData speciesData_LL()    { return checkCache(2, "LL"); }
-    static SpeciesData speciesData_LLL()   { return checkCache(3, "LLL"); }
-    static SpeciesData speciesData_LLLL()  { return checkCache(4, "LLLL"); }
-    static SpeciesData speciesData_LLLLL() { return checkCache(5, "LLLLL"); }
+    static SpeciesData speciesData_L()      { return checkCache(1, "L"); }
+    static SpeciesData speciesData_LL()     { return checkCache(2, "LL"); }
+    static SpeciesData speciesData_LLL()    { return checkCache(3, "LLL"); }
+    static SpeciesData speciesData_LLLL()   { return checkCache(4, "LLLL"); }
+    static SpeciesData speciesData_LLLLL()  { return checkCache(5, "LLLLL"); }
+    static SpeciesData speciesData_LLLLLL() { return checkCache(6, "LLLLLL"); }
 }
--- a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Mon Jul 04 13:00:15 2016 +0900
+++ b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Mon Jul 04 10:08:18 2016 +0200
@@ -25,22 +25,30 @@
 
 package java.lang.invoke;
 
-import java.io.*;
-import java.util.*;
-import java.lang.reflect.Modifier;
-
-import jdk.internal.org.objectweb.asm.*;
-
-import static java.lang.invoke.LambdaForm.*;
-import static java.lang.invoke.LambdaForm.BasicType.*;
-import static java.lang.invoke.MethodHandleStatics.*;
-import static java.lang.invoke.MethodHandleNatives.Constants.*;
-
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Label;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
 import sun.invoke.util.VerifyAccess;
 import sun.invoke.util.VerifyType;
 import sun.invoke.util.Wrapper;
 import sun.reflect.misc.ReflectUtil;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import static java.lang.invoke.LambdaForm.*;
+import static java.lang.invoke.LambdaForm.BasicType.*;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
+import static java.lang.invoke.MethodHandleStatics.*;
+
 /**
  * Code generation backend for LambdaForm.
  * <p>
@@ -75,8 +83,8 @@
     private final MethodType invokerType;
 
     /** Info about local variables in compiled lambda form */
-    private final int[]       localsMap;    // index
-    private final Class<?>[]  localClasses; // type
+    private int[]       localsMap;    // index
+    private Class<?>[]  localClasses; // type
 
     /** ASM bytecode generation. */
     private ClassWriter cw;
@@ -101,8 +109,7 @@
         this.lambdaForm = lambdaForm;
         this.invokerName = invokerName;
         this.invokerType = invokerType;
-        this.localsMap = new int[localsMapSize+1];
-        // last entry of localsMap is count of allocated local slots
+        this.localsMap = new int[localsMapSize+1]; // last entry of localsMap is count of allocated local slots
         this.localClasses = new Class<?>[localsMapSize+1];
     }
 
@@ -131,7 +138,6 @@
         }
     }
 
-
     /** instance counters for dumped classes */
     private static final HashMap<String,Integer> DUMP_CLASS_FILES_COUNTERS;
     /** debugging flag for saving generated class files */
@@ -177,7 +183,6 @@
                 }
             });
         }
-
     }
 
     private static String makeDumpableClassName(String className) {
@@ -280,14 +285,11 @@
 
     private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) {
         MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic);
-        //System.out.println("resolveInvokerMember => "+member);
-        //for (Method m : invokerClass.getDeclaredMethods())  System.out.println("  "+m);
         try {
             member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member, HOST_CLASS, ReflectiveOperationException.class);
         } catch (ReflectiveOperationException e) {
             throw newInternalError(e);
         }
-        //System.out.println("resolveInvokerMember => "+member);
         return member;
     }
 
@@ -541,11 +543,12 @@
         Name writeBack = null;  // local to write back result
         if (arg instanceof Name) {
             Name n = (Name) arg;
-            if (assertStaticType(cls, n))
-                return;  // this cast was already performed
             if (lambdaForm.useCount(n) > 1) {
                 // This guy gets used more than once.
                 writeBack = n;
+                if (assertStaticType(cls, n)) {
+                    return; // this cast was already performed
+                }
             }
         }
         if (isStaticallyNameable(cls)) {
@@ -679,19 +682,29 @@
             MethodHandleImpl.Intrinsic intr = name.function.intrinsicName();
             switch (intr) {
                 case SELECT_ALTERNATIVE:
-                    assert isSelectAlternative(i);
+                    assert lambdaForm.isSelectAlternative(i);
                     if (PROFILE_GWT) {
                         assert(name.arguments[0] instanceof Name &&
-                               nameRefersTo((Name)name.arguments[0], MethodHandleImpl.class, "profileBoolean"));
+                                ((Name)name.arguments[0]).refersTo(MethodHandleImpl.class, "profileBoolean"));
                         mv.visitAnnotation(INJECTEDPROFILE_SIG, true);
                     }
                     onStack = emitSelectAlternative(name, lambdaForm.names[i+1]);
                     i++;  // skip MH.invokeBasic of the selectAlternative result
                     continue;
                 case GUARD_WITH_CATCH:
-                    assert isGuardWithCatch(i);
+                    assert lambdaForm.isGuardWithCatch(i);
                     onStack = emitGuardWithCatch(i);
-                    i = i+2; // Jump to the end of GWC idiom
+                    i += 2; // jump to the end of GWC idiom
+                    continue;
+                case TRY_FINALLY:
+                    assert lambdaForm.isTryFinally(i);
+                    onStack = emitTryFinally(i);
+                    i += 2; // jump to the end of the TF idiom
+                    continue;
+                case LOOP:
+                    assert lambdaForm.isLoop(i);
+                    onStack = emitLoop(i);
+                    i += 2; // jump to the end of the LOOP idiom
                     continue;
                 case NEW_ARRAY:
                     Class<?> rtype = name.function.methodType().returnType();
@@ -763,7 +776,7 @@
      * Emit an invoke for the given name.
      */
     void emitInvoke(Name name) {
-        assert(!isLinkerMethodInvoke(name));  // should use the static path for these
+        assert(!name.isLinkerMethodInvoke());  // should use the static path for these
         if (true) {
             // push receiver
             MethodHandle target = name.function.resolvedHandle();
@@ -952,84 +965,6 @@
     }
 
     /**
-     * Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}.
-     */
-    private boolean memberRefersTo(MemberName member, Class<?> declaringClass, String name) {
-        return member != null &&
-               member.getDeclaringClass() == declaringClass &&
-               member.getName().equals(name);
-    }
-    private boolean nameRefersTo(Name name, Class<?> declaringClass, String methodName) {
-        return name.function != null &&
-               memberRefersTo(name.function.member(), declaringClass, methodName);
-    }
-
-    /**
-     * Check if MemberName is a call to MethodHandle.invokeBasic.
-     */
-    private boolean isInvokeBasic(Name name) {
-        if (name.function == null)
-            return false;
-        if (name.arguments.length < 1)
-            return false;  // must have MH argument
-        MemberName member = name.function.member();
-        return memberRefersTo(member, MethodHandle.class, "invokeBasic") &&
-               !member.isPublic() && !member.isStatic();
-    }
-
-    /**
-     * Check if MemberName is a call to MethodHandle.linkToStatic, etc.
-     */
-    private boolean isLinkerMethodInvoke(Name name) {
-        if (name.function == null)
-            return false;
-        if (name.arguments.length < 1)
-            return false;  // must have MH argument
-        MemberName member = name.function.member();
-        return member != null &&
-               member.getDeclaringClass() == MethodHandle.class &&
-               !member.isPublic() && member.isStatic() &&
-               member.getName().startsWith("linkTo");
-    }
-
-    /**
-     * Check if i-th name is a call to MethodHandleImpl.selectAlternative.
-     */
-    private boolean isSelectAlternative(int pos) {
-        // selectAlternative idiom:
-        //   t_{n}:L=MethodHandleImpl.selectAlternative(...)
-        //   t_{n+1}:?=MethodHandle.invokeBasic(t_{n}, ...)
-        if (pos+1 >= lambdaForm.names.length)  return false;
-        Name name0 = lambdaForm.names[pos];
-        Name name1 = lambdaForm.names[pos+1];
-        return nameRefersTo(name0, MethodHandleImpl.class, "selectAlternative") &&
-               isInvokeBasic(name1) &&
-               name1.lastUseIndex(name0) == 0 &&        // t_{n+1}:?=MethodHandle.invokeBasic(t_{n}, ...)
-               lambdaForm.lastUseIndex(name0) == pos+1; // t_{n} is local: used only in t_{n+1}
-    }
-
-    /**
-     * Check if i-th name is a start of GuardWithCatch idiom.
-     */
-    private boolean isGuardWithCatch(int pos) {
-        // GuardWithCatch idiom:
-        //   t_{n}:L=MethodHandle.invokeBasic(...)
-        //   t_{n+1}:L=MethodHandleImpl.guardWithCatch(*, *, *, t_{n});
-        //   t_{n+2}:?=MethodHandle.invokeBasic(t_{n+1})
-        if (pos+2 >= lambdaForm.names.length)  return false;
-        Name name0 = lambdaForm.names[pos];
-        Name name1 = lambdaForm.names[pos+1];
-        Name name2 = lambdaForm.names[pos+2];
-        return nameRefersTo(name1, MethodHandleImpl.class, "guardWithCatch") &&
-               isInvokeBasic(name0) &&
-               isInvokeBasic(name2) &&
-               name1.lastUseIndex(name0) == 3 &&          // t_{n+1}:L=MethodHandleImpl.guardWithCatch(*, *, *, t_{n});
-               lambdaForm.lastUseIndex(name0) == pos+1 && // t_{n} is local: used only in t_{n+1}
-               name2.lastUseIndex(name1) == 1 &&          // t_{n+2}:?=MethodHandle.invokeBasic(t_{n+1})
-               lambdaForm.lastUseIndex(name1) == pos+2;   // t_{n+1} is local: used only in t_{n+2}
-    }
-
-    /**
      * Emit bytecode for the selectAlternative idiom.
      *
      * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest):
@@ -1155,6 +1090,329 @@
         return result;
     }
 
+    /**
+     * Emit bytecode for the tryFinally idiom.
+     * <p>
+     * The pattern looks like (Cf. MethodHandleImpl.makeTryFinally):
+     * <blockquote><pre>{@code
+     * // a0: BMH
+     * // a1: target, a2: cleanup
+     * // a3: box, a4: unbox
+     * // a5 (and following): arguments
+     * tryFinally=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L)=>{
+     *   t6:L=MethodHandle.invokeBasic(a3:L,a5:L);         // box the arguments into an Object[]
+     *   t7:L=MethodHandleImpl.tryFinally(a1:L,a2:L,t6:L); // call the tryFinally executor
+     *   t8:L=MethodHandle.invokeBasic(a4:L,t7:L);t8:L}    // unbox the result; return the result
+     * }</pre></blockquote>
+     * <p>
+     * It is compiled into bytecode equivalent to the following code:
+     * <blockquote><pre>{@code
+     * Throwable t;
+     * Object r;
+     * try {
+     *     r = a1.invokeBasic(a5);
+     * } catch (Throwable thrown) {
+     *     t = thrown;
+     *     throw t;
+     * } finally {
+     *     r = a2.invokeBasic(t, r, a5);
+     * }
+     * return r;
+     * }</pre></blockquote>
+     * <p>
+     * Specifically, the bytecode will have the following form (the stack effects are given for the beginnings of
+     * blocks, and for the situations after executing the given instruction - the code will have a slightly different
+     * shape if the return type is {@code void}):
+     * <blockquote><pre>{@code
+     * TRY:                 (--)
+     *                      load target                             (-- target)
+     *                      load args                               (-- args... target)
+     *                      INVOKEVIRTUAL MethodHandle.invokeBasic  (depends)
+     * FINALLY_NORMAL:      (-- r)
+     *                      load cleanup                            (-- cleanup r)
+     *                      SWAP                                    (-- r cleanup)
+     *                      ACONST_NULL                             (-- t r cleanup)
+     *                      SWAP                                    (-- r t cleanup)
+     *                      load args                               (-- args... r t cleanup)
+     *                      INVOKEVIRTUAL MethodHandle.invokeBasic  (-- r)
+     *                      GOTO DONE
+     * CATCH:               (-- t)
+     *                      DUP                                     (-- t t)
+     * FINALLY_EXCEPTIONAL: (-- t t)
+     *                      load cleanup                            (-- cleanup t t)
+     *                      SWAP                                    (-- t cleanup t)
+     *                      load default for r                      (-- r t cleanup t)
+     *                      load args                               (-- args... r t cleanup t)
+     *                      INVOKEVIRTUAL MethodHandle.invokeBasic  (-- r t)
+     *                      POP                                     (-- t)
+     *                      ATHROW
+     * DONE:                (-- r)
+     * }</pre></blockquote>
+     */
+    private Name emitTryFinally(int pos) {
+        Name args    = lambdaForm.names[pos];
+        Name invoker = lambdaForm.names[pos+1];
+        Name result  = lambdaForm.names[pos+2];
+
+        Label lFrom = new Label();
+        Label lTo = new Label();
+        Label lCatch = new Label();
+        Label lDone = new Label();
+
+        Class<?> returnType = result.function.resolvedHandle().type().returnType();
+        boolean isNonVoid = returnType != void.class;
+        MethodType type = args.function.resolvedHandle().type()
+                .dropParameterTypes(0,1)
+                .changeReturnType(returnType);
+        MethodType cleanupType = type.insertParameterTypes(0, Throwable.class);
+        if (isNonVoid) {
+            cleanupType = cleanupType.insertParameterTypes(1, returnType);
+        }
+        String cleanupDesc = cleanupType.basicType().toMethodDescriptorString();
+
+        // exception handler table
+        mv.visitTryCatchBlock(lFrom, lTo, lCatch, "java/lang/Throwable");
+
+        // TRY:
+        mv.visitLabel(lFrom);
+        emitPushArgument(invoker, 0); // load target
+        emitPushArguments(args, 1); // load args (skip 0: method handle)
+        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.basicType().toMethodDescriptorString(), false);
+        mv.visitLabel(lTo);
+
+        // FINALLY_NORMAL:
+        emitPushArgument(invoker, 1); // load cleanup
+        if (isNonVoid) {
+            mv.visitInsn(Opcodes.SWAP);
+        }
+        mv.visitInsn(Opcodes.ACONST_NULL);
+        if (isNonVoid) {
+            mv.visitInsn(Opcodes.SWAP);
+        }
+        emitPushArguments(args, 1); // load args (skip 0: method handle)
+        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", cleanupDesc, false);
+        mv.visitJumpInsn(Opcodes.GOTO, lDone);
+
+        // CATCH:
+        mv.visitLabel(lCatch);
+        mv.visitInsn(Opcodes.DUP);
+
+        // FINALLY_EXCEPTIONAL:
+        emitPushArgument(invoker, 1); // load cleanup
+        mv.visitInsn(Opcodes.SWAP);
+        if (isNonVoid) {
+            emitZero(BasicType.basicType(returnType)); // load default for result
+        }
+        emitPushArguments(args, 1); // load args (skip 0: method handle)
+        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", cleanupDesc, false);
+        if (isNonVoid) {
+            mv.visitInsn(Opcodes.POP);
+        }
+        mv.visitInsn(Opcodes.ATHROW);
+
+        // DONE:
+        mv.visitLabel(lDone);
+
+        return result;
+    }
+
+    /**
+     * Emit bytecode for the loop idiom.
+     * <p>
+     * The pattern looks like (Cf. MethodHandleImpl.loop):
+     * <blockquote><pre>{@code
+     * // a0: BMH
+     * // a1: inits, a2: steps, a3: preds, a4: finis
+     * // a5: box, a6: unbox
+     * // a7 (and following): arguments
+     * loop=Lambda(a0:L,a1:L,a2:L,a3:L,a4:L,a5:L,a6:L,a7:L)=>{
+     *   t8:L=MethodHandle.invokeBasic(a5:L,a7:L);                  // box the arguments into an Object[]
+     *   t9:L=MethodHandleImpl.loop(bt:L,a1:L,a2:L,a3:L,a4:L,t8:L); // call the loop executor (with supplied types in bt)
+     *   t10:L=MethodHandle.invokeBasic(a6:L,t9:L);t10:L}           // unbox the result; return the result
+     * }</pre></blockquote>
+     * <p>
+     * It is compiled into bytecode equivalent to the code seen in {@link MethodHandleImpl#loop(BasicType[],
+     * MethodHandle[], MethodHandle[], MethodHandle[], MethodHandle[], Object...)}, with the difference that no arrays
+     * will be used for local state storage. Instead, the local state will be mapped to actual stack slots.
+     * <p>
+     * Bytecode generation applies an unrolling scheme to enable better bytecode generation regarding local state type
+     * handling. The generated bytecode will have the following form ({@code void} types are ignored for convenience).
+     * Assume there are {@code C} clauses in the loop.
+     * <blockquote><pre>{@code
+     * INIT: (INIT_SEQ for clause 1)
+     *       ...
+     *       (INIT_SEQ for clause C)
+     * LOOP: (LOOP_SEQ for clause 1)
+     *       ...
+     *       (LOOP_SEQ for clause C)
+     *       GOTO LOOP
+     * DONE: ...
+     * }</pre></blockquote>
+     * <p>
+     * The {@code INIT_SEQ_x} sequence for clause {@code x} (with {@code x} ranging from {@code 0} to {@code C-1}) has
+     * the following shape. Assume slot {@code vx} is used to hold the state for clause {@code x}.
+     * <blockquote><pre>{@code
+     * INIT_SEQ_x:  ALOAD inits
+     *              CHECKCAST MethodHandle[]
+     *              ICONST x
+     *              AALOAD      // load the init handle for clause x
+     *              load args
+     *              INVOKEVIRTUAL MethodHandle.invokeBasic
+     *              store vx
+     * }</pre></blockquote>
+     * <p>
+     * The {@code LOOP_SEQ_x} sequence for clause {@code x} (with {@code x} ranging from {@code 0} to {@code C-1}) has
+     * the following shape. Again, assume slot {@code vx} is used to hold the state for clause {@code x}.
+     * <blockquote><pre>{@code
+     * LOOP_SEQ_x:  ALOAD steps
+     *              CHECKCAST MethodHandle[]
+     *              ICONST x
+     *              AALOAD              // load the step handle for clause x
+     *              load locals
+     *              load args
+     *              INVOKEVIRTUAL MethodHandle.invokeBasic
+     *              store vx
+     *              ALOAD preds
+     *              CHECKCAST MethodHandle[]
+     *              ICONST x
+     *              AALOAD              // load the pred handle for clause x
+     *              load locals
+     *              load args
+     *              INVOKEVIRTUAL MethodHandle.invokeBasic
+     *              IFNE LOOP_SEQ_x+1   // predicate returned false -> jump to next clause
+     *              ALOAD finis
+     *              CHECKCAST MethodHandle[]
+     *              ICONST x
+     *              AALOAD              // load the fini handle for clause x
+     *              load locals
+     *              load args
+     *              INVOKEVIRTUAL MethodHandle.invokeBasic
+     *              GOTO DONE           // jump beyond end of clauses to return from loop
+     * }</pre></blockquote>
+     */
+    private Name emitLoop(int pos) {
+        Name args    = lambdaForm.names[pos];
+        Name invoker = lambdaForm.names[pos+1];
+        Name result  = lambdaForm.names[pos+2];
+
+        // extract clause and loop-local state types
+        // find the type info in the loop invocation
+        BasicType[] loopClauseTypes = (BasicType[]) invoker.arguments[0];
+        Class<?>[] loopLocalStateTypes = Stream.of(loopClauseTypes).
+                filter(bt -> bt != BasicType.V_TYPE).map(BasicType::basicTypeClass).toArray(Class<?>[]::new);
+
+        final int firstLoopStateIndex = extendLocalsMap(loopLocalStateTypes);
+
+        Class<?> returnType = result.function.resolvedHandle().type().returnType();
+        MethodType loopType = args.function.resolvedHandle().type()
+                .dropParameterTypes(0,1)
+                .changeReturnType(returnType);
+        MethodType loopHandleType = loopType.insertParameterTypes(0, loopLocalStateTypes);
+        MethodType predType = loopHandleType.changeReturnType(boolean.class);
+        MethodType finiType = loopHandleType;
+
+        final int nClauses = loopClauseTypes.length;
+
+        // indices to invoker arguments to load method handle arrays
+        final int inits = 1;
+        final int steps = 2;
+        final int preds = 3;
+        final int finis = 4;
+
+        Label lLoop = new Label();
+        Label lDone = new Label();
+        Label lNext;
+
+        // INIT:
+        for (int c = 0, state = 0; c < nClauses; ++c) {
+            MethodType cInitType = loopType.changeReturnType(loopClauseTypes[c].basicTypeClass());
+            emitLoopHandleInvoke(invoker, inits, c, args, false, cInitType, loopLocalStateTypes, firstLoopStateIndex);
+            if (cInitType.returnType() != void.class) {
+                emitStoreInsn(BasicType.basicType(cInitType.returnType()), firstLoopStateIndex + state);
+                ++state;
+            }
+        }
+
+        // LOOP:
+        mv.visitLabel(lLoop);
+
+        for (int c = 0, state = 0; c < nClauses; ++c) {
+            lNext = new Label();
+
+            MethodType stepType = loopHandleType.changeReturnType(loopClauseTypes[c].basicTypeClass());
+            boolean isVoid = stepType.returnType() == void.class;
+
+            // invoke loop step
+            emitLoopHandleInvoke(invoker, steps, c, args, true, stepType, loopLocalStateTypes, firstLoopStateIndex);
+            if (!isVoid) {
+                emitStoreInsn(BasicType.basicType(stepType.returnType()), firstLoopStateIndex + state);
+                ++state;
+            }
+
+            // invoke loop predicate
+            emitLoopHandleInvoke(invoker, preds, c, args, true, predType, loopLocalStateTypes, firstLoopStateIndex);
+            mv.visitJumpInsn(Opcodes.IFNE, lNext);
+
+            // invoke fini
+            emitLoopHandleInvoke(invoker, finis, c, args, true, finiType, loopLocalStateTypes, firstLoopStateIndex);
+            mv.visitJumpInsn(Opcodes.GOTO, lDone);
+
+            // this is the beginning of the next loop clause
+            mv.visitLabel(lNext);
+        }
+
+        mv.visitJumpInsn(Opcodes.GOTO, lLoop);
+
+        // DONE:
+        mv.visitLabel(lDone);
+
+        return result;
+    }
+
+    private int extendLocalsMap(Class<?>[] types) {
+        int firstSlot = localsMap.length - 1;
+        localsMap = Arrays.copyOf(localsMap, localsMap.length + types.length);
+        localClasses = Arrays.copyOf(localClasses, localClasses.length + types.length);
+        System.arraycopy(types, 0, localClasses, firstSlot, types.length);
+        int index = localsMap[firstSlot - 1] + 1;
+        int lastSlots = 0;
+        for (int i = 0; i < types.length; ++i) {
+            localsMap[firstSlot + i] = index;
+            lastSlots = BasicType.basicType(localClasses[firstSlot + i]).basicTypeSlots();
+            index += lastSlots;
+        }
+        localsMap[localsMap.length - 1] = index - lastSlots;
+        return firstSlot;
+    }
+
+    private void emitLoopHandleInvoke(Name holder, int handles, int clause, Name args, boolean pushLocalState,
+                                      MethodType type, Class<?>[] loopLocalStateTypes, int firstLoopStateSlot) {
+        // load handle for clause
+        emitPushArgument(holder, handles);
+        emitIconstInsn(clause);
+        mv.visitInsn(Opcodes.AALOAD);
+        // load loop state (preceding the other arguments)
+        if (pushLocalState) {
+            for (int s = 0; s < loopLocalStateTypes.length; ++s) {
+                emitLoadInsn(BasicType.basicType(loopLocalStateTypes[s]), firstLoopStateSlot + s);
+            }
+        }
+        // load loop args (skip 0: method handle)
+        emitPushArguments(args, 1);
+        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, MH, "invokeBasic", type.toMethodDescriptorString(), false);
+    }
+
+    private void emitZero(BasicType type) {
+        switch (type) {
+            case I_TYPE: mv.visitInsn(Opcodes.ICONST_0); break;
+            case J_TYPE: mv.visitInsn(Opcodes.LCONST_0); break;
+            case F_TYPE: mv.visitInsn(Opcodes.FCONST_0); break;
+            case D_TYPE: mv.visitInsn(Opcodes.DCONST_0); break;
+            case L_TYPE: mv.visitInsn(Opcodes.ACONST_NULL); break;
+            default: throw new InternalError("unknown type: " + type);
+        }
+    }
+
     private void emitPushArguments(Name args) {
         emitPushArguments(args, 0);
     }
--- a/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Mon Jul 04 13:00:15 2016 +0900
+++ b/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Mon Jul 04 10:08:18 2016 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -33,11 +33,9 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
-import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.HashMap;
-import java.util.List;
 
 import static java.lang.invoke.LambdaForm.BasicType.*;
 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
@@ -210,6 +208,29 @@
             }
             return btypes;
         }
+        static String basicTypeDesc(BasicType[] types) {
+            if (types == null) {
+                return null;
+            }
+            if (types.length == 0) {
+                return "";
+            }
+            StringBuilder sb = new StringBuilder();
+            for (BasicType bt : types) {
+                sb.append(bt.basicTypeChar());
+            }
+            return sb.toString();
+        }
+        static int[] basicTypeOrds(BasicType[] types) {
+            if (types == null) {
+                return null;
+            }
+            int[] a = new int[types.length];
+            for(int i = 0; i < types.length; ++i) {
+                a[i] = types[i].ordinal();
+            }
+            return a;
+        }
 
         static char basicTypeChar(Class<?> type) {
             return basicType(type).btChar;
@@ -565,6 +586,69 @@
         return MethodType.methodType(rtype, ptypes);
     }
 
+    /**
+     * Check if i-th name is a call to MethodHandleImpl.selectAlternative.
+     */
+    boolean isSelectAlternative(int pos) {
+        // selectAlternative idiom:
+        //   t_{n}:L=MethodHandleImpl.selectAlternative(...)
+        //   t_{n+1}:?=MethodHandle.invokeBasic(t_{n}, ...)
+        if (pos+1 >= names.length)  return false;
+        Name name0 = names[pos];
+        Name name1 = names[pos+1];
+        return name0.refersTo(MethodHandleImpl.class, "selectAlternative") &&
+                name1.isInvokeBasic() &&
+                name1.lastUseIndex(name0) == 0 && // t_{n+1}:?=MethodHandle.invokeBasic(t_{n}, ...)
+                lastUseIndex(name0) == pos+1;     // t_{n} is local: used only in t_{n+1}
+    }
+
+    private boolean isMatchingIdiom(int pos, String idiomName, int nArgs) {
+        if (pos+2 >= names.length)  return false;
+        Name name0 = names[pos];
+        Name name1 = names[pos+1];
+        Name name2 = names[pos+2];
+        return name1.refersTo(MethodHandleImpl.class, idiomName) &&
+                name0.isInvokeBasic() &&
+                name2.isInvokeBasic() &&
+                name1.lastUseIndex(name0) == nArgs && // t_{n+1}:L=MethodHandleImpl.<invoker>(<args>, t_{n});
+                lastUseIndex(name0) == pos+1 &&       // t_{n} is local: used only in t_{n+1}
+                name2.lastUseIndex(name1) == 1 &&     // t_{n+2}:?=MethodHandle.invokeBasic(*, t_{n+1})
+                lastUseIndex(name1) == pos+2;         // t_{n+1} is local: used only in t_{n+2}
+    }
+
+    /**
+     * Check if i-th name is a start of GuardWithCatch idiom.
+     */
+    boolean isGuardWithCatch(int pos) {
+        // GuardWithCatch idiom:
+        //   t_{n}:L=MethodHandle.invokeBasic(...)
+        //   t_{n+1}:L=MethodHandleImpl.guardWithCatch(*, *, *, t_{n});
+        //   t_{n+2}:?=MethodHandle.invokeBasic(*, t_{n+1})
+        return isMatchingIdiom(pos, "guardWithCatch", 3);
+    }
+
+    /**
+     * Check if i-th name is a start of the tryFinally idiom.
+     */
+    boolean isTryFinally(int pos) {
+        // tryFinally idiom:
+        //   t_{n}:L=MethodHandle.invokeBasic(...)
+        //   t_{n+1}:L=MethodHandleImpl.tryFinally(*, *, t_{n})
+        //   t_{n+2}:?=MethodHandle.invokeBasic(*, t_{n+1})
+        return isMatchingIdiom(pos, "tryFinally", 2);
+    }
+
+    /**
+     * Check if i-th name is a start of the loop idiom.
+     */
+    boolean isLoop(int pos) {
+        // loop idiom:
+        //   t_{n}:L=MethodHandle.invokeBasic(...)
+        //   t_{n+1}:L=MethodHandleImpl.loop(types, *, *, *, *, t_{n})
+        //   t_{n+2}:?=MethodHandle.invokeBasic(*, t_{n+1})
+        return isMatchingIdiom(pos, "loop", 5);
+    }
+
     /*
      * Code generation issues:
      *
@@ -1421,6 +1505,39 @@
             return !isParam() && arguments.length == 0 && function.isConstantZero();
         }
 
+        boolean refersTo(Class<?> declaringClass, String methodName) {
+            return function != null &&
+                    function.member() != null && function.member().refersTo(declaringClass, methodName);
+        }
+
+        /**
+         * Check if MemberName is a call to MethodHandle.invokeBasic.
+         */
+        boolean isInvokeBasic() {
+            if (function == null)
+                return false;
+            if (arguments.length < 1)
+                return false;  // must have MH argument
+            MemberName member = function.member();
+            return member != null && member.refersTo(MethodHandle.class, "invokeBasic") &&
+                    !member.isPublic() && !member.isStatic();
+        }
+
+        /**
+         * Check if MemberName is a call to MethodHandle.linkToStatic, etc.
+         */
+        boolean isLinkerMethodInvoke() {
+            if (function == null)
+                return false;
+            if (arguments.length < 1)
+                return false;  // must have MH argument
+            MemberName member = function.member();
+            return member != null &&
+                    member.getDeclaringClass() == MethodHandle.class &&
+                    !member.isPublic() && member.isStatic() &&
+                    member.getName().startsWith("linkTo");
+        }
+
         public String toString() {
             return (isParam()?"a":"t")+(index >= 0 ? index : System.identityHashCode(this))+":"+typeChar();
         }
--- a/src/java.base/share/classes/java/lang/invoke/LambdaFormBuffer.java	Mon Jul 04 13:00:15 2016 +0900
+++ b/src/java.base/share/classes/java/lang/invoke/LambdaFormBuffer.java	Mon Jul 04 10:08:18 2016 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -249,7 +249,7 @@
         assert(inTrans());
     }
 
-    private void changeName(int i, Name name) {
+    void changeName(int i, Name name) {
         assert(inTrans());
         assert(i < length);
         Name oldName = names[i];
--- a/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java	Mon Jul 04 13:00:15 2016 +0900
+++ b/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java	Mon Jul 04 10:08:18 2016 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,15 +25,17 @@
 
 package java.lang.invoke;
 
+import sun.invoke.util.Wrapper;
+
 import java.lang.ref.SoftReference;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.ConcurrentHashMap;
+
 import static java.lang.invoke.LambdaForm.*;
 import static java.lang.invoke.LambdaForm.BasicType.*;
 import static java.lang.invoke.MethodHandleImpl.Intrinsic;
-import java.util.Collections;
-import java.util.concurrent.ConcurrentHashMap;
-
-import sun.invoke.util.Wrapper;
+import static java.lang.invoke.MethodHandleImpl.NF_loop;
 
 /** Transforms on LFs.
  *  A lambda-form editor can derive new LFs from its base LF.
@@ -73,7 +75,8 @@
             FILTER_ARG, FILTER_RETURN, FILTER_RETURN_TO_ZERO,
             COLLECT_ARGS, COLLECT_ARGS_TO_VOID, COLLECT_ARGS_TO_ARRAY,
             FOLD_ARGS, FOLD_ARGS_TO_VOID,
-            PERMUTE_ARGS
+            PERMUTE_ARGS,
+            LOCAL_TYPES
             //maybe add more for guard with test, catch exception, pointwise type conversions
         }
 
@@ -847,6 +850,32 @@
         return putInCache(key, form);
     }
 
+    LambdaForm noteLoopLocalTypesForm(int pos, BasicType[] localTypes) {
+        assert(lambdaForm.isLoop(pos));
+        int[] desc = BasicType.basicTypeOrds(localTypes);
+        desc = Arrays.copyOf(desc, desc.length + 1);
+        desc[desc.length - 1] = pos;
+        Transform key = Transform.of(Transform.Kind.LOCAL_TYPES, desc);
+        LambdaForm form = getInCache(key);
+        if (form != null) {
+            return form;
+        }
+
+        // replace the null entry in the MHImpl.loop invocation with localTypes
+        Name invokeLoop = lambdaForm.names[pos + 1];
+        assert(invokeLoop.function == NF_loop);
+        Object[] args = Arrays.copyOf(invokeLoop.arguments, invokeLoop.arguments.length);
+        assert(args[0] == null);
+        args[0] = localTypes;
+
+        LambdaFormBuffer buf = buffer();
+        buf.startEdit();
+        buf.changeName(pos + 1, new Name(NF_loop, args));
+        form = buf.endEdit();
+
+        return putInCache(key, form);
+    }
+
     static boolean permutedTypesMatch(int[] reorder, BasicType[] types, Name[] names, int skip) {
         for (int i = 0; i < reorder.length; i++) {
             assert (names[skip + i].isParam());
--- a/src/java.base/share/classes/java/lang/invoke/MemberName.java	Mon Jul 04 13:00:15 2016 +0900
+++ b/src/java.base/share/classes/java/lang/invoke/MemberName.java	Mon Jul 04 10:08:18 2016 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -32,8 +32,8 @@
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
+import java.lang.reflect.Member;
 import java.lang.reflect.Method;
-import java.lang.reflect.Member;
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Module;
 import java.util.ArrayList;
@@ -41,9 +41,11 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
+
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
-import static java.lang.invoke.MethodHandleStatics.*;
-import java.util.Objects;
+import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
+import static java.lang.invoke.MethodHandleStatics.newInternalError;
 
 /**
  * A {@code MemberName} is a compact symbolic datum which fully characterizes
@@ -500,6 +502,13 @@
                                                lookupClass, mode);
     }
 
+    /**
+     * Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}.
+     */
+    public boolean refersTo(Class<?> declc, String n) {
+        return clazz == declc && getName().equals(n);
+    }
+
     /** Initialize a query.   It is not resolved. */
     private void init(Class<?> defClass, String name, Object type, int flags) {
         // defining class is allowed to be null (for a naked name/type pair)
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Mon Jul 04 13:00:15 2016 +0900
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Mon Jul 04 10:08:18 2016 +0200
@@ -25,6 +25,17 @@
 
 package java.lang.invoke;
 
+import jdk.internal.org.objectweb.asm.AnnotationVisitor;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.reflect.CallerSensitive;
+import jdk.internal.reflect.Reflection;
+import jdk.internal.vm.annotation.Stable;
+import sun.invoke.empty.Empty;
+import sun.invoke.util.ValueConversions;
+import sun.invoke.util.VerifyType;
+import sun.invoke.util.Wrapper;
+
 import java.lang.reflect.Array;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
@@ -33,18 +44,8 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.function.Function;
-
-import jdk.internal.reflect.CallerSensitive;
-import jdk.internal.reflect.Reflection;
-import jdk.internal.vm.annotation.Stable;
-import sun.invoke.empty.Empty;
-import sun.invoke.util.ValueConversions;
-import sun.invoke.util.VerifyType;
-import sun.invoke.util.Wrapper;
-
-import jdk.internal.org.objectweb.asm.AnnotationVisitor;
-import jdk.internal.org.objectweb.asm.ClassWriter;
-import jdk.internal.org.objectweb.asm.MethodVisitor;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import static java.lang.invoke.LambdaForm.*;
 import static java.lang.invoke.MethodHandleStatics.*;
@@ -1046,26 +1047,13 @@
         // Box arguments and wrap them into Object[]: ValueConversions.array().
         MethodType varargsType = type.changeReturnType(Object[].class);
         MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType);
-        // Result unboxing: ValueConversions.unbox() OR ValueConversions.identity() OR ValueConversions.ignore().
-        MethodHandle unboxResult;
-        Class<?> rtype = type.returnType();
-        if (rtype.isPrimitive()) {
-            if (rtype == void.class) {
-                unboxResult = ValueConversions.ignore();
-            } else {
-                Wrapper w = Wrapper.forPrimitiveType(type.returnType());
-                unboxResult = ValueConversions.unboxExact(w);
-            }
-        } else {
-            unboxResult = MethodHandles.identity(Object.class);
-        }
+        MethodHandle unboxResult = unboxResultHandle(type.returnType());
 
         BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLL();
         BoundMethodHandle mh;
         try {
-            mh = (BoundMethodHandle)
-                    data.constructor().invokeBasic(type, form, (Object) target, (Object) exType, (Object) catcher,
-                                                   (Object) collectArgs, (Object) unboxResult);
+            mh = (BoundMethodHandle) data.constructor().invokeBasic(type, form, (Object) target, (Object) exType,
+                    (Object) catcher, (Object) collectArgs, (Object) unboxResult);
         } catch (Throwable ex) {
             throw uncaughtException(ex);
         }
@@ -1085,16 +1073,18 @@
             return target.asFixedArity().invokeWithArguments(av);
         } catch (Throwable t) {
             if (!exType.isInstance(t)) throw t;
-            return catcher.asFixedArity().invokeWithArguments(prepend(t, av));
+            return catcher.asFixedArity().invokeWithArguments(prepend(av, t));
         }
     }
 
-    /** Prepend an element {@code elem} to an {@code array}. */
+    /** Prepend elements to an array. */
     @LambdaForm.Hidden
-    private static Object[] prepend(Object elem, Object[] array) {
-        Object[] newArray = new Object[array.length+1];
-        newArray[0] = elem;
-        System.arraycopy(array, 0, newArray, 1, array.length);
+    private static Object[] prepend(Object[] array, Object... elems) {
+        int nArray = array.length;
+        int nElems = elems.length;
+        Object[] newArray = new Object[nArray + nElems];
+        System.arraycopy(elems, 0, newArray, 0, nElems);
+        System.arraycopy(array, 0, newArray, nElems, nArray);
         return newArray;
     }
 
@@ -1352,6 +1342,8 @@
     enum Intrinsic {
         SELECT_ALTERNATIVE,
         GUARD_WITH_CATCH,
+        TRY_FINALLY,
+        LOOP,
         NEW_ARRAY,
         ARRAY_LOAD,
         ARRAY_STORE,
@@ -1363,7 +1355,7 @@
 
     /** Mark arbitrary method handle as intrinsic.
      * InvokerBytecodeGenerator uses this info to produce more efficient bytecode shape. */
-    private static final class IntrinsicMethodHandle extends DelegatingMethodHandle {
+    static final class IntrinsicMethodHandle extends DelegatingMethodHandle {
         private final MethodHandle target;
         private final Intrinsic intrinsicName;
 
@@ -1694,6 +1686,8 @@
         NF_checkSpreadArgument,
         NF_guardWithCatch,
         NF_throwException,
+        NF_tryFinally,
+        NF_loop,
         NF_profileBoolean;
 
     static {
@@ -1703,6 +1697,11 @@
             NF_guardWithCatch = new NamedFunction(MethodHandleImpl.class
                     .getDeclaredMethod("guardWithCatch", MethodHandle.class, Class.class,
                             MethodHandle.class, Object[].class));
+            NF_tryFinally = new NamedFunction(MethodHandleImpl.class
+                    .getDeclaredMethod("tryFinally", MethodHandle.class, MethodHandle.class, Object[].class));
+            NF_loop = new NamedFunction(MethodHandleImpl.class
+                    .getDeclaredMethod("loop", BasicType[].class, MethodHandle[].class, MethodHandle[].class,
+                            MethodHandle[].class, MethodHandle[].class, Object[].class));
             NF_throwException = new NamedFunction(MethodHandleImpl.class
                     .getDeclaredMethod("throwException", Throwable.class));
             NF_profileBoolean = new NamedFunction(MethodHandleImpl.class
@@ -1712,14 +1711,25 @@
         }
     }
 
+    /** Result unboxing: ValueConversions.unbox() OR ValueConversions.identity() OR ValueConversions.ignore(). */
+    private static MethodHandle unboxResultHandle(Class<?> returnType) {
+        if (returnType.isPrimitive()) {
+            if (returnType == void.class) {
+                return ValueConversions.ignore();
+            } else {
+                Wrapper w = Wrapper.forPrimitiveType(returnType);
+                return ValueConversions.unboxExact(w);
+            }
+        } else {
+            return MethodHandles.identity(Object.class);
+        }
+    }
+
     /**
-     * Assembles a loop method handle from the given handles and type information. This works by binding and configuring
-     * the {@linkplain #looper(MethodHandle[], MethodHandle[], MethodHandle[], MethodHandle[], int, int, Object[]) "most
-     * generic loop"}.
+     * Assembles a loop method handle from the given handles and type information.
      *
      * @param tloop the return type of the loop.
      * @param targs types of the arguments to be passed to the loop.
-     * @param tvars types of loop-local variables.
      * @param init sanitized array of initializers for loop-local variables.
      * @param step sanitited array of loop bodies.
      * @param pred sanitized array of predicates.
@@ -1727,64 +1737,144 @@
      *
      * @return a handle that, when invoked, will execute the loop.
      */
-    static MethodHandle makeLoop(Class<?> tloop, List<Class<?>> targs, List<Class<?>> tvars, List<MethodHandle> init,
-                                 List<MethodHandle> step, List<MethodHandle> pred, List<MethodHandle> fini) {
-        MethodHandle[] ainit = toArrayArgs(init);
-        MethodHandle[] astep = toArrayArgs(step);
-        MethodHandle[] apred = toArrayArgs(pred);
-        MethodHandle[] afini = toArrayArgs(fini);
+    static MethodHandle makeLoop(Class<?> tloop, List<Class<?>> targs, List<MethodHandle> init, List<MethodHandle> step,
+                                 List<MethodHandle> pred, List<MethodHandle> fini) {
+        MethodType type = MethodType.methodType(tloop, targs);
+        BasicType[] initClauseTypes =
+                init.stream().map(h -> h.type().returnType()).map(BasicType::basicType).toArray(BasicType[]::new);
+        LambdaForm form = makeLoopForm(type.basicType(), initClauseTypes);
 
-        MethodHandle l = getConstantHandle(MH_looper);
+        // Prepare auxiliary method handles used during LambdaForm interpretation.
+        // Box arguments and wrap them into Object[]: ValueConversions.array().
+        MethodType varargsType = type.changeReturnType(Object[].class);
+        MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType);
+        MethodHandle unboxResult = unboxResultHandle(tloop);
 
-        // Bind the statically known arguments.
-        l = MethodHandles.insertArguments(l, 0, ainit, astep, apred, afini, tvars.size(), targs.size());
+        BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLLL();
+        BoundMethodHandle mh;
+        try {
+            mh = (BoundMethodHandle) data.constructor().invokeBasic(type, form, (Object) toArray(init),
+                    (Object) toArray(step), (Object) toArray(pred), (Object) toArray(fini), (Object) collectArgs,
+                    (Object) unboxResult);
+        } catch (Throwable ex) {
+            throw uncaughtException(ex);
+        }
+        assert(mh.type() == type);
+        return mh;
+    }
 
-        // Turn the args array into an argument list.
-        l = l.asCollector(Object[].class, targs.size());
-
-        // Finally, make loop type.
-        MethodType loopType = MethodType.methodType(tloop, targs);
-        l = l.asType(loopType);
-
-        return l;
+    private static MethodHandle[] toArray(List<MethodHandle> l) {
+        return l.toArray(new MethodHandle[0]);
     }
 
     /**
-     * Converts all handles in the {@code hs} array to handles that accept an array of arguments.
-     *
-     * @param hs method handles to be converted.
-     *
-     * @return the {@code hs} array, with all method handles therein converted.
+     * Loops introduce some complexity as they can have additional local state. Hence, LambdaForms for loops are
+     * generated from a template. The LambdaForm template shape for the loop combinator is as follows (assuming one
+     * reference parameter passed in {@code a1}, and a reference return type, with the return value represented by
+     * {@code t12}):
+     * <blockquote><pre>{@code
+     *  loop=Lambda(a0:L,a1:L)=>{
+     *    t2:L=BoundMethodHandle$Species_L6.argL0(a0:L);             // array of init method handles
+     *    t3:L=BoundMethodHandle$Species_L6.argL1(a0:L);             // array of step method handles
+     *    t4:L=BoundMethodHandle$Species_L6.argL2(a0:L);             // array of pred method handles
+     *    t5:L=BoundMethodHandle$Species_L6.argL3(a0:L);             // array of fini method handles
+     *    t6:L=BoundMethodHandle$Species_L6.argL4(a0:L);             // helper handle to box the arguments into an Object[]
+     *    t7:L=BoundMethodHandle$Species_L6.argL5(a0:L);             // helper handle to unbox the result
+     *    t8:L=MethodHandle.invokeBasic(t6:L,a1:L);                  // box the arguments into an Object[]
+     *    t9:L=MethodHandleImpl.loop(null,t2:L,t3:L,t4:L,t5:L,t6:L); // call the loop executor
+     *    t10:L=MethodHandle.invokeBasic(t7:L,t9:L);t10:L}           // unbox the result; return the result
+     * }</pre></blockquote>
+     * <p>
+     * {@code argL0} through {@code argL3} are the arrays of init, step, pred, and fini method handles.
+     * {@code argL4} and {@code argL5} are auxiliary method handles: {@code argL2} boxes arguments and wraps them into
+     * {@code Object[]} ({@code ValueConversions.array()}), and {@code argL3} unboxes the result if necessary
+     * ({@code ValueConversions.unbox()}).
+     * <p>
+     * Having {@code t6} and {@code t7} passed in via a BMH and not hardcoded in the lambda form allows to share lambda
+     * forms among loop combinators with the same basic type.
+     * <p>
+     * The above template is instantiated by using the {@link LambdaFormEditor} to replace the {@code null} argument to
+     * the {@code loop} invocation with the {@code BasicType} array describing the loop clause types. This argument is
+     * ignored in the loop invoker, but will be extracted and used in {@linkplain InvokerBytecodeGenerator#emitLoop(int)
+     * bytecode generation}.
      */
-    static MethodHandle[] toArrayArgs(List<MethodHandle> hs) {
-        return hs.stream().map(h -> h.asSpreader(Object[].class, h.type().parameterCount())).toArray(MethodHandle[]::new);
+    private static LambdaForm makeLoopForm(MethodType basicType, BasicType[] localVarTypes) {
+        MethodType lambdaType = basicType.invokerType();
+
+        final int THIS_MH = 0;  // the BMH_LLLLLL
+        final int ARG_BASE = 1; // start of incoming arguments
+        final int ARG_LIMIT = ARG_BASE + basicType.parameterCount();
+
+        int nameCursor = ARG_LIMIT;
+        final int GET_INITS = nameCursor++;
+        final int GET_STEPS = nameCursor++;
+        final int GET_PREDS = nameCursor++;
+        final int GET_FINIS = nameCursor++;
+        final int GET_COLLECT_ARGS = nameCursor++;
+        final int GET_UNBOX_RESULT = nameCursor++;
+        final int BOXED_ARGS = nameCursor++;
+        final int LOOP = nameCursor++;
+        final int UNBOX_RESULT = nameCursor++;
+
+        LambdaForm lform = basicType.form().cachedLambdaForm(MethodTypeForm.LF_LOOP);
+        if (lform == null) {
+            Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType);
+
+            BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLLLL();
+            names[THIS_MH] = names[THIS_MH].withConstraint(data);
+            names[GET_INITS] = new Name(data.getterFunction(0), names[THIS_MH]);
+            names[GET_STEPS] = new Name(data.getterFunction(1), names[THIS_MH]);
+            names[GET_PREDS] = new Name(data.getterFunction(2), names[THIS_MH]);
+            names[GET_FINIS] = new Name(data.getterFunction(3), names[THIS_MH]);
+            names[GET_COLLECT_ARGS] = new Name(data.getterFunction(4), names[THIS_MH]);
+            names[GET_UNBOX_RESULT] = new Name(data.getterFunction(5), names[THIS_MH]);
+
+            // t_{i}:L=MethodHandle.invokeBasic(collectArgs:L,a1:L,...);
+            MethodType collectArgsType = basicType.changeReturnType(Object.class);
+            MethodHandle invokeBasic = MethodHandles.basicInvoker(collectArgsType);
+            Object[] args = new Object[invokeBasic.type().parameterCount()];
+            args[0] = names[GET_COLLECT_ARGS];
+            System.arraycopy(names, ARG_BASE, args, 1, ARG_LIMIT - ARG_BASE);
+            names[BOXED_ARGS] = new Name(makeIntrinsic(invokeBasic, Intrinsic.LOOP), args);
+
+            // t_{i+1}:L=MethodHandleImpl.loop(localTypes:L,inits:L,steps:L,preds:L,finis:L,t_{i}:L);
+            Object[] lArgs =
+                    new Object[]{null, // placeholder for BasicType[] localTypes - will be added by LambdaFormEditor
+                            names[GET_INITS], names[GET_STEPS], names[GET_PREDS], names[GET_FINIS], names[BOXED_ARGS]};
+            names[LOOP] = new Name(NF_loop, lArgs);
+
+            // t_{i+2}:I=MethodHandle.invokeBasic(unbox:L,t_{i+1}:L);
+            MethodHandle invokeBasicUnbox = MethodHandles.basicInvoker(MethodType.methodType(basicType.rtype(), Object.class));
+            Object[] unboxArgs = new Object[]{names[GET_UNBOX_RESULT], names[LOOP]};
+            names[UNBOX_RESULT] = new Name(invokeBasicUnbox, unboxArgs);
+
+            lform = basicType.form().setCachedLambdaForm(MethodTypeForm.LF_LOOP,
+                    new LambdaForm("loop", lambdaType.parameterCount(), names));
+        }
+
+        // BOXED_ARGS is the index into the names array where the loop idiom starts
+        return lform.editor().noteLoopLocalTypesForm(BOXED_ARGS, localVarTypes);
     }
 
+
     /**
-     * This method embodies the most generic loop for use by {@link MethodHandles#loop(MethodHandle[][])}. A handle on
-     * it will be transformed into a handle on a concrete loop instantiation by {@link #makeLoop}.
-     *
-     * @param init loop-local variable initializers.
-     * @param step bodies.
-     * @param pred predicates.
-     * @param fini finalizers.
-     * @param varSize number of loop-local variables.
-     * @param nArgs number of arguments passed to the loop.
-     * @param args arguments to the loop invocation.
-     *
-     * @return the result of executing the loop.
+     * Intrinsified during LambdaForm compilation
+     * (see {@link InvokerBytecodeGenerator#emitLoop(int)}).
      */
-    static Object looper(MethodHandle[] init, MethodHandle[] step, MethodHandle[] pred, MethodHandle[] fini,
-                         int varSize, int nArgs, Object[] args) throws Throwable {
+    @LambdaForm.Hidden
+    static Object loop(BasicType[] localTypes, MethodHandle[] init, MethodHandle[] step, MethodHandle[] pred,
+                       MethodHandle[] fini, Object... av) throws Throwable {
+        int varSize = (int) Stream.of(init).filter(h -> h.type().returnType() != void.class).count();
+        int nArgs = init[0].type().parameterCount();
         Object[] varsAndArgs = new Object[varSize + nArgs];
         for (int i = 0, v = 0; i < init.length; ++i) {
             if (init[i].type().returnType() == void.class) {
-                init[i].invoke(args);
+                init[i].asFixedArity().invokeWithArguments(av);
             } else {
-                varsAndArgs[v++] = init[i].invoke(args);
+                varsAndArgs[v++] = init[i].asFixedArity().invokeWithArguments(av);
             }
         }
-        System.arraycopy(args, 0, varsAndArgs, varSize, nArgs);
+        System.arraycopy(av, 0, varsAndArgs, varSize, nArgs);
         final int nSteps = step.length;
         for (; ; ) {
             for (int i = 0, v = 0; i < nSteps; ++i) {
@@ -1792,12 +1882,12 @@
                 MethodHandle s = step[i];
                 MethodHandle f = fini[i];
                 if (s.type().returnType() == void.class) {
-                    s.invoke(varsAndArgs);
+                    s.asFixedArity().invokeWithArguments(varsAndArgs);
                 } else {
-                    varsAndArgs[v++] = s.invoke(varsAndArgs);
+                    varsAndArgs[v++] = s.asFixedArity().invokeWithArguments(varsAndArgs);
                 }
-                if (!(boolean) p.invoke(varsAndArgs)) {
-                    return f.invoke(varsAndArgs);
+                if (!(boolean) p.asFixedArity().invokeWithArguments(varsAndArgs)) {
+                    return f.asFixedArity().invokeWithArguments(varsAndArgs);
                 }
             }
         }
@@ -1879,77 +1969,127 @@
      *
      * @param target the target to execute in a {@code try-finally} block.
      * @param cleanup the cleanup to execute in the {@code finally} block.
-     * @param type the result type of the entire construct.
+     * @param rtype the result type of the entire construct.
      * @param argTypes the types of the arguments.
      *
      * @return a handle on the constructed {@code try-finally} block.
      */
-    static MethodHandle makeTryFinally(MethodHandle target, MethodHandle cleanup, Class<?> type, List<Class<?>> argTypes) {
-        MethodHandle tf = getConstantHandle(type == void.class ? MH_tryFinallyVoidExec : MH_tryFinallyExec);
+    static MethodHandle makeTryFinally(MethodHandle target, MethodHandle cleanup, Class<?> rtype, List<Class<?>> argTypes) {
+        MethodType type = MethodType.methodType(rtype, argTypes);
+        LambdaForm form = makeTryFinallyForm(type.basicType());
 
-        // Bind the statically known arguments.
-        tf = MethodHandles.insertArguments(tf, 0, target, cleanup);
+        // Prepare auxiliary method handles used during LambdaForm interpretation.
+        // Box arguments and wrap them into Object[]: ValueConversions.array().
+        MethodType varargsType = type.changeReturnType(Object[].class);
+        MethodHandle collectArgs = varargsArray(type.parameterCount()).asType(varargsType);
+        MethodHandle unboxResult = unboxResultHandle(rtype);
 
-        // Turn the args array into an argument list.
-        tf = tf.asCollector(Object[].class, argTypes.size());
-
-        // Finally, make try-finally type.
-        MethodType tfType = MethodType.methodType(type, argTypes);
-        tf = tf.asType(tfType);
-
-        return tf;
+        BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLL();
+        BoundMethodHandle mh;
+        try {
+            mh = (BoundMethodHandle) data.constructor().invokeBasic(type, form, (Object) target, (Object) cleanup,
+                    (Object) collectArgs, (Object) unboxResult);
+        } catch (Throwable ex) {
+            throw uncaughtException(ex);
+        }
+        assert(mh.type() == type);
+        return mh;
     }
 
     /**
-     * A method that will be bound during construction of a {@code try-finally} handle with non-{@code void} return type
-     * by {@link MethodHandles#tryFinally(MethodHandle, MethodHandle)}.
-     *
-     * @param target the handle to wrap in a {@code try-finally} block. This will be bound.
-     * @param cleanup the handle to run in any case before returning. This will be bound.
-     * @param args the arguments to the call. These will remain as the argument list.
-     *
-     * @return whatever the execution of the {@code target} returned (it may have been modified by the execution of
-     *         {@code cleanup}).
-     * @throws Throwable in case anything is thrown by the execution of {@code target}, the {@link Throwable} will be
-     *         passed to the {@code cleanup} handle, which may decide to throw any exception it sees fit.
+     * The LambdaForm shape for the tryFinally combinator is as follows (assuming one reference parameter passed in
+     * {@code a1}, and a reference return type, with the return value represented by {@code t8}):
+     * <blockquote><pre>{@code
+     *  tryFinally=Lambda(a0:L,a1:L)=>{
+     *    t2:L=BoundMethodHandle$Species_LLLL.argL0(a0:L);  // target method handle
+     *    t3:L=BoundMethodHandle$Species_LLLL.argL1(a0:L);  // cleanup method handle
+     *    t4:L=BoundMethodHandle$Species_LLLL.argL2(a0:L);  // helper handle to box the arguments into an Object[]
+     *    t5:L=BoundMethodHandle$Species_LLLL.argL3(a0:L);  // helper handle to unbox the result
+     *    t6:L=MethodHandle.invokeBasic(t4:L,a1:L);         // box the arguments into an Object[]
+     *    t7:L=MethodHandleImpl.tryFinally(t2:L,t3:L,t6:L); // call the tryFinally executor
+     *    t8:L=MethodHandle.invokeBasic(t5:L,t7:L);t8:L}    // unbox the result; return the result
+     * }</pre></blockquote>
+     * <p>
+     * {@code argL0} and {@code argL1} are the target and cleanup method handles.
+     * {@code argL2} and {@code argL3} are auxiliary method handles: {@code argL2} boxes arguments and wraps them into
+     * {@code Object[]} ({@code ValueConversions.array()}), and {@code argL3} unboxes the result if necessary
+     * ({@code ValueConversions.unbox()}).
+     * <p>
+     * Having {@code t4} and {@code t5} passed in via a BMH and not hardcoded in the lambda form allows to share lambda
+     * forms among tryFinally combinators with the same basic type.
      */
-    static Object tryFinallyExecutor(MethodHandle target, MethodHandle cleanup, Object[] args) throws Throwable {
+    private static LambdaForm makeTryFinallyForm(MethodType basicType) {
+        MethodType lambdaType = basicType.invokerType();
+
+        LambdaForm lform = basicType.form().cachedLambdaForm(MethodTypeForm.LF_TF);
+        if (lform != null) {
+            return lform;
+        }
+        final int THIS_MH      = 0;  // the BMH_LLLL
+        final int ARG_BASE     = 1;  // start of incoming arguments
+        final int ARG_LIMIT    = ARG_BASE + basicType.parameterCount();
+
+        int nameCursor = ARG_LIMIT;
+        final int GET_TARGET       = nameCursor++;
+        final int GET_CLEANUP      = nameCursor++;
+        final int GET_COLLECT_ARGS = nameCursor++;
+        final int GET_UNBOX_RESULT = nameCursor++;
+        final int BOXED_ARGS       = nameCursor++;
+        final int TRY_FINALLY      = nameCursor++;
+        final int UNBOX_RESULT     = nameCursor++;
+
+        Name[] names = arguments(nameCursor - ARG_LIMIT, lambdaType);
+
+        BoundMethodHandle.SpeciesData data = BoundMethodHandle.speciesData_LLLL();
+        names[THIS_MH]          = names[THIS_MH].withConstraint(data);
+        names[GET_TARGET]       = new Name(data.getterFunction(0), names[THIS_MH]);
+        names[GET_CLEANUP]      = new Name(data.getterFunction(1), names[THIS_MH]);
+        names[GET_COLLECT_ARGS] = new Name(data.getterFunction(2), names[THIS_MH]);
+        names[GET_UNBOX_RESULT] = new Name(data.getterFunction(3), names[THIS_MH]);
+
+        // t_{i}:L=MethodHandle.invokeBasic(collectArgs:L,a1:L,...);
+        MethodType collectArgsType = basicType.changeReturnType(Object.class);
+        MethodHandle invokeBasic = MethodHandles.basicInvoker(collectArgsType);
+        Object[] args = new Object[invokeBasic.type().parameterCount()];
+        args[0] = names[GET_COLLECT_ARGS];
+        System.arraycopy(names, ARG_BASE, args, 1, ARG_LIMIT-ARG_BASE);
+        names[BOXED_ARGS] = new Name(makeIntrinsic(invokeBasic, Intrinsic.TRY_FINALLY), args);
+
+        // t_{i+1}:L=MethodHandleImpl.tryFinally(target:L,exType:L,catcher:L,t_{i}:L);
+        Object[] tfArgs = new Object[] {names[GET_TARGET], names[GET_CLEANUP], names[BOXED_ARGS]};
+        names[TRY_FINALLY] = new Name(NF_tryFinally, tfArgs);
+
+        // t_{i+2}:I=MethodHandle.invokeBasic(unbox:L,t_{i+1}:L);
+        MethodHandle invokeBasicUnbox = MethodHandles.basicInvoker(MethodType.methodType(basicType.rtype(), Object.class));
+        Object[] unboxArgs  = new Object[] {names[GET_UNBOX_RESULT], names[TRY_FINALLY]};
+        names[UNBOX_RESULT] = new Name(invokeBasicUnbox, unboxArgs);
+
+        lform = new LambdaForm("tryFinally", lambdaType.parameterCount(), names);
+
+        return basicType.form().setCachedLambdaForm(MethodTypeForm.LF_TF, lform);
+    }
+
+    /**
+     * Intrinsified during LambdaForm compilation
+     * (see {@link InvokerBytecodeGenerator#emitTryFinally emitTryFinally}).
+     */
+    @LambdaForm.Hidden
+    static Object tryFinally(MethodHandle target, MethodHandle cleanup, Object... av) throws Throwable {
         Throwable t = null;
         Object r = null;
         try {
-            r = target.invoke(args);
+            // Use asFixedArity() to avoid unnecessary boxing of last argument for VarargsCollector case.
+            r = target.asFixedArity().invokeWithArguments(av);
         } catch (Throwable thrown) {
             t = thrown;
             throw t;
         } finally {
-            r = cleanup.invoke(t, r, args);
+            Object[] args = target.type().returnType() == void.class ? prepend(av, t) : prepend(av, t, r);
+            r = cleanup.asFixedArity().invokeWithArguments(args);
         }
         return r;
     }
 
-    /**
-     * A method that will be bound during construction of a {@code try-finally} handle with {@code void} return type by
-     * {@link MethodHandles#tryFinally(MethodHandle, MethodHandle)}.
-     *
-     * @param target the handle to wrap in a {@code try-finally} block. This will be bound.
-     * @param cleanup the handle to run in any case before returning. This will be bound.
-     * @param args the arguments to the call. These will remain as the argument list.
-     *
-     * @throws Throwable in case anything is thrown by the execution of {@code target}, the {@link Throwable} will be
-     *         passed to the {@code cleanup} handle, which may decide to throw any exception it sees fit.
-     */
-    static void tryFinallyVoidExecutor(MethodHandle target, MethodHandle cleanup, Object[] args) throws Throwable {
-        Throwable t = null;
-        try {
-            target.invoke(args);
-        } catch (Throwable thrown) {
-            t = thrown;
-            throw t;
-        } finally {
-            cleanup.invoke(t, args);
-        }
-    }
-
     // Indexes into constant method handles:
     static final int
             MH_cast                  =  0,
@@ -1958,17 +2098,14 @@
             MH_fillNewTypedArray     =  3,
             MH_fillNewArray          =  4,
             MH_arrayIdentity         =  5,
-            MH_looper                =  6,
-            MH_countedLoopPred       =  7,
-            MH_countedLoopStep       =  8,
-            MH_iteratePred           =  9,
-            MH_initIterator          = 10,
-            MH_iterateNext           = 11,
-            MH_tryFinallyExec        = 12,
-            MH_tryFinallyVoidExec    = 13,
-            MH_decrementCounter      = 14,
-            MH_Array_newInstance     = 15,
-            MH_LIMIT                 = 16;
+            MH_countedLoopPred       =  6,
+            MH_countedLoopStep       =  7,
+            MH_iteratePred           =  8,
+            MH_initIterator          =  9,
+            MH_iterateNext           = 10,
+            MH_decrementCounter      = 11,
+            MH_Array_newInstance     = 12,
+            MH_LIMIT                 = 13;
 
     static MethodHandle getConstantHandle(int idx) {
         MethodHandle handle = HANDLES[idx];
@@ -2013,10 +2150,6 @@
                     return makeIntrinsic(IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative",
                             MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)),
                         Intrinsic.SELECT_ALTERNATIVE);
-                case MH_looper:
-                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "looper", MethodType.methodType(Object.class,
-                            MethodHandle[].class, MethodHandle[].class, MethodHandle[].class, MethodHandle[].class,
-                            int.class, int.class, Object[].class));
                 case MH_countedLoopPred:
                     return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "countedLoopPredicate",
                             MethodType.methodType(boolean.class, int.class, int.class));
@@ -2032,12 +2165,6 @@
                 case MH_iterateNext:
                     return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iterateNext",
                             MethodType.methodType(Object.class, Iterator.class));
-                case MH_tryFinallyExec:
-                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "tryFinallyExecutor",
-                            MethodType.methodType(Object.class, MethodHandle.class, MethodHandle.class, Object[].class));
-                case MH_tryFinallyVoidExec:
-                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "tryFinallyVoidExecutor",
-                            MethodType.methodType(void.class, MethodHandle.class, MethodHandle.class, Object[].class));
                 case MH_decrementCounter:
                     return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "decrementCounter",
                             MethodType.methodType(int.class, int.class));
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Mon Jul 04 13:00:15 2016 +0900
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Mon Jul 04 10:08:18 2016 +0200
@@ -4233,7 +4233,7 @@
         assert Stream.of(fstep, fpred, ffini).flatMap(List::stream).map(MethodHandle::type).map(MethodType::parameterList).
                 allMatch(pl -> pl.equals(commonParameterSequence));
 
-        return MethodHandleImpl.makeLoop(loopReturnType, commonSuffix, commonPrefix, finit, fstep, fpred, ffini);
+        return MethodHandleImpl.makeLoop(loopReturnType, commonSuffix, finit, fstep, fpred, ffini);
     }
 
     private static List<MethodHandle> fillParameterTypes(List<MethodHandle> hs, final List<Class<?>> targetParams) {
@@ -4740,10 +4740,8 @@
         // The cleanup parameter list (minus the leading Throwable and result parameters) must be a sublist of the
         // target parameter list.
         cleanup = dropArgumentsToMatch(cleanup, (rtype == void.class ? 1 : 2), targetParamTypes, 0);
-        MethodHandle aTarget = target.asSpreader(Object[].class, target.type().parameterCount());
-        MethodHandle aCleanup = cleanup.asSpreader(Object[].class, targetParamTypes.size());
 
-        return MethodHandleImpl.makeTryFinally(aTarget, aCleanup, rtype, targetParamTypes);
+        return MethodHandleImpl.makeTryFinally(target, cleanup, rtype, targetParamTypes);
     }
 
     /**
--- a/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java	Mon Jul 04 13:00:15 2016 +0900
+++ b/src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java	Mon Jul 04 10:08:18 2016 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,8 +27,10 @@
 
 import jdk.internal.vm.annotation.Stable;
 import sun.invoke.util.Wrapper;
+
 import java.lang.ref.SoftReference;
-import static java.lang.invoke.MethodHandleStatics.*;
+
+import static java.lang.invoke.MethodHandleStatics.newIllegalArgumentException;
 
 /**
  * Shared information for a group of method types, which differ
@@ -81,7 +83,9 @@
             LF_MH_LINKER               = 15,  // linkToCallSite_MH
             LF_GWC                     = 16,  // guardWithCatch (catchException)
             LF_GWT                     = 17,  // guardWithTest
-            LF_LIMIT                   = 18;
+            LF_TF                      = 18,  // tryFinally
+            LF_LOOP                    = 19,  // loop
+            LF_LIMIT                   = 20;
 
     /** Return the type corresponding uniquely (1-1) to this MT-form.
      *  It might have any primitive returns or arguments, but will have no references except Object.
--- a/test/java/lang/invoke/MethodHandlesTest.java	Mon Jul 04 13:00:15 2016 +0900
+++ b/test/java/lang/invoke/MethodHandlesTest.java	Mon Jul 04 10:08:18 2016 +0200
@@ -2792,7 +2792,7 @@
             System.arraycopy(steps, 0, preSteps, 1, nargs);
             System.arraycopy(finis, 0, preFinis, 0, nargs); // finis are also offset by 1 for pre-checked loops
             // Convert to clause-major form.
-            MethodHandle[][] preClauses = new MethodHandle[nargs+1][4];
+            MethodHandle[][] preClauses = new MethodHandle[nargs + 1][4];
             MethodHandle[][] postClauses = new MethodHandle[nargs][4];
             toClauseMajor(preClauses, preInits, preSteps, prePreds, preFinis);
             toClauseMajor(postClauses, inits, steps, usePreds, finis);