8060241: Immediately invoked function expressions cause lot of deoptimization
Reviewed-by: hannesw, lagergren
--- 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;
--- 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.<br>
+ * <b>Be aware that the way this is implemented, it forces full materialization (compilation and installation) of
+ * code for that specialization.</b>
+ * @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<CompiledFunction> forbidden) {