src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SharedScopeCall.java
changeset 48247 fa5a47cad0c9
parent 47216 71c04702a3d5
equal deleted inserted replaced
48246:aadc02050d3b 48247:fa5a47cad0c9
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 
    25 
    26 package jdk.nashorn.internal.codegen;
    26 package jdk.nashorn.internal.codegen;
    27 
    27 
       
    28 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
    28 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
    29 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
    29 
    30 
    30 import java.util.Arrays;
    31 import java.util.Arrays;
    31 import java.util.EnumSet;
    32 import java.util.EnumSet;
    32 import jdk.nashorn.internal.codegen.types.Type;
    33 import jdk.nashorn.internal.codegen.types.Type;
    33 import jdk.nashorn.internal.ir.Symbol;
    34 import jdk.nashorn.internal.ir.Symbol;
    34 import jdk.nashorn.internal.runtime.ScriptObject;
    35 import jdk.nashorn.internal.runtime.ScriptObject;
       
    36 import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
       
    37 import jdk.nashorn.internal.runtime.options.Options;
    35 
    38 
    36 /**
    39 /**
    37  * A scope call or get operation that can be shared by several callsites. This generates a static
    40  * A scope call or get operation that can be shared by several call sites. This generates a static
    38  * method that wraps the invokedynamic instructions to get or call scope variables.
    41  * method that wraps the invokedynamic instructions to get or call scope variables.
    39  * The rationale for this is that initial linking of invokedynamic callsites is expensive,
    42  * The reason for this is to reduce memory footprint and initial linking overhead of huge scripts.
    40  * so by sharing them we can reduce startup overhead and allow very large scripts to run that otherwise wouldn't.
    43  *
    41  *
    44  * <p>Static methods generated by this class expect three parameters in addition to the parameters of the
    42  * <p>Static methods generated by this class expect two parameters in addition to the parameters of the
    45  * function call: The current scope object, the depth of the target scope relative to the scope argument,
    43  * function call: The current scope object and the depth of the target scope relative to the scope argument
    46  * and the program point in case the target operation is optimistic.</p>
    44  * for when this is known at compile-time (fast-scope access).</p>
    47  *
    45  *
    48  * <p>Optimistic operations are called with program point <code>0</code>. If an <code>UnwarrentedOptimismException</code>
    46  * <p>The second argument may be -1 for non-fast-scope symbols, in which case the scope chain is checked
    49  * is thrown, it is caught by the shared call method and rethrown with the program point of the invoking call site.</p>
    47  * for each call. This may cause callsite invalidation when the shared method is used from different
    50  *
    48  * scopes, but such sharing of non-fast scope calls may still be necessary for very large scripts.</p>
    51  * <p>Shared scope calls are not used if the scope contains a <code>with</code> statement or a call to
    49  *
    52  * <code>eval</code>.</p>
    50  * <p>Scope calls must not be shared between normal callsites and callsites contained in a <tt>with</tt>
       
    51  * statement as this condition is not handled by current guards and will cause a runtime error.</p>
       
    52  */
    53  */
    53 class SharedScopeCall {
    54 class SharedScopeCall {
    54 
    55 
    55     /** Threshold for using shared scope calls with fast scope access. */
    56     /**
    56     public static final int FAST_SCOPE_CALL_THRESHOLD = 4;
    57      * Threshold for using shared scope function calls.
    57     /** Threshold for using shared scope calls with slow scope access. */
    58      */
    58     public static final int SLOW_SCOPE_CALL_THRESHOLD = 500;
    59     public static final int SHARED_CALL_THRESHOLD =
    59     /** Threshold for using shared scope gets with fast scope access. */
    60             Options.getIntProperty("nashorn.shared.scope.call.threshold", 5);
    60     public static final int FAST_SCOPE_GET_THRESHOLD  = 200;
    61     /**
    61 
    62      * Threshold for using shared scope variable getter. This is higher than for calls as lower values
    62     final Type valueType;
    63      * degrade performance on many scripts.
    63     final Symbol symbol;
    64      */
    64     final Type returnType;
    65     public static final int SHARED_GET_THRESHOLD  =
    65     final Type[] paramTypes;
    66             Options.getIntProperty("nashorn.shared.scope.get.threshold", 100);
    66     final int flags;
    67 
    67     final boolean isCall;
    68     private static final CompilerConstants.Call REPLACE_PROGRAM_POINT = virtualCallNoLookup(
       
    69                     UnwarrantedOptimismException.class, "replaceProgramPoint",
       
    70                     UnwarrantedOptimismException.class, int.class);
       
    71 
       
    72     /** Number of fixed parameters */
       
    73     private static final int FIXED_PARAM_COUNT = 3;
       
    74 
       
    75     private final Type valueType;
       
    76     private final Symbol symbol;
       
    77     private final Type returnType;
       
    78     private final Type[] paramTypes;
       
    79     private final int flags;
       
    80     private final boolean isCall;
       
    81     private final boolean isOptimistic;
    68     private CompileUnit compileUnit;
    82     private CompileUnit compileUnit;
    69     private String methodName;
    83     private String methodName;
    70     private String staticSignature;
    84     private String staticSignature;
    71 
    85 
    72     /**
    86     /**
    75      * @param symbol the symbol
    89      * @param symbol the symbol
    76      * @param valueType the type of the value
    90      * @param valueType the type of the value
    77      * @param returnType the return type
    91      * @param returnType the return type
    78      * @param paramTypes the function parameter types
    92      * @param paramTypes the function parameter types
    79      * @param flags the callsite flags
    93      * @param flags the callsite flags
    80      */
    94      * @param isOptimistic whether target call is optimistic and we need to handle UnwarrentedOptimismException
    81     SharedScopeCall(final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes, final int flags) {
    95      */
       
    96     SharedScopeCall(final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes,
       
    97                     final int flags, final boolean isOptimistic) {
    82         this.symbol = symbol;
    98         this.symbol = symbol;
    83         this.valueType = valueType;
    99         this.valueType = valueType;
    84         this.returnType = returnType;
   100         this.returnType = returnType;
    85         this.paramTypes = paramTypes;
   101         this.paramTypes = paramTypes;
    86         assert (flags & CALLSITE_OPTIMISTIC) == 0;
       
    87         this.flags = flags;
   102         this.flags = flags;
    88         // If paramTypes is not null this is a call, otherwise it's just a get.
   103         this.isCall = paramTypes != null; // If paramTypes is not null this is a call, otherwise it's just a get.
    89         this.isCall = paramTypes != null;
   104         this.isOptimistic = isOptimistic;
    90     }
   105     }
    91 
   106 
    92     @Override
   107     @Override
    93     public int hashCode() {
   108     public int hashCode() {
    94         return symbol.hashCode() ^ returnType.hashCode() ^ Arrays.hashCode(paramTypes) ^ flags;
   109         return symbol.hashCode() ^ returnType.hashCode() ^ Arrays.hashCode(paramTypes) ^ flags ^ Boolean.hashCode(isOptimistic);
    95     }
   110     }
    96 
   111 
    97     @Override
   112     @Override
    98     public boolean equals(final Object obj) {
   113     public boolean equals(final Object obj) {
    99         if (obj instanceof SharedScopeCall) {
   114         if (obj instanceof SharedScopeCall) {
   100             final SharedScopeCall c = (SharedScopeCall) obj;
   115             final SharedScopeCall c = (SharedScopeCall) obj;
   101             return symbol.equals(c.symbol)
   116             return symbol.equals(c.symbol)
   102                     && flags == c.flags
   117                     && flags == c.flags
   103                     && returnType.equals(c.returnType)
   118                     && returnType.equals(c.returnType)
   104                     && Arrays.equals(paramTypes, c.paramTypes);
   119                     && Arrays.equals(paramTypes, c.paramTypes)
       
   120                     && isOptimistic == c.isOptimistic;
   105         }
   121         }
   106         return false;
   122         return false;
   107     }
   123     }
   108 
   124 
   109     /**
   125     /**
   117     }
   133     }
   118 
   134 
   119     /**
   135     /**
   120      * Generate the invoke instruction for this shared scope call.
   136      * Generate the invoke instruction for this shared scope call.
   121      * @param method the method emitter
   137      * @param method the method emitter
   122      * @return the method emitter
   138      */
   123      */
   139     public void generateInvoke(final MethodEmitter method) {
   124     public MethodEmitter generateInvoke(final MethodEmitter method) {
   140         method.invokestatic(compileUnit.getUnitClassName(), methodName, getStaticSignature());
   125         return method.invokestatic(compileUnit.getUnitClassName(), methodName, getStaticSignature());
       
   126     }
   141     }
   127 
   142 
   128     /**
   143     /**
   129      * Generate the method that implements the scope get or call.
   144      * Generate the method that implements the scope get or call.
   130      */
   145      */
   138         // access, or -1 if this is not known at compile time (e.g. because of a "with" or "eval").
   153         // access, or -1 if this is not known at compile time (e.g. because of a "with" or "eval").
   139 
   154 
   140         final MethodEmitter method = classEmitter.method(methodFlags, methodName, getStaticSignature());
   155         final MethodEmitter method = classEmitter.method(methodFlags, methodName, getStaticSignature());
   141         method.begin();
   156         method.begin();
   142 
   157 
   143         // Load correct scope by calling getProto() on the scope argument as often as specified
   158         // Load correct scope by calling getProto(int) on the scope argument with the supplied depth argument
   144         // by the second argument.
       
   145         final Label parentLoopStart = new Label("parent_loop_start");
       
   146         final Label parentLoopDone  = new Label("parent_loop_done");
       
   147         method.load(Type.OBJECT, 0);
   159         method.load(Type.OBJECT, 0);
   148         method.label(parentLoopStart);
       
   149         method.load(Type.INT, 1);
   160         method.load(Type.INT, 1);
   150         method.iinc(1, -1);
   161         method.invoke(ScriptObject.GET_PROTO_DEPTH);
   151         method.ifle(parentLoopDone);
   162 
   152         method.invoke(ScriptObject.GET_PROTO);
   163         assert !isCall || valueType.isObject(); // Callables are always loaded as object
   153         method._goto(parentLoopStart);
   164 
   154         method.label(parentLoopDone);
   165         // Labels for catch of UnsupportedOptimismException
   155 
   166         final Label beginTry;
   156         assert !isCall || valueType.isObject(); // Callables are always objects
   167         final Label endTry;
   157         // If flags are optimistic, but we're doing a call, remove optimistic flags from the getter, as they obviously
   168         final Label catchLabel;
   158         // only apply to the call.
   169 
   159         method.dynamicGet(valueType, symbol.getName(), isCall ? CodeGenerator.nonOptimisticFlags(flags) : flags, isCall, false);
   170         if(isOptimistic) {
       
   171             beginTry = new Label("begin_try");
       
   172             endTry = new Label("end_try");
       
   173             catchLabel = new Label("catch_label");
       
   174             method.label(beginTry);
       
   175             method._try(beginTry, endTry, catchLabel, UnwarrantedOptimismException.class, false);
       
   176         } else {
       
   177             beginTry = endTry = catchLabel = null;
       
   178         }
       
   179 
       
   180         // If this is an optimistic get we set the optimistic flag but don't set the program point,
       
   181         // which implies a program point of 0. If optimism fails we'll replace it with the actual
       
   182         // program point which caller supplied as third argument.
       
   183         final int getFlags = isOptimistic && !isCall ? flags | CALLSITE_OPTIMISTIC : flags;
       
   184         method.dynamicGet(valueType, symbol.getName(), getFlags, isCall, false);
   160 
   185 
   161         // If this is a get we're done, otherwise call the value as function.
   186         // If this is a get we're done, otherwise call the value as function.
   162         if (isCall) {
   187         if (isCall) {
   163             method.convert(Type.OBJECT);
   188             method.convert(Type.OBJECT);
   164             // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly.
   189             // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly.
   165             method.loadUndefined(Type.OBJECT);
   190             method.loadUndefined(Type.OBJECT);
   166             int slot = 2;
   191             int slot = FIXED_PARAM_COUNT;
   167             for (final Type type : paramTypes) {
   192             for (final Type type : paramTypes) {
   168                 method.load(type, slot);
   193                 method.load(type, slot);
   169                 slot += type.getSlots();
   194                 slot += type.getSlots();
   170             }
   195             }
   171             // Shared scope calls disabled in optimistic world. TODO is this right?
   196 
   172             method.dynamicCall(returnType, 2 + paramTypes.length, flags, symbol.getName());
   197             // Same as above, set optimistic flag but leave program point as 0.
       
   198             final int callFlags = isOptimistic ? flags | CALLSITE_OPTIMISTIC : flags;
       
   199 
       
   200             method.dynamicCall(returnType, 2 + paramTypes.length, callFlags, symbol.getName());
       
   201 
       
   202         }
       
   203 
       
   204         if (isOptimistic) {
       
   205             method.label(endTry);
   173         }
   206         }
   174 
   207 
   175         method._return(returnType);
   208         method._return(returnType);
       
   209 
       
   210         if (isOptimistic) {
       
   211             // We caught a UnwarrantedOptimismException, replace 0 program point with actual program point
       
   212             method._catch(catchLabel);
       
   213             method.load(Type.INT, 2);
       
   214             method.invoke(REPLACE_PROGRAM_POINT);
       
   215             method.athrow();
       
   216         }
       
   217 
   176         method.end();
   218         method.end();
   177     }
   219     }
   178 
   220 
   179     private String getStaticSignature() {
   221     private String getStaticSignature() {
   180         if (staticSignature == null) {
   222         if (staticSignature == null) {
   181             if (paramTypes == null) {
   223             if (paramTypes == null) {
   182                 staticSignature = Type.getMethodDescriptor(returnType, Type.typeFor(ScriptObject.class), Type.INT);
   224                 staticSignature = Type.getMethodDescriptor(returnType, Type.typeFor(ScriptObject.class), Type.INT, Type.INT);
   183             } else {
   225             } else {
   184                 final Type[] params = new Type[paramTypes.length + 2];
   226                 final Type[] params = new Type[paramTypes.length + FIXED_PARAM_COUNT];
   185                 params[0] = Type.typeFor(ScriptObject.class);
   227                 params[0] = Type.typeFor(ScriptObject.class);
   186                 params[1] = Type.INT;
   228                 params[1] = Type.INT;
   187                 System.arraycopy(paramTypes, 0, params, 2, paramTypes.length);
   229                 params[2] = Type.INT;
       
   230                 System.arraycopy(paramTypes, 0, params, FIXED_PARAM_COUNT, paramTypes.length);
   188                 staticSignature = Type.getMethodDescriptor(returnType, params);
   231                 staticSignature = Type.getMethodDescriptor(returnType, params);
   189             }
   232             }
   190         }
   233         }
   191         return staticSignature;
   234         return staticSignature;
   192     }
   235     }