Skip to content

Commit a6d57fd

Browse files
committed
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>
1 parent 0b36fcc commit a6d57fd

20 files changed

+589
-676
lines changed

src/som/compiler/MixinDefinition.java

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121
import som.interpreter.nodes.SlotAccessNode.SlotReadNode;
2222
import som.interpreter.nodes.SlotAccessNode.SlotWriteNode;
2323
import som.interpreter.nodes.dispatch.AbstractDispatchNode;
24-
import som.interpreter.nodes.dispatch.CachedSlotAccessNode;
25-
import som.interpreter.nodes.dispatch.CachedSlotAccessNode.CachedSlotWriteNode;
26-
import som.interpreter.nodes.dispatch.CachedSlotAccessNode.CheckedCachedSlotAccessNode;
27-
import som.interpreter.nodes.dispatch.CachedSlotAccessNode.CheckedCachedSlotWriteNode;
24+
import som.interpreter.nodes.dispatch.CachedSlotAccessNode.CachedSlotRead;
25+
import som.interpreter.nodes.dispatch.CachedSlotAccessNode.CachedSlotWrite;
26+
import som.interpreter.nodes.dispatch.CachedSlotAccessNode.LexicallyBoundImmutableSlotRead;
27+
import som.interpreter.nodes.dispatch.CachedSlotAccessNode.LexicallyBoundMutableSlotRead;
28+
import som.interpreter.nodes.dispatch.CachedSlotAccessNode.LexicallyBoundMutableSlotWrite;
29+
import som.interpreter.nodes.dispatch.DispatchGuard;
2830
import som.interpreter.nodes.dispatch.Dispatchable;
2931
import som.interpreter.nodes.literals.NilLiteralNode;
3032
import som.interpreter.objectstorage.ClassFactory;
@@ -38,6 +40,7 @@
3840
import som.vmobjects.SInvokable;
3941
import som.vmobjects.SInvokable.SInitializer;
4042
import som.vmobjects.SObject;
43+
import som.vmobjects.SObject.SImmutableObject;
4144
import som.vmobjects.SObjectWithClass;
4245
import som.vmobjects.SSymbol;
4346

@@ -494,12 +497,17 @@ public String toString() {
494497

495498
@Override
496499
public AbstractDispatchNode getDispatchNode(final Object rcvr,
497-
final Object rcvrClass, final AbstractDispatchNode next) {
498-
assert rcvrClass instanceof SClass;
500+
final AbstractDispatchNode next) {
499501
if (modifier == AccessModifier.PRIVATE) {
500-
return new CachedSlotAccessNode(createNode());
502+
// TODO: we actually should try to remove SImmuableObject, and use a caching node
503+
// 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
504+
if (rcvr instanceof SImmutableObject) {
505+
return new LexicallyBoundImmutableSlotRead(createNode());
506+
} else {
507+
return new LexicallyBoundMutableSlotRead(createNode());
508+
}
501509
} else {
502-
return new CheckedCachedSlotAccessNode(((SClass) rcvrClass).getLayoutForInstances(), createNode(), next);
510+
return new CachedSlotRead(createNode(), DispatchGuard.create(rcvr), next);
503511
}
504512
}
505513

@@ -562,12 +570,12 @@ public SlotMutator(final SSymbol name, final AccessModifier acccessModifier,
562570

563571
@Override
564572
public AbstractDispatchNode getDispatchNode(final Object rcvr,
565-
final Object rcvrClass, final AbstractDispatchNode next) {
566-
assert rcvrClass instanceof SClass;
573+
final AbstractDispatchNode next) {
567574
if (modifier == AccessModifier.PRIVATE) {
568-
return new CachedSlotWriteNode(createWriteNode());
575+
return new LexicallyBoundMutableSlotWrite(createWriteNode());
569576
} else {
570-
return new CheckedCachedSlotWriteNode(((SClass) rcvrClass).getInstanceFactory(), createWriteNode(), next);
577+
return new CachedSlotWrite(createWriteNode(),
578+
DispatchGuard.create(rcvr), next);
571579
}
572580
}
573581

src/som/interpreter/nodes/MessageSendNode.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
import som.interpreter.nodes.dispatch.AbstractDispatchNode;
88
import som.interpreter.nodes.dispatch.DispatchChain.Cost;
99
import som.interpreter.nodes.dispatch.GenericDispatchNode;
10-
import som.interpreter.nodes.dispatch.LexicallyBoundDispatchNode;
11-
import som.interpreter.nodes.dispatch.SuperDispatchNode;
1210
import som.interpreter.nodes.dispatch.UninitializedDispatchNode;
1311
import som.interpreter.nodes.literals.BlockNode;
1412
import som.interpreter.nodes.nary.EagerBinaryPrimitiveNode;
@@ -119,7 +117,8 @@ public static GenericMessageSendNode createGeneric(final SSymbol selector,
119117
throw new NotYetImplementedException();
120118
} else {
121119
return new GenericMessageSendNode(selector, argumentNodes,
122-
new UninitializedDispatchNode(selector, AccessModifier.PUBLIC), source);
120+
UninitializedDispatchNode.createRcvrSend(selector, AccessModifier.PUBLIC),
121+
source);
123122
}
124123
}
125124

@@ -220,7 +219,7 @@ protected PreevaluatedExpression makeSend() {
220219
private GenericMessageSendNode makeOrdenarySend() {
221220
GenericMessageSendNode send = new GenericMessageSendNode(selector,
222221
argumentNodes,
223-
new UninitializedDispatchNode(selector, AccessModifier.PUBLIC),
222+
UninitializedDispatchNode.createRcvrSend(selector, AccessModifier.PUBLIC),
224223
getSourceSection());
225224
return replace(send);
226225
}
@@ -636,9 +635,9 @@ protected PreevaluatedExpression makeSpecialSend() {
636635
AbstractDispatchNode dispatch;
637636

638637
if (rcvrNode.isSuperSend()) {
639-
dispatch = SuperDispatchNode.create(selector, (ISuperReadNode) rcvrNode);
638+
dispatch = UninitializedDispatchNode.createSuper(selector, (ISuperReadNode) rcvrNode);
640639
} else {
641-
dispatch = new LexicallyBoundDispatchNode(selector, rcvrNode.getEnclosingMixinId());
640+
dispatch = UninitializedDispatchNode.createLexicallyBound(selector, rcvrNode.getEnclosingMixinId());
642641
}
643642

644643
GenericMessageSendNode node = new GenericMessageSendNode(selector,

src/som/interpreter/nodes/dispatch/AbstractCachedDnuNode.java

Lines changed: 0 additions & 42 deletions
This file was deleted.
Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,13 @@
11
package som.interpreter.nodes.dispatch;
22

3-
import com.oracle.truffle.api.CallTarget;
4-
import com.oracle.truffle.api.Truffle;
53
import com.oracle.truffle.api.frame.VirtualFrame;
6-
import com.oracle.truffle.api.nodes.DirectCallNode;
74
import com.oracle.truffle.api.nodes.Node;
85

96

10-
public abstract class AbstractDispatchNode extends Node implements DispatchChain {
7+
public abstract class AbstractDispatchNode
8+
extends Node implements DispatchChain {
119
public static final int INLINE_CACHE_SIZE = 6;
1210

1311
public abstract Object executeDispatch(
1412
final VirtualFrame frame, final Object[] arguments);
15-
16-
public abstract static class AbstractCachedDispatchNode
17-
extends AbstractDispatchNode {
18-
19-
@Child protected DirectCallNode cachedMethod;
20-
@Child protected AbstractDispatchNode nextInCache;
21-
22-
public AbstractCachedDispatchNode(final CallTarget methodCallTarget,
23-
final AbstractDispatchNode nextInCache) {
24-
this.cachedMethod = Truffle.getRuntime().createDirectCallNode(methodCallTarget);
25-
this.nextInCache = nextInCache;
26-
}
27-
28-
@Override
29-
public final int lengthOfDispatchChain() {
30-
return 1 + nextInCache.lengthOfDispatchChain();
31-
}
32-
}
3313
}

src/som/interpreter/nodes/dispatch/AbstractDispatchWithLookupNode.java

Lines changed: 0 additions & 20 deletions
This file was deleted.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package som.interpreter.nodes.dispatch;
2+
3+
import com.oracle.truffle.api.CallTarget;
4+
import com.oracle.truffle.api.CompilerDirectives;
5+
import com.oracle.truffle.api.Truffle;
6+
import com.oracle.truffle.api.frame.VirtualFrame;
7+
import com.oracle.truffle.api.nodes.DirectCallNode;
8+
import com.oracle.truffle.api.nodes.InvalidAssumptionException;
9+
10+
11+
public final class CachedDispatchNode extends AbstractDispatchNode {
12+
13+
@Child private DirectCallNode cachedMethod;
14+
@Child private AbstractDispatchNode nextInCache;
15+
16+
private final DispatchGuard guard;
17+
18+
public CachedDispatchNode(final CallTarget methodCallTarget,
19+
final DispatchGuard guard, final AbstractDispatchNode nextInCache) {
20+
this.cachedMethod = Truffle.getRuntime().createDirectCallNode(methodCallTarget);
21+
this.guard = guard;
22+
this.nextInCache = nextInCache;
23+
}
24+
25+
@Override
26+
public Object executeDispatch(final VirtualFrame frame, final Object[] arguments) {
27+
try {
28+
if (guard.entryMatches(arguments[0])) {
29+
return cachedMethod.call(frame, arguments);
30+
} else {
31+
return nextInCache.executeDispatch(frame, arguments);
32+
}
33+
} catch (InvalidAssumptionException e) {
34+
CompilerDirectives.transferToInterpreter();
35+
return replace(nextInCache).executeDispatch(frame, arguments);
36+
}
37+
}
38+
39+
@Override
40+
public int lengthOfDispatchChain() {
41+
return 1 + nextInCache.lengthOfDispatchChain();
42+
}
43+
}

src/som/interpreter/nodes/dispatch/CachedDispatchSObjectCheckNode.java

Lines changed: 0 additions & 56 deletions
This file was deleted.

src/som/interpreter/nodes/dispatch/CachedDispatchSimpleCheckNode.java

Lines changed: 0 additions & 65 deletions
This file was deleted.

0 commit comments

Comments
 (0)