8060241: Immediately invoked function expressions cause lot of deoptimization
authorattila
Wed, 15 Oct 2014 16:00:21 +0200
changeset 27105 ef61cca021aa
parent 27104 bc8ce3f84b84
child 27106 d2d7e511c05c
8060241: Immediately invoked function expressions cause lot of deoptimization Reviewed-by: hannesw, lagergren
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.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;
--- 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) {