Skip to content

Commit ba4986d

Browse files
committed
Added dispatch inline cache
Tail calls are now optimized with new direct call nodes.
1 parent f5ccc8d commit ba4986d

File tree

8 files changed

+170
-65
lines changed

8 files changed

+170
-65
lines changed

graal/src/mumbler/truffle/TruffleMumblerMain.java

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
import mumbler.truffle.node.builtin.NowBuiltinNodeFactory;
1616
import mumbler.truffle.node.builtin.PrintlnBuiltinNodeFactory;
1717
import mumbler.truffle.node.builtin.SubBuiltinNodeFactory;
18-
import mumbler.truffle.node.call.TailCallException;
18+
import mumbler.truffle.node.call.GenericDispatchNode;
19+
import mumbler.truffle.node.call.InvokeNode;
1920
import mumbler.truffle.type.MumblerFunction;
2021
import mumbler.truffle.type.MumblerList;
2122

2223
import com.oracle.truffle.api.Truffle;
2324
import com.oracle.truffle.api.frame.FrameDescriptor;
2425
import com.oracle.truffle.api.frame.FrameSlot;
2526
import com.oracle.truffle.api.frame.VirtualFrame;
26-
import com.oracle.truffle.api.nodes.DirectCallNode;
2727

2828
public class TruffleMumblerMain {
2929
public static void main(String[] args) throws IOException {
@@ -72,16 +72,11 @@ private static Object execute(MumblerList<MumblerNode> nodes,
7272
StreamSupport.stream(nodes.spliterator(), false)
7373
.toArray(size -> new MumblerNode[size]),
7474
frameDescriptor);
75-
DirectCallNode directCallNode = Truffle.getRuntime()
76-
.createDirectCallNode(function.callTarget);
7775

78-
try {
79-
return directCallNode.call(
80-
topFrame,
81-
new Object[] {topFrame.materialize()});
82-
} catch (TailCallException e) {
83-
return e.call(topFrame);
84-
}
76+
return InvokeNode.call(topFrame,
77+
function.callTarget,
78+
new Object[] {topFrame.materialize()},
79+
new GenericDispatchNode());
8580
}
8681

8782
private static VirtualFrame createTopFrame(FrameDescriptor frameDescriptor) {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package mumbler.truffle.node.call;
2+
3+
import com.oracle.truffle.api.CallTarget;
4+
import com.oracle.truffle.api.Truffle;
5+
import com.oracle.truffle.api.frame.VirtualFrame;
6+
import com.oracle.truffle.api.nodes.DirectCallNode;
7+
8+
public class DirectDispatchNode extends DispatchNode {
9+
private final CallTarget cachedCallTarget;
10+
11+
@Child private DirectCallNode callCachedTargetNode;
12+
@Child private DispatchNode nextNode;
13+
14+
public DirectDispatchNode(DispatchNode next, CallTarget callTarget) {
15+
this.cachedCallTarget = callTarget;
16+
this.callCachedTargetNode = Truffle.getRuntime().createDirectCallNode(
17+
this.cachedCallTarget);
18+
this.nextNode = next;
19+
}
20+
21+
@Override
22+
protected Object executeDispatch(VirtualFrame frame, CallTarget callTarget,
23+
Object[] arguments) {
24+
if (this.cachedCallTarget == callTarget) {
25+
return this.callCachedTargetNode.call(frame, arguments);
26+
}
27+
return this.nextNode.executeDispatch(frame, callTarget, arguments);
28+
}
29+
}
Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
11
package mumbler.truffle.node.call;
22

3-
import mumbler.truffle.type.MumblerFunction;
4-
5-
import com.oracle.truffle.api.Truffle;
3+
import com.oracle.truffle.api.CallTarget;
64
import com.oracle.truffle.api.frame.VirtualFrame;
7-
import com.oracle.truffle.api.nodes.IndirectCallNode;
85
import com.oracle.truffle.api.nodes.Node;
96

10-
public class DispatchNode extends Node {
11-
@Child private IndirectCallNode callNode = Truffle.getRuntime()
12-
.createIndirectCallNode();
7+
public abstract class DispatchNode extends Node {
8+
protected static final int INLINE_CACHE_SIZE = 2;
139

14-
public Object executeDispatch(VirtualFrame virtualFrame,
15-
MumblerFunction function, Object[] arguments) {
16-
return this.callNode.call(virtualFrame, function.callTarget, arguments);
17-
}
18-
}
10+
protected abstract Object executeDispatch(VirtualFrame virtualFrame,
11+
CallTarget callTarget, Object[] argumentValues);
12+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package mumbler.truffle.node.call;
2+
3+
import com.oracle.truffle.api.CallTarget;
4+
import com.oracle.truffle.api.Truffle;
5+
import com.oracle.truffle.api.frame.VirtualFrame;
6+
import com.oracle.truffle.api.nodes.IndirectCallNode;
7+
8+
public class GenericDispatchNode extends DispatchNode {
9+
@Child private IndirectCallNode callNode = Truffle.getRuntime()
10+
.createIndirectCallNode();
11+
12+
@Override
13+
protected Object executeDispatch(VirtualFrame virtualFrame,
14+
CallTarget callTarget, Object[] argumentValues) {
15+
16+
CallTarget currentCallTarget = callTarget;
17+
Object[] arguments = argumentValues;
18+
do {
19+
try {
20+
return this.callNode.call(virtualFrame, currentCallTarget,
21+
arguments);
22+
} catch (TailCallException e) {
23+
currentCallTarget = e.callTarget;
24+
arguments = e.arguments;
25+
}
26+
} while(true);
27+
}
28+
}

graal/src/mumbler/truffle/node/call/InvokeNode.java

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,23 @@
55
import mumbler.truffle.node.MumblerNode;
66
import mumbler.truffle.type.MumblerFunction;
77

8+
import com.oracle.truffle.api.CallTarget;
89
import com.oracle.truffle.api.CompilerAsserts;
9-
import com.oracle.truffle.api.CompilerDirectives;
10-
import com.oracle.truffle.api.Truffle;
1110
import com.oracle.truffle.api.dsl.UnsupportedSpecializationException;
1211
import com.oracle.truffle.api.frame.VirtualFrame;
13-
import com.oracle.truffle.api.nodes.DirectCallNode;
1412
import com.oracle.truffle.api.nodes.ExplodeLoop;
1513
import com.oracle.truffle.api.nodes.Node;
1614
import com.oracle.truffle.api.nodes.UnexpectedResultException;
1715

1816
public class InvokeNode extends MumblerNode {
1917
@Child protected MumblerNode functionNode;
2018
@Children protected final MumblerNode[] argumentNodes;
21-
@Child protected DirectCallNode callNode;
19+
@Child protected DispatchNode dispatchNode;
2220

2321
public InvokeNode(MumblerNode functionNode, MumblerNode[] argumentNodes) {
2422
this.functionNode = functionNode;
2523
this.argumentNodes = argumentNodes;
24+
this.dispatchNode = new UninitializedDispatchNode();
2625
}
2726

2827
@Override
@@ -37,25 +36,22 @@ public Object execute(VirtualFrame virtualFrame) {
3736
argumentValues[i+1] = this.argumentNodes[i].execute(virtualFrame);
3837
}
3938

40-
if (this.callNode == null) {
41-
CompilerDirectives.transferToInterpreterAndInvalidate();
42-
this.callNode = this.insert(Truffle.getRuntime()
43-
.createDirectCallNode(function.callTarget));
44-
}
45-
46-
if (function.callTarget != this.callNode.getCallTarget()) {
47-
CompilerDirectives.transferToInterpreterAndInvalidate();
48-
throw new UnsupportedOperationException(
49-
"Need to implement a proper inline cache.");
39+
if (this.isTail()) {
40+
throw new TailCallException(function.callTarget, argumentValues);
5041
}
42+
return call(virtualFrame, function.callTarget, argumentValues,
43+
this.dispatchNode);
44+
}
5145

52-
if (this.isTail()) {
53-
throw new TailCallException(this.callNode, argumentValues);
54-
} else {
46+
public static Object call(VirtualFrame virtualFrame, CallTarget callTarget,
47+
Object[] arguments, DispatchNode dispatchNode) {
48+
while (true) {
5549
try {
56-
return this.callNode.call(virtualFrame, argumentValues);
50+
return dispatchNode.executeDispatch(virtualFrame,
51+
callTarget, arguments);
5752
} catch (TailCallException e) {
58-
return e.call(virtualFrame);
53+
callTarget = e.callTarget;
54+
arguments = e.arguments;
5955
}
6056
}
6157
}
Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,16 @@
11
package mumbler.truffle.node.call;
22

3-
import com.oracle.truffle.api.frame.VirtualFrame;
3+
import com.oracle.truffle.api.CallTarget;
44
import com.oracle.truffle.api.nodes.ControlFlowException;
5-
import com.oracle.truffle.api.nodes.DirectCallNode;
65

76
public class TailCallException extends ControlFlowException {
87
private static final long serialVersionUID = 1L;
98

10-
private final DirectCallNode callNode;
11-
private final Object[] arguments;
12-
public boolean outOfFunction = false;
9+
public final CallTarget callTarget;
10+
public final Object[] arguments;
1311

14-
public TailCallException(DirectCallNode callNode, Object[] arguments) {
15-
this.callNode = callNode;
12+
public TailCallException(CallTarget callTarget, Object[] arguments) {
13+
this.callTarget = callTarget;
1614
this.arguments = arguments;
1715
}
18-
19-
public Object call(VirtualFrame virtualFrame) {
20-
TailCallException tailCall = null;
21-
try {
22-
return this.callNode.call(virtualFrame, this.arguments);
23-
} catch (TailCallException e) {
24-
tailCall = e;
25-
}
26-
do {
27-
try {
28-
return tailCall.callNode.call(virtualFrame, tailCall.arguments);
29-
} catch (TailCallException e) {
30-
tailCall = e;
31-
}
32-
} while (true);
33-
}
3416
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package mumbler.truffle.node.call;
2+
3+
import java.util.Arrays;
4+
5+
import mumbler.truffle.node.MumblerNode;
6+
7+
import com.oracle.truffle.api.frame.VirtualFrame;
8+
import com.oracle.truffle.api.nodes.DirectCallNode;
9+
10+
public class TailCallInvokeNode extends MumblerNode {
11+
@Child protected MumblerNode functionNode;
12+
@Children protected final MumblerNode[] argumentNodes;
13+
@Child protected DirectCallNode callNode;
14+
15+
public TailCallInvokeNode(MumblerNode functionNode,
16+
MumblerNode[] argumentNodes) {
17+
this.functionNode = functionNode;
18+
this.argumentNodes = argumentNodes;
19+
}
20+
21+
@Override
22+
public String toString() {
23+
return "(apply " + this.functionNode + " " +
24+
Arrays.toString(this.argumentNodes) + ")";
25+
}
26+
27+
@Override
28+
public Object execute(VirtualFrame virtualFrame) {
29+
// TODO Auto-generated method stub
30+
return null;
31+
}
32+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package mumbler.truffle.node.call;
2+
3+
import com.oracle.truffle.api.CallTarget;
4+
import com.oracle.truffle.api.CompilerDirectives;
5+
import com.oracle.truffle.api.frame.VirtualFrame;
6+
import com.oracle.truffle.api.nodes.Node;
7+
8+
final public class UninitializedDispatchNode extends DispatchNode {
9+
@Override
10+
protected Object executeDispatch(VirtualFrame virtualFrame,
11+
CallTarget callTarget, Object[] arguments) {
12+
CompilerDirectives.transferToInterpreterAndInvalidate();
13+
14+
Node cur = this;
15+
int depth = 0;
16+
while (cur.getParent() instanceof DispatchNode) {
17+
cur = cur.getParent();
18+
depth++;
19+
}
20+
InvokeNode invokeNode = (InvokeNode) cur.getParent();
21+
22+
DispatchNode replacement;
23+
if (callTarget == null) {
24+
/* Corner case: the function is not defined, so report an error to
25+
* the user. */
26+
throw new RuntimeException("Call of undefined function");
27+
} else if (depth < INLINE_CACHE_SIZE) {
28+
/* Extend the inline cache. Allocate the new cache entry, and the
29+
* new end of the cache. */
30+
DispatchNode next = new UninitializedDispatchNode();
31+
replacement = new DirectDispatchNode(next, callTarget);
32+
/* Replace ourself with the new cache entry. */
33+
this.replace(replacement);
34+
} else {
35+
/* Cache size exceeded, fall back to a single generic dispatch
36+
* node. */
37+
replacement = new GenericDispatchNode();
38+
/* Replace the whole chain, not just ourself, with the new generic
39+
* node. */
40+
invokeNode.dispatchNode.replace(replacement);
41+
}
42+
43+
/*
44+
* Execute the newly created node perform the actual dispatch. That
45+
* saves us from duplicating the actual call logic here.
46+
*/
47+
return replacement.executeDispatch(virtualFrame, callTarget, arguments);
48+
}
49+
}

0 commit comments

Comments
 (0)