Skip to content

Commit 344a5f9

Browse files
committed
Refactor sending of caller frames to optimise more cases.
1 parent cc491b9 commit 344a5f9

13 files changed

+210
-124
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ Performance:
7070
* Change to using thread local buffers for socket calls to reduce allocations.
7171
* Refactor `IO.select` to reduce copying and optimisation boundaries.
7272
* Refactor various `String` and `Rope` nodes to avoid Truffle performance warnings.
73+
* Reading caller frames should now work in more cases without deoptimisation.
7374

7475
# 19.3.0
7576

src/main/java/org/truffleruby/core/module/ModuleNodes.java

Lines changed: 55 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import org.truffleruby.language.WarningNode;
6767
import org.truffleruby.language.arguments.MissingArgumentBehavior;
6868
import org.truffleruby.language.arguments.ProfileArgumentNodeGen;
69+
import org.truffleruby.language.arguments.ReadCallerFrameNode;
6970
import org.truffleruby.language.arguments.ReadPreArgumentNode;
7071
import org.truffleruby.language.arguments.ReadSelfNode;
7172
import org.truffleruby.language.arguments.RubyArguments;
@@ -386,24 +387,24 @@ public GenerateAccessorNode(boolean isGetter) {
386387
this.isGetter = isGetter;
387388
}
388389

389-
public abstract DynamicObject executeGenerateAccessor(DynamicObject module, Object name);
390+
public abstract DynamicObject executeGenerateAccessor(VirtualFrame frame, DynamicObject module, Object name);
390391

391392
@Specialization
392-
protected DynamicObject generateAccessor(DynamicObject module, Object nameObject,
393-
@Cached NameToJavaStringNode nameToJavaStringNode) {
393+
protected DynamicObject generateAccessor(VirtualFrame frame, DynamicObject module, Object nameObject,
394+
@Cached NameToJavaStringNode nameToJavaStringNode,
395+
@Cached ReadCallerFrameNode readCallerFrame) {
394396
final String name = nameToJavaStringNode.executeToJavaString(nameObject);
395-
createAccessor(module, name);
397+
createAccessor(module, name, readCallerFrame.execute(frame));
396398
return nil();
397399
}
398400

399401
@TruffleBoundary
400-
private void createAccessor(DynamicObject module, String name) {
402+
private void createAccessor(DynamicObject module, String name, MaterializedFrame callerFrame) {
401403
final SourceSection sourceSection = getContext()
402404
.getCallStack()
403405
.getCallerNodeIgnoringSend()
404406
.getEncapsulatingSourceSection();
405407
final SourceIndexLength sourceIndexLength = new SourceIndexLength(sourceSection);
406-
final Frame callerFrame = getContext().getCallStack().getCallerFrameIgnoringSend(FrameAccess.READ_ONLY);
407408
final Visibility visibility = DeclarationContext.findVisibility(callerFrame);
408409
final Arity arity = isGetter ? Arity.NO_ARGUMENTS : Arity.ONE_REQUIRED;
409410
final String ivar = "@" + name;
@@ -464,7 +465,7 @@ public abstract static class AttrNode extends CoreMethodArrayArgumentsNode {
464465
@Child private WarningNode warnNode;
465466

466467
@Specialization
467-
protected DynamicObject attr(DynamicObject module, Object[] names) {
468+
protected DynamicObject attr(VirtualFrame frame, DynamicObject module, Object[] names) {
468469
final boolean setter;
469470
if (names.length == 2 && names[1] instanceof Boolean) {
470471
warnObsoletedBooleanArgument();
@@ -475,9 +476,9 @@ protected DynamicObject attr(DynamicObject module, Object[] names) {
475476
}
476477

477478
for (Object name : names) {
478-
generateGetterNode.executeGenerateAccessor(module, name);
479+
generateGetterNode.executeGenerateAccessor(frame, module, name);
479480
if (setter) {
480-
generateSetterNode.executeGenerateAccessor(module, name);
481+
generateSetterNode.executeGenerateAccessor(frame, module, name);
481482
}
482483
}
483484
return nil();
@@ -501,10 +502,10 @@ public abstract static class AttrAccessorNode extends CoreMethodArrayArgumentsNo
501502
@Child private GenerateAccessorNode generateSetterNode = GenerateAccessorNodeGen.create(false);
502503

503504
@Specialization
504-
protected DynamicObject attrAccessor(DynamicObject module, Object[] names) {
505+
protected DynamicObject attrAccessor(VirtualFrame frame, DynamicObject module, Object[] names) {
505506
for (Object name : names) {
506-
generateGetterNode.executeGenerateAccessor(module, name);
507-
generateSetterNode.executeGenerateAccessor(module, name);
507+
generateGetterNode.executeGenerateAccessor(frame, module, name);
508+
generateSetterNode.executeGenerateAccessor(frame, module, name);
508509
}
509510
return nil();
510511
}
@@ -517,9 +518,9 @@ public abstract static class AttrReaderNode extends CoreMethodArrayArgumentsNode
517518
@Child private GenerateAccessorNode generateGetterNode = GenerateAccessorNodeGen.create(true);
518519

519520
@Specialization
520-
protected DynamicObject attrReader(DynamicObject module, Object[] names) {
521+
protected DynamicObject attrReader(VirtualFrame frame, DynamicObject module, Object[] names) {
521522
for (Object name : names) {
522-
generateGetterNode.executeGenerateAccessor(module, name);
523+
generateGetterNode.executeGenerateAccessor(frame, module, name);
523524
}
524525
return nil();
525526
}
@@ -532,9 +533,9 @@ public abstract static class AttrWriterNode extends CoreMethodArrayArgumentsNode
532533
@Child private GenerateAccessorNode generateSetterNode = GenerateAccessorNodeGen.create(false);
533534

534535
@Specialization
535-
protected DynamicObject attrWriter(DynamicObject module, Object[] names) {
536+
protected DynamicObject attrWriter(VirtualFrame frame, DynamicObject module, Object[] names) {
536537
for (Object name : names) {
537-
generateSetterNode.executeGenerateAccessor(module, name);
538+
generateSetterNode.executeGenerateAccessor(frame, module, name);
538539
}
539540
return nil();
540541
}
@@ -612,6 +613,7 @@ public abstract static class ClassEvalNode extends CoreMethodArrayArgumentsNode
612613

613614
@Child private CreateEvalSourceNode createEvalSourceNode = new CreateEvalSourceNode();
614615
@Child private ToStrNode toStrNode;
616+
@Child private ReadCallerFrameNode readCallerFrameNode = ReadCallerFrameNode.create();
615617

616618
protected DynamicObject toStr(VirtualFrame frame, Object object) {
617619
if (toStrNode == null) {
@@ -622,24 +624,25 @@ protected DynamicObject toStr(VirtualFrame frame, Object object) {
622624
}
623625

624626
@Specialization(guards = "isRubyString(code)")
625-
protected Object classEval(DynamicObject module, DynamicObject code, NotProvided file, NotProvided line,
627+
protected Object classEval(VirtualFrame frame, DynamicObject module, DynamicObject code, NotProvided file, NotProvided line,
626628
NotProvided block,
627629
@Cached IndirectCallNode callNode) {
628-
return classEvalSource(module, code, "(eval)", callNode);
630+
return classEvalSource(frame, module, code, "(eval)", callNode);
629631
}
630632

631633
@Specialization(guards = { "isRubyString(code)", "isRubyString(file)" })
632-
protected Object classEval(DynamicObject module, DynamicObject code, DynamicObject file, NotProvided line,
634+
protected Object classEval(VirtualFrame frame, DynamicObject module, DynamicObject code, DynamicObject file, NotProvided line,
633635
NotProvided block,
634636
@Cached IndirectCallNode callNode) {
635-
return classEvalSource(module, code, StringOperations.getString(file), callNode);
637+
return classEvalSource(frame, module, code, StringOperations.getString(file), callNode);
636638
}
637639

638640
@Specialization(guards = { "isRubyString(code)", "isRubyString(file)" })
639-
protected Object classEval(DynamicObject module, DynamicObject code, DynamicObject file, int line,
641+
protected Object classEval(VirtualFrame frame, DynamicObject module, DynamicObject code, DynamicObject file, int line,
640642
NotProvided block,
641643
@Cached IndirectCallNode callNode) {
642644
final CodeLoader.DeferredCall deferredCall = classEvalSource(
645+
frame,
643646
module,
644647
code,
645648
StringOperations.getString(file),
@@ -651,35 +654,37 @@ protected Object classEval(DynamicObject module, DynamicObject code, DynamicObje
651654
protected Object classEval(VirtualFrame frame, DynamicObject module, Object code, NotProvided file,
652655
NotProvided line, NotProvided block,
653656
@Cached IndirectCallNode callNode) {
654-
return classEvalSource(module, toStr(frame, code), "(eval)", callNode);
657+
return classEvalSource(frame, module, toStr(frame, code), "(eval)", callNode);
655658
}
656659

657660
@Specialization(guards = { "isRubyString(code)", "wasProvided(file)" })
658661
protected Object classEval(VirtualFrame frame, DynamicObject module, DynamicObject code, Object file,
659662
NotProvided line, NotProvided block,
660663
@Cached IndirectCallNode callNode) {
661-
return classEvalSource(module, code, StringOperations.getString(toStr(frame, file)), callNode);
664+
return classEvalSource(frame, module, code, StringOperations.getString(toStr(frame, file)), callNode);
662665
}
663666

664-
private Object classEvalSource(DynamicObject module, DynamicObject code, String file,
667+
private Object classEvalSource(VirtualFrame frame, DynamicObject module, DynamicObject code, String file,
665668
@Cached IndirectCallNode callNode) {
666-
final CodeLoader.DeferredCall deferredCall = classEvalSource(module, code, file, 1);
669+
final CodeLoader.DeferredCall deferredCall = classEvalSource(frame, module, code, file, 1);
667670
return deferredCall.call(callNode);
668671
}
669672

670-
@TruffleBoundary
671-
private CodeLoader.DeferredCall classEvalSource(DynamicObject module, DynamicObject rubySource, String file,
673+
private CodeLoader.DeferredCall classEvalSource(VirtualFrame frame, DynamicObject module, DynamicObject rubySource, String file,
672674
int line) {
673675
assert RubyGuards.isRubyString(rubySource);
674676

677+
final MaterializedFrame callerFrame = readCallerFrameNode.execute(frame);
678+
679+
return classEvalSourceInternal(module, rubySource, file, line, callerFrame);
680+
}
681+
682+
@TruffleBoundary
683+
private CodeLoader.DeferredCall classEvalSourceInternal(DynamicObject module, DynamicObject rubySource, String file, int line,
684+
final MaterializedFrame callerFrame) {
675685
final RubySource source = createEvalSourceNode
676686
.createEvalSource(StringOperations.rope(rubySource), "class/module_eval", file, line);
677687

678-
final MaterializedFrame callerFrame = getContext()
679-
.getCallStack()
680-
.getCallerFrameIgnoringSend(FrameAccess.MATERIALIZE)
681-
.materialize();
682-
683688
final RubyRootNode rootNode = getContext().getCodeLoader().parse(
684689
source,
685690
ParserContext.MODULE,
@@ -1071,6 +1076,7 @@ private void warnAlreadyInitializedConstant(DynamicObject module, String name,
10711076
public abstract static class DefineMethodNode extends CoreMethodNode {
10721077

10731078
@Child private AddMethodNode addMethodNode = AddMethodNode.create(false);
1079+
@Child private ReadCallerFrameNode readCallerFrame = ReadCallerFrameNode.create();
10741080

10751081
@CreateCast("name")
10761082
protected RubyNode coerceToString(RubyNode name) {
@@ -1083,18 +1089,16 @@ protected DynamicObject defineMethod(DynamicObject module, String name, NotProvi
10831089
throw new RaiseException(getContext(), coreExceptions().argumentError("needs either proc or block", this));
10841090
}
10851091

1086-
@TruffleBoundary
10871092
@Specialization
1088-
protected DynamicObject defineMethodBlock(DynamicObject module, String name, NotProvided proc,
1093+
protected DynamicObject defineMethodBlock(VirtualFrame frame, DynamicObject module, String name, NotProvided proc,
10891094
DynamicObject block) {
1090-
return defineMethodProc(module, name, block, NotProvided.INSTANCE);
1095+
return defineMethodProc(frame, module, name, block, NotProvided.INSTANCE);
10911096
}
10921097

1093-
@TruffleBoundary
10941098
@Specialization(guards = "isRubyProc(proc)")
1095-
protected DynamicObject defineMethodProc(DynamicObject module, String name, DynamicObject proc,
1099+
protected DynamicObject defineMethodProc(VirtualFrame frame, DynamicObject module, String name, DynamicObject proc,
10961100
NotProvided block) {
1097-
return defineMethod(module, name, proc);
1101+
return defineMethod(module, name, proc, readCallerFrame.execute(frame));
10981102
}
10991103

11001104
@TruffleBoundary
@@ -1121,10 +1125,16 @@ protected DynamicObject defineMethodMethod(DynamicObject module, String name, Dy
11211125
return getSymbol(name);
11221126
}
11231127

1124-
@TruffleBoundary
11251128
@Specialization(guards = "isRubyUnboundMethod(method)")
1126-
protected DynamicObject defineMethod(DynamicObject module, String name, DynamicObject method,
1129+
protected DynamicObject defineMethod(VirtualFrame frame, DynamicObject module, String name, DynamicObject method,
11271130
NotProvided block) {
1131+
final MaterializedFrame callerFrame = readCallerFrame.execute(frame);
1132+
return defineMethodInternal(module, name, method, callerFrame);
1133+
}
1134+
1135+
@TruffleBoundary
1136+
private DynamicObject defineMethodInternal(DynamicObject module, String name, DynamicObject method,
1137+
final MaterializedFrame callerFrame) {
11281138
final InternalMethod internalMethod = Layouts.UNBOUND_METHOD.getMethod(method);
11291139
if (!ModuleOperations.canBindMethodTo(internalMethod, module)) {
11301140
final DynamicObject declaringModule = internalMethod.getDeclaringModule();
@@ -1142,11 +1152,11 @@ protected DynamicObject defineMethod(DynamicObject module, String name, DynamicO
11421152
}
11431153
}
11441154

1145-
return addMethod(module, name, internalMethod);
1155+
return addMethod(module, name, internalMethod, callerFrame);
11461156
}
11471157

11481158
@TruffleBoundary
1149-
private DynamicObject defineMethod(DynamicObject module, String name, DynamicObject proc) {
1159+
private DynamicObject defineMethod(DynamicObject module, String name, DynamicObject proc, MaterializedFrame callerFrame) {
11501160
final RootCallTarget callTarget = Layouts.PROC.getCallTargetForLambdas(proc);
11511161
final RubyRootNode rootNode = (RubyRootNode) callTarget.getRootNode();
11521162
final SharedMethodInfo info = Layouts.PROC.getSharedMethodInfo(proc).forDefineMethod(module, name);
@@ -1173,7 +1183,7 @@ private DynamicObject defineMethod(DynamicObject module, String name, DynamicObj
11731183
Visibility.PUBLIC,
11741184
proc,
11751185
newCallTarget);
1176-
return addMethod(module, name, method);
1186+
return addMethod(module, name, method, callerFrame);
11771187
}
11781188

11791189
private static class CallMethodWithProcBody extends RubyNode {
@@ -1195,11 +1205,10 @@ public Object execute(VirtualFrame frame) {
11951205
}
11961206

11971207
@TruffleBoundary
1198-
private DynamicObject addMethod(DynamicObject module, String name, InternalMethod method) {
1208+
private DynamicObject addMethod(DynamicObject module, String name, InternalMethod method, MaterializedFrame callerFrame) {
11991209
method = method.withName(name);
12001210

1201-
final Frame frame = getContext().getCallStack().getCallerFrameIgnoringSend(FrameAccess.READ_ONLY);
1202-
final Visibility visibility = GetCurrentVisibilityNode.getVisibilityFromNameAndFrame(name, frame);
1211+
final Visibility visibility = GetCurrentVisibilityNode.getVisibilityFromNameAndFrame(name, callerFrame);
12031212
addMethodNode.executeAddMethod(module, method, visibility);
12041213
return getSymbol(method.getName());
12051214
}

src/main/java/org/truffleruby/core/proc/ProcNodes.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.truffleruby.language.NotProvided;
2626
import org.truffleruby.language.Visibility;
2727
import org.truffleruby.language.arguments.ArgumentDescriptorUtils;
28+
import org.truffleruby.language.arguments.ReadCallerFrameNode;
2829
import org.truffleruby.language.arguments.RubyArguments;
2930
import org.truffleruby.language.control.RaiseException;
3031
import org.truffleruby.language.dispatch.CallDispatchHeadNode;
@@ -73,11 +74,9 @@ public abstract DynamicObject executeProcNew(
7374

7475
@Specialization
7576
protected DynamicObject proc(VirtualFrame frame, DynamicObject procClass, Object[] args, NotProvided block,
76-
@Cached("create(nil())") FindAndReadDeclarationVariableNode readNode) {
77-
final MaterializedFrame parentFrame = getContext()
78-
.getCallStack()
79-
.getCallerFrameIgnoringSend(FrameAccess.MATERIALIZE)
80-
.materialize();
77+
@Cached("create(nil())") FindAndReadDeclarationVariableNode readNode,
78+
@Cached ReadCallerFrameNode readCaller) {
79+
final MaterializedFrame parentFrame = readCaller.execute(frame);
8180

8281
DynamicObject parentBlock = (DynamicObject) readNode
8382
.execute(parentFrame, TranslatorEnvironment.METHOD_BLOCK_NAME);

src/main/java/org/truffleruby/language/CallStackManager.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ public Frame getCurrentFrame(FrameAccess frameAccess) {
5252

5353
@TruffleBoundary
5454
public Frame getCallerFrameIgnoringSend(FrameAccess frameAccess) {
55+
// System.err.printf("Getting a caller frame...\n");
56+
// new Error().printStackTrace();
5557
return getCallerFrameIgnoringSend(f -> isRubyFrameAndNotSend(f.getFrame(FrameAccess.READ_ONLY)), frameAccess);
5658
}
5759

0 commit comments

Comments
 (0)