Skip to content

Commit 41f27ab

Browse files
committed
Make dispatch chain walk non-recursive
Signed-off-by: Stefan Marr <[email protected]>
1 parent 8d7828b commit 41f27ab

File tree

8 files changed

+247
-335
lines changed

8 files changed

+247
-335
lines changed

src/trufflesom/interpreter/nodes/MessageSendNode.java

Lines changed: 149 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,33 @@
11
package trufflesom.interpreter.nodes;
22

3-
import com.oracle.truffle.api.CompilerAsserts;
3+
import com.oracle.truffle.api.CallTarget;
4+
import com.oracle.truffle.api.CompilerDirectives;
5+
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
46
import com.oracle.truffle.api.Truffle;
57
import com.oracle.truffle.api.frame.VirtualFrame;
68
import com.oracle.truffle.api.nodes.DirectCallNode;
79
import com.oracle.truffle.api.nodes.ExplodeLoop;
10+
import com.oracle.truffle.api.nodes.InvalidAssumptionException;
811
import com.oracle.truffle.api.nodes.NodeCost;
912
import com.oracle.truffle.api.source.SourceSection;
1013

1114
import bd.primitives.Specializer;
1215
import bd.primitives.nodes.PreevaluatedExpression;
1316
import bd.tools.nodes.Invocation;
14-
import trufflesom.interpreter.TruffleCompiler;
17+
import trufflesom.interpreter.Types;
1518
import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode;
16-
import trufflesom.interpreter.nodes.dispatch.DispatchChain.Cost;
19+
import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.CachedDispatchNode;
20+
import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.CachedExprNode;
21+
import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.GuardedDispatchNode;
22+
import trufflesom.interpreter.nodes.dispatch.CachedDnuNode;
23+
import trufflesom.interpreter.nodes.dispatch.DispatchGuard;
1724
import trufflesom.interpreter.nodes.dispatch.GenericDispatchNode;
18-
import trufflesom.interpreter.nodes.dispatch.UninitializedDispatchNode;
1925
import trufflesom.primitives.Primitives;
2026
import trufflesom.vm.NotYetImplementedException;
2127
import trufflesom.vm.Universe;
2228
import trufflesom.vmobjects.SClass;
2329
import trufflesom.vmobjects.SInvokable;
30+
import trufflesom.vmobjects.SObject;
2431
import trufflesom.vmobjects.SSymbol;
2532

2633

@@ -32,8 +39,7 @@ public static ExpressionNode create(final SSymbol selector,
3239
Specializer<Universe, ExpressionNode, SSymbol> specializer =
3340
prims.getParserSpecializer(selector, arguments);
3441
if (specializer == null) {
35-
return new UninitializedMessageSendNode(
36-
selector, arguments, universe).initialize(source);
42+
return new GenericMessageSendNode(selector, arguments, universe).initialize(source);
3743
}
3844

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

4551
public static AbstractMessageSendNode createForPerformNodes(final SSymbol selector,
4652
final SourceSection source, final Universe universe) {
47-
return new UninitializedMessageSendNode(selector, NO_ARGS, universe).initialize(source);
53+
return new GenericMessageSendNode(selector, NO_ARGS, universe).initialize(source);
4854
}
4955

5056
public static GenericMessageSendNode createGeneric(final SSymbol selector,
5157
final ExpressionNode[] argumentNodes, final SourceSection source,
5258
final Universe universe) {
53-
return new GenericMessageSendNode(selector, argumentNodes,
54-
new UninitializedDispatchNode(selector, universe)).initialize(source);
59+
return new GenericMessageSendNode(
60+
selector, argumentNodes, universe, true).initialize(source);
5561
}
5662

5763
public static AbstractMessageSendNode createSuperSend(final SClass superClass,
@@ -102,109 +108,171 @@ private Object[] evaluateArguments(final VirtualFrame frame) {
102108
public abstract int getNumberOfArguments();
103109
}
104110

105-
public static final class UninitializedMessageSendNode extends AbstractMessageSendNode {
111+
public static final class GenericMessageSendNode
112+
extends AbstractMessageSendNode {
113+
114+
private final SSymbol selector;
115+
private final Universe universe;
116+
117+
private final int numberOfSignatureArguments;
118+
119+
@CompilationFinal private boolean triedEager;
106120

107-
protected final SSymbol selector;
108-
protected final Universe universe;
121+
@Child private GuardedDispatchNode dispatchCache;
109122

110-
protected UninitializedMessageSendNode(final SSymbol selector,
111-
final ExpressionNode[] arguments, final Universe universe) {
123+
private GenericMessageSendNode(final SSymbol selector, final ExpressionNode[] arguments,
124+
final Universe universe, final boolean triedEager) {
112125
super(arguments);
113126
this.selector = selector;
114127
this.universe = universe;
128+
this.triedEager = triedEager;
129+
this.numberOfSignatureArguments = selector.getNumberOfSignatureArguments();
115130
}
116131

117-
@Override
118-
public String toString() {
119-
return getClass().getSimpleName() + "(" + selector.getString() + ")";
132+
private GenericMessageSendNode(final SSymbol selector, final ExpressionNode[] arguments,
133+
final Universe universe) {
134+
this(selector, arguments, universe, false);
120135
}
121136

122137
@Override
123-
public Object doPreEvaluated(final VirtualFrame frame, final Object[] arguments) {
124-
return specialize(arguments).doPreEvaluated(frame, arguments);
125-
}
126-
127-
private PreevaluatedExpression specialize(final Object[] arguments) {
128-
TruffleCompiler.transferToInterpreterAndInvalidate("Specialize Message Node");
129-
130-
// We treat super sends separately for simplicity, might not be the
131-
// optimal solution, especially in cases were the knowledge of the
132-
// receiver class also allows us to do more specific things, but for the
133-
// moment we will leave it at this.
134-
// TODO: revisit, and also do more specific optimizations for super sends.
135-
136-
Primitives prims = universe.getPrimitives();
137-
138-
Specializer<Universe, ExpressionNode, SSymbol> specializer =
139-
prims.getEagerSpecializer(selector, arguments, argumentNodes);
140-
141-
if (specializer != null) {
142-
PreevaluatedExpression newNode =
143-
(PreevaluatedExpression) specializer.create(arguments, argumentNodes,
144-
sourceSection, universe);
138+
@ExplodeLoop
139+
public Object doPreEvaluated(final VirtualFrame frame,
140+
final Object[] arguments) {
145141

146-
return (PreevaluatedExpression) replace((ExpressionNode) newNode);
142+
GuardedDispatchNode cache = dispatchCache;
143+
144+
Object rcvr = arguments[0];
145+
146+
while (cache != null) {
147+
try {
148+
if (cache.entryMatches(rcvr)) {
149+
return cache.doPreEvaluated(frame, arguments);
150+
}
151+
} catch (InvalidAssumptionException e) {
152+
// Remove invalid node from dispatch chain
153+
CompilerDirectives.transferToInterpreterAndInvalidate();
154+
if (cache.getParent() == this) {
155+
if (cache.next == null) {
156+
dispatchCache = null;
157+
cache = null;
158+
} else {
159+
dispatchCache = insert(cache.next);
160+
cache = cache.next;
161+
}
162+
} else {
163+
GuardedDispatchNode parent = (GuardedDispatchNode) cache.getParent();
164+
if (cache.next == null) {
165+
parent.next = null;
166+
cache = null;
167+
} else {
168+
parent.next = insert(cache.next);
169+
cache = cache.next;
170+
}
171+
}
172+
continue;
173+
}
174+
// cache guard
175+
// --> apply cache
176+
cache = cache.next;
147177
}
148178

149-
return makeGenericSend();
150-
}
179+
CompilerDirectives.transferToInterpreterAndInvalidate();
151180

152-
private GenericMessageSendNode makeGenericSend() {
153-
GenericMessageSendNode send = new GenericMessageSendNode(selector, argumentNodes,
154-
new UninitializedDispatchNode(selector, universe)).initialize(sourceSection);
155-
return replace(send);
181+
return specialize(arguments).doPreEvaluated(frame, arguments);
156182
}
157183

158184
@Override
159-
public SSymbol getInvocationIdentifier() {
160-
return selector;
185+
public String toString() {
186+
return "GMsgSend(" + selector.getString() + ")";
161187
}
162188

163189
@Override
164-
public int getNumberOfArguments() {
165-
return selector.getNumberOfSignatureArguments();
190+
public NodeCost getCost() {
191+
return NodeCost.NONE;
166192
}
167-
}
168193

169-
// TODO: currently, we do not only specialize the given stuff above, but also what has been
170-
// classified as 'value' sends in the OMOP branch. Is that a problem?
194+
private PreevaluatedExpression specialize(final Object[] arguments) {
195+
if (!triedEager) {
196+
triedEager = true;
197+
PreevaluatedExpression eager = attemptEagerSpecialization(arguments);
198+
if (eager != null) {
199+
return eager;
200+
}
201+
}
171202

172-
public static final class GenericMessageSendNode
173-
extends AbstractMessageSendNode {
203+
final GuardedDispatchNode first = dispatchCache;
204+
GuardedDispatchNode cache = first;
205+
int cacheSize = 0;
206+
while (cache != null) {
207+
cache = cache.next;
208+
cacheSize += 1;
209+
}
174210

175-
private final SSymbol selector;
176-
private final int numberOfSignatureArguments;
211+
Object rcvr = arguments[0];
212+
assert rcvr != null;
177213

178-
@Child private AbstractDispatchNode dispatchNode;
214+
if (rcvr instanceof SObject) {
215+
SObject r = (SObject) rcvr;
216+
if (r.updateLayoutToMatchClass() && first != null) {
217+
// if the dispatchCache is null, we end up here, so continue directly below instead
218+
// otherwise, let's retry the cache!
219+
return this;
220+
}
221+
}
179222

180-
private GenericMessageSendNode(final SSymbol selector, final ExpressionNode[] arguments,
181-
final AbstractDispatchNode dispatchNode) {
182-
super(arguments);
183-
this.selector = selector;
184-
this.dispatchNode = dispatchNode;
185-
this.numberOfSignatureArguments = selector.getNumberOfSignatureArguments();
186-
}
223+
if (cacheSize < AbstractDispatchNode.INLINE_CACHE_SIZE) {
224+
SClass rcvrClass = Types.getClassOf(rcvr, universe);
225+
SInvokable method = rcvrClass.lookupInvokable(selector);
226+
CallTarget callTarget = null;
227+
PreevaluatedExpression expr = null;
228+
if (method != null) {
229+
if (method.isTrivial()) {
230+
expr = method.copyTrivialNode();
231+
assert expr != null;
232+
} else {
233+
callTarget = method.getCallTarget();
234+
}
235+
}
236+
237+
DispatchGuard guard = DispatchGuard.create(rcvr);
238+
239+
GuardedDispatchNode node;
240+
if (expr != null) {
241+
node = new CachedExprNode(guard, expr);
242+
} else if (method != null) {
243+
node = new CachedDispatchNode(guard, callTarget);
244+
} else {
245+
node = new CachedDnuNode(rcvrClass, guard, selector);
246+
}
247+
248+
if (first != null) {
249+
node.next = node.insertHere(first);
250+
}
251+
dispatchCache = insert(node);
252+
return node;
253+
}
187254

188-
@Override
189-
public Object doPreEvaluated(final VirtualFrame frame,
190-
final Object[] arguments) {
191-
return dispatchNode.executeDispatch(frame, arguments);
255+
// the chain is longer than the maximum defined by INLINE_CACHE_SIZE and
256+
// thus, this callsite is considered to be megaprophic, and we generalize it.
257+
GenericDispatchNode generic = new GenericDispatchNode(selector, universe);
258+
dispatchCache = insert(generic);
259+
return generic;
192260
}
193261

194-
public void replaceDispatchListHead(
195-
final GenericDispatchNode replacement) {
196-
CompilerAsserts.neverPartOfCompilation();
197-
dispatchNode.replace(replacement);
198-
}
262+
private PreevaluatedExpression attemptEagerSpecialization(final Object[] arguments) {
263+
Primitives prims = universe.getPrimitives();
199264

200-
@Override
201-
public String toString() {
202-
return "GMsgSend(" + selector.getString() + ")";
203-
}
265+
Specializer<Universe, ExpressionNode, SSymbol> specializer =
266+
prims.getEagerSpecializer(selector, arguments, argumentNodes);
204267

205-
@Override
206-
public NodeCost getCost() {
207-
return Cost.getCost(dispatchNode);
268+
if (specializer != null) {
269+
PreevaluatedExpression newNode =
270+
(PreevaluatedExpression) specializer.create(arguments, argumentNodes,
271+
sourceSection, universe);
272+
273+
return (PreevaluatedExpression) replace((ExpressionNode) newNode);
274+
}
275+
return null;
208276
}
209277

210278
@Override

src/trufflesom/interpreter/nodes/dispatch/AbstractDispatchNode.java

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,66 @@
44
import com.oracle.truffle.api.Truffle;
55
import com.oracle.truffle.api.frame.VirtualFrame;
66
import com.oracle.truffle.api.nodes.DirectCallNode;
7+
import com.oracle.truffle.api.nodes.InvalidAssumptionException;
78
import com.oracle.truffle.api.nodes.Node;
89

10+
import bd.primitives.nodes.PreevaluatedExpression;
911

10-
public abstract class AbstractDispatchNode extends Node implements DispatchChain {
12+
13+
public abstract class AbstractDispatchNode extends Node implements PreevaluatedExpression {
1114
public static final int INLINE_CACHE_SIZE = 6;
1215

13-
public abstract Object executeDispatch(VirtualFrame frame, Object[] arguments);
16+
@Override
17+
public abstract Object doPreEvaluated(VirtualFrame frame, Object[] args);
18+
19+
public abstract boolean entryMatches(Object rcvr) throws InvalidAssumptionException;
20+
21+
public abstract static class GuardedDispatchNode extends AbstractDispatchNode {
22+
private final DispatchGuard guard;
23+
24+
@Child public GuardedDispatchNode next;
25+
26+
protected GuardedDispatchNode(final DispatchGuard guard) {
27+
this.guard = guard;
28+
}
1429

15-
public abstract static class AbstractCachedDispatchNode
16-
extends AbstractDispatchNode {
30+
@Override
31+
public boolean entryMatches(final Object rcvr) throws InvalidAssumptionException {
32+
return guard.entryMatches(rcvr);
33+
}
34+
35+
public final <T extends Node> T insertHere(final T newChild) {
36+
return super.insert(newChild);
37+
}
38+
}
39+
40+
public static final class CachedDispatchNode extends GuardedDispatchNode {
41+
42+
@Child protected DirectCallNode cachedMethod;
43+
44+
public CachedDispatchNode(final DispatchGuard guard, final CallTarget callTarget) {
45+
super(guard);
46+
cachedMethod = insert(Truffle.getRuntime().createDirectCallNode(callTarget));
47+
}
48+
49+
@Override
50+
public Object doPreEvaluated(final VirtualFrame frame, final Object[] arguments) {
51+
return cachedMethod.call(arguments);
52+
}
53+
}
1754

18-
@Child protected DirectCallNode cachedMethod;
19-
@Child protected AbstractDispatchNode nextInCache;
55+
public static final class CachedExprNode extends GuardedDispatchNode {
2056

21-
public AbstractCachedDispatchNode(final CallTarget methodCallTarget,
22-
final AbstractDispatchNode nextInCache) {
23-
DirectCallNode cachedMethod =
24-
Truffle.getRuntime().createDirectCallNode(methodCallTarget);
57+
@Child protected PreevaluatedExpression expr;
2558

26-
this.cachedMethod = cachedMethod;
27-
this.nextInCache = nextInCache;
59+
public CachedExprNode(final DispatchGuard guard, final PreevaluatedExpression expr) {
60+
super(guard);
61+
this.expr = expr;
2862
}
2963

3064
@Override
31-
public final int lengthOfDispatchChain() {
32-
return 1 + nextInCache.lengthOfDispatchChain();
65+
public Object doPreEvaluated(final VirtualFrame frame, final Object[] arguments) {
66+
return expr.doPreEvaluated(frame, arguments);
3367
}
3468
}
3569
}

0 commit comments

Comments
 (0)