Skip to content

Commit 3b28d6d

Browse files
committed
[GR-53462] Fix: Memmove node bug
PullRequest: graalpython/3322
2 parents 2877cdb + f630b4e commit 3b28d6d

File tree

2 files changed

+155
-2
lines changed

2 files changed

+155
-2
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.graal.python.test.nodes;
42+
43+
import com.oracle.graal.python.builtins.objects.PNone;
44+
import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
45+
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.MemMoveNode;
46+
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.StorageToNativeNode;
47+
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.GetItemScalarNode;
48+
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodesFactory.MemMoveNodeGen;
49+
import com.oracle.graal.python.runtime.GilNode;
50+
import com.oracle.graal.python.runtime.sequence.storage.IntSequenceStorage;
51+
import com.oracle.graal.python.runtime.sequence.storage.NativeSequenceStorage;
52+
import com.oracle.graal.python.test.PythonTests;
53+
import com.oracle.truffle.api.frame.VirtualFrame;
54+
import com.oracle.truffle.api.nodes.RootNode;
55+
import org.junit.After;
56+
import org.junit.Assert;
57+
import org.junit.Before;
58+
import org.junit.Test;
59+
60+
public class MemMoveNodeTests {
61+
62+
private GilNode.UncachedAcquire gil;
63+
64+
@Before
65+
public void setUp() {
66+
PythonTests.enterContext();
67+
this.gil = GilNode.uncachedAcquire();
68+
CApiContext.ensureCapiWasLoaded();
69+
}
70+
71+
@After
72+
public void tearDown() {
73+
this.gil.close();
74+
PythonTests.closeContext();
75+
}
76+
77+
@Test
78+
public void doArrayBasedSpecialization() {
79+
final var storage = new IntSequenceStorage(new int[]{1, 2, 2, 3, 0});
80+
new RootNode(null) {
81+
82+
@Override
83+
public Object execute(VirtualFrame frame) {
84+
var memMoveNode = MemMoveNodeGen.getUncached();
85+
memMoveNode.execute(this, storage, 3, 2, 2);
86+
return PNone.NO_VALUE;
87+
}
88+
}.getCallTarget().call();
89+
90+
var expectedArr = new int[]{1, 2, 2, 2, 3};
91+
Assert.assertArrayEquals(expectedArr, storage.getInternalIntArray());
92+
}
93+
94+
@Test
95+
public void doOtherSpecialization() {
96+
final NativeSequenceStorage storage = StorageToNativeNode.executeUncached(new Object[]{1, 2, 2, 3, 0}, 5);
97+
98+
new RootNode(null) {
99+
@Override
100+
public Object execute(VirtualFrame frame) {
101+
MemMoveNode.executeUncached(storage, 3, 2, 2);
102+
return PNone.NO_VALUE;
103+
}
104+
105+
}.getCallTarget().call();
106+
107+
Assert.assertEquals(1, GetItemScalarNode.executeUncached(storage, 0));
108+
Assert.assertEquals(2, GetItemScalarNode.executeUncached(storage, 1));
109+
Assert.assertEquals(2, GetItemScalarNode.executeUncached(storage, 2));
110+
Assert.assertEquals(2, GetItemScalarNode.executeUncached(storage, 3));
111+
Assert.assertEquals(3, GetItemScalarNode.executeUncached(storage, 4));
112+
}
113+
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/SequenceStorageNodes.java

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@
7373
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodesFactory.SetItemDynamicNodeGen;
7474
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodesFactory.SetItemNodeGen;
7575
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodesFactory.ToByteArrayNodeGen;
76+
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodesFactory.StorageToNativeNodeGen;
77+
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodesFactory.MemMoveNodeGen;
7678
import com.oracle.graal.python.builtins.objects.ints.PInt;
7779
import com.oracle.graal.python.builtins.objects.iterator.IteratorBuiltins.NextHelperNode;
7880
import com.oracle.graal.python.builtins.objects.iterator.IteratorNodes.BuiltinIteratorLengthHint;
@@ -1171,6 +1173,10 @@ public abstract static class MemMoveNode extends Node {
11711173

11721174
public abstract void execute(Node inliningTarget, SequenceStorage s, int distPos, int srcPos, int length);
11731175

1176+
public static void executeUncached(SequenceStorage s, int distPos, int srcPos, int length) {
1177+
MemMoveNodeGen.getUncached().execute(null, s, distPos, srcPos, length);
1178+
}
1179+
11741180
@SuppressWarnings("unused")
11751181
@Specialization(guards = "length <= 0")
11761182
protected static void nothing(SequenceStorage storage, int distPos, int srcPos, int length) {
@@ -1187,8 +1193,38 @@ protected static void doArrayBasedMove(ArrayBasedSequenceStorage storage, int di
11871193
protected static void doOther(Node inliningTarget, SequenceStorage storage, int distPos, int srcPos, int length,
11881194
@Cached SetItemScalarNode setLeftItemNode,
11891195
@Cached GetItemScalarNode getRightItemNode) {
1190-
for (int cur = distPos, j = srcPos, i = 0; i < length; cur += 1, j++, i++) {
1191-
setLeftItemNode.execute(inliningTarget, storage, cur, getRightItemNode.execute(inliningTarget, storage, j));
1196+
1197+
if (srcPos < distPos && srcPos + length > distPos) {
1198+
// case where we override values which we later want to read, therefore we need to
1199+
// copy from the end
1200+
copyBackward(inliningTarget, storage, getRightItemNode, setLeftItemNode, distPos, srcPos, length);
1201+
1202+
} else {
1203+
// otherwise copying forwards
1204+
copyForward(inliningTarget, storage, getRightItemNode, setLeftItemNode, distPos, srcPos, length);
1205+
}
1206+
}
1207+
1208+
private static void copyForward(Node inliningTarget, SequenceStorage storage, GetItemScalarNode getRightItemNode, SetItemScalarNode setLeftItemNode, int destPos, int srcPos, int length) {
1209+
while (length > 0) {
1210+
var value = getRightItemNode.execute(inliningTarget, storage, srcPos);
1211+
setLeftItemNode.execute(inliningTarget, storage, destPos, value);
1212+
destPos++;
1213+
srcPos++;
1214+
length--;
1215+
1216+
}
1217+
}
1218+
1219+
private static void copyBackward(Node inliningTarget, SequenceStorage storage, GetItemScalarNode getRightItemNode, SetItemScalarNode setLeftItemNode, int destPos, int srcPos, int length) {
1220+
destPos += length;
1221+
srcPos += length;
1222+
while (length > 0) {
1223+
destPos--;
1224+
srcPos--;
1225+
length--;
1226+
var value = getRightItemNode.execute(inliningTarget, storage, srcPos);
1227+
setLeftItemNode.execute(inliningTarget, storage, destPos, value);
11921228
}
11931229
}
11941230

@@ -1475,6 +1511,10 @@ public final NativeByteSequenceStorage executeBytes(Node inliningTarget, byte[]
14751511
return executeBytes(inliningTarget, obj, length, true);
14761512
}
14771513

1514+
public static NativeSequenceStorage executeUncached(Object obj, int length) {
1515+
return StorageToNativeNodeGen.getUncached().execute(null, obj, length);
1516+
}
1517+
14781518
public final NativeSequenceStorage execute(Node inliningTarget, Object obj, int length) {
14791519
return execute(inliningTarget, obj, length, true);
14801520
}

0 commit comments

Comments
 (0)