1 /* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package jdk.nashorn.internal.runtime; 26 27 import static jdk.nashorn.internal.lookup.Lookup.MH; 28 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; 29 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; 30 31 import java.lang.invoke.CallSite; 32 import java.lang.invoke.MethodHandle; 33 import java.lang.invoke.MethodHandles; 34 import java.lang.invoke.MethodType; 35 import java.lang.invoke.MutableCallSite; 36 import java.lang.invoke.SwitchPoint; 37 import java.util.ArrayList; 38 import java.util.Collection; 39 import java.util.Collections; 40 import java.util.Iterator; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.TreeMap; 44 import java.util.function.Supplier; 45 import java.util.logging.Level; 46 import jdk.internal.dynalink.linker.GuardedInvocation; 47 import jdk.nashorn.internal.codegen.Compiler; 48 import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; 49 import jdk.nashorn.internal.codegen.TypeMap; 50 import jdk.nashorn.internal.codegen.types.ArrayType; 51 import jdk.nashorn.internal.codegen.types.Type; 52 import jdk.nashorn.internal.ir.FunctionNode; 53 import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic; 54 import jdk.nashorn.internal.runtime.events.RecompilationEvent; 55 import jdk.nashorn.internal.runtime.linker.Bootstrap; 56 import jdk.nashorn.internal.runtime.logging.DebugLogger; 57 58 /** 59 * An version of a JavaScript function, native or JavaScript. 60 * Supports lazily generating a constructor version of the invocation. 61 */ 62 final class CompiledFunction { 63 64 private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class); 65 private static final MethodHandle RELINK_COMPOSABLE_INVOKER = findOwnMH("relinkComposableInvoker", void.class, CallSite.class, CompiledFunction.class, boolean.class); 66 private static final MethodHandle HANDLE_REWRITE_EXCEPTION = findOwnMH("handleRewriteException", MethodHandle.class, CompiledFunction.class, OptimismInfo.class, RewriteException.class); 67 private static final MethodHandle RESTOF_INVOKER = MethodHandles.exactInvoker(MethodType.methodType(Object.class, RewriteException.class)); 68 69 private final DebugLogger log; 70 71 static final Collection<CompiledFunction> NO_FUNCTIONS = Collections.emptySet(); 72 73 /** 74 * The method type may be more specific than the invoker, if. e.g. 75 * the invoker is guarded, and a guard with a generic object only 76 * fallback, while the target is more specific, we still need the 77 * more specific type for sorting 78 */ 79 private MethodHandle invoker; 80 private MethodHandle constructor; 81 private OptimismInfo optimismInfo; 82 private final int flags; // from FunctionNode 83 private final MethodType callSiteType; 84 85 private final Specialization specialization; 86 CompiledFunction(final MethodHandle invoker)87 CompiledFunction(final MethodHandle invoker) { 88 this(invoker, null, null); 89 } 90 createBuiltInConstructor(final MethodHandle invoker, final Specialization specialization)91 static CompiledFunction createBuiltInConstructor(final MethodHandle invoker, final Specialization specialization) { 92 return new CompiledFunction(MH.insertArguments(invoker, 0, false), createConstructorFromInvoker(MH.insertArguments(invoker, 0, true)), specialization); 93 } 94 CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final Specialization specialization)95 CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final Specialization specialization) { 96 this(invoker, constructor, 0, null, specialization, DebugLogger.DISABLED_LOGGER); 97 } 98 CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final int flags, final MethodType callSiteType, final Specialization specialization, final DebugLogger log)99 CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final int flags, final MethodType callSiteType, final Specialization specialization, final DebugLogger log) { 100 this.specialization = specialization; 101 if (specialization != null && specialization.isOptimistic()) { 102 /* 103 * An optimistic builtin with isOptimistic=true works like any optimistic generated function, i.e. it 104 * can throw unwarranted optimism exceptions. As native functions trivially can't have parts of them 105 * regenerated as "restOf" methods, this only works if the methods are atomic/functional in their behavior 106 * and doesn't modify state before an UOE can be thrown. If they aren't, we can reexecute a wider version 107 * of the same builtin in a recompilation handler for FinalScriptFunctionData. There are several 108 * candidate methods in Native* that would benefit from this, but I haven't had time to implement any 109 * of them currently. In order to fit in with the relinking framework, the current thinking is 110 * that the methods still take a program point to fit in with other optimistic functions, but 111 * it is set to "first", which is the beginning of the method. The relinker can tell the difference 112 * between builtin and JavaScript functions. This might change. TODO 113 */ 114 this.invoker = MH.insertArguments(invoker, invoker.type().parameterCount() - 1, UnwarrantedOptimismException.FIRST_PROGRAM_POINT); 115 throw new AssertionError("Optimistic (UnwarrantedOptimismException throwing) builtin functions are currently not in use"); 116 } 117 this.invoker = invoker; 118 this.constructor = constructor; 119 this.flags = flags; 120 this.callSiteType = callSiteType; 121 this.log = log; 122 } 123 CompiledFunction(final MethodHandle invoker, final RecompilableScriptFunctionData functionData, final Map<Integer, Type> invalidatedProgramPoints, final MethodType callSiteType, final int flags)124 CompiledFunction(final MethodHandle invoker, final RecompilableScriptFunctionData functionData, 125 final Map<Integer, Type> invalidatedProgramPoints, final MethodType callSiteType, final int flags) { 126 this(invoker, null, flags, callSiteType, null, functionData.getLogger()); 127 if ((flags & FunctionNode.IS_DEOPTIMIZABLE) != 0) { 128 optimismInfo = new OptimismInfo(functionData, invalidatedProgramPoints); 129 } else { 130 optimismInfo = null; 131 } 132 } 133 createBuiltInConstructor(final MethodHandle invoker)134 static CompiledFunction createBuiltInConstructor(final MethodHandle invoker) { 135 return new CompiledFunction(MH.insertArguments(invoker, 0, false), createConstructorFromInvoker(MH.insertArguments(invoker, 0, true)), null); 136 } 137 isSpecialization()138 boolean isSpecialization() { 139 return specialization != null; 140 } 141 hasLinkLogic()142 boolean hasLinkLogic() { 143 return getLinkLogicClass() != null; 144 } 145 getLinkLogicClass()146 Class<? extends LinkLogic> getLinkLogicClass() { 147 if (isSpecialization()) { 148 final Class<? extends LinkLogic> linkLogicClass = specialization.getLinkLogicClass(); 149 assert !LinkLogic.isEmpty(linkLogicClass) : "empty link logic classes should have been removed by nasgen"; 150 return linkLogicClass; 151 } 152 return null; 153 } 154 getFlags()155 int getFlags() { 156 return flags; 157 } 158 159 /** 160 * An optimistic specialization is one that can throw UnwarrantedOptimismException. 161 * This is allowed for native methods, as long as they are functional, i.e. don't change 162 * any state between entering and throwing the UOE. Then we can re-execute a wider version 163 * of the method in the continuation. Rest-of method generation for optimistic builtins is 164 * of course not possible, but this approach works and fits into the same relinking 165 * framework 166 * 167 * @return true if optimistic builtin 168 */ isOptimistic()169 boolean isOptimistic() { 170 return isSpecialization() ? specialization.isOptimistic() : false; 171 } 172 isApplyToCall()173 boolean isApplyToCall() { 174 return (flags & FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION) != 0; 175 } 176 isVarArg()177 boolean isVarArg() { 178 return isVarArgsType(invoker.type()); 179 } 180 181 @Override toString()182 public String toString() { 183 final StringBuilder sb = new StringBuilder(); 184 final Class<? extends LinkLogic> linkLogicClass = getLinkLogicClass(); 185 186 sb.append("[invokerType="). 187 append(invoker.type()). 188 append(" ctor="). 189 append(constructor). 190 append(" weight="). 191 append(weight()). 192 append(" linkLogic="). 193 append(linkLogicClass != null ? linkLogicClass.getSimpleName() : "none"); 194 195 return sb.toString(); 196 } 197 needsCallee()198 boolean needsCallee() { 199 return ScriptFunctionData.needsCallee(invoker); 200 } 201 202 /** 203 * Returns an invoker method handle for this function. Note that the handle is safely composable in 204 * the sense that you can compose it with other handles using any combinators even if you can't affect call site 205 * invalidation. If this compiled function is non-optimistic, then it returns the same value as 206 * {@link #getInvokerOrConstructor(boolean)}. However, if the function is optimistic, then this handle will 207 * incur an overhead as it will add an intermediate internal call site that can relink itself when the function 208 * needs to regenerate its code to always point at the latest generated code version. 209 * @return a guaranteed composable invoker method handle for this function. 210 */ createComposableInvoker()211 MethodHandle createComposableInvoker() { 212 return createComposableInvoker(false); 213 } 214 215 /** 216 * Returns an invoker method handle for this function when invoked as a constructor. Note that the handle should be 217 * considered non-composable in the sense that you can only compose it with other handles using any combinators if 218 * you can ensure that the composition is guarded by {@link #getOptimisticAssumptionsSwitchPoint()} if it's 219 * non-null, and that you can relink the call site it is set into as a target if the switch point is invalidated. In 220 * all other cases, use {@link #createComposableConstructor()}. 221 * @return a direct constructor method handle for this function. 222 */ getConstructor()223 private MethodHandle getConstructor() { 224 if (constructor == null) { 225 constructor = createConstructorFromInvoker(createInvokerForPessimisticCaller()); 226 } 227 228 return constructor; 229 } 230 231 /** 232 * Creates a version of the invoker intended for a pessimistic caller (return type is Object, no caller optimistic 233 * program point available). 234 * @return a version of the invoker intended for a pessimistic caller. 235 */ createInvokerForPessimisticCaller()236 private MethodHandle createInvokerForPessimisticCaller() { 237 return createInvoker(Object.class, INVALID_PROGRAM_POINT); 238 } 239 240 /** 241 * Compose a constructor from an invoker. 242 * 243 * @param invoker invoker 244 * @return the composed constructor 245 */ createConstructorFromInvoker(final MethodHandle invoker)246 private static MethodHandle createConstructorFromInvoker(final MethodHandle invoker) { 247 final boolean needsCallee = ScriptFunctionData.needsCallee(invoker); 248 // If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having 249 // "this" in the first argument position is what allows the elegant folded composition of 250 // (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor 251 // always returns Object. 252 final MethodHandle swapped = needsCallee ? swapCalleeAndThis(invoker) : invoker; 253 254 final MethodHandle returnsObject = MH.asType(swapped, swapped.type().changeReturnType(Object.class)); 255 256 final MethodType ctorType = returnsObject.type(); 257 258 // Construct a dropping type list for NEWFILTER, but don't include constructor "this" into it, so it's actually 259 // captured as "allocation" parameter of NEWFILTER after we fold the constructor into it. 260 // (this, [callee, ]args...) => ([callee, ]args...) 261 final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray(); 262 263 // Fold constructor into newFilter that replaces the return value from the constructor with the originally 264 // allocated value when the originally allocated value is a JS primitive (String, Boolean, Number). 265 // (result, this, [callee, ]args...) x (this, [callee, ]args...) => (this, [callee, ]args...) 266 final MethodHandle filtered = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), returnsObject); 267 268 // allocate() takes a ScriptFunction and returns a newly allocated ScriptObject... 269 if (needsCallee) { 270 // ...we either fold it into the previous composition, if we need both the ScriptFunction callee object and 271 // the newly allocated object in the arguments, so (this, callee, args...) x (callee) => (callee, args...), 272 // or... 273 return MH.foldArguments(filtered, ScriptFunction.ALLOCATE); 274 } 275 276 // ...replace the ScriptFunction argument with the newly allocated object, if it doesn't need the callee 277 // (this, args...) filter (callee) => (callee, args...) 278 return MH.filterArguments(filtered, 0, ScriptFunction.ALLOCATE); 279 } 280 281 /** 282 * Permutes the parameters in the method handle from {@code (callee, this, ...)} to {@code (this, callee, ...)}. 283 * Used when creating a constructor handle. 284 * @param mh a method handle with order of arguments {@code (callee, this, ...)} 285 * @return a method handle with order of arguments {@code (this, callee, ...)} 286 */ swapCalleeAndThis(final MethodHandle mh)287 private static MethodHandle swapCalleeAndThis(final MethodHandle mh) { 288 final MethodType type = mh.type(); 289 assert type.parameterType(0) == ScriptFunction.class : type; 290 assert type.parameterType(1) == Object.class : type; 291 final MethodType newType = type.changeParameterType(0, Object.class).changeParameterType(1, ScriptFunction.class); 292 final int[] reorder = new int[type.parameterCount()]; 293 reorder[0] = 1; 294 assert reorder[1] == 0; 295 for (int i = 2; i < reorder.length; ++i) { 296 reorder[i] = i; 297 } 298 return MethodHandles.permuteArguments(mh, newType, reorder); 299 } 300 301 /** 302 * Returns an invoker method handle for this function when invoked as a constructor. Note that the handle is safely 303 * composable in the sense that you can compose it with other handles using any combinators even if you can't affect 304 * call site invalidation. If this compiled function is non-optimistic, then it returns the same value as 305 * {@link #getConstructor()}. However, if the function is optimistic, then this handle will incur an overhead as it 306 * will add an intermediate internal call site that can relink itself when the function needs to regenerate its code 307 * to always point at the latest generated code version. 308 * @return a guaranteed composable constructor method handle for this function. 309 */ createComposableConstructor()310 MethodHandle createComposableConstructor() { 311 return createComposableInvoker(true); 312 } 313 hasConstructor()314 boolean hasConstructor() { 315 return constructor != null; 316 } 317 type()318 MethodType type() { 319 return invoker.type(); 320 } 321 weight()322 int weight() { 323 return weight(type()); 324 } 325 weight(final MethodType type)326 private static int weight(final MethodType type) { 327 if (isVarArgsType(type)) { 328 return Integer.MAX_VALUE; //if there is a varargs it should be the heavist and last fallback 329 } 330 331 int weight = Type.typeFor(type.returnType()).getWeight(); 332 for (int i = 0 ; i < type.parameterCount() ; i++) { 333 final Class<?> paramType = type.parameterType(i); 334 final int pweight = Type.typeFor(paramType).getWeight() * 2; //params are more important than call types as return values are always specialized 335 weight += pweight; 336 } 337 338 weight += type.parameterCount(); //more params outweigh few parameters 339 340 return weight; 341 } 342 isVarArgsType(final MethodType type)343 static boolean isVarArgsType(final MethodType type) { 344 assert type.parameterCount() >= 1 : type; 345 return type.parameterType(type.parameterCount() - 1) == Object[].class; 346 } 347 moreGenericThan(final MethodType mt0, final MethodType mt1)348 static boolean moreGenericThan(final MethodType mt0, final MethodType mt1) { 349 return weight(mt0) > weight(mt1); 350 } 351 betterThanFinal(final CompiledFunction other, final MethodType callSiteMethodType)352 boolean betterThanFinal(final CompiledFunction other, final MethodType callSiteMethodType) { 353 // Prefer anything over nothing, as we can't compile new versions. 354 if (other == null) { 355 return true; 356 } 357 return betterThanFinal(this, other, callSiteMethodType); 358 } 359 betterThanFinal(final CompiledFunction cf, final CompiledFunction other, final MethodType callSiteMethodType)360 private static boolean betterThanFinal(final CompiledFunction cf, final CompiledFunction other, final MethodType callSiteMethodType) { 361 final MethodType thisMethodType = cf.type(); 362 final MethodType otherMethodType = other.type(); 363 final int thisParamCount = getParamCount(thisMethodType); 364 final int otherParamCount = getParamCount(otherMethodType); 365 final int callSiteRawParamCount = getParamCount(callSiteMethodType); 366 final boolean csVarArg = callSiteRawParamCount == Integer.MAX_VALUE; 367 // Subtract 1 for callee for non-vararg call sites 368 final int callSiteParamCount = csVarArg ? callSiteRawParamCount : callSiteRawParamCount - 1; 369 370 // Prefer the function that discards less parameters 371 final int thisDiscardsParams = Math.max(callSiteParamCount - thisParamCount, 0); 372 final int otherDiscardsParams = Math.max(callSiteParamCount - otherParamCount, 0); 373 if(thisDiscardsParams < otherDiscardsParams) { 374 return true; 375 } 376 if(thisDiscardsParams > otherDiscardsParams) { 377 return false; 378 } 379 380 final boolean thisVarArg = thisParamCount == Integer.MAX_VALUE; 381 final boolean otherVarArg = otherParamCount == Integer.MAX_VALUE; 382 if(!(thisVarArg && otherVarArg && csVarArg)) { 383 // At least one of them isn't vararg 384 final Type[] thisType = toTypeWithoutCallee(thisMethodType, 0); // Never has callee 385 final Type[] otherType = toTypeWithoutCallee(otherMethodType, 0); // Never has callee 386 final Type[] callSiteType = toTypeWithoutCallee(callSiteMethodType, 1); // Always has callee 387 388 int narrowWeightDelta = 0; 389 int widenWeightDelta = 0; 390 final int minParamsCount = Math.min(Math.min(thisParamCount, otherParamCount), callSiteParamCount); 391 for(int i = 0; i < minParamsCount; ++i) { 392 final int callSiteParamWeight = getParamType(i, callSiteType, csVarArg).getWeight(); 393 // Delta is negative for narrowing, positive for widening 394 final int thisParamWeightDelta = getParamType(i, thisType, thisVarArg).getWeight() - callSiteParamWeight; 395 final int otherParamWeightDelta = getParamType(i, otherType, otherVarArg).getWeight() - callSiteParamWeight; 396 // Only count absolute values of narrowings 397 narrowWeightDelta += Math.max(-thisParamWeightDelta, 0) - Math.max(-otherParamWeightDelta, 0); 398 // Only count absolute values of widenings 399 widenWeightDelta += Math.max(thisParamWeightDelta, 0) - Math.max(otherParamWeightDelta, 0); 400 } 401 402 // If both functions accept more arguments than what is passed at the call site, account for ability 403 // to receive Undefined un-narrowed in the remaining arguments. 404 if(!thisVarArg) { 405 for(int i = callSiteParamCount; i < thisParamCount; ++i) { 406 narrowWeightDelta += Math.max(Type.OBJECT.getWeight() - thisType[i].getWeight(), 0); 407 } 408 } 409 if(!otherVarArg) { 410 for(int i = callSiteParamCount; i < otherParamCount; ++i) { 411 narrowWeightDelta -= Math.max(Type.OBJECT.getWeight() - otherType[i].getWeight(), 0); 412 } 413 } 414 415 // Prefer function that narrows less 416 if(narrowWeightDelta < 0) { 417 return true; 418 } 419 if(narrowWeightDelta > 0) { 420 return false; 421 } 422 423 // Prefer function that widens less 424 if(widenWeightDelta < 0) { 425 return true; 426 } 427 if(widenWeightDelta > 0) { 428 return false; 429 } 430 } 431 432 // Prefer the function that exactly matches the arity of the call site. 433 if(thisParamCount == callSiteParamCount && otherParamCount != callSiteParamCount) { 434 return true; 435 } 436 if(thisParamCount != callSiteParamCount && otherParamCount == callSiteParamCount) { 437 return false; 438 } 439 440 // Otherwise, neither function matches arity exactly. We also know that at this point, they both can receive 441 // more arguments than call site, otherwise we would've already chosen the one that discards less parameters. 442 // Note that variable arity methods are preferred, as they actually match the call site arity better, since they 443 // really have arbitrary arity. 444 if(thisVarArg) { 445 if(!otherVarArg) { 446 return true; // 447 } 448 } else if(otherVarArg) { 449 return false; 450 } 451 452 // Neither is variable arity; chose the one that has less extra parameters. 453 final int fnParamDelta = thisParamCount - otherParamCount; 454 if(fnParamDelta < 0) { 455 return true; 456 } 457 if(fnParamDelta > 0) { 458 return false; 459 } 460 461 final int callSiteRetWeight = Type.typeFor(callSiteMethodType.returnType()).getWeight(); 462 // Delta is negative for narrower return type, positive for wider return type 463 final int thisRetWeightDelta = Type.typeFor(thisMethodType.returnType()).getWeight() - callSiteRetWeight; 464 final int otherRetWeightDelta = Type.typeFor(otherMethodType.returnType()).getWeight() - callSiteRetWeight; 465 466 // Prefer function that returns a less wide return type 467 final int widenRetDelta = Math.max(thisRetWeightDelta, 0) - Math.max(otherRetWeightDelta, 0); 468 if(widenRetDelta < 0) { 469 return true; 470 } 471 if(widenRetDelta > 0) { 472 return false; 473 } 474 475 // Prefer function that returns a less narrow return type 476 final int narrowRetDelta = Math.max(-thisRetWeightDelta, 0) - Math.max(-otherRetWeightDelta, 0); 477 if(narrowRetDelta < 0) { 478 return true; 479 } 480 if(narrowRetDelta > 0) { 481 return false; 482 } 483 484 //if they are equal, pick the specialized one first 485 if (cf.isSpecialization() != other.isSpecialization()) { 486 return cf.isSpecialization(); //always pick the specialized version if we can 487 } 488 489 if (cf.isSpecialization() && other.isSpecialization()) { 490 return cf.getLinkLogicClass() != null; //pick link logic specialization above generic specializations 491 } 492 493 // Signatures are identical 494 throw new AssertionError(thisMethodType + " identically applicable to " + otherMethodType + " for " + callSiteMethodType); 495 } 496 toTypeWithoutCallee(final MethodType type, final int thisIndex)497 private static Type[] toTypeWithoutCallee(final MethodType type, final int thisIndex) { 498 final int paramCount = type.parameterCount(); 499 final Type[] t = new Type[paramCount - thisIndex]; 500 for(int i = thisIndex; i < paramCount; ++i) { 501 t[i - thisIndex] = Type.typeFor(type.parameterType(i)); 502 } 503 return t; 504 } 505 getParamType(final int i, final Type[] paramTypes, final boolean isVarArg)506 private static Type getParamType(final int i, final Type[] paramTypes, final boolean isVarArg) { 507 final int fixParamCount = paramTypes.length - (isVarArg ? 1 : 0); 508 if(i < fixParamCount) { 509 return paramTypes[i]; 510 } 511 assert isVarArg; 512 return ((ArrayType)paramTypes[paramTypes.length - 1]).getElementType(); 513 } 514 matchesCallSite(final MethodType other, final boolean pickVarArg)515 boolean matchesCallSite(final MethodType other, final boolean pickVarArg) { 516 if (other.equals(this.callSiteType)) { 517 return true; 518 } 519 final MethodType type = type(); 520 final int fnParamCount = getParamCount(type); 521 final boolean isVarArg = fnParamCount == Integer.MAX_VALUE; 522 if (isVarArg) { 523 return pickVarArg; 524 } 525 526 final int csParamCount = getParamCount(other); 527 final boolean csIsVarArg = csParamCount == Integer.MAX_VALUE; 528 if (csIsVarArg && isApplyToCall()) { 529 return false; // apply2call function must be called with exact number of parameters 530 } 531 final int thisThisIndex = needsCallee() ? 1 : 0; // Index of "this" parameter in this function's type 532 533 final int fnParamCountNoCallee = fnParamCount - thisThisIndex; 534 final int minParams = Math.min(csParamCount - 1, fnParamCountNoCallee); // callSiteType always has callee, so subtract 1 535 // We must match all incoming parameters, including "this". "this" will usually be Object, but there 536 // are exceptions, e.g. when calling functions with primitive "this" in strict mode or through call/apply. 537 for(int i = 0; i < minParams; ++i) { 538 final Type fnType = Type.typeFor(type.parameterType(i + thisThisIndex)); 539 final Type csType = csIsVarArg ? Type.OBJECT : Type.typeFor(other.parameterType(i + 1)); 540 if(!fnType.isEquivalentTo(csType)) { 541 return false; 542 } 543 } 544 545 // Must match any undefined parameters to Object type. 546 for(int i = minParams; i < fnParamCountNoCallee; ++i) { 547 if(!Type.typeFor(type.parameterType(i + thisThisIndex)).isEquivalentTo(Type.OBJECT)) { 548 return false; 549 } 550 } 551 552 return true; 553 } 554 getParamCount(final MethodType type)555 private static int getParamCount(final MethodType type) { 556 final int paramCount = type.parameterCount(); 557 return type.parameterType(paramCount - 1).isArray() ? Integer.MAX_VALUE : paramCount; 558 } 559 canBeDeoptimized()560 private boolean canBeDeoptimized() { 561 return optimismInfo != null; 562 } 563 createComposableInvoker(final boolean isConstructor)564 private MethodHandle createComposableInvoker(final boolean isConstructor) { 565 final MethodHandle handle = getInvokerOrConstructor(isConstructor); 566 567 // If compiled function is not optimistic, it can't ever change its invoker/constructor, so just return them 568 // directly. 569 if(!canBeDeoptimized()) { 570 return handle; 571 } 572 573 // Otherwise, we need a new level of indirection; need to introduce a mutable call site that can relink itself 574 // to the compiled function's changed target whenever the optimistic assumptions are invalidated. 575 final CallSite cs = new MutableCallSite(handle.type()); 576 relinkComposableInvoker(cs, this, isConstructor); 577 return cs.dynamicInvoker(); 578 } 579 580 private static class HandleAndAssumptions { 581 final MethodHandle handle; 582 final SwitchPoint assumptions; 583 HandleAndAssumptions(final MethodHandle handle, final SwitchPoint assumptions)584 HandleAndAssumptions(final MethodHandle handle, final SwitchPoint assumptions) { 585 this.handle = handle; 586 this.assumptions = assumptions; 587 } 588 createInvocation()589 GuardedInvocation createInvocation() { 590 return new GuardedInvocation(handle, assumptions); 591 } 592 } 593 594 /** 595 * Returns a pair of an invocation created with a passed-in supplier and a non-invalidated switch point for 596 * optimistic assumptions (or null for the switch point if the function can not be deoptimized). While the method 597 * makes a best effort to return a non-invalidated switch point (compensating for possible deoptimizing 598 * recompilation happening on another thread) it is still possible that by the time this method returns the 599 * switchpoint has been invalidated by a {@code RewriteException} triggered on another thread for this function. 600 * This is not a problem, though, as these switch points are always used to produce call sites that fall back to 601 * relinking when they are invalidated, and in this case the execution will end up here again. What this method 602 * basically does is minimize such busy-loop relinking while the function is being recompiled on a different thread. 603 * @param invocationSupplier the supplier that constructs the actual invocation method handle; should use the 604 * {@code CompiledFunction} method itself in some capacity. 605 * @return a tuple object containing the method handle as created by the supplier and an optimistic assumptions 606 * switch point that is guaranteed to not have been invalidated before the call to this method (or null if the 607 * function can't be further deoptimized). 608 */ getValidOptimisticInvocation(final Supplier<MethodHandle> invocationSupplier)609 private synchronized HandleAndAssumptions getValidOptimisticInvocation(final Supplier<MethodHandle> invocationSupplier) { 610 for(;;) { 611 final MethodHandle handle = invocationSupplier.get(); 612 final SwitchPoint assumptions = canBeDeoptimized() ? optimismInfo.optimisticAssumptions : null; 613 if(assumptions != null && assumptions.hasBeenInvalidated()) { 614 // We can be in a situation where one thread is in the middle of a deoptimizing compilation when we hit 615 // this and thus, it has invalidated the old switch point, but hasn't created the new one yet. Note that 616 // the behavior of invalidating the old switch point before recompilation, and only creating the new one 617 // after recompilation is by design. If we didn't wait here for the recompilation to complete, we would 618 // be busy looping through the fallback path of the invalidated switch point, relinking the call site 619 // again with the same invalidated switch point, invoking the fallback, etc. stealing CPU cycles from 620 // the recompilation task we're dependent on. This can still happen if the switch point gets invalidated 621 // after we grabbed it here, in which case we'll indeed do one busy relink immediately. 622 try { 623 wait(); 624 } catch (final InterruptedException e) { 625 // Intentionally ignored. There's nothing meaningful we can do if we're interrupted 626 } 627 } else { 628 return new HandleAndAssumptions(handle, assumptions); 629 } 630 } 631 } 632 relinkComposableInvoker(final CallSite cs, final CompiledFunction inv, final boolean constructor)633 private static void relinkComposableInvoker(final CallSite cs, final CompiledFunction inv, final boolean constructor) { 634 final HandleAndAssumptions handleAndAssumptions = inv.getValidOptimisticInvocation(new Supplier<MethodHandle>() { 635 @Override 636 public MethodHandle get() { 637 return inv.getInvokerOrConstructor(constructor); 638 } 639 }); 640 final MethodHandle handle = handleAndAssumptions.handle; 641 final SwitchPoint assumptions = handleAndAssumptions.assumptions; 642 final MethodHandle target; 643 if(assumptions == null) { 644 target = handle; 645 } else { 646 final MethodHandle relink = MethodHandles.insertArguments(RELINK_COMPOSABLE_INVOKER, 0, cs, inv, constructor); 647 target = assumptions.guardWithTest(handle, MethodHandles.foldArguments(cs.dynamicInvoker(), relink)); 648 } 649 cs.setTarget(target.asType(cs.type())); 650 } 651 getInvokerOrConstructor(final boolean selectCtor)652 private MethodHandle getInvokerOrConstructor(final boolean selectCtor) { 653 return selectCtor ? getConstructor() : createInvokerForPessimisticCaller(); 654 } 655 656 /** 657 * Returns a guarded invocation for this function when not invoked as a constructor. The guarded invocation has no 658 * guard but it potentially has an optimistic assumptions switch point. As such, it will probably not be used as a 659 * final guarded invocation, but rather as a holder for an invocation handle and switch point to be decomposed and 660 * reassembled into a different final invocation by the user of this method. Any recompositions should take care to 661 * continue to use the switch point. If that is not possible, use {@link #createComposableInvoker()} instead. 662 * @return a guarded invocation for an ordinary (non-constructor) invocation of this function. 663 */ createFunctionInvocation(final Class<?> callSiteReturnType, final int callerProgramPoint)664 GuardedInvocation createFunctionInvocation(final Class<?> callSiteReturnType, final int callerProgramPoint) { 665 return getValidOptimisticInvocation(new Supplier<MethodHandle>() { 666 @Override 667 public MethodHandle get() { 668 return createInvoker(callSiteReturnType, callerProgramPoint); 669 } 670 }).createInvocation(); 671 } 672 673 /** 674 * Returns a guarded invocation for this function when invoked as a constructor. The guarded invocation has no guard 675 * but it potentially has an optimistic assumptions switch point. As such, it will probably not be used as a final 676 * guarded invocation, but rather as a holder for an invocation handle and switch point to be decomposed and 677 * reassembled into a different final invocation by the user of this method. Any recompositions should take care to 678 * continue to use the switch point. If that is not possible, use {@link #createComposableConstructor()} instead. 679 * @return a guarded invocation for invocation of this function as a constructor. 680 */ 681 GuardedInvocation createConstructorInvocation() { 682 return getValidOptimisticInvocation(new Supplier<MethodHandle>() { 683 @Override 684 public MethodHandle get() { 685 return getConstructor(); 686 } 687 }).createInvocation(); 688 } 689 690 private MethodHandle createInvoker(final Class<?> callSiteReturnType, final int callerProgramPoint) { 691 final boolean isOptimistic = canBeDeoptimized(); 692 MethodHandle handleRewriteException = isOptimistic ? createRewriteExceptionHandler() : null; 693 694 MethodHandle inv = invoker; 695 if(isValid(callerProgramPoint)) { 696 inv = OptimisticReturnFilters.filterOptimisticReturnValue(inv, callSiteReturnType, callerProgramPoint); 697 inv = changeReturnType(inv, callSiteReturnType); 698 if(callSiteReturnType.isPrimitive() && handleRewriteException != null) { 699 // because handleRewriteException always returns Object 700 handleRewriteException = OptimisticReturnFilters.filterOptimisticReturnValue(handleRewriteException, 701 callSiteReturnType, callerProgramPoint); 702 } 703 } else if(isOptimistic) { 704 // Required so that rewrite exception has the same return type. It'd be okay to do it even if we weren't 705 // optimistic, but it isn't necessary as the linker upstream will eventually convert the return type. 706 inv = changeReturnType(inv, callSiteReturnType); 707 } 708 709 if(isOptimistic) { 710 assert handleRewriteException != null; 711 final MethodHandle typedHandleRewriteException = changeReturnType(handleRewriteException, inv.type().returnType()); 712 return MH.catchException(inv, RewriteException.class, typedHandleRewriteException); 713 } 714 return inv; 715 } 716 717 private MethodHandle createRewriteExceptionHandler() { 718 return MH.foldArguments(RESTOF_INVOKER, MH.insertArguments(HANDLE_REWRITE_EXCEPTION, 0, this, optimismInfo)); 719 } 720 721 private static MethodHandle changeReturnType(final MethodHandle mh, final Class<?> newReturnType) { 722 return Bootstrap.getLinkerServices().asType(mh, mh.type().changeReturnType(newReturnType)); 723 } 724 725 @SuppressWarnings("unused") 726 private static MethodHandle handleRewriteException(final CompiledFunction function, final OptimismInfo oldOptimismInfo, final RewriteException re) { 727 return function.handleRewriteException(oldOptimismInfo, re); 728 } 729 730 /** 731 * Debug function for printing out all invalidated program points and their 732 * invalidation mapping to next type 733 * @param ipp 734 * @return string describing the ipp map 735 */ 736 private static List<String> toStringInvalidations(final Map<Integer, Type> ipp) { 737 if (ipp == null) { 738 return Collections.emptyList(); 739 } 740 741 final List<String> list = new ArrayList<>(); 742 743 for (final Iterator<Map.Entry<Integer, Type>> iter = ipp.entrySet().iterator(); iter.hasNext(); ) { 744 final Map.Entry<Integer, Type> entry = iter.next(); 745 final char bct = entry.getValue().getBytecodeStackType(); 746 final String type; 747 748 switch (entry.getValue().getBytecodeStackType()) { 749 case 'A': 750 type = "object"; 751 break; 752 case 'I': 753 type = "int"; 754 break; 755 case 'J': 756 type = "long"; 757 break; 758 case 'D': 759 type = "double"; 760 break; 761 default: 762 type = String.valueOf(bct); 763 break; 764 } 765 766 final StringBuilder sb = new StringBuilder(); 767 sb.append('['). 768 append("program point: "). 769 append(entry.getKey()). 770 append(" -> "). 771 append(type). 772 append(']'); 773 774 list.add(sb.toString()); 775 } 776 777 return list; 778 } 779 780 private void logRecompile(final String reason, final FunctionNode fn, final MethodType type, final Map<Integer, Type> ipp) { 781 if (log.isEnabled()) { 782 log.info(reason, DebugLogger.quote(fn.getName()), " signature: ", type); 783 log.indent(); 784 for (final String str : toStringInvalidations(ipp)) { 785 log.fine(str); 786 } 787 log.unindent(); 788 } 789 } 790 791 /** 792 * Handles a {@link RewriteException} raised during the execution of this function by recompiling (if needed) the 793 * function with an optimistic assumption invalidated at the program point indicated by the exception, and then 794 * executing a rest-of method to complete the execution with the deoptimized version. 795 * @param oldOptInfo the optimism info of this function. We must store it explicitly as a bound argument in the 796 * method handle, otherwise it could be null for handling a rewrite exception in an outer invocation of a recursive 797 * function when recursive invocations of the function have completely deoptimized it. 798 * @param re the rewrite exception that was raised 799 * @return the method handle for the rest-of method, for folding composition. 800 */ 801 private synchronized MethodHandle handleRewriteException(final OptimismInfo oldOptInfo, final RewriteException re) { 802 if (log.isEnabled()) { 803 log.info( 804 new RecompilationEvent( 805 Level.INFO, 806 re, 807 re.getReturnValueNonDestructive()), 808 "caught RewriteException ", 809 re.getMessageShort()); 810 log.indent(); 811 } 812 813 final MethodType type = type(); 814 815 // Compiler needs a call site type as its input, which always has a callee parameter, so we must add it if 816 // this function doesn't have a callee parameter. 817 final MethodType ct = type.parameterType(0) == ScriptFunction.class ? 818 type : 819 type.insertParameterTypes(0, ScriptFunction.class); 820 final OptimismInfo currentOptInfo = optimismInfo; 821 final boolean shouldRecompile = currentOptInfo != null && currentOptInfo.requestRecompile(re); 822 823 // Effective optimism info, for subsequent use. We'll normally try to use the current (latest) one, but if it 824 // isn't available, we'll use the old one bound into the call site. 825 final OptimismInfo effectiveOptInfo = currentOptInfo != null ? currentOptInfo : oldOptInfo; 826 FunctionNode fn = effectiveOptInfo.reparse(); 827 final boolean cached = fn.isCached(); 828 final Compiler compiler = effectiveOptInfo.getCompiler(fn, ct, re); //set to non rest-of 829 830 if (!shouldRecompile) { 831 // It didn't necessarily recompile, e.g. for an outer invocation of a recursive function if we already 832 // recompiled a deoptimized version for an inner invocation. 833 // We still need to do the rest of from the beginning 834 logRecompile("Rest-of compilation [STANDALONE] ", fn, ct, effectiveOptInfo.invalidatedProgramPoints); 835 return restOfHandle(effectiveOptInfo, compiler.compile(fn, cached ? CompilationPhases.COMPILE_CACHED_RESTOF : CompilationPhases.COMPILE_ALL_RESTOF), currentOptInfo != null); 836 } 837 838 logRecompile("Deoptimizing recompilation (up to bytecode) ", fn, ct, effectiveOptInfo.invalidatedProgramPoints); 839 fn = compiler.compile(fn, cached ? CompilationPhases.RECOMPILE_CACHED_UPTO_BYTECODE : CompilationPhases.COMPILE_UPTO_BYTECODE); 840 log.fine("Reusable IR generated"); 841 842 // compile the rest of the function, and install it 843 log.info("Generating and installing bytecode from reusable IR..."); 844 logRecompile("Rest-of compilation [CODE PIPELINE REUSE] ", fn, ct, effectiveOptInfo.invalidatedProgramPoints); 845 final FunctionNode normalFn = compiler.compile(fn, CompilationPhases.GENERATE_BYTECODE_AND_INSTALL); 846 847 if (effectiveOptInfo.data.usePersistentCodeCache()) { 848 final RecompilableScriptFunctionData data = effectiveOptInfo.data; 849 final int functionNodeId = data.getFunctionNodeId(); 850 final TypeMap typeMap = data.typeMap(ct); 851 final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId); 852 final String cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes); 853 compiler.persistClassInfo(cacheKey, normalFn); 854 } 855 856 final boolean canBeDeoptimized = normalFn.canBeDeoptimized(); 857 858 if (log.isEnabled()) { 859 log.unindent(); 860 log.info("Done."); 861 862 log.info("Recompiled '", fn.getName(), "' (", Debug.id(this), ") ", canBeDeoptimized ? "can still be deoptimized." : " is completely deoptimized."); 863 log.finest("Looking up invoker..."); 864 } 865 866 final MethodHandle newInvoker = effectiveOptInfo.data.lookup(fn); 867 invoker = newInvoker.asType(type.changeReturnType(newInvoker.type().returnType())); 868 constructor = null; // Will be regenerated when needed 869 870 log.info("Done: ", invoker); 871 final MethodHandle restOf = restOfHandle(effectiveOptInfo, compiler.compile(fn, CompilationPhases.GENERATE_BYTECODE_AND_INSTALL_RESTOF), canBeDeoptimized); 872 873 // Note that we only adjust the switch point after we set the invoker/constructor. This is important. 874 if (canBeDeoptimized) { 875 effectiveOptInfo.newOptimisticAssumptions(); // Otherwise, set a new switch point. 876 } else { 877 optimismInfo = null; // If we got to a point where we no longer have optimistic assumptions, let the optimism info go. 878 } 879 notifyAll(); 880 881 return restOf; 882 } 883 884 private MethodHandle restOfHandle(final OptimismInfo info, final FunctionNode restOfFunction, final boolean canBeDeoptimized) { 885 assert info != null; 886 assert restOfFunction.getCompileUnit().getUnitClassName().contains("restOf"); 887 final MethodHandle restOf = 888 changeReturnType( 889 info.data.lookupCodeMethod( 890 restOfFunction.getCompileUnit().getCode(), 891 MH.type(restOfFunction.getReturnType().getTypeClass(), 892 RewriteException.class)), 893 Object.class); 894 895 if (!canBeDeoptimized) { 896 return restOf; 897 } 898 899 // If rest-of is itself optimistic, we must make sure that we can repeat a deoptimization if it, too hits an exception. 900 return MH.catchException(restOf, RewriteException.class, createRewriteExceptionHandler()); 901 902 } 903 904 private static class OptimismInfo { 905 // TODO: this is pointing to its owning ScriptFunctionData. Re-evaluate if that's okay. 906 private final RecompilableScriptFunctionData data; 907 private final Map<Integer, Type> invalidatedProgramPoints; 908 private SwitchPoint optimisticAssumptions; 909 private final DebugLogger log; 910 911 OptimismInfo(final RecompilableScriptFunctionData data, final Map<Integer, Type> invalidatedProgramPoints) { 912 this.data = data; 913 this.log = data.getLogger(); 914 this.invalidatedProgramPoints = invalidatedProgramPoints == null ? new TreeMap<Integer, Type>() : invalidatedProgramPoints; 915 newOptimisticAssumptions(); 916 } 917 918 private void newOptimisticAssumptions() { 919 optimisticAssumptions = new SwitchPoint(); 920 } 921 922 boolean requestRecompile(final RewriteException e) { 923 final Type retType = e.getReturnType(); 924 final Type previousFailedType = invalidatedProgramPoints.put(e.getProgramPoint(), retType); 925 926 if (previousFailedType != null && !previousFailedType.narrowerThan(retType)) { 927 final StackTraceElement[] stack = e.getStackTrace(); 928 final String functionId = stack.length == 0 ? 929 data.getName() : 930 stack[0].getClassName() + "." + stack[0].getMethodName(); 931 932 log.info("RewriteException for an already invalidated program point ", e.getProgramPoint(), " in ", functionId, ". This is okay for a recursive function invocation, but a bug otherwise."); 933 934 return false; 935 } 936 937 SwitchPoint.invalidateAll(new SwitchPoint[] { optimisticAssumptions }); 938 939 return true; 940 } 941 942 Compiler getCompiler(final FunctionNode fn, final MethodType actualCallSiteType, final RewriteException e) { 943 return data.getCompiler(fn, actualCallSiteType, e.getRuntimeScope(), invalidatedProgramPoints, getEntryPoints(e)); 944 } 945 946 private static int[] getEntryPoints(final RewriteException e) { 947 final int[] prevEntryPoints = e.getPreviousContinuationEntryPoints(); 948 final int[] entryPoints; 949 if (prevEntryPoints == null) { 950 entryPoints = new int[1]; 951 } else { 952 final int l = prevEntryPoints.length; 953 entryPoints = new int[l + 1]; 954 System.arraycopy(prevEntryPoints, 0, entryPoints, 1, l); 955 } 956 entryPoints[0] = e.getProgramPoint(); 957 return entryPoints; 958 } 959 960 FunctionNode reparse() { 961 return data.reparse(); 962 } 963 } 964 965 @SuppressWarnings("unused") 966 private static Object newFilter(final Object result, final Object allocation) { 967 return (result instanceof ScriptObject || !JSType.isPrimitive(result))? result : allocation; 968 } 969 970 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 971 return MH.findStatic(MethodHandles.lookup(), CompiledFunction.class, name, MH.type(rtype, types)); 972 } 973 } 974