Permalink
Browse files

Reflectoring of message sending dispatch chains to simplify guards

The refactoring is general cleanup, removal of code duplication, but also a change in how guards in the dispatch chain and the chain itself works.

Now, we have simple unstructured chain. Before, the chain was structured, by having first all primitives, then a test node, and then all object types. Don't think this was really a working thing, because the type check was costing in the interpreter at every object check again, because of the checked cast.
In the compiled code, the check was bad, because it was not a leaf-type, and thus, required code for traversing the type hierarchy. This is now gone, we try to have guards and checked casts only on leaf-types to reduce the cost of guards.

In the interpreter, this hopefully also reduces the cost of dispatching. However guards are now in their own classes `DispatchGuard`, which means there is an extra virtual dispatch, which might be an issue for interpreter performance.

Further changes include the use of the object layout for objects with fields as guard critierion. This means, we can hopefully move more of the guards into the dispatch chain, and avoid duplicated guards. This however also means that handling invalidation of object layouts is part of this chain. Objects with old layouts are handled in the uninitialized node of the dispatch chain. After transfering an object to the new layout, lookup is retried to avoid adding duplicate entries to the dispatch chain.

Super, lexically bound, and receiver sends are now all handled with the same uninitialized node hierarchy, to avoid incomplete implementation (missing #dnu support for super, missing dispatch chain length, etc)

Signed-off-by: Stefan Marr <git@stefan-marr.de>
  • Loading branch information...
smarr committed Dec 28, 2015
1 parent 0b36fcc commit a6d57fd1a4d7d8b2ce28927607ea41a52a171760
@@ -21,10 +21,12 @@
import som.interpreter.nodes.SlotAccessNode.SlotReadNode;
import som.interpreter.nodes.SlotAccessNode.SlotWriteNode;
import som.interpreter.nodes.dispatch.AbstractDispatchNode;
import som.interpreter.nodes.dispatch.CachedSlotAccessNode;
import som.interpreter.nodes.dispatch.CachedSlotAccessNode.CachedSlotWriteNode;
import som.interpreter.nodes.dispatch.CachedSlotAccessNode.CheckedCachedSlotAccessNode;
import som.interpreter.nodes.dispatch.CachedSlotAccessNode.CheckedCachedSlotWriteNode;
import som.interpreter.nodes.dispatch.CachedSlotAccessNode.CachedSlotRead;
import som.interpreter.nodes.dispatch.CachedSlotAccessNode.CachedSlotWrite;
import som.interpreter.nodes.dispatch.CachedSlotAccessNode.LexicallyBoundImmutableSlotRead;
import som.interpreter.nodes.dispatch.CachedSlotAccessNode.LexicallyBoundMutableSlotRead;
import som.interpreter.nodes.dispatch.CachedSlotAccessNode.LexicallyBoundMutableSlotWrite;
import som.interpreter.nodes.dispatch.DispatchGuard;
import som.interpreter.nodes.dispatch.Dispatchable;
import som.interpreter.nodes.literals.NilLiteralNode;
import som.interpreter.objectstorage.ClassFactory;
@@ -38,6 +40,7 @@
import som.vmobjects.SInvokable;
import som.vmobjects.SInvokable.SInitializer;
import som.vmobjects.SObject;
import som.vmobjects.SObject.SImmutableObject;
import som.vmobjects.SObjectWithClass;
import som.vmobjects.SSymbol;
@@ -494,12 +497,17 @@ public String toString() {
@Override
public AbstractDispatchNode getDispatchNode(final Object rcvr,
final Object rcvrClass, final AbstractDispatchNode next) {
assert rcvrClass instanceof SClass;
final AbstractDispatchNode next) {
if (modifier == AccessModifier.PRIVATE) {
return new CachedSlotAccessNode(createNode());
// TODO: we actually should try to remove SImmuableObject, and use a caching node
// the caching node could also be more beneficial, because we can cache for all immutable fields, and if rcvr identidy check fails, we can do the load, but then still use the cached value
if (rcvr instanceof SImmutableObject) {
return new LexicallyBoundImmutableSlotRead(createNode());
} else {
return new LexicallyBoundMutableSlotRead(createNode());
}
} else {
return new CheckedCachedSlotAccessNode(((SClass) rcvrClass).getLayoutForInstances(), createNode(), next);
return new CachedSlotRead(createNode(), DispatchGuard.create(rcvr), next);
}
}
@@ -562,12 +570,12 @@ public SlotMutator(final SSymbol name, final AccessModifier acccessModifier,
@Override
public AbstractDispatchNode getDispatchNode(final Object rcvr,
final Object rcvrClass, final AbstractDispatchNode next) {
assert rcvrClass instanceof SClass;
final AbstractDispatchNode next) {
if (modifier == AccessModifier.PRIVATE) {
return new CachedSlotWriteNode(createWriteNode());
return new LexicallyBoundMutableSlotWrite(createWriteNode());
} else {
return new CheckedCachedSlotWriteNode(((SClass) rcvrClass).getInstanceFactory(), createWriteNode(), next);
return new CachedSlotWrite(createWriteNode(),
DispatchGuard.create(rcvr), next);
}
}
@@ -7,8 +7,6 @@
import som.interpreter.nodes.dispatch.AbstractDispatchNode;
import som.interpreter.nodes.dispatch.DispatchChain.Cost;
import som.interpreter.nodes.dispatch.GenericDispatchNode;
import som.interpreter.nodes.dispatch.LexicallyBoundDispatchNode;
import som.interpreter.nodes.dispatch.SuperDispatchNode;
import som.interpreter.nodes.dispatch.UninitializedDispatchNode;
import som.interpreter.nodes.literals.BlockNode;
import som.interpreter.nodes.nary.EagerBinaryPrimitiveNode;
@@ -119,7 +117,8 @@ public static GenericMessageSendNode createGeneric(final SSymbol selector,
throw new NotYetImplementedException();
} else {
return new GenericMessageSendNode(selector, argumentNodes,
new UninitializedDispatchNode(selector, AccessModifier.PUBLIC), source);
UninitializedDispatchNode.createRcvrSend(selector, AccessModifier.PUBLIC),
source);
}
}
@@ -220,7 +219,7 @@ protected PreevaluatedExpression makeSend() {
private GenericMessageSendNode makeOrdenarySend() {
GenericMessageSendNode send = new GenericMessageSendNode(selector,
argumentNodes,
new UninitializedDispatchNode(selector, AccessModifier.PUBLIC),
UninitializedDispatchNode.createRcvrSend(selector, AccessModifier.PUBLIC),
getSourceSection());
return replace(send);
}
@@ -636,9 +635,9 @@ protected PreevaluatedExpression makeSpecialSend() {
AbstractDispatchNode dispatch;
if (rcvrNode.isSuperSend()) {
dispatch = SuperDispatchNode.create(selector, (ISuperReadNode) rcvrNode);
dispatch = UninitializedDispatchNode.createSuper(selector, (ISuperReadNode) rcvrNode);
} else {
dispatch = new LexicallyBoundDispatchNode(selector, rcvrNode.getEnclosingMixinId());
dispatch = UninitializedDispatchNode.createLexicallyBound(selector, rcvrNode.getEnclosingMixinId());
}
GenericMessageSendNode node = new GenericMessageSendNode(selector,
@@ -1,42 +0,0 @@
package som.interpreter.nodes.dispatch;
import som.VM;
import som.compiler.AccessModifier;
import som.interpreter.SArguments;
import som.interpreter.nodes.dispatch.AbstractDispatchNode.AbstractCachedDispatchNode;
import som.vm.Symbols;
import som.vmobjects.SClass;
import som.vmobjects.SSymbol;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.frame.VirtualFrame;
public abstract class AbstractCachedDnuNode extends AbstractCachedDispatchNode {
private final SSymbol selector;
@TruffleBoundary
public static CallTarget getDnu(final SClass rcvrClass) {
Dispatchable disp = rcvrClass.lookupMessage(
Symbols.DNU, AccessModifier.PROTECTED);
if (disp == null) {
VM.errorExit("Lookup of " + rcvrClass.getName().getString() + ">>#doesNotUnderstand:arguments: failed.");
}
return disp.getCallTarget();
}
public AbstractCachedDnuNode(final SClass rcvrClass,
final SSymbol selector, final AbstractDispatchNode nextInCache) {
super(getDnu(rcvrClass), nextInCache);
this.selector = selector;
}
protected final Object performDnu(final VirtualFrame frame, final Object[] arguments,
final Object rcvr) {
Object[] argsArr = new Object[] {
rcvr, selector, SArguments.getArgumentsWithoutReceiver(arguments) };
return cachedMethod.call(frame, argsArr);
}
}
@@ -1,33 +1,13 @@
package som.interpreter.nodes.dispatch;
import com.oracle.truffle.api.CallTarget;
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.Node;
public abstract class AbstractDispatchNode extends Node implements DispatchChain {
public abstract class AbstractDispatchNode
extends Node implements DispatchChain {
public static final int INLINE_CACHE_SIZE = 6;
public abstract Object executeDispatch(
final VirtualFrame frame, final Object[] arguments);
public abstract static class AbstractCachedDispatchNode
extends AbstractDispatchNode {
@Child protected DirectCallNode cachedMethod;
@Child protected AbstractDispatchNode nextInCache;
public AbstractCachedDispatchNode(final CallTarget methodCallTarget,
final AbstractDispatchNode nextInCache) {
this.cachedMethod = Truffle.getRuntime().createDirectCallNode(methodCallTarget);
this.nextInCache = nextInCache;
}
@Override
public final int lengthOfDispatchChain() {
return 1 + nextInCache.lengthOfDispatchChain();
}
}
}
@@ -1,20 +0,0 @@
package som.interpreter.nodes.dispatch;
import som.vmobjects.SSymbol;
public abstract class AbstractDispatchWithLookupNode extends
AbstractDispatchNode {
protected final SSymbol selector;
public AbstractDispatchWithLookupNode(final SSymbol selector) {
super();
this.selector = selector;
}
@Override
public String toString() {
return "*Dispatch(" + selector.getString() + ")";
}
}
@@ -0,0 +1,43 @@
package som.interpreter.nodes.dispatch;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives;
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.InvalidAssumptionException;
public final class CachedDispatchNode extends AbstractDispatchNode {
@Child private DirectCallNode cachedMethod;
@Child private AbstractDispatchNode nextInCache;
private final DispatchGuard guard;
public CachedDispatchNode(final CallTarget methodCallTarget,
final DispatchGuard guard, final AbstractDispatchNode nextInCache) {
this.cachedMethod = Truffle.getRuntime().createDirectCallNode(methodCallTarget);
this.guard = guard;
this.nextInCache = nextInCache;
}
@Override
public Object executeDispatch(final VirtualFrame frame, final Object[] arguments) {
try {
if (guard.entryMatches(arguments[0])) {
return cachedMethod.call(frame, arguments);
} else {
return nextInCache.executeDispatch(frame, arguments);
}
} catch (InvalidAssumptionException e) {
CompilerDirectives.transferToInterpreter();
return replace(nextInCache).executeDispatch(frame, arguments);
}
}
@Override
public int lengthOfDispatchChain() {
return 1 + nextInCache.lengthOfDispatchChain();
}
}
@@ -1,56 +0,0 @@
package som.interpreter.nodes.dispatch;
import som.interpreter.nodes.dispatch.AbstractDispatchNode.AbstractCachedDispatchNode;
import som.interpreter.objectstorage.ClassFactory;
import som.vmobjects.SObjectWithClass;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.frame.VirtualFrame;
public final class CachedDispatchSObjectCheckNode extends AbstractCachedDispatchNode {
private final ClassFactory expectedClassFactory;
public CachedDispatchSObjectCheckNode(final ClassFactory rcvrFactory,
final CallTarget callTarget, final AbstractDispatchNode nextInCache) {
super(callTarget, nextInCache);
this.expectedClassFactory = rcvrFactory;
}
@Override
public Object executeDispatch(
final VirtualFrame frame, final Object[] arguments) {
SObjectWithClass rcvr = (SObjectWithClass) arguments[0];
if (rcvr.getFactory() == expectedClassFactory) {
return cachedMethod.call(frame, arguments);
} else {
return nextInCache.executeDispatch(frame, arguments);
}
}
public static final class CachedDispatchSObjectWithoutFieldsCheckNode extends AbstractCachedDispatchNode {
private final ClassFactory expectedClassFactory;
public CachedDispatchSObjectWithoutFieldsCheckNode(
final ClassFactory rcvrFactory, final CallTarget callTarget,
final AbstractDispatchNode nextInCache) {
super(callTarget, nextInCache);
this.expectedClassFactory = rcvrFactory;
}
@Override
public Object executeDispatch(
final VirtualFrame frame, final Object[] arguments) {
SObjectWithClass rcvr = (SObjectWithClass) arguments[0];
if (rcvr.getFactory() == expectedClassFactory) {
return cachedMethod.call(frame, arguments);
} else {
return nextInCache.executeDispatch(frame, arguments);
}
}
}
}
@@ -1,65 +0,0 @@
package som.interpreter.nodes.dispatch;
import som.interpreter.nodes.dispatch.AbstractDispatchNode.AbstractCachedDispatchNode;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.frame.VirtualFrame;
public final class CachedDispatchSimpleCheckNode extends AbstractCachedDispatchNode {
private final Class<?> expectedClass;
public CachedDispatchSimpleCheckNode(final Class<?> rcvrClass,
final CallTarget callTarget, final AbstractDispatchNode nextInCache) {
super(callTarget, nextInCache);
this.expectedClass = rcvrClass;
}
@Override
public Object executeDispatch(final VirtualFrame frame,
final Object[] arguments) {
Object rcvr = arguments[0];
if (rcvr.getClass() == expectedClass) {
return cachedMethod.call(frame, arguments);
} else {
return nextInCache.executeDispatch(frame, arguments);
}
}
public static final class CachedDispatchTrueCheckNode
extends AbstractCachedDispatchNode {
public CachedDispatchTrueCheckNode(final CallTarget callTarget,
final AbstractDispatchNode nextInCache) {
super(callTarget, nextInCache);
}
@Override
public Object executeDispatch(final VirtualFrame frame,
final Object[] arguments) {
if (arguments[0] == Boolean.TRUE) {
return cachedMethod.call(frame, arguments);
} else {
return nextInCache.executeDispatch(frame, arguments);
}
}
}
public static final class CachedDispatchFalseCheckNode
extends AbstractCachedDispatchNode {
public CachedDispatchFalseCheckNode(final CallTarget callTarget,
final AbstractDispatchNode nextInCache) {
super(callTarget, nextInCache);
}
@Override
public Object executeDispatch(final VirtualFrame frame,
final Object[] arguments) {
if (arguments[0] == Boolean.FALSE) {
return cachedMethod.call(frame, arguments);
} else {
return nextInCache.executeDispatch(frame, arguments);
}
}
}
}
Oops, something went wrong.

0 comments on commit a6d57fd

Please sign in to comment.