Fri, 25 Oct 2013 15:21:12 +0200
8027301: Optimizations for Function.prototype.apply
Reviewed-by: jlaskey
1.1 --- a/src/jdk/nashorn/internal/runtime/CompiledFunctions.java Fri Oct 25 10:20:49 2013 +0200 1.2 +++ b/src/jdk/nashorn/internal/runtime/CompiledFunctions.java Fri Oct 25 15:21:12 2013 +0200 1.3 @@ -24,6 +24,7 @@ 1.4 */ 1.5 package jdk.nashorn.internal.runtime; 1.6 1.7 +import java.lang.invoke.MethodHandle; 1.8 import java.lang.invoke.MethodType; 1.9 import java.util.Iterator; 1.10 import java.util.TreeSet; 1.11 @@ -35,6 +36,8 @@ 1.12 @SuppressWarnings("serial") 1.13 final class CompiledFunctions extends TreeSet<CompiledFunction> { 1.14 1.15 + private CompiledFunction generic; 1.16 + 1.17 CompiledFunction best(final MethodType type) { 1.18 final Iterator<CompiledFunction> iter = iterator(); 1.19 while (iter.hasNext()) { 1.20 @@ -43,13 +46,10 @@ 1.21 return next; 1.22 } 1.23 } 1.24 - return mostGeneric(); 1.25 + return generic(); 1.26 } 1.27 1.28 boolean needsCallee() { 1.29 - for (final CompiledFunction inv : this) { 1.30 - assert ScriptFunctionData.needsCallee(inv.getInvoker()) == ScriptFunctionData.needsCallee(mostGeneric().getInvoker()); 1.31 - } 1.32 return ScriptFunctionData.needsCallee(mostGeneric().getInvoker()); 1.33 } 1.34 1.35 @@ -57,6 +57,48 @@ 1.36 return last(); 1.37 } 1.38 1.39 + CompiledFunction generic() { 1.40 + CompiledFunction gen = this.generic; 1.41 + if (gen == null) { 1.42 + gen = this.generic = makeGeneric(mostGeneric()); 1.43 + } 1.44 + return gen; 1.45 + } 1.46 + 1.47 + private static CompiledFunction makeGeneric(final CompiledFunction func) { 1.48 + final MethodHandle invoker = composeGenericMethod(func.getInvoker()); 1.49 + final MethodHandle constructor = func.hasConstructor() ? composeGenericMethod(func.getConstructor()) : null; 1.50 + return new CompiledFunction(invoker.type(), invoker, constructor); 1.51 + } 1.52 + 1.53 + /** 1.54 + * Takes a method handle, and returns a potentially different method handle that can be used in 1.55 + * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}. 1.56 + * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into 1.57 + * {@code Object} as well, except for the following ones: 1.58 + * <ul> 1.59 + * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li> 1.60 + * <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself 1.61 + * (callee) as an argument.</li> 1.62 + * </ul> 1.63 + * 1.64 + * @param mh the original method handle 1.65 + * 1.66 + * @return the new handle, conforming to the rules above. 1.67 + */ 1.68 + private static MethodHandle composeGenericMethod(final MethodHandle mh) { 1.69 + final MethodType type = mh.type(); 1.70 + final boolean isVarArg = ScriptFunctionData.isVarArg(mh); 1.71 + final int paramCount = isVarArg ? type.parameterCount() - 1 : type.parameterCount(); 1.72 + 1.73 + MethodType newType = MethodType.genericMethodType(paramCount, isVarArg); 1.74 + 1.75 + if (ScriptFunctionData.needsCallee(mh)) { 1.76 + newType = newType.changeParameterType(0, ScriptFunction.class); 1.77 + } 1.78 + return type.equals(newType) ? mh : mh.asType(newType); 1.79 + } 1.80 + 1.81 /** 1.82 * Is the given type even more specific than this entire list? That means 1.83 * we have an opportunity for more specific versions of the method
2.1 --- a/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java Fri Oct 25 10:20:49 2013 +0200 2.2 +++ b/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java Fri Oct 25 15:21:12 2013 +0200 2.3 @@ -40,7 +40,7 @@ 2.4 * 2.5 * @param name name 2.6 * @param arity arity 2.7 - * @param list precompiled code 2.8 + * @param functions precompiled code 2.9 * @param isStrict strict 2.10 * @param isBuiltin builtin 2.11 * @param isConstructor constructor 2.12 @@ -73,12 +73,13 @@ 2.13 } 2.14 2.15 private void addInvoker(final MethodHandle mh) { 2.16 - boolean needsCallee = needsCallee(mh); 2.17 if (isConstructor(mh)) { 2.18 - //only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor 2.19 - //is too conservative a check. However, isConstructor(mh) always implies isConstructor param 2.20 + // only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor 2.21 + // is too conservative a check. However, isConstructor(mh) always implies isConstructor param 2.22 assert isConstructor(); 2.23 - code.add(new CompiledFunction(mh.type(), MH.insertArguments(mh, 0, false), composeConstructor(MH.insertArguments(mh, 0, true), needsCallee))); //make sure callee state can be determined when we reach constructor 2.24 + final MethodHandle invoker = MH.insertArguments(mh, 0, false); 2.25 + final MethodHandle constructor = composeConstructor(MH.insertArguments(mh, 0, true)); 2.26 + code.add(new CompiledFunction(mh.type(), invoker, constructor)); 2.27 } else { 2.28 code.add(new CompiledFunction(mh.type(), mh)); 2.29 }
3.1 --- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Fri Oct 25 10:20:49 2013 +0200 3.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Fri Oct 25 15:21:12 2013 +0200 3.3 @@ -213,13 +213,13 @@ 3.4 */ 3.5 public final MethodHandle getGenericInvoker() { 3.6 ensureCodeGenerated(); 3.7 - return composeGenericMethod(code.mostGeneric().getInvoker()); 3.8 + return code.generic().getInvoker(); 3.9 } 3.10 3.11 final MethodHandle getGenericConstructor() { 3.12 ensureCodeGenerated(); 3.13 - ensureConstructor(code.mostGeneric()); 3.14 - return composeGenericMethod(code.mostGeneric().getConstructor()); 3.15 + ensureConstructor(code.generic()); 3.16 + return code.generic().getConstructor(); 3.17 } 3.18 3.19 private CompiledFunction getBest(final MethodType callSiteType) { 3.20 @@ -267,18 +267,17 @@ 3.21 } 3.22 3.23 /** 3.24 - * Compose a constructor given a primordial constructor handle 3.25 + * Compose a constructor given a primordial constructor handle. 3.26 * 3.27 - * @param ctor primordial constructor handle 3.28 - * @param needsCallee do we need to pass a callee 3.29 - * 3.30 + * @param ctor primordial constructor handle 3.31 * @return the composed constructor 3.32 */ 3.33 - protected MethodHandle composeConstructor(final MethodHandle ctor, final boolean needsCallee) { 3.34 + protected MethodHandle composeConstructor(final MethodHandle ctor) { 3.35 // If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having 3.36 // "this" in the first argument position is what allows the elegant folded composition of 3.37 // (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor 3.38 // always returns Object. 3.39 + final boolean needsCallee = needsCallee(ctor); 3.40 MethodHandle composedCtor = needsCallee ? swapCalleeAndThis(ctor) : ctor; 3.41 3.42 composedCtor = changeReturnTypeToObject(composedCtor); 3.43 @@ -472,33 +471,6 @@ 3.44 } 3.45 3.46 /** 3.47 - * Takes a method handle, and returns a potentially different method handle that can be used in 3.48 - * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}. 3.49 - * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into 3.50 - * {@code Object} as well, except for the following ones: 3.51 - * <ul> 3.52 - * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li> 3.53 - * <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself 3.54 - * (callee) as an argument.</li> 3.55 - * </ul> 3.56 - * 3.57 - * @param mh the original method handle 3.58 - * 3.59 - * @return the new handle, conforming to the rules above. 3.60 - */ 3.61 - protected MethodHandle composeGenericMethod(final MethodHandle mh) { 3.62 - final MethodType type = mh.type(); 3.63 - MethodType newType = type.generic(); 3.64 - if (isVarArg(mh)) { 3.65 - newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class); 3.66 - } 3.67 - if (needsCallee(mh)) { 3.68 - newType = newType.changeParameterType(0, ScriptFunction.class); 3.69 - } 3.70 - return type.equals(newType) ? mh : mh.asType(newType); 3.71 - } 3.72 - 3.73 - /** 3.74 * Execute this script function. 3.75 * 3.76 * @param self Target object. 3.77 @@ -508,10 +480,9 @@ 3.78 * @throws Throwable if there is an exception/error with the invocation or thrown from it 3.79 */ 3.80 Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable { 3.81 - final MethodHandle mh = getGenericInvoker(); 3.82 - 3.83 - final Object selfObj = convertThisObject(self); 3.84 - final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; 3.85 + final MethodHandle mh = getGenericInvoker(); 3.86 + final Object selfObj = convertThisObject(self); 3.87 + final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; 3.88 3.89 if (isVarArg(mh)) { 3.90 if (needsCallee(mh)) { 3.91 @@ -531,6 +502,12 @@ 3.92 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1)); 3.93 case 5: 3.94 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 3.95 + case 6: 3.96 + return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3)); 3.97 + case 7: 3.98 + return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4)); 3.99 + case 8: 3.100 + return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5)); 3.101 default: 3.102 return mh.invokeWithArguments(withArguments(fn, selfObj, paramCount, args)); 3.103 } 3.104 @@ -545,15 +522,20 @@ 3.105 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1)); 3.106 case 4: 3.107 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 3.108 + case 5: 3.109 + return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3)); 3.110 + case 6: 3.111 + return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4)); 3.112 + case 7: 3.113 + return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5)); 3.114 default: 3.115 return mh.invokeWithArguments(withArguments(null, selfObj, paramCount, args)); 3.116 } 3.117 } 3.118 3.119 Object construct(final ScriptFunction fn, final Object... arguments) throws Throwable { 3.120 - final MethodHandle mh = getGenericConstructor(); 3.121 - 3.122 - final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; 3.123 + final MethodHandle mh = getGenericConstructor(); 3.124 + final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; 3.125 3.126 if (isVarArg(mh)) { 3.127 if (needsCallee(mh)) { 3.128 @@ -573,6 +555,12 @@ 3.129 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1)); 3.130 case 4: 3.131 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2)); 3.132 + case 5: 3.133 + return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3)); 3.134 + case 6: 3.135 + return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4)); 3.136 + case 7: 3.137 + return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5)); 3.138 default: 3.139 return mh.invokeWithArguments(withArguments(fn, paramCount, args)); 3.140 } 3.141 @@ -587,6 +575,12 @@ 3.142 return mh.invokeExact(getArg(args, 0), getArg(args, 1)); 3.143 case 3: 3.144 return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2)); 3.145 + case 4: 3.146 + return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3)); 3.147 + case 5: 3.148 + return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4)); 3.149 + case 6: 3.150 + return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5)); 3.151 default: 3.152 return mh.invokeWithArguments(withArguments(null, paramCount, args)); 3.153 } 3.154 @@ -664,20 +658,21 @@ 3.155 * @return the adapted handle 3.156 */ 3.157 private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) { 3.158 - return MH.asType(mh, mh.type().changeReturnType(Object.class)); 3.159 + final MethodType type = mh.type(); 3.160 + return (type.returnType() == Object.class) ? mh : MH.asType(mh, type.changeReturnType(Object.class)); 3.161 } 3.162 3.163 private void ensureConstructor(final CompiledFunction inv) { 3.164 if (!inv.hasConstructor()) { 3.165 - inv.setConstructor(composeConstructor(inv.getInvoker(), needsCallee(inv.getInvoker()))); 3.166 + inv.setConstructor(composeConstructor(inv.getInvoker())); 3.167 } 3.168 } 3.169 3.170 /** 3.171 - * Heuristic to figure out if the method handle has a callee argument. If it's type is either 3.172 - * {@code (boolean, ScriptFunction, ...)} or {@code (ScriptFunction, ...)}, then we'll assume it has 3.173 - * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly 3.174 - * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore 3.175 + * Heuristic to figure out if the method handle has a callee argument. If it's type is 3.176 + * {@code (ScriptFunction, ...)}, then we'll assume it has a callee argument. We need this as 3.177 + * the constructor above is not passed this information, and can't just blindly assume it's false 3.178 + * (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore 3.179 * they also always receive a callee). 3.180 * 3.181 * @param mh the examined method handle 3.182 @@ -685,18 +680,8 @@ 3.183 * @return true if the method handle expects a callee, false otherwise 3.184 */ 3.185 protected static boolean needsCallee(final MethodHandle mh) { 3.186 - final MethodType type = mh.type(); 3.187 - final int length = type.parameterCount(); 3.188 - 3.189 - if (length == 0) { 3.190 - return false; 3.191 - } 3.192 - 3.193 - if (type.parameterType(0) == ScriptFunction.class) { 3.194 - return true; 3.195 - } 3.196 - 3.197 - return length > 1 && type.parameterType(0) == boolean.class && type.parameterType(1) == ScriptFunction.class; 3.198 + final MethodType type = mh.type(); 3.199 + return (type.parameterCount() > 0 && type.parameterType(0) == ScriptFunction.class); 3.200 } 3.201 3.202 /**