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:
parent
e83ae85105
commit
89f65d6006
nashorn
bin
src/jdk/nashorn/internal
codegen
ir
runtime
test/script/basic
@ -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,6 @@
|
||||
11
|
||||
12
|
||||
13
|
||||
11
|
||||
12
|
||||
13
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user