Wed, 15 Oct 2014 16:00:21 +0200
8060241: Immediately invoked function expressions cause lot of deoptimization
Reviewed-by: hannesw, lagergren
1.1 --- a/src/jdk/nashorn/internal/codegen/TypeEvaluator.java Wed Oct 15 15:57:46 2014 +0200 1.2 +++ b/src/jdk/nashorn/internal/codegen/TypeEvaluator.java Wed Oct 15 16:00:21 2014 +0200 1.3 @@ -29,9 +29,12 @@ 1.4 import static jdk.nashorn.internal.runtime.Property.NOT_ENUMERABLE; 1.5 import static jdk.nashorn.internal.runtime.Property.NOT_WRITABLE; 1.6 1.7 +import java.lang.invoke.MethodType; 1.8 import jdk.nashorn.internal.codegen.types.Type; 1.9 import jdk.nashorn.internal.ir.AccessNode; 1.10 +import jdk.nashorn.internal.ir.CallNode; 1.11 import jdk.nashorn.internal.ir.Expression; 1.12 +import jdk.nashorn.internal.ir.FunctionNode; 1.13 import jdk.nashorn.internal.ir.IdentNode; 1.14 import jdk.nashorn.internal.ir.IndexNode; 1.15 import jdk.nashorn.internal.ir.Optimistic; 1.16 @@ -40,6 +43,8 @@ 1.17 import jdk.nashorn.internal.runtime.FindProperty; 1.18 import jdk.nashorn.internal.runtime.JSType; 1.19 import jdk.nashorn.internal.runtime.Property; 1.20 +import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; 1.21 +import jdk.nashorn.internal.runtime.ScriptFunction; 1.22 import jdk.nashorn.internal.runtime.ScriptObject; 1.23 import jdk.nashorn.internal.runtime.ScriptRuntime; 1.24 1.25 @@ -48,6 +53,13 @@ 1.26 * Used during recompilation. 1.27 */ 1.28 final class TypeEvaluator { 1.29 + /** 1.30 + * Type signature for invocation of functions without parameters: we must pass (callee, this) of type 1.31 + * (ScriptFunction, Object) respectively. We also use Object as the return type (we must pass something, 1.32 + * but it'll be ignored; it can't be void, though). 1.33 + */ 1.34 + private static final MethodType EMPTY_INVOCATION_TYPE = MethodType.methodType(Object.class, ScriptFunction.class, Object.class); 1.35 + 1.36 private final Compiler compiler; 1.37 private final ScriptObject runtimeScope; 1.38 1.39 @@ -191,28 +203,39 @@ 1.40 return null; 1.41 } 1.42 return getPropertyType(runtimeScope, ((IdentNode)expr).getName()); 1.43 - } 1.44 - 1.45 - if (expr instanceof AccessNode) { 1.46 + } else if (expr instanceof AccessNode) { 1.47 final AccessNode accessNode = (AccessNode)expr; 1.48 final Object base = evaluateSafely(accessNode.getBase()); 1.49 if (!(base instanceof ScriptObject)) { 1.50 return null; 1.51 } 1.52 return getPropertyType((ScriptObject)base, accessNode.getProperty()); 1.53 - } 1.54 - 1.55 - if (expr instanceof IndexNode) { 1.56 + } else if (expr instanceof IndexNode) { 1.57 final IndexNode indexNode = (IndexNode)expr; 1.58 final Object base = evaluateSafely(indexNode.getBase()); 1.59 if(base instanceof NativeArray || base instanceof ArrayBufferView) { 1.60 - // NOTE: optimistic array getters throw UnwarrantedOptimismException based on the type of their underlying 1.61 - // array storage, not based on values of individual elements. Thus, a LongArrayData will throw UOE for every 1.62 - // optimistic int linkage attempt, even if the long value being returned in the first invocation would be 1.63 - // representable as int. That way, we can presume that the array's optimistic type is the most optimistic 1.64 - // type for which an element getter has a chance of executing successfully. 1.65 + // NOTE: optimistic array getters throw UnwarrantedOptimismException based on the type of their 1.66 + // underlying array storage, not based on values of individual elements. Thus, a LongArrayData will 1.67 + // throw UOE for every optimistic int linkage attempt, even if the long value being returned in the 1.68 + // first invocation would be representable as int. That way, we can presume that the array's optimistic 1.69 + // type is the most optimistic type for which an element getter has a chance of executing successfully. 1.70 return ((ScriptObject)base).getArray().getOptimisticType(); 1.71 } 1.72 + } else if (expr instanceof CallNode) { 1.73 + // Currently, we'll only try to guess the return type of immediately invoked function expressions with no 1.74 + // parameters, that is (function() { ... })(). We could do better, but these are all heuristics and we can 1.75 + // gradually introduce them as needed. An easy one would be to do the same for .call(this) idiom. 1.76 + final CallNode callExpr = (CallNode)expr; 1.77 + final Expression fnExpr = callExpr.getFunction(); 1.78 + if (fnExpr instanceof FunctionNode) { 1.79 + final FunctionNode fn = (FunctionNode)fnExpr; 1.80 + if (callExpr.getArgs().isEmpty()) { 1.81 + final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(fn.getId()); 1.82 + if (data != null) { 1.83 + return Type.typeFor(data.getReturnType(EMPTY_INVOCATION_TYPE, runtimeScope)); 1.84 + } 1.85 + } 1.86 + } 1.87 } 1.88 1.89 return null;
2.1 --- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed Oct 15 15:57:46 2014 +0200 2.2 +++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed Oct 15 16:00:21 2014 +0200 2.3 @@ -676,6 +676,22 @@ 2.4 return addCode(lookup(fnInit).asType(toType), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags()); 2.5 } 2.6 2.7 + /** 2.8 + * Returns the return type of a function specialization for particular parameter types.<br> 2.9 + * <b>Be aware that the way this is implemented, it forces full materialization (compilation and installation) of 2.10 + * code for that specialization.</b> 2.11 + * @param callSiteType the parameter types at the call site. It must include the mandatory {@code callee} and 2.12 + * {@code this} parameters, so it needs to start with at least {@code ScriptFunction.class} and 2.13 + * {@code Object.class} class. Since the return type of the function is calculated from the code itself, it is 2.14 + * irrelevant and should be set to {@code Object.class}. 2.15 + * @param runtimeScope a current runtime scope. Can be null but when it's present it will be used as a source of 2.16 + * current runtime values that can improve the compiler's type speculations (and thus reduce the need for later 2.17 + * recompilations) if the specialization is not already present and thus needs to be freshly compiled. 2.18 + * @return the return type of the function specialization. 2.19 + */ 2.20 + public Class<?> getReturnType(final MethodType callSiteType, final ScriptObject runtimeScope) { 2.21 + return getBest(callSiteType, runtimeScope, CompiledFunction.NO_FUNCTIONS).type().returnType(); 2.22 + } 2.23 2.24 @Override 2.25 synchronized CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {