# HG changeset patch # User vlivanov # Date 1410362390 -14400 # Node ID 0abc9399a1a61bd1b63829e99f8a7591aefacfc3 # Parent 71b6a6f208c018599a0487b0aba94cb9d0b70e2d 8050200: Make LambdaForm intrinsics detection more robust Reviewed-by: vlivanov, psandoz Contributed-by: john.r.rose@oracle.com diff -r 71b6a6f208c0 -r 0abc9399a1a6 jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Wed Sep 10 19:19:49 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Wed Sep 10 19:19:50 2014 +0400 @@ -35,7 +35,7 @@ import static java.lang.invoke.LambdaForm.BasicType.*; import static java.lang.invoke.MethodHandleStatics.*; import static java.lang.invoke.MethodHandleNatives.Constants.*; -import java.lang.invoke.MethodHandleImpl.ArrayAccessor; + import sun.invoke.util.VerifyAccess; import sun.invoke.util.VerifyType; import sun.invoke.util.Wrapper; @@ -636,26 +636,44 @@ Name onStack = null; for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) { Name name = lambdaForm.names[i]; - MemberName member = name.function.member(); - Class rtype = name.function.methodType().returnType(); emitStoreResult(onStack); onStack = name; // unless otherwise modified below + MethodHandleImpl.Intrinsic intr = name.function.intrinsicName(); + switch (intr) { + case SELECT_ALTERNATIVE: + assert isSelectAlternative(i); + onStack = emitSelectAlternative(name, lambdaForm.names[i+1]); + i++; // skip MH.invokeBasic of the selectAlternative result + continue; + case GUARD_WITH_CATCH: + assert isGuardWithCatch(i); + onStack = emitGuardWithCatch(i); + i = i+2; // Jump to the end of GWC idiom + continue; + case NEW_ARRAY: + Class rtype = name.function.methodType().returnType(); + if (isStaticallyNameable(rtype)) { + emitNewArray(name); + continue; + } + break; + case ARRAY_LOAD: + emitArrayLoad(name); + continue; + case ARRAY_STORE: + emitArrayStore(name); + continue; + case NONE: + // no intrinsic associated + break; + default: + throw newInternalError("Unknown intrinsic: "+intr); + } - if (isSelectAlternative(i)) { - onStack = emitSelectAlternative(name, lambdaForm.names[i + 1]); - i++; // skip MH.invokeBasic of the selectAlternative result - } else if (isGuardWithCatch(i)) { - onStack = emitGuardWithCatch(i); - i = i+2; // Jump to the end of GWC idiom - } else if (isNewArray(rtype, name)) { - emitNewArray(rtype, name); - } else if (isArrayLoad(member)) { - emitArrayLoad(name); - } else if (isArrayStore(member)) { - emitArrayStore(name); - } else if (isStaticallyInvocable(member)) { - emitStaticInvoke(name); + MemberName member = name.function.member(); + if (isStaticallyInvocable(member)) { + emitStaticInvoke(member, name); } else { emitInvoke(name); } @@ -672,20 +690,6 @@ return classFile; } - boolean isArrayLoad(MemberName member) { - return member != null && - member.getDeclaringClass() == ArrayAccessor.class && - member.getName() != null && - member.getName().startsWith("getElement"); - } - - boolean isArrayStore(MemberName member) { - return member != null && - member.getDeclaringClass() == ArrayAccessor.class && - member.getName() != null && - member.getName().startsWith("setElement"); - } - void emitArrayLoad(Name name) { emitArrayOp(name, Opcodes.AALOAD); } void emitArrayStore(Name name) { emitArrayOp(name, Opcodes.AASTORE); } @@ -837,33 +841,31 @@ } } - boolean isNewArray(Class rtype, Name name) { - return rtype.isArray() && - isStaticallyNameable(rtype) && - isArrayBuilder(name.function.resolvedHandle) && - name.arguments.length > 0; - } - - void emitNewArray(Class rtype, Name name) throws InternalError { + void emitNewArray(Name name) throws InternalError { + Class rtype = name.function.methodType().returnType(); + if (name.arguments.length == 0) { + // The array will be a constant. + Object emptyArray; + try { + emptyArray = name.function.resolvedHandle.invoke(); + } catch (Throwable ex) { + throw newInternalError(ex); + } + assert(java.lang.reflect.Array.getLength(emptyArray) == 0); + assert(emptyArray.getClass() == rtype); // exact typing + mv.visitLdcInsn(constantPlaceholder(emptyArray)); + emitReferenceCast(rtype, emptyArray); + return; + } Class arrayElementType = rtype.getComponentType(); + assert(arrayElementType != null); emitIconstInsn(name.arguments.length); - int xas; + int xas = Opcodes.AASTORE; if (!arrayElementType.isPrimitive()) { mv.visitTypeInsn(Opcodes.ANEWARRAY, getInternalName(arrayElementType)); - xas = Opcodes.AASTORE; } else { - int tc; - switch (Wrapper.forPrimitiveType(arrayElementType)) { - case BOOLEAN: tc = Opcodes.T_BOOLEAN; xas = Opcodes.BASTORE; break; - case BYTE: tc = Opcodes.T_BYTE; xas = Opcodes.BASTORE; break; - case CHAR: tc = Opcodes.T_CHAR; xas = Opcodes.CASTORE; break; - case SHORT: tc = Opcodes.T_SHORT; xas = Opcodes.SASTORE; break; - case INT: tc = Opcodes.T_INT; xas = Opcodes.IASTORE; break; - case LONG: tc = Opcodes.T_LONG; xas = Opcodes.LASTORE; break; - case FLOAT: tc = Opcodes.T_FLOAT; xas = Opcodes.FASTORE; break; - case DOUBLE: tc = Opcodes.T_DOUBLE; xas = Opcodes.DASTORE; break; - default: throw new InternalError(rtype.getName()); - } + byte tc = arrayTypeCode(Wrapper.forPrimitiveType(arrayElementType)); + xas = arrayInsnOpcode(tc, xas); mv.visitIntInsn(Opcodes.NEWARRAY, tc); } // store arguments @@ -890,24 +892,6 @@ throw new InternalError("refKind="+refKind); } - static boolean isArrayBuilder(MethodHandle fn) { - if (fn == null) - return false; - MethodType mtype = fn.type(); - Class rtype = mtype.returnType(); - Class arrayElementType = rtype.getComponentType(); - if (arrayElementType == null) - return false; - List> ptypes = mtype.parameterList(); - int size = ptypes.size(); - if (!ptypes.equals(Collections.nCopies(size, arrayElementType))) - return false; - // Assume varargsArray caches pointers. - if (fn != MethodHandleImpl.varargsArray(rtype, size)) - return false; - return true; - } - /** * Check if MemberName is a call to a method named {@code name} in class {@code declaredClass}. */ diff -r 71b6a6f208c0 -r 0abc9399a1a6 jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Wed Sep 10 19:19:49 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Wed Sep 10 19:19:50 2014 +0400 @@ -27,11 +27,10 @@ import java.lang.annotation.*; import java.lang.reflect.Method; -import java.util.Map; import java.util.List; import java.util.Arrays; import java.util.HashMap; -import java.util.concurrent.ConcurrentHashMap; + import sun.invoke.util.Wrapper; import java.lang.reflect.Field; @@ -1371,6 +1370,11 @@ public boolean isConstantZero() { return this.equals(constantZero(returnType())); } + + public MethodHandleImpl.Intrinsic intrinsicName() { + return resolvedHandle == null ? MethodHandleImpl.Intrinsic.NONE + : resolvedHandle.intrinsicName(); + } } public static String basicTypeSignature(MethodType type) { diff -r 71b6a6f208c0 -r 0abc9399a1a6 jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Wed Sep 10 19:19:49 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java Wed Sep 10 19:19:50 2014 +0400 @@ -27,11 +27,8 @@ import java.util.*; -import java.lang.invoke.LambdaForm.BasicType; -import sun.misc.Unsafe; import static java.lang.invoke.MethodHandleStatics.*; -import static java.lang.invoke.LambdaForm.BasicType.*; /** * A method handle is a typed, directly executable reference to an underlying method, @@ -1356,6 +1353,12 @@ } /*non-public*/ + MethodHandleImpl.Intrinsic intrinsicName() { + // no special intrinsic meaning to most MHs + return MethodHandleImpl.Intrinsic.NONE; + } + + /*non-public*/ MethodHandle withInternalMemberName(MemberName member, boolean isInvokeSpecial) { if (member != null) { return MethodHandleImpl.makeWrappedMember(this, member, isInvokeSpecial); diff -r 71b6a6f208c0 -r 0abc9399a1a6 jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java --- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Sep 10 19:19:49 2014 +0400 +++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Sep 10 19:19:50 2014 +0400 @@ -30,7 +30,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.List; import sun.invoke.empty.Empty; import sun.invoke.util.ValueConversions; @@ -86,6 +85,7 @@ // safe to view non-strictly, because element type follows from array type mh = mh.viewAsType(correctType, false); } + mh = makeIntrinsic(mh, (isSetter ? Intrinsic.ARRAY_STORE : Intrinsic.ARRAY_LOAD)); // Atomically update accessor cache. synchronized(cache) { if (cache[cacheIndex] == null) { @@ -111,8 +111,8 @@ static final MethodHandle OBJECT_ARRAY_GETTER, OBJECT_ARRAY_SETTER; static { MethodHandle[] cache = TYPED_ACCESSORS.get(Object[].class); - cache[GETTER_INDEX] = OBJECT_ARRAY_GETTER = getAccessor(Object[].class, false); - cache[SETTER_INDEX] = OBJECT_ARRAY_SETTER = getAccessor(Object[].class, true); + cache[GETTER_INDEX] = OBJECT_ARRAY_GETTER = makeIntrinsic(getAccessor(Object[].class, false), Intrinsic.ARRAY_LOAD); + cache[SETTER_INDEX] = OBJECT_ARRAY_SETTER = makeIntrinsic(getAccessor(Object[].class, true), Intrinsic.ARRAY_STORE); assert(InvokerBytecodeGenerator.isStaticallyInvocable(ArrayAccessor.OBJECT_ARRAY_GETTER.internalMemberName())); assert(InvokerBytecodeGenerator.isStaticallyInvocable(ArrayAccessor.OBJECT_ARRAY_SETTER.internalMemberName())); @@ -502,10 +502,10 @@ static final NamedFunction NF_checkSpreadArgument; static final NamedFunction NF_guardWithCatch; - static final NamedFunction NF_selectAlternative; static final NamedFunction NF_throwException; static final MethodHandle MH_castReference; + static final MethodHandle MH_selectAlternative; static final MethodHandle MH_copyAsPrimitiveArray; static final MethodHandle MH_fillNewTypedArray; static final MethodHandle MH_fillNewArray; @@ -516,13 +516,10 @@ NF_checkSpreadArgument = new NamedFunction(MHI.getDeclaredMethod("checkSpreadArgument", Object.class, int.class)); NF_guardWithCatch = new NamedFunction(MHI.getDeclaredMethod("guardWithCatch", MethodHandle.class, Class.class, MethodHandle.class, Object[].class)); - NF_selectAlternative = new NamedFunction(MHI.getDeclaredMethod("selectAlternative", boolean.class, MethodHandle.class, - MethodHandle.class)); NF_throwException = new NamedFunction(MHI.getDeclaredMethod("throwException", Throwable.class)); NF_checkSpreadArgument.resolve(); NF_guardWithCatch.resolve(); - NF_selectAlternative.resolve(); NF_throwException.resolve(); MH_castReference = IMPL_LOOKUP.findStatic(MHI, "castReference", @@ -535,6 +532,11 @@ MethodType.methodType(Object[].class, Integer.class, Object[].class)); MH_fillNewTypedArray = IMPL_LOOKUP.findStatic(MHI, "fillNewTypedArray", MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class)); + + MH_selectAlternative = makeIntrinsic( + IMPL_LOOKUP.findStatic(MHI, "selectAlternative", + MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)), + Intrinsic.SELECT_ALTERNATIVE); } catch (ReflectiveOperationException ex) { throw newInternalError(ex); } @@ -620,7 +622,7 @@ // call selectAlternative Object[] selectArgs = { names[arity + 1], target, fallback }; - names[arity + 2] = new Name(Lazy.NF_selectAlternative, selectArgs); + names[arity + 2] = new Name(Lazy.MH_selectAlternative, selectArgs); targetArgs[0] = names[arity + 2]; // call target or fallback @@ -689,7 +691,7 @@ 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(new NamedFunction(invokeBasic), args); + names[BOXED_ARGS] = new Name(makeIntrinsic(invokeBasic, Intrinsic.GUARD_WITH_CATCH), args); // t_{i+1}:L=MethodHandleImpl.guardWithCatch(target:L,exType:L,catcher:L,t_{i}:L); Object[] gwcArgs = new Object[] {names[GET_TARGET], names[GET_CLASS], names[GET_CATCHER], names[BOXED_ARGS]}; @@ -698,7 +700,7 @@ // 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_CATCH]}; - names[UNBOX_RESULT] = new Name(new NamedFunction(invokeBasicUnbox), unboxArgs); + names[UNBOX_RESULT] = new Name(invokeBasicUnbox, unboxArgs); lform = new LambdaForm("guardWithCatch", lambdaType.parameterCount(), names); @@ -1004,6 +1006,63 @@ return new WrappedMember(target, target.type(), member, isInvokeSpecial, null); } + /** Intrinsic IDs */ + /*non-public*/ + enum Intrinsic { + SELECT_ALTERNATIVE, + GUARD_WITH_CATCH, + NEW_ARRAY, + ARRAY_LOAD, + ARRAY_STORE, + NONE // no intrinsic associated + } + + /** Mark arbitrary method handle as intrinsic. + * InvokerBytecodeGenerator uses this info to produce more efficient bytecode shape. */ + private static final class IntrinsicMethodHandle extends DelegatingMethodHandle { + private final MethodHandle target; + private final Intrinsic intrinsicName; + + IntrinsicMethodHandle(MethodHandle target, Intrinsic intrinsicName) { + super(target.type(), target); + this.target = target; + this.intrinsicName = intrinsicName; + } + + @Override + protected MethodHandle getTarget() { + return target; + } + + @Override + Intrinsic intrinsicName() { + return intrinsicName; + } + + @Override + public MethodHandle asTypeUncached(MethodType newType) { + // This MH is an alias for target, except for the intrinsic name + // Drop the name if there is any conversion. + return asTypeCache = target.asType(newType); + } + + @Override + String internalProperties() { + return super.internalProperties() + + "\n& Intrinsic="+intrinsicName; + } + } + + static MethodHandle makeIntrinsic(MethodHandle target, Intrinsic intrinsicName) { + if (intrinsicName == target.intrinsicName()) + return target; + return new IntrinsicMethodHandle(target, intrinsicName); + } + + static MethodHandle makeIntrinsic(MethodType type, LambdaForm form, Intrinsic intrinsicName) { + return new IntrinsicMethodHandle(SimpleMethodHandle.make(type, form), intrinsicName); + } + /// Collection of multiple arguments. private static MethodHandle findCollector(String name, int nargs, Class rtype, Class... ptypes) { @@ -1053,6 +1112,7 @@ for (;;) { MethodHandle mh = findCollector("array", mhs.size(), Object[].class); if (mh == null) break; + mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY); mhs.add(mh); } assert(mhs.size() == 11); // current number of methods @@ -1131,9 +1191,11 @@ MethodHandle mh = ARRAYS[nargs]; if (mh != null) return mh; mh = findCollector("array", nargs, Object[].class); + if (mh != null) mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY); if (mh != null) return ARRAYS[nargs] = mh; mh = buildVarargsArray(Lazy.MH_fillNewArray, Lazy.MH_arrayIdentity, nargs); assert(assertCorrectArity(mh, nargs)); + mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY); return ARRAYS[nargs] = mh; } @@ -1263,6 +1325,7 @@ mh = buildVarargsArray(builder, producer, nargs); } mh = mh.asType(MethodType.methodType(arrayType, Collections.>nCopies(nargs, elemType))); + mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY); assert(assertCorrectArity(mh, nargs)); if (nargs < cache.length) cache[nargs] = mh;