# HG changeset patch # User attila # Date 1413381621 -7200 # Node ID ef61cca021aa54023c5110e281c32684eb775adf # Parent bc8ce3f84b84b25d92e096d9ffdd4dd34360f6ac 8060241: Immediately invoked function expressions cause lot of deoptimization Reviewed-by: hannesw, lagergren diff -r bc8ce3f84b84 -r ef61cca021aa nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java Wed Oct 15 15:57:46 2014 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java Wed Oct 15 16:00:21 2014 +0200 @@ -29,9 +29,12 @@ import static jdk.nashorn.internal.runtime.Property.NOT_ENUMERABLE; import static jdk.nashorn.internal.runtime.Property.NOT_WRITABLE; +import java.lang.invoke.MethodType; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.AccessNode; +import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.Expression; +import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.Optimistic; @@ -40,6 +43,8 @@ import jdk.nashorn.internal.runtime.FindProperty; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.Property; +import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; +import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; @@ -48,6 +53,13 @@ * Used during recompilation. */ final class TypeEvaluator { + /** + * Type signature for invocation of functions without parameters: we must pass (callee, this) of type + * (ScriptFunction, Object) respectively. We also use Object as the return type (we must pass something, + * but it'll be ignored; it can't be void, though). + */ + private static final MethodType EMPTY_INVOCATION_TYPE = MethodType.methodType(Object.class, ScriptFunction.class, Object.class); + private final Compiler compiler; private final ScriptObject runtimeScope; @@ -191,28 +203,39 @@ return null; } return getPropertyType(runtimeScope, ((IdentNode)expr).getName()); - } - - if (expr instanceof AccessNode) { + } else if (expr instanceof AccessNode) { final AccessNode accessNode = (AccessNode)expr; final Object base = evaluateSafely(accessNode.getBase()); if (!(base instanceof ScriptObject)) { return null; } return getPropertyType((ScriptObject)base, accessNode.getProperty()); - } - - if (expr instanceof IndexNode) { + } else if (expr instanceof IndexNode) { final IndexNode indexNode = (IndexNode)expr; final Object base = evaluateSafely(indexNode.getBase()); if(base instanceof NativeArray || base instanceof ArrayBufferView) { - // NOTE: optimistic array getters throw UnwarrantedOptimismException based on the type of their underlying - // array storage, not based on values of individual elements. Thus, a LongArrayData will throw UOE for every - // optimistic int linkage attempt, even if the long value being returned in the first invocation would be - // representable as int. That way, we can presume that the array's optimistic type is the most optimistic - // type for which an element getter has a chance of executing successfully. + // NOTE: optimistic array getters throw UnwarrantedOptimismException based on the type of their + // underlying array storage, not based on values of individual elements. Thus, a LongArrayData will + // throw UOE for every optimistic int linkage attempt, even if the long value being returned in the + // first invocation would be representable as int. That way, we can presume that the array's optimistic + // type is the most optimistic type for which an element getter has a chance of executing successfully. return ((ScriptObject)base).getArray().getOptimisticType(); } + } else if (expr instanceof CallNode) { + // Currently, we'll only try to guess the return type of immediately invoked function expressions with no + // parameters, that is (function() { ... })(). We could do better, but these are all heuristics and we can + // gradually introduce them as needed. An easy one would be to do the same for .call(this) idiom. + final CallNode callExpr = (CallNode)expr; + final Expression fnExpr = callExpr.getFunction(); + if (fnExpr instanceof FunctionNode) { + final FunctionNode fn = (FunctionNode)fnExpr; + if (callExpr.getArgs().isEmpty()) { + final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(fn.getId()); + if (data != null) { + return Type.typeFor(data.getReturnType(EMPTY_INVOCATION_TYPE, runtimeScope)); + } + } + } } return null; diff -r bc8ce3f84b84 -r ef61cca021aa nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed Oct 15 15:57:46 2014 +0200 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Wed Oct 15 16:00:21 2014 +0200 @@ -676,6 +676,22 @@ return addCode(lookup(fnInit).asType(toType), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags()); } + /** + * Returns the return type of a function specialization for particular parameter types.
+ * Be aware that the way this is implemented, it forces full materialization (compilation and installation) of + * code for that specialization. + * @param callSiteType the parameter types at the call site. It must include the mandatory {@code callee} and + * {@code this} parameters, so it needs to start with at least {@code ScriptFunction.class} and + * {@code Object.class} class. Since the return type of the function is calculated from the code itself, it is + * irrelevant and should be set to {@code Object.class}. + * @param runtimeScope a current runtime scope. Can be null but when it's present it will be used as a source of + * current runtime values that can improve the compiler's type speculations (and thus reduce the need for later + * recompilations) if the specialization is not already present and thus needs to be freshly compiled. + * @return the return type of the function specialization. + */ + public Class getReturnType(final MethodType callSiteType, final ScriptObject runtimeScope) { + return getBest(callSiteType, runtimeScope, CompiledFunction.NO_FUNCTIONS).type().returnType(); + } @Override synchronized CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection forbidden) {