|
25 | 25 | public class ReadCallerFrameNode extends RubyBaseNode {
|
26 | 26 |
|
27 | 27 | private final ConditionProfile callerFrameProfile = ConditionProfile.createBinaryProfile();
|
28 |
| - @CompilationFinal private volatile boolean firstCall = true; |
| 28 | + @CompilationFinal private volatile boolean deoptWhenNotPassedCallerFrame = true; |
29 | 29 |
|
30 | 30 | public static ReadCallerFrameNode create() {
|
31 | 31 | return new ReadCallerFrameNode();
|
32 | 32 | }
|
33 | 33 |
|
34 | 34 | public MaterializedFrame execute(VirtualFrame frame) {
|
35 |
| - // Avoid polluting the profile for the first call which has to use getCallerFrame() |
36 |
| - if (firstCall) { |
37 |
| - CompilerDirectives.transferToInterpreterAndInvalidate(); |
38 |
| - firstCall = false; |
39 |
| - notifyCallerToSendFrame(); |
40 |
| - return getCallerFrame(); |
41 |
| - } |
42 |
| - |
43 | 35 | final MaterializedFrame callerFrame = RubyArguments.getCallerFrame(frame);
|
44 | 36 |
|
45 | 37 | if (callerFrameProfile.profile(callerFrame != null)) {
|
46 | 38 | return callerFrame;
|
47 | 39 | } else {
|
| 40 | + // Every time the caller of the method using ReadCallerFrameNode changes, |
| 41 | + // we need to notify the caller's CachedDispatchNode to pass us the frame next time. |
| 42 | + if (deoptWhenNotPassedCallerFrame) { |
| 43 | + // Invalidate because deoptWhenNotPassedCallerFrame might change and require recompilation |
| 44 | + CompilerDirectives.transferToInterpreterAndInvalidate(); |
| 45 | + } |
48 | 46 | return getCallerFrame();
|
49 | 47 | }
|
50 | 48 | }
|
51 | 49 |
|
52 |
| - private void notifyCallerToSendFrame() { |
| 50 | + @TruffleBoundary |
| 51 | + private MaterializedFrame getCallerFrame() { |
| 52 | + if (!notifyCallerToSendFrame()) { |
| 53 | + // If we fail to notify the call node (e.g., because it is a UncachedDispatchNode which is not handled yet), |
| 54 | + // we don't want to deoptimize this CallTarget on every call. |
| 55 | + deoptWhenNotPassedCallerFrame = false; |
| 56 | + } |
| 57 | + return getContext().getCallStack().getCallerFrameIgnoringSend().getFrame(FrameInstance.FrameAccess.MATERIALIZE).materialize(); |
| 58 | + } |
| 59 | + |
| 60 | + private boolean notifyCallerToSendFrame() { |
53 | 61 | final Node callerNode = getContext().getCallStack().getCallerNode(0, false);
|
54 | 62 | if (callerNode instanceof DirectCallNode) {
|
55 | 63 | final Node parent = callerNode.getParent();
|
56 | 64 | if (parent instanceof CachedDispatchNode) {
|
57 | 65 | ((CachedDispatchNode) parent).startSendingOwnFrame();
|
| 66 | + return true; |
58 | 67 | }
|
59 | 68 | }
|
60 |
| - } |
61 | 69 |
|
62 |
| - @TruffleBoundary |
63 |
| - private MaterializedFrame getCallerFrame() { |
64 |
| - return getContext().getCallStack().getCallerFrameIgnoringSend().getFrame(FrameInstance.FrameAccess.MATERIALIZE).materialize(); |
| 70 | + return false; |
65 | 71 | }
|
66 | 72 |
|
67 | 73 | }
|
0 commit comments