Skip to content

Commit b6c91ec

Browse files
committed
[GR-28932] Always inline generated accessors, Module#attr_*, visibility methods and #using.
PullRequest: truffleruby/2432
2 parents 634c0ae + e71b4ee commit b6c91ec

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+656
-751
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ Performance:
6262
* Improve random number generation performance by avoiding synchronization (#2190, @ivoanjo).
6363
* We now create a single call target per block by default instead of two.
6464
* Some uses of class variables are now much better optimized (#2259, @chrisseaton).
65+
* Several methods that need the caller frame are now always inlined in their caller, which speeds up the interpreter and reduces footprint.
6566

6667
Changes:
6768

spec/truffle/always_inlined_spec.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,37 @@
3838
__send__(Object.new)
3939
}.should raise_error(TypeError) { |e| e.backtrace_locations[0].label.should == '__send__' }
4040
end
41+
42+
it "for a generated attr_reader" do
43+
obj = Class.new { attr_reader :foo }.new
44+
-> {
45+
obj.foo(:too, :many, :args)
46+
}.should raise_error(ArgumentError) { |e| e.backtrace_locations[0].label.should == 'foo' }
47+
end
48+
49+
it "for a generated attr_writer" do
50+
obj = Class.new do
51+
attr_writer :foo
52+
alias_method :writer, :foo= # so it can be called without send and a different number of arguments
53+
end.new
54+
-> {
55+
obj.send(:foo=, :too, :many, :args)
56+
}.should raise_error(ArgumentError) { |e| e.backtrace_locations[0].label.should == 'foo=' }
57+
-> {
58+
obj.writer(:too, :many, :args)
59+
}.should raise_error(ArgumentError) { |e| e.backtrace_locations[0].label.should == 'foo=' }
60+
61+
obj.freeze
62+
-> {
63+
obj.foo = 42
64+
}.should raise_error(FrozenError) { |e| e.backtrace_locations[0].label.should == 'foo=' }
65+
end
66+
end
67+
68+
it "for main.using" do
69+
-> do
70+
eval('using "foo"', TOPLEVEL_BINDING)
71+
end.should raise_error(TypeError) { |e| e.backtrace_locations[0].label.should == 'using' }
4172
end
4273
end
4374

@@ -76,4 +107,18 @@
76107
}
77108
end
78109
end
110+
111+
it "go uncached if seeing too many different always-inlined methods at a call site" do
112+
names = (1..10).map { |i| :"attr#{i}" }
113+
obj = Class.new { attr_reader(*names) }.new
114+
names.each { |name| obj.send(name).should == nil }
115+
end
116+
117+
it "work with each(&method(:always_inlined_method))" do
118+
obj = Class.new do
119+
[:foo].each(&method(:attr_accessor))
120+
end.new
121+
obj.foo = 42
122+
obj.foo.should == 42
123+
end
79124
end

src/main/java/org/truffleruby/builtins/CoreMethodNodeManager.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,12 @@ public void addLazyCoreMethod(
177177

178178
final RubyModule module = getModule(moduleName, isClass);
179179
final Arity arity = createArity(required, optional, rest, keywordAsOptional);
180-
final Split finalSplit = context.getOptions().CORE_ALWAYS_CLONE ? Split.ALWAYS : split;
180+
final Split finalSplit;
181+
if (alwaysInlined) {
182+
finalSplit = Split.NEVER;
183+
} else {
184+
finalSplit = context.getOptions().CORE_ALWAYS_CLONE ? Split.ALWAYS : split;
185+
}
181186

182187
final Function<SharedMethodInfo, RootCallTarget> callTargetFactory = sharedMethodInfo -> {
183188
final NodeFactory<? extends RubyBaseNode> nodeFactory = loadNodeFactory(nodeFactoryName);
@@ -264,7 +269,7 @@ private static void addMethod(
264269
if (alwaysInlined) {
265270
callTarget = callTargetFactory.apply(sharedMethodInfo);
266271
callTargetSupplier = null;
267-
final RubyRootNode rootNode = (RubyRootNode) callTarget.getRootNode();
272+
final RubyRootNode rootNode = RubyRootNode.of(callTarget);
268273
alwaysInlinedNodeFactory = ((ReRaiseInlinedExceptionNode) rootNode.getBody()).nodeFactory;
269274
} else {
270275
if (context.getLanguageSlow().options.LAZY_CALLTARGETS) {

src/main/java/org/truffleruby/builtins/ReRaiseInlinedExceptionNode.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import com.oracle.truffle.api.CompilerDirectives;
2020
import com.oracle.truffle.api.frame.VirtualFrame;
2121

22+
import java.util.Arrays;
23+
2224
public class ReRaiseInlinedExceptionNode extends RubyContextSourceNode {
2325

2426
public final NodeFactory<? extends RubyBaseNode> nodeFactory;
@@ -31,8 +33,10 @@ public ReRaiseInlinedExceptionNode(NodeFactory<? extends RubyBaseNode> nodeFacto
3133
public Object execute(VirtualFrame frame) {
3234
final Object[] arguments = frame.getArguments();
3335
if (arguments.length != 1 || !(arguments[0] instanceof RaiseException)) {
36+
CompilerDirectives.transferToInterpreterAndInvalidate();
3437
throw CompilerDirectives.shouldNotReachHere(
35-
"CallTarget of always-inlined builtin should be called with a single RaiseException argument");
38+
"CallTarget of always-inlined builtin should be called with a single RaiseException argument" +
39+
", but was called with: " + Arrays.toString(arguments));
3640
}
3741

3842
final RaiseException raiseException = (RaiseException) arguments[0];

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

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@
4040
import org.truffleruby.core.klass.RubyClass;
4141
import org.truffleruby.core.module.MethodLookupResult;
4242
import org.truffleruby.core.module.ModuleNodes.ConstSetUncheckedNode;
43-
import org.truffleruby.core.module.ModuleNodes.SetVisibilityNode;
44-
import org.truffleruby.core.module.ModuleNodesFactory.SetVisibilityNodeGen;
43+
import org.truffleruby.core.module.ModuleNodes.SetMethodVisibilityNode;
4544
import org.truffleruby.core.module.ModuleOperations;
4645
import org.truffleruby.core.module.RubyModule;
4746
import org.truffleruby.core.mutex.MutexOperations;
@@ -268,25 +267,15 @@ public abstract static class IntegerBytesNode extends CoreMethodArrayArgumentsNo
268267
@Specialization
269268
@TruffleBoundary
270269
protected RubyArray bytes(
271-
int num,
272-
int num_words,
273-
int word_length,
274-
boolean msw_first,
275-
boolean twosComp,
276-
boolean bigEndian) {
270+
int num, int num_words, int word_length, boolean msw_first, boolean twosComp, boolean bigEndian) {
277271
BigInteger bi = BigInteger.valueOf(num);
278272
return bytes(bi, num_words, word_length, msw_first, twosComp, bigEndian);
279273
}
280274

281275
@Specialization
282276
@TruffleBoundary
283277
protected RubyArray bytes(
284-
long num,
285-
int num_words,
286-
int word_length,
287-
boolean msw_first,
288-
boolean twosComp,
289-
boolean bigEndian) {
278+
long num, int num_words, int word_length, boolean msw_first, boolean twosComp, boolean bigEndian) {
290279
BigInteger bi = BigInteger.valueOf(num);
291280
return bytes(bi, num_words, word_length, msw_first, twosComp, bigEndian);
292281
}
@@ -727,11 +716,12 @@ protected Object rbConstSet(RubyModule module, String name, Object value,
727716
@CoreMethod(names = "cext_module_function", onSingleton = true, required = 2)
728717
public abstract static class CextModuleFunctionNode extends CoreMethodArrayArgumentsNode {
729718

730-
@Child SetVisibilityNode setVisibilityNode = SetVisibilityNodeGen.create(Visibility.MODULE_FUNCTION);
719+
@Child SetMethodVisibilityNode setMethodVisibilityNode = SetMethodVisibilityNode.create();
731720

732721
@Specialization
733-
protected RubyModule cextModuleFunction(VirtualFrame frame, RubyModule module, RubySymbol name) {
734-
return setVisibilityNode.executeSetVisibility(frame, module, new Object[]{ name });
722+
protected RubyModule cextModuleFunction(RubyModule module, RubySymbol name) {
723+
setMethodVisibilityNode.execute(module, name, Visibility.MODULE_FUNCTION);
724+
return module;
735725
}
736726

737727
}

src/main/java/org/truffleruby/core/MainNodes.java

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

12+
import com.oracle.truffle.api.RootCallTarget;
13+
import com.oracle.truffle.api.dsl.CachedContext;
14+
import com.oracle.truffle.api.dsl.GenerateUncached;
15+
import org.truffleruby.RubyContext;
16+
import org.truffleruby.RubyLanguage;
1217
import org.truffleruby.builtins.CoreMethod;
13-
import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
1418
import org.truffleruby.builtins.CoreModule;
19+
import org.truffleruby.core.inlined.AlwaysInlinedMethodNode;
1520
import org.truffleruby.core.module.ModuleNodes;
16-
import org.truffleruby.core.module.ModuleNodesFactory;
17-
import org.truffleruby.core.module.RubyModule;
1821
import org.truffleruby.language.Visibility;
1922
import org.truffleruby.language.arguments.RubyArguments;
2023
import org.truffleruby.language.control.RaiseException;
24+
import org.truffleruby.language.methods.InternalMethod;
2125
import org.truffleruby.language.methods.UsingNode;
22-
import org.truffleruby.language.methods.UsingNodeGen;
2326

2427
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
2528
import com.oracle.truffle.api.dsl.Cached;
2629
import com.oracle.truffle.api.dsl.Specialization;
2730
import com.oracle.truffle.api.frame.Frame;
28-
import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
29-
import com.oracle.truffle.api.frame.VirtualFrame;
3031
import com.oracle.truffle.api.profiles.BranchProfile;
3132

3233
@CoreModule(value = "main", isClass = true)
3334
public abstract class MainNodes {
3435

35-
@CoreMethod(names = "public", rest = true, needsSelf = false, visibility = Visibility.PRIVATE)
36-
public abstract static class PublicNode extends CoreMethodArrayArgumentsNode {
37-
38-
@Child private ModuleNodes.PublicNode publicNode = ModuleNodesFactory.PublicNodeFactory.create(null);
39-
36+
@GenerateUncached
37+
@CoreMethod(names = "public", rest = true, visibility = Visibility.PRIVATE, alwaysInlined = true)
38+
public abstract static class PublicNode extends AlwaysInlinedMethodNode {
4039
@Specialization
41-
protected RubyModule doPublic(VirtualFrame frame, Object[] args) {
42-
return publicNode.executePublic(frame, coreLibrary().objectClass, args);
40+
protected Object forward(Frame callerFrame, Object self, Object[] args, Object block, RootCallTarget target,
41+
@Cached ModuleNodes.PublicNode publicNode,
42+
@CachedContext(RubyLanguage.class) RubyContext context) {
43+
return publicNode.execute(callerFrame, context.getCoreLibrary().objectClass, args, block, target);
4344
}
4445
}
4546

46-
@CoreMethod(names = "private", rest = true, needsSelf = false, visibility = Visibility.PRIVATE)
47-
public abstract static class PrivateNode extends CoreMethodArrayArgumentsNode {
48-
49-
@Child private ModuleNodes.PrivateNode privateNode = ModuleNodesFactory.PrivateNodeFactory.create(null);
50-
47+
@GenerateUncached
48+
@CoreMethod(names = "private", rest = true, visibility = Visibility.PRIVATE, alwaysInlined = true)
49+
public abstract static class PrivateNode extends AlwaysInlinedMethodNode {
5150
@Specialization
52-
protected RubyModule doPrivate(VirtualFrame frame, Object[] args) {
53-
return privateNode.executePrivate(frame, coreLibrary().objectClass, args);
51+
protected Object forward(Frame callerFrame, Object self, Object[] args, Object block, RootCallTarget target,
52+
@Cached ModuleNodes.PrivateNode privateNode,
53+
@CachedContext(RubyLanguage.class) RubyContext context) {
54+
return privateNode.execute(callerFrame, context.getCoreLibrary().objectClass, args, block, target);
5455
}
5556
}
5657

57-
@CoreMethod(names = "using", required = 1, needsSelf = false)
58-
public abstract static class MainUsingNode extends CoreMethodArrayArgumentsNode {
59-
60-
@Child private UsingNode usingNode = UsingNodeGen.create();
61-
58+
@GenerateUncached
59+
@CoreMethod(names = "using", required = 1, alwaysInlined = true)
60+
public abstract static class MainUsingNode extends UsingNode {
6261
@Specialization
63-
protected Object mainUsing(RubyModule refinementModule,
62+
protected Object mainUsing(Frame callerFrame, Object self, Object[] args, Object block, RootCallTarget target,
63+
@CachedContext(RubyLanguage.class) RubyContext context,
6464
@Cached BranchProfile errorProfile) {
65-
if (!isCalledFromTopLevel()) {
65+
final Object refinementModule = args[0];
66+
final InternalMethod callerMethod = RubyArguments.getMethod(callerFrame);
67+
if (!isCalledFromTopLevel(callerMethod)) {
6668
errorProfile.enter();
6769
throw new RaiseException(
68-
getContext(),
69-
coreExceptions().runtimeError("main.using is permitted only at toplevel", this));
70+
context,
71+
context.getCoreExceptions().runtimeError("main.using is permitted only at toplevel", this));
7072
}
71-
usingNode.executeUsing(refinementModule);
73+
using(context, callerFrame, refinementModule, errorProfile);
7274
return nil;
7375
}
7476

7577
@TruffleBoundary
76-
private boolean isCalledFromTopLevel() {
77-
final Frame callerFrame = getContext().getCallStack().getCallerFrame(FrameAccess.READ_ONLY);
78-
final String name = RubyArguments.getMethod(callerFrame).getSharedMethodInfo().getBacktraceName();
78+
private boolean isCalledFromTopLevel(InternalMethod callerMethod) {
79+
final String name = callerMethod.getSharedMethodInfo().getBacktraceName();
7980
return name.equals("<main>") || name.startsWith("<top ");
8081
}
81-
8282
}
8383

8484
}

src/main/java/org/truffleruby/core/array/ArrayNodes.java

Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,31 +1076,22 @@ protected RubyArray initializeNoArgs(RubyArray array, NotProvided size, NotProvi
10761076

10771077
@Specialization
10781078
protected RubyArray initializeOnlyBlock(
1079-
RubyArray array,
1080-
NotProvided size,
1081-
NotProvided fillingValue,
1082-
RubyProc block) {
1079+
RubyArray array, NotProvided size, NotProvided fillingValue, RubyProc block) {
10831080
setStoreAndSize(array, ArrayStoreLibrary.INITIAL_STORE, 0);
10841081
return array;
10851082
}
10861083

10871084
@TruffleBoundary
10881085
@Specialization(guards = "size < 0")
10891086
protected RubyArray initializeNegativeIntSize(
1090-
RubyArray array,
1091-
int size,
1092-
Object unusedFillingValue,
1093-
Object unusedBlock) {
1087+
RubyArray array, int size, Object unusedFillingValue, Object unusedBlock) {
10941088
throw new RaiseException(getContext(), coreExceptions().argumentError("negative array size", this));
10951089
}
10961090

10971091
@TruffleBoundary
10981092
@Specialization(guards = "size < 0")
10991093
protected RubyArray initializeNegativeLongSize(
1100-
RubyArray array,
1101-
long size,
1102-
Object unusedFillingValue,
1103-
Object unusedBlock) {
1094+
RubyArray array, long size, Object unusedFillingValue, Object unusedBlock) {
11041095
throw new RaiseException(getContext(), coreExceptions().argumentError("negative array size", this));
11051096
}
11061097

@@ -1175,10 +1166,7 @@ protected Object initializeBlock(RubyArray array, int size, Object unusedFilling
11751166

11761167
@Specialization
11771168
protected RubyArray initializeFromArray(
1178-
RubyArray array,
1179-
RubyArray copy,
1180-
NotProvided unusedValue,
1181-
Object maybeBlock,
1169+
RubyArray array, RubyArray copy, NotProvided unusedValue, Object maybeBlock,
11821170
@Cached ReplaceNode replaceNode) {
11831171
replaceNode.executeReplace(array, copy);
11841172
return array;
@@ -1271,10 +1259,7 @@ protected Object injectEmptyArray(RubyArray array, Object initialOrSymbol, NotPr
12711259
@Specialization(guards = { "isEmptyArray(array)" })
12721260
@ReportPolymorphism.Exclude
12731261
protected Object injectEmptyArrayNoInitial(
1274-
RubyArray array,
1275-
NotProvided initialOrSymbol,
1276-
NotProvided symbol,
1277-
RubyProc block) {
1262+
RubyArray array, NotProvided initialOrSymbol, NotProvided symbol, RubyProc block) {
12781263
return nil;
12791264
}
12801265

@@ -1294,10 +1279,7 @@ protected Object injectWithInitial(RubyArray array, Object initialOrSymbol, NotP
12941279
guards = { "!isEmptyArray(array)" },
12951280
limit = "storageStrategyLimit()")
12961281
protected Object injectNoInitial(
1297-
RubyArray array,
1298-
NotProvided initialOrSymbol,
1299-
NotProvided symbol,
1300-
RubyProc block,
1282+
RubyArray array, NotProvided initialOrSymbol, NotProvided symbol, RubyProc block,
13011283
@CachedLibrary("array.store") ArrayStoreLibrary stores,
13021284
@Cached("createCountingProfile()") LoopConditionProfile loopProfile) {
13031285
final Object store = array.store;
@@ -1333,10 +1315,7 @@ protected Object injectSymbolEmptyArray(RubyArray array, Object initialOrSymbol,
13331315

13341316
@Specialization(guards = { "isEmptyArray(array)" })
13351317
protected Object injectSymbolEmptyArrayNoInitial(
1336-
RubyArray array,
1337-
RubySymbol initialOrSymbol,
1338-
NotProvided symbol,
1339-
Nil block) {
1318+
RubyArray array, RubySymbol initialOrSymbol, NotProvided symbol, Nil block) {
13401319
return nil;
13411320
}
13421321

@@ -1346,11 +1325,7 @@ protected Object injectSymbolEmptyArrayNoInitial(
13461325
"wasProvided(initialOrSymbol)" },
13471326
limit = "storageStrategyLimit()")
13481327
protected Object injectSymbolWithInitial(
1349-
VirtualFrame frame,
1350-
RubyArray array,
1351-
Object initialOrSymbol,
1352-
RubySymbol symbol,
1353-
Nil block,
1328+
VirtualFrame frame, RubyArray array, Object initialOrSymbol, RubySymbol symbol, Nil block,
13541329
@CachedLibrary("array.store") ArrayStoreLibrary stores,
13551330
@Cached("createCountingProfile()") LoopConditionProfile loopProfile,
13561331
@Cached ToJavaStringNode toJavaString) {
@@ -1370,11 +1345,7 @@ protected Object injectSymbolWithInitial(
13701345
guards = { "!isEmptyArray(array)" },
13711346
limit = "storageStrategyLimit()")
13721347
protected Object injectSymbolNoInitial(
1373-
VirtualFrame frame,
1374-
RubyArray array,
1375-
RubySymbol initialOrSymbol,
1376-
NotProvided symbol,
1377-
Nil block,
1348+
VirtualFrame frame, RubyArray array, RubySymbol initialOrSymbol, NotProvided symbol, Nil block,
13781349
@CachedLibrary("array.store") ArrayStoreLibrary stores,
13791350
@Cached("createCountingProfile()") LoopConditionProfile loopProfile,
13801351
@Cached ToJavaStringNode toJavaString) {

src/main/java/org/truffleruby/core/array/library/DoubleArrayStore.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,7 @@ static class CopyContents {
110110

111111
@Specialization
112112
protected static void copyContents(
113-
double[] srcStore,
114-
int srcStart,
115-
double[] destStore,
116-
int destStart,
117-
int length) {
113+
double[] srcStore, int srcStart, double[] destStore, int destStart, int length) {
118114
System.arraycopy(srcStore, srcStart, destStore, destStart, length);
119115
}
120116

0 commit comments

Comments
 (0)