Skip to content

Commit 6105b1e

Browse files
committed
[GR-25674] Cache Symbol#to_proc by DeclarationContext.
PullRequest: truffleruby/1895
2 parents 5cfbbd0 + a35c460 commit 6105b1e

26 files changed

+445
-284
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ Compatibility:
4242
Performance:
4343

4444
* Calls with a literal block are no longer always split but instead the decision is made by the Truffle splitting heuristic.
45+
* `Symbol#to_proc` is now AST-inlined in order to not rely on splitting and to avoid needing the caller frame to find refinements which apply.
46+
* `Symbol#to_proc` is now globally cached per Symbol and refinements, to avoid creating many redundant `CallTargets`.
4547

4648
Changes:
4749

src/main/java/org/truffleruby/RubyLanguage.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.truffleruby.core.rope.RopeCache;
2323
import org.truffleruby.core.string.CoreStrings;
2424
import org.truffleruby.core.string.StringUtils;
25+
import org.truffleruby.core.symbol.CoreSymbols;
2526
import org.truffleruby.core.symbol.RubySymbol;
2627
import org.truffleruby.core.symbol.SymbolTable;
2728
import org.truffleruby.core.thread.RubyThread;
@@ -94,6 +95,7 @@ public class RubyLanguage extends TruffleLanguage<RubyContext> {
9495
@CompilationFinal private volatile Assumption tracingAssumption = tracingCyclicAssumption.getAssumption();
9596

9697
public final CoreStrings coreStrings;
98+
public final CoreSymbols coreSymbols;
9799
public final RopeCache ropeCache;
98100
public final SymbolTable symbolTable;
99101

@@ -103,8 +105,9 @@ public class RubyLanguage extends TruffleLanguage<RubyContext> {
103105

104106
public RubyLanguage() {
105107
coreStrings = new CoreStrings(this);
106-
ropeCache = new RopeCache();
107-
symbolTable = new SymbolTable(ropeCache);
108+
coreSymbols = new CoreSymbols();
109+
ropeCache = new RopeCache(coreSymbols);
110+
symbolTable = new SymbolTable(ropeCache, coreSymbols);
108111
}
109112

110113
@TruffleBoundary

src/main/java/org/truffleruby/cext/IDToSymbolNode.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import static org.truffleruby.core.symbol.CoreSymbols.idToIndex;
1313

14+
import com.oracle.truffle.api.dsl.CachedLanguage;
1415
import org.truffleruby.RubyContext;
1516
import org.truffleruby.RubyLanguage;
1617
import org.truffleruby.core.string.StringUtils;
@@ -38,10 +39,11 @@ public static IDToSymbolNode create() {
3839

3940
@Specialization(guards = "isStaticSymbol(value)")
4041
protected Object unwrapStaticUncached(long value,
42+
@CachedLanguage RubyLanguage language,
4143
@CachedContext(RubyLanguage.class) RubyContext context,
4244
@Cached BranchProfile errorProfile) {
4345
final int index = idToIndex(value);
44-
final RubySymbol symbol = CoreSymbols.STATIC_SYMBOLS[index];
46+
final RubySymbol symbol = language.coreSymbols.STATIC_SYMBOLS[index];
4547
if (symbol == null) {
4648
errorProfile.enter();
4749
throw new RaiseException(

src/main/java/org/truffleruby/core/cast/SplatCastNode.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
*/
1010
package org.truffleruby.core.cast;
1111

12+
import org.truffleruby.RubyLanguage;
1213
import org.truffleruby.core.array.ArrayDupNode;
1314
import org.truffleruby.core.array.ArrayDupNodeGen;
1415
import org.truffleruby.core.array.ArrayHelpers;
1516
import org.truffleruby.core.array.RubyArray;
16-
import org.truffleruby.core.symbol.CoreSymbols;
1717
import org.truffleruby.core.symbol.RubySymbol;
1818
import org.truffleruby.language.Nil;
1919
import org.truffleruby.language.RubyContextSourceNode;
@@ -47,10 +47,10 @@ public enum NilBehavior {
4747
@Child private ArrayDupNode dup;
4848
@Child private DispatchNode toA;
4949

50-
public SplatCastNode(NilBehavior nilBehavior, boolean useToAry) {
50+
public SplatCastNode(RubyLanguage language, NilBehavior nilBehavior, boolean useToAry) {
5151
this.nilBehavior = nilBehavior;
5252
// Calling private #to_a is allowed for the *splat operator.
53-
conversionMethod = useToAry ? CoreSymbols.TO_ARY : CoreSymbols.TO_A;
53+
conversionMethod = useToAry ? language.coreSymbols.TO_ARY : language.coreSymbols.TO_A;
5454
}
5555

5656
public void doNotCopy() {

src/main/java/org/truffleruby/core/cast/ToProcNode.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@
99
*/
1010
package org.truffleruby.core.cast;
1111

12+
import org.truffleruby.core.module.RubyModule;
1213
import org.truffleruby.core.proc.RubyProc;
14+
import org.truffleruby.core.symbol.RubySymbol;
15+
import org.truffleruby.core.symbol.SymbolNodes;
1316
import org.truffleruby.language.Nil;
1417
import org.truffleruby.language.RubyContextSourceNode;
1518
import org.truffleruby.language.RubyNode;
19+
import org.truffleruby.language.arguments.RubyArguments;
1620
import org.truffleruby.language.control.RaiseException;
1721
import org.truffleruby.language.dispatch.DispatchNode;
1822

@@ -22,7 +26,9 @@
2226
import com.oracle.truffle.api.frame.VirtualFrame;
2327
import com.oracle.truffle.api.profiles.BranchProfile;
2428

25-
/** Casts an object to a Ruby Proc object. */
29+
import java.util.Map;
30+
31+
/** The `&` in `foo(&block)`. Converts the passed block to a RubyProc or nil. */
2632
@NodeChild(value = "child", type = RubyNode.class)
2733
public abstract class ToProcNode extends RubyContextSourceNode {
2834

@@ -36,10 +42,23 @@ protected RubyProc doRubyProc(RubyProc proc) {
3642
return proc;
3743
}
3844

39-
@Specialization(guards = { "!isNil(object)", "!isRubyProc(object)" })
45+
// AST-inlined version of Symbol#to_proc
46+
// No need to guard the refinements here since refinements are always the same in a given source location
47+
@Specialization(
48+
guards = "symbol == cachedSymbol",
49+
assumptions = "getContext().getCoreMethods().symbolToProcAssumption",
50+
limit = "1")
51+
protected Object doRubySymbolASTInlined(VirtualFrame frame, RubySymbol symbol,
52+
@Cached("symbol") RubySymbol cachedSymbol,
53+
@Cached("getProcForSymbol(getRefinements(frame), cachedSymbol)") RubyProc cachedProc) {
54+
return cachedProc;
55+
}
56+
57+
@Specialization(guards = { "!isNil(object)", "!isRubyProc(object)" }, replaces = "doRubySymbolASTInlined")
4058
protected RubyProc doObject(VirtualFrame frame, Object object,
4159
@Cached DispatchNode toProc,
4260
@Cached BranchProfile errorProfile) {
61+
// The semantics are to call #method_missing here
4362
final Object coerced;
4463
try {
4564
coerced = toProc.dispatch(frame, object, "to_proc", null, EMPTY_ARGUMENTS);
@@ -64,4 +83,12 @@ protected RubyProc doObject(VirtualFrame frame, Object object,
6483
}
6584
}
6685

86+
protected RubyProc getProcForSymbol(Map<RubyModule, RubyModule[]> refinements, RubySymbol symbol) {
87+
return SymbolNodes.ToProcNode.getOrCreateProc(getContext(), refinements, symbol);
88+
}
89+
90+
protected static Map<RubyModule, RubyModule[]> getRefinements(VirtualFrame frame) {
91+
return RubyArguments.getDeclarationContext(frame).getRefinements();
92+
}
93+
6794
}

src/main/java/org/truffleruby/core/hash/HashNodes.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
import java.util.Arrays;
1313

14+
import com.oracle.truffle.api.dsl.CachedLanguage;
15+
import org.truffleruby.RubyLanguage;
1416
import org.truffleruby.builtins.CoreMethod;
1517
import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
1618
import org.truffleruby.builtins.CoreModule;
@@ -29,7 +31,6 @@
2931
import org.truffleruby.core.hash.HashNodesFactory.InternalRehashNodeGen;
3032
import org.truffleruby.core.klass.RubyClass;
3133
import org.truffleruby.core.proc.RubyProc;
32-
import org.truffleruby.core.symbol.CoreSymbols;
3334
import org.truffleruby.language.Nil;
3435
import org.truffleruby.language.NotProvided;
3536
import org.truffleruby.language.RubyContextNode;
@@ -639,14 +640,15 @@ protected RubyHash replaceBuckets(RubyHash self, RubyHash from) {
639640

640641
@Specialization(guards = "!isRubyHash(from)")
641642
protected RubyHash replaceCoerce(RubyHash self, Object from,
643+
@CachedLanguage RubyLanguage language,
642644
@Cached DispatchNode coerceNode,
643645
@Cached InitializeCopyNode initializeCopyNode) {
644646
final Object otherHash = coerceNode.call(
645647
coreLibrary().truffleTypeModule,
646648
"coerce_to",
647649
from,
648650
coreLibrary().hashClass,
649-
CoreSymbols.TO_HASH);
651+
language.coreSymbols.TO_HASH);
650652
return initializeCopyNode.executeReplace(self, (RubyHash) otherHash);
651653
}
652654

src/main/java/org/truffleruby/core/inlined/CoreMethods.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ public class CoreMethods {
7474

7575
final Assumption nilClassIsNilAssumption;
7676

77+
public final Assumption symbolToProcAssumption;
78+
7779
public final InternalMethod EXCEPTION_BACKTRACE;
7880
public final InternalMethod BLOCK_GIVEN;
7981
public final InternalMethod LAMBDA;
@@ -85,6 +87,7 @@ public class CoreMethods {
8587
public final InternalMethod STRING_BYTESIZE;
8688
public final InternalMethod MODULE_CASE_EQUAL;
8789
public final InternalMethod STRING_EQUAL;
90+
public final InternalMethod SYMBOL_TO_PROC;
8891

8992
public CoreMethods(RubyContext context) {
9093
this.context = context;
@@ -96,6 +99,7 @@ public CoreMethods(RubyContext context) {
9699
final RubyClass moduleClass = context.getCoreLibrary().moduleClass;
97100
final RubyClass nilClass = context.getCoreLibrary().nilClass;
98101
final RubyClass stringClass = context.getCoreLibrary().stringClass;
102+
final RubyClass symbolClass = context.getCoreLibrary().symbolClass;
99103

100104
integerNegAssumption = registerAssumption(integerClass, "-@");
101105
floatNegAssumption = registerAssumption(floatClass, "-@");
@@ -132,6 +136,8 @@ public CoreMethods(RubyContext context) {
132136

133137
nilClassIsNilAssumption = registerAssumption(nilClass, "nil?");
134138

139+
symbolToProcAssumption = registerAssumption(symbolClass, "to_proc");
140+
135141
BLOCK_GIVEN = getMethod(kernelModule, "block_given?");
136142
LAMBDA = getMethod(kernelModule, "lambda");
137143
BINDING = getMethod(kernelModule, "binding");
@@ -143,6 +149,7 @@ public CoreMethods(RubyContext context) {
143149
KERNEL_KIND_OF = getMethod(kernelModule, "kind_of?");
144150
MODULE_CASE_EQUAL = getMethod(moduleClass, "===");
145151
STRING_EQUAL = getMethod(stringClass, "==");
152+
SYMBOL_TO_PROC = getMethod(symbolClass, "to_proc");
146153
}
147154

148155
private Assumption registerAssumption(RubyClass klass, String methodName) {

src/main/java/org/truffleruby/core/numeric/FloatNodes.java

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.oracle.truffle.api.CompilerDirectives;
1313
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
1414
import com.oracle.truffle.api.dsl.Cached;
15+
import com.oracle.truffle.api.dsl.CachedLanguage;
1516
import com.oracle.truffle.api.dsl.ImportStatic;
1617
import com.oracle.truffle.api.dsl.Specialization;
1718
import com.oracle.truffle.api.frame.VirtualFrame;
@@ -20,6 +21,7 @@
2021
import com.oracle.truffle.api.profiles.ConditionProfile;
2122
import org.jcodings.specific.USASCIIEncoding;
2223
import org.jcodings.specific.UTF8Encoding;
24+
import org.truffleruby.RubyLanguage;
2325
import org.truffleruby.SuppressFBWarnings;
2426
import org.truffleruby.builtins.CoreMethod;
2527
import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
@@ -33,7 +35,6 @@
3335
import org.truffleruby.core.string.RubyString;
3436
import org.truffleruby.core.string.StringNodes;
3537
import org.truffleruby.core.string.StringUtils;
36-
import org.truffleruby.core.symbol.CoreSymbols;
3738
import org.truffleruby.language.Nil;
3839
import org.truffleruby.language.RubyDynamicObject;
3940
import org.truffleruby.language.Visibility;
@@ -76,8 +77,9 @@ protected double add(double a, RubyBignum b) {
7677

7778
@Specialization(guards = "!isRubyNumber(b)")
7879
protected Object addCoerced(double a, Object b,
79-
@Cached DispatchNode redoCoerced) {
80-
return redoCoerced.call(a, "redo_coerced", CoreSymbols.PLUS, b);
80+
@Cached DispatchNode redoCoerced,
81+
@CachedLanguage RubyLanguage language) {
82+
return redoCoerced.call(a, "redo_coerced", language.coreSymbols.PLUS, b);
8183
}
8284
}
8385

@@ -101,8 +103,9 @@ protected double sub(double a, RubyBignum b) {
101103

102104
@Specialization(guards = "!isRubyNumber(b)")
103105
protected Object subCoerced(double a, Object b,
104-
@Cached DispatchNode redoCoerced) {
105-
return redoCoerced.call(a, "redo_coerced", CoreSymbols.MINUS, b);
106+
@Cached DispatchNode redoCoerced,
107+
@CachedLanguage RubyLanguage language) {
108+
return redoCoerced.call(a, "redo_coerced", language.coreSymbols.MINUS, b);
106109
}
107110

108111
}
@@ -127,8 +130,9 @@ protected double mul(double a, RubyBignum b) {
127130

128131
@Specialization(guards = "!isRubyNumber(b)")
129132
protected Object mulCoerced(double a, Object b,
130-
@Cached DispatchNode redoCoerced) {
131-
return redoCoerced.call(a, "redo_coerced", CoreSymbols.MULTIPLY, b);
133+
@Cached DispatchNode redoCoerced,
134+
@CachedLanguage RubyLanguage language) {
135+
return redoCoerced.call(a, "redo_coerced", language.coreSymbols.MULTIPLY, b);
132136
}
133137

134138
}
@@ -183,8 +187,9 @@ protected double pow(double base, RubyBignum exponent) {
183187

184188
@Specialization(guards = "!isRubyNumber(exponent)")
185189
protected Object powCoerced(double base, Object exponent,
186-
@Cached DispatchNode redoCoerced) {
187-
return redoCoerced.call(base, "redo_coerced", CoreSymbols.POW, exponent);
190+
@Cached DispatchNode redoCoerced,
191+
@CachedLanguage RubyLanguage language) {
192+
return redoCoerced.call(base, "redo_coerced", language.coreSymbols.POW, exponent);
188193
}
189194

190195
}
@@ -209,8 +214,9 @@ protected double div(double a, RubyBignum b) {
209214

210215
@Specialization(guards = "!isRubyNumber(b)")
211216
protected Object divCoerced(double a, Object b,
212-
@Cached DispatchNode redoCoerced) {
213-
return redoCoerced.call(a, "redo_coerced", CoreSymbols.DIVIDE, b);
217+
@Cached DispatchNode redoCoerced,
218+
@CachedLanguage RubyLanguage language) {
219+
return redoCoerced.call(a, "redo_coerced", language.coreSymbols.DIVIDE, b);
214220
}
215221

216222
}
@@ -255,8 +261,9 @@ protected double mod(double a, RubyBignum b) {
255261

256262
@Specialization(guards = "!isRubyNumber(b)")
257263
protected Object modCoerced(double a, Object b,
258-
@Cached DispatchNode redoCoerced) {
259-
return redoCoerced.call(a, "redo_coerced", CoreSymbols.MODULO, b);
264+
@Cached DispatchNode redoCoerced,
265+
@CachedLanguage RubyLanguage language) {
266+
return redoCoerced.call(a, "redo_coerced", language.coreSymbols.MODULO, b);
260267
}
261268

262269
}
@@ -283,8 +290,9 @@ protected RubyArray divMod(double a, RubyBignum b) {
283290

284291
@Specialization(guards = "!isRubyBignum(b)")
285292
protected Object divModCoerced(double a, RubyDynamicObject b,
286-
@Cached DispatchNode redoCoerced) {
287-
return redoCoerced.call(a, "redo_coerced", CoreSymbols.DIVMOD, b);
293+
@Cached DispatchNode redoCoerced,
294+
@CachedLanguage RubyLanguage language) {
295+
return redoCoerced.call(a, "redo_coerced", language.coreSymbols.DIVMOD, b);
288296
}
289297

290298
}
@@ -309,8 +317,9 @@ protected boolean lessBignum(double a, RubyBignum b) {
309317

310318
@Specialization(guards = "!isRubyNumber(b)")
311319
protected Object lessCoerced(double a, Object b,
312-
@Cached DispatchNode redoCompare) {
313-
return redoCompare.call(a, "redo_compare", CoreSymbols.LESS_THAN, b);
320+
@Cached DispatchNode redoCompare,
321+
@CachedLanguage RubyLanguage language) {
322+
return redoCompare.call(a, "redo_compare", language.coreSymbols.LESS_THAN, b);
314323
}
315324
}
316325

@@ -334,8 +343,9 @@ protected boolean lessEqual(double a, RubyBignum b) {
334343

335344
@Specialization(guards = "!isRubyNumber(b)")
336345
protected Object lessEqualCoerced(double a, Object b,
337-
@Cached DispatchNode redoCompare) {
338-
return redoCompare.call(a, "redo_compare", CoreSymbols.LEQ, b);
346+
@Cached DispatchNode redoCompare,
347+
@CachedLanguage RubyLanguage language) {
348+
return redoCompare.call(a, "redo_compare", language.coreSymbols.LEQ, b);
339349
}
340350
}
341351

@@ -451,8 +461,9 @@ protected boolean greaterEqual(double a, RubyBignum b) {
451461

452462
@Specialization(guards = "!isRubyNumber(b)")
453463
protected Object greaterEqualCoerced(double a, Object b,
454-
@Cached DispatchNode redoCompare) {
455-
return redoCompare.call(a, "redo_compare", CoreSymbols.GEQ, b);
464+
@Cached DispatchNode redoCompare,
465+
@CachedLanguage RubyLanguage language) {
466+
return redoCompare.call(a, "redo_compare", language.coreSymbols.GEQ, b);
456467
}
457468

458469
}
@@ -477,8 +488,9 @@ protected boolean greater(double a, RubyBignum b) {
477488

478489
@Specialization(guards = "!isRubyNumber(b)")
479490
protected Object greaterCoerced(double a, Object b,
480-
@Cached DispatchNode redoCompare) {
481-
return redoCompare.call(a, "redo_compare", CoreSymbols.GREATER_THAN, b);
491+
@Cached DispatchNode redoCompare,
492+
@CachedLanguage RubyLanguage language) {
493+
return redoCompare.call(a, "redo_compare", language.coreSymbols.GREATER_THAN, b);
482494
}
483495
}
484496

0 commit comments

Comments
 (0)