Skip to content

Commit ae9986b

Browse files
committed
memory barrier
1 parent c17c3d0 commit ae9986b

3 files changed

Lines changed: 120 additions & 31 deletions

File tree

quaddtype/numpy_quaddtype/src/casts.cpp

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ quad_to_quad_resolve_descriptors(PyObject *NPY_UNUSED(self),
5555
// Different backends require actual conversion, no view possible
5656
*view_offset = NPY_MIN_INTP;
5757
if (given_descrs[0]->backend == BACKEND_SLEEF) {
58-
return NPY_SAME_KIND_CASTING; // SLEEF -> long double may lose precision
58+
// SLEEF -> long double may lose precision
59+
return static_cast<NPY_CASTING>(NPY_SAME_KIND_CASTING | NPY_SAME_VALUE_CASTING_FLAG);
5960
}
6061
// long double -> SLEEF preserves value exactly
6162
return static_cast<NPY_CASTING>(NPY_SAFE_CASTING | NPY_SAME_VALUE_CASTING_FLAG);
@@ -81,42 +82,47 @@ quad_to_quad_strided_loop(PyArrayMethod_Context *context, char *const data[],
8182
QuadPrecDTypeObject *descr_out = (QuadPrecDTypeObject *)context->descriptors[1];
8283
QuadBackendType backend_in = descr_in->backend;
8384
QuadBackendType backend_out = descr_out->backend;
85+
int same_value_casting = ((context->flags & NPY_SAME_VALUE_CONTEXT_FLAG) == NPY_SAME_VALUE_CONTEXT_FLAG);
8486

8587
// inter-backend casting
8688
if (backend_in != backend_out) {
8789
while (N--) {
88-
quad_value in_val = load_quad<Aligned>(in_ptr, backend_in);
90+
quad_value in_val;
91+
load_quad<Aligned>(in_ptr, backend_in, &in_val);
92+
quad_value out_val;
8993
if (backend_in == BACKEND_SLEEF)
9094
{
91-
long double res = Sleef_cast_to_doubleq1(in_val.sleef_value);
92-
store<Aligned>(out_ptr, res);
95+
out_val.longdouble_value = static_cast<long double>(Sleef_cast_to_doubleq1(in_val.sleef_value));
9396
}
9497
else
9598
{
96-
Sleef_quad res;
9799
long double ld = in_val.longdouble_value;
98100
if (std::isnan(ld)) {
99-
res = QUAD_PRECISION_NAN;
101+
out_val.sleef_value = QUAD_PRECISION_NAN;
100102
}
101103
else if (std::isinf(ld)) {
102-
res = (ld > 0) ? QUAD_PRECISION_INF : QUAD_PRECISION_NINF;
104+
out_val.sleef_value = (ld > 0) ? QUAD_PRECISION_INF : QUAD_PRECISION_NINF;
103105
}
104106
else
105107
{
106-
res = Sleef_cast_from_doubleq1(static_cast<double>(ld));
108+
// to prevent compiler optimizations, ABI handling issues with __float128 on x86-64 machines
109+
// won't be expensive as for fixed size compiler can optimize memcpy with movq
110+
Sleef_quad temp = Sleef_cast_from_doubleq1(static_cast<double>(ld));
111+
std::memcpy(&out_val.sleef_value, &temp, sizeof(Sleef_quad));
107112
}
108-
store<Aligned>(out_ptr, res);
109113
}
114+
store_quad<Aligned>(out_ptr, &out_val, backend_out);
110115
in_ptr += in_stride;
111116
out_ptr += out_stride;
112117
}
113118
return 0;
114119
}
115120

116121
// same backend: direct copy
117-
while (N--) {
118-
quad_value val = load_quad<Aligned>(in_ptr, backend_in);
119-
store_quad<Aligned>(out_ptr, val, backend_out);
122+
while(N--) {
123+
quad_value val;
124+
load_quad<Aligned>(in_ptr, backend_in, &val);
125+
store_quad<Aligned>(out_ptr, &val, backend_out);
120126
in_ptr += in_stride;
121127
out_ptr += out_stride;
122128
}
@@ -242,7 +248,7 @@ unicode_to_quad_strided_loop(PyArrayMethod_Context *context, char *const data[],
242248
return -1;
243249
}
244250

245-
store_quad<Aligned>(out_ptr, out_val, backend);
251+
store_quad<Aligned>(out_ptr, &out_val, backend);
246252

247253
in_ptr += in_stride;
248254
out_ptr += out_stride;
@@ -464,7 +470,8 @@ quad_to_unicode_loop(PyArrayMethod_Context *context, char *const data[],
464470
int same_value_casting = ((context->flags & NPY_SAME_VALUE_CONTEXT_FLAG) == NPY_SAME_VALUE_CONTEXT_FLAG);
465471

466472
while (N--) {
467-
quad_value in_val = load_quad<Aligned>(in_ptr, backend);
473+
quad_value in_val;
474+
load_quad<Aligned>(in_ptr, backend, &in_val);
468475

469476
// Convert to Sleef_quad for Dragon4
470477
Sleef_quad sleef_val = quad_to_sleef_quad(&in_val, backend);
@@ -595,7 +602,7 @@ bytes_to_quad_strided_loop(PyArrayMethod_Context *context, char *const data[],
595602
return -1;
596603
}
597604

598-
store_quad<Aligned>(out_ptr, out_val, backend);
605+
store_quad<Aligned>(out_ptr, &out_val, backend);
599606

600607
in_ptr += in_stride;
601608
out_ptr += out_stride;
@@ -657,7 +664,8 @@ quad_to_bytes_loop(PyArrayMethod_Context *context, char *const data[],
657664
int same_value_casting = ((context->flags & NPY_SAME_VALUE_CONTEXT_FLAG) == NPY_SAME_VALUE_CONTEXT_FLAG);
658665

659666
while (N--) {
660-
quad_value in_val = load_quad<Aligned>(in_ptr, backend);
667+
quad_value in_val;
668+
load_quad<Aligned>(in_ptr, backend, &in_val);
661669
Sleef_quad sleef_val = quad_to_sleef_quad(&in_val, backend);
662670

663671
const char *temp_str = quad_to_string_adaptive_cstr(&sleef_val, bytes_size);
@@ -756,7 +764,7 @@ stringdtype_to_quad_strided_loop(PyArrayMethod_Context *context, char *const dat
756764
return -1;
757765
}
758766

759-
store_quad<Aligned>(out_ptr, out_val, backend);
767+
store_quad<Aligned>(out_ptr, &out_val, backend);
760768

761769
in_ptr += in_stride;
762770
out_ptr += out_stride;
@@ -814,7 +822,8 @@ quad_to_stringdtype_strided_loop(PyArrayMethod_Context *context, char *const dat
814822
npy_string_allocator *allocator = NpyString_acquire_allocator(str_descr);
815823

816824
while (N--) {
817-
quad_value in_val = load_quad<Aligned>(in_ptr, backend);
825+
quad_value in_val;
826+
load_quad<Aligned>(in_ptr, backend, &in_val);
818827
Sleef_quad sleef_val = quad_to_sleef_quad(&in_val, backend);
819828

820829
// Get string representation with adaptive notation
@@ -1123,7 +1132,7 @@ numpy_to_quad_strided_loop(PyArrayMethod_Context *context, char *const data[],
11231132
while (N--) {
11241133
typename NpyType<T>::TYPE in_val = load<Aligned, typename NpyType<T>::TYPE>(in_ptr);
11251134
quad_value out_val = to_quad<T>(in_val, backend);
1126-
store_quad<Aligned>(out_ptr, out_val, backend);
1135+
store_quad<Aligned>(out_ptr, &out_val, backend);
11271136

11281137
in_ptr += strides[0];
11291138
out_ptr += strides[1];
@@ -1408,7 +1417,8 @@ quad_to_numpy_strided_loop(PyArrayMethod_Context *context, char *const data[],
14081417

14091418
if (same_value_casting) {
14101419
while (N--) {
1411-
quad_value in_val = load_quad<Aligned>(in_ptr, backend);
1420+
quad_value in_val;
1421+
load_quad<Aligned>(in_ptr, backend, &in_val);
14121422
typename NpyType<T>::TYPE out_val;
14131423
int ret = quad_to_numpy_same_value_check<T>(&in_val, backend, &out_val);
14141424
if(ret < 0)
@@ -1421,7 +1431,8 @@ quad_to_numpy_strided_loop(PyArrayMethod_Context *context, char *const data[],
14211431
return 0;
14221432
}
14231433
while (N--) {
1424-
quad_value in_val = load_quad<Aligned>(in_ptr, backend);
1434+
quad_value in_val;
1435+
load_quad<Aligned>(in_ptr, backend, &in_val);
14251436
typename NpyType<T>::TYPE out_val = from_quad<T>(&in_val, backend);
14261437
store<Aligned, typename NpyType<T>::TYPE>(out_ptr, out_val);
14271438

quaddtype/numpy_quaddtype/src/utilities.h

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,29 +60,27 @@ store(char *ptr, const T &val)
6060

6161
// Load quad_value from memory based on backend and alignment
6262
template <bool Aligned>
63-
static inline quad_value
64-
load_quad(const char *ptr, QuadBackendType backend)
63+
static inline void
64+
load_quad(const char *ptr, QuadBackendType backend, quad_value *out)
6565
{
66-
quad_value val{};
6766
if (backend == BACKEND_SLEEF) {
68-
val.sleef_value = load<Aligned, Sleef_quad>(ptr);
67+
out->sleef_value = load<Aligned, Sleef_quad>(ptr);
6968
}
7069
else {
71-
val.longdouble_value = load<Aligned, long double>(ptr);
70+
out->longdouble_value = load<Aligned, long double>(ptr);
7271
}
73-
return val;
7472
}
7573

7674
// Store quad_value to memory based on backend and alignment
7775
template <bool Aligned>
7876
static inline void
79-
store_quad(char *ptr, const quad_value &val, QuadBackendType backend)
77+
store_quad(char *ptr, const quad_value *val, QuadBackendType backend)
8078
{
8179
if (backend == BACKEND_SLEEF) {
82-
store<Aligned, Sleef_quad>(ptr, val.sleef_value);
80+
store<Aligned, Sleef_quad>(ptr, val->sleef_value);
8381
}
8482
else {
85-
store<Aligned, long double>(ptr, val.longdouble_value);
83+
store<Aligned, long double>(ptr, val->longdouble_value);
8684
}
8785
}
8886

quaddtype/tests/test_quaddtype.py

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5575,4 +5575,84 @@ def test_same_value_cast_strings_narrow_width(self, dtype):
55755575
for val in failing_values:
55765576
q = np.array([val], dtype=QuadPrecDType())
55775577
with pytest.raises(ValueError):
5578-
q.astype(dtype, casting="same_value")
5578+
q.astype(dtype, casting="same_value")
5579+
5580+
# @pytest.mark.parametrize("src_backend,dst_backend", [
5581+
# ("sleef", "longdouble"),
5582+
# ("longdouble", "sleef"),
5583+
# ("sleef", "sleef"),
5584+
# ("longdouble", "longdouble")
5585+
# ])
5586+
# def test_quad_to_quad_same_value_casting_passing(self, src_backend, dst_backend):
5587+
# """Test values that should roundtrip exactly between backends."""
5588+
# # Values exactly representable in both backends (and in double, since
5589+
# # inter-backend conversion goes through double)
5590+
# passing_values = [
5591+
# "0.0", "-0.0", "1.0", "-1.0",
5592+
# "0.5", "0.25", "0.125",
5593+
# "2.0", "4.0", "8.0",
5594+
# "inf", "-inf", "nan",
5595+
# "1e100", "-1e-100",
5596+
# str(2**52), # Largest consecutive integer in double
5597+
# ]
5598+
5599+
# for val in passing_values:
5600+
# src = np.array([val], dtype=QuadPrecDType(backend=src_backend))
5601+
# result = src.astype(QuadPrecDType(backend=dst_backend), casting="same_value")
5602+
5603+
# # Verify value is preserved
5604+
# if val == "nan":
5605+
# assert np.isnan(result[0])
5606+
# else:
5607+
# assert result[0] == src[0], f"Value {val} failed for {src_backend} -> {dst_backend}"
5608+
5609+
5610+
# @pytest.mark.parametrize("src_backend,dst_backend", [
5611+
# ("sleef", "longdouble"),
5612+
# ("longdouble", "sleef"),
5613+
# ])
5614+
# def test_quad_to_quad_interbackend_same_value_casting_failing(self, src_backend, dst_backend):
5615+
# """Test values that cannot roundtrip exactly between backends."""
5616+
5617+
# # Inter-backend conversion goes through double, so values outside
5618+
# # double's precision should fail
5619+
# ld_info = np.finfo(np.longdouble)
5620+
5621+
# # Skip if longdouble has same precision as quad (PowerPC binary128)
5622+
# if ld_info.nmant >= 112:
5623+
# pytest.skip("longdouble has same precision as quad on this platform")
5624+
5625+
# # Also need to consider that conversion goes through double
5626+
# # So precision is limited by double (~52 bit mantissa)
5627+
# double_info = np.finfo(np.float64)
5628+
5629+
# # Values that exceed double precision
5630+
# failing_values = [
5631+
# str(2**53 + 1), # First integer not exactly representable in double
5632+
# "1.0000000000000001", # 1 + small epsilon beyond double precision
5633+
# "3.141592653589793238462643383279502884197", # Pi with more than double precision
5634+
# ]
5635+
5636+
# for val in failing_values:
5637+
# src = np.array([val], dtype=QuadPrecDType(backend=src_backend))
5638+
# with pytest.raises(ValueError):
5639+
# src.astype(QuadPrecDType(backend=dst_backend), casting="same_value")
5640+
5641+
5642+
# @pytest.mark.parametrize("backend", ["sleef", "longdouble"])
5643+
# def test_quad_to_quad_same_backend_always_passes(self, backend):
5644+
# """Same backend conversion should always pass same_value."""
5645+
# # Even high-precision values should pass when backend is the same
5646+
# values = [
5647+
# "3.141592653589793238462643383279502884197",
5648+
# "2.718281828459045235360287471352662497757",
5649+
# str(2**113), # Large integer
5650+
# "1e4000", # Large exponent (within quad range)
5651+
# ]
5652+
5653+
# for val in values:
5654+
# src = np.array([val], dtype=QuadPrecDType(backend=backend))
5655+
# result = src.astype(QuadPrecDType(backend=backend), casting="same_value")
5656+
# # Should not raise, and value should be unchanged
5657+
# assert str(result[0]) == str(src[0])
5658+

0 commit comments

Comments
 (0)