From 8a0979b6dbff12033af5c95ff291a2d3464853ff Mon Sep 17 00:00:00 2001 From: SwayamInSync Date: Sat, 25 Jul 2026 14:57:03 +0530 Subject: [PATCH 1/2] added new helpers; replaced the callers --- src/csrc/dtype.c | 67 ++------------ src/csrc/scalar.c | 3 +- src/csrc/umath/binary_ops.cpp | 35 ++++---- src/csrc/umath/matmul.cpp | 2 +- src/csrc/umath/unary_ops.cpp | 29 +++---- src/csrc/utilities.c | 7 +- src/include/quad_common.h | 50 +++++++++++ src/include/utilities.h | 14 +-- tests/test_longdouble_padding.py | 145 +++++++++++++++++++++++++++++++ 9 files changed, 240 insertions(+), 112 deletions(-) create mode 100644 tests/test_longdouble_padding.py diff --git a/src/csrc/dtype.c b/src/csrc/dtype.c index 02debc9..a6d92e6 100644 --- a/src/csrc/dtype.c +++ b/src/csrc/dtype.c @@ -23,36 +23,6 @@ #include "constants.hpp" #include "utilities.h" -static inline int -quad_load(void *x, char *data_ptr, QuadBackendType backend) -{ - if (data_ptr == NULL || x == NULL) { - return -1; - } - if (backend == BACKEND_SLEEF) { - *(Sleef_quad *)x = *(Sleef_quad *)data_ptr; - } - else { - *(long double *)x = *(long double *)data_ptr; - } - return 0; -} - -static inline int -quad_store(char *data_ptr, void *x, QuadBackendType backend) -{ - if (data_ptr == NULL || x == NULL) { - return -1; - } - if (backend == BACKEND_SLEEF) { - *(Sleef_quad *)data_ptr = *(Sleef_quad *)x; - } - else { - *(long double *)data_ptr = *(long double *)x; - } - return 0; -} - QuadPrecDTypeObject * new_quaddtype_instance(QuadBackendType backend) { @@ -160,13 +130,7 @@ quadprec_setitem(QuadPrecDTypeObject *descr, PyObject *obj, char *dataptr) } } - if (quad_store(dataptr, &value->value, descr->backend) < 0) { - Py_DECREF(value); - char error_msg[100]; - snprintf(error_msg, sizeof(error_msg), "Invalid memory location %p", (void *)dataptr); - PyErr_SetString(PyExc_ValueError, error_msg); - return -1; - } + quad_value_store(dataptr, &value->value, descr->backend); Py_DECREF(value); return 0; @@ -179,13 +143,7 @@ quadprec_getitem(QuadPrecDTypeObject *descr, char *dataptr) if (!new) { return NULL; } - if (quad_load(&new->value, dataptr, descr->backend) < 0) { - Py_DECREF(new); - char error_msg[100]; - snprintf(error_msg, sizeof(error_msg), "Invalid memory location %p", (void *)dataptr); - PyErr_SetString(PyExc_ValueError, error_msg); - return NULL; - } + quad_value_load(&new->value, dataptr, descr->backend); return (PyObject *)new; } @@ -315,9 +273,10 @@ quadprec_fill(void *buffer, npy_intp length, void *arr_) long double *buf = (long double *)buffer; long double start = buf[0]; long double delta = buf[1] - start; - + char *bytes = (char *)buffer; + for (i = 2; i < length; ++i) { - buf[i] = start + i * delta; + quad_longdouble_store(bytes + i * descr->base.elsize, start + i * delta); } } @@ -362,13 +321,8 @@ quadprec_scanfunc(FILE *fp, void *dptr, char *ignore, PyArray_Descr *descr_gener if (err < 0 || *endptr != '\0') { return 0; /* Return 0 on parse error (no items read) */ } - if (descr->backend == BACKEND_SLEEF) { - *(Sleef_quad *)dptr = val.sleef_value; - } - else { - *(long double *)dptr = val.longdouble_value; - } - + quad_value_store(dptr, &val, descr->backend); + return 1; /* Return 1 on success (1 item read) */ } @@ -381,12 +335,7 @@ quadprec_fromstr(char *s, void *dptr, char **endptr, PyArray_Descr *descr_generi if (err < 0) { return -1; } - if(descr->backend == BACKEND_SLEEF) { - *(Sleef_quad *)dptr = val.sleef_value; - } - else { - *(long double *)dptr = val.longdouble_value; - } + quad_value_store(dptr, &val, descr->backend); return 0; } diff --git a/src/csrc/scalar.c b/src/csrc/scalar.c index 82a3057..4728574 100644 --- a/src/csrc/scalar.c +++ b/src/csrc/scalar.c @@ -43,8 +43,7 @@ QuadPrecision_raw_new(QuadBackendType backend) } else { // An 80-bit long double occupies 16 bytes but writes only 10 - memset(&new->value, 0, sizeof(new->value)); - new->value.longdouble_value = 0.0L; + quad_value_set_longdouble(&new->value, 0.0L); } return new; } diff --git a/src/csrc/umath/binary_ops.cpp b/src/csrc/umath/binary_ops.cpp index b92bbde..841b4ce 100644 --- a/src/csrc/umath/binary_ops.cpp +++ b/src/csrc/umath/binary_ops.cpp @@ -92,19 +92,17 @@ quad_generic_binop_strided_loop_unaligned(PyArrayMethod_Context *context, char * QuadPrecDTypeObject *descr = (QuadPrecDTypeObject *)context->descriptors[0]; QuadBackendType backend = descr->backend; - size_t elem_size = (backend == BACKEND_SLEEF) ? sizeof(Sleef_quad) : sizeof(long double); - quad_value in1, in2, out; while (N--) { - memcpy(&in1, in1_ptr, elem_size); - memcpy(&in2, in2_ptr, elem_size); + quad_value_load(&in1, in1_ptr, backend); + quad_value_load(&in2, in2_ptr, backend); if (backend == BACKEND_SLEEF) { out.sleef_value = sleef_op(&in1.sleef_value, &in2.sleef_value); } else { out.longdouble_value = longdouble_op(&in1.longdouble_value, &in2.longdouble_value); } - memcpy(out_ptr, &out, elem_size); + quad_value_store(out_ptr, &out, backend); in1_ptr += in1_stride; in2_ptr += in2_stride; @@ -134,7 +132,8 @@ quad_generic_binop_strided_loop_aligned(PyArrayMethod_Context *context, char *co *(Sleef_quad *)out_ptr = sleef_op((Sleef_quad *)in1_ptr, (Sleef_quad *)in2_ptr); } else { - *(long double *)out_ptr = longdouble_op((long double *)in1_ptr, (long double *)in2_ptr); + quad_longdouble_store(out_ptr, + longdouble_op((long double *)in1_ptr, (long double *)in2_ptr)); } in1_ptr += in1_stride; @@ -222,12 +221,10 @@ quad_generic_binop_2out_strided_loop_unaligned(PyArrayMethod_Context *context, c QuadPrecDTypeObject *descr = (QuadPrecDTypeObject *)context->descriptors[0]; QuadBackendType backend = descr->backend; - size_t elem_size = (backend == BACKEND_SLEEF) ? sizeof(Sleef_quad) : sizeof(long double); - quad_value in1, in2, out1, out2; while (N--) { - memcpy(&in1, in1_ptr, elem_size); - memcpy(&in2, in2_ptr, elem_size); + quad_value_load(&in1, in1_ptr, backend); + quad_value_load(&in2, in2_ptr, backend); if (backend == BACKEND_SLEEF) { sleef_op(&in1.sleef_value, &in2.sleef_value, &out1.sleef_value, &out2.sleef_value); } @@ -235,8 +232,8 @@ quad_generic_binop_2out_strided_loop_unaligned(PyArrayMethod_Context *context, c longdouble_op(&in1.longdouble_value, &in2.longdouble_value, &out1.longdouble_value, &out2.longdouble_value); } - memcpy(out1_ptr, &out1, elem_size); - memcpy(out2_ptr, &out2, elem_size); + quad_value_store(out1_ptr, &out1, backend); + quad_value_store(out2_ptr, &out2, backend); in1_ptr += in1_stride; in2_ptr += in2_stride; @@ -270,8 +267,10 @@ quad_generic_binop_2out_strided_loop_aligned(PyArrayMethod_Context *context, cha (Sleef_quad *)out1_ptr, (Sleef_quad *)out2_ptr); } else { - longdouble_op((long double *)in1_ptr, (long double *)in2_ptr, - (long double *)out1_ptr, (long double *)out2_ptr); + long double out1, out2; + longdouble_op((long double *)in1_ptr, (long double *)in2_ptr, &out1, &out2); + quad_longdouble_store(out1_ptr, out1); + quad_longdouble_store(out2_ptr, out2); } in1_ptr += in1_stride; @@ -338,12 +337,10 @@ quad_ldexp_strided_loop_unaligned(PyArrayMethod_Context *context, char *const da QuadPrecDTypeObject *descr = (QuadPrecDTypeObject *)context->descriptors[0]; QuadBackendType backend = descr->backend; - size_t elem_size = (backend == BACKEND_SLEEF) ? sizeof(Sleef_quad) : sizeof(long double); - quad_value in1, out; npy_intp in2_intp; // Platform-native integer (int64 on 64-bit, int32 on 32-bit) while (N--) { - memcpy(&in1, in1_ptr, elem_size); + quad_value_load(&in1, in1_ptr, backend); memcpy(&in2_intp, in2_ptr, sizeof(npy_intp)); int exp_value = (int)in2_intp; @@ -353,7 +350,7 @@ quad_ldexp_strided_loop_unaligned(PyArrayMethod_Context *context, char *const da } else { out.longdouble_value = longdouble_op(&in1.longdouble_value, &exp_value); } - memcpy(out_ptr, &out, elem_size); + quad_value_store(out_ptr, &out, backend); in1_ptr += in1_stride; in2_ptr += in2_stride; @@ -387,7 +384,7 @@ quad_ldexp_strided_loop_aligned(PyArrayMethod_Context *context, char *const data if (backend == BACKEND_SLEEF) { *(Sleef_quad *)out_ptr = sleef_op((Sleef_quad *)in1_ptr, &exp_value); } else { - *(long double *)out_ptr = longdouble_op((long double *)in1_ptr, &exp_value); + quad_longdouble_store(out_ptr, longdouble_op((long double *)in1_ptr, &exp_value)); } in1_ptr += in1_stride; diff --git a/src/csrc/umath/matmul.cpp b/src/csrc/umath/matmul.cpp index c04fe06..6d4b5de 100644 --- a/src/csrc/umath/matmul.cpp +++ b/src/csrc/umath/matmul.cpp @@ -470,7 +470,7 @@ naive_matmul_strided_loop(PyArrayMethod_Context *context, char *const data[], sum += a_val * b_val; } - memcpy(C_ij, &sum, sizeof(long double)); + quad_longdouble_store(C_ij, sum); } } } diff --git a/src/csrc/umath/unary_ops.cpp b/src/csrc/umath/unary_ops.cpp index cbfc062..574c37c 100644 --- a/src/csrc/umath/unary_ops.cpp +++ b/src/csrc/umath/unary_ops.cpp @@ -61,18 +61,16 @@ quad_generic_unary_op_strided_loop_unaligned(PyArrayMethod_Context *context, cha QuadPrecDTypeObject *descr = (QuadPrecDTypeObject *)context->descriptors[0]; QuadBackendType backend = descr->backend; - size_t elem_size = (backend == BACKEND_SLEEF) ? sizeof(Sleef_quad) : sizeof(long double); - quad_value in, out; while (N--) { - memcpy(&in, in_ptr, elem_size); + quad_value_load(&in, in_ptr, backend); if (backend == BACKEND_SLEEF) { out.sleef_value = sleef_op(&in.sleef_value); } else { out.longdouble_value = longdouble_op(&in.longdouble_value); } - memcpy(out_ptr, &out, elem_size); + quad_value_store(out_ptr, &out, backend); in_ptr += in_stride; out_ptr += out_stride; @@ -100,7 +98,7 @@ quad_generic_unary_op_strided_loop_aligned(PyArrayMethod_Context *context, char *(Sleef_quad *)out_ptr = sleef_op((Sleef_quad *)in_ptr); } else { - *(long double *)out_ptr = longdouble_op((long double *)in_ptr); + quad_longdouble_store(out_ptr, longdouble_op((long double *)in_ptr)); } in_ptr += in_stride; out_ptr += out_stride; @@ -321,19 +319,17 @@ quad_generic_unary_op_2out_strided_loop_unaligned(PyArrayMethod_Context *context QuadPrecDTypeObject *descr = (QuadPrecDTypeObject *)context->descriptors[0]; QuadBackendType backend = descr->backend; - size_t elem_size = (backend == BACKEND_SLEEF) ? sizeof(Sleef_quad) : sizeof(long double); - quad_value in, out1, out2; while (N--) { - memcpy(&in, in_ptr, elem_size); + quad_value_load(&in, in_ptr, backend); if (backend == BACKEND_SLEEF) { sleef_op(&in.sleef_value, &out1.sleef_value, &out2.sleef_value); } else { longdouble_op(&in.longdouble_value, &out1.longdouble_value, &out2.longdouble_value); } - memcpy(out1_ptr, &out1, elem_size); - memcpy(out2_ptr, &out2, elem_size); + quad_value_store(out1_ptr, &out1, backend); + quad_value_store(out2_ptr, &out2, backend); in_ptr += in_stride; out1_ptr += out1_stride; @@ -365,7 +361,10 @@ quad_generic_unary_op_2out_strided_loop_aligned(PyArrayMethod_Context *context, sleef_op((Sleef_quad *)in_ptr, (Sleef_quad *)out1_ptr, (Sleef_quad *)out2_ptr); } else { - longdouble_op((long double *)in_ptr, (long double *)out1_ptr, (long double *)out2_ptr); + long double out1, out2; + longdouble_op((long double *)in_ptr, &out1, &out2); + quad_longdouble_store(out1_ptr, out1); + quad_longdouble_store(out2_ptr, out2); } in_ptr += in_stride; out1_ptr += out1_stride; @@ -470,20 +469,18 @@ quad_frexp_strided_loop_unaligned(PyArrayMethod_Context *context, char *const da QuadPrecDTypeObject *descr = (QuadPrecDTypeObject *)context->descriptors[0]; QuadBackendType backend = descr->backend; - size_t elem_size = (backend == BACKEND_SLEEF) ? sizeof(Sleef_quad) : sizeof(long double); - quad_value in, out_mantissa; int out_exp; while (N--) { - memcpy(&in, in_ptr, elem_size); + quad_value_load(&in, in_ptr, backend); if (backend == BACKEND_SLEEF) { out_mantissa.sleef_value = sleef_op(&in.sleef_value, &out_exp); } else { out_mantissa.longdouble_value = longdouble_op(&in.longdouble_value, &out_exp); } - memcpy(out_mantissa_ptr, &out_mantissa, elem_size); + quad_value_store(out_mantissa_ptr, &out_mantissa, backend); memcpy(out_exp_ptr, &out_exp, sizeof(int)); in_ptr += in_stride; @@ -520,7 +517,7 @@ quad_frexp_strided_loop_aligned(PyArrayMethod_Context *context, char *const data } else { long double mantissa = longdouble_op((long double *)in_ptr, &out_exp); - memcpy(out_mantissa_ptr, &mantissa, sizeof(long double)); + quad_longdouble_store(out_mantissa_ptr, mantissa); } memcpy(out_exp_ptr, &out_exp, sizeof(int)); diff --git a/src/csrc/utilities.c b/src/csrc/utilities.c index dea2e50..9d1a176 100644 --- a/src/csrc/utilities.c +++ b/src/csrc/utilities.c @@ -158,7 +158,7 @@ cstring_to_quad_internal(const char *str, const char *start, QuadBackendType bac } } else { - out_value->longdouble_value = strtold(str, endptr); + quad_value_set_longdouble(out_value, strtold(str, endptr)); } if (endptr && *endptr == str) { @@ -218,7 +218,8 @@ NumPyOS_ascii_strtoq(const char *s, QuadBackendType backend, quad_value *out_val out_value->sleef_value = sign > 0 ? QUAD_PRECISION_INF : QUAD_PRECISION_NINF; } else { - out_value->longdouble_value = sign > 0 ? strtold("inf", NULL) : strtold("-inf", NULL); + quad_value_set_longdouble(out_value, + sign > 0 ? strtold("inf", NULL) : strtold("-inf", NULL)); } if (endptr) { @@ -252,7 +253,7 @@ NumPyOS_ascii_strtoq(const char *s, QuadBackendType backend, quad_value *out_val out_value->sleef_value = nan_val; } else { - out_value->longdouble_value = sign < 0 ? -nanl("") : nanl(""); + quad_value_set_longdouble(out_value, sign < 0 ? -nanl("") : nanl("")); } if (endptr) { diff --git a/src/include/quad_common.h b/src/include/quad_common.h index 566269a..17ff8f7 100644 --- a/src/include/quad_common.h +++ b/src/include/quad_common.h @@ -7,6 +7,8 @@ extern "C" { #include #include +#include +#include typedef enum { BACKEND_INVALID = -1, @@ -19,6 +21,54 @@ typedef union { long double longdouble_value; } quad_value; +static inline void +quad_value_zero(quad_value *value) +{ + // Prevent the compiler from eliding writes to long double padding bytes. + volatile unsigned char *bytes = (volatile unsigned char *)value; + for (size_t i = 0; i < sizeof(*value); i++) { + bytes[i] = 0; + } +} + +static inline void +quad_value_set_longdouble(quad_value *value, long double input) +{ + quad_value_zero(value); + value->longdouble_value = input; +} + +static inline void +quad_value_load(quad_value *value, const void *src, QuadBackendType backend) +{ + if (backend == BACKEND_SLEEF) { + memcpy(&value->sleef_value, src, sizeof(value->sleef_value)); + } + else { + long double input; + memcpy(&input, src, sizeof(input)); + quad_value_set_longdouble(value, input); + } +} + +static inline void +quad_longdouble_store(void *dst, long double value) +{ + quad_value canonical; + quad_value_set_longdouble(&canonical, value); + memcpy(dst, &canonical, sizeof(canonical.longdouble_value)); +} + +static inline void +quad_value_store(void *dst, const quad_value *value, QuadBackendType backend) +{ + if (backend == BACKEND_SLEEF) { + memcpy(dst, &value->sleef_value, sizeof(value->sleef_value)); + } + else { + quad_longdouble_store(dst, value->longdouble_value); + } +} // For IEEE 754 binary128 (quad precision), we need 36 decimal digits // to guarantee round-trip conversion (string -> parse -> equals original value) diff --git a/src/include/utilities.h b/src/include/utilities.h index 6a0384c..a3f7b7e 100644 --- a/src/include/utilities.h +++ b/src/include/utilities.h @@ -63,12 +63,7 @@ template static inline void load_quad(const char *ptr, QuadBackendType backend, quad_value *out) { - if (backend == BACKEND_SLEEF) { - out->sleef_value = load(ptr); - } - else { - out->longdouble_value = load(ptr); - } + quad_value_load(out, ptr, backend); } // Store quad_value to memory based on backend and alignment @@ -76,12 +71,7 @@ template static inline void store_quad(char *ptr, const quad_value *val, QuadBackendType backend) { - if (backend == BACKEND_SLEEF) { - store(ptr, val->sleef_value); - } - else { - store(ptr, val->longdouble_value); - } + quad_value_store(ptr, val, backend); } #endif // __cplusplus diff --git a/tests/test_longdouble_padding.py b/tests/test_longdouble_padding.py new file mode 100644 index 0000000..e1b1605 --- /dev/null +++ b/tests/test_longdouble_padding.py @@ -0,0 +1,145 @@ +import numpy as np +import pytest + +from numpy_quaddtype import QuadPrecDType, QuadPrecision + + +def x87_longdouble_dtype(): + dtype = QuadPrecDType(backend="longdouble") + if dtype.itemsize != 16 or np.finfo(np.longdouble).nmant != 63: + pytest.skip("long double is not x87 80-bit stored in 16 bytes") + return dtype + + +def poisoned_array(dtype, offset=0): + storage = bytearray(b"\xa5" * (offset + dtype.itemsize)) + array = np.ndarray((1,), dtype=dtype, buffer=storage, offset=offset) + return array, storage + + +def assert_zero_padding(storage, offset=0): + assert bytes(storage[offset + 10 : offset + 16]) == b"\x00" * 6 + + +def assert_array_padding_zero(array): + raw = array.tobytes() + for start in range(0, len(raw), array.dtype.itemsize): + assert raw[start + 10 : start + 16] == b"\x00" * 6 + + +@pytest.mark.parametrize("offset", [0, 1]) +def test_setitem_zeroes_longdouble_padding(offset): + dtype = x87_longdouble_dtype() + array, storage = poisoned_array(dtype, offset) + + array[0] = QuadPrecision("1.5", backend="longdouble") + + assert array[0] == QuadPrecision("1.5", backend="longdouble") + assert_zero_padding(storage, offset) + + +@pytest.mark.parametrize("offset", [0, 1]) +@pytest.mark.parametrize( + ("operation", "expected"), + [ + (lambda value, out: np.negative(value, out=out), -1.5), + (lambda value, out: np.add(value, value, out=out), 3.0), + (lambda value, out: np.ldexp(value, 2, out=out), 6.0), + ], +) +def test_ufunc_zeroes_longdouble_padding(offset, operation, expected): + dtype = x87_longdouble_dtype() + value = np.array([1.5], dtype=dtype) + out, storage = poisoned_array(dtype, offset) + + operation(value, out) + + assert out[0] == QuadPrecision(str(expected), backend="longdouble") + assert_zero_padding(storage, offset) + + +@pytest.mark.parametrize("offset", [0, 1]) +def test_multi_output_ufunc_zeroes_longdouble_padding(offset): + dtype = x87_longdouble_dtype() + value = np.array([1.5], dtype=dtype) + fractional, fractional_storage = poisoned_array(dtype, offset) + integral, integral_storage = poisoned_array(dtype, offset) + + np.modf(value, out=(fractional, integral)) + + assert fractional[0] == QuadPrecision("0.5", backend="longdouble") + assert integral[0] == QuadPrecision("1.0", backend="longdouble") + assert_zero_padding(fractional_storage, offset) + assert_zero_padding(integral_storage, offset) + + +@pytest.mark.parametrize("offset", [0, 1]) +def test_binary_multi_output_ufunc_zeroes_longdouble_padding(offset): + dtype = x87_longdouble_dtype() + dividend = np.array([5.5], dtype=dtype) + divisor = np.array([2.0], dtype=dtype) + quotient, quotient_storage = poisoned_array(dtype, offset) + remainder, remainder_storage = poisoned_array(dtype, offset) + + np.divmod(dividend, divisor, out=(quotient, remainder)) + + assert quotient[0] == QuadPrecision("2.0", backend="longdouble") + assert remainder[0] == QuadPrecision("1.5", backend="longdouble") + assert_zero_padding(quotient_storage, offset) + assert_zero_padding(remainder_storage, offset) + + +@pytest.mark.parametrize("offset", [0, 1]) +def test_frexp_zeroes_longdouble_padding(offset): + dtype = x87_longdouble_dtype() + value = np.array([6.0], dtype=dtype) + mantissa, storage = poisoned_array(dtype, offset) + exponent = np.empty(1, dtype=np.int32) + + np.frexp(value, out=(mantissa, exponent)) + + assert mantissa[0] == QuadPrecision("0.75", backend="longdouble") + assert exponent[0] == 3 + assert_zero_padding(storage, offset) + + +@pytest.mark.parametrize("offset", [0, 1]) +@pytest.mark.parametrize("source_backend", [None, "sleef"]) +def test_cast_zeroes_longdouble_padding(offset, source_backend): + dtype = x87_longdouble_dtype() + if source_backend is None: + source = np.array([1.5], dtype=np.float64) + else: + source = np.array( + [QuadPrecision("1.5", backend=source_backend)], + dtype=QuadPrecDType(backend=source_backend), + ) + out, storage = poisoned_array(dtype, offset) + + np.copyto(out, source, casting="unsafe") + + assert out[0] == QuadPrecision("1.5", backend="longdouble") + assert_zero_padding(storage, offset) + + +def test_arange_zeroes_longdouble_padding(): + dtype = x87_longdouble_dtype() + + result = np.arange(8, dtype=dtype) + + np.testing.assert_array_equal( + result.astype(np.float64), np.arange(8, dtype=np.float64) + ) + assert_array_padding_zero(result) + + +def test_fromstring_zeroes_longdouble_padding(): + dtype = x87_longdouble_dtype() + + result = np.fromstring("1.5 inf -nan", dtype=dtype, sep=" ") + + assert result[0] == QuadPrecision("1.5", backend="longdouble") + assert np.isposinf(result[1]) + assert np.isnan(result[2]) + assert np.signbit(result[2]) + assert_array_padding_zero(result) From 3c892831bd12f03a5ee29615cbfa09ffdbacb757 Mon Sep 17 00:00:00 2001 From: SwayamInSync Date: Sat, 25 Jul 2026 15:44:39 +0530 Subject: [PATCH 2/2] platform dependent hotpath for perf --- src/csrc/dtype.c | 2 +- src/csrc/scalar.c | 14 +++++++++---- src/include/quad_common.h | 34 +++++++++++++++++++++++++++++++- tests/test_longdouble_padding.py | 14 +++++++++++++ 4 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/csrc/dtype.c b/src/csrc/dtype.c index a6d92e6..67ba982 100644 --- a/src/csrc/dtype.c +++ b/src/csrc/dtype.c @@ -143,7 +143,7 @@ quadprec_getitem(QuadPrecDTypeObject *descr, char *dataptr) if (!new) { return NULL; } - quad_value_load(&new->value, dataptr, descr->backend); + quad_value_load_canonical(&new->value, dataptr, descr->backend); return (PyObject *)new; } diff --git a/src/csrc/scalar.c b/src/csrc/scalar.c index 4728574..86cb49e 100644 --- a/src/csrc/scalar.c +++ b/src/csrc/scalar.c @@ -759,10 +759,16 @@ QuadPrecision_from_raw_bytes(PyObject *Py_UNUSED(module), PyObject *args) PyBuffer_Release(&view); return NULL; } - unsigned char *dst = (backend == BACKEND_SLEEF) - ? (unsigned char *)&self->value.sleef_value - : (unsigned char *)&self->value.longdouble_value; - quad_copy_canonical(dst, (const unsigned char *)view.buf, expected); + unsigned char raw[sizeof(quad_value)]; + quad_copy_canonical(raw, (const unsigned char *)view.buf, expected); + if (backend == BACKEND_SLEEF) { + memcpy(&self->value.sleef_value, raw, expected); + } + else { + long double value; + memcpy(&value, raw, expected); + quad_value_set_longdouble(&self->value, value); + } PyBuffer_Release(&view); return (PyObject *)self; } diff --git a/src/include/quad_common.h b/src/include/quad_common.h index 17ff8f7..35ad7de 100644 --- a/src/include/quad_common.h +++ b/src/include/quad_common.h @@ -7,9 +7,14 @@ extern "C" { #include #include +#include #include #include +#ifdef _MSC_VER +#include +#endif + typedef enum { BACKEND_INVALID = -1, BACKEND_SLEEF, @@ -24,11 +29,19 @@ typedef union { static inline void quad_value_zero(quad_value *value) { - // Prevent the compiler from eliding writes to long double padding bytes. + // Make all object-representation bytes observable before assigning a value. +#if defined(__GNUC__) || defined(__clang__) + memset(value, 0, sizeof(*value)); + __asm__ __volatile__("" : : "r"(value) : "memory"); +#elif defined(_MSC_VER) + memset(value, 0, sizeof(*value)); + _ReadWriteBarrier(); +#else volatile unsigned char *bytes = (volatile unsigned char *)value; for (size_t i = 0; i < sizeof(*value); i++) { bytes[i] = 0; } +#endif } static inline void @@ -40,6 +53,17 @@ quad_value_set_longdouble(quad_value *value, long double input) static inline void quad_value_load(quad_value *value, const void *src, QuadBackendType backend) +{ + if (backend == BACKEND_SLEEF) { + memcpy(&value->sleef_value, src, sizeof(value->sleef_value)); + } + else { + memcpy(&value->longdouble_value, src, sizeof(value->longdouble_value)); + } +} + +static inline void +quad_value_load_canonical(quad_value *value, const void *src, QuadBackendType backend) { if (backend == BACKEND_SLEEF) { memcpy(&value->sleef_value, src, sizeof(value->sleef_value)); @@ -54,9 +78,17 @@ quad_value_load(quad_value *value, const void *src, QuadBackendType backend) static inline void quad_longdouble_store(void *dst, long double value) { +#if (defined(__i386__) || defined(__x86_64__)) && LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 + // x87 extended precision occupies bytes 0..9; the remaining bytes are padding. + memset(dst, 0, sizeof(value)); + memcpy(dst, &value, 10); +#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 quad_value canonical; quad_value_set_longdouble(&canonical, value); memcpy(dst, &canonical, sizeof(canonical.longdouble_value)); +#else + memcpy(dst, &value, sizeof(value)); +#endif } static inline void diff --git a/tests/test_longdouble_padding.py b/tests/test_longdouble_padding.py index e1b1605..cb66cb3 100644 --- a/tests/test_longdouble_padding.py +++ b/tests/test_longdouble_padding.py @@ -2,6 +2,7 @@ import pytest from numpy_quaddtype import QuadPrecDType, QuadPrecision +from numpy_quaddtype._quaddtype_main import from_raw_bytes def x87_longdouble_dtype(): @@ -143,3 +144,16 @@ def test_fromstring_zeroes_longdouble_padding(): assert np.isnan(result[2]) assert np.signbit(result[2]) assert_array_padding_zero(result) + + +def test_from_raw_bytes_zeroes_longdouble_padding(): + x87_longdouble_dtype() + original = QuadPrecision("1.5", backend="longdouble") + raw = bytearray(original.__reduce__()[1][0]) + raw[10:] = b"\xa5" * 6 + + result = from_raw_bytes(bytes(raw), "longdouble") + result_raw = result.__reduce__()[1][0] + + assert result == original + assert result_raw[10:] == b"\x00" * 6