Skip to content

Commit 50b598a

Browse files
committed
[GR-27251] AST inline new and dup.
PullRequest: truffleruby/2129
2 parents df6d1b8 + 567fecb commit 50b598a

File tree

12 files changed

+565
-82
lines changed

12 files changed

+565
-82
lines changed

src/main/java/org/truffleruby/core/basicobject/BasicObjectNodes.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717
import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
1818
import org.truffleruby.builtins.CoreModule;
1919
import org.truffleruby.builtins.UnaryCoreMethodNode;
20+
import org.truffleruby.core.basicobject.BasicObjectNodesFactory.AllocateNodeFactory;
21+
import org.truffleruby.core.basicobject.BasicObjectNodesFactory.InitializeNodeFactory;
2022
import org.truffleruby.core.basicobject.BasicObjectNodesFactory.InstanceExecNodeFactory;
2123
import org.truffleruby.core.basicobject.BasicObjectNodesFactory.ReferenceEqualNodeFactory;
2224
import org.truffleruby.core.cast.BooleanCastNode;
2325
import org.truffleruby.core.cast.NameToJavaStringNode;
2426
import org.truffleruby.core.exception.ExceptionOperations;
2527
import org.truffleruby.core.exception.RubyException;
28+
import org.truffleruby.core.inlined.InlinedMethodNode;
2629
import org.truffleruby.core.klass.RubyClass;
2730
import org.truffleruby.core.module.ModuleOperations;
2831
import org.truffleruby.core.numeric.RubyBignum;
@@ -318,13 +321,28 @@ protected int getCacheLimit() {
318321
}
319322

320323
@CoreMethod(names = "initialize", needsSelf = false)
321-
public abstract static class InitializeNode extends CoreMethodArrayArgumentsNode {
324+
public abstract static class InitializeNode extends InlinedMethodNode {
325+
326+
public static InitializeNode create() {
327+
return InitializeNodeFactory.create(null);
328+
}
329+
330+
public abstract Object execute();
322331

323332
@Specialization
324333
protected Object initialize() {
325334
return nil;
326335
}
327336

337+
@Override
338+
public InternalMethod getMethod() {
339+
return getContext().getCoreMethods().BASIC_OBJECT_INITIALIZE;
340+
}
341+
342+
@Override
343+
public Object inlineExecute(VirtualFrame frame, Object self, Object[] args, Object proc) {
344+
return execute();
345+
}
328346
}
329347

330348
@CoreMethod(
@@ -628,7 +646,13 @@ protected Object send(VirtualFrame frame, Object self, Object name, Object[] arg
628646
// chain. We use a normal Ruby method, different that Class#allocate as Class#allocate
629647
// must be able to instantiate any Ruby object and should not be overridden.
630648
@CoreMethod(names = { "__allocate__", "__layout_allocate__" }, constructor = true, visibility = Visibility.PRIVATE)
631-
public abstract static class AllocateNode extends CoreMethodArrayArgumentsNode {
649+
public abstract static class AllocateNode extends InlinedMethodNode {
650+
651+
public static AllocateNode create() {
652+
return AllocateNodeFactory.create(null);
653+
}
654+
655+
public abstract Object execute(VirtualFrame frame, Object rubyClass);
632656

633657
@Specialization
634658
protected RubyBasicObject allocate(RubyClass rubyClass,
@@ -639,6 +663,15 @@ protected RubyBasicObject allocate(RubyClass rubyClass,
639663
return instance;
640664
}
641665

666+
@Override
667+
public Object inlineExecute(VirtualFrame frame, Object self, Object[] args, Object proc) {
668+
return execute(frame, proc);
669+
}
670+
671+
@Override
672+
public InternalMethod getMethod() {
673+
return getContext().getCoreMethods().BASIC_OBJECT_ALLOCATE;
674+
}
642675
}
643676

644677
}

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

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,17 @@
99
*/
1010
package org.truffleruby.core.inlined;
1111

12+
import java.util.ArrayList;
13+
import java.util.List;
14+
import java.util.function.Consumer;
15+
1216
import com.oracle.truffle.api.Assumption;
1317
import com.oracle.truffle.api.Truffle;
18+
1419
import org.truffleruby.RubyLanguage;
1520
import org.truffleruby.core.CoreLibrary;
21+
import org.truffleruby.core.kernel.KernelNodes.DupNode;
22+
import org.truffleruby.core.klass.ClassNodes.NewNode;
1623
import org.truffleruby.core.klass.RubyClass;
1724
import org.truffleruby.core.module.ModuleFields;
1825
import org.truffleruby.language.RubyNode;
@@ -21,10 +28,6 @@
2128
import org.truffleruby.language.methods.BlockDefinitionNode;
2229
import org.truffleruby.parser.TranslatorEnvironment;
2330

24-
import java.util.ArrayList;
25-
import java.util.List;
26-
import java.util.function.Consumer;
27-
2831
/** We inline basic operations directly in the AST (instead of a method call) as it makes little sense to compile them
2932
* in isolation without the surrounding method and it delays more interesting compilations by filling the compilation
3033
* queue. The performance in interpreter is typically also improved, making inlined basic operations an optimization
@@ -150,6 +153,20 @@ public RubyNode createCallNode(RubyCallNodeParameters callParameters, Translator
150153
final RubyNode[] args = callParameters.getArguments();
151154
int n = 1 /* self */ + args.length;
152155

156+
if ("new".equals(callParameters.getMethodName())) {
157+
return new InlinedCallNode(
158+
language,
159+
NewNode.create(),
160+
callParameters);
161+
}
162+
163+
if ("dup".equals(callParameters.getMethodName())) {
164+
return new InlinedCallNode(
165+
language,
166+
DupNode.create(),
167+
callParameters);
168+
}
169+
153170
if (callParameters.getBlock() != null) {
154171
if (callParameters.getMethodName().equals("lambda") && callParameters.isIgnoreVisibility() &&
155172
n == 1 && callParameters.getBlock() instanceof BlockDefinitionNode) {

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ public class CoreMethods {
2222
public final InternalMethod LAMBDA;
2323
public final InternalMethod BINDING;
2424
public final InternalMethod NOT;
25+
public final InternalMethod KERNEL_DUP;
26+
public final InternalMethod KERNEL_INITIALIZE_DUP;
27+
public final InternalMethod KERNEL_INITIALIZE_COPY;
2528
public final InternalMethod KERNEL_IS_NIL;
2629
public final InternalMethod KERNEL_IS_A;
2730
public final InternalMethod KERNEL_KIND_OF;
@@ -32,6 +35,9 @@ public class CoreMethods {
3235
public final InternalMethod ARRAY_AT;
3336
public final InternalMethod ARRAY_INDEX_GET;
3437
public final InternalMethod ARRAY_INDEX_SET;
38+
public final InternalMethod CLASS_NEW;
39+
public final InternalMethod BASIC_OBJECT_ALLOCATE;
40+
public final InternalMethod BASIC_OBJECT_INITIALIZE;
3541

3642
public CoreMethods(RubyLanguage language, RubyContext context) {
3743
final RubyClass basicObjectClass = context.getCoreLibrary().basicObjectClass;
@@ -41,12 +47,16 @@ public CoreMethods(RubyLanguage language, RubyContext context) {
4147
final RubyClass stringClass = context.getCoreLibrary().stringClass;
4248
final RubyClass symbolClass = context.getCoreLibrary().symbolClass;
4349
final RubyClass arrayClass = context.getCoreLibrary().arrayClass;
50+
final RubyClass classClass = context.getCoreLibrary().classClass;
4451

4552
BLOCK_GIVEN = getMethod(kernelModule, "block_given?");
4653
LAMBDA = getMethod(kernelModule, "lambda");
4754
BINDING = getMethod(kernelModule, "binding");
4855
NOT = getMethod(basicObjectClass, "!");
4956
EXCEPTION_BACKTRACE = getMethod(exceptionClass, "backtrace");
57+
KERNEL_DUP = getMethod(kernelModule, "dup");
58+
KERNEL_INITIALIZE_DUP = getMethod(kernelModule, "initialize_dup");
59+
KERNEL_INITIALIZE_COPY = getMethod(kernelModule, "initialize_copy");
5060
KERNEL_IS_NIL = getMethod(kernelModule, "nil?");
5161
STRING_BYTESIZE = getMethod(stringClass, "bytesize");
5262
KERNEL_IS_A = getMethod(kernelModule, "is_a?");
@@ -57,6 +67,9 @@ public CoreMethods(RubyLanguage language, RubyContext context) {
5767
ARRAY_AT = getMethod(arrayClass, "at");
5868
ARRAY_INDEX_GET = getMethod(arrayClass, "[]");
5969
ARRAY_INDEX_SET = getMethod(arrayClass, "[]=");
70+
CLASS_NEW = getMethod(classClass, "new");
71+
BASIC_OBJECT_ALLOCATE = getMethod(basicObjectClass.getMetaClass(), "__allocate__");
72+
BASIC_OBJECT_INITIALIZE = getMethod(basicObjectClass, "initialize");
6073

6174
language.coreMethodAssumptions.registerAssumptions(context.getCoreLibrary());
6275
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. This
3+
* code is released under a tri EPL/GPL/LGPL license. You can use it,
4+
* redistribute it and/or modify it under the terms of the:
5+
*
6+
* Eclipse Public License version 2.0, or
7+
* GNU General Public License version 2, or
8+
* GNU Lesser General Public License version 2.1.
9+
*/
10+
package org.truffleruby.core.inlined;
11+
12+
import com.oracle.truffle.api.Assumption;
13+
import com.oracle.truffle.api.CompilerDirectives;
14+
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
15+
import com.oracle.truffle.api.frame.VirtualFrame;
16+
import com.oracle.truffle.api.nodes.ExplodeLoop;
17+
18+
import org.truffleruby.RubyLanguage;
19+
import org.truffleruby.core.cast.ProcOrNullNode;
20+
import org.truffleruby.core.cast.ProcOrNullNodeGen;
21+
import org.truffleruby.core.proc.RubyProc;
22+
import org.truffleruby.language.NotProvided;
23+
import org.truffleruby.language.RubyNode;
24+
import org.truffleruby.language.dispatch.RubyCallNodeParameters;
25+
import org.truffleruby.language.methods.InternalMethod;
26+
import org.truffleruby.language.methods.LookupMethodOnSelfNode;
27+
28+
public class InlinedCallNode extends InlinedReplaceableNode {
29+
private final String methodName;
30+
@CompilationFinal private InternalMethod coreMethod;
31+
32+
@Child private RubyNode receiver;
33+
@Child private ProcOrNullNode block;
34+
@Children private final RubyNode[] arguments;
35+
36+
@Child private LookupMethodOnSelfNode lookupNode;
37+
38+
@Child private InlinedMethodNode inlinedMethod;
39+
40+
public InlinedCallNode(
41+
RubyLanguage language,
42+
InlinedMethodNode inlinedMethod,
43+
RubyCallNodeParameters parameters,
44+
Assumption... assumptions) {
45+
super(language, parameters, assumptions);
46+
47+
this.methodName = parameters.getMethodName();
48+
this.receiver = parameters.getReceiver();
49+
this.arguments = parameters.getArguments();
50+
51+
if (parameters.getBlock() == null) {
52+
this.block = null;
53+
} else {
54+
this.block = ProcOrNullNodeGen.create(parameters.getBlock());
55+
}
56+
57+
lookupNode = LookupMethodOnSelfNode.create();
58+
59+
this.inlinedMethod = inlinedMethod;
60+
}
61+
62+
63+
@Override
64+
public Object execute(VirtualFrame frame) {
65+
final Object receiverObject = receiver.execute(frame);
66+
67+
final Object[] executedArguments = executeArguments(frame);
68+
69+
final RubyProc blockObject = executeBlock(frame);
70+
71+
// The expansion of the splat is done after executing the block, for m(*args, &args.pop)
72+
73+
if ((lookupNode.lookupProtected(frame, receiverObject, methodName) != coreMethod()) ||
74+
!Assumption.isValidAssumption(assumptions)) {
75+
return rewriteAndCallWithBlock(frame, receiverObject, blockObject, executedArguments);
76+
} else {
77+
return executeWithArgumentsEvaluated(frame, receiverObject, blockObject, executedArguments);
78+
}
79+
}
80+
81+
public Object executeWithArgumentsEvaluated(VirtualFrame frame, Object receiverObject, RubyProc blockObject,
82+
Object[] argumentsObjects) {
83+
return inlinedMethod.inlineExecute(
84+
frame,
85+
receiverObject,
86+
argumentsObjects,
87+
blockObject == null ? NotProvided.INSTANCE : blockObject);
88+
}
89+
90+
private RubyProc executeBlock(VirtualFrame frame) {
91+
if (block != null) {
92+
return block.executeProcOrNull(frame);
93+
} else {
94+
return null;
95+
}
96+
}
97+
98+
@ExplodeLoop
99+
private Object[] executeArguments(VirtualFrame frame) {
100+
final Object[] argumentsObjects = new Object[arguments.length];
101+
102+
for (int i = 0; i < arguments.length; i++) {
103+
argumentsObjects[i] = arguments[i].execute(frame);
104+
}
105+
106+
return argumentsObjects;
107+
}
108+
109+
protected Object rewriteAndCallWithBlock(VirtualFrame frame, Object receiver, RubyProc block,
110+
Object... arguments) {
111+
return rewriteToCallNode().executeWithArgumentsEvaluated(frame, receiver, block, arguments);
112+
}
113+
114+
protected InternalMethod coreMethod() {
115+
if (coreMethod == null) {
116+
CompilerDirectives.transferToInterpreterAndInvalidate();
117+
coreMethod = inlinedMethod.getMethod();
118+
}
119+
return coreMethod;
120+
}
121+
122+
@Override
123+
protected RubyNode getReceiverNode() {
124+
return receiver;
125+
}
126+
127+
@Override
128+
protected RubyNode[] getArgumentNodes() {
129+
return arguments;
130+
}
131+
}

0 commit comments

Comments
 (0)