From bd47b93f578e2d2f25c1c8c3fc06a41068218d51 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 30 Apr 2026 15:22:46 +0100 Subject: [PATCH 1/9] Update vec structs and add buf access macros --- mypyc/lib-rt/vecs/librt_vecs.h | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/mypyc/lib-rt/vecs/librt_vecs.h b/mypyc/lib-rt/vecs/librt_vecs.h index d0bb4760ed57..8636e88aa9d7 100644 --- a/mypyc/lib-rt/vecs/librt_vecs.h +++ b/mypyc/lib-rt/vecs/librt_vecs.h @@ -97,48 +97,63 @@ typedef struct _VecNestedBufObject { // Unboxed vec objects +// +// The items pointer points to the first element of the items array in the +// corresponding buffer object. Use VEC_*_BUF() to recover the buffer object +// from the items pointer (only needed on cold paths like grow/refcount). +// Items pointer is NULL for empty/uninitialized vecs. typedef struct _VecI64 { Py_ssize_t len; - VecI64BufObject *buf; + int64_t *items; } VecI64; typedef struct _VecI32 { Py_ssize_t len; - VecI32BufObject *buf; + int32_t *items; } VecI32; typedef struct _VecI16 { Py_ssize_t len; - VecI16BufObject *buf; + int16_t *items; } VecI16; typedef struct _VecU8 { Py_ssize_t len; - VecU8BufObject *buf; + uint8_t *items; } VecU8; typedef struct _VecFloat { Py_ssize_t len; - VecFloatBufObject *buf; + double *items; } VecFloat; typedef struct _VecBool { Py_ssize_t len; - VecBoolBufObject *buf; + char *items; } VecBool; typedef struct _VecT { Py_ssize_t len; - VecTBufObject *buf; + PyObject **items; } VecT; typedef struct _VecNested { Py_ssize_t len; - VecNestedBufObject *buf; + VecNestedBufItem *items; } VecNested; +// Recover buffer object from items pointer. Only valid when items != NULL. +#define VEC_I64_BUF(v) ((VecI64BufObject *)((char *)(v).items - offsetof(VecI64BufObject, items))) +#define VEC_I32_BUF(v) ((VecI32BufObject *)((char *)(v).items - offsetof(VecI32BufObject, items))) +#define VEC_I16_BUF(v) ((VecI16BufObject *)((char *)(v).items - offsetof(VecI16BufObject, items))) +#define VEC_U8_BUF(v) ((VecU8BufObject *)((char *)(v).items - offsetof(VecU8BufObject, items))) +#define VEC_FLOAT_BUF(v) ((VecFloatBufObject *)((char *)(v).items - offsetof(VecFloatBufObject, items))) +#define VEC_BOOL_BUF(v) ((VecBoolBufObject *)((char *)(v).items - offsetof(VecBoolBufObject, items))) +#define VEC_T_BUF(v) ((VecTBufObject *)((char *)(v).items - offsetof(VecTBufObject, items))) +#define VEC_NESTED_BUF(v) ((VecNestedBufObject *)((char *)(v).items - offsetof(VecNestedBufObject, items))) + // Boxed vec objects From cd0e60ce8f4582babb292080d8e9011845195e27 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 30 Apr 2026 15:47:36 +0100 Subject: [PATCH 2/9] Update librt.vecs C code --- mypyc/lib-rt/vecs/librt_vecs.c | 80 ++++++------- mypyc/lib-rt/vecs/librt_vecs.h | 104 ++++++++++------ mypyc/lib-rt/vecs/vec_nested.c | 151 +++++++++++++---------- mypyc/lib-rt/vecs/vec_t.c | 198 +++++++++++++++++-------------- mypyc/lib-rt/vecs/vec_template.c | 108 +++++++++-------- mypyc/lib-rt/vecs_extra_ops.h | 2 +- 6 files changed, 365 insertions(+), 278 deletions(-) diff --git a/mypyc/lib-rt/vecs/librt_vecs.c b/mypyc/lib-rt/vecs/librt_vecs.c index 8726b63de7a1..98ece4654520 100644 --- a/mypyc/lib-rt/vecs/librt_vecs.c +++ b/mypyc/lib-rt/vecs/librt_vecs.c @@ -566,7 +566,7 @@ static PyObject *vec_append(PyObject *self, PyObject *args) return NULL; } VecI64 v = ((VecI64Object *)vec)->vec; - VEC_INCREF(v); + VEC_I64_INCREF(v); v = VecI64_Append(v, x); if (VEC_IS_ERROR(v)) return NULL; @@ -577,7 +577,7 @@ static PyObject *vec_append(PyObject *self, PyObject *args) return NULL; } VecU8 v = ((VecU8Object *)vec)->vec; - VEC_INCREF(v); + VEC_U8_INCREF(v); v = VecU8_Append(v, x); if (VEC_IS_ERROR(v)) return NULL; @@ -588,7 +588,7 @@ static PyObject *vec_append(PyObject *self, PyObject *args) return NULL; } VecFloat v = ((VecFloatObject *)vec)->vec; - VEC_INCREF(v); + VEC_FLOAT_INCREF(v); v = VecFloat_Append(v, x); if (VEC_IS_ERROR(v)) return NULL; @@ -599,7 +599,7 @@ static PyObject *vec_append(PyObject *self, PyObject *args) return NULL; } VecI32 v = ((VecI32Object *)vec)->vec; - VEC_INCREF(v); + VEC_I32_INCREF(v); v = VecI32_Append(v, x); if (VEC_IS_ERROR(v)) return NULL; @@ -610,7 +610,7 @@ static PyObject *vec_append(PyObject *self, PyObject *args) return NULL; } VecI16 v = ((VecI16Object *)vec)->vec; - VEC_INCREF(v); + VEC_I16_INCREF(v); v = VecI16_Append(v, x); if (VEC_IS_ERROR(v)) return NULL; @@ -621,27 +621,27 @@ static PyObject *vec_append(PyObject *self, PyObject *args) return NULL; } VecBool v = ((VecBoolObject *)vec)->vec; - VEC_INCREF(v); + VEC_BOOL_INCREF(v); v = VecBool_Append(v, x); if (VEC_IS_ERROR(v)) return NULL; return VecBool_Box(v); } else if (VecT_Check(vec)) { VecT v = ((VecTObject *)vec)->vec; - if (!VecT_ItemCheck(v, item, v.buf->item_type)) { + if (!VecT_ItemCheck(v, item, VEC_T_BUF(v)->item_type)) { return NULL; } - VEC_INCREF(v); - v = VecT_Append(v, item, v.buf->item_type); + VEC_T_INCREF(v); + v = VecT_Append(v, item, VEC_T_BUF(v)->item_type); if (VEC_IS_ERROR(v)) return NULL; - return VecT_Box(v, v.buf->item_type); + return VecT_Box(v, VEC_T_BUF(v)->item_type); } else if (VecNested_Check(vec)) { VecNested v = ((VecNestedObject *)vec)->vec; VecNestedBufItem vecitem; if (VecNested_UnboxItem(v, item, &vecitem) < 0) return NULL; - VEC_INCREF(v); + VEC_NESTED_INCREF(v); v = VecNested_Append(v, vecitem); if (VEC_IS_ERROR(v)) return NULL; @@ -667,7 +667,7 @@ static PyObject *vec_remove(PyObject *self, PyObject *args) return NULL; } VecI64 v = ((VecI64Object *)vec)->vec; - VEC_INCREF(v); + VEC_I64_INCREF(v); v = VecI64_Remove(v, x); if (VEC_IS_ERROR(v)) return NULL; @@ -678,7 +678,7 @@ static PyObject *vec_remove(PyObject *self, PyObject *args) return NULL; } VecU8 v = ((VecU8Object *)vec)->vec; - VEC_INCREF(v); + VEC_U8_INCREF(v); v = VecU8_Remove(v, x); if (VEC_IS_ERROR(v)) return NULL; @@ -689,7 +689,7 @@ static PyObject *vec_remove(PyObject *self, PyObject *args) return NULL; } VecFloat v = ((VecFloatObject *)vec)->vec; - VEC_INCREF(v); + VEC_FLOAT_INCREF(v); v = VecFloat_Remove(v, x); if (VEC_IS_ERROR(v)) return NULL; @@ -700,7 +700,7 @@ static PyObject *vec_remove(PyObject *self, PyObject *args) return NULL; } VecI32 v = ((VecI32Object *)vec)->vec; - VEC_INCREF(v); + VEC_I32_INCREF(v); v = VecI32_Remove(v, x); if (VEC_IS_ERROR(v)) return NULL; @@ -711,7 +711,7 @@ static PyObject *vec_remove(PyObject *self, PyObject *args) return NULL; } VecI16 v = ((VecI16Object *)vec)->vec; - VEC_INCREF(v); + VEC_I16_INCREF(v); v = VecI16_Remove(v, x); if (VEC_IS_ERROR(v)) return NULL; @@ -722,27 +722,27 @@ static PyObject *vec_remove(PyObject *self, PyObject *args) return NULL; } VecBool v = ((VecBoolObject *)vec)->vec; - VEC_INCREF(v); + VEC_BOOL_INCREF(v); v = VecBool_Remove(v, x); if (VEC_IS_ERROR(v)) return NULL; return VecBool_Box(v); } else if (VecT_Check(vec)) { VecT v = ((VecTObject *)vec)->vec; - if (!VecT_ItemCheck(v, item, v.buf->item_type)) { + if (!VecT_ItemCheck(v, item, VEC_T_BUF(v)->item_type)) { return NULL; } - VEC_INCREF(v); + VEC_T_INCREF(v); v = VecT_Remove(v, item); if (VEC_IS_ERROR(v)) return NULL; - return VecT_Box(v, v.buf->item_type); + return VecT_Box(v, VEC_T_BUF(v)->item_type); } else if (VecNested_Check(vec)) { VecNested v = ((VecNestedObject *)vec)->vec; VecNestedBufItem vecitem; if (VecNested_UnboxItem(v, item, &vecitem) < 0) return NULL; - VEC_INCREF(v); + VEC_NESTED_INCREF(v); v = VecNested_Remove(v, vecitem); if (VEC_IS_ERROR(v)) return NULL; @@ -767,7 +767,7 @@ static PyObject *vec_pop(PyObject *self, PyObject *args) if (VecI64_Check(vec)) { VecI64 v = ((VecI64Object *)vec)->vec; - VEC_INCREF(v); + VEC_I64_INCREF(v); VecI64PopResult r; r = VecI64_Pop(v, index); if (VEC_IS_ERROR(r.f0)) @@ -777,7 +777,7 @@ static PyObject *vec_pop(PyObject *self, PyObject *args) result_item1 = VecI64_BoxItem(r.f1); } else if (VecU8_Check(vec)) { VecU8 v = ((VecU8Object *)vec)->vec; - VEC_INCREF(v); + VEC_U8_INCREF(v); VecU8PopResult r; r = VecU8_Pop(v, index); if (VEC_IS_ERROR(r.f0)) @@ -787,7 +787,7 @@ static PyObject *vec_pop(PyObject *self, PyObject *args) result_item1 = VecU8_BoxItem(r.f1); } else if (VecFloat_Check(vec)) { VecFloat v = ((VecFloatObject *)vec)->vec; - VEC_INCREF(v); + VEC_FLOAT_INCREF(v); VecFloatPopResult r; r = VecFloat_Pop(v, index); if (VEC_IS_ERROR(r.f0)) @@ -797,7 +797,7 @@ static PyObject *vec_pop(PyObject *self, PyObject *args) result_item1 = VecFloat_BoxItem(r.f1); } else if (VecI32_Check(vec)) { VecI32 v = ((VecI32Object *)vec)->vec; - VEC_INCREF(v); + VEC_I32_INCREF(v); VecI32PopResult r; r = VecI32_Pop(v, index); if (VEC_IS_ERROR(r.f0)) @@ -807,7 +807,7 @@ static PyObject *vec_pop(PyObject *self, PyObject *args) result_item1 = VecI32_BoxItem(r.f1); } else if (VecI16_Check(vec)) { VecI16 v = ((VecI16Object *)vec)->vec; - VEC_INCREF(v); + VEC_I16_INCREF(v); VecI16PopResult r; r = VecI16_Pop(v, index); if (VEC_IS_ERROR(r.f0)) @@ -817,7 +817,7 @@ static PyObject *vec_pop(PyObject *self, PyObject *args) result_item1 = VecI16_BoxItem(r.f1); } else if (VecBool_Check(vec)) { VecBool v = ((VecBoolObject *)vec)->vec; - VEC_INCREF(v); + VEC_BOOL_INCREF(v); VecBoolPopResult r; r = VecBool_Pop(v, index); if (VEC_IS_ERROR(r.f0)) @@ -827,12 +827,12 @@ static PyObject *vec_pop(PyObject *self, PyObject *args) result_item1 = VecBool_BoxItem(r.f1); } else if (VecT_Check(vec)) { VecT v = ((VecTObject *)vec)->vec; - VEC_INCREF(v); + VEC_T_INCREF(v); VecTPopResult r; r = VecT_Pop(v, index); if (VEC_IS_ERROR(r.f0)) return NULL; - result_item0 = VecT_Box(r.f0, v.buf->item_type); + result_item0 = VecT_Box(r.f0, VEC_T_BUF(v)->item_type); if (result_item0 == NULL) { Py_DECREF(r.f1); return NULL; @@ -840,14 +840,14 @@ static PyObject *vec_pop(PyObject *self, PyObject *args) result_item1 = r.f1; } else if (VecNested_Check(vec)) { VecNested v = ((VecNestedObject *)vec)->vec; - VEC_INCREF(v); + VEC_NESTED_INCREF(v); VecNestedPopResult r; r = VecNested_Pop(v, index); if (VEC_IS_ERROR(r.f0)) return NULL; result_item0 = VecNested_Box(r.f0); if (result_item0 == NULL) { - Py_DECREF(r.f0.buf); + VEC_NESTED_DECREF(r.f0); Py_DECREF(r.f1.buf); return NULL; } @@ -890,57 +890,57 @@ static PyObject *vec_extend(PyObject *self, PyObject *args) if (VecI64_Check(vec)) { VecI64 v = ((VecI64Object *)vec)->vec; - VEC_INCREF(v); + VEC_I64_INCREF(v); v = VecI64_Extend(v, iterable); if (VEC_IS_ERROR(v)) return NULL; return VecI64_Box(v); } else if (VecU8_Check(vec)) { VecU8 v = ((VecU8Object *)vec)->vec; - VEC_INCREF(v); + VEC_U8_INCREF(v); v = VecU8_Extend(v, iterable); if (VEC_IS_ERROR(v)) return NULL; return VecU8_Box(v); } else if (VecFloat_Check(vec)) { VecFloat v = ((VecFloatObject *)vec)->vec; - VEC_INCREF(v); + VEC_FLOAT_INCREF(v); v = VecFloat_Extend(v, iterable); if (VEC_IS_ERROR(v)) return NULL; return VecFloat_Box(v); } else if (VecI32_Check(vec)) { VecI32 v = ((VecI32Object *)vec)->vec; - VEC_INCREF(v); + VEC_I32_INCREF(v); v = VecI32_Extend(v, iterable); if (VEC_IS_ERROR(v)) return NULL; return VecI32_Box(v); } else if (VecI16_Check(vec)) { VecI16 v = ((VecI16Object *)vec)->vec; - VEC_INCREF(v); + VEC_I16_INCREF(v); v = VecI16_Extend(v, iterable); if (VEC_IS_ERROR(v)) return NULL; return VecI16_Box(v); } else if (VecBool_Check(vec)) { VecBool v = ((VecBoolObject *)vec)->vec; - VEC_INCREF(v); + VEC_BOOL_INCREF(v); v = VecBool_Extend(v, iterable); if (VEC_IS_ERROR(v)) return NULL; return VecBool_Box(v); } else if (VecT_Check(vec)) { VecT v = ((VecTObject *)vec)->vec; - size_t item_type = v.buf->item_type; - VEC_INCREF(v); + size_t item_type = VEC_T_BUF(v)->item_type; + VEC_T_INCREF(v); v = VecT_Extend(v, iterable, item_type); if (VEC_IS_ERROR(v)) return NULL; return VecT_Box(v, item_type); } else if (VecNested_Check(vec)) { VecNested v = ((VecNestedObject *)vec)->vec; - VEC_INCREF(v); + VEC_NESTED_INCREF(v); v = VecNested_Extend(v, iterable); if (VEC_IS_ERROR(v)) return NULL; diff --git a/mypyc/lib-rt/vecs/librt_vecs.h b/mypyc/lib-rt/vecs/librt_vecs.h index 8636e88aa9d7..2cdf7206b565 100644 --- a/mypyc/lib-rt/vecs/librt_vecs.h +++ b/mypyc/lib-rt/vecs/librt_vecs.h @@ -154,6 +154,24 @@ typedef struct _VecNested { #define VEC_T_BUF(v) ((VecTBufObject *)((char *)(v).items - offsetof(VecTBufObject, items))) #define VEC_NESTED_BUF(v) ((VecNestedBufObject *)((char *)(v).items - offsetof(VecNestedBufObject, items))) +// Type-specific incref/decref. Safe when items may be NULL. +#define VEC_I64_INCREF(v) do { if ((v).items) Py_INCREF(VEC_I64_BUF(v)); } while (0) +#define VEC_I64_DECREF(v) do { if ((v).items) Py_DECREF(VEC_I64_BUF(v)); } while (0) +#define VEC_I32_INCREF(v) do { if ((v).items) Py_INCREF(VEC_I32_BUF(v)); } while (0) +#define VEC_I32_DECREF(v) do { if ((v).items) Py_DECREF(VEC_I32_BUF(v)); } while (0) +#define VEC_I16_INCREF(v) do { if ((v).items) Py_INCREF(VEC_I16_BUF(v)); } while (0) +#define VEC_I16_DECREF(v) do { if ((v).items) Py_DECREF(VEC_I16_BUF(v)); } while (0) +#define VEC_U8_INCREF(v) do { if ((v).items) Py_INCREF(VEC_U8_BUF(v)); } while (0) +#define VEC_U8_DECREF(v) do { if ((v).items) Py_DECREF(VEC_U8_BUF(v)); } while (0) +#define VEC_FLOAT_INCREF(v) do { if ((v).items) Py_INCREF(VEC_FLOAT_BUF(v)); } while (0) +#define VEC_FLOAT_DECREF(v) do { if ((v).items) Py_DECREF(VEC_FLOAT_BUF(v)); } while (0) +#define VEC_BOOL_INCREF(v) do { if ((v).items) Py_INCREF(VEC_BOOL_BUF(v)); } while (0) +#define VEC_BOOL_DECREF(v) do { if ((v).items) Py_DECREF(VEC_BOOL_BUF(v)); } while (0) +#define VEC_T_INCREF(v) do { if ((v).items) Py_INCREF(VEC_T_BUF(v)); } while (0) +#define VEC_T_DECREF(v) do { if ((v).items) Py_DECREF(VEC_T_BUF(v)); } while (0) +#define VEC_NESTED_INCREF(v) do { if ((v).items) Py_INCREF(VEC_NESTED_BUF(v)); } while (0) +#define VEC_NESTED_DECREF(v) do { if ((v).items) Py_DECREF(VEC_NESTED_BUF(v)); } while (0) + // Boxed vec objects @@ -478,10 +496,7 @@ typedef struct { #define VEC_BUF_SIZE(b) ((b)->ob_base.ob_size) #define VEC_ITEM_TYPE(t) ((PyTypeObject *)((t) & ~1)) #define VEC_BUF_ITEM_TYPE(b) VEC_ITEM_TYPE((b)->item_type) -#define VEC_CAP(v) ((v).buf->ob_base.ob_size) #define VEC_IS_ERROR(v) ((v).len < 0) -#define VEC_DECREF(v) Py_XDECREF((v).buf) -#define VEC_INCREF(v) Py_XINCREF((v).buf) // Type objects @@ -772,52 +787,54 @@ VecNestedPopResult VecNested_Pop(VecNested v, Py_ssize_t index); // Return 0 on success, -1 on error. Store unboxed item in *unboxed if successful. // Return a *borrowed* reference. static inline int VecNested_UnboxItem(VecNested v, PyObject *item, VecNestedBufItem *unboxed) { - size_t depth = v.buf->depth; + VecNestedBufObject *v_buf = VEC_NESTED_BUF(v); + size_t depth = v_buf->depth; if (depth == 1) { if (item->ob_type == &VecTType) { VecNestedObject *o = (VecNestedObject *)item; - if (o->vec.buf->item_type == v.buf->item_type) { + if (VEC_NESTED_BUF(o->vec)->item_type == v_buf->item_type) { unboxed->len = o->vec.len; - unboxed->buf = (PyObject *)o->vec.buf; + unboxed->buf = (PyObject *)VEC_NESTED_BUF(o->vec); return 0; } - } else if (item->ob_type == &VecI64Type && v.buf->item_type == VEC_ITEM_TYPE_I64) { + } else if (item->ob_type == &VecI64Type && v_buf->item_type == VEC_ITEM_TYPE_I64) { VecI64Object *o = (VecI64Object *)item; unboxed->len = o->vec.len; - unboxed->buf = (PyObject *)o->vec.buf; + unboxed->buf = o->vec.items ? (PyObject *)VEC_I64_BUF(o->vec) : NULL; return 0; - } else if (item->ob_type == &VecU8Type && v.buf->item_type == VEC_ITEM_TYPE_U8) { + } else if (item->ob_type == &VecU8Type && v_buf->item_type == VEC_ITEM_TYPE_U8) { VecU8Object *o = (VecU8Object *)item; unboxed->len = o->vec.len; - unboxed->buf = (PyObject *)o->vec.buf; + unboxed->buf = o->vec.items ? (PyObject *)VEC_U8_BUF(o->vec) : NULL; return 0; - } else if (item->ob_type == &VecFloatType && v.buf->item_type == VEC_ITEM_TYPE_FLOAT) { + } else if (item->ob_type == &VecFloatType && v_buf->item_type == VEC_ITEM_TYPE_FLOAT) { VecFloatObject *o = (VecFloatObject *)item; unboxed->len = o->vec.len; - unboxed->buf = (PyObject *)o->vec.buf; + unboxed->buf = o->vec.items ? (PyObject *)VEC_FLOAT_BUF(o->vec) : NULL; return 0; - } else if (item->ob_type == &VecI32Type && v.buf->item_type == VEC_ITEM_TYPE_I32) { + } else if (item->ob_type == &VecI32Type && v_buf->item_type == VEC_ITEM_TYPE_I32) { VecI32Object *o = (VecI32Object *)item; unboxed->len = o->vec.len; - unboxed->buf = (PyObject *)o->vec.buf; + unboxed->buf = o->vec.items ? (PyObject *)VEC_I32_BUF(o->vec) : NULL; return 0; - } else if (item->ob_type == &VecI16Type && v.buf->item_type == VEC_ITEM_TYPE_I16) { + } else if (item->ob_type == &VecI16Type && v_buf->item_type == VEC_ITEM_TYPE_I16) { VecI16Object *o = (VecI16Object *)item; unboxed->len = o->vec.len; - unboxed->buf = (PyObject *)o->vec.buf; + unboxed->buf = o->vec.items ? (PyObject *)VEC_I16_BUF(o->vec) : NULL; return 0; - } else if (item->ob_type == &VecBoolType && v.buf->item_type == VEC_ITEM_TYPE_BOOL) { + } else if (item->ob_type == &VecBoolType && v_buf->item_type == VEC_ITEM_TYPE_BOOL) { VecBoolObject *o = (VecBoolObject *)item; unboxed->len = o->vec.len; - unboxed->buf = (PyObject *)o->vec.buf; + unboxed->buf = o->vec.items ? (PyObject *)VEC_BOOL_BUF(o->vec) : NULL; return 0; } } else if (item->ob_type == &VecNestedType) { VecNestedObject *o = (VecNestedObject *)item; - if (o->vec.buf->depth == v.buf->depth - 1 - && o->vec.buf->item_type == v.buf->item_type) { + VecNestedBufObject *o_buf = VEC_NESTED_BUF(o->vec); + if (o_buf->depth == v_buf->depth - 1 + && o_buf->item_type == v_buf->item_type) { unboxed->len = o->vec.len; - unboxed->buf = (PyObject *)o->vec.buf; + unboxed->buf = (PyObject *)o_buf; return 0; } } @@ -830,35 +847,44 @@ static inline PyObject *VecNested_BoxItem(VecNested v, VecNestedBufItem item) { if (item.len < 0) Py_RETURN_NONE; Py_XINCREF(item.buf); - if (v.buf->depth > 1) { + VecNestedBufObject *v_buf = VEC_NESTED_BUF(v); + if (v_buf->depth > 1) { // Item is a nested vec - VecNested v = { .len = item.len, .buf = (VecNestedBufObject *)item.buf }; - return VecNested_Box(v); + VecNested iv = { .len = item.len, + .items = item.buf ? ((VecNestedBufObject *)item.buf)->items : NULL }; + return VecNested_Box(iv); } else { // Item is a non-nested vec - size_t item_type = v.buf->item_type; + size_t item_type = v_buf->item_type; if (item_type == VEC_ITEM_TYPE_I64) { - VecI64 v = { .len = item.len, .buf = (VecI64BufObject *)item.buf }; - return VecI64_Box(v); + VecI64 iv = { .len = item.len, + .items = item.buf ? ((VecI64BufObject *)item.buf)->items : NULL }; + return VecI64_Box(iv); } else if (item_type == VEC_ITEM_TYPE_U8) { - VecU8 v = { .len = item.len, .buf = (VecU8BufObject *)item.buf }; - return VecU8_Box(v); + VecU8 iv = { .len = item.len, + .items = item.buf ? ((VecU8BufObject *)item.buf)->items : NULL }; + return VecU8_Box(iv); } else if (item_type == VEC_ITEM_TYPE_FLOAT) { - VecFloat v = { .len = item.len, .buf = (VecFloatBufObject *)item.buf }; - return VecFloat_Box(v); + VecFloat iv = { .len = item.len, + .items = item.buf ? ((VecFloatBufObject *)item.buf)->items : NULL }; + return VecFloat_Box(iv); } else if (item_type == VEC_ITEM_TYPE_I32) { - VecI32 v = { .len = item.len, .buf = (VecI32BufObject *)item.buf }; - return VecI32_Box(v); + VecI32 iv = { .len = item.len, + .items = item.buf ? ((VecI32BufObject *)item.buf)->items : NULL }; + return VecI32_Box(iv); } else if (item_type == VEC_ITEM_TYPE_I16) { - VecI16 v = { .len = item.len, .buf = (VecI16BufObject *)item.buf }; - return VecI16_Box(v); + VecI16 iv = { .len = item.len, + .items = item.buf ? ((VecI16BufObject *)item.buf)->items : NULL }; + return VecI16_Box(iv); } else if (item_type == VEC_ITEM_TYPE_BOOL) { - VecBool v = { .len = item.len, .buf = (VecBoolBufObject *)item.buf }; - return VecBool_Box(v); + VecBool iv = { .len = item.len, + .items = item.buf ? ((VecBoolBufObject *)item.buf)->items : NULL }; + return VecBool_Box(iv); } else { // Generic vec[t] - VecT v = { .len = item.len, .buf = (VecTBufObject *)item.buf }; - return VecT_Box(v, item_type); + VecT iv = { .len = item.len, + .items = item.buf ? ((VecTBufObject *)item.buf)->items : NULL }; + return VecT_Box(iv, item_type); } } } diff --git a/mypyc/lib-rt/vecs/vec_nested.c b/mypyc/lib-rt/vecs/vec_nested.c index 2f4a16c12a31..72976777e820 100644 --- a/mypyc/lib-rt/vecs/vec_nested.c +++ b/mypyc/lib-rt/vecs/vec_nested.c @@ -11,21 +11,26 @@ #include "librt_vecs.h" #include "vecs_internal.h" +#define VEC_BUF(v) ((VecNestedBufObject *)((char *)(v).items - offsetof(VecNestedBufObject, items))) +#define VEC_CAP(v) (VEC_BUF(v)->ob_base.ob_size) +#define VEC_INCREF(v) do { if ((v).items) Py_INCREF(VEC_BUF(v)); } while (0) +#define VEC_DECREF(v) do { if ((v).items) Py_DECREF(VEC_BUF(v)); } while (0) + static inline VecNested vec_error() { VecNested v = { .len = -1 }; return v; } static inline void vec_track_buffer(VecNested *vec) { - PyObject_GC_Track(vec->buf); + PyObject_GC_Track(VEC_BUF(*vec)); } static inline PyObject *box_vec_item_by_index(VecNested v, Py_ssize_t index) { - return VecNested_BoxItem(v, v.buf->items[index]); + return VecNested_BoxItem(v, v.items[index]); } // Alloc a partially initialized vec. If size > 0, caller *must* immediately initialize len, -// and buf->items. Caller *must* also call vec_track_buffer on the returned vec but only +// and items. Caller *must* also call vec_track_buffer on the returned vec but only // after initializing the items. static VecNested vec_alloc(Py_ssize_t size, size_t item_type, size_t depth) { VecNestedBufObject *buf = PyObject_GC_NewVar(VecNestedBufObject, &VecNestedBufType, size); @@ -35,7 +40,7 @@ static VecNested vec_alloc(Py_ssize_t size, size_t item_type, size_t depth) { buf->depth = depth; if (!Vec_IsMagicItemType(item_type)) Py_INCREF(VEC_BUF_ITEM_TYPE(buf)); - VecNested res = { .buf = buf }; + VecNested res = { .items = buf->items }; return res; } @@ -54,7 +59,8 @@ PyObject *VecNested_Box(VecNested vec) { VecNested VecNested_Unbox(PyObject *obj, size_t item_type, size_t depth) { if (obj->ob_type == &VecNestedType) { VecNested result = ((VecNestedObject *)obj)->vec; - if (result.buf->item_type == item_type && result.buf->depth == depth) { + VecNestedBufObject *buf = VEC_BUF(result); + if (buf->item_type == item_type && buf->depth == depth) { VEC_INCREF(result); // TODO: Should we borrow instead? return result; } @@ -65,7 +71,7 @@ VecNested VecNested_Unbox(PyObject *obj, size_t item_type, size_t depth) { } VecNested VecNested_ConvertFromNested(VecNestedBufItem item) { - return (VecNested) { item.len, (VecNestedBufObject *)item.buf }; + return (VecNested) { item.len, item.buf ? ((VecNestedBufObject *)item.buf)->items : NULL }; } VecNested VecNested_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type, size_t depth) { @@ -79,8 +85,8 @@ VecNested VecNested_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type, size_ if (VEC_IS_ERROR(vec)) return vec; for (Py_ssize_t i = 0; i < cap; i++) { - vec.buf->items[i].len = -1; - vec.buf->items[i].buf = NULL; + vec.items[i].len = -1; + vec.items[i].buf = NULL; } vec.len = size; vec_track_buffer(&vec); @@ -89,7 +95,8 @@ VecNested VecNested_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type, size_ static PyObject *vec_repr(PyObject *self) { VecNested v = ((VecNestedObject *)self)->vec; - return Vec_GenericRepr(self, v.buf->item_type, v.buf->depth, 1); + VecNestedBufObject *buf = VEC_BUF(v); + return Vec_GenericRepr(self, buf->item_type, buf->depth, 1); } static PyObject *vec_get_item(PyObject *o, Py_ssize_t i) { @@ -118,14 +125,15 @@ VecNested VecNested_Slice(VecNested vec, int64_t start, int64_t end) { if (end > vec.len) end = vec.len; int64_t slicelength = end - start; - VecNested res = vec_alloc(slicelength, vec.buf->item_type, vec.buf->depth); + VecNestedBufObject *vec_buf = VEC_BUF(vec); + VecNested res = vec_alloc(slicelength, vec_buf->item_type, vec_buf->depth); if (VEC_IS_ERROR(res)) return res; res.len = slicelength; for (Py_ssize_t i = 0; i < slicelength; i++) { - VecNestedBufItem item = vec.buf->items[start + i]; + VecNestedBufItem item = vec.items[start + i]; Py_XINCREF(item.buf); - res.buf->items[i] = item; + res.items[i] = item; } vec_track_buffer(&res); return res; @@ -150,15 +158,16 @@ static PyObject *vec_subscript(PyObject *self, PyObject *item) { if (PySlice_Unpack(item, &start, &stop, &step) < 0) return NULL; Py_ssize_t slicelength = PySlice_AdjustIndices(vec.len, &start, &stop, step); - VecNested res = vec_alloc(slicelength, vec.buf->item_type, vec.buf->depth); + VecNestedBufObject *vec_buf = VEC_BUF(vec); + VecNested res = vec_alloc(slicelength, vec_buf->item_type, vec_buf->depth); if (VEC_IS_ERROR(res)) return NULL; res.len = slicelength; Py_ssize_t j = start; for (Py_ssize_t i = 0; i < slicelength; i++) { - VecNestedBufItem item = vec.buf->items[j]; + VecNestedBufItem item = vec.items[j]; Py_INCREF(item.buf); - res.buf->items[i] = item; + res.items[i] = item; j += step; } vec_track_buffer(&res); @@ -179,9 +188,9 @@ static int vec_ass_item(PyObject *self, Py_ssize_t i, PyObject *o) { VecNestedBufItem item; if (VecNested_UnboxItem(v, o, &item) < 0) return -1; - VEC_INCREF(item); - VEC_DECREF(v.buf->items[i]); - v.buf->items[i] = item; + Py_XINCREF(item.buf); + Py_XDECREF(v.items[i].buf); + v.items[i] = item; return 0; } else { PyErr_SetString(PyExc_IndexError, "index out of range"); @@ -210,9 +219,11 @@ static int vec_contains(PyObject *self, PyObject *value) { static PyObject *compare_vec_eq(VecNested x, VecNested y, int op) { int cmp = 1; PyObject *res; + VecNestedBufObject *x_buf = VEC_BUF(x); + VecNestedBufObject *y_buf = VEC_BUF(y); if (x.len != y.len - || x.buf->item_type != y.buf->item_type - || x.buf->depth != y.buf->depth) { + || x_buf->item_type != y_buf->item_type + || x_buf->depth != y_buf->depth) { cmp = 0; } else { for (Py_ssize_t i = 0; i < x.len; i++) { @@ -254,38 +265,39 @@ PyObject *vec_richcompare(PyObject *self, PyObject *other, int op) { // Append item to 'vec', stealing 'vec'. Return 'vec' with item appended. VecNested VecNested_Append(VecNested vec, VecNestedBufItem x) { Py_ssize_t cap = VEC_CAP(vec); - VEC_INCREF(x); + Py_XINCREF(x.buf); if (vec.len < cap) { // Slot may have duplicate ref from prior remove/pop - Py_XDECREF(vec.buf->items[vec.len].buf); - vec.buf->items[vec.len] = x; + Py_XDECREF(vec.items[vec.len].buf); + vec.items[vec.len] = x; vec.len++; return vec; } else { Py_ssize_t new_size = Vec_GrowCapacity(cap); // TODO: Avoid initializing to zero here - VecNested new = vec_alloc(new_size, vec.buf->item_type, vec.buf->depth); + VecNestedBufObject *vec_buf = VEC_BUF(vec); + VecNested new = vec_alloc(new_size, vec_buf->item_type, vec_buf->depth); if (VEC_IS_ERROR(new)) { - VEC_DECREF(x); + Py_XDECREF(x.buf); // The input vec is being consumed/stolen by this function, so on error // we must decref it to avoid leaking the buffer. VEC_DECREF(vec); return new; } // Copy items to new vec. - memcpy(new.buf->items, vec.buf->items, sizeof(VecNestedBufItem) * vec.len); + memcpy(new.items, vec.items, sizeof(VecNestedBufItem) * vec.len); // TODO: How to safely represent deleted items? - memset(new.buf->items + vec.len, 0, sizeof(VecNestedBufItem) * (new_size - vec.len)); - if (Py_REFCNT(vec.buf) > 1) { + memset(new.items + vec.len, 0, sizeof(VecNestedBufItem) * (new_size - vec.len)); + if (Py_REFCNT(vec_buf) > 1) { // Other references to old buffer exist; INCREF items in new buffer // so old buffer keeps valid references for aliases. for (Py_ssize_t i = 0; i < vec.len; i++) - Py_XINCREF(new.buf->items[i].buf); + Py_XINCREF(new.items[i].buf); } else { // No aliases; transfer ownership by clearing old buffer items. - memset(vec.buf->items, 0, sizeof(VecNestedBufItem) * vec.len); + memset(vec.items, 0, sizeof(VecNestedBufItem) * vec.len); } - new.buf->items[vec.len] = x; + new.items[vec.len] = x; new.len = vec.len + 1; vec_track_buffer(&new); VEC_DECREF(vec); @@ -298,7 +310,9 @@ VecNested VecNested_Append(VecNested vec, VecNestedBufItem x) { VecNested VecNested_Extend(VecNested vec, PyObject *iterable) { if (VecNested_Check(iterable)) { VecNested src = ((VecNestedObject *)iterable)->vec; - if (src.buf->item_type == vec.buf->item_type && src.buf->depth == vec.buf->depth) { + VecNestedBufObject *vec_buf = VEC_BUF(vec); + VecNestedBufObject *src_buf = VEC_BUF(src); + if (src_buf->item_type == vec_buf->item_type && src_buf->depth == vec_buf->depth) { return VecNested_ExtendVec(vec, src); } } @@ -345,22 +359,23 @@ VecNested VecNested_ExtendVec(VecNested dst, VecNested src) { Py_ssize_t new_len = dst.len + src.len; // VecNested buf is never NULL (even for empty vecs), so no NULL guard needed Py_ssize_t cap = VEC_CAP(dst); - if (new_len <= cap && dst.buf != src.buf) { + if (new_len <= cap && dst.items != src.items) { // Fast path: enough capacity and no aliasing for (Py_ssize_t i = 0; i < src.len; i++) { - VecNestedBufItem item = src.buf->items[i]; + VecNestedBufItem item = src.items[i]; Py_XINCREF(item.buf); // Slot may have duplicate ref from prior remove/pop - Py_XDECREF(dst.buf->items[dst.len + i].buf); - dst.buf->items[dst.len + i] = item; + Py_XDECREF(dst.items[dst.len + i].buf); + dst.items[dst.len + i] = item; } dst.len = new_len; return dst; } // Need to reallocate (or dst and src share a buffer) Py_ssize_t new_cap = Vec_GrowCapacityTo(cap, new_len); - int aliased = dst.buf == src.buf; - VecNested new = vec_alloc(new_cap, dst.buf->item_type, dst.buf->depth); + int aliased = dst.items == src.items; + VecNestedBufObject *dst_buf = VEC_BUF(dst); + VecNested new = vec_alloc(new_cap, dst_buf->item_type, dst_buf->depth); if (VEC_IS_ERROR(new)) { VEC_DECREF(dst); return new; @@ -369,25 +384,25 @@ VecNested VecNested_ExtendVec(VecNested dst, VecNested src) { // dst and src share a buffer -- incref all items instead of // moving refs, to avoid mutating the shared buffer for (Py_ssize_t i = 0; i < dst.len; i++) { - Py_XINCREF(dst.buf->items[i].buf); - new.buf->items[i] = dst.buf->items[i]; + Py_XINCREF(dst.items[i].buf); + new.items[i] = dst.items[i]; } } else { - memcpy(new.buf->items, dst.buf->items, sizeof(VecNestedBufItem) * dst.len); - if (Py_REFCNT(dst.buf) > 1) { + memcpy(new.items, dst.items, sizeof(VecNestedBufItem) * dst.len); + if (Py_REFCNT(dst_buf) > 1) { for (Py_ssize_t i = 0; i < dst.len; i++) - Py_XINCREF(new.buf->items[i].buf); + Py_XINCREF(new.items[i].buf); } else { - memset(dst.buf->items, 0, sizeof(VecNestedBufItem) * dst.len); + memset(dst.items, 0, sizeof(VecNestedBufItem) * dst.len); } } // Copy src items (incref each buf) for (Py_ssize_t i = 0; i < src.len; i++) { - VecNestedBufItem item = src.buf->items[i]; + VecNestedBufItem item = src.items[i]; Py_XINCREF(item.buf); - new.buf->items[dst.len + i] = item; + new.items[dst.len + i] = item; } - memset(new.buf->items + new_len, 0, sizeof(VecNestedBufItem) * (new_cap - new_len)); + memset(new.items + new_len, 0, sizeof(VecNestedBufItem) * (new_cap - new_len)); new.len = new_len; vec_track_buffer(&new); VEC_DECREF(dst); @@ -396,7 +411,7 @@ VecNested VecNested_ExtendVec(VecNested dst, VecNested src) { // Remove item from 'vec', stealing 'vec'. Return 'vec' with item removed. VecNested VecNested_Remove(VecNested self, VecNestedBufItem arg) { - VecNestedBufItem *items = self.buf->items; + VecNestedBufItem *items = self.items; PyObject *boxed_arg = VecNested_BoxItem(self, arg); if (boxed_arg == NULL) { @@ -470,7 +485,7 @@ VecNestedPopResult VecNested_Pop(VecNested v, Py_ssize_t index) { return result; } - VecNestedBufItem *items = v.buf->items; + VecNestedBufItem *items = v.items; result.f1 = items[index]; for (Py_ssize_t i = index; i < v.len - 1; i++) items[i] = items[i + 1]; @@ -485,14 +500,18 @@ VecNestedPopResult VecNested_Pop(VecNested v, Py_ssize_t index) { static int VecNested_traverse(VecNestedObject *self, visitproc visit, void *arg) { - Py_VISIT(self->vec.buf); + if (self->vec.items) + Py_VISIT(VEC_BUF(self->vec)); return 0; } static int VecNested_clear(VecNestedObject *self) { - Py_CLEAR(self->vec.buf); + if (self->vec.items) { + Py_DECREF(VEC_BUF(self->vec)); + self->vec.items = NULL; + } return 0; } @@ -501,7 +520,10 @@ VecNested_dealloc(VecNestedObject *self) { PyObject_GC_UnTrack(self); Py_TRASHCAN_BEGIN(self, VecNested_dealloc) - Py_CLEAR(self->vec.buf); + if (self->vec.items) { + Py_DECREF(VEC_BUF(self->vec)); + self->vec.items = NULL; + } Py_TYPE(self)->tp_free((PyObject *)self); Py_TRASHCAN_END } @@ -563,7 +585,7 @@ static PyMethodDef vec_methods[] = { typedef struct { PyObject_HEAD - VecNested vec; // Unboxed vec (keeps buffer alive via buf reference) + VecNested vec; // Unboxed vec (keeps buffer alive via items reference) Py_ssize_t index; // Current iteration index } VecNestedIterObject; @@ -574,7 +596,7 @@ static PyObject *VecNested_iter(PyObject *self) { if (it == NULL) return NULL; it->vec = ((VecNestedObject *)self)->vec; - Py_INCREF(it->vec.buf); + VEC_INCREF(it->vec); it->index = 0; PyObject_GC_Track(it); return (PyObject *)it; @@ -583,25 +605,29 @@ static PyObject *VecNested_iter(PyObject *self) { static int VecNestedIter_traverse(VecNestedIterObject *self, visitproc visit, void *arg) { - Py_VISIT(self->vec.buf); + if (self->vec.items) + Py_VISIT(VEC_BUF(self->vec)); return 0; } static int VecNestedIter_clear(VecNestedIterObject *self) { - Py_CLEAR(self->vec.buf); + if (self->vec.items) { + Py_DECREF(VEC_BUF(self->vec)); + self->vec.items = NULL; + } return 0; } static void VecNestedIter_dealloc(VecNestedIterObject *self) { PyObject_GC_UnTrack(self); - Py_XDECREF(self->vec.buf); + VEC_DECREF(self->vec); PyObject_GC_Del(self); } static PyObject *VecNestedIter_next(VecNestedIterObject *self) { - if (self->vec.buf == NULL) + if (self->vec.items == NULL) return NULL; if (self->index < self->vec.len) { PyObject *item = box_vec_item_by_index(self->vec, self->index); @@ -610,12 +636,13 @@ static PyObject *VecNestedIter_next(VecNestedIterObject *self) { self->index++; return item; } - Py_CLEAR(self->vec.buf); + VEC_DECREF(self->vec); + self->vec.items = NULL; return NULL; // StopIteration } static PyObject *VecNestedIter_len(VecNestedIterObject *self, PyObject *Py_UNUSED(ignored)) { - if (self->vec.buf == NULL) + if (self->vec.items == NULL) return PyLong_FromSsize_t(0); Py_ssize_t remaining = self->vec.len - self->index; if (remaining < 0) @@ -683,8 +710,8 @@ PyObject *VecNested_FromIterable(size_t item_type, size_t depth, PyObject *itera return NULL; if (cap > 0) { for (int64_t i = 0; i < cap; i++) { - v.buf->items[i].len = -1; - v.buf->items[i].buf = NULL; + v.items[i].len = -1; + v.items[i].buf = NULL; } } v.len = 0; diff --git a/mypyc/lib-rt/vecs/vec_t.c b/mypyc/lib-rt/vecs/vec_t.c index b40c37b7a45e..3284a073c591 100644 --- a/mypyc/lib-rt/vecs/vec_t.c +++ b/mypyc/lib-rt/vecs/vec_t.c @@ -13,6 +13,11 @@ #include "librt_vecs.h" #include "vecs_internal.h" +#define VEC_BUF(v) ((VecTBufObject *)((char *)(v).items - offsetof(VecTBufObject, items))) +#define VEC_CAP(v) (VEC_BUF(v)->ob_base.ob_size) +#define VEC_INCREF(v) do { if ((v).items) Py_INCREF(VEC_BUF(v)); } while (0) +#define VEC_DECREF(v) do { if ((v).items) Py_DECREF(VEC_BUF(v)); } while (0) + static inline VecT vec_error() { VecT v = { .len = -1 }; return v; @@ -28,13 +33,13 @@ static inline VecTBufObject *alloc_buf(Py_ssize_t size, size_t item_type) { } static inline void vec_track_buffer(VecT *vec) { - if (vec->buf != NULL) { - PyObject_GC_Track(vec->buf); + if (vec->items != NULL) { + PyObject_GC_Track(VEC_T_BUF(*vec)); } } // Alloc a partially initialized vec. If size > 0, caller *must* immediately initialize len, -// and buf->items. Caller *must* also call vec_track_buffer on the returned vec but only +// and items. Caller *must* also call vec_track_buffer on the returned vec but only // after initializing the items. static VecT vec_alloc(Py_ssize_t size, size_t item_type) { VecTBufObject *buf; @@ -46,23 +51,24 @@ static VecT vec_alloc(Py_ssize_t size, size_t item_type) { if (buf == NULL) return vec_error(); } - return (VecT) { .buf = buf }; + return (VecT) { .items = (buf != NULL) ? buf->items : NULL }; } // Box a VecT value, stealing 'vec'. On failure, return NULL and decref 'vec'. PyObject *VecT_Box(VecT vec, size_t item_type) { - // An unboxed empty vec may have a NULL buf, but a boxed vec must have it + // An unboxed empty vec may have NULL items, but a boxed vec must have a buf // allocated, since it contains the item type - if (vec.buf == NULL) { - vec.buf = alloc_buf(0, item_type); - if (vec.buf == NULL) + if (vec.items == NULL) { + VecTBufObject *buf = alloc_buf(0, item_type); + if (buf == NULL) return NULL; + vec.items = buf->items; vec_track_buffer(&vec); } VecTObject *obj = PyObject_GC_New(VecTObject, &VecTType); if (obj == NULL) { - // vec.buf is always defined, so no need for a NULL check - Py_DECREF(vec.buf); + // items is always defined, so no need for a NULL check + Py_DECREF(VEC_BUF(vec)); return NULL; } obj->vec = vec; @@ -73,7 +79,7 @@ PyObject *VecT_Box(VecT vec, size_t item_type) { VecT VecT_Unbox(PyObject *obj, size_t item_type) { if (obj->ob_type == &VecTType) { VecT result = ((VecTObject *)obj)->vec; - if (result.buf->item_type == item_type) { + if (VEC_BUF(result)->item_type == item_type) { VEC_INCREF(result); // TODO: Should we borrow instead? return result; } @@ -84,7 +90,7 @@ VecT VecT_Unbox(PyObject *obj, size_t item_type) { } VecT VecT_ConvertFromNested(VecNestedBufItem item) { - return (VecT) { item.len, (VecTBufObject *)item.buf }; + return (VecT) { item.len, item.buf ? ((VecTBufObject *)item.buf)->items : NULL }; } VecT VecT_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type) { @@ -98,7 +104,7 @@ VecT VecT_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type) { if (VEC_IS_ERROR(vec)) return vec; for (Py_ssize_t i = 0; i < cap; i++) { - vec.buf->items[i] = NULL; + vec.items[i] = NULL; } vec_track_buffer(&vec); vec.len = size; @@ -107,17 +113,17 @@ VecT VecT_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type) { static PyObject *vec_repr(PyObject *self) { VecTObject *v = (VecTObject *)self; - return Vec_GenericRepr(self, v->vec.buf->item_type, 0, 1); + return Vec_GenericRepr(self, VEC_BUF(v->vec)->item_type, 0, 1); } static PyObject *vec_get_item(PyObject *o, Py_ssize_t i) { VecT v = ((VecTObject *)o)->vec; if ((size_t)i < (size_t)v.len) { - PyObject *item = v.buf->items[i]; + PyObject *item = v.items[i]; Py_INCREF(item); return item; } else if ((size_t)i + (size_t)v.len < (size_t)v.len) { - PyObject *item = v.buf->items[i + v.len]; + PyObject *item = v.items[i + v.len]; Py_INCREF(item); return item; } else { @@ -141,15 +147,15 @@ VecT VecT_Slice(VecT vec, int64_t start, int64_t end) { end = vec.len; int64_t slicelength = end - start; if (slicelength == 0) - return (VecT) { .len = 0, .buf = NULL }; - VecT res = vec_alloc(slicelength, vec.buf->item_type); + return (VecT) { .len = 0, .items = NULL }; + VecT res = vec_alloc(slicelength, VEC_BUF(vec)->item_type); if (VEC_IS_ERROR(res)) return res; res.len = slicelength; for (Py_ssize_t i = 0; i < slicelength; i++) { - PyObject *item = vec.buf->items[start + i]; + PyObject *item = vec.items[start + i]; Py_INCREF(item); - res.buf->items[i] = item; + res.items[i] = item; } vec_track_buffer(&res); return res; @@ -162,11 +168,11 @@ static PyObject *vec_subscript(PyObject *self, PyObject *item) { if (i == -1 && PyErr_Occurred()) return NULL; if ((size_t)i < (size_t)vec.len) { - PyObject *result = vec.buf->items[i]; + PyObject *result = vec.items[i]; Py_INCREF(result); return result; } else if ((size_t)i + (size_t)vec.len < (size_t)vec.len) { - PyObject *result = vec.buf->items[i + vec.len]; + PyObject *result = vec.items[i + vec.len]; Py_INCREF(result); return result; } else { @@ -178,19 +184,19 @@ static PyObject *vec_subscript(PyObject *self, PyObject *item) { if (PySlice_Unpack(item, &start, &stop, &step) < 0) return NULL; Py_ssize_t slicelength = PySlice_AdjustIndices(vec.len, &start, &stop, step); - VecT res = vec_alloc(slicelength, vec.buf->item_type); + VecT res = vec_alloc(slicelength, VEC_BUF(vec)->item_type); if (VEC_IS_ERROR(res)) return NULL; res.len = slicelength; Py_ssize_t j = start; for (Py_ssize_t i = 0; i < slicelength; i++) { - PyObject *item = vec.buf->items[j]; + PyObject *item = vec.items[j]; Py_INCREF(item); - res.buf->items[i] = item; + res.items[i] = item; j += step; } vec_track_buffer(&res); - PyObject *result = VecT_Box(res, vec.buf->item_type); + PyObject *result = VecT_Box(res, VEC_BUF(vec)->item_type); if (result == NULL) { VEC_DECREF(res); } @@ -204,18 +210,18 @@ static PyObject *vec_subscript(PyObject *self, PyObject *item) { static int vec_ass_item(PyObject *self, Py_ssize_t i, PyObject *o) { VecT v = ((VecTObject *)self)->vec; - if (!VecT_ItemCheck(v, o, v.buf->item_type)) + if (!VecT_ItemCheck(v, o, VEC_BUF(v)->item_type)) return -1; if ((size_t)i < (size_t)v.len) { - PyObject *old = v.buf->items[i]; + PyObject *old = v.items[i]; Py_INCREF(o); - v.buf->items[i] = o; + v.items[i] = o; Py_XDECREF(old); return 0; } else if ((size_t)i + (size_t)v.len < (size_t)v.len) { - PyObject *old = v.buf->items[i + v.len]; + PyObject *old = v.items[i + v.len]; Py_INCREF(o); - v.buf->items[i + v.len] = o; + v.items[i + v.len] = o; Py_XDECREF(old); return 0; } else { @@ -227,7 +233,7 @@ static int vec_ass_item(PyObject *self, Py_ssize_t i, PyObject *o) { static int vec_contains(PyObject *self, PyObject *value) { VecT v = ((VecTObject *)self)->vec; for (Py_ssize_t i = 0; i < v.len; i++) { - PyObject *item = v.buf->items[i]; + PyObject *item = v.items[i]; if (item == value) { return 1; } @@ -248,11 +254,11 @@ static PyObject *vec_richcompare(PyObject *self, PyObject *other, int op) { } else { VecT x = ((VecTObject *)self)->vec; VecT y = ((VecTObject *)other)->vec; - if (x.buf->item_type != y.buf->item_type) { + if (VEC_BUF(x)->item_type != VEC_BUF(y)->item_type) { res = op == Py_EQ ? Py_False : Py_True; } else { // TODO: why pointers to len? - return Vec_GenericRichcompare(&x.len, x.buf->items, &y.len, y.buf->items, op); + return Vec_GenericRichcompare(&x.len, x.items, &y.len, y.items, op); } } } else @@ -263,13 +269,13 @@ static PyObject *vec_richcompare(PyObject *self, PyObject *other, int op) { // Append item to 'vec', stealing 'vec'. Return 'vec' with item appended. VecT VecT_Append(VecT vec, PyObject *x, size_t item_type) { - if (vec.buf == NULL) { + if (vec.items == NULL) { VecT new = vec_alloc(1, item_type); if (VEC_IS_ERROR(new)) return new; Py_INCREF(x); new.len = 1; - new.buf->items[0] = x; + new.items[0] = x; vec_track_buffer(&new); return new; } @@ -277,13 +283,13 @@ VecT VecT_Append(VecT vec, PyObject *x, size_t item_type) { Py_INCREF(x); if (vec.len < cap) { // Slot may have duplicate ref from prior remove/pop - Py_XSETREF(vec.buf->items[vec.len], x); + Py_XSETREF(vec.items[vec.len], x); vec.len++; return vec; } else { Py_ssize_t new_size = Vec_GrowCapacity(cap); // TODO: Avoid initializing to zero here - VecT new = vec_alloc(new_size, vec.buf->item_type); + VecT new = vec_alloc(new_size, VEC_BUF(vec)->item_type); if (VEC_IS_ERROR(new)) { Py_DECREF(x); // The input vec is being consumed/stolen by this function, so on error @@ -292,18 +298,19 @@ VecT VecT_Append(VecT vec, PyObject *x, size_t item_type) { return new; } // Copy items to new vec. - memcpy(new.buf->items, vec.buf->items, sizeof(PyObject *) * vec.len); - memset(new.buf->items + vec.len, 0, sizeof(PyObject *) * (new_size - vec.len)); - if (Py_REFCNT(vec.buf) > 1) { + memcpy(new.items, vec.items, sizeof(PyObject *) * vec.len); + memset(new.items + vec.len, 0, sizeof(PyObject *) * (new_size - vec.len)); + VecTBufObject *old_buf = VEC_BUF(vec); + if (Py_REFCNT(old_buf) > 1) { // Other references to old buffer exist; INCREF items in new buffer // so old buffer keeps valid references for aliases. for (Py_ssize_t i = 0; i < vec.len; i++) - Py_XINCREF(new.buf->items[i]); + Py_XINCREF(new.items[i]); } else { // No aliases; transfer ownership by clearing old buffer items. - memset(vec.buf->items, 0, sizeof(PyObject *) * vec.len); + memset(vec.items, 0, sizeof(PyObject *) * vec.len); } - new.buf->items[vec.len] = x; + new.items[vec.len] = x; new.len = vec.len + 1; vec_track_buffer(&new); VEC_DECREF(vec); @@ -316,7 +323,7 @@ VecT VecT_Append(VecT vec, PyObject *x, size_t item_type) { VecT VecT_Extend(VecT vec, PyObject *iterable, size_t item_type) { if (VecT_Check(iterable)) { VecT src = ((VecTObject *)iterable)->vec; - if (src.buf != NULL && src.buf->item_type == item_type) { + if (src.items != NULL && VEC_BUF(src)->item_type == item_type) { return VecT_ExtendVec(vec, src, item_type); } } @@ -360,7 +367,7 @@ VecT VecT_ExtendVec(VecT dst, VecT src, size_t item_type) { return vec_error(); } Py_ssize_t new_len = dst.len + src.len; - if (dst.buf == NULL) { + if (dst.items == NULL) { // dst is empty, allocate new buf VecT new = vec_alloc(new_len, item_type); if (VEC_IS_ERROR(new)) { @@ -368,29 +375,29 @@ VecT VecT_ExtendVec(VecT dst, VecT src, size_t item_type) { return new; } for (Py_ssize_t i = 0; i < src.len; i++) { - Py_INCREF(src.buf->items[i]); - new.buf->items[i] = src.buf->items[i]; + Py_INCREF(src.items[i]); + new.items[i] = src.items[i]; } - memset(new.buf->items + src.len, 0, sizeof(PyObject *) * (new_len - src.len)); + memset(new.items + src.len, 0, sizeof(PyObject *) * (new_len - src.len)); new.len = new_len; vec_track_buffer(&new); return new; } Py_ssize_t cap = VEC_CAP(dst); - if (new_len <= cap && dst.buf != src.buf) { + if (new_len <= cap && dst.items != src.items) { // Fast path: enough capacity and no aliasing for (Py_ssize_t i = 0; i < src.len; i++) { - Py_INCREF(src.buf->items[i]); + Py_INCREF(src.items[i]); // Slot may have duplicate ref from prior remove/pop - Py_XSETREF(dst.buf->items[dst.len + i], src.buf->items[i]); + Py_XSETREF(dst.items[dst.len + i], src.items[i]); } dst.len = new_len; return dst; } // Need to reallocate (or dst and src share a buffer) Py_ssize_t new_cap = Vec_GrowCapacityTo(cap, new_len); - int aliased = dst.buf == src.buf; - VecT new = vec_alloc(new_cap, dst.buf->item_type); + int aliased = dst.items == src.items; + VecT new = vec_alloc(new_cap, VEC_BUF(dst)->item_type); if (VEC_IS_ERROR(new)) { VEC_DECREF(dst); return new; @@ -399,24 +406,25 @@ VecT VecT_ExtendVec(VecT dst, VecT src, size_t item_type) { // dst and src share a buffer -- incref all items instead of // moving refs, to avoid mutating the shared buffer for (Py_ssize_t i = 0; i < dst.len; i++) { - Py_INCREF(dst.buf->items[i]); - new.buf->items[i] = dst.buf->items[i]; + Py_INCREF(dst.items[i]); + new.items[i] = dst.items[i]; } } else { - memcpy(new.buf->items, dst.buf->items, sizeof(PyObject *) * dst.len); - if (Py_REFCNT(dst.buf) > 1) { + memcpy(new.items, dst.items, sizeof(PyObject *) * dst.len); + VecTBufObject *dst_buf = VEC_BUF(dst); + if (Py_REFCNT(dst_buf) > 1) { for (Py_ssize_t i = 0; i < dst.len; i++) - Py_XINCREF(new.buf->items[i]); + Py_XINCREF(new.items[i]); } else { - memset(dst.buf->items, 0, sizeof(PyObject *) * dst.len); + memset(dst.items, 0, sizeof(PyObject *) * dst.len); } } // Copy src items (incref each) for (Py_ssize_t i = 0; i < src.len; i++) { - Py_INCREF(src.buf->items[i]); - new.buf->items[dst.len + i] = src.buf->items[i]; + Py_INCREF(src.items[i]); + new.items[dst.len + i] = src.items[i]; } - memset(new.buf->items + new_len, 0, sizeof(PyObject *) * (new_cap - new_len)); + memset(new.items + new_len, 0, sizeof(PyObject *) * (new_cap - new_len)); new.len = new_len; vec_track_buffer(&new); VEC_DECREF(dst); @@ -431,14 +439,14 @@ PyObject *VecT_ToList(VecT v) { VEC_DECREF(v); return NULL; } - if (n > 0 && Py_REFCNT(v.buf) == 1) { + if (n > 0 && Py_REFCNT(VEC_BUF(v)) == 1) { for (Py_ssize_t i = 0; i < n; i++) { - PyList_SET_ITEM(list, i, v.buf->items[i]); - v.buf->items[i] = NULL; + PyList_SET_ITEM(list, i, v.items[i]); + v.items[i] = NULL; } } else { for (Py_ssize_t i = 0; i < n; i++) { - PyObject *item = v.buf->items[i]; + PyObject *item = v.items[i]; Py_INCREF(item); PyList_SET_ITEM(list, i, item); } @@ -455,14 +463,14 @@ PyObject *VecT_ToTuple(VecT v) { VEC_DECREF(v); return NULL; } - if (n > 0 && Py_REFCNT(v.buf) == 1) { + if (n > 0 && Py_REFCNT(VEC_BUF(v)) == 1) { for (Py_ssize_t i = 0; i < n; i++) { - PyTuple_SET_ITEM(tuple, i, v.buf->items[i]); - v.buf->items[i] = NULL; + PyTuple_SET_ITEM(tuple, i, v.items[i]); + v.items[i] = NULL; } } else { for (Py_ssize_t i = 0; i < n; i++) { - PyObject *item = v.buf->items[i]; + PyObject *item = v.items[i]; Py_INCREF(item); PyTuple_SET_ITEM(tuple, i, item); } @@ -473,7 +481,7 @@ PyObject *VecT_ToTuple(VecT v) { // Remove item from 'vec', stealing 'vec'. Return 'vec' with item removed. VecT VecT_Remove(VecT v, PyObject *arg) { - PyObject **items = v.buf->items; + PyObject **items = v.items; for (Py_ssize_t i = 0; i < v.len; i++) { int match = 0; if (items[i] == arg) @@ -527,7 +535,7 @@ VecTPopResult VecT_Pop(VecT v, Py_ssize_t index) { return result; } - PyObject **items = v.buf->items; + PyObject **items = v.items; result.f1 = items[index]; for (Py_ssize_t i = index; i < v.len - 1; i++) items[i] = items[i + 1]; @@ -543,14 +551,18 @@ VecTPopResult VecT_Pop(VecT v, Py_ssize_t index) { static int VecT_traverse(VecTObject *self, visitproc visit, void *arg) { - Py_VISIT(self->vec.buf); + if (self->vec.items) + Py_VISIT(VEC_BUF(self->vec)); return 0; } static int VecT_clear(VecTObject *self) { - Py_CLEAR(self->vec.buf); + if (self->vec.items) { + Py_DECREF(VEC_BUF(self->vec)); + self->vec.items = NULL; + } return 0; } @@ -559,7 +571,10 @@ VecT_dealloc(VecTObject *self) { PyObject_GC_UnTrack(self); Py_TRASHCAN_BEGIN(self, VecT_dealloc) - Py_CLEAR(self->vec.buf); + if (self->vec.items) { + Py_DECREF(VEC_BUF(self->vec)); + self->vec.items = NULL; + } Py_TYPE(self)->tp_free((PyObject *)self); Py_TRASHCAN_END } @@ -620,7 +635,7 @@ static PyMethodDef vec_methods[] = { typedef struct { PyObject_HEAD - VecT vec; // Unboxed vec (keeps buffer alive via buf reference) + VecT vec; // Unboxed vec (keeps buffer alive via items reference) Py_ssize_t index; // Current iteration index } VecTIterObject; @@ -631,7 +646,7 @@ static PyObject *VecT_iter(PyObject *self) { if (it == NULL) return NULL; it->vec = ((VecTObject *)self)->vec; - Py_INCREF(it->vec.buf); + VEC_INCREF(it->vec); it->index = 0; PyObject_GC_Track(it); return (PyObject *)it; @@ -640,38 +655,43 @@ static PyObject *VecT_iter(PyObject *self) { static int VecTIter_traverse(VecTIterObject *self, visitproc visit, void *arg) { - Py_VISIT(self->vec.buf); + if (self->vec.items) + Py_VISIT(VEC_BUF(self->vec)); return 0; } static int VecTIter_clear(VecTIterObject *self) { - Py_CLEAR(self->vec.buf); + if (self->vec.items) { + Py_DECREF(VEC_BUF(self->vec)); + self->vec.items = NULL; + } return 0; } static void VecTIter_dealloc(VecTIterObject *self) { PyObject_GC_UnTrack(self); - Py_XDECREF(self->vec.buf); + VEC_DECREF(self->vec); PyObject_GC_Del(self); } static PyObject *VecTIter_next(VecTIterObject *self) { - if (self->vec.buf == NULL) + if (self->vec.items == NULL) return NULL; if (self->index < self->vec.len) { - PyObject *item = self->vec.buf->items[self->index]; + PyObject *item = self->vec.items[self->index]; self->index++; Py_INCREF(item); return item; } - Py_CLEAR(self->vec.buf); + VEC_DECREF(self->vec); + self->vec.items = NULL; return NULL; // StopIteration } static PyObject *VecTIter_len(VecTIterObject *self, PyObject *Py_UNUSED(ignored)) { - if (self->vec.buf == NULL) + if (self->vec.items == NULL) return PyLong_FromSsize_t(0); Py_ssize_t remaining = self->vec.len - self->index; if (remaining < 0) @@ -744,15 +764,15 @@ static inline VecT vec_from_sequence( PyObject *item = is_list ? PyList_GET_ITEM(seq, i) : PyTuple_GET_ITEM(seq, i); if (!VecT_ItemCheck(v, item, item_type)) { for (Py_ssize_t j = i; j < alloc_size; j++) - v.buf->items[j] = NULL; + v.items[j] = NULL; VEC_DECREF(v); return vec_error(); } Py_INCREF(item); - v.buf->items[i] = item; + v.items[i] = item; } for (Py_ssize_t j = n; j < alloc_size; j++) - v.buf->items[j] = NULL; + v.items[j] = NULL; vec_track_buffer(&v); v.len = n; return v; @@ -770,7 +790,7 @@ VecT VecT_FromIterable(size_t item_type, PyObject *iterable, int64_t cap) { return vec_error(); if (cap > 0) { for (int64_t i = 0; i < cap; i++) - v.buf->items[i] = NULL; + v.items[i] = NULL; } v.len = 0; vec_track_buffer(&v); diff --git a/mypyc/lib-rt/vecs/vec_template.c b/mypyc/lib-rt/vecs/vec_template.c index cdbe21187482..58f1c8975dd6 100644 --- a/mypyc/lib-rt/vecs/vec_template.c +++ b/mypyc/lib-rt/vecs/vec_template.c @@ -31,6 +31,11 @@ #include "vecs_internal.h" #include "mypyc_util.h" +#define VEC_BUF(v) ((BUF_OBJECT *)((char *)(v).items - offsetof(BUF_OBJECT, items))) +#define VEC_CAP(v) (VEC_BUF(v)->ob_base.ob_size) +#define VEC_INCREF(v) do { if ((v).items) Py_INCREF(VEC_BUF(v)); } while (0) +#define VEC_DECREF(v) do { if ((v).items) Py_DECREF(VEC_BUF(v)); } while (0) + inline static VEC vec_error() { VEC v = { .len = -1 }; return v; @@ -48,12 +53,15 @@ static VEC vec_alloc(Py_ssize_t size) if (buf == NULL) return vec_error(); } - VEC res = { .buf = buf }; + VEC res = { .items = (buf != NULL) ? buf->items : NULL }; return res; } static void vec_dealloc(VEC_OBJECT *self) { - Py_CLEAR(self->vec.buf); + if (self->vec.items) { + Py_DECREF(VEC_BUF(self->vec)); + self->vec.items = NULL; + } PyObject_Del(self); } @@ -80,7 +88,7 @@ VEC FUNC(Unbox)(PyObject *obj) { } VEC FUNC(ConvertFromNested)(VecNestedBufItem item) { - return (VEC) { item.len, (BUF_OBJECT *)item.buf }; + return (VEC) { item.len, item.buf ? ((BUF_OBJECT *)item.buf)->items : NULL }; } VEC FUNC(New)(Py_ssize_t size, Py_ssize_t cap) { @@ -94,7 +102,7 @@ VEC FUNC(New)(Py_ssize_t size, Py_ssize_t cap) { if (VEC_IS_ERROR(vec)) return vec; for (Py_ssize_t i = 0; i < cap; i++) { - vec.buf->items[i] = 0; + vec.items[i] = 0; } vec.len = size; return vec; @@ -148,7 +156,7 @@ static inline VEC vec_from_sequence(PyObject *seq, int64_t cap, const int is_lis VEC_DECREF(v); return vec_error(); } - v.buf->items[i] = x; + v.items[i] = x; } v.len = n; return v; @@ -167,7 +175,7 @@ VEC FUNC(FromIterable)(PyObject *iterable, int64_t cap) { if (VEC_IS_ERROR(v)) return vec_error(); if (n > 0) - memcpy(v.buf->items, PyBytes_AS_STRING(iterable), n); + memcpy(v.items, PyBytes_AS_STRING(iterable), n); v.len = n; return v; } @@ -186,7 +194,7 @@ VEC FUNC(FromIterable)(PyObject *iterable, int64_t cap) { return vec_error(); } if (n > 0) { - memcpy(v.buf->items, view.buf, n * sizeof(ITEM_C_TYPE)); + memcpy(v.items, view.buf, n * sizeof(ITEM_C_TYPE)); } v.len = n; PyBuffer_Release(&view); @@ -204,7 +212,7 @@ VEC FUNC(FromIterable)(PyObject *iterable, int64_t cap) { if (VEC_IS_ERROR(v)) return vec_error(); if (cap > 0) { - memset(v.buf->items, 0, sizeof(ITEM_C_TYPE) * cap); + memset(v.items, 0, sizeof(ITEM_C_TYPE) * cap); } v.len = 0; @@ -265,9 +273,9 @@ static PyObject *vec_repr(PyObject *self) { static PyObject *vec_get_item(PyObject *o, Py_ssize_t i) { VEC v = ((VEC_OBJECT *)o)->vec; if ((size_t)i < (size_t)v.len) { - return BOX_ITEM(v.buf->items[i]); + return BOX_ITEM(v.items[i]); } else if ((size_t)i + (size_t)v.len < (size_t)v.len) { - return BOX_ITEM(v.buf->items[i + v.len]); + return BOX_ITEM(v.items[i + v.len]); } else { PyErr_SetString(PyExc_IndexError, "index out of range"); return NULL; @@ -293,7 +301,7 @@ VEC FUNC(Slice)(VEC vec, int64_t start, int64_t end) { return res; res.len = slicelength; for (Py_ssize_t i = 0; i < slicelength; i++) - res.buf->items[i] = vec.buf->items[start + i]; + res.items[i] = vec.items[start + i]; return res; } @@ -304,9 +312,9 @@ static PyObject *vec_subscript(PyObject *self, PyObject *item) { if (i == -1 && PyErr_Occurred()) return NULL; if ((size_t)i < (size_t)vec.len) { - return BOX_ITEM(vec.buf->items[i]); + return BOX_ITEM(vec.items[i]); } else if ((size_t)i + (size_t)vec.len < (size_t)vec.len) { - return BOX_ITEM(vec.buf->items[i + vec.len]); + return BOX_ITEM(vec.items[i + vec.len]); } else { PyErr_SetString(PyExc_IndexError, "index out of range"); return NULL; @@ -322,7 +330,7 @@ static PyObject *vec_subscript(PyObject *self, PyObject *item) { res.len = slicelength; Py_ssize_t j = start; for (Py_ssize_t i = 0; i < slicelength; i++) { - res.buf->items[i] = vec.buf->items[j]; + res.items[i] = vec.items[j]; j += step; } return FUNC(Box)(res); @@ -339,10 +347,10 @@ static int vec_ass_item(PyObject *self, Py_ssize_t i, PyObject *o) { return -1; VEC v = ((VEC_OBJECT *)self)->vec; if ((size_t)i < (size_t)v.len) { - v.buf->items[i] = x; + v.items[i] = x; return 0; } else if ((size_t)i + (size_t)v.len < (size_t)v.len) { - v.buf->items[i + v.len] = x; + v.items[i + v.len] = x; return 0; } else { PyErr_SetString(PyExc_IndexError, "index out of range"); @@ -358,7 +366,7 @@ static int vec_contains(PyObject *self, PyObject *value) { // Fall back to boxed comparison (e.g. 2.0 == 2) VEC v = ((VEC_OBJECT *)self)->vec; for (Py_ssize_t i = 0; i < v.len; i++) { - PyObject *boxed = BOX_ITEM(v.buf->items[i]); + PyObject *boxed = BOX_ITEM(v.items[i]); if (boxed == NULL) return -1; int cmp = PyObject_RichCompareBool(boxed, value, Py_EQ); @@ -370,7 +378,7 @@ static int vec_contains(PyObject *self, PyObject *value) { } VEC v = ((VEC_OBJECT *)self)->vec; for (Py_ssize_t i = 0; i < v.len; i++) { - if (v.buf->items[i] == x) + if (v.items[i] == x) return 1; } return 0; @@ -393,7 +401,7 @@ static PyObject *vec_richcompare(PyObject *self, PyObject *other, int op) { cmp = 0; } else { for (Py_ssize_t i = 0; i < x.len; i++) { - if (x.buf->items[i] != y.buf->items[i]) { + if (x.items[i] != y.items[i]) { cmp = 0; break; } @@ -411,12 +419,12 @@ static PyObject *vec_richcompare(PyObject *self, PyObject *other, int op) { // Append item to 'vec', stealing 'vec'. Return 'vec' with item appended. VEC FUNC(Append)(VEC vec, ITEM_C_TYPE x) { - if (vec.buf && vec.len < VEC_CAP(vec)) { - vec.buf->items[vec.len] = x; + if (vec.items && vec.len < VEC_CAP(vec)) { + vec.items[vec.len] = x; vec.len++; return vec; } else { - Py_ssize_t cap = vec.buf ? VEC_CAP(vec) : 0; + Py_ssize_t cap = vec.items ? VEC_CAP(vec) : 0; Py_ssize_t new_size = Vec_GrowCapacity(cap); VEC new = vec_alloc(new_size); if (VEC_IS_ERROR(new)) { @@ -427,9 +435,9 @@ VEC FUNC(Append)(VEC vec, ITEM_C_TYPE x) { } new.len = vec.len + 1; if (vec.len > 0) - memcpy(new.buf->items, vec.buf->items, sizeof(ITEM_C_TYPE) * vec.len); - new.buf->items[vec.len] = x; - Py_XDECREF(vec.buf); + memcpy(new.items, vec.items, sizeof(ITEM_C_TYPE) * vec.len); + new.items[vec.len] = x; + VEC_DECREF(vec); return new; } } @@ -456,9 +464,9 @@ inline static VEC vec_extend_items( return vec_error(); } Py_ssize_t new_len = dst.len + n; - Py_ssize_t cap = dst.buf ? VEC_CAP(dst) : 0; + Py_ssize_t cap = dst.items ? VEC_CAP(dst) : 0; if (!force_alloc && new_len <= cap) { - memcpy(dst.buf->items + dst.len, items, sizeof(ITEM_C_TYPE) * n); + memcpy(dst.items + dst.len, items, sizeof(ITEM_C_TYPE) * n); dst.len = new_len; return dst; } @@ -469,10 +477,10 @@ inline static VEC vec_extend_items( return vec_error(); } if (dst.len > 0) - memcpy(new.buf->items, dst.buf->items, sizeof(ITEM_C_TYPE) * dst.len); - memcpy(new.buf->items + dst.len, items, sizeof(ITEM_C_TYPE) * n); + memcpy(new.items, dst.items, sizeof(ITEM_C_TYPE) * dst.len); + memcpy(new.items + dst.len, items, sizeof(ITEM_C_TYPE) * n); new.len = new_len; - Py_XDECREF(dst.buf); + VEC_DECREF(dst); return new; } @@ -501,10 +509,10 @@ VEC FUNC(Extend)(VEC vec, PyObject *iterable) { Py_ssize_t n = view.len / (Py_ssize_t)sizeof(ITEM_C_TYPE); if (n > 0) { Py_ssize_t dst_bytes = n * (Py_ssize_t)sizeof(ITEM_C_TYPE); - int force_alloc = vec.buf != NULL + int force_alloc = vec.items != NULL && n <= VEC_CAP(vec) - vec.len && vec_memory_overlaps(view.buf, view.len, - vec.buf->items + vec.len, dst_bytes); + vec.items + vec.len, dst_bytes); vec = vec_extend_items(vec, (const ITEM_C_TYPE *)view.buf, n, force_alloc); } PyBuffer_Release(&view); @@ -545,7 +553,7 @@ VEC FUNC(Extend)(VEC vec, PyObject *iterable) { VEC FUNC(ExtendVec)(VEC dst, VEC src) { if (src.len == 0) return dst; - return vec_extend_items(dst, src.buf->items, src.len, dst.buf == src.buf); + return vec_extend_items(dst, src.items, src.len, dst.items == src.items); } // Convert vec to list, stealing 'v'. @@ -557,7 +565,7 @@ PyObject *FUNC(ToList)(VEC v) { return NULL; } for (Py_ssize_t i = 0; i < n; i++) { - PyObject *item = BOX_ITEM(v.buf->items[i]); + PyObject *item = BOX_ITEM(v.items[i]); if (item == NULL) { Py_DECREF(list); VEC_DECREF(v); @@ -578,7 +586,7 @@ PyObject *FUNC(ToTuple)(VEC v) { return NULL; } for (Py_ssize_t i = 0; i < n; i++) { - PyObject *item = BOX_ITEM(v.buf->items[i]); + PyObject *item = BOX_ITEM(v.items[i]); if (item == NULL) { Py_DECREF(tuple); VEC_DECREF(v); @@ -593,9 +601,9 @@ PyObject *FUNC(ToTuple)(VEC v) { // Remove item from 'vec', stealing 'vec'. Return 'vec' with item removed. VEC FUNC(Remove)(VEC v, ITEM_C_TYPE x) { for (Py_ssize_t i = 0; i < v.len; i++) { - if (v.buf->items[i] == x) { + if (v.items[i] == x) { for (; i < v.len - 1; i++) { - v.buf->items[i] = v.buf->items[i + 1]; + v.items[i] = v.items[i + 1]; } v.len--; // Return the stolen reference without INCREF @@ -626,9 +634,9 @@ NAME(PopResult) FUNC(Pop)(VEC v, Py_ssize_t index) { return result; } - result.f1 = v.buf->items[index]; + result.f1 = v.items[index]; for (Py_ssize_t i = index; i < v.len - 1; i++) { - v.buf->items[i] = v.buf->items[i + 1]; + v.items[i] = v.items[i + 1]; } v.len--; @@ -657,7 +665,7 @@ static int vec_getbuffer(VEC_OBJECT *self, Py_buffer *view, int flags) { view->obj = (PyObject *)self; Py_INCREF(self); - view->buf = (self->vec.buf != NULL) ? (void *)self->vec.buf->items : NULL; + view->buf = (self->vec.items != NULL) ? (void *)self->vec.items : NULL; view->len = self->vec.len * (Py_ssize_t)sizeof(ITEM_C_TYPE); view->readonly = 1; view->itemsize = sizeof(ITEM_C_TYPE); @@ -696,7 +704,7 @@ static PyMethodDef vec_methods[] = { typedef struct { PyObject_HEAD - VEC vec; // Unboxed vec (keeps buffer alive via buf reference) + VEC vec; // Unboxed vec (keeps buffer alive via items reference) Py_ssize_t index; // Current iteration index } NAME(IterObject); @@ -707,32 +715,33 @@ static PyObject *vec_iter(PyObject *self) { if (it == NULL) return NULL; it->vec = ((VEC_OBJECT *)self)->vec; - Py_XINCREF(it->vec.buf); + VEC_INCREF(it->vec); it->index = 0; return (PyObject *)it; } static void vec_iter_dealloc(NAME(IterObject) *self) { - Py_XDECREF(self->vec.buf); + VEC_DECREF(self->vec); PyObject_Del(self); } static PyObject *vec_iter_next(NAME(IterObject) *self) { - if (self->vec.buf == NULL) + if (self->vec.items == NULL) return NULL; if (self->index < self->vec.len) { - PyObject *item = BOX_ITEM(self->vec.buf->items[self->index]); + PyObject *item = BOX_ITEM(self->vec.items[self->index]); if (item == NULL) return NULL; self->index++; return item; } - Py_CLEAR(self->vec.buf); + VEC_DECREF(self->vec); + self->vec.items = NULL; return NULL; // StopIteration } static PyObject *vec_iter_len(NAME(IterObject) *self, PyObject *Py_UNUSED(ignored)) { - if (self->vec.buf == NULL) + if (self->vec.items == NULL) return PyLong_FromSsize_t(0); Py_ssize_t remaining = self->vec.len - self->index; if (remaining < 0) @@ -808,4 +817,9 @@ NAME(API) FEATURES = { FUNC(ToTuple), }; +#undef VEC_BUF +#undef VEC_CAP +#undef VEC_INCREF +#undef VEC_DECREF + #endif // MYPYC_EXPERIMENTAL diff --git a/mypyc/lib-rt/vecs_extra_ops.h b/mypyc/lib-rt/vecs_extra_ops.h index 561bf0a1b652..c7f0676d9df3 100644 --- a/mypyc/lib-rt/vecs_extra_ops.h +++ b/mypyc/lib-rt/vecs_extra_ops.h @@ -14,7 +14,7 @@ static inline PyObject *CPyVecU8_ToBytes(VecU8 v) { if (v.len == 0) { return PyBytes_FromStringAndSize(NULL, 0); } - return PyBytes_FromStringAndSize((const char *)v.buf->items, v.len); + return PyBytes_FromStringAndSize((const char *)v.items, v.len); } #endif // MYPYC_EXPERIMENTAL From 862da2b487436ae24798932a6e3eb15f0c273238 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 30 Apr 2026 15:57:05 +0100 Subject: [PATCH 3/9] Fixes --- mypyc/lib-rt/vecs/librt_vecs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/mypyc/lib-rt/vecs/librt_vecs.h b/mypyc/lib-rt/vecs/librt_vecs.h index 2cdf7206b565..923d987c8e1e 100644 --- a/mypyc/lib-rt/vecs/librt_vecs.h +++ b/mypyc/lib-rt/vecs/librt_vecs.h @@ -6,6 +6,7 @@ #define PY_SSIZE_T_CLEAN #include +#include #include #include "mypyc_util.h" From bae5f91d5340bf7ae7af371f8cf0a3c79250e721 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 30 Apr 2026 16:15:43 +0100 Subject: [PATCH 4/9] Fix issue --- mypyc/lib-rt/vecs/librt_vecs.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mypyc/lib-rt/vecs/librt_vecs.h b/mypyc/lib-rt/vecs/librt_vecs.h index 923d987c8e1e..3adb35c5ad04 100644 --- a/mypyc/lib-rt/vecs/librt_vecs.h +++ b/mypyc/lib-rt/vecs/librt_vecs.h @@ -792,10 +792,11 @@ static inline int VecNested_UnboxItem(VecNested v, PyObject *item, VecNestedBufI size_t depth = v_buf->depth; if (depth == 1) { if (item->ob_type == &VecTType) { - VecNestedObject *o = (VecNestedObject *)item; - if (VEC_NESTED_BUF(o->vec)->item_type == v_buf->item_type) { + // Boxed vec[t] always has items != NULL (buf is allocated on boxing) + VecTObject *o = (VecTObject *)item; + if (VEC_T_BUF(o->vec)->item_type == v_buf->item_type) { unboxed->len = o->vec.len; - unboxed->buf = (PyObject *)VEC_NESTED_BUF(o->vec); + unboxed->buf = (PyObject *)VEC_T_BUF(o->vec); return 0; } } else if (item->ob_type == &VecI64Type && v_buf->item_type == VEC_ITEM_TYPE_I64) { From 13de494ae0d67228d88f65f46c71daf59fb5e111 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 30 Apr 2026 16:29:11 +0100 Subject: [PATCH 5/9] Update mypyc to use the new vec representation --- mypyc/codegen/emit.py | 40 ++- mypyc/ir/rtypes.py | 18 +- mypyc/irbuild/vec.py | 28 +- mypyc/test-data/irbuild-vec-i64.test | 374 +++++++++++------------- mypyc/test-data/irbuild-vec-misc.test | 82 +++--- mypyc/test-data/irbuild-vec-nested.test | 162 +++++----- mypyc/test-data/irbuild-vec-t.test | 52 ++-- 7 files changed, 365 insertions(+), 391 deletions(-) diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index 54e77836a76c..45ff34ab045d 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -83,6 +83,18 @@ NAMESPACE_TYPE_VAR: TYPE_VAR_PREFIX, } +# Map from RVec._ctype to C macro prefix for VEC_*_INCREF/DECREF/BUF macros +VEC_MACRO_PREFIX: Final = { + "VecI64": "VEC_I64", + "VecI32": "VEC_I32", + "VecI16": "VEC_I16", + "VecU8": "VEC_U8", + "VecFloat": "VEC_FLOAT", + "VecBool": "VEC_BOOL", + "VecT": "VEC_T", + "VecNested": "VEC_NESTED", +} + class HeaderDeclaration: """A representation of a declaration in C. @@ -348,7 +360,7 @@ def ctype_spaced(self, rtype: RType) -> str: def set_undefined_value(self, target: str, rtype: RType) -> None: if isinstance(rtype, RVec): self.emit_line(f"{target}.len = -1;") - self.emit_line(f"{target}.buf = NULL;") + self.emit_line(f"{target}.items = NULL;") else: self.emit_line(f"{target} = {self.c_undefined_value(rtype)};") @@ -574,8 +586,8 @@ def emit_inc_ref(self, dest: str, rtype: RType, *, rare: bool = False) -> None: for i, item_type in enumerate(rtype.types): self.emit_inc_ref(f"{dest}.f{i}", item_type) elif isinstance(rtype, RVec): - # TODO: Only use the X variant if buf can be NULL - self.emit_line(f"Py_XINCREF({dest}.buf);") + prefix = VEC_MACRO_PREFIX[rtype._ctype] + self.emit_line(f"{prefix}_INCREF({dest});") elif not rtype.is_unboxed: # Always inline, since this is a simple but very hot op if rtype.may_be_immortal or not HAVE_IMMORTAL: @@ -605,11 +617,8 @@ def emit_dec_ref( for i, item_type in enumerate(rtype.types): self.emit_dec_ref(f"{dest}.f{i}", item_type, is_xdec=is_xdec, rare=rare) elif isinstance(rtype, RVec): - # TODO: Only use the X variant if buf can be NULL - if rare: - self.emit_line(f"CPy_XDecRef({dest}.buf);") - else: - self.emit_line(f"CPy_XDECREF({dest}.buf);") + prefix = VEC_MACRO_PREFIX[rtype._ctype] + self.emit_line(f"{prefix}_DECREF({dest});") elif not rtype.is_unboxed: if rare: self.emit_line(f"CPy_{x}DecRef({dest});") @@ -831,7 +840,7 @@ def emit_cast( item_type_c = self.vec_item_type_c(typ) check = ( f"(Py_TYPE({src}) == VecTApi.boxed_type && " - f"((VecTObject *){src})->vec.buf->item_type == {item_type_c})" + f"VEC_T_BUF(((VecTObject *){src})->vec)->item_type == {item_type_c})" ) else: # Nested vec types (vec[vec[...]]). Check boxed type, item type, and depth. @@ -842,8 +851,8 @@ def emit_cast( type_value = self.vec_item_type_c(typ) check = ( f"(Py_TYPE({src}) == VecNestedApi.boxed_type && " - f"((VecNestedObject *){src})->vec.buf->item_type == {type_value} && " - f"((VecNestedObject *){src})->vec.buf->depth == {depth})" + f"VEC_NESTED_BUF(((VecNestedObject *){src})->vec)->item_type == {type_value} && " + f"VEC_NESTED_BUF(((VecNestedObject *){src})->vec)->depth == {depth})" ) if likely: check = f"(likely{check})" @@ -1284,7 +1293,8 @@ def emit_gc_visit(self, target: str, rtype: RType) -> None: for i, item_type in enumerate(rtype.types): self.emit_gc_visit(f"{target}.f{i}", item_type) elif isinstance(rtype, RVec): - self.emit_line(f"Py_VISIT({target}.buf);") + prefix = VEC_MACRO_PREFIX[rtype._ctype] + self.emit_line(f"if ({target}.items) {{ Py_VISIT({prefix}_BUF({target})); }}") elif self.ctype(rtype) == "PyObject *": # The simplest case. self.emit_line(f"Py_VISIT({target});") @@ -1310,7 +1320,11 @@ def emit_gc_clear(self, target: str, rtype: RType) -> None: for i, item_type in enumerate(rtype.types): self.emit_gc_clear(f"{target}.f{i}", item_type) elif isinstance(rtype, RVec): - self.emit_line(f"Py_CLEAR({target}.buf);") + prefix = VEC_MACRO_PREFIX[rtype._ctype] + self.emit_line(f"if ({target}.items) {{") + self.emit_line(f" Py_DECREF({prefix}_BUF({target}));") + self.emit_line(f" {target}.items = NULL;") + self.emit_line("}") elif self.ctype(rtype) == "PyObject *" and self.c_undefined_value(rtype) == "NULL": # The simplest case. self.emit_line(f"Py_CLEAR({target});") diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 1429e8a45cf6..961ddf2116e1 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -1017,7 +1017,7 @@ class RVec(RType): def __init__(self, item_type: RType) -> None: self.name = "vec[%s]" % item_type self.item_type = item_type - self.names = ["len", "buf"] + self.names = ["len", "items"] self.dependencies = (LIBRT_VECS,) if isinstance(item_type, RUnion): non_opt = optional_value_type(item_type) @@ -1026,14 +1026,14 @@ def __init__(self, item_type: RType) -> None: if item_type in vec_buf_types: self._ctype = vec_c_types[item_type] self.buf_type = vec_buf_types[item_type] - self.types = [c_pyssize_t_rprimitive, self.buf_type] + self.types = [c_pyssize_t_rprimitive, pointer_rprimitive] elif isinstance(non_opt, RVec): self._ctype = "VecNested" - self.types = [c_pyssize_t_rprimitive, VecTBufObject] + self.types = [c_pyssize_t_rprimitive, pointer_rprimitive] self.buf_type = VecNestedBufObject else: self._ctype = "VecT" - self.types = [c_pyssize_t_rprimitive, VecTBufObject] + self.types = [c_pyssize_t_rprimitive, pointer_rprimitive] self.buf_type = VecTBufObject @property @@ -1076,8 +1076,8 @@ def depth(self) -> int: def field_type(self, name: str) -> RType: if name == "len": return c_pyssize_t_rprimitive - elif name == "buf": - return object_rprimitive + elif name == "items": + return pointer_rprimitive assert False, f"RVec has no field '{name}'" def accept(self, visitor: RTypeVisitor[T]) -> T: @@ -1346,7 +1346,7 @@ def check_native_int_range(rtype: RPrimitive, n: int) -> bool: # Struct type for vec[i64] (in most cases use RVec instead). VecI64 = RStruct( - name="VecI64", names=["len", "buf"], types=[c_pyssize_t_rprimitive, object_rprimitive] + name="VecI64", names=["len", "items"], types=[c_pyssize_t_rprimitive, pointer_rprimitive] ) @@ -1359,7 +1359,7 @@ def check_native_int_range(rtype: RPrimitive, n: int) -> bool: # Struct type for vec[t] (in most cases use RVec instead). VecT = RStruct( - name="VecT", names=["len", "buf"], types=[c_pyssize_t_rprimitive, object_rprimitive] + name="VecT", names=["len", "items"], types=[c_pyssize_t_rprimitive, pointer_rprimitive] ) VecNestedBufItem = RStruct( @@ -1383,7 +1383,7 @@ def check_native_int_range(rtype: RPrimitive, n: int) -> bool: # Struct type for vec[vec[...]] (in most cases use RVec instead). VecNested = RStruct( - name="VecNested", names=["len", "buf"], types=[c_pyssize_t_rprimitive, object_rprimitive] + name="VecNested", names=["len", "items"], types=[c_pyssize_t_rprimitive, pointer_rprimitive] ) VecNestedBufObject_rprimitive = RPrimitive( diff --git a/mypyc/irbuild/vec.py b/mypyc/irbuild/vec.py index 00e6f0adcdd8..b0f5645f418a 100644 --- a/mypyc/irbuild/vec.py +++ b/mypyc/irbuild/vec.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Final, cast +from typing import TYPE_CHECKING, Final from mypyc.common import IS_32_BIT_PLATFORM, PLATFORM_SIZE from mypyc.ir.ops import ( @@ -244,14 +244,24 @@ def vec_len_native(builder: LowLevelIRBuilder, val: Value) -> Value: def vec_items(builder: LowLevelIRBuilder, vecobj: Value) -> Value: - """Return pointer to first item in vec's buf. + """Return pointer to first item in vec. - Safe to call even when buf is NULL (empty vec), since GetElementPtr - uses offsetof-based arithmetic instead of &((T*)p)->field. + The items field points directly to the first element in the buffer. """ - vtype = cast(RVec, vecobj.type) - buf = builder.get_element(vecobj, "buf") - return builder.add(GetElementPtr(buf, vtype.buf_type, "items")) + return builder.get_element(vecobj, "items") + + +def vec_buf(builder: LowLevelIRBuilder, vecobj: Value) -> Value: + """Recover the buffer object pointer from a vec's items pointer. + + Emits: items_ptr - offsetof(BufType, items) + """ + vtype = vecobj.type + assert isinstance(vtype, RVec) + items = builder.get_element(vecobj, "items") + # offsetof(BufType, items) == GetElementPtr(0, buf_type, "items") + offset = builder.add(GetElementPtr(Integer(0, pointer_rprimitive), vtype.buf_type, "items")) + return builder.int_sub(items, offset) def vec_item_ptr(builder: LowLevelIRBuilder, vecobj: Value, index: Value) -> Value: @@ -364,9 +374,9 @@ def vec_init_item_unsafe( def convert_to_t_ext_item(builder: LowLevelIRBuilder, item: Value) -> Value: vec_len = builder.add(GetElement(item, "len")) - vec_buf = builder.add(GetElement(item, "buf")) + buf_ptr = vec_buf(builder, item) temp = builder.add(SetElement(Undef(VecNestedBufItem), "len", vec_len)) - return builder.add(SetElement(temp, "buf", vec_buf)) + return builder.add(SetElement(temp, "buf", buf_ptr)) def convert_from_t_ext_item(builder: LowLevelIRBuilder, item: Value, vec_type: RVec) -> Value: diff --git a/mypyc/test-data/irbuild-vec-i64.test b/mypyc/test-data/irbuild-vec-i64.test index 37603cdd4084..1ca33f1f78b3 100644 --- a/mypyc/test-data/irbuild-vec-i64.test +++ b/mypyc/test-data/irbuild-vec-i64.test @@ -65,11 +65,10 @@ def f(v, i): r3 :: bit r4 :: bool r5 :: i64 - r6 :: object - r7 :: ptr - r8 :: i64 - r9 :: ptr - r10 :: i64 + r6 :: ptr + r7 :: i64 + r8 :: ptr + r9 :: i64 L0: r0 = v.len r1 = i < r0 :: unsigned @@ -87,13 +86,12 @@ L3: L4: r5 = i L5: - r6 = v.buf - r7 = get_element_ptr r6 items :: VecI64BufObject - r8 = r5 * 8 - r9 = r7 + r8 - r10 = load_mem r9 :: i64* + r6 = v.items + r7 = r5 * 8 + r8 = r6 + r7 + r9 = load_mem r8 :: i64* keep_alive v - return r10 + return r9 [case testVecI64GetItem_32bit] # The IR is quite verbose, but it's acceptable since 32-bit targets are not common any more @@ -218,10 +216,9 @@ def f(v, i, x): r3 :: bit r4 :: bool r5 :: i64 - r6 :: object - r7 :: ptr - r8 :: i64 - r9 :: ptr + r6 :: ptr + r7 :: i64 + r8 :: ptr L0: r0 = v.len r1 = i < r0 :: unsigned @@ -239,11 +236,10 @@ L3: L4: r5 = i L5: - r6 = v.buf - r7 = get_element_ptr r6 items :: VecI64BufObject - r8 = r5 * 8 - r9 = r7 + r8 - set_mem r9, x :: i64* + r6 = v.items + r7 = r5 * 8 + r8 = r6 + r7 + set_mem r8, x :: i64* keep_alive v return 1 @@ -256,18 +252,16 @@ def f() -> vec[i64]: [out] def f(): r0 :: vec[i64] - r1 :: object - r2, r3, r4, r5 :: ptr + r1, r2, r3, r4 :: ptr L0: r0 = VecI64Api.alloc(3, 3) - r1 = r0.buf - r2 = get_element_ptr r1 items :: VecI64BufObject - set_mem r2, 1 :: i64* + r1 = r0.items + set_mem r1, 1 :: i64* + r2 = r1 + 8 + set_mem r2, 5 :: i64* r3 = r2 + 8 - set_mem r3, 5 :: i64* + set_mem r3, 14 :: i64* r4 = r3 + 8 - set_mem r4, 14 :: i64* - r5 = r4 + 8 keep_alive r0 return r0 @@ -281,26 +275,24 @@ def f(n: i64) -> vec[i64]: def f(n): n :: i64 r0 :: vec[i64] - r1 :: object - r2 :: ptr - r3 :: i64 - r4, r5 :: ptr - r6 :: bit - r7 :: ptr + r1 :: ptr + r2 :: i64 + r3, r4 :: ptr + r5 :: bit + r6 :: ptr L0: r0 = VecI64Api.alloc(n, n) - r1 = r0.buf - r2 = get_element_ptr r1 items :: VecI64BufObject - r3 = n * 8 - r4 = r2 + r3 - r5 = r2 + r1 = r0.items + r2 = n * 8 + r3 = r1 + r2 + r4 = r1 L1: - r6 = r5 < r4 :: unsigned - if r6 goto L2 else goto L3 :: bool + r5 = r4 < r3 :: unsigned + if r5 goto L2 else goto L3 :: bool L2: - set_mem r5, 3 :: i64* - r7 = r5 + 8 - r5 = r7 + set_mem r4, 3 :: i64* + r6 = r4 + 8 + r4 = r6 goto L1 L3: keep_alive r0 @@ -316,26 +308,24 @@ def f(n: i64, x: i64) -> vec[i64]: def f(n, x): n, x :: i64 r0 :: vec[i64] - r1 :: object - r2 :: ptr - r3 :: native_int - r4, r5 :: ptr - r6 :: bit - r7 :: ptr + r1 :: ptr + r2 :: native_int + r3, r4 :: ptr + r5 :: bit + r6 :: ptr L0: r0 = VecI64Api.alloc(3, 3) - r1 = r0.buf - r2 = get_element_ptr r1 items :: VecI64BufObject - r3 = 3 * 8 - r4 = r2 + r3 - r5 = r2 + r1 = r0.items + r2 = 3 * 8 + r3 = r1 + r2 + r4 = r1 L1: - r6 = r5 < r4 :: unsigned - if r6 goto L2 else goto L3 :: bool + r5 = r4 < r3 :: unsigned + if r5 goto L2 else goto L3 :: bool L2: - set_mem r5, x :: i64* - r7 = r5 + 8 - r5 = r7 + set_mem r4, x :: i64* + r6 = r4 + 8 + r4 = r6 goto L1 L3: keep_alive r0 @@ -394,11 +384,10 @@ def f(n, l): r4 :: bit r5 :: object r6, x, r7 :: i64 - r8 :: object - r9 :: ptr - r10 :: native_int - r11 :: ptr - r12 :: native_int + r8 :: ptr + r9 :: native_int + r10 :: ptr + r11 :: native_int L0: r0 = var_object_size l r1 = VecI64Api.alloc(r0, r0) @@ -412,15 +401,14 @@ L2: r6 = unbox(i64, r5) x = r6 r7 = x + 1 - r8 = r1.buf - r9 = get_element_ptr r8 items :: VecI64BufObject - r10 = r2 * 8 - r11 = r9 + r10 - set_mem r11, r7 :: i64* + r8 = r1.items + r9 = r2 * 8 + r10 = r8 + r9 + set_mem r10, r7 :: i64* keep_alive r1 L3: - r12 = r2 + 1 - r2 = r12 + r11 = r2 + 1 + r2 = r11 goto L1 L4: return r1 @@ -440,16 +428,14 @@ def f(n, v): r1 :: vec[i64] r2, r3 :: native_int r4 :: bit - r5 :: object - r6 :: ptr - r7 :: native_int - r8 :: ptr - r9, x, r10 :: i64 - r11 :: object + r5 :: ptr + r6 :: native_int + r7 :: ptr + r8, x, r9 :: i64 + r10 :: ptr + r11 :: native_int r12 :: ptr r13 :: native_int - r14 :: ptr - r15 :: native_int L0: r0 = v.len r1 = VecI64Api.alloc(r0, r0) @@ -459,23 +445,21 @@ L1: r4 = r2 < r3 :: signed if r4 goto L2 else goto L4 :: bool L2: - r5 = v.buf - r6 = get_element_ptr r5 items :: VecI64BufObject - r7 = r2 * 8 - r8 = r6 + r7 - r9 = load_mem r8 :: i64* - x = r9 + r5 = v.items + r6 = r2 * 8 + r7 = r5 + r6 + r8 = load_mem r7 :: i64* + x = r8 keep_alive v - r10 = x + 1 - r11 = r1.buf - r12 = get_element_ptr r11 items :: VecI64BufObject - r13 = r2 * 8 - r14 = r12 + r13 - set_mem r14, r10 :: i64* + r9 = x + 1 + r10 = r1.items + r11 = r2 * 8 + r12 = r10 + r11 + set_mem r12, r9 :: i64* keep_alive r1 L3: - r15 = r2 + 1 - r2 = r15 + r13 = r2 + 1 + r2 = r13 goto L1 L4: return r1 @@ -532,12 +516,11 @@ def f(v): t :: i64 r0, r1 :: native_int r2 :: bit - r3 :: object - r4 :: ptr - r5 :: native_int - r6 :: ptr - r7, x, r8 :: i64 - r9 :: native_int + r3 :: ptr + r4 :: native_int + r5 :: ptr + r6, x, r7 :: i64 + r8 :: native_int L0: t = 0 r0 = 0 @@ -546,18 +529,17 @@ L1: r2 = r0 < r1 :: signed if r2 goto L2 else goto L4 :: bool L2: - r3 = v.buf - r4 = get_element_ptr r3 items :: VecI64BufObject - r5 = r0 * 8 - r6 = r4 + r5 - r7 = load_mem r6 :: i64* - x = r7 + r3 = v.items + r4 = r0 * 8 + r5 = r3 + r4 + r6 = load_mem r5 :: i64* + x = r6 keep_alive v - r8 = t + 1 - t = r8 + r7 = t + 1 + t = r7 L3: - r9 = r0 + 1 - r0 = r9 + r8 = r0 + 1 + r0 = r8 goto L1 L4: return t @@ -573,41 +555,39 @@ def contains(v, n): v :: vec[i64] n :: i64 r0 :: native_int - r1 :: object - r2 :: ptr - r3 :: native_int - r4, r5 :: ptr - r6 :: bit - r7 :: i64 - r8 :: bit - r9 :: ptr - r10 :: bool + r1 :: ptr + r2 :: native_int + r3, r4 :: ptr + r5 :: bit + r6 :: i64 + r7 :: bit + r8 :: ptr + r9 :: bool L0: r0 = v.len - r1 = v.buf - r2 = get_element_ptr r1 items :: VecI64BufObject - r3 = r0 * 8 - r4 = r2 + r3 - r5 = r2 + r1 = v.items + r2 = r0 * 8 + r3 = r1 + r2 + r4 = r1 L1: - r6 = r5 < r4 :: unsigned - if r6 goto L2 else goto L4 :: bool + r5 = r4 < r3 :: unsigned + if r5 goto L2 else goto L4 :: bool L2: - r7 = load_mem r5 :: i64* - r8 = r7 == n - if r8 goto L5 else goto L3 :: bool + r6 = load_mem r4 :: i64* + r7 = r6 == n + if r7 goto L5 else goto L3 :: bool L3: - r9 = r5 + 8 - r5 = r9 + r8 = r4 + 8 + r4 = r8 goto L1 L4: keep_alive v - r10 = 0 + r9 = 0 goto L6 L5: - r10 = 1 + r9 = 1 L6: - return r10 + return r9 [case testVecI64GetItemWithInt_64bit] from librt.vecs import vec @@ -624,11 +604,10 @@ def f(v): r3 :: bit r4 :: bool r5 :: i64 - r6 :: object - r7 :: ptr - r8 :: i64 - r9 :: ptr - r10 :: i64 + r6 :: ptr + r7 :: i64 + r8 :: ptr + r9 :: i64 L0: r0 = v.len r1 = 0 < r0 :: unsigned @@ -646,13 +625,12 @@ L3: L4: r5 = 0 L5: - r6 = v.buf - r7 = get_element_ptr r6 items :: VecI64BufObject - r8 = r5 * 8 - r9 = r7 + r8 - r10 = load_mem r9 :: i64* + r6 = v.items + r7 = r5 * 8 + r8 = r6 + r7 + r9 = load_mem r8 :: i64* keep_alive v - return r10 + return r9 [case testVecI64Slicing_64bit] from librt.vecs import vec @@ -745,21 +723,19 @@ def inplace(v, n, m): r3 :: bit r4 :: bool r5 :: i64 - r6 :: object - r7 :: ptr - r8 :: i64 - r9 :: ptr - r10, r11 :: i64 - r12 :: native_int - r13 :: bit - r14 :: i64 - r15 :: bit - r16 :: bool - r17 :: i64 - r18 :: object + r6 :: ptr + r7 :: i64 + r8 :: ptr + r9, r10 :: i64 + r11 :: native_int + r12 :: bit + r13 :: i64 + r14 :: bit + r15 :: bool + r16 :: i64 + r17 :: ptr + r18 :: i64 r19 :: ptr - r20 :: i64 - r21 :: ptr L0: r0 = v.len r1 = n < r0 :: unsigned @@ -777,34 +753,32 @@ L3: L4: r5 = n L5: - r6 = v.buf - r7 = get_element_ptr r6 items :: VecI64BufObject - r8 = r5 * 8 - r9 = r7 + r8 - r10 = load_mem r9 :: i64* + r6 = v.items + r7 = r5 * 8 + r8 = r6 + r7 + r9 = load_mem r8 :: i64* keep_alive v - r11 = r10 + m - r12 = v.len - r13 = n < r12 :: unsigned - if r13 goto L9 else goto L6 :: bool + r10 = r9 + m + r11 = v.len + r12 = n < r11 :: unsigned + if r12 goto L9 else goto L6 :: bool L6: - r14 = n + r12 - r15 = r14 < r12 :: unsigned - if r15 goto L8 else goto L7 :: bool + r13 = n + r11 + r14 = r13 < r11 :: unsigned + if r14 goto L8 else goto L7 :: bool L7: - r16 = raise IndexError + r15 = raise IndexError unreachable L8: - r17 = r14 + r16 = r13 goto L10 L9: - r17 = n + r16 = n L10: - r18 = v.buf - r19 = get_element_ptr r18 items :: VecI64BufObject - r20 = r17 * 8 - r21 = r19 + r20 - set_mem r21, r11 :: i64* + r17 = v.items + r18 = r16 * 8 + r19 = r17 + r18 + set_mem r19, r10 :: i64* keep_alive v return 1 @@ -881,16 +855,14 @@ def list_with_cap() -> vec[i64]: [out] def list_with_cap(): r0 :: vec[i64] - r1 :: object - r2, r3, r4 :: ptr + r1, r2, r3 :: ptr L0: r0 = VecI64Api.alloc(2, 5) - r1 = r0.buf - r2 = get_element_ptr r1 items :: VecI64BufObject - set_mem r2, 1 :: i64* + r1 = r0.items + set_mem r1, 1 :: i64* + r2 = r1 + 8 + set_mem r2, 2 :: i64* r3 = r2 + 8 - set_mem r3, 2 :: i64* - r4 = r3 + 8 keep_alive r0 return r0 @@ -904,26 +876,24 @@ def repeated_with_cap(n: i64) -> vec[i64]: def repeated_with_cap(n): n :: i64 r0 :: vec[i64] - r1 :: object - r2 :: ptr - r3 :: i64 - r4, r5 :: ptr - r6 :: bit - r7 :: ptr + r1 :: ptr + r2 :: i64 + r3, r4 :: ptr + r5 :: bit + r6 :: ptr L0: r0 = VecI64Api.alloc(n, 10) - r1 = r0.buf - r2 = get_element_ptr r1 items :: VecI64BufObject - r3 = n * 8 - r4 = r2 + r3 - r5 = r2 + r1 = r0.items + r2 = n * 8 + r3 = r1 + r2 + r4 = r1 L1: - r6 = r5 < r4 :: unsigned - if r6 goto L2 else goto L3 :: bool + r5 = r4 < r3 :: unsigned + if r5 goto L2 else goto L3 :: bool L2: - set_mem r5, 3 :: i64* - r7 = r5 + 8 - r5 = r7 + set_mem r4, 3 :: i64* + r6 = r4 + 8 + r4 = r6 goto L1 L3: keep_alive r0 diff --git a/mypyc/test-data/irbuild-vec-misc.test b/mypyc/test-data/irbuild-vec-misc.test index 58753326b905..cb02cbe5d4d8 100644 --- a/mypyc/test-data/irbuild-vec-misc.test +++ b/mypyc/test-data/irbuild-vec-misc.test @@ -60,16 +60,14 @@ def create_i32() -> vec[i32]: [out] def create_i32(): r0 :: vec[i32] - r1 :: object - r2, r3, r4 :: ptr + r1, r2, r3 :: ptr L0: r0 = VecI32Api.alloc(2, 2) - r1 = r0.buf - r2 = get_element_ptr r1 items :: VecI32BufObject - set_mem r2, 1 :: i32* + r1 = r0.items + set_mem r1, 1 :: i32* + r2 = r1 + 4 + set_mem r2, -5 :: i32* r3 = r2 + 4 - set_mem r3, -5 :: i32* - r4 = r3 + 4 keep_alive r0 return r0 @@ -127,11 +125,10 @@ def get_item_bool(v, i): r3 :: bit r4 :: bool r5 :: i64 - r6 :: object - r7 :: ptr - r8 :: i64 - r9 :: ptr - r10 :: bool + r6 :: ptr + r7 :: i64 + r8 :: ptr + r9 :: bool L0: r0 = v.len r1 = i < r0 :: unsigned @@ -149,13 +146,12 @@ L3: L4: r5 = i L5: - r6 = v.buf - r7 = get_element_ptr r6 items :: VecBoolBufObject - r8 = r5 * 1 - r9 = r7 + r8 - r10 = load_mem r9 :: builtins.bool* + r6 = v.items + r7 = r5 * 1 + r8 = r6 + r7 + r9 = load_mem r8 :: builtins.bool* keep_alive v - return r10 + return r9 [case testVecMiscPop] from librt.vecs import vec, pop @@ -229,12 +225,11 @@ def for_bool(v): s :: i16 r0, r1 :: native_int r2 :: bit - r3 :: object - r4 :: ptr - r5 :: native_int - r6 :: ptr - r7, x, r8 :: i16 - r9 :: native_int + r3 :: ptr + r4 :: native_int + r5 :: ptr + r6, x, r7 :: i16 + r8 :: native_int L0: s = 0 r0 = 0 @@ -243,18 +238,17 @@ L1: r2 = r0 < r1 :: signed if r2 goto L2 else goto L4 :: bool L2: - r3 = v.buf - r4 = get_element_ptr r3 items :: VecI16BufObject - r5 = r0 * 2 - r6 = r4 + r5 - r7 = load_mem r6 :: i16* - x = r7 + r3 = v.items + r4 = r0 * 2 + r5 = r3 + r4 + r6 = load_mem r5 :: i16* + x = r6 keep_alive v - r8 = s + x - s = r8 + r7 = s + x + s = r7 L3: - r9 = r0 + 1 - r0 = r9 + r8 = r0 + 1 + r0 = r8 goto L1 L4: return s @@ -276,11 +270,10 @@ def get_item_nested(v, i): r3 :: bit r4 :: bool r5 :: i64 - r6 :: object - r7 :: ptr - r8 :: i64 - r9 :: ptr - r10 :: vec[i32] + r6 :: ptr + r7 :: i64 + r8 :: ptr + r9 :: vec[i32] L0: r0 = v.len r1 = i < r0 :: unsigned @@ -298,13 +291,12 @@ L3: L4: r5 = i L5: - r6 = v.buf - r7 = get_element_ptr r6 items :: VecNestedBufObject - r8 = r5 * 16 - r9 = r7 + r8 - r10 = load_mem r9 :: vec[i32]* + r6 = v.items + r7 = r5 * 16 + r8 = r6 + r7 + r9 = load_mem r8 :: vec[i32]* keep_alive v - return r10 + return r9 [case testVecMiscNestedPop_64bit] from librt.vecs import vec, pop diff --git a/mypyc/test-data/irbuild-vec-nested.test b/mypyc/test-data/irbuild-vec-nested.test index 2d0b4dbbcae7..8a4a4dfeb036 100644 --- a/mypyc/test-data/irbuild-vec-nested.test +++ b/mypyc/test-data/irbuild-vec-nested.test @@ -104,17 +104,19 @@ def f(v, vv): v :: vec[vec[str]] vv :: vec[str] r0 :: native_int - r1 :: object - r2, r3 :: VecNestedBufItem{len:native_int, buf:object_nrc} - r4 :: vec[vec[str]] + r1, r2, r3 :: ptr + r4, r5 :: VecNestedBufItem{len:native_int, buf:object_nrc} + r6 :: vec[vec[str]] L0: r0 = vv.len - r1 = vv.buf - r2 = set_element undef VecNestedBufItem, len, r0 - r3 = set_element r2, buf, r1 - r4 = VecNestedApi.append(v, r3) + r1 = vv.items + r2 = get_element_ptr 0 items :: VecTBufObject + r3 = r1 - r2 + r4 = set_element undef VecNestedBufItem, len, r0 + r5 = set_element r4, buf, r3 + r6 = VecNestedApi.append(v, r5) keep_alive vv - return r4 + return r6 [case testVecNestedVecI64Append] from librt.vecs import vec, append @@ -127,17 +129,19 @@ def f(v, vv): v :: vec[vec[i64]] vv :: vec[i64] r0 :: native_int - r1 :: object - r2, r3 :: VecNestedBufItem{len:native_int, buf:object_nrc} - r4 :: vec[vec[i64]] + r1, r2, r3 :: ptr + r4, r5 :: VecNestedBufItem{len:native_int, buf:object_nrc} + r6 :: vec[vec[i64]] L0: r0 = vv.len - r1 = vv.buf - r2 = set_element undef VecNestedBufItem, len, r0 - r3 = set_element r2, buf, r1 - r4 = VecNestedApi.append(v, r3) + r1 = vv.items + r2 = get_element_ptr 0 items :: VecI64BufObject + r3 = r1 - r2 + r4 = set_element undef VecNestedBufItem, len, r0 + r5 = set_element r4, buf, r3 + r6 = VecNestedApi.append(v, r5) keep_alive vv - return r4 + return r6 [case testVecNestedExtend] from librt.vecs import vec, extend @@ -206,11 +210,10 @@ def f(v, n): r3 :: bit r4 :: bool r5 :: i64 - r6 :: object - r7 :: ptr - r8 :: i64 - r9 :: ptr - r10 :: vec[str] + r6 :: ptr + r7 :: i64 + r8 :: ptr + r9 :: vec[str] L0: r0 = v.len r1 = n < r0 :: unsigned @@ -228,13 +231,12 @@ L3: L4: r5 = n L5: - r6 = v.buf - r7 = get_element_ptr r6 items :: VecNestedBufObject - r8 = r5 * 16 - r9 = r7 + r8 - r10 = load_mem r9 :: vec[str]* + r6 = v.items + r7 = r5 * 16 + r8 = r6 + r7 + r9 = load_mem r8 :: vec[str]* keep_alive v - return r10 + return r9 [case testVecNestedI64GetItem_64bit] from librt.vecs import vec @@ -252,11 +254,10 @@ def f(v, n): r3 :: bit r4 :: bool r5 :: i64 - r6 :: object - r7 :: ptr - r8 :: i64 - r9 :: ptr - r10 :: vec[i64] + r6 :: ptr + r7 :: i64 + r8 :: ptr + r9 :: vec[i64] L0: r0 = v.len r1 = n < r0 :: unsigned @@ -274,13 +275,12 @@ L3: L4: r5 = n L5: - r6 = v.buf - r7 = get_element_ptr r6 items :: VecNestedBufObject - r8 = r5 * 16 - r9 = r7 + r8 - r10 = load_mem r9 :: vec[i64]* + r6 = v.items + r7 = r5 * 16 + r8 = r6 + r7 + r9 = load_mem r8 :: vec[i64]* keep_alive v - return r10 + return r9 [case testVecNestedI64GetItemWithBorrow_64bit] from librt.vecs import vec @@ -298,22 +298,20 @@ def f(v, n): r3 :: bit r4 :: bool r5 :: i64 - r6 :: object - r7 :: ptr - r8 :: i64 - r9 :: ptr - r10 :: vec[i64] - r11 :: native_int - r12 :: bit - r13 :: i64 - r14 :: bit - r15 :: bool - r16 :: i64 - r17 :: object + r6 :: ptr + r7 :: i64 + r8 :: ptr + r9 :: vec[i64] + r10 :: native_int + r11 :: bit + r12 :: i64 + r13 :: bit + r14 :: bool + r15 :: i64 + r16 :: ptr + r17 :: i64 r18 :: ptr r19 :: i64 - r20 :: ptr - r21 :: i64 L0: r0 = v.len r1 = n < r0 :: unsigned @@ -331,34 +329,32 @@ L3: L4: r5 = n L5: - r6 = v.buf - r7 = get_element_ptr r6 items :: VecNestedBufObject - r8 = r5 * 16 - r9 = r7 + r8 - r10 = borrow load_mem r9 :: vec[i64]* - r11 = r10.len - r12 = n < r11 :: unsigned - if r12 goto L9 else goto L6 :: bool + r6 = v.items + r7 = r5 * 16 + r8 = r6 + r7 + r9 = borrow load_mem r8 :: vec[i64]* + r10 = r9.len + r11 = n < r10 :: unsigned + if r11 goto L9 else goto L6 :: bool L6: - r13 = n + r11 - r14 = r13 < r11 :: unsigned - if r14 goto L8 else goto L7 :: bool + r12 = n + r10 + r13 = r12 < r10 :: unsigned + if r13 goto L8 else goto L7 :: bool L7: - r15 = raise IndexError + r14 = raise IndexError unreachable L8: - r16 = r13 + r15 = r12 goto L10 L9: - r16 = n + r15 = n L10: - r17 = r10.buf - r18 = get_element_ptr r17 items :: VecI64BufObject - r19 = r16 * 8 - r20 = r18 + r19 - r21 = load_mem r20 :: i64* - keep_alive v, r10 - return r21 + r16 = r9.items + r17 = r15 * 8 + r18 = r16 + r17 + r19 = load_mem r18 :: i64* + keep_alive v, r9 + return r19 [case testVecDoublyNestedGetItem_64bit] from librt.vecs import vec @@ -376,11 +372,10 @@ def f(v, n): r3 :: bit r4 :: bool r5 :: i64 - r6 :: object - r7 :: ptr - r8 :: i64 - r9 :: ptr - r10 :: vec[vec[str]] + r6 :: ptr + r7 :: i64 + r8 :: ptr + r9 :: vec[vec[str]] L0: r0 = v.len r1 = n < r0 :: unsigned @@ -398,13 +393,12 @@ L3: L4: r5 = n L5: - r6 = v.buf - r7 = get_element_ptr r6 items :: VecNestedBufObject - r8 = r5 * 16 - r9 = r7 + r8 - r10 = load_mem r9 :: vec[vec[str]]* + r6 = v.items + r7 = r5 * 16 + r8 = r6 + r7 + r9 = load_mem r8 :: vec[vec[str]]* keep_alive v - return r10 + return r9 [case testVecNestedCreateWithCap_64bit] from librt.vecs import vec diff --git a/mypyc/test-data/irbuild-vec-t.test b/mypyc/test-data/irbuild-vec-t.test index b2bd185c039c..63ad14bc2d7a 100644 --- a/mypyc/test-data/irbuild-vec-t.test +++ b/mypyc/test-data/irbuild-vec-t.test @@ -215,11 +215,10 @@ def f(v, n): r3 :: bit r4 :: bool r5 :: i64 - r6 :: object - r7 :: ptr - r8 :: i64 - r9 :: ptr - r10 :: str + r6 :: ptr + r7 :: i64 + r8 :: ptr + r9 :: str L0: r0 = v.len r1 = n < r0 :: unsigned @@ -237,13 +236,12 @@ L3: L4: r5 = n L5: - r6 = v.buf - r7 = get_element_ptr r6 items :: VecTBufObject - r8 = r5 * 8 - r9 = r7 + r8 - r10 = load_mem r9 :: builtins.str* + r6 = v.items + r7 = r5 * 8 + r8 = r6 + r7 + r9 = load_mem r8 :: builtins.str* keep_alive v - return r10 + return r9 [case testVecTOptionalGetItem_64bit] from librt.vecs import vec @@ -262,11 +260,10 @@ def f(v, n): r3 :: bit r4 :: bool r5 :: i64 - r6 :: object - r7 :: ptr - r8 :: i64 - r9 :: ptr - r10 :: union[str, None] + r6 :: ptr + r7 :: i64 + r8 :: ptr + r9 :: union[str, None] L0: r0 = v.len r1 = n < r0 :: unsigned @@ -284,13 +281,12 @@ L3: L4: r5 = n L5: - r6 = v.buf - r7 = get_element_ptr r6 items :: VecTBufObject - r8 = r5 * 8 - r9 = r7 + r8 - r10 = load_mem r9 :: union* + r6 = v.items + r7 = r5 * 8 + r8 = r6 + r7 + r9 = load_mem r8 :: union* keep_alive v - return r10 + return r9 [case testNewTPopLast] from typing import Tuple @@ -463,20 +459,18 @@ def list_with_cap(): r2 :: object r3 :: ptr r4 :: vec[str] - r5 :: object - r6, r7, r8 :: ptr + r5, r6, r7 :: ptr L0: r0 = 'a' r1 = 'b' r2 = load_address PyUnicode_Type r3 = r2 r4 = VecTApi.alloc(2, 5, r3) - r5 = r4.buf - r6 = get_element_ptr r5 items :: VecTBufObject - set_mem r6, r0 :: builtins.str* + r5 = r4.items + set_mem r5, r0 :: builtins.str* + r6 = r5 + 8 + set_mem r6, r1 :: builtins.str* r7 = r6 + 8 - set_mem r7, r1 :: builtins.str* - r8 = r7 + 8 keep_alive r4 return r4 From 03bb923a337fc1c1f63ed534d2ef8ce2ee8506ed Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 30 Apr 2026 18:00:24 +0100 Subject: [PATCH 6/9] WIP fixes --- mypyc/irbuild/vec.py | 89 ++++++++++-- mypyc/test-data/irbuild-vec-misc.test | 9 +- mypyc/test-data/irbuild-vec-nested.test | 141 +++++++++++------- mypyc/test-data/refcount.test | 184 ++++++++++++------------ 4 files changed, 264 insertions(+), 159 deletions(-) diff --git a/mypyc/irbuild/vec.py b/mypyc/irbuild/vec.py index b0f5645f418a..22cc307791c0 100644 --- a/mypyc/irbuild/vec.py +++ b/mypyc/irbuild/vec.py @@ -17,6 +17,7 @@ GetElement, GetElementPtr, Integer, + IncRef, IntOp, RaiseStandardError, Register, @@ -47,6 +48,7 @@ is_int_rprimitive, is_short_int_rprimitive, list_rprimitive, + object_non_refcounted_rprimitive, object_rprimitive, optional_value_type, pointer_rprimitive, @@ -170,7 +172,7 @@ def vec_create_initialized( for_loop = builder.begin_for( items_start, items_end, Integer(step, c_pyssize_t_rprimitive), signed=False ) - builder.set_mem(for_loop.index, item_type, init) + vec_set_mem_item(builder, for_loop.index, item_type, init, line) for_loop.finish() builder.keep_alive([vec], line) @@ -190,7 +192,7 @@ def vec_create_from_values( item_type = vtype.item_type step = step_size(item_type) for value in values: - builder.set_mem(ptr, item_type, value) + vec_set_mem_item(builder, ptr, item_type, value, line) ptr = builder.int_add(ptr, step) builder.keep_alive([vec], line) return vec @@ -205,6 +207,13 @@ def step_size(item_type: RType) -> int: return PLATFORM_SIZE +def vec_storage_type(item_type: RType) -> RType: + """Return the actual memory type used for items in a vec buffer.""" + if isinstance(item_type, RVec): + return VecNestedBufItem + return item_type + + VEC_TYPE_INFO_I64: Final = 2 @@ -254,14 +263,34 @@ def vec_items(builder: LowLevelIRBuilder, vecobj: Value) -> Value: def vec_buf(builder: LowLevelIRBuilder, vecobj: Value) -> Value: """Recover the buffer object pointer from a vec's items pointer. - Emits: items_ptr - offsetof(BufType, items) + Emits NULL if items is NULL, otherwise: + (PyObject *)(items_ptr - offsetof(BufType, items)) """ vtype = vecobj.type assert isinstance(vtype, RVec) + line = vecobj.line items = builder.get_element(vecobj, "items") + + result = Register(object_non_refcounted_rprimitive) + is_null = builder.add( + ComparisonOp(items, Integer(0, pointer_rprimitive, line), ComparisonOp.EQ, line) + ) + null_block, non_null_block, done = BasicBlock(), BasicBlock(), BasicBlock() + builder.add(Branch(is_null, null_block, non_null_block, Branch.BOOL, line)) + + builder.activate_block(null_block) + builder.add(Assign(result, Integer(0, pointer_rprimitive, line), line)) + builder.goto(done) + + builder.activate_block(non_null_block) # offsetof(BufType, items) == GetElementPtr(0, buf_type, "items") offset = builder.add(GetElementPtr(Integer(0, pointer_rprimitive), vtype.buf_type, "items")) - return builder.int_sub(items, offset) + buf_ptr = builder.int_sub(items, offset) + builder.add(Assign(result, buf_ptr, line)) + builder.goto(done) + + builder.activate_block(done) + return result def vec_item_ptr(builder: LowLevelIRBuilder, vecobj: Value, index: Value) -> Value: @@ -279,6 +308,36 @@ def vec_item_ptr(builder: LowLevelIRBuilder, vecobj: Value, index: Value) -> Val return builder.int_add(items_addr, delta) +def vec_load_mem_item( + builder: LowLevelIRBuilder, + ptr: Value, + item_type: RType, + line: int, + *, + can_borrow: bool = False, +) -> Value: + """Load a vec item from storage, converting nested vec slots to RVec values.""" + if isinstance(item_type, RVec): + item = builder.load_mem(ptr, VecNestedBufItem, borrow=True) + result = convert_from_t_ext_item(builder, item, item_type, is_borrowed=can_borrow) + if not can_borrow: + # The storage item only holds a borrowed buffer reference. + # Make the returned RVec own a reference like a normal item load. + builder.add(IncRef(result, line)) + return result + return builder.load_mem(ptr, item_type, borrow=can_borrow) + + +def vec_set_mem_item( + builder: LowLevelIRBuilder, ptr: Value, item_type: RType, item: Value, line: int +) -> None: + """Store a vec item, converting RVec values to nested storage items.""" + if isinstance(item_type, RVec): + builder.add(IncRef(item, line)) + item = convert_to_t_ext_item(builder, item) + builder.set_mem(ptr, vec_storage_type(item_type), item) + + def vec_check_and_adjust_index( builder: LowLevelIRBuilder, lenv: Value, index: Value, line: int ) -> Value: @@ -334,7 +393,7 @@ def vec_get_item_unsafe( index = as_platform_int(builder, index, line) vtype = base.type item_addr = vec_item_ptr(builder, base, index) - result = builder.load_mem(item_addr, vtype.item_type, borrow=can_borrow) + result = vec_load_mem_item(builder, item_addr, vtype.item_type, line, can_borrow=can_borrow) builder.keep_alives.append(base) return result @@ -353,9 +412,9 @@ def vec_set_item( if item_type.is_refcounted: # Read an unborrowed reference to cause a decref to be # generated for the old item. - old_item = builder.load_mem(item_addr, item_type, borrow=True) + old_item = vec_load_mem_item(builder, item_addr, item_type, line, can_borrow=True) builder.add(DecRef(old_item)) - builder.set_mem(item_addr, item_type, item) + vec_set_mem_item(builder, item_addr, item_type, item, line) builder.keep_alive([base], line) @@ -368,7 +427,7 @@ def vec_init_item_unsafe( item_addr = vec_item_ptr(builder, base, index) item_type = vtype.item_type item = builder.coerce(item, item_type, line) - builder.set_mem(item_addr, item_type, item) + vec_set_mem_item(builder, item_addr, item_type, item, line) builder.keep_alive([base], line) @@ -379,7 +438,9 @@ def convert_to_t_ext_item(builder: LowLevelIRBuilder, item: Value) -> Value: return builder.add(SetElement(temp, "buf", buf_ptr)) -def convert_from_t_ext_item(builder: LowLevelIRBuilder, item: Value, vec_type: RVec) -> Value: +def convert_from_t_ext_item( + builder: LowLevelIRBuilder, item: Value, vec_type: RVec, *, is_borrowed: bool = False +) -> Value: """Convert a value of type VecNestedBufItem to the corresponding RVec value.""" api_name = vec_api_by_item_type.get(vec_type.item_type) if api_name is not None: @@ -391,7 +452,13 @@ def convert_from_t_ext_item(builder: LowLevelIRBuilder, item: Value, vec_type: R return builder.add( CallC( - name, [item], vec_type, steals=[True], is_borrowed=False, error_kind=ERR_NEVER, line=-1 + name, + [item], + vec_type, + steals=[True], + is_borrowed=is_borrowed, + error_kind=ERR_NEVER, + line=-1, ) ) @@ -561,7 +628,7 @@ def vec_contains(builder: LowLevelIRBuilder, vec: Value, target: Value, line: in for_loop = builder.begin_for( items_start, items_end, Integer(step, c_pyssize_t_rprimitive), signed=False ) - item = builder.load_mem(for_loop.index, item_type, borrow=True) + item = vec_load_mem_item(builder, for_loop.index, item_type, line, can_borrow=True) comp = builder.binary_op(item, target, "==", line) false = BasicBlock() builder.add(Branch(comp, true, false, Branch.BOOL)) diff --git a/mypyc/test-data/irbuild-vec-misc.test b/mypyc/test-data/irbuild-vec-misc.test index cb02cbe5d4d8..b3e1ffdbea61 100644 --- a/mypyc/test-data/irbuild-vec-misc.test +++ b/mypyc/test-data/irbuild-vec-misc.test @@ -273,7 +273,8 @@ def get_item_nested(v, i): r6 :: ptr r7 :: i64 r8 :: ptr - r9 :: vec[i32] + r9 :: VecNestedBufItem{len:native_int, buf:object_nrc} + r10 :: vec[i32] L0: r0 = v.len r1 = i < r0 :: unsigned @@ -294,9 +295,11 @@ L5: r6 = v.items r7 = r5 * 16 r8 = r6 + r7 - r9 = load_mem r8 :: vec[i32]* + r9 = load_mem r8 :: VecNestedBufItem* + r10 = VecI32Api.convert_from_nested(r9) + inc_ref r10 keep_alive v - return r9 + return r10 [case testVecMiscNestedPop_64bit] from librt.vecs import vec, pop diff --git a/mypyc/test-data/irbuild-vec-nested.test b/mypyc/test-data/irbuild-vec-nested.test index 8a4a4dfeb036..1fe55295291f 100644 --- a/mypyc/test-data/irbuild-vec-nested.test +++ b/mypyc/test-data/irbuild-vec-nested.test @@ -104,19 +104,30 @@ def f(v, vv): v :: vec[vec[str]] vv :: vec[str] r0 :: native_int - r1, r2, r3 :: ptr - r4, r5 :: VecNestedBufItem{len:native_int, buf:object_nrc} - r6 :: vec[vec[str]] + r1 :: ptr + r2 :: bit + r3 :: object_nrc + r4, r5 :: ptr + r6, r7 :: VecNestedBufItem{len:native_int, buf:object_nrc} + r8 :: vec[vec[str]] L0: r0 = vv.len r1 = vv.items - r2 = get_element_ptr 0 items :: VecTBufObject - r3 = r1 - r2 - r4 = set_element undef VecNestedBufItem, len, r0 - r5 = set_element r4, buf, r3 - r6 = VecNestedApi.append(v, r5) + r2 = r1 == 0 + if r2 goto L1 else goto L2 :: bool +L1: + r3 = 0 + goto L3 +L2: + r4 = get_element_ptr 0 items :: VecTBufObject + r5 = r1 - r4 + r3 = r5 +L3: + r6 = set_element undef VecNestedBufItem, len, r0 + r7 = set_element r6, buf, r3 + r8 = VecNestedApi.append(v, r7) keep_alive vv - return r6 + return r8 [case testVecNestedVecI64Append] from librt.vecs import vec, append @@ -129,19 +140,30 @@ def f(v, vv): v :: vec[vec[i64]] vv :: vec[i64] r0 :: native_int - r1, r2, r3 :: ptr - r4, r5 :: VecNestedBufItem{len:native_int, buf:object_nrc} - r6 :: vec[vec[i64]] + r1 :: ptr + r2 :: bit + r3 :: object_nrc + r4, r5 :: ptr + r6, r7 :: VecNestedBufItem{len:native_int, buf:object_nrc} + r8 :: vec[vec[i64]] L0: r0 = vv.len r1 = vv.items - r2 = get_element_ptr 0 items :: VecI64BufObject - r3 = r1 - r2 - r4 = set_element undef VecNestedBufItem, len, r0 - r5 = set_element r4, buf, r3 - r6 = VecNestedApi.append(v, r5) + r2 = r1 == 0 + if r2 goto L1 else goto L2 :: bool +L1: + r3 = 0 + goto L3 +L2: + r4 = get_element_ptr 0 items :: VecI64BufObject + r5 = r1 - r4 + r3 = r5 +L3: + r6 = set_element undef VecNestedBufItem, len, r0 + r7 = set_element r6, buf, r3 + r8 = VecNestedApi.append(v, r7) keep_alive vv - return r6 + return r8 [case testVecNestedExtend] from librt.vecs import vec, extend @@ -213,7 +235,8 @@ def f(v, n): r6 :: ptr r7 :: i64 r8 :: ptr - r9 :: vec[str] + r9 :: VecNestedBufItem{len:native_int, buf:object_nrc} + r10 :: vec[str] L0: r0 = v.len r1 = n < r0 :: unsigned @@ -234,9 +257,11 @@ L5: r6 = v.items r7 = r5 * 16 r8 = r6 + r7 - r9 = load_mem r8 :: vec[str]* + r9 = load_mem r8 :: VecNestedBufItem* + r10 = VecTApi.convert_from_nested(r9) + inc_ref r10 keep_alive v - return r9 + return r10 [case testVecNestedI64GetItem_64bit] from librt.vecs import vec @@ -257,7 +282,8 @@ def f(v, n): r6 :: ptr r7 :: i64 r8 :: ptr - r9 :: vec[i64] + r9 :: VecNestedBufItem{len:native_int, buf:object_nrc} + r10 :: vec[i64] L0: r0 = v.len r1 = n < r0 :: unsigned @@ -278,9 +304,11 @@ L5: r6 = v.items r7 = r5 * 16 r8 = r6 + r7 - r9 = load_mem r8 :: vec[i64]* + r9 = load_mem r8 :: VecNestedBufItem* + r10 = VecI64Api.convert_from_nested(r9) + inc_ref r10 keep_alive v - return r9 + return r10 [case testVecNestedI64GetItemWithBorrow_64bit] from librt.vecs import vec @@ -301,17 +329,18 @@ def f(v, n): r6 :: ptr r7 :: i64 r8 :: ptr - r9 :: vec[i64] - r10 :: native_int - r11 :: bit - r12 :: i64 - r13 :: bit - r14 :: bool - r15 :: i64 - r16 :: ptr - r17 :: i64 - r18 :: ptr - r19 :: i64 + r9 :: VecNestedBufItem{len:native_int, buf:object_nrc} + r10 :: vec[i64] + r11 :: native_int + r12 :: bit + r13 :: i64 + r14 :: bit + r15 :: bool + r16 :: i64 + r17 :: ptr + r18 :: i64 + r19 :: ptr + r20 :: i64 L0: r0 = v.len r1 = n < r0 :: unsigned @@ -332,29 +361,30 @@ L5: r6 = v.items r7 = r5 * 16 r8 = r6 + r7 - r9 = borrow load_mem r8 :: vec[i64]* - r10 = r9.len - r11 = n < r10 :: unsigned - if r11 goto L9 else goto L6 :: bool + r9 = load_mem r8 :: VecNestedBufItem* + r10 = VecI64Api.convert_from_nested(r9) + r11 = r10.len + r12 = n < r11 :: unsigned + if r12 goto L9 else goto L6 :: bool L6: - r12 = n + r10 - r13 = r12 < r10 :: unsigned - if r13 goto L8 else goto L7 :: bool + r13 = n + r11 + r14 = r13 < r11 :: unsigned + if r14 goto L8 else goto L7 :: bool L7: - r14 = raise IndexError + r15 = raise IndexError unreachable L8: - r15 = r12 + r16 = r13 goto L10 L9: - r15 = n + r16 = n L10: - r16 = r9.items - r17 = r15 * 8 - r18 = r16 + r17 - r19 = load_mem r18 :: i64* - keep_alive v, r9 - return r19 + r17 = r10.items + r18 = r16 * 8 + r19 = r17 + r18 + r20 = load_mem r19 :: i64* + keep_alive v, r10 + return r20 [case testVecDoublyNestedGetItem_64bit] from librt.vecs import vec @@ -375,7 +405,8 @@ def f(v, n): r6 :: ptr r7 :: i64 r8 :: ptr - r9 :: vec[vec[str]] + r9 :: VecNestedBufItem{len:native_int, buf:object_nrc} + r10 :: vec[vec[str]] L0: r0 = v.len r1 = n < r0 :: unsigned @@ -396,9 +427,11 @@ L5: r6 = v.items r7 = r5 * 16 r8 = r6 + r7 - r9 = load_mem r8 :: vec[vec[str]]* + r9 = load_mem r8 :: VecNestedBufItem* + r10 = VecNestedApi.convert_from_nested(r9) + inc_ref r10 keep_alive v - return r9 + return r10 [case testVecNestedCreateWithCap_64bit] from librt.vecs import vec diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index 733d23d3dd6c..472bd14ed116 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -1519,11 +1519,10 @@ def f(v, i, x): r3 :: bit r4 :: bool r5 :: i64 - r6 :: object - r7 :: ptr - r8 :: i64 - r9 :: ptr - r10 :: str + r6 :: ptr + r7 :: i64 + r8 :: ptr + r9 :: str L0: r0 = v.len r1 = i < r0 :: unsigned @@ -1541,14 +1540,13 @@ L3: L4: r5 = i L5: - r6 = v.buf - r7 = get_element_ptr r6 items :: VecTBufObject - r8 = r5 * 8 - r9 = r7 + r8 - r10 = borrow load_mem r9 :: builtins.str* - dec_ref r10 + r6 = v.items + r7 = r5 * 8 + r8 = r6 + r7 + r9 = borrow load_mem r8 :: builtins.str* + dec_ref r9 inc_ref x - set_mem r9, x :: builtins.str* + set_mem r8, x :: builtins.str* return 1 [case testVecTConstructFromListMultiply_64bit] @@ -1564,31 +1562,29 @@ def f(n): r0, r1 :: object r2, r3 :: ptr r4 :: vec[str | None] - r5 :: object - r6 :: ptr - r7 :: i64 - r8, r9 :: ptr - r10 :: bit - r11 :: ptr + r5 :: ptr + r6 :: i64 + r7, r8 :: ptr + r9 :: bit + r10 :: ptr L0: r0 = box(None, 1) r1 = load_address PyUnicode_Type r2 = r1 r3 = r2 | 1 r4 = VecTApi.alloc(n, n, r3) - r5 = r4.buf - r6 = get_element_ptr r5 items :: VecTBufObject - r7 = n * 8 - r8 = r6 + r7 - r9 = r6 + r5 = r4.items + r6 = n * 8 + r7 = r5 + r6 + r8 = r5 L1: - r10 = r9 < r8 :: unsigned - if r10 goto L2 else goto L3 :: bool + r9 = r8 < r7 :: unsigned + if r9 goto L2 else goto L3 :: bool L2: inc_ref r0 - set_mem r9, r0 :: union* - r11 = r9 + 8 - r9 = r11 + set_mem r8, r0 :: union* + r10 = r8 + 8 + r8 = r10 goto L1 L3: return r4 @@ -1610,13 +1606,12 @@ def f(v): t :: i64 r0, r1 :: native_int r2 :: bit - r3 :: object - r4 :: ptr - r5 :: native_int - r6 :: ptr - r7, s :: str - r8 :: None - r9 :: native_int + r3 :: ptr + r4 :: native_int + r5 :: ptr + r6, s :: str + r7 :: None + r8 :: native_int L0: t = 0 r0 = 0 @@ -1625,17 +1620,16 @@ L1: r2 = r0 < r1 :: signed if r2 goto L2 else goto L4 :: bool L2: - r3 = v.buf - r4 = get_element_ptr r3 items :: VecTBufObject - r5 = r0 * 8 - r6 = r4 + r5 - r7 = load_mem r6 :: builtins.str* - s = r7 - r8 = g(s) + r3 = v.items + r4 = r0 * 8 + r5 = r3 + r4 + r6 = load_mem r5 :: builtins.str* + s = r6 + r7 = g(s) dec_ref s L3: - r9 = r0 + 1 - r0 = r9 + r8 = r0 + 1 + r0 = r8 goto L1 L4: return t @@ -1655,22 +1649,20 @@ def f(): r2 :: object r3 :: ptr r4 :: vec[str] - r5 :: object - r6, r7, r8 :: ptr + r5, r6, r7 :: ptr L0: r0 = 'x' r1 = 'y' r2 = load_address PyUnicode_Type r3 = r2 r4 = VecTApi.alloc(2, 2, r3) - r5 = r4.buf - r6 = get_element_ptr r5 items :: VecTBufObject + r5 = r4.items inc_ref r0 - set_mem r6, r0 :: builtins.str* - r7 = r6 + 8 + set_mem r5, r0 :: builtins.str* + r6 = r5 + 8 inc_ref r1 - set_mem r7, r1 :: builtins.str* - r8 = r7 + 8 + set_mem r6, r1 :: builtins.str* + r7 = r6 + 8 return r4 [case testVecI64GetItemBorrowVec_64bit] @@ -1693,11 +1685,10 @@ def C.f(self, x): r4 :: bit r5 :: bool r6 :: i64 - r7 :: object - r8 :: ptr - r9 :: i64 - r10 :: ptr - r11 :: i64 + r7 :: ptr + r8 :: i64 + r9 :: ptr + r10 :: i64 L0: r0 = borrow self.v r1 = r0.len @@ -1716,12 +1707,11 @@ L3: L4: r6 = x L5: - r7 = r0.buf - r8 = get_element_ptr r7 items :: VecI64BufObject - r9 = r6 * 8 - r10 = r8 + r9 - r11 = load_mem r10 :: i64* - return r11 + r7 = r0.items + r8 = r6 * 8 + r9 = r7 + r8 + r10 = load_mem r9 :: i64* + return r10 [case testVecI64LenBorrowVec_64bit] from librt.vecs import vec @@ -1753,17 +1743,30 @@ def f(vv, v): vv :: vec[vec[str]] v :: vec[str] r0 :: native_int - r1 :: object - r2, r3 :: VecNestedBufItem{len:native_int, buf:object_nrc} - r4 :: vec[vec[str]] + r1 :: ptr + r2 :: bit + r3 :: object_nrc + r4, r5 :: ptr + r6, r7 :: VecNestedBufItem{len:native_int, buf:object_nrc} + r8 :: vec[vec[str]] L0: r0 = v.len - r1 = v.buf - r2 = set_element undef VecNestedBufItem, len, r0 - r3 = set_element r2, buf, r1 + r1 = v.items + r2 = r1 == 0 + if r2 goto L1 else goto L2 :: bool +L1: + r3 = 0 + goto L3 +L2: + r4 = get_element_ptr 0 items :: VecTBufObject + r5 = r1 - r4 + r3 = r5 +L3: + r6 = set_element undef VecNestedBufItem, len, r0 + r7 = set_element r6, buf, r3 inc_ref vv - r4 = VecNestedApi.append(vv, r3) - return r4 + r8 = VecNestedApi.append(vv, r7) + return r8 [case testVecTGetItem_64bit] from librt.vecs import vec @@ -1781,11 +1784,10 @@ def f(v, n): r3 :: bit r4 :: bool r5 :: i64 - r6 :: object - r7 :: ptr - r8 :: i64 - r9 :: ptr - r10 :: str + r6 :: ptr + r7 :: i64 + r8 :: ptr + r9 :: str L0: r0 = v.len r1 = n < r0 :: unsigned @@ -1803,12 +1805,11 @@ L3: L4: r5 = n L5: - r6 = v.buf - r7 = get_element_ptr r6 items :: VecTBufObject - r8 = r5 * 8 - r9 = r7 + r8 - r10 = load_mem r9 :: builtins.str* - return r10 + r6 = v.items + r7 = r5 * 8 + r8 = r6 + r7 + r9 = load_mem r8 :: builtins.str* + return r9 [case testVecNestedGetItem_64bit] from librt.vecs import vec @@ -1829,10 +1830,10 @@ def f(v, n): r6 :: bit r7 :: bool r8 :: i64 - r9 :: object - r10 :: ptr - r11 :: i64 - r12 :: ptr + r9 :: ptr + r10 :: i64 + r11 :: ptr + r12 :: VecNestedBufItem{len:native_int, buf:object_nrc} r13, vv :: vec[str] L0: r0 = load_address PyUnicode_Type @@ -1854,11 +1855,12 @@ L3: L4: r8 = n L5: - r9 = r2.buf - r10 = get_element_ptr r9 items :: VecNestedBufObject - r11 = r8 * 16 - r12 = r10 + r11 - r13 = load_mem r12 :: vec[str]* + r9 = r2.items + r10 = r8 * 16 + r11 = r9 + r10 + r12 = load_mem r11 :: VecNestedBufItem* + r13 = VecTApi.convert_from_nested(r12) + inc_ref r13 dec_ref r2 vv = r13 dec_ref vv From b9774503beeb28788a9d0a4cfeac2777ceb96dd1 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 30 Apr 2026 18:08:37 +0100 Subject: [PATCH 7/9] Some more fixes --- mypyc/codegen/emitfunc.py | 4 ++-- mypyc/irbuild/vec.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 737b002b4201..a0c2bd2b33e9 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -557,7 +557,7 @@ def visit_tuple_get(self, op: TupleGet) -> None: dest = self.reg(op) src = self.reg(op.src) self.emit_line(f"{dest} = {src}.f{op.index};") - if not op.is_borrowed: + if not op.is_borrowed and op.type.is_refcounted: self.emit_inc_ref(dest, op.type) def get_dest_assign(self, dest: Value) -> str: @@ -794,7 +794,7 @@ def visit_load_mem(self, op: LoadMem) -> None: # TODO: we shouldn't dereference to type that are pointer type so far type = self.ctype(op.type) self.emit_line(f"{dest} = *({type} *){src};") - if not op.is_borrowed: + if not op.is_borrowed and op.type.is_refcounted: self.emit_inc_ref(dest, op.type) def visit_set_mem(self, op: SetMem) -> None: diff --git a/mypyc/irbuild/vec.py b/mypyc/irbuild/vec.py index 22cc307791c0..e091d4ac3f16 100644 --- a/mypyc/irbuild/vec.py +++ b/mypyc/irbuild/vec.py @@ -273,13 +273,13 @@ def vec_buf(builder: LowLevelIRBuilder, vecobj: Value) -> Value: result = Register(object_non_refcounted_rprimitive) is_null = builder.add( - ComparisonOp(items, Integer(0, pointer_rprimitive, line), ComparisonOp.EQ, line) + ComparisonOp(items, Integer(0, c_size_t_rprimitive, line), ComparisonOp.EQ, line) ) null_block, non_null_block, done = BasicBlock(), BasicBlock(), BasicBlock() builder.add(Branch(is_null, null_block, non_null_block, Branch.BOOL, line)) builder.activate_block(null_block) - builder.add(Assign(result, Integer(0, pointer_rprimitive, line), line)) + builder.add(Assign(result, Integer(0, object_non_refcounted_rprimitive, line), line)) builder.goto(done) builder.activate_block(non_null_block) @@ -455,7 +455,7 @@ def convert_from_t_ext_item( name, [item], vec_type, - steals=[True], + steals=[False], is_borrowed=is_borrowed, error_kind=ERR_NEVER, line=-1, From c5593933f451815ab73dfaac6b25f61aebe37ea5 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 6 May 2026 11:29:48 +0100 Subject: [PATCH 8/9] Use the new representation more consistently --- mypyc/codegen/emitfunc.py | 20 +++- mypyc/ir/rtypes.py | 4 +- mypyc/irbuild/vec.py | 90 ++-------------- mypyc/lib-rt/vecs/librt_vecs.c | 7 +- mypyc/lib-rt/vecs/librt_vecs.h | 116 +++++++++++++++----- mypyc/lib-rt/vecs/vec_nested.c | 60 ++++++----- mypyc/lib-rt/vecs/vec_t.c | 2 +- mypyc/lib-rt/vecs/vec_template.c | 2 +- mypyc/test-data/irbuild-vec-misc.test | 48 +++++---- mypyc/test-data/irbuild-vec-nested.test | 133 +++++++++-------------- mypyc/test-data/refcount.test | 135 +++++++++++------------- 11 files changed, 290 insertions(+), 327 deletions(-) diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index a0c2bd2b33e9..f89a4f06d20c 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -86,6 +86,17 @@ is_tagged, ) +VEC_ITEMS_C_TYPE: Final = { + "VecI64": "int64_t *", + "VecI32": "int32_t *", + "VecI16": "int16_t *", + "VecU8": "uint8_t *", + "VecFloat": "double *", + "VecBool": "char *", + "VecT": "PyObject **", + "VecNested": "VecNestedBufItem *", +} + def native_function_type(fn: FuncIR, emitter: Emitter) -> str: return native_function_type_from_decl(fn.decl, emitter) @@ -829,8 +840,8 @@ def visit_get_element_ptr(self, op: GetElementPtr) -> None: def visit_set_element(self, op: SetElement) -> None: dest = self.reg(op) - item = self.reg(op.item) field = op.field + item = self.set_element_item(op.src.type, field, self.reg(op.item)) if isinstance(op.src, Undef): # First assignment to an undefined struct is trivial. self.emit_line(f"{dest}.{field} = {item};") @@ -843,7 +854,7 @@ def visit_set_element(self, op: SetElement) -> None: # TODO: Support tuples (or use RStruct for tuples)? src = self.reg(op.src) src_type = op.src.type - assert isinstance(src_type, RStruct), src_type + assert isinstance(src_type, (RStruct, RVec)), src_type init_items = [] for n in src_type.names: if n != field: @@ -852,6 +863,11 @@ def visit_set_element(self, op: SetElement) -> None: init_items.append(item) self.emit_line(f"{dest} = ({self.ctype(src_type)}) {{ {', '.join(init_items)} }};") + def set_element_item(self, src_type: RType, field: str, item: str) -> str: + if isinstance(src_type, RVec) and field == "items": + return f"({VEC_ITEMS_C_TYPE[src_type._ctype]}){item}" + return item + def visit_load_address(self, op: LoadAddress) -> None: typ = op.type dest = self.reg(op) diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 961ddf2116e1..60e9b49582bc 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -1364,8 +1364,8 @@ def check_native_int_range(rtype: RPrimitive, n: int) -> bool: VecNestedBufItem = RStruct( name="VecNestedBufItem", - names=["len", "buf"], - types=[c_pyssize_t_rprimitive, object_non_refcounted_rprimitive], + names=["len", "items"], + types=[c_pyssize_t_rprimitive, pointer_rprimitive], ) # Buffer for vec[vec[t]] diff --git a/mypyc/irbuild/vec.py b/mypyc/irbuild/vec.py index e091d4ac3f16..36ec7c754966 100644 --- a/mypyc/irbuild/vec.py +++ b/mypyc/irbuild/vec.py @@ -7,7 +7,6 @@ from mypyc.common import IS_32_BIT_PLATFORM, PLATFORM_SIZE from mypyc.ir.ops import ( ERR_MAGIC, - ERR_NEVER, Assign, BasicBlock, Branch, @@ -15,9 +14,7 @@ ComparisonOp, DecRef, GetElement, - GetElementPtr, Integer, - IncRef, IntOp, RaiseStandardError, Register, @@ -48,7 +45,6 @@ is_int_rprimitive, is_short_int_rprimitive, list_rprimitive, - object_non_refcounted_rprimitive, object_rprimitive, optional_value_type, pointer_rprimitive, @@ -207,13 +203,6 @@ def step_size(item_type: RType) -> int: return PLATFORM_SIZE -def vec_storage_type(item_type: RType) -> RType: - """Return the actual memory type used for items in a vec buffer.""" - if isinstance(item_type, RVec): - return VecNestedBufItem - return item_type - - VEC_TYPE_INFO_I64: Final = 2 @@ -260,39 +249,6 @@ def vec_items(builder: LowLevelIRBuilder, vecobj: Value) -> Value: return builder.get_element(vecobj, "items") -def vec_buf(builder: LowLevelIRBuilder, vecobj: Value) -> Value: - """Recover the buffer object pointer from a vec's items pointer. - - Emits NULL if items is NULL, otherwise: - (PyObject *)(items_ptr - offsetof(BufType, items)) - """ - vtype = vecobj.type - assert isinstance(vtype, RVec) - line = vecobj.line - items = builder.get_element(vecobj, "items") - - result = Register(object_non_refcounted_rprimitive) - is_null = builder.add( - ComparisonOp(items, Integer(0, c_size_t_rprimitive, line), ComparisonOp.EQ, line) - ) - null_block, non_null_block, done = BasicBlock(), BasicBlock(), BasicBlock() - builder.add(Branch(is_null, null_block, non_null_block, Branch.BOOL, line)) - - builder.activate_block(null_block) - builder.add(Assign(result, Integer(0, object_non_refcounted_rprimitive, line), line)) - builder.goto(done) - - builder.activate_block(non_null_block) - # offsetof(BufType, items) == GetElementPtr(0, buf_type, "items") - offset = builder.add(GetElementPtr(Integer(0, pointer_rprimitive), vtype.buf_type, "items")) - buf_ptr = builder.int_sub(items, offset) - builder.add(Assign(result, buf_ptr, line)) - builder.goto(done) - - builder.activate_block(done) - return result - - def vec_item_ptr(builder: LowLevelIRBuilder, vecobj: Value, index: Value) -> Value: items_addr = vec_items(builder, vecobj) assert isinstance(vecobj.type, RVec) @@ -317,14 +273,6 @@ def vec_load_mem_item( can_borrow: bool = False, ) -> Value: """Load a vec item from storage, converting nested vec slots to RVec values.""" - if isinstance(item_type, RVec): - item = builder.load_mem(ptr, VecNestedBufItem, borrow=True) - result = convert_from_t_ext_item(builder, item, item_type, is_borrowed=can_borrow) - if not can_borrow: - # The storage item only holds a borrowed buffer reference. - # Make the returned RVec own a reference like a normal item load. - builder.add(IncRef(result, line)) - return result return builder.load_mem(ptr, item_type, borrow=can_borrow) @@ -332,10 +280,7 @@ def vec_set_mem_item( builder: LowLevelIRBuilder, ptr: Value, item_type: RType, item: Value, line: int ) -> None: """Store a vec item, converting RVec values to nested storage items.""" - if isinstance(item_type, RVec): - builder.add(IncRef(item, line)) - item = convert_to_t_ext_item(builder, item) - builder.set_mem(ptr, vec_storage_type(item_type), item) + builder.set_mem(ptr, item_type, item) def vec_check_and_adjust_index( @@ -433,34 +378,17 @@ def vec_init_item_unsafe( def convert_to_t_ext_item(builder: LowLevelIRBuilder, item: Value) -> Value: vec_len = builder.add(GetElement(item, "len")) - buf_ptr = vec_buf(builder, item) + vec_items = builder.add(GetElement(item, "items")) temp = builder.add(SetElement(Undef(VecNestedBufItem), "len", vec_len)) - return builder.add(SetElement(temp, "buf", buf_ptr)) + return builder.add(SetElement(temp, "items", vec_items)) -def convert_from_t_ext_item( - builder: LowLevelIRBuilder, item: Value, vec_type: RVec, *, is_borrowed: bool = False -) -> Value: - """Convert a value of type VecNestedBufItem to the corresponding RVec value.""" - api_name = vec_api_by_item_type.get(vec_type.item_type) - if api_name is not None: - name = f"{api_name}.convert_from_nested" - elif isinstance(vec_type.item_type, RVec): - name = "VecNestedApi.convert_from_nested" - else: - name = "VecTApi.convert_from_nested" - - return builder.add( - CallC( - name, - [item], - vec_type, - steals=[False], - is_borrowed=is_borrowed, - error_kind=ERR_NEVER, - line=-1, - ) - ) +def convert_from_t_ext_item(builder: LowLevelIRBuilder, item: Value, vec_type: RVec) -> Value: + """Convert an owned VecNestedBufItem to the corresponding RVec value.""" + vec_len = builder.add(GetElement(item, "len")) + vec_items = builder.add(GetElement(item, "items")) + temp = builder.add(SetElement(Undef(vec_type), "len", vec_len)) + return builder.add(SetElement(temp, "items", vec_items)) def vec_item_type(builder: LowLevelIRBuilder, item_type: RType, line: int) -> Value: diff --git a/mypyc/lib-rt/vecs/librt_vecs.c b/mypyc/lib-rt/vecs/librt_vecs.c index 98ece4654520..cad8cbfb23d2 100644 --- a/mypyc/lib-rt/vecs/librt_vecs.c +++ b/mypyc/lib-rt/vecs/librt_vecs.c @@ -845,18 +845,19 @@ static PyObject *vec_pop(PyObject *self, PyObject *args) r = VecNested_Pop(v, index); if (VEC_IS_ERROR(r.f0)) return NULL; + PyObject *popped_buf = VecNested_ItemBuf(VEC_NESTED_BUF(r.f0), r.f1); result_item0 = VecNested_Box(r.f0); if (result_item0 == NULL) { - VEC_NESTED_DECREF(r.f0); - Py_DECREF(r.f1.buf); + Py_XDECREF(popped_buf); return NULL; } result_item1 = VecNested_BoxItem(r.f0, r.f1); if (result_item1 == NULL) { Py_DECREF(result_item0); - Py_DECREF(r.f1.buf); + Py_XDECREF(popped_buf); return NULL; } + Py_XDECREF(popped_buf); } else { PyErr_Format(PyExc_TypeError, "vec argument expected, got %.100s", Py_TYPE(vec)->tp_name); diff --git a/mypyc/lib-rt/vecs/librt_vecs.h b/mypyc/lib-rt/vecs/librt_vecs.h index 3adb35c5ad04..300dcc1727a0 100644 --- a/mypyc/lib-rt/vecs/librt_vecs.h +++ b/mypyc/lib-rt/vecs/librt_vecs.h @@ -82,7 +82,7 @@ typedef struct _VecTBufObject { typedef struct _VecNestedBufItem { Py_ssize_t len; - PyObject *buf; + CPyPtr items; } VecNestedBufItem; // Nested vec type: vec[vec[...]], vec[vec[...] | None], etc. @@ -146,14 +146,31 @@ typedef struct _VecNested { } VecNested; // Recover buffer object from items pointer. Only valid when items != NULL. -#define VEC_I64_BUF(v) ((VecI64BufObject *)((char *)(v).items - offsetof(VecI64BufObject, items))) -#define VEC_I32_BUF(v) ((VecI32BufObject *)((char *)(v).items - offsetof(VecI32BufObject, items))) -#define VEC_I16_BUF(v) ((VecI16BufObject *)((char *)(v).items - offsetof(VecI16BufObject, items))) -#define VEC_U8_BUF(v) ((VecU8BufObject *)((char *)(v).items - offsetof(VecU8BufObject, items))) -#define VEC_FLOAT_BUF(v) ((VecFloatBufObject *)((char *)(v).items - offsetof(VecFloatBufObject, items))) -#define VEC_BOOL_BUF(v) ((VecBoolBufObject *)((char *)(v).items - offsetof(VecBoolBufObject, items))) -#define VEC_T_BUF(v) ((VecTBufObject *)((char *)(v).items - offsetof(VecTBufObject, items))) -#define VEC_NESTED_BUF(v) ((VecNestedBufObject *)((char *)(v).items - offsetof(VecNestedBufObject, items))) +#define VEC_I64_BUF_FROM_ITEMS(items_) \ + ((VecI64BufObject *)((char *)(items_) - offsetof(VecI64BufObject, items))) +#define VEC_I32_BUF_FROM_ITEMS(items_) \ + ((VecI32BufObject *)((char *)(items_) - offsetof(VecI32BufObject, items))) +#define VEC_I16_BUF_FROM_ITEMS(items_) \ + ((VecI16BufObject *)((char *)(items_) - offsetof(VecI16BufObject, items))) +#define VEC_U8_BUF_FROM_ITEMS(items_) \ + ((VecU8BufObject *)((char *)(items_) - offsetof(VecU8BufObject, items))) +#define VEC_FLOAT_BUF_FROM_ITEMS(items_) \ + ((VecFloatBufObject *)((char *)(items_) - offsetof(VecFloatBufObject, items))) +#define VEC_BOOL_BUF_FROM_ITEMS(items_) \ + ((VecBoolBufObject *)((char *)(items_) - offsetof(VecBoolBufObject, items))) +#define VEC_T_BUF_FROM_ITEMS(items_) \ + ((VecTBufObject *)((char *)(items_) - offsetof(VecTBufObject, items))) +#define VEC_NESTED_BUF_FROM_ITEMS(items_) \ + ((VecNestedBufObject *)((char *)(items_) - offsetof(VecNestedBufObject, items))) + +#define VEC_I64_BUF(v) VEC_I64_BUF_FROM_ITEMS((v).items) +#define VEC_I32_BUF(v) VEC_I32_BUF_FROM_ITEMS((v).items) +#define VEC_I16_BUF(v) VEC_I16_BUF_FROM_ITEMS((v).items) +#define VEC_U8_BUF(v) VEC_U8_BUF_FROM_ITEMS((v).items) +#define VEC_FLOAT_BUF(v) VEC_FLOAT_BUF_FROM_ITEMS((v).items) +#define VEC_BOOL_BUF(v) VEC_BOOL_BUF_FROM_ITEMS((v).items) +#define VEC_T_BUF(v) VEC_T_BUF_FROM_ITEMS((v).items) +#define VEC_NESTED_BUF(v) VEC_NESTED_BUF_FROM_ITEMS((v).items) // Type-specific incref/decref. Safe when items may be NULL. #define VEC_I64_INCREF(v) do { if ((v).items) Py_INCREF(VEC_I64_BUF(v)); } while (0) @@ -460,7 +477,7 @@ typedef struct tuple_T2VvVi { VecNested f0; VecNestedBufItem f1; } tuple_T2VvVi; -static tuple_T2VvVi tuple_undefined_T2VvVi = { { -1, NULL } , { -1, NULL } }; +static tuple_T2VvVi tuple_undefined_T2VvVi = { { -1, NULL } , { -1, 0 } }; #endif typedef tuple_T2VvVi VecNestedPopResult; @@ -785,6 +802,51 @@ VecNested VecNested_ExtendVec(VecNested dst, VecNested src); VecNested VecNested_Remove(VecNested vec, VecNestedBufItem x); VecNestedPopResult VecNested_Pop(VecNested v, Py_ssize_t index); +static inline PyObject *VecNested_ItemBuf(VecNestedBufObject *parent, VecNestedBufItem item) { + if (item.items == 0) + return NULL; + if (parent->depth > 1) + return (PyObject *)VEC_NESTED_BUF_FROM_ITEMS(item.items); + if (parent->item_type == VEC_ITEM_TYPE_I64) + return (PyObject *)VEC_I64_BUF_FROM_ITEMS(item.items); + if (parent->item_type == VEC_ITEM_TYPE_U8) + return (PyObject *)VEC_U8_BUF_FROM_ITEMS(item.items); + if (parent->item_type == VEC_ITEM_TYPE_FLOAT) + return (PyObject *)VEC_FLOAT_BUF_FROM_ITEMS(item.items); + if (parent->item_type == VEC_ITEM_TYPE_I32) + return (PyObject *)VEC_I32_BUF_FROM_ITEMS(item.items); + if (parent->item_type == VEC_ITEM_TYPE_I16) + return (PyObject *)VEC_I16_BUF_FROM_ITEMS(item.items); + if (parent->item_type == VEC_ITEM_TYPE_BOOL) + return (PyObject *)VEC_BOOL_BUF_FROM_ITEMS(item.items); + return (PyObject *)VEC_T_BUF_FROM_ITEMS(item.items); +} + +static inline void VecNested_ItemXINCREF(VecNestedBufObject *parent, VecNestedBufItem item) { + Py_XINCREF(VecNested_ItemBuf(parent, item)); +} + +static inline void VecNested_ItemXDECREF(VecNestedBufObject *parent, VecNestedBufItem item) { + Py_XDECREF(VecNested_ItemBuf(parent, item)); +} + +static inline void VecNested_ItemCLEAR(VecNestedBufObject *parent, VecNestedBufItem *item) { + PyObject *buf = VecNested_ItemBuf(parent, *item); + item->items = 0; + Py_XDECREF(buf); +} + +static inline int VecNested_ItemVISIT( + VecNestedBufObject *parent, VecNestedBufItem item, visitproc visit, void *arg) { + PyObject *buf = VecNested_ItemBuf(parent, item); + if (buf) { + int ret = visit(buf, arg); + if (ret) + return ret; + } + return 0; +} + // Return 0 on success, -1 on error. Store unboxed item in *unboxed if successful. // Return a *borrowed* reference. static inline int VecNested_UnboxItem(VecNested v, PyObject *item, VecNestedBufItem *unboxed) { @@ -796,38 +858,38 @@ static inline int VecNested_UnboxItem(VecNested v, PyObject *item, VecNestedBufI VecTObject *o = (VecTObject *)item; if (VEC_T_BUF(o->vec)->item_type == v_buf->item_type) { unboxed->len = o->vec.len; - unboxed->buf = (PyObject *)VEC_T_BUF(o->vec); + unboxed->items = (CPyPtr)o->vec.items; return 0; } } else if (item->ob_type == &VecI64Type && v_buf->item_type == VEC_ITEM_TYPE_I64) { VecI64Object *o = (VecI64Object *)item; unboxed->len = o->vec.len; - unboxed->buf = o->vec.items ? (PyObject *)VEC_I64_BUF(o->vec) : NULL; + unboxed->items = (CPyPtr)o->vec.items; return 0; } else if (item->ob_type == &VecU8Type && v_buf->item_type == VEC_ITEM_TYPE_U8) { VecU8Object *o = (VecU8Object *)item; unboxed->len = o->vec.len; - unboxed->buf = o->vec.items ? (PyObject *)VEC_U8_BUF(o->vec) : NULL; + unboxed->items = (CPyPtr)o->vec.items; return 0; } else if (item->ob_type == &VecFloatType && v_buf->item_type == VEC_ITEM_TYPE_FLOAT) { VecFloatObject *o = (VecFloatObject *)item; unboxed->len = o->vec.len; - unboxed->buf = o->vec.items ? (PyObject *)VEC_FLOAT_BUF(o->vec) : NULL; + unboxed->items = (CPyPtr)o->vec.items; return 0; } else if (item->ob_type == &VecI32Type && v_buf->item_type == VEC_ITEM_TYPE_I32) { VecI32Object *o = (VecI32Object *)item; unboxed->len = o->vec.len; - unboxed->buf = o->vec.items ? (PyObject *)VEC_I32_BUF(o->vec) : NULL; + unboxed->items = (CPyPtr)o->vec.items; return 0; } else if (item->ob_type == &VecI16Type && v_buf->item_type == VEC_ITEM_TYPE_I16) { VecI16Object *o = (VecI16Object *)item; unboxed->len = o->vec.len; - unboxed->buf = o->vec.items ? (PyObject *)VEC_I16_BUF(o->vec) : NULL; + unboxed->items = (CPyPtr)o->vec.items; return 0; } else if (item->ob_type == &VecBoolType && v_buf->item_type == VEC_ITEM_TYPE_BOOL) { VecBoolObject *o = (VecBoolObject *)item; unboxed->len = o->vec.len; - unboxed->buf = o->vec.items ? (PyObject *)VEC_BOOL_BUF(o->vec) : NULL; + unboxed->items = (CPyPtr)o->vec.items; return 0; } } else if (item->ob_type == &VecNestedType) { @@ -836,7 +898,7 @@ static inline int VecNested_UnboxItem(VecNested v, PyObject *item, VecNestedBufI if (o_buf->depth == v_buf->depth - 1 && o_buf->item_type == v_buf->item_type) { unboxed->len = o->vec.len; - unboxed->buf = (PyObject *)o_buf; + unboxed->items = (CPyPtr)o->vec.items; return 0; } } @@ -848,44 +910,44 @@ static inline int VecNested_UnboxItem(VecNested v, PyObject *item, VecNestedBufI static inline PyObject *VecNested_BoxItem(VecNested v, VecNestedBufItem item) { if (item.len < 0) Py_RETURN_NONE; - Py_XINCREF(item.buf); VecNestedBufObject *v_buf = VEC_NESTED_BUF(v); + VecNested_ItemXINCREF(v_buf, item); if (v_buf->depth > 1) { // Item is a nested vec VecNested iv = { .len = item.len, - .items = item.buf ? ((VecNestedBufObject *)item.buf)->items : NULL }; + .items = (VecNestedBufItem *)item.items }; return VecNested_Box(iv); } else { // Item is a non-nested vec size_t item_type = v_buf->item_type; if (item_type == VEC_ITEM_TYPE_I64) { VecI64 iv = { .len = item.len, - .items = item.buf ? ((VecI64BufObject *)item.buf)->items : NULL }; + .items = (int64_t *)item.items }; return VecI64_Box(iv); } else if (item_type == VEC_ITEM_TYPE_U8) { VecU8 iv = { .len = item.len, - .items = item.buf ? ((VecU8BufObject *)item.buf)->items : NULL }; + .items = (uint8_t *)item.items }; return VecU8_Box(iv); } else if (item_type == VEC_ITEM_TYPE_FLOAT) { VecFloat iv = { .len = item.len, - .items = item.buf ? ((VecFloatBufObject *)item.buf)->items : NULL }; + .items = (double *)item.items }; return VecFloat_Box(iv); } else if (item_type == VEC_ITEM_TYPE_I32) { VecI32 iv = { .len = item.len, - .items = item.buf ? ((VecI32BufObject *)item.buf)->items : NULL }; + .items = (int32_t *)item.items }; return VecI32_Box(iv); } else if (item_type == VEC_ITEM_TYPE_I16) { VecI16 iv = { .len = item.len, - .items = item.buf ? ((VecI16BufObject *)item.buf)->items : NULL }; + .items = (int16_t *)item.items }; return VecI16_Box(iv); } else if (item_type == VEC_ITEM_TYPE_BOOL) { VecBool iv = { .len = item.len, - .items = item.buf ? ((VecBoolBufObject *)item.buf)->items : NULL }; + .items = (char *)item.items }; return VecBool_Box(iv); } else { // Generic vec[t] VecT iv = { .len = item.len, - .items = item.buf ? ((VecTBufObject *)item.buf)->items : NULL }; + .items = (PyObject **)item.items }; return VecT_Box(iv, item_type); } } diff --git a/mypyc/lib-rt/vecs/vec_nested.c b/mypyc/lib-rt/vecs/vec_nested.c index 72976777e820..93f8eddc4d2c 100644 --- a/mypyc/lib-rt/vecs/vec_nested.c +++ b/mypyc/lib-rt/vecs/vec_nested.c @@ -71,7 +71,7 @@ VecNested VecNested_Unbox(PyObject *obj, size_t item_type, size_t depth) { } VecNested VecNested_ConvertFromNested(VecNestedBufItem item) { - return (VecNested) { item.len, item.buf ? ((VecNestedBufObject *)item.buf)->items : NULL }; + return (VecNested) { item.len, (VecNestedBufItem *)item.items }; } VecNested VecNested_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type, size_t depth) { @@ -86,7 +86,7 @@ VecNested VecNested_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type, size_ return vec; for (Py_ssize_t i = 0; i < cap; i++) { vec.items[i].len = -1; - vec.items[i].buf = NULL; + vec.items[i].items = 0; } vec.len = size; vec_track_buffer(&vec); @@ -132,7 +132,7 @@ VecNested VecNested_Slice(VecNested vec, int64_t start, int64_t end) { res.len = slicelength; for (Py_ssize_t i = 0; i < slicelength; i++) { VecNestedBufItem item = vec.items[start + i]; - Py_XINCREF(item.buf); + VecNested_ItemXINCREF(vec_buf, item); res.items[i] = item; } vec_track_buffer(&res); @@ -166,7 +166,7 @@ static PyObject *vec_subscript(PyObject *self, PyObject *item) { Py_ssize_t j = start; for (Py_ssize_t i = 0; i < slicelength; i++) { VecNestedBufItem item = vec.items[j]; - Py_INCREF(item.buf); + VecNested_ItemXINCREF(vec_buf, item); res.items[i] = item; j += step; } @@ -188,8 +188,9 @@ static int vec_ass_item(PyObject *self, Py_ssize_t i, PyObject *o) { VecNestedBufItem item; if (VecNested_UnboxItem(v, o, &item) < 0) return -1; - Py_XINCREF(item.buf); - Py_XDECREF(v.items[i].buf); + VecNestedBufObject *v_buf = VEC_BUF(v); + VecNested_ItemXINCREF(v_buf, item); + VecNested_ItemXDECREF(v_buf, v.items[i]); v.items[i] = item; return 0; } else { @@ -265,20 +266,20 @@ PyObject *vec_richcompare(PyObject *self, PyObject *other, int op) { // Append item to 'vec', stealing 'vec'. Return 'vec' with item appended. VecNested VecNested_Append(VecNested vec, VecNestedBufItem x) { Py_ssize_t cap = VEC_CAP(vec); - Py_XINCREF(x.buf); + VecNestedBufObject *vec_buf = VEC_BUF(vec); + VecNested_ItemXINCREF(vec_buf, x); if (vec.len < cap) { // Slot may have duplicate ref from prior remove/pop - Py_XDECREF(vec.items[vec.len].buf); + VecNested_ItemXDECREF(vec_buf, vec.items[vec.len]); vec.items[vec.len] = x; vec.len++; return vec; } else { Py_ssize_t new_size = Vec_GrowCapacity(cap); // TODO: Avoid initializing to zero here - VecNestedBufObject *vec_buf = VEC_BUF(vec); VecNested new = vec_alloc(new_size, vec_buf->item_type, vec_buf->depth); if (VEC_IS_ERROR(new)) { - Py_XDECREF(x.buf); + VecNested_ItemXDECREF(vec_buf, x); // The input vec is being consumed/stolen by this function, so on error // we must decref it to avoid leaking the buffer. VEC_DECREF(vec); @@ -292,7 +293,7 @@ VecNested VecNested_Append(VecNested vec, VecNestedBufItem x) { // Other references to old buffer exist; INCREF items in new buffer // so old buffer keeps valid references for aliases. for (Py_ssize_t i = 0; i < vec.len; i++) - Py_XINCREF(new.items[i].buf); + VecNested_ItemXINCREF(vec_buf, new.items[i]); } else { // No aliases; transfer ownership by clearing old buffer items. memset(vec.items, 0, sizeof(VecNestedBufItem) * vec.len); @@ -359,13 +360,15 @@ VecNested VecNested_ExtendVec(VecNested dst, VecNested src) { Py_ssize_t new_len = dst.len + src.len; // VecNested buf is never NULL (even for empty vecs), so no NULL guard needed Py_ssize_t cap = VEC_CAP(dst); + VecNestedBufObject *dst_buf = VEC_BUF(dst); + VecNestedBufObject *src_buf = VEC_BUF(src); if (new_len <= cap && dst.items != src.items) { // Fast path: enough capacity and no aliasing for (Py_ssize_t i = 0; i < src.len; i++) { VecNestedBufItem item = src.items[i]; - Py_XINCREF(item.buf); + VecNested_ItemXINCREF(src_buf, item); // Slot may have duplicate ref from prior remove/pop - Py_XDECREF(dst.items[dst.len + i].buf); + VecNested_ItemXDECREF(dst_buf, dst.items[dst.len + i]); dst.items[dst.len + i] = item; } dst.len = new_len; @@ -374,7 +377,6 @@ VecNested VecNested_ExtendVec(VecNested dst, VecNested src) { // Need to reallocate (or dst and src share a buffer) Py_ssize_t new_cap = Vec_GrowCapacityTo(cap, new_len); int aliased = dst.items == src.items; - VecNestedBufObject *dst_buf = VEC_BUF(dst); VecNested new = vec_alloc(new_cap, dst_buf->item_type, dst_buf->depth); if (VEC_IS_ERROR(new)) { VEC_DECREF(dst); @@ -384,14 +386,14 @@ VecNested VecNested_ExtendVec(VecNested dst, VecNested src) { // dst and src share a buffer -- incref all items instead of // moving refs, to avoid mutating the shared buffer for (Py_ssize_t i = 0; i < dst.len; i++) { - Py_XINCREF(dst.items[i].buf); + VecNested_ItemXINCREF(dst_buf, dst.items[i]); new.items[i] = dst.items[i]; } } else { memcpy(new.items, dst.items, sizeof(VecNestedBufItem) * dst.len); if (Py_REFCNT(dst_buf) > 1) { for (Py_ssize_t i = 0; i < dst.len; i++) - Py_XINCREF(new.items[i].buf); + VecNested_ItemXINCREF(dst_buf, new.items[i]); } else { memset(dst.items, 0, sizeof(VecNestedBufItem) * dst.len); } @@ -399,7 +401,7 @@ VecNested VecNested_ExtendVec(VecNested dst, VecNested src) { // Copy src items (incref each buf) for (Py_ssize_t i = 0; i < src.len; i++) { VecNestedBufItem item = src.items[i]; - Py_XINCREF(item.buf); + VecNested_ItemXINCREF(src_buf, item); new.items[dst.len + i] = item; } memset(new.items + new_len, 0, sizeof(VecNestedBufItem) * (new_cap - new_len)); @@ -412,6 +414,7 @@ VecNested VecNested_ExtendVec(VecNested dst, VecNested src) { // Remove item from 'vec', stealing 'vec'. Return 'vec' with item removed. VecNested VecNested_Remove(VecNested self, VecNestedBufItem arg) { VecNestedBufItem *items = self.items; + VecNestedBufObject *self_buf = VEC_BUF(self); PyObject *boxed_arg = VecNested_BoxItem(self, arg); if (boxed_arg == NULL) { @@ -423,7 +426,7 @@ VecNested VecNested_Remove(VecNested self, VecNestedBufItem arg) { for (Py_ssize_t i = 0; i < self.len; i++) { int match = 0; - if (items[i].len == arg.len && items[i].buf == arg.buf) + if (items[i].len == arg.len && items[i].items == arg.items) match = 1; else { PyObject *item = box_vec_item_by_index(self, i); @@ -447,11 +450,11 @@ VecNested VecNested_Remove(VecNested self, VecNestedBufItem arg) { } if (match) { if (i < self.len - 1) { - Py_CLEAR(items[i].buf); + VecNested_ItemCLEAR(self_buf, &items[i]); for (; i < self.len - 1; i++) { items[i] = items[i + 1]; } - Py_XINCREF(items[self.len - 1].buf); + VecNested_ItemXINCREF(self_buf, items[self.len - 1]); } self.len--; Py_DECREF(boxed_arg); @@ -481,16 +484,17 @@ VecNestedPopResult VecNested_Pop(VecNested v, Py_ssize_t index) { VEC_DECREF(v); result.f0 = vec_error(); result.f1.len = 0; - result.f1.buf = NULL; + result.f1.items = 0; return result; } VecNestedBufItem *items = v.items; + VecNestedBufObject *v_buf = VEC_BUF(v); result.f1 = items[index]; for (Py_ssize_t i = index; i < v.len - 1; i++) items[i] = items[i + 1]; if (v.len > 0) - Py_XINCREF(items[v.len - 1].buf); + VecNested_ItemXINCREF(v_buf, items[v.len - 1]); v.len--; // Return the stolen reference without INCREF result.f0 = v; @@ -534,7 +538,9 @@ VecNestedBuf_traverse(VecNestedBufObject *self, visitproc visit, void *arg) if (!Vec_IsMagicItemType(self->item_type)) Py_VISIT(VEC_BUF_ITEM_TYPE(self)); for (Py_ssize_t i = 0; i < VEC_BUF_SIZE(self); i++) { - Py_VISIT(self->items[i].buf); + int ret = VecNested_ItemVISIT(self, self->items[i], visit, arg); + if (ret) + return ret; } return 0; } @@ -542,13 +548,13 @@ VecNestedBuf_traverse(VecNestedBufObject *self, visitproc visit, void *arg) static inline int VecNestedBuf_clear(VecNestedBufObject *self) { + for (Py_ssize_t i = 0; i < VEC_BUF_SIZE(self); i++) { + VecNested_ItemCLEAR(self, &self->items[i]); + } if (self->item_type && !Vec_IsMagicItemType(self->item_type)) { Py_DECREF(VEC_BUF_ITEM_TYPE(self)); self->item_type = 0; } - for (Py_ssize_t i = 0; i < VEC_BUF_SIZE(self); i++) { - Py_CLEAR(self->items[i].buf); - } return 0; } @@ -711,7 +717,7 @@ PyObject *VecNested_FromIterable(size_t item_type, size_t depth, PyObject *itera if (cap > 0) { for (int64_t i = 0; i < cap; i++) { v.items[i].len = -1; - v.items[i].buf = NULL; + v.items[i].items = 0; } } v.len = 0; diff --git a/mypyc/lib-rt/vecs/vec_t.c b/mypyc/lib-rt/vecs/vec_t.c index 3284a073c591..996b48a94078 100644 --- a/mypyc/lib-rt/vecs/vec_t.c +++ b/mypyc/lib-rt/vecs/vec_t.c @@ -90,7 +90,7 @@ VecT VecT_Unbox(PyObject *obj, size_t item_type) { } VecT VecT_ConvertFromNested(VecNestedBufItem item) { - return (VecT) { item.len, item.buf ? ((VecTBufObject *)item.buf)->items : NULL }; + return (VecT) { item.len, (PyObject **)item.items }; } VecT VecT_New(Py_ssize_t size, Py_ssize_t cap, size_t item_type) { diff --git a/mypyc/lib-rt/vecs/vec_template.c b/mypyc/lib-rt/vecs/vec_template.c index 58f1c8975dd6..5c7982420d32 100644 --- a/mypyc/lib-rt/vecs/vec_template.c +++ b/mypyc/lib-rt/vecs/vec_template.c @@ -88,7 +88,7 @@ VEC FUNC(Unbox)(PyObject *obj) { } VEC FUNC(ConvertFromNested)(VecNestedBufItem item) { - return (VEC) { item.len, item.buf ? ((BUF_OBJECT *)item.buf)->items : NULL }; + return (VEC) { item.len, (ITEM_C_TYPE *)item.items }; } VEC FUNC(New)(Py_ssize_t size, Py_ssize_t cap) { diff --git a/mypyc/test-data/irbuild-vec-misc.test b/mypyc/test-data/irbuild-vec-misc.test index b3e1ffdbea61..95d854780db9 100644 --- a/mypyc/test-data/irbuild-vec-misc.test +++ b/mypyc/test-data/irbuild-vec-misc.test @@ -273,8 +273,7 @@ def get_item_nested(v, i): r6 :: ptr r7 :: i64 r8 :: ptr - r9 :: VecNestedBufItem{len:native_int, buf:object_nrc} - r10 :: vec[i32] + r9 :: vec[i32] L0: r0 = v.len r1 = i < r0 :: unsigned @@ -295,11 +294,9 @@ L5: r6 = v.items r7 = r5 * 16 r8 = r6 + r7 - r9 = load_mem r8 :: VecNestedBufItem* - r10 = VecI32Api.convert_from_nested(r9) - inc_ref r10 + r9 = load_mem r8 :: vec[i32]* keep_alive v - return r10 + return r9 [case testVecMiscNestedPop_64bit] from librt.vecs import vec, pop @@ -313,31 +310,36 @@ def get_item_nested(v: vec[vec[i32]], i: i64) -> vec[i32]: def get_item_nested(v, i): v :: vec[vec[i32]] i :: i64 - r0 :: tuple[vec[vec[i32]], VecNestedBufItem{len:native_int, buf:object_nrc}] + r0 :: tuple[vec[vec[i32]], VecNestedBufItem{len:native_int, items:ptr}] r1, r2 :: vec[vec[i32]] - r3, r4 :: VecNestedBufItem{len:native_int, buf:object_nrc} - r5 :: vec[i32] - r6 :: tuple[vec[vec[i32]], vec[i32]] - r7 :: vec[vec[i32]] - r8 :: vec[i32] - r9 :: vec[vec[i32]] - r10, x :: vec[i32] + r3, r4 :: VecNestedBufItem{len:native_int, items:ptr} + r5 :: native_int + r6 :: ptr + r7, r8 :: vec[i32] + r9 :: tuple[vec[vec[i32]], vec[i32]] + r10 :: vec[vec[i32]] + r11 :: vec[i32] + r12 :: vec[vec[i32]] + r13, x :: vec[i32] L0: r0 = VecNestedApi.pop(v, i) r1 = borrow r0[0] r2 = unborrow r1 r3 = borrow r0[1] r4 = unborrow r3 - r5 = VecI32Api.convert_from_nested(r4) - r6 = (r2, r5) + r5 = r4.len + r6 = r4.items + r7 = set_element undef vec[i32], len, r5 + r8 = set_element r7, items, r6 + r9 = (r2, r8) keep_alive steal r0 - r7 = borrow r6[0] - r8 = borrow r6[1] - keep_alive steal r6 - r9 = unborrow r7 - v = r9 - r10 = unborrow r8 - x = r10 + r10 = borrow r9[0] + r11 = borrow r9[1] + keep_alive steal r9 + r12 = unborrow r10 + v = r12 + r13 = unborrow r11 + x = r13 return x [case testVecU8ToBytes_experimental] diff --git a/mypyc/test-data/irbuild-vec-nested.test b/mypyc/test-data/irbuild-vec-nested.test index 1fe55295291f..1fe42a880d5b 100644 --- a/mypyc/test-data/irbuild-vec-nested.test +++ b/mypyc/test-data/irbuild-vec-nested.test @@ -105,29 +105,16 @@ def f(v, vv): vv :: vec[str] r0 :: native_int r1 :: ptr - r2 :: bit - r3 :: object_nrc - r4, r5 :: ptr - r6, r7 :: VecNestedBufItem{len:native_int, buf:object_nrc} - r8 :: vec[vec[str]] + r2, r3 :: VecNestedBufItem{len:native_int, items:ptr} + r4 :: vec[vec[str]] L0: r0 = vv.len r1 = vv.items - r2 = r1 == 0 - if r2 goto L1 else goto L2 :: bool -L1: - r3 = 0 - goto L3 -L2: - r4 = get_element_ptr 0 items :: VecTBufObject - r5 = r1 - r4 - r3 = r5 -L3: - r6 = set_element undef VecNestedBufItem, len, r0 - r7 = set_element r6, buf, r3 - r8 = VecNestedApi.append(v, r7) + r2 = set_element undef VecNestedBufItem, len, r0 + r3 = set_element r2, items, r1 + r4 = VecNestedApi.append(v, r3) keep_alive vv - return r8 + return r4 [case testVecNestedVecI64Append] from librt.vecs import vec, append @@ -141,29 +128,16 @@ def f(v, vv): vv :: vec[i64] r0 :: native_int r1 :: ptr - r2 :: bit - r3 :: object_nrc - r4, r5 :: ptr - r6, r7 :: VecNestedBufItem{len:native_int, buf:object_nrc} - r8 :: vec[vec[i64]] + r2, r3 :: VecNestedBufItem{len:native_int, items:ptr} + r4 :: vec[vec[i64]] L0: r0 = vv.len r1 = vv.items - r2 = r1 == 0 - if r2 goto L1 else goto L2 :: bool -L1: - r3 = 0 - goto L3 -L2: - r4 = get_element_ptr 0 items :: VecI64BufObject - r5 = r1 - r4 - r3 = r5 -L3: - r6 = set_element undef VecNestedBufItem, len, r0 - r7 = set_element r6, buf, r3 - r8 = VecNestedApi.append(v, r7) + r2 = set_element undef VecNestedBufItem, len, r0 + r3 = set_element r2, items, r1 + r4 = VecNestedApi.append(v, r3) keep_alive vv - return r8 + return r4 [case testVecNestedExtend] from librt.vecs import vec, extend @@ -235,8 +209,7 @@ def f(v, n): r6 :: ptr r7 :: i64 r8 :: ptr - r9 :: VecNestedBufItem{len:native_int, buf:object_nrc} - r10 :: vec[str] + r9 :: vec[str] L0: r0 = v.len r1 = n < r0 :: unsigned @@ -257,11 +230,9 @@ L5: r6 = v.items r7 = r5 * 16 r8 = r6 + r7 - r9 = load_mem r8 :: VecNestedBufItem* - r10 = VecTApi.convert_from_nested(r9) - inc_ref r10 + r9 = load_mem r8 :: vec[str]* keep_alive v - return r10 + return r9 [case testVecNestedI64GetItem_64bit] from librt.vecs import vec @@ -282,8 +253,7 @@ def f(v, n): r6 :: ptr r7 :: i64 r8 :: ptr - r9 :: VecNestedBufItem{len:native_int, buf:object_nrc} - r10 :: vec[i64] + r9 :: vec[i64] L0: r0 = v.len r1 = n < r0 :: unsigned @@ -304,11 +274,9 @@ L5: r6 = v.items r7 = r5 * 16 r8 = r6 + r7 - r9 = load_mem r8 :: VecNestedBufItem* - r10 = VecI64Api.convert_from_nested(r9) - inc_ref r10 + r9 = load_mem r8 :: vec[i64]* keep_alive v - return r10 + return r9 [case testVecNestedI64GetItemWithBorrow_64bit] from librt.vecs import vec @@ -329,18 +297,17 @@ def f(v, n): r6 :: ptr r7 :: i64 r8 :: ptr - r9 :: VecNestedBufItem{len:native_int, buf:object_nrc} - r10 :: vec[i64] - r11 :: native_int - r12 :: bit - r13 :: i64 - r14 :: bit - r15 :: bool - r16 :: i64 - r17 :: ptr - r18 :: i64 - r19 :: ptr - r20 :: i64 + r9 :: vec[i64] + r10 :: native_int + r11 :: bit + r12 :: i64 + r13 :: bit + r14 :: bool + r15 :: i64 + r16 :: ptr + r17 :: i64 + r18 :: ptr + r19 :: i64 L0: r0 = v.len r1 = n < r0 :: unsigned @@ -361,30 +328,29 @@ L5: r6 = v.items r7 = r5 * 16 r8 = r6 + r7 - r9 = load_mem r8 :: VecNestedBufItem* - r10 = VecI64Api.convert_from_nested(r9) - r11 = r10.len - r12 = n < r11 :: unsigned - if r12 goto L9 else goto L6 :: bool + r9 = borrow load_mem r8 :: vec[i64]* + r10 = r9.len + r11 = n < r10 :: unsigned + if r11 goto L9 else goto L6 :: bool L6: - r13 = n + r11 - r14 = r13 < r11 :: unsigned - if r14 goto L8 else goto L7 :: bool + r12 = n + r10 + r13 = r12 < r10 :: unsigned + if r13 goto L8 else goto L7 :: bool L7: - r15 = raise IndexError + r14 = raise IndexError unreachable L8: - r16 = r13 + r15 = r12 goto L10 L9: - r16 = n + r15 = n L10: - r17 = r10.items - r18 = r16 * 8 - r19 = r17 + r18 - r20 = load_mem r19 :: i64* - keep_alive v, r10 - return r20 + r16 = r9.items + r17 = r15 * 8 + r18 = r16 + r17 + r19 = load_mem r18 :: i64* + keep_alive v, r9 + return r19 [case testVecDoublyNestedGetItem_64bit] from librt.vecs import vec @@ -405,8 +371,7 @@ def f(v, n): r6 :: ptr r7 :: i64 r8 :: ptr - r9 :: VecNestedBufItem{len:native_int, buf:object_nrc} - r10 :: vec[vec[str]] + r9 :: vec[vec[str]] L0: r0 = v.len r1 = n < r0 :: unsigned @@ -427,11 +392,9 @@ L5: r6 = v.items r7 = r5 * 16 r8 = r6 + r7 - r9 = load_mem r8 :: VecNestedBufItem* - r10 = VecNestedApi.convert_from_nested(r9) - inc_ref r10 + r9 = load_mem r8 :: vec[vec[str]]* keep_alive v - return r10 + return r9 [case testVecNestedCreateWithCap_64bit] from librt.vecs import vec diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index 472bd14ed116..2f03a159dfac 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -1744,29 +1744,16 @@ def f(vv, v): v :: vec[str] r0 :: native_int r1 :: ptr - r2 :: bit - r3 :: object_nrc - r4, r5 :: ptr - r6, r7 :: VecNestedBufItem{len:native_int, buf:object_nrc} - r8 :: vec[vec[str]] + r2, r3 :: VecNestedBufItem{len:native_int, items:ptr} + r4 :: vec[vec[str]] L0: r0 = v.len r1 = v.items - r2 = r1 == 0 - if r2 goto L1 else goto L2 :: bool -L1: - r3 = 0 - goto L3 -L2: - r4 = get_element_ptr 0 items :: VecTBufObject - r5 = r1 - r4 - r3 = r5 -L3: - r6 = set_element undef VecNestedBufItem, len, r0 - r7 = set_element r6, buf, r3 + r2 = set_element undef VecNestedBufItem, len, r0 + r3 = set_element r2, items, r1 inc_ref vv - r8 = VecNestedApi.append(vv, r7) - return r8 + r4 = VecNestedApi.append(vv, r3) + return r4 [case testVecTGetItem_64bit] from librt.vecs import vec @@ -1833,8 +1820,7 @@ def f(v, n): r9 :: ptr r10 :: i64 r11 :: ptr - r12 :: VecNestedBufItem{len:native_int, buf:object_nrc} - r13, vv :: vec[str] + r12, vv :: vec[str] L0: r0 = load_address PyUnicode_Type r1 = r0 @@ -1858,11 +1844,9 @@ L5: r9 = r2.items r10 = r8 * 16 r11 = r9 + r10 - r12 = load_mem r11 :: VecNestedBufItem* - r13 = VecTApi.convert_from_nested(r12) - inc_ref r13 + r12 = load_mem r11 :: vec[str]* dec_ref r2 - vv = r13 + vv = r12 dec_ref vv return 1 L6: @@ -1885,22 +1869,20 @@ def f(v, n, m): r3 :: bit r4 :: bool r5 :: i64 - r6 :: object - r7 :: ptr - r8 :: i64 - r9 :: ptr - r10 :: vec[i64] - r11 :: native_int - r12 :: bit - r13 :: i64 - r14 :: bit - r15 :: bool - r16 :: i64 - r17 :: object + r6 :: ptr + r7 :: i64 + r8 :: ptr + r9 :: vec[i64] + r10 :: native_int + r11 :: bit + r12 :: i64 + r13 :: bit + r14 :: bool + r15 :: i64 + r16 :: ptr + r17 :: i64 r18 :: ptr r19 :: i64 - r20 :: ptr - r21 :: i64 L0: r0 = v.len r1 = n < r0 :: unsigned @@ -1918,33 +1900,31 @@ L3: L4: r5 = n L5: - r6 = v.buf - r7 = get_element_ptr r6 items :: VecNestedBufObject - r8 = r5 * 16 - r9 = r7 + r8 - r10 = borrow load_mem r9 :: vec[i64]* - r11 = r10.len - r12 = m < r11 :: unsigned - if r12 goto L9 else goto L6 :: bool + r6 = v.items + r7 = r5 * 16 + r8 = r6 + r7 + r9 = borrow load_mem r8 :: vec[i64]* + r10 = r9.len + r11 = m < r10 :: unsigned + if r11 goto L9 else goto L6 :: bool L6: - r13 = m + r11 - r14 = r13 < r11 :: unsigned - if r14 goto L8 else goto L7 :: bool + r12 = m + r10 + r13 = r12 < r10 :: unsigned + if r13 goto L8 else goto L7 :: bool L7: - r15 = raise IndexError + r14 = raise IndexError unreachable L8: - r16 = r13 + r15 = r12 goto L10 L9: - r16 = m + r15 = m L10: - r17 = r10.buf - r18 = get_element_ptr r17 items :: VecI64BufObject - r19 = r16 * 8 - r20 = r18 + r19 - r21 = load_mem r20 :: i64* - return r21 + r16 = r9.items + r17 = r15 * 8 + r18 = r16 + r17 + r19 = load_mem r18 :: i64* + return r19 [case testVecPop] from librt.vecs import vec, pop, append @@ -1986,15 +1966,17 @@ def f(v: vec[vec[str]]) -> vec[str]: [out] def f(v): v :: vec[vec[str]] - r0 :: tuple[vec[vec[str]], VecNestedBufItem{len:native_int, buf:object_nrc}] + r0 :: tuple[vec[vec[str]], VecNestedBufItem{len:native_int, items:ptr}] r1, r2 :: vec[vec[str]] - r3, r4 :: VecNestedBufItem{len:native_int, buf:object_nrc} - r5 :: vec[str] - r6 :: tuple[vec[vec[str]], vec[str]] - r7 :: vec[vec[str]] - r8 :: vec[str] - r9, vv :: vec[vec[str]] - r10, x :: vec[str] + r3, r4 :: VecNestedBufItem{len:native_int, items:ptr} + r5 :: native_int + r6 :: ptr + r7, r8 :: vec[str] + r9 :: tuple[vec[vec[str]], vec[str]] + r10 :: vec[vec[str]] + r11 :: vec[str] + r12, vv :: vec[vec[str]] + r13, x :: vec[str] L0: inc_ref v r0 = VecNestedApi.pop(v, -1) @@ -2002,15 +1984,18 @@ L0: r2 = unborrow r1 r3 = borrow r0[1] r4 = unborrow r3 - r5 = VecTApi.convert_from_nested(r4) - r6 = (r2, r5) - r7 = borrow r6[0] - r8 = borrow r6[1] - r9 = unborrow r7 - vv = r9 + r5 = r4.len + r6 = r4.items + r7 = set_element undef vec[str], len, r5 + r8 = set_element r7, items, r6 + r9 = (r2, r8) + r10 = borrow r9[0] + r11 = borrow r9[1] + r12 = unborrow r10 + vv = r12 dec_ref vv - r10 = unborrow r8 - x = r10 + r13 = unborrow r11 + x = r13 return x [case testPropertySetterCallWithRefcountedObject] From 13d65fc49d3cacf949eaf7a337199989e1261b82 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 6 May 2026 16:01:00 +0100 Subject: [PATCH 9/9] Update 32-bit test --- mypyc/test-data/irbuild-vec-i64.test | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/mypyc/test-data/irbuild-vec-i64.test b/mypyc/test-data/irbuild-vec-i64.test index 1ca33f1f78b3..aeab3ed9f2a8 100644 --- a/mypyc/test-data/irbuild-vec-i64.test +++ b/mypyc/test-data/irbuild-vec-i64.test @@ -113,11 +113,10 @@ def f(v, i): r6 :: i64 r7, r8 :: bit r9 :: native_int - r10 :: object - r11 :: ptr - r12 :: native_int - r13 :: ptr - r14 :: i64 + r10 :: ptr + r11 :: native_int + r12 :: ptr + r13 :: i64 L0: r0 = v.len r1 = extend signed r0: native_int to i64 @@ -148,13 +147,12 @@ L8: CPyInt32_Overflow() unreachable L9: - r10 = v.buf - r11 = get_element_ptr r10 items :: VecI64BufObject - r12 = r9 * 8 - r13 = r11 + r12 - r14 = load_mem r13 :: i64* + r10 = v.items + r11 = r9 * 8 + r12 = r10 + r11 + r13 = load_mem r12 :: i64* keep_alive v - return r14 + return r13 [case testVecI64Append] from librt.vecs import vec, append