8040089: Apply to call transform was incomplete. Now passes all tests and performance is back
Reviewed-by: hannesw, attila, sundar, jlaskey
--- a/nashorn/bin/fixwhitespace.sh Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/bin/fixwhitespace.sh Thu Apr 17 20:01:19 2014 +0200
@@ -28,3 +28,9 @@
#remove trailing whitespace
find . -name "*.java" -exec sed -i "" 's/[ ]*$//' \{} \;
+#convert tabs to spaces
+find . -name "*.js" -exec sed -i "" 's/ / /g' {} \;
+
+#remove trailing whitespace
+find . -name "*.js" -exec sed -i "" 's/[ ]*$//' \{} \;
+
--- a/nashorn/bin/runoptdualcatch.sh Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/bin/runoptdualcatch.sh Thu Apr 17 20:01:19 2014 +0200
@@ -6,7 +6,8 @@
FILENAME="./optimistic_dual_catch_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
DIR=..
-FAST_CATCH_COMBINATOR=$DIR/bin/fastCatchCombinator.jar
+FAST_CATCH_COMBINATOR=
+#$DIR/bin/fastCatchCombinator.jar
NASHORN_JAR=$DIR/dist/nashorn.jar
$JAVA_HOME/bin/java \
--- a/nashorn/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ApplySpecialization.java Thu Apr 17 20:01:19 2014 +0200
@@ -47,6 +47,7 @@
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
+import jdk.nashorn.internal.runtime.options.Options;
/**
* An optimization that attempts to turn applies into calls. This pattern
@@ -79,7 +80,9 @@
* </pre>
*/
-public class ApplySpecialization {
+public final class ApplySpecialization {
+
+ private static final boolean USE_APPLY2CALL = Options.getBooleanProperty("nashorn.apply2call", true);
private final RecompilableScriptFunctionData data;
@@ -95,10 +98,8 @@
private boolean finished;
- private final boolean isRestOf;
-
/**
- * Return the apply to call specialization logger
+ * Return the apply to call specialization logger g
* @return the logger
*/
public static DebugLogger getLogger() {
@@ -113,13 +114,11 @@
* @param data recompilable script function data, which contains e.g. needs callee information
* @param functionNode functionNode
* @param actualCallSiteType actual call site type that we use (not Object[] varargs)
- * @param isRestOf is this a restof method
*/
- public ApplySpecialization(final RecompilableScriptFunctionData data, final FunctionNode functionNode, final MethodType actualCallSiteType, final boolean isRestOf) {
+ public ApplySpecialization(final RecompilableScriptFunctionData data, final FunctionNode functionNode, final MethodType actualCallSiteType) {
this.data = data;
this.functionNode = functionNode;
this.actualCallSiteType = actualCallSiteType;
- this.isRestOf = isRestOf;
}
/**
@@ -194,6 +193,10 @@
* @return true if successful, false otherwise
*/
public boolean transform() {
+ if (!USE_APPLY2CALL) {
+ return false;
+ }
+
if (finished) {
throw new AssertionError("Can't apply transform twice");
}
@@ -262,7 +265,8 @@
setParameters(null, newParams);
}
- LOG.info("Successfully specialized apply to call in '" + functionNode.getName() + "' id=" + functionNode.getId() + " signature=" + actualCallSiteType + " isRestOf=" + isRestOf);
+ LOG.info("Successfully specialized apply to call in '" + functionNode.getName() + "' id=" + functionNode.getId() + " signature=" + actualCallSiteType);
+
return finish();
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Thu Apr 17 20:01:19 2014 +0200
@@ -1533,7 +1533,7 @@
if (isRestOf) {
final ContinuationInfo ci = new ContinuationInfo();
fnIdToContinuationInfo.put(fnId, ci);
- method._goto(ci.handlerLabel);
+ method._goto(ci.getHandlerLabel());
}
}
@@ -1939,7 +1939,7 @@
//in the properties above, we need to reset the map to oc.getMap() in the continuation
//handler
if (restOfProperty) {
- getContinuationInfo().objectLiteralMap = oc.getMap();
+ getContinuationInfo().setObjectLiteralMap(oc.getMap());
}
method.dup();
@@ -3995,15 +3995,13 @@
}
if(currentContinuationEntryPoint) {
final ContinuationInfo ci = getContinuationInfo();
- assert ci.targetLabel == null; // No duplicate program points
- ci.targetLabel = afterConsumeStack;
- ci.localVariableTypes = localTypes;
-
- ci.stackStoreSpec = localLoads;
-
- ci.stackTypes = Arrays.copyOf(method.getTypesFromStack(method.getStackSize()), stackSizeOnEntry);
- assert ci.stackStoreSpec.length == ci.stackTypes.length;
- ci.returnValueType = method.peekType();
+ assert !ci.hasTargetLabel(); // No duplicate program points
+ ci.setTargetLabel(afterConsumeStack);
+ ci.setLocalVariableTypes(localTypes);
+ ci.setStackStoreSpec(localLoads);
+ ci.setStackTypes(Arrays.copyOf(method.getTypesFromStack(method.getStackSize()), stackSizeOnEntry));
+ assert ci.getStackStoreSpec().length == ci.getStackTypes().length;
+ ci.setReturnValueType(method.peekType());
}
}
return method;
@@ -4436,28 +4434,83 @@
}
private static class ContinuationInfo {
- final Label handlerLabel;
- Label targetLabel; // Label for the target instruction.
+ private final Label handlerLabel;
+ private Label targetLabel; // Label for the target instruction.
// Types the local variable slots have to have when this node completes
- Type[] localVariableTypes;
+ private Type[] localVariableTypes;
// Indices of local variables that need to be loaded on the stack when this node completes
- int[] stackStoreSpec;
+ private int[] stackStoreSpec;
// Types of values loaded on the stack
- Type[] stackTypes;
+ private Type[] stackTypes;
// If non-null, this node should perform the requisite type conversion
- Type returnValueType;
- // If we are in the middle of an object literal initialization, we need to update
- // the map
- PropertyMap objectLiteralMap;
+ private Type returnValueType;
+ // If we are in the middle of an object literal initialization, we need to update the map
+ private PropertyMap objectLiteralMap;
ContinuationInfo() {
this.handlerLabel = new Label("continuation_handler");
}
+ Label getHandlerLabel() {
+ return handlerLabel;
+ }
+
+ boolean hasTargetLabel() {
+ return targetLabel != null;
+ }
+
+ Label getTargetLabel() {
+ return targetLabel;
+ }
+
+ void setTargetLabel(final Label targetLabel) {
+ this.targetLabel = targetLabel;
+ }
+
+ Type[] getLocalVariableTypes() {
+ return localVariableTypes.clone();
+ }
+
+ void setLocalVariableTypes(final Type[] localVariableTypes) {
+ this.localVariableTypes = localVariableTypes;
+ }
+
+ int[] getStackStoreSpec() {
+ return stackStoreSpec.clone();
+ }
+
+ void setStackStoreSpec(final int[] stackStoreSpec) {
+ this.stackStoreSpec = stackStoreSpec;
+ }
+
+ Type[] getStackTypes() {
+ return stackTypes.clone();
+ }
+
+ void setStackTypes(final Type[] stackTypes) {
+ this.stackTypes = stackTypes;
+ }
+
+ Type getReturnValueType() {
+ return returnValueType;
+ }
+
+ void setReturnValueType(final Type returnValueType) {
+ this.returnValueType = returnValueType;
+ }
+
+ PropertyMap getObjectLiteralMap() {
+ return objectLiteralMap;
+ }
+
+ void setObjectLiteralMap(final PropertyMap objectLiteralMap) {
+ this.objectLiteralMap = objectLiteralMap;
+ }
+
@Override
public String toString() {
- return "[localVariableTypes=" + Arrays.toString(localVariableTypes) + ", stackStoreSpec=" +
- Arrays.toString(stackStoreSpec) + ", returnValueType=" + returnValueType + "]";
+ return "[localVariableTypes=" + Arrays.toString(localVariableTypes) + ", stackStoreSpec=" +
+ Arrays.toString(stackStoreSpec) + ", returnValueType=" + returnValueType + "]";
}
}
@@ -4471,13 +4524,13 @@
}
final ContinuationInfo ci = getContinuationInfo();
- method.label(ci.handlerLabel);
+ method.label(ci.getHandlerLabel());
// There should never be an exception thrown from the continuation handler, but in case there is (meaning,
// Nashorn has a bug), then line number 0 will be an indication of where it came from (line numbers are Uint16).
method.lineNumber(0);
- final Type[] lvarTypes = ci.localVariableTypes;
+ final Type[] lvarTypes = ci.getLocalVariableTypes();
final int lvarCount = lvarTypes.length;
final Type rewriteExceptionType = Type.typeFor(RewriteException.class);
@@ -4499,9 +4552,9 @@
lvarIndex = nextLvarIndex;
}
- final int[] stackStoreSpec = ci.stackStoreSpec;
- final Type[] stackTypes = ci.stackTypes;
- final boolean isStackEmpty = stackStoreSpec.length == 0;
+ final int[] stackStoreSpec = ci.getStackStoreSpec();
+ final Type[] stackTypes = ci.getStackTypes();
+ final boolean isStackEmpty = stackStoreSpec.length == 0;
if(!isStackEmpty) {
// Store the RewriteException into an unused local variable slot.
method.store(rewriteExceptionType, lvarCount);
@@ -4515,10 +4568,10 @@
// stack: s0=object literal being initialized
// change map of s0 so that the property we are initilizing when we failed
// is now ci.returnValueType
- if (ci.objectLiteralMap != null) {
+ if (ci.getObjectLiteralMap() != null) {
method.dup(); //dup script object
assert ScriptObject.class.isAssignableFrom(method.peekType().getTypeClass()) : method.peekType().getTypeClass() + " is not a script object";
- loadConstant(ci.objectLiteralMap);
+ loadConstant(ci.getObjectLiteralMap());
method.invoke(ScriptObject.SET_MAP);
}
@@ -4530,9 +4583,9 @@
// Load return value on the stack
method.invoke(RewriteException.GET_RETURN_VALUE);
- method.convert(ci.returnValueType);
+ method.convert(ci.getReturnValueType());
// Jump to continuation point
- method._goto(ci.targetLabel);
+ method._goto(ci.getTargetLabel());
}
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/ParamTypeMap.java Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ParamTypeMap.java Thu Apr 17 20:01:19 2014 +0200
@@ -26,6 +26,7 @@
package jdk.nashorn.internal.codegen;
import java.lang.invoke.MethodType;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -64,7 +65,7 @@
}
ParamTypeMap(final Map<FunctionNode, Type[]> typeMap) {
- for (Map.Entry<FunctionNode, Type[]> entry : typeMap.entrySet()) {
+ for (final Map.Entry<FunctionNode, Type[]> entry : typeMap.entrySet()) {
map.put(entry.getKey().getId(), entry.getValue());
}
}
@@ -77,10 +78,24 @@
*/
Type get(final FunctionNode functionNode, final int pos) {
final Type[] types = map.get(functionNode.getId());
- assert types == null || pos < types.length;
+ assert types == null || pos < types.length : "fn = " + functionNode.getId() + " " + "types=" + Arrays.toString(types) + " || pos=" + pos + " >= length=" + types.length + " in " + this;
if (types != null && pos < types.length) {
return types[pos];
}
return null;
}
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("\n[ParamTypeMap]\n");
+ if (map.isEmpty()) {
+ sb.append("\t{}");
+ } else {
+ for (final Map.Entry<Integer, Type[]> entry : map.entrySet()) {
+ sb.append('\t').append(entry.getKey() + "=>" + ((entry.getValue() == null) ? "[]" : Arrays.toString(entry.getValue()))).append('\n');
+ }
+ }
+ return sb.toString();
+ }
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/ProgramPoints.java Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ProgramPoints.java Thu Apr 17 20:01:19 2014 +0200
@@ -29,6 +29,9 @@
import java.util.ArrayDeque;
import java.util.Deque;
+import java.util.HashSet;
+import java.util.Set;
+
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.CallNode;
@@ -40,6 +43,7 @@
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.Optimistic;
import jdk.nashorn.internal.ir.UnaryNode;
+import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
/**
@@ -48,6 +52,7 @@
class ProgramPoints extends NodeVisitor<LexicalContext> {
private final Deque<int[]> nextProgramPoint = new ArrayDeque<>();
+ private final Set<Node> noProgramPoint = new HashSet<>();
ProgramPoints() {
super(new LexicalContext());
@@ -73,9 +78,25 @@
return functionNode;
}
- private static Optimistic setProgramPoint(final Optimistic optimistic, final int programPoint) {
- final Expression node = (Expression)optimistic.setProgramPoint(programPoint);
- return (Optimistic)node;
+ private Optimistic setProgramPoint(final Optimistic optimistic, final int programPoint) {
+ if (noProgramPoint.contains(optimistic)) {
+ return optimistic;
+ }
+ return (Optimistic)(Expression)optimistic.setProgramPoint(programPoint);
+ }
+
+ @Override
+ public boolean enterVarNode(final VarNode varNode) {
+ noProgramPoint.add(varNode.getAssignmentDest());
+ return true;
+ }
+
+ @Override
+ public boolean enterIdentNode(final IdentNode identNode) {
+ if (identNode.isInternal()) {
+ noProgramPoint.add(identNode);
+ }
+ return true;
}
@Override
--- a/nashorn/src/jdk/nashorn/internal/ir/CallNode.java Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/CallNode.java Thu Apr 17 20:01:19 2014 +0200
@@ -233,6 +233,8 @@
function.toString(fsb);
if (isApplyToCall()) {
sb.append(fsb.toString().replace("apply", "[apply => call]"));
+ } else {
+ sb.append(fsb);
}
sb.append('(');
--- a/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/FunctionNode.java Thu Apr 17 20:01:19 2014 +0200
@@ -554,10 +554,13 @@
* (since it exposes {@code arguments.callee} property) will need to have a callee parameter. We also return true
* for split functions to make sure symbols slots are the same in the main and split methods.
*
+ * A function that has had an apply(this,arguments) turned into a call doesn't need arguments anymore, but still
+ * has to fit the old callsite, thus, we require a dummy callee parameter for those functions as well
+ *
* @return true if the function's generated Java method needs a {@code callee} parameter.
*/
public boolean needsCallee() {
- return needsParentScope() || usesSelfSymbol() || isSplit() || (needsArguments() && !isStrict());
+ return needsParentScope() || usesSelfSymbol() || isSplit() || (needsArguments() && !isStrict()) || hasOptimisticApplyToCall();
}
/**
--- a/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java Thu Apr 17 20:01:19 2014 +0200
@@ -269,6 +269,16 @@
return (flags & OPTIMISTIC) == OPTIMISTIC;
}
+ /**
+ * Is this an internal symbol, i.e. one that starts with ':'. Those can
+ * never be optimistic.
+ * @return true if internal symbol
+ */
+ public boolean isInternal() {
+ assert name != null;
+ return name.charAt(0) == ':';
+ }
+
@Override
public Optimistic setIsOptimistic(final boolean isOptimistic) {
if (isOptimistic() == isOptimistic) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunction.java Thu Apr 17 20:01:19 2014 +0200
@@ -27,6 +27,7 @@
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
+
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
@@ -73,6 +74,7 @@
private MethodHandle constructor;
private OptimismInfo optimismInfo;
private int flags; // from FunctionNode
+ private boolean applyToCall;
CompiledFunction(final MethodHandle invoker) {
this.invoker = invoker;
@@ -102,13 +104,21 @@
return flags;
}
+ void setIsApplyToCall() {
+ applyToCall = true;
+ }
+
+ boolean isApplyToCall() {
+ return applyToCall;
+ }
+
boolean isVarArg() {
return isVarArgsType(invoker.type());
}
@Override
public String toString() {
- return "<callSiteType=" + invoker.type() + " invoker=" + invoker + " ctor=" + constructor + " weight=" + weight() + ">";
+ return "[invokerType=" + invoker.type() + " ctor=" + constructor + " weight=" + weight() + " isApplyToCall=" + isApplyToCall() + "]";
}
boolean needsCallee() {
@@ -637,7 +647,7 @@
return null;
}
SwitchPoint.invalidateAll(new SwitchPoint[] { optimisticAssumptions });
- return data.compile(callSiteType, invalidatedProgramPoints, e.getRuntimeScope(), "Deoptimizing recompilation");
+ return data.compile(callSiteType, invalidatedProgramPoints, e.getRuntimeScope(), "Deoptimizing recompilation", data.getDefaultTransform(callSiteType));
}
MethodHandle compileRestOfMethod(final MethodType callSiteType, final RewriteException e) {
@@ -651,7 +661,7 @@
System.arraycopy(prevEntryPoints, 0, entryPoints, 1, l);
}
entryPoints[0] = e.getProgramPoint();
- return data.compileRestOfMethod(callSiteType, invalidatedProgramPoints, entryPoints, e.getRuntimeScope());
+ return data.compileRestOfMethod(callSiteType, invalidatedProgramPoints, entryPoints, e.getRuntimeScope(), data.getDefaultTransform(callSiteType));
}
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java Thu Apr 17 20:01:19 2014 +0200
@@ -24,6 +24,8 @@
*/
package jdk.nashorn.internal.runtime;
+import static jdk.nashorn.internal.lookup.Lookup.MH;
+
import java.lang.invoke.MethodType;
import java.util.LinkedList;
@@ -61,6 +63,38 @@
return '\'' + name + "' code=" + functions;
}
+ /**
+ * Used to find an apply to call version that fits this callsite.
+ * We cannot just, as in the normal matcher case, return e.g. (Object, Object, int)
+ * for (Object, Object, int, int, int) or we will destroy the semantics and get
+ * a function that, when padded with undefineds, behaves differently
+ * @param type actual call site type
+ * @return apply to call that perfectly fits this callsite or null if none found
+ */
+ CompiledFunction lookupExactApplyToCall(final MethodType type) {
+ for (final CompiledFunction cf : functions) {
+ if (!cf.isApplyToCall()) {
+ continue;
+ }
+
+ final MethodType cftype = cf.type();
+ if (cftype.parameterCount() != type.parameterCount()) {
+ continue;
+ }
+
+ final Class<?>[] paramTypes = new Class<?>[cftype.parameterCount()];
+ for (int i = 0; i < cftype.parameterCount(); i++) {
+ paramTypes[i] = cftype.parameterType(i).isPrimitive() ? cftype.parameterType(i) : Object.class;
+ }
+
+ if (MH.type(cftype.returnType(), paramTypes).equals(type)) {
+ return cf;
+ }
+ }
+
+ return null;
+ }
+
private CompiledFunction pick(final MethodType callSiteType, final boolean canPickVarArg) {
for (final CompiledFunction candidate : functions) {
if (candidate.matchesCallSite(callSiteType, false)) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Thu Apr 17 20:01:19 2014 +0200
@@ -38,9 +38,9 @@
import java.util.concurrent.atomic.AtomicInteger;
import jdk.internal.dynalink.support.NameCodec;
+import jdk.nashorn.internal.codegen.ApplySpecialization;
import jdk.nashorn.internal.codegen.CompilationEnvironment;
import jdk.nashorn.internal.codegen.CompilationEnvironment.CompilationPhases;
-import jdk.nashorn.internal.codegen.ApplySpecialization;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants;
@@ -339,7 +339,7 @@
}
private static String getShortDescriptor(final Object value) {
- if (value.getClass() == Object.class) {
+ if (value == null || !value.getClass().isPrimitive() || value.getClass() != Boolean.class) {
return "O";
}
return value.getClass().getSimpleName();
@@ -365,17 +365,49 @@
return sb.toString();
}
- MethodHandle compileRestOfMethod(final MethodType fnCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final int[] continuationEntryPoints, final ScriptObject runtimeScope) {
+ FunctionNodeTransform getNopTransform() {
+ return new FunctionNodeTransform() {
+ @Override
+ FunctionNode apply(final FunctionNode functionNode) {
+ return functionNode;
+ }
+
+ @Override
+ int getArity() {
+ return RecompilableScriptFunctionData.this.getArity();
+ }
+
+ @Override
+ public String toString() {
+ return "[NopTransform]";
+ }
+ };
+ }
+
+ FunctionNodeTransform getDefaultTransform(final MethodType callSiteType) {
+ return new ApplyToCallTransform(this, callSiteType);
+ }
+
+ private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope, final FunctionNodeTransform tr) {
+ return compile(actualCallSiteType, null, runtimeScope, "Type specialized compilation", tr);
+ }
+
+ private ParamTypeMap typeMap(final MethodType fnCallSiteType, final FunctionNodeTransform tr) {
+ if (isVariableArity() && !tr.wasTransformed()) {
+ return null;
+ }
+ return new ParamTypeMap(functionNodeId, explicitParams(fnCallSiteType, tr.getArity()));
+ }
+
+ MethodHandle compileRestOfMethod(final MethodType fnCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final int[] continuationEntryPoints, final ScriptObject runtimeScope, final FunctionNodeTransform tr) {
if (LOG.isEnabled()) {
LOG.info("Rest-of compilation of '", functionName, "' signature: ", fnCallSiteType, " ", stringifyInvalidations(invalidatedProgramPoints));
}
final String scriptName = RECOMPILATION_PREFIX + RECOMPILE_ID.incrementAndGet() + "$restOf";
- FunctionNode fn = reparse(scriptName);
- final ApplyToCallTransform tr = new ApplyToCallTransform(fn, fnCallSiteType, true);
- fn = tr.transform();
- final int newArity = tr.arity;
+ FunctionNode fn = tr.apply(reparse(scriptName));
+ final ParamTypeMap ptm = typeMap(fnCallSiteType, tr);
final Compiler compiler = new Compiler(
new CompilationEnvironment(
@@ -383,9 +415,7 @@
isStrict(),
this,
runtimeScope,
- isVariableArity() && !tr.transformed ?
- null :
- new ParamTypeMap(functionNodeId, explicitParams(fnCallSiteType, newArity)),
+ ptm,
invalidatedProgramPoints,
continuationEntryPoints,
true
@@ -400,11 +430,7 @@
return mh;
}
- private FunctionNode compileTypeSpecialization(final MethodType actualCallSiteType, final ScriptObject runtimeScope) {
- return compile(actualCallSiteType, null, runtimeScope, "Type specialized compilation");
- }
-
- FunctionNode compile(final MethodType actualCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final ScriptObject runtimeScope, final String reason) {
+ FunctionNode compile(final MethodType actualCallSiteType, final Map<Integer, Type> invalidatedProgramPoints, final ScriptObject runtimeScope, final String reason, final FunctionNodeTransform tr) {
final String scriptName = RECOMPILATION_PREFIX + RECOMPILE_ID.incrementAndGet();
final MethodType fnCallSiteType = actualCallSiteType == null ? null : actualCallSiteType.changeParameterType(0, ScriptFunction.class);
@@ -412,11 +438,9 @@
LOG.info(reason, " of '", functionName, "' signature: ", fnCallSiteType, " ", stringifyInvalidations(invalidatedProgramPoints));
}
- FunctionNode fn = reparse(scriptName);
+ FunctionNode fn = tr.apply(reparse(scriptName));
- final ApplyToCallTransform tr = new ApplyToCallTransform(fn, actualCallSiteType, false);
- fn = tr.transform();
- final int newArity = tr.arity;
+ final ParamTypeMap ptm = fnCallSiteType == null ? null : typeMap(fnCallSiteType, tr);
final CompilationPhases phases = CompilationPhases.EAGER;
final Compiler compiler = new Compiler(
@@ -425,17 +449,12 @@
isStrict(),
this,
runtimeScope,
- fnCallSiteType == null || isVariableArity() && !tr.transformed ?
- null :
- new ParamTypeMap(
- functionNodeId,
- explicitParams(fnCallSiteType, newArity)),
+ ptm,
invalidatedProgramPoints,
true),
installer);
fn = compiler.compile(scriptName, fn);
-
compiler.install(fn);
return fn;
@@ -552,42 +571,51 @@
// If method has an Object parameter, but call site had String, preserve it as Object. No need to narrow it
// artificially. Note that this is related to how CompiledFunction.matchesCallSite() works, specifically
// the fact that various reference types compare to equal (see "fnType.isEquivalentTo(csType)" there).
- if(fromParam != toParam && !fromParam.isPrimitive() && !toParam.isPrimitive()) {
+ if (fromParam != toParam && !fromParam.isPrimitive() && !toParam.isPrimitive()) {
assert fromParam.isAssignableFrom(toParam);
toType = toType.changeParameterType(i, fromParam);
}
}
- if(fromCount > toCount) {
+ if (fromCount > toCount) {
toType = toType.appendParameterTypes(fromType.parameterList().subList(toCount, fromCount));
- } else if(fromCount < toCount) {
+ } else if (fromCount < toCount) {
toType = toType.dropParameterTypes(fromCount, toCount);
}
return addCode(lookup(fn).asType(toType), fn.getFlags());
}
+
@Override
CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) {
synchronized (code) {
- final CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
- if (existingBest != null) {
- /*
- * If callsite type isn't vararg and our best is vararg, generate a specialization
- * we DO have a generic version, which means that we know which ones of the applies
- * were actual applies
- */
- if (existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType)) {
- //System.err.println("Looking in code for best " + callSiteType + " " + existingBest + " code=" + code);
- final FunctionNode fn = compileTypeSpecialization(callSiteType, runtimeScope);
- if (fn.hasOptimisticApplyToCall()) { //did the specialization work
- final CompiledFunction cf = addCode(fn, callSiteType);
- assert !cf.isVarArg();
- return cf;
- }
+ CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
+ if (existingBest == null) {
+ existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope, getNopTransform()), callSiteType);
+ }
+
+ assert existingBest != null;
+ boolean applyToCall = existingBest.isVarArg() && !CompiledFunction.isVarArgsType(callSiteType);
+
+ //if the best one is an apply to call, it has to match the callsite exactly
+ //or we need to regenerate
+ if (existingBest.isApplyToCall()) {
+ final CompiledFunction best = code.lookupExactApplyToCall(callSiteType);
+ if (best != null) {
+ return best;
}
- return existingBest;
+ applyToCall = true;
}
- return addCode(compileTypeSpecialization(callSiteType, runtimeScope), callSiteType);
+
+ if (applyToCall) {
+ final FunctionNode fn = compileTypeSpecialization(callSiteType, runtimeScope, new ApplyToCallTransform(this, callSiteType));
+ if (fn.hasOptimisticApplyToCall()) { //did the specialization work
+ existingBest = addCode(fn, callSiteType);
+ existingBest.setIsApplyToCall();
+ }
+ }
+
+ return existingBest;
}
}
@@ -676,33 +704,65 @@
return true;
}
+ private static abstract class FunctionNodeTransform {
+
+ abstract int getArity();
+
+ boolean wasTransformed() {
+ return false;
+ }
+
+ abstract FunctionNode apply(final FunctionNode functionNode);
+ }
+
/**
* Helper class for transforming apply calls to calls
*/
- private class ApplyToCallTransform {
+ private static class ApplyToCallTransform extends FunctionNodeTransform {
+ private final RecompilableScriptFunctionData data;
private final MethodType actualCallSiteType;
- private final boolean isRestOf;
private int arity;
- private FunctionNode functionNode;
- private boolean transformed;
+ private FunctionNode initialFunctionNode;
+ private FunctionNode transformedFunctionNode;
- ApplyToCallTransform(final FunctionNode functionNode, final MethodType actualCallSiteType, final boolean isRestOf) {
- this.functionNode = functionNode;
+ ApplyToCallTransform(final RecompilableScriptFunctionData data, final MethodType actualCallSiteType) {
+ this.data = data;
this.actualCallSiteType = actualCallSiteType;
- this.arity = getArity();
- this.isRestOf = isRestOf;
+ this.arity = data.getArity();
}
- FunctionNode transform() {
- if (isVariableArity()) {
- final ApplySpecialization spec = new ApplySpecialization(RecompilableScriptFunctionData.this, functionNode, actualCallSiteType, isRestOf);
+ @Override
+ public FunctionNode apply(final FunctionNode functionNode) {
+ this.initialFunctionNode = functionNode;
+ if (data.isVariableArity()) {
+ final ApplySpecialization spec = new ApplySpecialization(data, functionNode, actualCallSiteType);
if (spec.transform()) {
- functionNode = spec.getFunctionNode();
- arity = functionNode.getParameters().size();
- transformed = true;
+ setTransformedFunctionNode(spec.getFunctionNode());
+ return transformedFunctionNode;
}
}
return functionNode;
}
+
+ private void setTransformedFunctionNode(final FunctionNode transformedFunctionNode) {
+ this.transformedFunctionNode = transformedFunctionNode;
+ assert !transformedFunctionNode.isVarArg();
+ this.arity = transformedFunctionNode.getParameters().size();
+ }
+
+ @Override
+ public int getArity() {
+ return arity;
+ }
+
+ @Override
+ public boolean wasTransformed() {
+ return initialFunctionNode != transformedFunctionNode;
+ }
+
+ @Override
+ public String toString() {
+ return "[ApplyToCallTransform]";
+ }
}
}
--- a/nashorn/test/script/basic/apply_to_call/apply_to_call1.js Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call1.js Thu Apr 17 20:01:19 2014 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+* Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
--- a/nashorn/test/script/basic/apply_to_call/apply_to_call2.js Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call2.js Thu Apr 17 20:01:19 2014 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
--- a/nashorn/test/script/basic/apply_to_call/apply_to_call3.js Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call3.js Thu Apr 17 20:01:19 2014 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
--- a/nashorn/test/script/basic/apply_to_call/apply_to_call4.js Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call4.js Thu Apr 17 20:01:19 2014 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
--- a/nashorn/test/script/basic/apply_to_call/apply_to_call_bench.js Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call_bench.js Thu Apr 17 20:01:19 2014 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call_recompile.js Thu Apr 17 20:01:19 2014 +0200
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * apply_to_recompile.js - make sure that recompilations of methods are
+ * transform equivalent when it comes to apply2call, or we will have
+ * erroneous contiunation info generation
+ *
+ * @test
+ * @run
+ */
+
+function K() {
+ K.b2BoundValues.apply(this, arguments);
+ this.constructor === K && K.b2BoundValues.apply(this, arguments)
+}
+
+K.b2BoundValues = function(a,b,c) {
+ print(a);
+ print(b);
+ print(c);
+};
+
+new K(11,12,13,14,15,16);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call_recompile.js.EXPECTED Thu Apr 17 20:01:19 2014 +0200
@@ -0,0 +1,6 @@
+11
+12
+13
+11
+12
+13
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call_varargs.js Thu Apr 17 20:01:19 2014 +0200
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * apply_to_call_varars - make sure that apply to call transform works
+ * even when supplying too few arguments
+ *
+ * @test
+ * @run
+ */
+
+var Class = {
+ create: function() {
+ return function() { //vararg
+ this.initialize.apply(this, arguments);
+ }
+ }
+};
+
+Color = Class.create();
+Color.prototype = {
+ red: 0, green: 0, blue: 0,
+ initialize: function(r) {
+ this.red = r;
+ this.green = 255;
+ this.blue = 255;
+ },
+ toString: function() {
+ print("[red=" + this.red + ", green=" + this.green + ", blue=" + this.blue + "]");
+ }
+};
+
+var colors = new Array(16);
+function run() {
+ for (var i = 0; i < colors.length; i++) {
+ colors[i&0xf] = (new Color(i));
+ }
+}
+
+run();
+for (var i = 0; i < colors.length; i++) {
+ print(colors[i]);
+}
+
+print("Swapping out call");
+Function.prototype.call = function() {
+ throw "This should not happen, apply should be called instead";
+};
+
+run();
+for (var i = 0; i < colors.length; i++) {
+ print(colors[i]);
+}
+
+print("All done!");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/apply_to_call/apply_to_call_varargs.js.EXPECTED Thu Apr 17 20:01:19 2014 +0200
@@ -0,0 +1,66 @@
+[red=0, green=255, blue=255]
+undefined
+[red=1, green=255, blue=255]
+undefined
+[red=2, green=255, blue=255]
+undefined
+[red=3, green=255, blue=255]
+undefined
+[red=4, green=255, blue=255]
+undefined
+[red=5, green=255, blue=255]
+undefined
+[red=6, green=255, blue=255]
+undefined
+[red=7, green=255, blue=255]
+undefined
+[red=8, green=255, blue=255]
+undefined
+[red=9, green=255, blue=255]
+undefined
+[red=10, green=255, blue=255]
+undefined
+[red=11, green=255, blue=255]
+undefined
+[red=12, green=255, blue=255]
+undefined
+[red=13, green=255, blue=255]
+undefined
+[red=14, green=255, blue=255]
+undefined
+[red=15, green=255, blue=255]
+undefined
+Swapping out call
+[red=0, green=255, blue=255]
+undefined
+[red=1, green=255, blue=255]
+undefined
+[red=2, green=255, blue=255]
+undefined
+[red=3, green=255, blue=255]
+undefined
+[red=4, green=255, blue=255]
+undefined
+[red=5, green=255, blue=255]
+undefined
+[red=6, green=255, blue=255]
+undefined
+[red=7, green=255, blue=255]
+undefined
+[red=8, green=255, blue=255]
+undefined
+[red=9, green=255, blue=255]
+undefined
+[red=10, green=255, blue=255]
+undefined
+[red=11, green=255, blue=255]
+undefined
+[red=12, green=255, blue=255]
+undefined
+[red=13, green=255, blue=255]
+undefined
+[red=14, green=255, blue=255]
+undefined
+[red=15, green=255, blue=255]
+undefined
+All done!
--- a/nashorn/test/script/basic/run-octane.js Fri Apr 11 16:52:56 2014 +0200
+++ b/nashorn/test/script/basic/run-octane.js Thu Apr 17 20:01:19 2014 +0200
@@ -156,7 +156,9 @@
} catch (e) {
print_always("*** Aborted and setting score to zero. Reason: " + e);
- e.printStackTrace();
+ if (e instanceof java.lang.Throwable) {
+ e.printStackTrace();
+ }
mean_score = min_score = max_score = 0;
scores = [0];
}
@@ -218,13 +220,19 @@
for (var i = 0; i < args.length; i++) {
arg = args[i];
if (arg == "--iterations") {
- iters = +args[++i];
+ iters = +args[++i];
+ if (isNaN(iters)) {
+ throw "'--iterations' must be followed by integer";
+ }
} else if (arg == "--runtime") {
runtime = args[++i];
} else if (arg == "--verbose") {
verbose = true;
} else if (arg == "--min-time") {
min_time = +args[++i];
+ if (isNaN(iters)) {
+ throw "'--min-time' must be followed by integer";
+ }
} else if (arg == "") {
continue; //skip
} else {