Skip to content

Commit 79de4d7

Browse files
committed
Allocate native tuple in PyTuple_New
1 parent 737d123 commit 79de4d7

10 files changed

Lines changed: 135 additions & 50 deletions

File tree

graalpython/com.oracle.graal.python.cext/src/tupleobject.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,66 @@ PyTuple_Size(PyObject *op)
105105
}
106106
#endif // GraalPy change
107107

108+
/* Allocate an uninitialized tuple object. Before making it public, following
109+
steps must be done:
110+
111+
- Initialize its items.
112+
- Call _PyObject_GC_TRACK() on it.
113+
114+
GraalPy change: unlike CPython, this does not use the tuple freelist.
115+
*/
116+
static PyTupleObject *
117+
tuple_alloc(Py_ssize_t size)
118+
{
119+
if (size < 0) {
120+
PyErr_BadInternalCall();
121+
return NULL;
122+
}
123+
/* Check for overflow */
124+
if ((size_t)size > ((size_t)PY_SSIZE_T_MAX - (sizeof(PyTupleObject) -
125+
sizeof(PyObject *))) / sizeof(PyObject *)) {
126+
return (PyTupleObject *)PyErr_NoMemory();
127+
}
128+
PyTupleObject *op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size);
129+
if (op == NULL) {
130+
return NULL;
131+
}
132+
return op;
133+
}
134+
135+
static inline PyObject *
136+
tuple_get_empty(void)
137+
{
138+
/*
139+
* GraalPy change: allocate a native empty tuple instead of returning CPython's static
140+
* singleton, so PyTuple_New consistently returns native tuples.
141+
*/
142+
PyTupleObject *op = tuple_alloc(0);
143+
if (op == NULL) {
144+
return NULL;
145+
}
146+
_PyObject_GC_TRACK(op);
147+
return (PyObject *)op;
148+
}
149+
150+
PyObject *
151+
PyTuple_New(Py_ssize_t size)
152+
{
153+
PyTupleObject *op;
154+
if (size == 0) {
155+
return tuple_get_empty();
156+
}
157+
op = tuple_alloc(size);
158+
if (op == NULL) {
159+
return NULL;
160+
}
161+
for (Py_ssize_t i = 0; i < size; i++) {
162+
op->ob_item[i] = NULL;
163+
}
164+
_PyObject_GC_TRACK(op);
165+
return (PyObject *)op;
166+
}
167+
108168
PyObject *
109169
PyTuple_GetItem(PyObject *op, Py_ssize_t i) {
110170
// GraalPy change: different implementation

graalpython/com.oracle.graal.python.cext/src/typeobject.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7321,6 +7321,9 @@ type_ready_mro(PyTypeObject *type)
73217321
}
73227322
#else // GraalPy change
73237323
PyObject* mro = GraalPyPrivate_Compute_Mro(type, type->tp_name);
7324+
if (mro == NULL) {
7325+
return -1;
7326+
}
73247327
type->tp_mro = mro;
73257328
#endif // GraalPy change
73267329
return 0;

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextErrBuiltins.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,11 @@
9898
import com.oracle.graal.python.lib.PyObjectLookupAttr;
9999
import com.oracle.graal.python.lib.PyObjectSetAttr;
100100
import com.oracle.graal.python.lib.PyObjectStrAsObjectNode;
101+
import com.oracle.graal.python.lib.PyTupleCheckNode;
101102
import com.oracle.graal.python.nodes.PRaiseNode;
102103
import com.oracle.graal.python.nodes.WriteUnraisableNode;
103104
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
105+
import com.oracle.graal.python.nodes.builtins.TupleNodes.ConstructTupleNode;
104106
import com.oracle.graal.python.nodes.call.CallNode;
105107
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
106108
import com.oracle.graal.python.nodes.object.GetClassNode;
@@ -234,6 +236,8 @@ static Object newEx(TruffleString name, Object base, Object dict,
234236
@Cached InlinedBranchProfile notDotProfile,
235237
@Cached InlinedBranchProfile notModuleProfile,
236238
@Cached InlinedConditionProfile baseProfile,
239+
@Cached PyTupleCheckNode tupleCheck,
240+
@Cached ConstructTupleNode constructTupleNode,
237241
@Cached PRaiseNode raiseNode) {
238242
if (base == PNone.NO_VALUE) {
239243
base = PythonErrorType.Exception;
@@ -252,8 +256,8 @@ static Object newEx(TruffleString name, Object base, Object dict,
252256
setItemNode.execute(null, inliningTarget, (PDict) dict, T___MODULE__, substringNode.execute(name, 0, dotIdx, TS_ENCODING, false));
253257
}
254258
PTuple bases;
255-
if (baseProfile.profile(inliningTarget, base instanceof PTuple)) {
256-
bases = (PTuple) base;
259+
if (baseProfile.profile(inliningTarget, tupleCheck.execute(inliningTarget, base))) {
260+
bases = constructTupleNode.execute(null, base);
257261
} else {
258262
bases = PFactory.createTuple(language, new Object[]{base});
259263
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextObjectBuiltins.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@
101101
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins.GetAttributeNode;
102102
import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins.SetattrNode;
103103
import com.oracle.graal.python.builtins.objects.object.PythonObject;
104-
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
105104
import com.oracle.graal.python.builtins.objects.type.TypeNodes;
106105
import com.oracle.graal.python.lib.PyBytesCheckNode;
107106
import com.oracle.graal.python.lib.PyLongCheckNode;
@@ -121,11 +120,13 @@
121120
import com.oracle.graal.python.lib.PyObjectReprAsObjectNode;
122121
import com.oracle.graal.python.lib.PyObjectSetItem;
123122
import com.oracle.graal.python.lib.PyObjectStrAsObjectNode;
123+
import com.oracle.graal.python.lib.PyTupleCheckNode;
124124
import com.oracle.graal.python.nodes.BuiltinNames;
125125
import com.oracle.graal.python.nodes.ErrorMessages;
126126
import com.oracle.graal.python.nodes.PRaiseNode;
127127
import com.oracle.graal.python.nodes.StringLiterals;
128128
import com.oracle.graal.python.nodes.argument.keywords.ExpandKeywordStarargsNode;
129+
import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage;
129130
import com.oracle.graal.python.nodes.call.CallNode;
130131
import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode;
131132
import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodNode;
@@ -318,6 +319,8 @@ static Object doGeneric(@SuppressWarnings("unused") long threadState, Object cal
318319
@Cached ExpandKeywordStarargsNode castKwargsNode,
319320
@Cached SequenceStorageNodes.GetItemScalarNode getItemScalarNode,
320321
@Cached CallNode callNode,
322+
@Cached PyTupleCheckNode tupleCheck,
323+
@Cached GetTupleStorage getTupleStorage,
321324
@Cached CastToTruffleStringNode castToTruffleStringNode) {
322325
try {
323326

@@ -327,10 +330,9 @@ static Object doGeneric(@SuppressWarnings("unused") long threadState, Object cal
327330
keywords = PKeyword.EMPTY_KEYWORDS;
328331
} else if (kwargs instanceof PDict) {
329332
keywords = castKwargsNode.execute(inliningTarget, kwargs);
330-
} else if (kwargs instanceof PTuple) {
333+
} else if (tupleCheck.execute(inliningTarget, kwargs)) {
331334
// We have a tuple with kw names and an array with kw values
332-
PTuple kwTuple = (PTuple) kwargs;
333-
SequenceStorage storage = kwTuple.getSequenceStorage();
335+
SequenceStorage storage = getTupleStorage.execute(inliningTarget, kwargs);
334336
int kwcount = storage.length();
335337
Object[] kwValues = readKwNode.readPyObjectArray(argsArray, kwcount, (int) nargs);
336338
keywords = new PKeyword[kwcount];

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTupleBuiltins.java

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@
4949
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectTransfer;
5050
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.Py_ssize_t;
5151
import static com.oracle.graal.python.runtime.PythonContext.NATIVE_NULL;
52-
import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.callocPtrArray;
5352

54-
import com.oracle.graal.python.PythonLanguage;
5553
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
5654
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBinaryBuiltinNode;
5755
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin;
@@ -60,8 +58,6 @@
6058
import com.oracle.graal.python.builtins.objects.PNone;
6159
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
6260
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode;
63-
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming;
64-
import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeInternalNode;
6561
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
6662
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.EnsureCapacityNode;
6763
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.GetItemNode;
@@ -76,8 +72,6 @@
7672
import com.oracle.graal.python.nodes.PRaiseNode;
7773
import com.oracle.graal.python.nodes.builtins.TupleNodes.GetNativeTupleStorage;
7874
import com.oracle.graal.python.runtime.PythonContext;
79-
import com.oracle.graal.python.runtime.object.PFactory;
80-
import com.oracle.graal.python.runtime.sequence.storage.NativeObjectSequenceStorage;
8175
import com.oracle.graal.python.runtime.sequence.storage.NativeSequenceStorage;
8276
import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage;
8377
import com.oracle.truffle.api.dsl.Bind;
@@ -90,29 +84,6 @@
9084

9185
public final class PythonCextTupleBuiltins {
9286

93-
private static final CApiTiming TIMING_PYTUPLE_NEW = CApiTiming.create(false, "PyTuple_New");
94-
95-
@CApiBuiltin(ret = PyObjectTransfer, args = {Py_ssize_t}, call = Direct, acquireGil = false)
96-
static long PyTuple_New(long longSize) {
97-
CApiTiming.enter();
98-
try {
99-
int size = (int) longSize;
100-
if (longSize != size) {
101-
throw PRaiseNode.raiseStatic(null, PythonBuiltinClassType.MemoryError);
102-
}
103-
/*
104-
* Already allocate the tuple with native memory, since it has to be populated from the
105-
* native side
106-
*/
107-
long mem = callocPtrArray(longSize + 1);
108-
NativeObjectSequenceStorage storage = NativeObjectSequenceStorage.create(mem, size, size, true);
109-
PTuple tuple = PFactory.createTuple(PythonLanguage.get(null), storage);
110-
return PythonToNativeInternalNode.executeUncached(tuple, true);
111-
} finally {
112-
CApiTiming.exit(TIMING_PYTUPLE_NEW);
113-
}
114-
}
115-
11687
@CApiBuiltin(ret = PyObjectBorrowed, args = {PyObject, Py_ssize_t}, call = Ignored)
11788
public abstract static class GraalPyPrivate_Tuple_GetItem extends CApiBinaryBuiltinNode {
11889

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,7 @@ public final class CApiFunction {
510510
@CApiBuiltin(name = "PyThread_tss_set", ret = Int, args = {PY_TSS_T_PTR, Pointer}, call = CImpl)
511511
@CApiBuiltin(name = "PyTraceMalloc_Track", ret = Int, args = {UNSIGNED_INT, UINTPTR_T, SIZE_T}, call = CImpl)
512512
@CApiBuiltin(name = "PyTraceMalloc_Untrack", ret = Int, args = {UNSIGNED_INT, UINTPTR_T}, call = CImpl)
513+
@CApiBuiltin(name = "PyTuple_New", ret = PyObject, args = {Py_ssize_t}, call = CImpl)
513514
@CApiBuiltin(name = "PyTuple_Pack", ret = PyObject, args = {Py_ssize_t, VARARGS}, call = CImpl)
514515
@CApiBuiltin(name = "PyTuple_GetItem", ret = PyObjectBorrowed, args = {PyObject, Py_ssize_t}, call = CImpl)
515516
@CApiBuiltin(name = "PyTuple_SetItem", ret = Int, args = {PyObject, Py_ssize_t, PyObjectTransfer}, call = CImpl)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
import com.oracle.graal.python.lib.PyObjectIsTrueNode;
133133
import com.oracle.graal.python.lib.PyObjectLookupAttr;
134134
import com.oracle.graal.python.lib.PyObjectReprAsTruffleStringNode;
135+
import com.oracle.graal.python.lib.PyTupleCheckNode;
135136
import com.oracle.graal.python.nodes.ErrorMessages;
136137
import com.oracle.graal.python.nodes.HiddenAttr;
137138
import com.oracle.graal.python.nodes.PConstructAndRaiseNode;
@@ -144,6 +145,7 @@
144145
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
145146
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
146147
import com.oracle.graal.python.nodes.builtins.ListNodes.ConstructListNode;
148+
import com.oracle.graal.python.nodes.builtins.TupleNodes.ConstructTupleNode;
147149
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
148150
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
149151
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
@@ -413,22 +415,30 @@ public static TypeNode create() {
413415
}
414416

415417
@Specialization(guards = {"!isNoValue(bases)", "!isNoValue(dict)"})
416-
Object typeGeneric(VirtualFrame frame, Object cls, Object name, Object bases, Object dict, PKeyword[] kwds,
418+
static Object typeGeneric(VirtualFrame frame, Object cls, Object name, Object bases, Object dict, PKeyword[] kwds,
417419
@Bind Node inliningTarget,
418420
@Cached TypeNode nextTypeNode,
419421
@Cached PRaiseNode raiseNode,
422+
@Cached PyTupleCheckNode tupleCheck,
423+
@Cached ConstructTupleNode constructTupleNode,
420424
@Exclusive @Cached IsTypeNode isTypeNode) {
425+
Object basesTuple;
421426
if (!(name instanceof TruffleString || name instanceof PString)) {
422427
throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.MUST_BE_STRINGS_NOT_P, "type() argument 1", name);
423-
} else if (!(bases instanceof PTuple)) {
428+
} else if (bases instanceof PTuple) {
429+
basesTuple = bases;
430+
} else if (tupleCheck.execute(inliningTarget, bases)) {
431+
basesTuple = constructTupleNode.execute(frame, bases);
432+
} else {
424433
throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.MUST_BE_STRINGS_NOT_P, "type() argument 2", bases);
425-
} else if (!(dict instanceof PDict)) {
434+
}
435+
if (!(dict instanceof PDict)) {
426436
throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.MUST_BE_STRINGS_NOT_P, "type() argument 3", dict);
427437
} else if (!isTypeNode.execute(inliningTarget, cls)) {
428438
// TODO: this is actually allowed, deal with it
429439
throw raiseNode.raise(inliningTarget, NotImplementedError, ErrorMessages.CREATING_CLASS_NON_CLS_META_CLS);
430440
}
431-
return nextTypeNode.execute(frame, cls, name, bases, dict, kwds);
441+
return nextTypeNode.execute(frame, cls, name, basesTuple, dict, kwds);
432442
}
433443

434444
private TypeNodes.IsAcceptableBaseNode ensureIsAcceptableBaseNode() {
@@ -674,9 +684,11 @@ static Object getBases(Object self, @SuppressWarnings("unused") PNone value,
674684
return PFactory.createTuple(language, getBaseClassesNode.execute(inliningTarget, self));
675685
}
676686

677-
@Specialization
678-
static Object setBases(VirtualFrame frame, PythonClass cls, PTuple value,
687+
@Specialization(guards = "tupleCheck.execute(inliningTarget, value)", limit = "1")
688+
static Object setBases(VirtualFrame frame, PythonClass cls, Object value,
679689
@Bind Node inliningTarget,
690+
@SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheck,
691+
@Cached ConstructTupleNode constructTupleNode,
680692
@Cached GetObjectArrayNode getArray,
681693
@Cached GetBaseClassNode getBase,
682694
@Cached GetBestBaseClassNode getBestBase,
@@ -687,7 +699,8 @@ static Object setBases(VirtualFrame frame, PythonClass cls, PTuple value,
687699
@Cached GetMroNode getMroNode,
688700
@Cached PRaiseNode raiseNode) {
689701

690-
Object[] a = getArray.execute(inliningTarget, value);
702+
PTuple bases = value instanceof PTuple ? (PTuple) value : constructTupleNode.execute(frame, value);
703+
Object[] a = getArray.execute(inliningTarget, bases);
691704
if (a.length == 0) {
692705
throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.CAN_ONLY_ASSIGN_NON_EMPTY_TUPLE_TO_P, cls);
693706
}
@@ -744,9 +757,10 @@ private static boolean typeIsSubtypeBaseChain(Node inliningTarget, Object a, Obj
744757
return (isSameTypeNode.execute(inliningTarget, b, PythonBuiltinClassType.PythonObject));
745758
}
746759

747-
@Specialization(guards = "!isPTuple(value)")
760+
@Specialization(guards = "!tupleCheck.execute(inliningTarget, value)", limit = "1")
748761
static Object setObject(@SuppressWarnings("unused") PythonClass cls, @SuppressWarnings("unused") Object value,
749-
@Bind Node inliningTarget) {
762+
@Bind Node inliningTarget,
763+
@SuppressWarnings("unused") @Exclusive @Cached PyTupleCheckNode tupleCheck) {
750764
throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.CAN_ONLY_ASSIGN_S_TO_S_S_NOT_P, "tuple", GetNameNode.executeUncached(cls), "__bases__", value);
751765
}
752766

@@ -1241,8 +1255,9 @@ public static void dir(PSet names, Object klass) {
12411255
updateSingleNode.execute(null, names, ns);
12421256
}
12431257
Object basesAttr = PyObjectLookupAttr.executeUncached(klass, T___BASES__);
1244-
if (basesAttr instanceof PTuple) {
1245-
Object[] bases = ToArrayNode.executeUncached(((PTuple) basesAttr).getSequenceStorage());
1258+
if (basesAttr instanceof PTuple || PyTupleCheckNode.executeUncached(basesAttr)) {
1259+
PTuple basesTuple = basesAttr instanceof PTuple ? (PTuple) basesAttr : ConstructTupleNode.getUncached().execute(null, basesAttr);
1260+
Object[] bases = ToArrayNode.executeUncached(basesTuple.getSequenceStorage());
12461261
for (Object cls : bases) {
12471262
// Note that since we are only interested in the keys, the order
12481263
// we merge classes is unimportant

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_name;
5555
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_subclasses;
5656
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_weaklistoffset;
57+
import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTupleObject__ob_item;
5758
import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField;
5859
import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.writeLongField;
5960
import static com.oracle.graal.python.builtins.objects.type.TypeFlags.BASETYPE;
@@ -960,14 +961,26 @@ static PythonAbstractClass[] doNative(Node inliningTarget, PythonNativeClass obj
960961
} catch (ClassCastException e) {
961962
throw raise.raise(inliningTarget, PythonBuiltinClassType.SystemError, ErrorMessages.UNSUPPORTED_OBJ_IN, "tp_bases");
962963
}
964+
} else if (result instanceof PythonAbstractNativeObject nativeTuple) {
965+
int length = Math.toIntExact(readLongField(nativeTuple.getPtr(), CFields.PyVarObject__ob_size));
966+
Object[] values = getTpBasesNode.readPyObjectArray(CStructAccess.getFieldPtr(nativeTuple.getPtr(), PyTupleObject__ob_item), length);
967+
try {
968+
return cast(values, length);
969+
} catch (ClassCastException e) {
970+
throw raise.raise(inliningTarget, PythonBuiltinClassType.SystemError, ErrorMessages.UNSUPPORTED_OBJ_IN, "tp_bases");
971+
}
963972
}
964973
throw raise.raise(inliningTarget, PythonBuiltinClassType.SystemError, ErrorMessages.TYPE_DOES_NOT_PROVIDE_BASES);
965974
}
966975

967976
// TODO: get rid of this
968977
private static PythonAbstractClass[] cast(Object[] arr, SequenceStorage storage) {
969-
PythonAbstractClass[] bases = new PythonAbstractClass[storage.length()];
970-
for (int i = 0; i < storage.length(); i++) {
978+
return cast(arr, storage.length());
979+
}
980+
981+
private static PythonAbstractClass[] cast(Object[] arr, int length) {
982+
PythonAbstractClass[] bases = new PythonAbstractClass[length];
983+
for (int i = 0; i < length; i++) {
971984
bases[i] = (PythonAbstractClass) arr[i];
972985
}
973986
return bases;

0 commit comments

Comments
 (0)