Skip to content

Commit

Permalink
Merge pull request #2588 from fniephaus/truffle-kwargs
Browse files Browse the repository at this point in the history
JRuby+Truffle: Keyword Arguments Optimization
  • Loading branch information
chrisseaton committed Feb 13, 2015
2 parents 08da5c5 + d8e86dd commit 5dc368f
Show file tree
Hide file tree
Showing 39 changed files with 752 additions and 246 deletions.
16 changes: 16 additions & 0 deletions core/src/main/java/org/jruby/ast/ArgsNode.java
Expand Up @@ -154,6 +154,18 @@ protected Arity calculateArity() {
public boolean hasKwargs() {
return hasKwargs;
}

public int countKeywords() {
if (hasKwargs) {
if (keywords == null) {
// Rest keyword argument
return 0;
}
return keywords.size();
} else {
return 0;
}
}

protected boolean hasMasgnArgs() {
if (preCount > 0) for (Node node : pre.childNodes()) {
Expand Down Expand Up @@ -254,6 +266,10 @@ public KeywordRestArgNode getKeyRest() {
return keyRest;
}

public boolean hasKeyRest() {
return keyRest != null;
}

public void checkArgCount(Ruby runtime, int argsLength) {
Arity.checkArgumentCount(runtime, argsLength, requiredArgsCount, maxArgsCount, hasKwargs);
}
Expand Down
Expand Up @@ -146,7 +146,7 @@ public void init() {

@Override
public Object execute(final Object self, final org.jruby.ast.RootNode rootNode) {
return execute(TranslatorDriver.ParserContext.TOP_LEVEL, self, null, rootNode);
return execute(TranslatorDriver.ParserContext.TOP_LEVEL, self, null, rootNode);
}

public Object execute(final TranslatorDriver.ParserContext parserContext, final Object self, final MaterializedFrame parentFrame, final org.jruby.ast.RootNode rootNode) {
Expand Down
Expand Up @@ -32,7 +32,7 @@ public ReadConstantNode(RubyContext context, SourceSection sourceSection, String
super(context, sourceSection);
this.name = name;
this.receiver = receiver;
dispatch = new DispatchHeadNode(context, false, false, MissingBehavior.CALL_CONST_MISSING, lexicalScope, DispatchAction.READ_CONSTANT);
dispatch = new DispatchHeadNode(context, false, false, MissingBehavior.CALL_CONST_MISSING, lexicalScope, DispatchAction.READ_CONSTANT, null, null, false);

}

Expand Down
108 changes: 7 additions & 101 deletions truffle/src/main/java/org/jruby/truffle/nodes/RubyCallNode.java
Expand Up @@ -10,11 +10,8 @@
package org.jruby.truffle.nodes;

import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.BranchProfile;
import org.jruby.truffle.nodes.cast.BooleanCastNode;
import org.jruby.truffle.nodes.cast.BooleanCastNodeFactory;
import org.jruby.truffle.nodes.cast.ProcOrNullNode;
Expand All @@ -23,32 +20,18 @@
import org.jruby.truffle.runtime.ModuleOperations;
import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyArray;
import org.jruby.truffle.runtime.core.RubyProc;
import org.jruby.truffle.runtime.methods.InternalMethod;
import org.jruby.truffle.runtime.util.ArrayUtils;

public class RubyCallNode extends RubyNode {

private final String methodName;

@Child private RubyNode receiver;
@Child private ProcOrNullNode block;
@Children private final RubyNode[] arguments;

private final boolean isSplatted;
private final boolean isVCall;

@Child private CallDispatchHeadNode dispatchHead;

private final BranchProfile splatNotArrayProfile = BranchProfile.create();

@CompilerDirectives.CompilationFinal private boolean seenNullInUnsplat = false;
@CompilerDirectives.CompilationFinal private boolean seenIntegerFixnumInUnsplat = false;
@CompilerDirectives.CompilationFinal private boolean seenLongFixnumInUnsplat = false;
@CompilerDirectives.CompilationFinal private boolean seenFloatInUnsplat = false;
@CompilerDirectives.CompilationFinal private boolean seenObjectInUnsplat = false;

@Child private CallDispatchHeadNode respondToMissing;
@Child private BooleanCastNode respondToMissingCast;

Expand All @@ -66,20 +49,16 @@ public RubyCallNode(RubyContext context, SourceSection section, String methodNam
super(context, section);

this.methodName = methodName;

this.receiver = receiver;

if (block == null) {
this.block = null;
} else {
this.block = ProcOrNullNodeFactory.create(context, section, block);
ProcOrNullNode blockNode = null;
if (block != null) {
blockNode = ProcOrNullNodeFactory.create(context, section, block);
}

this.arguments = arguments;
this.isSplatted = isSplatted;
this.isVCall = isVCall;

dispatchHead = DispatchHeadNodeFactory.createMethodCall(context, ignoreVisibility, false, MissingBehavior.CALL_METHOD_MISSING);
dispatchHead = DispatchHeadNodeFactory.createMethodCall(context, ignoreVisibility, false, MissingBehavior.CALL_METHOD_MISSING, arguments, blockNode, isSplatted);
respondToMissing = DispatchHeadNodeFactory.createMethodCall(context, true, MissingBehavior.RETURN_MISSING);
respondToMissingCast = BooleanCastNodeFactory.create(context, section, null);

Expand All @@ -89,82 +68,9 @@ public RubyCallNode(RubyContext context, SourceSection section, String methodNam
@Override
public Object execute(VirtualFrame frame) {
final Object receiverObject = receiver.execute(frame);
final Object[] argumentsObjects = executeArguments(frame);
final RubyProc blockObject = executeBlock(frame);

return dispatchHead.call(frame, receiverObject, methodName, blockObject, argumentsObjects);
return dispatchHead.call(frame, receiverObject, methodName, null, (Object[]) null);
}

private RubyProc executeBlock(VirtualFrame frame) {
if (block != null) {
return block.executeRubyProc(frame);
} else {
return null;
}
}

@ExplodeLoop
private Object[] executeArguments(VirtualFrame frame) {
final Object[] argumentsObjects = new Object[arguments.length];

for (int i = 0; i < arguments.length; i++) {
argumentsObjects[i] = arguments[i].execute(frame);
}

if (isSplatted) {
return splat(argumentsObjects[0]);
} else {
return argumentsObjects;
}
}

private Object[] splat(Object argument) {
// TODO(CS): what happens if isn't just one argument, or it isn't an Array?

if (!(argument instanceof RubyArray)) {
splatNotArrayProfile.enter();
notDesignedForCompilation();
throw new UnsupportedOperationException();
}

final RubyArray array = (RubyArray) argument;
final int size = array.getSize();
final Object store = array.getStore();

if (seenNullInUnsplat && store == null) {
return new Object[]{};
} else if (seenIntegerFixnumInUnsplat && store instanceof int[]) {
return ArrayUtils.boxUntil((int[]) store, size);
} else if (seenLongFixnumInUnsplat && store instanceof long[]) {
return ArrayUtils.boxUntil((long[]) store, size);
} else if (seenFloatInUnsplat && store instanceof double[]) {
return ArrayUtils.boxUntil((double[]) store, size);
} else if (seenObjectInUnsplat && store instanceof Object[]) {
return ArrayUtils.extractRange((Object[]) store, 0, size);
}

CompilerDirectives.transferToInterpreterAndInvalidate();

if (store == null) {
seenNullInUnsplat = true;
return new Object[]{};
} else if (store instanceof int[]) {
seenIntegerFixnumInUnsplat = true;
return ArrayUtils.boxUntil((int[]) store, size);
} else if (store instanceof long[]) {
seenLongFixnumInUnsplat = true;
return ArrayUtils.boxUntil((long[]) store, size);
} else if (store instanceof double[]) {
seenFloatInUnsplat = true;
return ArrayUtils.boxUntil((double[]) store, size);
} else if (store instanceof Object[]) {
seenObjectInUnsplat = true;
return ArrayUtils.extractRange((Object[]) store, 0, size);
}

throw new UnsupportedOperationException();
}


@Override
public Object isDefined(VirtualFrame frame) {
notDesignedForCompilation();
Expand All @@ -173,7 +79,7 @@ public Object isDefined(VirtualFrame frame) {
return getContext().getCoreLibrary().getNilObject();
}

for (RubyNode argument : arguments) {
for (RubyNode argument : dispatchHead.getArgumentNodes()) {
if (argument.isDefined(frame) == getContext().getCoreLibrary().getNilObject()) {
return getContext().getCoreLibrary().getNilObject();
}
Expand Down
Expand Up @@ -142,7 +142,7 @@ private static RubyRootNode makeGenericMethod(RubyContext context, MethodDetails
optional = methodDetails.getMethodAnnotation().optional();
}

final Arity arity = new Arity(required, optional, methodDetails.getMethodAnnotation().argumentsAsArray(), false);
final Arity arity = new Arity(required, optional, methodDetails.getMethodAnnotation().argumentsAsArray(), false, false, 0);

final List<RubyNode> argumentsNodes = new ArrayList<>();

Expand Down
Expand Up @@ -10,14 +10,10 @@
package org.jruby.truffle.nodes.core;

import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;

import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.UndefinedPlaceholder;
import org.jruby.truffle.runtime.core.RubyArray;
import org.jruby.truffle.runtime.core.RubyFile;
import org.jruby.truffle.runtime.core.RubyProc;
import org.jruby.truffle.runtime.core.RubyString;

import java.io.BufferedReader;
Expand Down
Expand Up @@ -24,11 +24,8 @@
import org.jruby.common.IRubyWarnings;
import org.jruby.runtime.Visibility;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.cast.BooleanCastNode;
import org.jruby.truffle.nodes.cast.BooleanCastNodeFactory;
import org.jruby.truffle.nodes.cast.NumericToFloatNode;
import org.jruby.truffle.nodes.cast.NumericToFloatNodeFactory;
import org.jruby.truffle.nodes.coerce.ToStrNode;
import org.jruby.truffle.nodes.coerce.ToStrNodeFactory;
import org.jruby.truffle.nodes.control.WhileNode;
import org.jruby.truffle.nodes.core.KernelNodesFactory.SameOrEqualNodeFactory;
Expand All @@ -49,7 +46,6 @@
import org.jruby.truffle.runtime.backtrace.Backtrace;
import org.jruby.truffle.runtime.backtrace.MRIBacktraceFormatter;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.control.ThrowException;
import org.jruby.truffle.runtime.core.*;
import org.jruby.truffle.runtime.hash.HashOperations;
import org.jruby.truffle.runtime.hash.KeyValue;
Expand Down Expand Up @@ -1062,8 +1058,8 @@ public abstract static class IntegerNode extends CoreMethodNode {

public IntegerNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
toIntRespondTo = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.CALL_METHOD_MISSING, null);
toInt = new CallDispatchHeadNode(context, false, false, MissingBehavior.CALL_METHOD_MISSING, null);
toIntRespondTo = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.CALL_METHOD_MISSING, null, null, null, false);
toInt = new CallDispatchHeadNode(context, false, false, MissingBehavior.CALL_METHOD_MISSING, null, null, null, false);
}

public IntegerNode(IntegerNode prev) {
Expand Down Expand Up @@ -1682,8 +1678,8 @@ public abstract static class RespondToNode extends CoreMethodNode {
public RespondToNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);

dispatch = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.RETURN_MISSING, null);
dispatchIgnoreVisibility = new DoesRespondDispatchHeadNode(context, true, false, MissingBehavior.RETURN_MISSING, null);
dispatch = new DoesRespondDispatchHeadNode(context, false, false, MissingBehavior.RETURN_MISSING, null, null, null, false);
dispatchIgnoreVisibility = new DoesRespondDispatchHeadNode(context, true, false, MissingBehavior.RETURN_MISSING, null, null, null, false);

if (Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_UNCACHED.load()) {
dispatch.forceUncached();
Expand Down
Expand Up @@ -14,7 +14,6 @@
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node.Child;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;

Expand Down Expand Up @@ -274,7 +273,7 @@ public RubyNilClass attrReader(RubyModule module, Object[] args) {
public static void attrReader(RubyNode currentNode, RubyContext context, SourceSection sourceSection, RubyModule module, String name) {
CompilerDirectives.transferToInterpreter();

final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, new Arity(0, 0, false, false));
final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, new Arity(0, 0, false, false, false, 0));

final SelfNode self = new SelfNode(context, sourceSection);
final ReadInstanceVariableNode readInstanceVariable = new ReadInstanceVariableNode(context, sourceSection, "@" + name, self, false);
Expand Down Expand Up @@ -326,7 +325,7 @@ public RubyNilClass attrWriter(RubyModule module, Object[] args) {
public static void attrWriter(RubyNode currentNode, RubyContext context, SourceSection sourceSection, RubyModule module, String name) {
CompilerDirectives.transferToInterpreter();

final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, new Arity(1, 0, false, false));
final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, new Arity(1, 0, false, false, false, 0));

final SelfNode self = new SelfNode(context, sourceSection);
final ReadPreArgumentNode readArgument = new ReadPreArgumentNode(context, sourceSection, 0, MissingArgumentBehaviour.RUNTIME_ERROR);
Expand Down Expand Up @@ -639,7 +638,7 @@ public abstract static class ConstGetNode extends CoreMethodNode {

public ConstGetNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
dispatch = new DispatchHeadNode(context, false, false, MissingBehavior.CALL_CONST_MISSING, null, DispatchAction.READ_CONSTANT);
dispatch = new DispatchHeadNode(context, false, false, MissingBehavior.CALL_CONST_MISSING, null, DispatchAction.READ_CONSTANT, null, null, false);
}

public ConstGetNode(ConstGetNode prev) {
Expand Down
Expand Up @@ -12,12 +12,14 @@
import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.nodes.InvalidAssumptionException;
import com.oracle.truffle.api.utilities.BranchProfile;

import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.cast.ProcOrNullNode;
import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyProc;
Expand Down Expand Up @@ -52,8 +54,12 @@ public CachedBooleanDispatchNode(
Object trueValue,
InternalMethod trueMethod,
boolean indirect,
DispatchAction dispatchAction) {
super(context, cachedName, next, indirect, dispatchAction);
DispatchAction dispatchAction,
RubyNode[] argumentNodes,
ProcOrNullNode block,
boolean isSplatted) {
super(context, cachedName, next, indirect, dispatchAction,
argumentNodes, block, isSplatted);

this.falseUnmodifiedAssumption = falseUnmodifiedAssumption;
this.falseMethod = falseMethod;
Expand Down Expand Up @@ -123,6 +129,8 @@ public Object executeDispatch(

switch (getDispatchAction()) {
case CALL_METHOD: {
argumentsObjects = executeArguments(frame, argumentsObjects);
blockObject = executeBlock(frame, blockObject);
if (isIndirect()) {
return indirectCallNode.call(
frame,
Expand Down Expand Up @@ -171,6 +179,8 @@ public Object executeDispatch(

switch (getDispatchAction()) {
case CALL_METHOD: {
argumentsObjects = executeArguments(frame, argumentsObjects);
blockObject = executeBlock(frame, blockObject);
if (isIndirect()) {
return indirectCallNode.call(
frame,
Expand Down

0 comments on commit 5dc368f

Please sign in to comment.