Skip to content

Commit be01950

Browse files
committed
[GR-70214] Bytecode DSL: fix bug in BinarySubscript, initialize empty lists to empty storage.
PullRequest: graalpython/4036
2 parents 666e455 + a64ecf5 commit be01950

File tree

7 files changed

+184
-22
lines changed

7 files changed

+184
-22
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
import sys
40+
41+
42+
if sys.implementation.name == "graalpy":
43+
if not hasattr(__graalpython__, 'get_storage_strategy'):
44+
raise SystemError("This test must run with --python.EnableDebuggingBuiltins=true")
45+
46+
def check_strategy(container, expected):
47+
actual = __graalpython__.get_storage_strategy(container)
48+
assert actual == expected, f"Container '{container}', wrong storage strategy : {actual=} != {expected=}"
49+
50+
LITERAL_INT_STORAGE = "NativeIntSequenceStorage" if __graalpython__.using_native_primitive_storage_strategy else "IntSequenceStorage"
51+
else:
52+
# For CPython, just to verify other test results
53+
def check_strategy(container, expected):
54+
pass
55+
56+
LITERAL_INT_STORAGE = "dummy"
57+
58+
59+
def test_appending_ints():
60+
l = list()
61+
for i in range(10):
62+
l.append(i)
63+
assert l[5] == 5
64+
check_strategy(l, "IntSequenceStorage")
65+
66+
67+
def test_appending_doubles():
68+
l = list()
69+
for i in range(10):
70+
l.append(i * 0.1)
71+
assert l[5] == 0.5
72+
check_strategy(l, "DoubleSequenceStorage")
73+
74+
75+
def test_generator_int():
76+
l = [x for x in range(10)]
77+
assert l[5] == 5
78+
check_strategy(l, "IntSequenceStorage")
79+
80+
81+
def test_generator_double():
82+
l = [x * 0.1 for x in range(10)]
83+
assert l[5] == 0.5
84+
check_strategy(l, "DoubleSequenceStorage")
85+
86+
87+
# GR-70364
88+
# def test_generator_bool():
89+
# l = [x >= 5 for x in range(10)]
90+
# assert l[5] == True
91+
# check_strategy(l, "BoolSequenceStorage")
92+
93+
94+
def test_literal_int():
95+
l = [1,2,3,4,5]
96+
assert l[4] == 5
97+
check_strategy(l, LITERAL_INT_STORAGE)
98+
99+
100+
def test_literal_double():
101+
l = [1.1,2.2,3.3,4.4,0.5]
102+
assert l[4] == 0.5
103+
check_strategy(l, "DoubleSequenceStorage")
104+
105+
106+
def test_literal_mixed():
107+
l = [1,2,3,4,0.5]
108+
assert l[4] == 0.5
109+
check_strategy(l, "ObjectSequenceStorage")

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ public void postInitialize(Python3Core core) {
270270
mod.setAttribute(tsLiteral("dump_truffle_ast"), PNone.NO_VALUE);
271271
mod.setAttribute(tsLiteral("tdebug"), PNone.NO_VALUE);
272272
mod.setAttribute(tsLiteral("set_storage_strategy"), PNone.NO_VALUE);
273+
mod.setAttribute(tsLiteral("get_storage_strategy"), PNone.NO_VALUE);
273274
mod.setAttribute(tsLiteral("storage_to_native"), PNone.NO_VALUE);
274275
mod.setAttribute(tsLiteral("dump_heap"), PNone.NO_VALUE);
275276
mod.setAttribute(tsLiteral("is_native_object"), PNone.NO_VALUE);
@@ -278,6 +279,8 @@ public void postInitialize(Python3Core core) {
278279
mod.setAttribute(tsLiteral("clear_interop_type_registry"), PNone.NO_VALUE);
279280
mod.setAttribute(tsLiteral("foreign_number_list"), PNone.NO_VALUE);
280281
mod.setAttribute(tsLiteral("foreign_wrapper"), PNone.NO_VALUE);
282+
} else {
283+
addBuiltinConstant("using_native_primitive_storage_strategy", context.getLanguage().getEngineOption(PythonOptions.UseNativePrimitiveStorageStrategy));
281284
}
282285
if (PythonImageBuildOptions.WITHOUT_PLATFORM_ACCESS || !context.getOption(PythonOptions.RunViaLauncher)) {
283286
mod.setAttribute(tsLiteral("list_files"), PNone.NO_VALUE);
@@ -693,7 +696,7 @@ static long doIt(@SuppressWarnings("unused") Object dummy) {
693696
}
694697
}
695698

696-
// Internal builtin used for testing: changes strategy of newly allocated set or map
699+
// Internal builtin used for testing: changes strategy of newly allocated set or map
697700
@Builtin(name = "set_storage_strategy", minNumOfPositionalArgs = 2)
698701
@GenerateNodeFactory
699702
public abstract static class SetStorageStrategyNode extends PythonBinaryBuiltinNode {
@@ -734,6 +737,16 @@ private void validate(HashingStorage dictStorage) {
734737
}
735738
}
736739

740+
@Builtin(name = "get_storage_strategy", minNumOfPositionalArgs = 1)
741+
@GenerateNodeFactory
742+
public abstract static class GetStorageStrategyNode extends PythonUnaryBuiltinNode {
743+
@Specialization
744+
@TruffleBoundary
745+
TruffleString doSet(PSequence seq) {
746+
return PythonUtils.toTruffleStringUncached(seq.getSequenceStorage().getClass().getSimpleName());
747+
}
748+
}
749+
737750
@Builtin(name = "storage_to_native", minNumOfPositionalArgs = 1)
738751
@GenerateNodeFactory
739752
abstract static class StorageToNative extends PythonUnaryBuiltinNode {

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@
4444
import com.oracle.graal.python.builtins.objects.common.IndexNodesFactory.NormalizeIndexWithBoundsCheckNodeGen;
4545
import com.oracle.graal.python.builtins.objects.common.IndexNodesFactory.NormalizeIndexWithoutBoundsCheckNodeGen;
4646
import com.oracle.graal.python.builtins.objects.ints.PInt;
47+
import com.oracle.graal.python.builtins.objects.list.PList;
4748
import com.oracle.graal.python.nodes.ErrorMessages;
4849
import com.oracle.graal.python.nodes.PRaiseNode;
50+
import com.oracle.graal.python.runtime.sequence.PTupleListBase;
4951
import com.oracle.graal.python.util.OverflowException;
5052
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
5153
import com.oracle.truffle.api.dsl.Bind;
@@ -159,6 +161,16 @@ public static NormalizeIndexWithBoundsCheckNode getUncached() {
159161
return NormalizeIndexWithBoundsCheckNodeGen.getUncached();
160162
}
161163

164+
public static int normalizeIntIndex(Node inliningTarget, int index, int length, PTupleListBase container,
165+
InlinedConditionProfile negativeIndexProfile, PRaiseNode raiseNode) {
166+
int normalizedIndex = index;
167+
if (negativeIndexProfile.profile(inliningTarget, normalizedIndex < 0)) {
168+
normalizedIndex += length;
169+
}
170+
checkBounds(inliningTarget, raiseNode, container, normalizedIndex, length);
171+
return normalizedIndex;
172+
}
173+
162174
@Specialization
163175
static int doInt(int index, int length, TruffleString errorMessage,
164176
@Bind Node inliningTarget,
@@ -322,6 +334,12 @@ static long doLongLong(long index, long length, @SuppressWarnings("unused") Truf
322334
}
323335
}
324336

337+
public static void checkBounds(Node inliningTarget, PRaiseNode raiseNode, PTupleListBase container, int idx, int length) {
338+
if (idx < 0 || idx >= length) {
339+
raiseIndexError(inliningTarget, container, raiseNode);
340+
}
341+
}
342+
325343
public static void checkBounds(Node inliningTarget, PRaiseNode raiseNode, TruffleString errorMessage, int idx, int length) {
326344
if (idx < 0 || idx >= length) {
327345
raiseIndexError(inliningTarget, errorMessage, raiseNode);
@@ -340,6 +358,12 @@ public static void checkBounds(Node inliningTarget, PRaiseNode raiseNode, Truffl
340358
}
341359
}
342360

361+
@InliningCutoff
362+
private static void raiseIndexError(Node inliningTarget, PTupleListBase container, PRaiseNode raiseNode) {
363+
TruffleString errorMessage = container instanceof PList ? ErrorMessages.LIST_INDEX_OUT_OF_RANGE : ErrorMessages.TUPLE_OUT_OF_BOUNDS;
364+
raiseIndexError(inliningTarget, errorMessage, raiseNode);
365+
}
366+
343367
@InliningCutoff
344368
private static void raiseIndexError(Node inliningTarget, TruffleString errorMessage, PRaiseNode raiseNode) {
345369
throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.IndexError, errorMessage);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/list/PList.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
import com.oracle.graal.python.nodes.interop.PForeignToPTypeNode;
3535
import com.oracle.graal.python.runtime.GilNode;
3636
import com.oracle.graal.python.runtime.exception.PException;
37-
import com.oracle.graal.python.runtime.sequence.PSequenceWithStorage;
37+
import com.oracle.graal.python.runtime.sequence.PTupleListBase;
3838
import com.oracle.graal.python.runtime.sequence.storage.ArrayBasedSequenceStorage;
3939
import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage;
4040
import com.oracle.graal.python.util.OverflowException;
@@ -57,7 +57,7 @@
5757

5858
@SuppressWarnings("truffle-abstract-export")
5959
@ExportLibrary(InteropLibrary.class)
60-
public final class PList extends PSequenceWithStorage {
60+
public final class PList extends PTupleListBase {
6161
private final ListOrigin origin;
6262

6363
public PList(Object cls, Shape instanceShape, SequenceStorage store) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/PTuple.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
import static com.oracle.graal.python.util.PythonUtils.builtinClassToType;
2929

30-
import com.oracle.graal.python.runtime.sequence.PSequenceWithStorage;
30+
import com.oracle.graal.python.runtime.sequence.PTupleListBase;
3131
import com.oracle.graal.python.runtime.sequence.storage.ObjectSequenceStorage;
3232
import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage;
3333
import com.oracle.truffle.api.CompilerAsserts;
@@ -40,7 +40,7 @@
4040

4141
@SuppressWarnings("truffle-abstract-export")
4242
@ExportLibrary(InteropLibrary.class)
43-
public final class PTuple extends PSequenceWithStorage {
43+
public final class PTuple extends PTupleListBase {
4444

4545
private long hash = -1;
4646

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,10 @@
217217
import com.oracle.graal.python.runtime.exception.PythonErrorType;
218218
import com.oracle.graal.python.runtime.object.PFactory;
219219
import com.oracle.graal.python.runtime.sequence.PSequence;
220-
import com.oracle.graal.python.runtime.sequence.PSequenceWithStorage;
220+
import com.oracle.graal.python.runtime.sequence.PTupleListBase;
221221
import com.oracle.graal.python.runtime.sequence.storage.BoolSequenceStorage;
222222
import com.oracle.graal.python.runtime.sequence.storage.DoubleSequenceStorage;
223+
import com.oracle.graal.python.runtime.sequence.storage.EmptySequenceStorage;
223224
import com.oracle.graal.python.runtime.sequence.storage.IntSequenceStorage;
224225
import com.oracle.graal.python.runtime.sequence.storage.LongSequenceStorage;
225226
import com.oracle.graal.python.runtime.sequence.storage.ObjectSequenceStorage;
@@ -1636,7 +1637,16 @@ public static Object perform(VirtualFrame frame,
16361637

16371638
@Operation(storeBytecodeIndex = false)
16381639
public static final class MakeList {
1639-
@Specialization
1640+
@Specialization(guards = "elements.length == 0")
1641+
public static PList doEmpty(@Variadic Object[] elements,
1642+
@Bind PBytecodeDSLRootNode rootNode) {
1643+
// Common pattern is to create an empty list and then add items.
1644+
// We need to start from empty storage, so that we can specialize to, say, int storage
1645+
// if only ints are appended to this list
1646+
return PFactory.createList(rootNode.getLanguage(), EmptySequenceStorage.INSTANCE);
1647+
}
1648+
1649+
@Specialization(guards = "elements.length > 0")
16401650
public static PList perform(@Variadic Object[] elements,
16411651
@Bind PBytecodeDSLRootNode rootNode) {
16421652
return PFactory.createList(rootNode.getLanguage(), elements);
@@ -3209,45 +3219,51 @@ public static boolean doObject(long typeFlags, Object value,
32093219
@Operation(storeBytecodeIndex = true)
32103220
@ImportStatic(PGuards.class)
32113221
public static final class BinarySubscript {
3212-
static boolean isBuiltinListOrTuple(PSequenceWithStorage s) {
3213-
return PGuards.isBuiltinTuple(s) && PGuards.isBuiltinList(s);
3222+
static boolean isBuiltinListOrTuple(PTupleListBase s) {
3223+
return PGuards.isBuiltinTuple(s) || PGuards.isBuiltinList(s);
32143224
}
32153225

3216-
public static boolean isIntBuiltinListOrTuple(PSequenceWithStorage s) {
3226+
public static boolean isIntBuiltinListOrTuple(PTupleListBase s) {
32173227
return isBuiltinListOrTuple(s) && s.getSequenceStorage() instanceof IntSequenceStorage;
32183228
}
32193229

32203230
@Specialization(guards = "isIntBuiltinListOrTuple(list)")
3221-
public static int doIntStorage(PSequenceWithStorage list, int index,
3222-
@Shared @Cached NormalizeIndexWithBoundsCheckNode normalizeIndexNode) {
3231+
public static int doIntStorage(PTupleListBase list, int index,
3232+
@Bind Node inliningTarget,
3233+
@Shared @Cached InlinedConditionProfile negativeIndexProfile,
3234+
@Shared @Cached PRaiseNode raiseNode) {
32233235
IntSequenceStorage storage = (IntSequenceStorage) list.getSequenceStorage();
3224-
int normalizedIndex = normalizeIndexNode.execute(index, storage.length(), ErrorMessages.LIST_INDEX_OUT_OF_RANGE);
3236+
int normalizedIndex = NormalizeIndexWithBoundsCheckNode.normalizeIntIndex(inliningTarget, index, storage.length(), list, negativeIndexProfile, raiseNode);
32253237
return storage.getIntItemNormalized(normalizedIndex);
32263238
}
32273239

3228-
public static boolean isDoubleBuiltinListOrTuple(PSequenceWithStorage s) {
3240+
public static boolean isDoubleBuiltinListOrTuple(PTupleListBase s) {
32293241
return isBuiltinListOrTuple(s) && s.getSequenceStorage() instanceof DoubleSequenceStorage;
32303242
}
32313243

32323244
@Specialization(guards = "isDoubleBuiltinListOrTuple(list)")
3233-
public static double doDoubleStorage(PSequenceWithStorage list, int index,
3234-
@Shared @Cached NormalizeIndexWithBoundsCheckNode normalizeIndexNode) {
3245+
public static double doDoubleStorage(PTupleListBase list, int index,
3246+
@Bind Node inliningTarget,
3247+
@Shared @Cached InlinedConditionProfile negativeIndexProfile,
3248+
@Shared @Cached PRaiseNode raiseNode) {
32353249
DoubleSequenceStorage storage = (DoubleSequenceStorage) list.getSequenceStorage();
3236-
int normalizedIndex = normalizeIndexNode.execute(index, storage.length(), ErrorMessages.LIST_INDEX_OUT_OF_RANGE);
3250+
int normalizedIndex = NormalizeIndexWithBoundsCheckNode.normalizeIntIndex(inliningTarget, index, storage.length(), list, negativeIndexProfile, raiseNode);
32373251
return storage.getDoubleItemNormalized(normalizedIndex);
32383252
}
32393253

32403254
@Specialization(replaces = {"doIntStorage", "doDoubleStorage"}, guards = "isBuiltinListOrTuple(list)")
3241-
public static Object doObjectStorage(PSequenceWithStorage list, int index,
3255+
public static Object doGenericStorage(PTupleListBase list, int index,
32423256
@Bind Node inliningTarget,
3243-
@Shared @Cached NormalizeIndexWithBoundsCheckNode normalizeIndexNode,
3257+
@Exclusive @Cached InlinedConditionProfile negativeIndexProfile,
3258+
@Exclusive @Cached PRaiseNode raiseNode,
32443259
@Cached GetItemScalarNode getItemScalarNode) {
32453260
SequenceStorage storage = list.getSequenceStorage();
3246-
int normalizedIndex = normalizeIndexNode.execute(index, storage.length(), ErrorMessages.LIST_INDEX_OUT_OF_RANGE);
3261+
int normalizedIndex = NormalizeIndexWithBoundsCheckNode.normalizeIntIndex(inliningTarget, index, storage.length(), list, negativeIndexProfile, raiseNode);
32473262
return getItemScalarNode.execute(inliningTarget, storage, normalizedIndex);
32483263
}
32493264

32503265
@Fallback
3266+
@InliningCutoff
32513267
public static Object doOther(VirtualFrame frame, Object receiver, Object key,
32523268
@Bind Node inliningTarget,
32533269
@Cached GetObjectSlotsNode getSlotsNode,
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@
4444
import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage;
4545
import com.oracle.truffle.api.object.Shape;
4646

47-
public abstract class PSequenceWithStorage extends PSequence {
47+
public abstract class PTupleListBase extends PSequence {
4848
protected SequenceStorage store;
4949

50-
public PSequenceWithStorage(Object cls, Shape instanceShape) {
50+
public PTupleListBase(Object cls, Shape instanceShape) {
5151
super(cls, instanceShape);
5252
}
5353

0 commit comments

Comments
 (0)