8027301: Optimizations for Function.prototype.apply

Fri, 25 Oct 2013 15:21:12 +0200

author
hannesw
date
Fri, 25 Oct 2013 15:21:12 +0200
changeset 653
71cfb21c68dc
parent 652
7985ec3782b5
child 654
406f2b672937
child 655
adab2c628923

8027301: Optimizations for Function.prototype.apply
Reviewed-by: jlaskey

src/jdk/nashorn/internal/runtime/CompiledFunctions.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/ScriptFunctionData.java file | annotate | diff | comparison | revisions
     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      /**

mercurial