Skip to content

Commit 9c9d8d0

Browse files
committed
[GR-29207] Handle all cases of multiple assignment with a single node
PullRequest: truffleruby/2418
2 parents a2685ef + 41af197 commit 9c9d8d0

31 files changed

+475
-465
lines changed

ci.jsonnet

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ local part_definitions = {
301301

302302
test_unit_tck: {
303303
# Run unittests first before cleaning, they need a full non-cleaned build
304-
run+: jt(["test", "unit"]) +
304+
run+: jt(["test", "unit", "--verbose"]) +
305305
jt(["test", "tck"])
306306
},
307307

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ protected RubyArray cachedCreateArray(Object store, int size) {
6060
}
6161

6262
@Override
63-
public abstract Object execute(VirtualFrame frame);
63+
public abstract RubyArray execute(VirtualFrame frame);
6464

6565
@ExplodeLoop
6666
@Override
@@ -100,7 +100,7 @@ public EmptyArrayLiteralNode(RubyLanguage language, RubyNode[] values) {
100100
}
101101

102102
@Override
103-
public Object execute(VirtualFrame frame) {
103+
public RubyArray execute(VirtualFrame frame) {
104104
return cachedCreateArray(ArrayStoreLibrary.INITIAL_STORE, 0);
105105
}
106106

@@ -114,7 +114,7 @@ public FloatArrayLiteralNode(RubyLanguage language, RubyNode[] values) {
114114

115115
@ExplodeLoop
116116
@Override
117-
public Object execute(VirtualFrame frame) {
117+
public RubyArray execute(VirtualFrame frame) {
118118
final double[] executedValues = new double[values.length];
119119

120120
for (int n = 0; n < values.length; n++) {
@@ -151,7 +151,7 @@ public IntegerArrayLiteralNode(RubyLanguage language, RubyNode[] values) {
151151

152152
@ExplodeLoop
153153
@Override
154-
public Object execute(VirtualFrame frame) {
154+
public RubyArray execute(VirtualFrame frame) {
155155
final int[] executedValues = new int[values.length];
156156

157157
for (int n = 0; n < values.length; n++) {
@@ -188,7 +188,7 @@ public LongArrayLiteralNode(RubyLanguage language, RubyNode[] values) {
188188

189189
@ExplodeLoop
190190
@Override
191-
public Object execute(VirtualFrame frame) {
191+
public RubyArray execute(VirtualFrame frame) {
192192
final long[] executedValues = new long[values.length];
193193

194194
for (int n = 0; n < values.length; n++) {
@@ -225,7 +225,7 @@ public ObjectArrayLiteralNode(RubyLanguage language, RubyNode[] values) {
225225

226226
@ExplodeLoop
227227
@Override
228-
public Object execute(VirtualFrame frame) {
228+
public RubyArray execute(VirtualFrame frame) {
229229
final Object[] executedValues = new Object[values.length];
230230

231231
for (int n = 0; n < values.length; n++) {
@@ -244,7 +244,7 @@ public UninitialisedArrayLiteralNode(RubyLanguage language, RubyNode[] values) {
244244
}
245245

246246
@Override
247-
public Object execute(VirtualFrame frame) {
247+
public RubyArray execute(VirtualFrame frame) {
248248
CompilerDirectives.transferToInterpreterAndInvalidate();
249249

250250
final Object[] executedValues = new Object[values.length];

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ public ArraySliceNode(int from, int to) {
3232
this.to = to;
3333
}
3434

35+
public abstract RubyArray execute(RubyArray array);
36+
3537
@Specialization
36-
protected RubyArray readInBounds(RubyArray array,
38+
protected RubyArray readSlice(RubyArray array,
3739
@Cached ArrayCopyOnWriteNode cowNode,
3840
@Cached ConditionProfile emptyArray) {
3941
final int length = array.size + to - from;

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,12 @@ public static Object[] unshift(Object[] array, Object element) {
219219
return newArray;
220220
}
221221

222+
public static Object[] append(Object[] array, Object element) {
223+
final Object[] newArray = grow(array, array.length + 1);
224+
newArray[array.length] = element;
225+
return newArray;
226+
}
227+
222228
public static int memcmp(final byte[] first, final int firstStart, final byte[] second, final int secondStart,
223229
int size) {
224230
assert firstStart + size <= first.length;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (c) 2021 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.array;
11+
12+
import com.oracle.truffle.api.nodes.NodeInterface;
13+
14+
import com.oracle.truffle.api.frame.VirtualFrame;
15+
16+
public interface AssignableNode extends NodeInterface {
17+
18+
static final AssignableNode[] EMPTY_ARRAY = new AssignableNode[0];
19+
20+
/** This should null out the valueNode field, as that node should not be executed */
21+
AssignableNode toAssignableNode();
22+
23+
/** Execute the node and assign the given value */
24+
void assign(VirtualFrame frame, Object value);
25+
26+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright (c) 2021 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.array;
11+
12+
import com.oracle.truffle.api.CompilerDirectives;
13+
import com.oracle.truffle.api.frame.VirtualFrame;
14+
import com.oracle.truffle.api.nodes.ExplodeLoop;
15+
import org.truffleruby.RubyContext;
16+
import org.truffleruby.RubyLanguage;
17+
import org.truffleruby.core.array.ArrayIndexNodes.ReadNormalizedNode;
18+
import org.truffleruby.core.cast.SplatCastNode;
19+
import org.truffleruby.language.RubyContextSourceNode;
20+
import org.truffleruby.language.RubyNode;
21+
22+
import com.oracle.truffle.api.profiles.ConditionProfile;
23+
24+
public class MultipleAssignmentNode extends RubyContextSourceNode implements AssignableNode {
25+
26+
@Child RubyNode rhsNode;
27+
@Child SplatCastNode splatCastNode;
28+
29+
@Children final AssignableNode[] preNodes;
30+
@Child AssignableNode restNode;
31+
@Children final AssignableNode[] postNodes;
32+
33+
@Child ReadNormalizedNode arrayReadNormalizedNode;
34+
@Child ArraySliceNode arraySliceNode;
35+
36+
private final ConditionProfile enoughElementsForAllPost;
37+
38+
public MultipleAssignmentNode(
39+
AssignableNode[] preNodes,
40+
AssignableNode restNode,
41+
AssignableNode[] postNodes,
42+
SplatCastNode splatCastNode,
43+
RubyNode rhsNode) {
44+
this.preNodes = preNodes;
45+
this.restNode = restNode;
46+
this.postNodes = postNodes;
47+
this.splatCastNode = splatCastNode;
48+
this.rhsNode = rhsNode;
49+
this.enoughElementsForAllPost = postNodes.length == 0 ? null : ConditionProfile.create();
50+
}
51+
52+
@Override
53+
public Object execute(VirtualFrame frame) {
54+
final Object rhs = rhsNode.execute(frame);
55+
assign(frame, rhs);
56+
return rhs;
57+
}
58+
59+
@ExplodeLoop
60+
@Override
61+
public void assign(VirtualFrame frame, Object rhs) {
62+
final RubyArray array = (RubyArray) splatCastNode.execute(rhs);
63+
64+
for (int i = 0; i < preNodes.length; i++) {
65+
preNodes[i].assign(frame, read(array, i));
66+
}
67+
68+
if (restNode != null) {
69+
restNode.assign(frame, readSlice(array));
70+
}
71+
72+
if (postNodes.length > 0) {
73+
final int size = array.size;
74+
for (int i = 0; i < postNodes.length; i++) {
75+
// a, b, *, c, d = ary
76+
final int index;
77+
if (enoughElementsForAllPost.profile(size >= preNodes.length + postNodes.length)) {
78+
index = size - postNodes.length + i; // post take size-2, size-1 (size >= 4)
79+
} else {
80+
index = preNodes.length + i; // post take pre, pre+1, some of them (the tail) will read nil
81+
}
82+
postNodes[i].assign(frame, read(array, index));
83+
}
84+
}
85+
}
86+
87+
@Override
88+
public Object isDefined(VirtualFrame frame, RubyLanguage language, RubyContext context) {
89+
return language.coreStrings.ASSIGNMENT.createInstance(context);
90+
}
91+
92+
private Object read(RubyArray array, int i) {
93+
if (arrayReadNormalizedNode == null) {
94+
CompilerDirectives.transferToInterpreterAndInvalidate();
95+
arrayReadNormalizedNode = insert(ReadNormalizedNode.create());
96+
}
97+
98+
return arrayReadNormalizedNode.executeRead(array, i);
99+
}
100+
101+
private Object readSlice(RubyArray array) {
102+
if (arraySliceNode == null) {
103+
CompilerDirectives.transferToInterpreterAndInvalidate();
104+
arraySliceNode = insert(ArraySliceNodeGen.create(preNodes.length, -postNodes.length, null));
105+
}
106+
107+
return arraySliceNode.execute(array);
108+
}
109+
110+
@Override
111+
public AssignableNode toAssignableNode() {
112+
this.rhsNode = null;
113+
return this;
114+
}
115+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (c) 2021 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.array;
11+
12+
import com.oracle.truffle.api.frame.VirtualFrame;
13+
import org.truffleruby.language.RubyBaseNode;
14+
15+
/** An AssignableNode to represent the * in <code>* = 1, 2</code> */
16+
public class NoopAssignableNode extends RubyBaseNode implements AssignableNode {
17+
@Override
18+
public void assign(VirtualFrame frame, Object value) {
19+
// The RHS is executed now, nothing else to do
20+
}
21+
22+
@Override
23+
public AssignableNode toAssignableNode() {
24+
return this;
25+
}
26+
}

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,8 @@
2525
import com.oracle.truffle.api.frame.VirtualFrame;
2626
import com.oracle.truffle.api.profiles.BranchProfile;
2727

28-
29-
/*
30-
* TODO(CS): could probably unify this with SplatCastNode with some final configuration getContext().getOptions().
31-
*/
32-
33-
/** See also org.truffleruby.core.array.ArrayConvertNode */
28+
// TODO(CS): could probably unify this with SplatCastNode with some final configuration getContext().getOptions().
29+
/** See also {@link org.truffleruby.core.array.ArrayConvertNode} */
3430
@NodeChild(value = "child", type = RubyNode.class)
3531
public abstract class ArrayCastNode extends RubyContextSourceNode {
3632

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ public void doNotCopy() {
5757

5858
public abstract RubyNode getChild();
5959

60+
public abstract Object execute(Object value);
61+
6062
@Specialization
6163
protected Object splatNil(Nil nil) {
6264
switch (nilBehavior) {

src/main/java/org/truffleruby/core/regexp/RegexpNodes.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
11
/*
2-
* Copyright (c) 2016, 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-
/*
11-
* Copyright (c) 2013, 2015, 2016 Oracle and/or its affiliates. All rights reserved. This
2+
* Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved. This
123
* code is released under a tri EPL/GPL/LGPL license. You can use it,
134
* redistribute it and/or modify it under the terms of the:
145
*

0 commit comments

Comments
 (0)