Skip to content

Commit 0c6cfff

Browse files
committed
[GR-19549] Refactor sending of caller frames to optimise more cases.
PullRequest: truffleruby/1203
2 parents 51fe27b + e74f1d5 commit 0c6cfff

13 files changed

+212
-133
lines changed

CHANGELOG.md

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

8788
# 19.3.0
8889

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

Lines changed: 64 additions & 47 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,28 @@ 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,
628+
NotProvided line,
626629
NotProvided block,
627630
@Cached IndirectCallNode callNode) {
628-
return classEvalSource(module, code, "(eval)", callNode);
631+
return classEvalSource(frame, module, code, "(eval)", callNode);
629632
}
630633

631634
@Specialization(guards = { "isRubyString(code)", "isRubyString(file)" })
632-
protected Object classEval(DynamicObject module, DynamicObject code, DynamicObject file, NotProvided line,
635+
protected Object classEval(VirtualFrame frame, DynamicObject module, DynamicObject code, DynamicObject file,
636+
NotProvided line,
633637
NotProvided block,
634638
@Cached IndirectCallNode callNode) {
635-
return classEvalSource(module, code, StringOperations.getString(file), callNode);
639+
return classEvalSource(frame, module, code, StringOperations.getString(file), callNode);
636640
}
637641

638642
@Specialization(guards = { "isRubyString(code)", "isRubyString(file)" })
639-
protected Object classEval(DynamicObject module, DynamicObject code, DynamicObject file, int line,
643+
protected Object classEval(VirtualFrame frame, DynamicObject module, DynamicObject code, DynamicObject file,
644+
int line,
640645
NotProvided block,
641646
@Cached IndirectCallNode callNode) {
642647
final CodeLoader.DeferredCall deferredCall = classEvalSource(
648+
frame,
643649
module,
644650
code,
645651
StringOperations.getString(file),
@@ -651,35 +657,37 @@ protected Object classEval(DynamicObject module, DynamicObject code, DynamicObje
651657
protected Object classEval(VirtualFrame frame, DynamicObject module, Object code, NotProvided file,
652658
NotProvided line, NotProvided block,
653659
@Cached IndirectCallNode callNode) {
654-
return classEvalSource(module, toStr(frame, code), "(eval)", callNode);
660+
return classEvalSource(frame, module, toStr(frame, code), "(eval)", callNode);
655661
}
656662

657663
@Specialization(guards = { "isRubyString(code)", "wasProvided(file)" })
658664
protected Object classEval(VirtualFrame frame, DynamicObject module, DynamicObject code, Object file,
659665
NotProvided line, NotProvided block,
660666
@Cached IndirectCallNode callNode) {
661-
return classEvalSource(module, code, StringOperations.getString(toStr(frame, file)), callNode);
667+
return classEvalSource(frame, module, code, StringOperations.getString(toStr(frame, file)), callNode);
662668
}
663669

664-
private Object classEvalSource(DynamicObject module, DynamicObject code, String file,
670+
private Object classEvalSource(VirtualFrame frame, DynamicObject module, DynamicObject code, String file,
665671
@Cached IndirectCallNode callNode) {
666-
final CodeLoader.DeferredCall deferredCall = classEvalSource(module, code, file, 1);
672+
final CodeLoader.DeferredCall deferredCall = classEvalSource(frame, module, code, file, 1);
667673
return deferredCall.call(callNode);
668674
}
669675

670-
@TruffleBoundary
671-
private CodeLoader.DeferredCall classEvalSource(DynamicObject module, DynamicObject rubySource, String file,
672-
int line) {
676+
private CodeLoader.DeferredCall classEvalSource(VirtualFrame frame, DynamicObject module,
677+
DynamicObject rubySource, String file, int line) {
673678
assert RubyGuards.isRubyString(rubySource);
674679

680+
final MaterializedFrame callerFrame = readCallerFrameNode.execute(frame);
681+
682+
return classEvalSourceInternal(module, rubySource, file, line, callerFrame);
683+
}
684+
685+
@TruffleBoundary
686+
private CodeLoader.DeferredCall classEvalSourceInternal(DynamicObject module, DynamicObject rubySource,
687+
String file, int line, MaterializedFrame callerFrame) {
675688
final RubySource source = createEvalSourceNode
676689
.createEvalSource(StringOperations.rope(rubySource), "class/module_eval", file, line);
677690

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

10731081
@Child private AddMethodNode addMethodNode = AddMethodNode.create(false);
1082+
@Child private ReadCallerFrameNode readCallerFrame = ReadCallerFrameNode.create();
10741083

10751084
@CreateCast("name")
10761085
protected RubyNode coerceToString(RubyNode name) {
@@ -1083,18 +1092,18 @@ protected DynamicObject defineMethod(DynamicObject module, String name, NotProvi
10831092
throw new RaiseException(getContext(), coreExceptions().argumentError("needs either proc or block", this));
10841093
}
10851094

1086-
@TruffleBoundary
10871095
@Specialization
1088-
protected DynamicObject defineMethodBlock(DynamicObject module, String name, NotProvided proc,
1096+
protected DynamicObject defineMethodBlock(VirtualFrame frame, DynamicObject module, String name,
1097+
NotProvided proc,
10891098
DynamicObject block) {
1090-
return defineMethodProc(module, name, block, NotProvided.INSTANCE);
1099+
return defineMethodProc(frame, module, name, block, NotProvided.INSTANCE);
10911100
}
10921101

1093-
@TruffleBoundary
10941102
@Specialization(guards = "isRubyProc(proc)")
1095-
protected DynamicObject defineMethodProc(DynamicObject module, String name, DynamicObject proc,
1103+
protected DynamicObject defineMethodProc(VirtualFrame frame, DynamicObject module, String name,
1104+
DynamicObject proc,
10961105
NotProvided block) {
1097-
return defineMethod(module, name, proc);
1106+
return defineMethod(module, name, proc, readCallerFrame.execute(frame));
10981107
}
10991108

11001109
@TruffleBoundary
@@ -1121,10 +1130,17 @@ protected DynamicObject defineMethodMethod(DynamicObject module, String name, Dy
11211130
return getSymbol(name);
11221131
}
11231132

1124-
@TruffleBoundary
11251133
@Specialization(guards = "isRubyUnboundMethod(method)")
1126-
protected DynamicObject defineMethod(DynamicObject module, String name, DynamicObject method,
1134+
protected DynamicObject defineMethod(VirtualFrame frame, DynamicObject module, String name,
1135+
DynamicObject method,
11271136
NotProvided block) {
1137+
final MaterializedFrame callerFrame = readCallerFrame.execute(frame);
1138+
return defineMethodInternal(module, name, method, callerFrame);
1139+
}
1140+
1141+
@TruffleBoundary
1142+
private DynamicObject defineMethodInternal(DynamicObject module, String name, DynamicObject method,
1143+
final MaterializedFrame callerFrame) {
11281144
final InternalMethod internalMethod = Layouts.UNBOUND_METHOD.getMethod(method);
11291145
if (!ModuleOperations.canBindMethodTo(internalMethod, module)) {
11301146
final DynamicObject declaringModule = internalMethod.getDeclaringModule();
@@ -1142,11 +1158,12 @@ protected DynamicObject defineMethod(DynamicObject module, String name, DynamicO
11421158
}
11431159
}
11441160

1145-
return addMethod(module, name, internalMethod);
1161+
return addMethod(module, name, internalMethod, callerFrame);
11461162
}
11471163

11481164
@TruffleBoundary
1149-
private DynamicObject defineMethod(DynamicObject module, String name, DynamicObject proc) {
1165+
private DynamicObject defineMethod(DynamicObject module, String name, DynamicObject proc,
1166+
MaterializedFrame callerFrame) {
11501167
final RootCallTarget callTarget = Layouts.PROC.getCallTargetForLambdas(proc);
11511168
final RubyRootNode rootNode = (RubyRootNode) callTarget.getRootNode();
11521169
final SharedMethodInfo info = Layouts.PROC.getSharedMethodInfo(proc).forDefineMethod(module, name);
@@ -1173,7 +1190,7 @@ private DynamicObject defineMethod(DynamicObject module, String name, DynamicObj
11731190
Visibility.PUBLIC,
11741191
proc,
11751192
newCallTarget);
1176-
return addMethod(module, name, method);
1193+
return addMethod(module, name, method, callerFrame);
11771194
}
11781195

11791196
private static class CallMethodWithProcBody extends RubyNode {
@@ -1195,11 +1212,11 @@ public Object execute(VirtualFrame frame) {
11951212
}
11961213

11971214
@TruffleBoundary
1198-
private DynamicObject addMethod(DynamicObject module, String name, InternalMethod method) {
1215+
private DynamicObject addMethod(DynamicObject module, String name, InternalMethod method,
1216+
MaterializedFrame callerFrame) {
11991217
method = method.withName(name);
12001218

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

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

Lines changed: 4 additions & 6 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;
@@ -38,7 +39,6 @@
3839
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
3940
import com.oracle.truffle.api.dsl.Cached;
4041
import com.oracle.truffle.api.dsl.Specialization;
41-
import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
4242
import com.oracle.truffle.api.frame.MaterializedFrame;
4343
import com.oracle.truffle.api.frame.VirtualFrame;
4444
import com.oracle.truffle.api.object.DynamicObject;
@@ -73,11 +73,9 @@ public abstract DynamicObject executeProcNew(
7373

7474
@Specialization
7575
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();
76+
@Cached("create(nil())") FindAndReadDeclarationVariableNode readNode,
77+
@Cached ReadCallerFrameNode readCaller) {
78+
final MaterializedFrame parentFrame = readCaller.execute(frame);
8179

8280
DynamicObject parentBlock = (DynamicObject) readNode
8381
.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)