/** * This file is included by `_simd.dispatch.c.src`. Its contents are affected by the simd configuration, and * therefore must be built multiple times. Making it a standalone `.c` file with `NPY_VISIBILITY_HIDDEN` * symbols would require judicious use of `NPY_CPU_DISPATCH_DECLARE` and `NPY_CPU_DISPATCH_CURFX`, which was * deemed too harmful to readability. */ /************************************ ** Private Definitions ************************************/ static Py_ssize_t simd__vector_length(PySIMDVectorObject *self) { return simd_data_getinfo(self->dtype)->nlanes; } static PyObject * simd__vector_item(PySIMDVectorObject *self, Py_ssize_t i) { const simd_data_info *info = simd_data_getinfo(self->dtype); int nlanes = info->nlanes; if (i >= nlanes) { PyErr_SetString(PyExc_IndexError, "vector index out of range"); return NULL; } npyv_lanetype_u8 *src = self->data + i * info->lane_size; simd_data data; memcpy(&data.u64, src, info->lane_size); return simd_scalar_to_number(data, info->to_scalar); } static PySequenceMethods simd__vector_as_sequence = { .sq_length = (lenfunc) simd__vector_length, .sq_item = (ssizeargfunc) simd__vector_item }; static PyObject * simd__vector_name(PySIMDVectorObject *self, void *NPY_UNUSED(ignored)) { return PyUnicode_FromString(simd_data_getinfo(self->dtype)->pyname); } static PyGetSetDef simd__vector_getset[] = { { "__name__", (getter)simd__vector_name, NULL, NULL, NULL }, { NULL, NULL, NULL, NULL, NULL } }; static PyObject * simd__vector_repr(PySIMDVectorObject *self) { PyObject *obj = PySequence_List((PyObject*)self); if (obj != NULL) { const char *type_name = simd_data_getinfo(self->dtype)->pyname; PyObject *repr = PyUnicode_FromFormat("<%s of %R>", type_name, obj); Py_DECREF(obj); return repr; } return obj; } static PyObject * simd__vector_compare(PyObject *self, PyObject *other, int cmp_op) { PyObject *obj; if (PyTuple_Check(other)) { obj = PySequence_Tuple(self); } else if (PyList_Check(other)) { obj = PySequence_List(self); } else { obj = PySequence_Fast(self, "invalid argument, expected a vector"); } if (obj != NULL) { PyObject *rich = PyObject_RichCompare(obj, other, cmp_op); Py_DECREF(obj); return rich; } return obj; } static PyTypeObject PySIMDVectorType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = NPY_TOSTRING(NPY_CPU_DISPATCH_CURFX(VECTOR)), .tp_basicsize = sizeof(PySIMDVectorObject), .tp_repr = (reprfunc)simd__vector_repr, .tp_as_sequence = &simd__vector_as_sequence, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_richcompare = simd__vector_compare, .tp_getset = simd__vector_getset }; /************************************ ** Protected Definitions ************************************/ /* * Force inlining the following functions on CYGWIN to avoid spilling vector * registers into the stack to workaround GCC/WIN64 bug that performs * miss-align load variable of 256/512-bit vector from non-aligned * 256/512-bit stack pointer. * * check the following links for more clarification: * https://github.com/numpy/numpy/pull/18330#issuecomment-821539919 * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=49001 */ #if defined(__CYGWIN__) || (defined(__GNUC__) && defined(_WIN64)) #define CYG_FINLINE NPY_FINLINE #else #define CYG_FINLINE static #endif CYG_FINLINE PySIMDVectorObject * PySIMDVector_FromData(simd_data data, simd_data_type dtype) { const simd_data_info *info = simd_data_getinfo(dtype); assert(info->is_vector && info->nlanes > 0); PySIMDVectorObject *vec = PyObject_New(PySIMDVectorObject, &PySIMDVectorType); if (vec == NULL) { return (PySIMDVectorObject*)PyErr_NoMemory(); } vec->dtype = dtype; if (info->is_bool) { // boolean vectors are internally treated as unsigned // vectors to add compatibility among all SIMD extensions switch(dtype) { case simd_data_vb8: data.vu8 = npyv_cvt_u8_b8(data.vb8); break; case simd_data_vb16: data.vu16 = npyv_cvt_u16_b16(data.vb16); break; case simd_data_vb32: data.vu32 = npyv_cvt_u32_b32(data.vb32); break; default: data.vu64 = npyv_cvt_u64_b64(data.vb64); } } npyv_store_u8(vec->data, data.vu8); return vec; } CYG_FINLINE simd_data PySIMDVector_AsData(PySIMDVectorObject *vec, simd_data_type dtype) { const simd_data_info *info = simd_data_getinfo(dtype); assert(info->is_vector && info->nlanes > 0); simd_data data = {.u64 = 0}; if (!PyObject_IsInstance( (PyObject *)vec, (PyObject *)&PySIMDVectorType )) { PyErr_Format(PyExc_TypeError, "a vector type %s is required", info->pyname ); return data; } if (vec->dtype != dtype) { PyErr_Format(PyExc_TypeError, "a vector type %s is required, got(%s)", info->pyname, simd_data_getinfo(vec->dtype)->pyname ); return data; } data.vu8 = npyv_load_u8(vec->data); if (info->is_bool) { // boolean vectors are internally treated as unsigned // vectors to add compatibility among all SIMD extensions switch(dtype) { case simd_data_vb8: data.vb8 = npyv_cvt_b8_u8(data.vu8); break; case simd_data_vb16: data.vb16 = npyv_cvt_b16_u16(data.vu16); break; case simd_data_vb32: data.vb32 = npyv_cvt_b32_u32(data.vu32); break; default: data.vb64 = npyv_cvt_b64_u64(data.vu64); } } return data; } static int PySIMDVectorType_Init(PyObject *module) { Py_INCREF(&PySIMDVectorType); if (PyType_Ready(&PySIMDVectorType)) { return -1; } if (PyModule_AddObject( module, "vector_type",(PyObject *)&PySIMDVectorType )) { return -1; } return 0; }