Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Turn dispatch from recursive to iterative #67

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
2 changes: 0 additions & 2 deletions build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@

<path id="common.cp">
<pathelement location="${classes.dir}" />

<pathelement location="${svm.build}/svm-core.jar" />
</path>

<path id="som.cp">
Expand Down
2 changes: 1 addition & 1 deletion src/trufflesom/compiler/MethodGenerationContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public class MethodGenerationContext
private Internal frameOnStack;
protected final LexicalScope currentScope;

private final List<SMethod> embeddedBlockMethods;
protected final List<SMethod> embeddedBlockMethods;

public final StructuralProbe<SSymbol, SClass, SInvokable, Field, Variable> structuralProbe;

Expand Down
6 changes: 6 additions & 0 deletions src/trufflesom/compiler/bc/BytecodeMethodGenContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ public void addBytecodeArgument(final byte code) {
bytecode.add(code);
}

public void replaceWith(final SMethod oldBlock, final SMethod newBlock) {
boolean wasInList = embeddedBlockMethods.remove(oldBlock);
assert wasInList : "The block to be removed is expected to be in the list of embedded methods";
embeddedBlockMethods.add(newBlock);
}

public void patchJumpOffsetToPointToNextInstruction(final int idxOfOffset,
final ParserBc parser) throws ParseError {
int instructionStart = idxOfOffset - 1;
Expand Down
3 changes: 2 additions & 1 deletion src/trufflesom/interpreter/Invokable.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package trufflesom.interpreter;

import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.RootNode;
Expand All @@ -20,7 +21,7 @@ public abstract class Invokable extends RootNode {

protected final ExpressionNode uninitializedBody;

protected SClass holder;
@CompilationFinal protected SClass holder;

protected Invokable(final String name, final SourceSection sourceSection,
final FrameDescriptor frameDescriptor,
Expand Down
1 change: 1 addition & 0 deletions src/trufflesom/interpreter/Method.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ public Method cloneAndAdaptAfterScopeChange(final BytecodeMethodGenContext mgenc
Method clone = new Method(name, sourceSection, adaptedBody, adaptedScope, uninit,
getLanguage(SomLanguage.class));
adaptedScope.setMethod(clone);
clone.setHolder(holder);
return clone;
}

Expand Down
4 changes: 3 additions & 1 deletion src/trufflesom/interpreter/Primitive.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ public Primitive(final String name, final SourceSection sourceSection,
@Override
public Node deepCopy() {
assert getFrameDescriptor().getSize() == 0 : "Make sure there are no slots to be taken care off";
return new Primitive(name, sourceSection, NodeUtil.cloneNode(uninitializedBody),
Primitive p = new Primitive(name, sourceSection, NodeUtil.cloneNode(uninitializedBody),
getFrameDescriptor(), uninitializedBody, getLanguage(SomLanguage.class));
p.setHolder(holder);
return p;
}

@Override
Expand Down
232 changes: 153 additions & 79 deletions src/trufflesom/interpreter/nodes/MessageSendNode.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
package trufflesom.interpreter.nodes;

import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.InvalidAssumptionException;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeCost;
import com.oracle.truffle.api.source.SourceSection;

import bd.primitives.Specializer;
import bd.primitives.nodes.PreevaluatedExpression;
import bd.tools.nodes.Invocation;
import trufflesom.interpreter.TruffleCompiler;
import trufflesom.interpreter.Types;
import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode;
import trufflesom.interpreter.nodes.dispatch.DispatchChain.Cost;
import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.CachedDispatchNode;
import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.CachedExprNode;
import trufflesom.interpreter.nodes.dispatch.CachedDnuNode;
import trufflesom.interpreter.nodes.dispatch.DispatchGuard;
import trufflesom.interpreter.nodes.dispatch.GenericDispatchNode;
import trufflesom.interpreter.nodes.dispatch.UninitializedDispatchNode;
import trufflesom.primitives.Primitives;
import trufflesom.vm.NotYetImplementedException;
import trufflesom.vm.Universe;
import trufflesom.vmobjects.SClass;
import trufflesom.vmobjects.SInvokable;
import trufflesom.vmobjects.SObject;
import trufflesom.vmobjects.SSymbol;


Expand All @@ -32,8 +39,7 @@ public static ExpressionNode create(final SSymbol selector,
Specializer<Universe, ExpressionNode, SSymbol> specializer =
prims.getParserSpecializer(selector, arguments);
if (specializer == null) {
return new UninitializedMessageSendNode(
selector, arguments, universe).initialize(source);
return new GenericMessageSendNode(selector, arguments, universe).initialize(source);
}

ExpressionNode newNode = specializer.create(null, arguments, source, universe);
Expand All @@ -44,14 +50,14 @@ public static ExpressionNode create(final SSymbol selector,

public static AbstractMessageSendNode createForPerformNodes(final SSymbol selector,
final SourceSection source, final Universe universe) {
return new UninitializedMessageSendNode(selector, NO_ARGS, universe).initialize(source);
return new GenericMessageSendNode(selector, NO_ARGS, universe).initialize(source);
}

public static GenericMessageSendNode createGeneric(final SSymbol selector,
final ExpressionNode[] argumentNodes, final SourceSection source,
final Universe universe) {
return new GenericMessageSendNode(selector, argumentNodes,
new UninitializedDispatchNode(selector, universe)).initialize(source);
return new GenericMessageSendNode(
selector, argumentNodes, universe, 0).initialize(source);
}

public static AbstractMessageSendNode createSuperSend(final SClass superClass,
Expand Down Expand Up @@ -102,109 +108,177 @@ private Object[] evaluateArguments(final VirtualFrame frame) {
public abstract int getNumberOfArguments();
}

public static final class UninitializedMessageSendNode extends AbstractMessageSendNode {
public static final class GenericMessageSendNode
extends AbstractMessageSendNode {

private final SSymbol selector;
private final Universe universe;

private final int numberOfSignatureArguments;

@CompilationFinal private int numCacheNodes;

protected final SSymbol selector;
protected final Universe universe;
@Child private AbstractDispatchNode dispatchCache;

protected UninitializedMessageSendNode(final SSymbol selector,
final ExpressionNode[] arguments, final Universe universe) {
private GenericMessageSendNode(final SSymbol selector, final ExpressionNode[] arguments,
final Universe universe, final int numCacheNodes) {
super(arguments);
this.selector = selector;
this.universe = universe;
this.numCacheNodes = numCacheNodes;
this.numberOfSignatureArguments = selector.getNumberOfSignatureArguments();
}

@Override
public String toString() {
return getClass().getSimpleName() + "(" + selector.getString() + ")";
private GenericMessageSendNode(final SSymbol selector, final ExpressionNode[] arguments,
final Universe universe) {
this(selector, arguments, universe, -1);
}

@Override
@ExplodeLoop
public Object doPreEvaluated(final VirtualFrame frame, final Object[] arguments) {
AbstractDispatchNode cache = dispatchCache;

if (cache != null) {
Object rcvr = arguments[0];

do {
try {
if (cache.entryMatches(rcvr)) {
return cache.doPreEvaluated(frame, arguments);
}
} catch (InvalidAssumptionException e) {
CompilerDirectives.transferToInterpreterAndInvalidate();
cache = removeInvalidEntryAndReturnNext(cache);
continue;
}
cache = cache.next;
} while (cache != null);
}

CompilerDirectives.transferToInterpreterAndInvalidate();
return specialize(arguments).doPreEvaluated(frame, arguments);
}

private PreevaluatedExpression specialize(final Object[] arguments) {
TruffleCompiler.transferToInterpreterAndInvalidate("Specialize Message Node");

// We treat super sends separately for simplicity, might not be the
// optimal solution, especially in cases were the knowledge of the
// receiver class also allows us to do more specific things, but for the
// moment we will leave it at this.
// TODO: revisit, and also do more specific optimizations for super sends.

Primitives prims = universe.getPrimitives();

Specializer<Universe, ExpressionNode, SSymbol> specializer =
prims.getEagerSpecializer(selector, arguments, argumentNodes);

if (specializer != null) {
PreevaluatedExpression newNode =
(PreevaluatedExpression) specializer.create(arguments, argumentNodes,
sourceSection, universe);

return (PreevaluatedExpression) replace((ExpressionNode) newNode);
private AbstractDispatchNode removeInvalidEntryAndReturnNext(
final AbstractDispatchNode cache) {
Node prev = cache.getParent();
if (prev == this) {
return dispatchCache = insert(cache.next);
}

return makeGenericSend();
}

private GenericMessageSendNode makeGenericSend() {
GenericMessageSendNode send = new GenericMessageSendNode(selector, argumentNodes,
new UninitializedDispatchNode(selector, universe)).initialize(sourceSection);
return replace(send);
AbstractDispatchNode parent = (AbstractDispatchNode) prev;
return parent.next = parent.insertHere(cache.next);
}

@Override
public SSymbol getInvocationIdentifier() {
return selector;
public String toString() {
return "GMsgSend(" + selector.getString() + ")";
}

@Override
public int getNumberOfArguments() {
return selector.getNumberOfSignatureArguments();
public NodeCost getCost() {
if (numCacheNodes < 0) {
return NodeCost.UNINITIALIZED;
}

int cacheSize = numCacheNodes;

if (cacheSize == 0) {
return NodeCost.UNINITIALIZED;
}

if (cacheSize == 1) {
return NodeCost.MONOMORPHIC;
}

if (cacheSize < AbstractDispatchNode.INLINE_CACHE_SIZE) {
return NodeCost.POLYMORPHIC;
}
return NodeCost.MEGAMORPHIC;
}
}

// TODO: currently, we do not only specialize the given stuff above, but also what has been
// classified as 'value' sends in the OMOP branch. Is that a problem?
private PreevaluatedExpression specialize(final Object[] arguments) {
int cacheSize = numCacheNodes;
if (cacheSize < 0) {
PreevaluatedExpression eager = attemptEagerSpecialization(arguments);
if (eager != null) {
return eager;
}

cacheSize = numCacheNodes = 0;
}

public static final class GenericMessageSendNode
extends AbstractMessageSendNode {
Object rcvr = arguments[0];
assert rcvr != null;

private final SSymbol selector;
private final int numberOfSignatureArguments;
if (rcvr instanceof SObject) {
SObject r = (SObject) rcvr;
if (r.updateLayoutToMatchClass() && cacheSize > 0) {
// if the dispatchCache is null, we end up here, so continue directly below instead
// otherwise, let's retry the cache!
return this;
}
}

@Child private AbstractDispatchNode dispatchNode;
if (cacheSize < AbstractDispatchNode.INLINE_CACHE_SIZE) {
SClass rcvrClass = Types.getClassOf(rcvr, universe);
SInvokable method = rcvrClass.lookupInvokable(selector);
CallTarget callTarget = null;
PreevaluatedExpression expr = null;
if (method != null) {
if (method.isTrivial()) {
expr = method.copyTrivialNode();
assert expr != null;
} else {
callTarget = method.getCallTarget();
}
}

DispatchGuard guard = DispatchGuard.create(rcvr);

AbstractDispatchNode node;
if (expr != null) {
node = new CachedExprNode(guard, expr);
} else if (method != null) {
node = new CachedDispatchNode(guard, callTarget);
} else {
node = new CachedDnuNode(rcvrClass, guard, selector);
}

if (cacheSize > 0) {
reportPolymorphicSpecialize();
final AbstractDispatchNode first = dispatchCache;
node.next = node.insertHere(first);
}
dispatchCache = insert(node);
numCacheNodes = cacheSize + 1;
return node;
}

private GenericMessageSendNode(final SSymbol selector, final ExpressionNode[] arguments,
final AbstractDispatchNode dispatchNode) {
super(arguments);
this.selector = selector;
this.dispatchNode = dispatchNode;
this.numberOfSignatureArguments = selector.getNumberOfSignatureArguments();
// the chain is longer than the maximum defined by INLINE_CACHE_SIZE and
// thus, this callsite is considered to be megaprophic, and we generalize it.
GenericDispatchNode generic = new GenericDispatchNode(selector, universe);
dispatchCache = insert(generic);
reportPolymorphicSpecialize();
numCacheNodes = cacheSize + 1;
return generic;
}

@Override
public Object doPreEvaluated(final VirtualFrame frame,
final Object[] arguments) {
return dispatchNode.executeDispatch(frame, arguments);
}
private PreevaluatedExpression attemptEagerSpecialization(final Object[] arguments) {
Primitives prims = universe.getPrimitives();

public void replaceDispatchListHead(
final GenericDispatchNode replacement) {
CompilerAsserts.neverPartOfCompilation();
dispatchNode.replace(replacement);
}
Specializer<Universe, ExpressionNode, SSymbol> specializer =
prims.getEagerSpecializer(selector, arguments, argumentNodes);

@Override
public String toString() {
return "GMsgSend(" + selector.getString() + ")";
}
if (specializer != null) {
PreevaluatedExpression newNode =
(PreevaluatedExpression) specializer.create(arguments, argumentNodes,
sourceSection, universe);

@Override
public NodeCost getCost() {
return Cost.getCost(dispatchNode);
return (PreevaluatedExpression) replace((ExpressionNode) newNode);
}
return null;
}

@Override
Expand Down
1 change: 1 addition & 0 deletions src/trufflesom/interpreter/nodes/bc/BytecodeLoopNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -1334,6 +1334,7 @@ private void inlineInto(final BytecodeMethodGenContext mgenc, final int targetCo
SMethod newMethod = new SMethod(blockMethod.getSignature(), adapted,
blockMethod.getEmbeddedBlocks(), blockIvk.getSourceSection());
newMethod.setHolder(blockMethod.getHolder());
mgenc.replaceWith(blockMethod, newMethod);
mgenc.addLiteralIfAbsent(newMethod, null);
emitPUSHBLOCK(mgenc, newMethod, bytecodes[i] == PUSH_BLOCK);
break;
Expand Down
Loading