8040089: Apply to call transform was incomplete. Now passes all tests and performance is back

Reviewed-by: hannesw, attila, sundar, jlaskey
This commit is contained in:
Marcus Lagergren 2014-04-17 20:01:19 +02:00
parent e83ae85105
commit 89f65d6006
22 changed files with 534 additions and 116 deletions

@ -28,3 +28,9 @@ find . -name "*.java" -exec sed -i "" 's/ / /g' {} \;
#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/[ ]*$//' \{} \;

@ -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 \

@ -47,6 +47,7 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
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 @@ import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
* </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 @@ public class ApplySpecialization {
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 @@ public class ApplySpecialization {
* @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 @@ public class ApplySpecialization {
* @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 @@ public class ApplySpecialization {
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();
}

@ -1533,7 +1533,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
if (isRestOf) {
final ContinuationInfo ci = new ContinuationInfo();
fnIdToContinuationInfo.put(fnId, ci);
method._goto(ci.handlerLabel);
method._goto(ci.getHandlerLabel());
}
}
@ -1939,7 +1939,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
//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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
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 class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
}
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
// 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 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
// 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());
}
}

@ -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 @@ public class ParamTypeMap {
}
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 @@ public class ParamTypeMap {
*/
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();
}
}

@ -29,6 +29,9 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.MAX_
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.LexicalContext;
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 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor;
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 @@ class ProgramPoints extends NodeVisitor<LexicalContext> {
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

@ -233,6 +233,8 @@ public final class CallNode extends LexicalContextExpression implements Optimist
function.toString(fsb);
if (isApplyToCall()) {
sb.append(fsb.toString().replace("apply", "[apply => call]"));
} else {
sb.append(fsb);
}
sb.append('(');

@ -554,10 +554,13 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* (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();
}
/**

@ -269,6 +269,16 @@ public final class IdentNode extends Expression implements PropertyKey, Function
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) {

@ -27,6 +27,7 @@ package jdk.nashorn.internal.runtime;
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 @@ final class CompiledFunction {
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 @@ final class CompiledFunction {
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 @@ final class CompiledFunction {
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 @@ final class CompiledFunction {
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));
}
}

@ -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 @@ final class CompiledFunctions {
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)) {

@ -38,9 +38,9 @@ import java.util.Set;
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
}
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
isStrict(),
this,
runtimeScope,
isVariableArity() && !tr.transformed ?
null :
new ParamTypeMap(functionNodeId, explicitParams(fnCallSiteType, newArity)),
ptm,
invalidatedProgramPoints,
continuationEntryPoints,
true
@ -400,11 +430,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
// 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;
}
}
return existingBest;
CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
if (existingBest == null) {
existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope, getNopTransform()), callSiteType);
}
return addCode(compileTypeSpecialization(callSiteType, runtimeScope), 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;
}
applyToCall = true;
}
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 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
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]";
}
}
}

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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);

@ -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!");

@ -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!

@ -156,7 +156,9 @@ function run_one_benchmark(arg, iters) {
} 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 @@ var min_time = 5;
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 {