From b545becbddbb1b42320ba114110b01768615600b Mon Sep 17 00:00:00 2001 From: ZhangFengyao Date: Fri, 20 Jun 2025 02:07:44 +0800 Subject: [PATCH 1/5] Fix: Correct `PyLongObj_Size` for signed length in Python 3.12+ My apologies for the prior misunderstanding of the `lv_tag` field in the `_PyLongValue` structure. **Note**: `lv_tag` is an internal CPython detail. Relying on such undocumented internals is unstable and may change (PEP 757). This fix addresses the immediate issue but highlights this fragility. --- charm/core/math/pairing/pairingmodule.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/charm/core/math/pairing/pairingmodule.c b/charm/core/math/pairing/pairingmodule.c index 23ef8f82..69ae438f 100644 --- a/charm/core/math/pairing/pairingmodule.c +++ b/charm/core/math/pairing/pairingmodule.c @@ -35,9 +35,9 @@ #define PyLongObj_Size(l) Py_SIZE(l) #else #define PyLongObj_Val(l, i) (l)->long_value.ob_digit[i] - // #define _PyLong_SIZE_SHIFTS __builtin_popcount(_PyLong_NON_SIZE_BITS) - #define _PyLong_SIZE_SHIFTS 2 - #define PyLongObj_Size(l) ((l)->long_value.lv_tag >> _PyLong_SIZE_SHIFTS) + // lv_tag sign bits: 00 = positive, 01 = zero, 10 = negative + #define PyLongObj_Size(l) ((1 - ((l)->long_value.lv_tag & _PyLong_SIGN_MASK)) \ + * ((l)->long_value.lv_tag >> _PyLong_NON_SIZE_BITS)) #endif // PEP 674 – Disallow using macros as l-values @@ -124,7 +124,6 @@ static PyObject *f(PyObject *v, PyObject *w) { \ return NULL; \ } -// TODO: update these two functions to convert neg numbers PyObject *mpzToLongObj (mpz_t m) { /* borrowed from gmpy */ From efd7126de16a663db33ec73487c1b274df514cdb Mon Sep 17 00:00:00 2001 From: ZhangFengyao Date: Fri, 20 Jun 2025 02:28:17 +0800 Subject: [PATCH 2/5] Perf: Optimize `longObjToMPZ` by removing temp variables This commit eliminates unnecessary initialization and copying overhead associated with the `temp` and `temp2` variables. The function's efficiency is improved without altering its core functionality. --- charm/core/math/pairing/pairingmodule.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/charm/core/math/pairing/pairingmodule.c b/charm/core/math/pairing/pairingmodule.c index 69ae438f..04ff11c7 100644 --- a/charm/core/math/pairing/pairingmodule.c +++ b/charm/core/math/pairing/pairingmodule.c @@ -154,26 +154,17 @@ PyObject *mpzToLongObj (mpz_t m) void longObjToMPZ (mpz_t m, PyLongObject * p) { - int size, i, tmp = PyLongObj_Size(p); + int size = PyLongObj_Size(p); int isNeg = FALSE; - mpz_t temp, temp2; - mpz_init (temp); - mpz_init (temp2); - if (tmp > 0) - size = tmp; - else { - size = -tmp; + if (size < 0) { + size = -size; isNeg = TRUE; } mpz_set_ui (m, 0); - for (i = 0; i < size; i++) - { - mpz_set_ui (temp, PyLongObj_Val(p, i)); - mpz_mul_2exp (temp2, temp, PyLong_SHIFT * i); - mpz_add (m, m, temp2); + for (int i = size - 1; i >= 0; i--) { + mpz_mul_2exp (m, m, PyLong_SHIFT); + mpz_add_ui (m, m, PyLongObj_Val(p, i)); } - mpz_clear (temp); - mpz_clear (temp2); if(isNeg) mpz_neg(m, m); } From bb370792c047abc448fad970e375e12f85195fec Mon Sep 17 00:00:00 2001 From: ZhangFengyao Date: Fri, 20 Jun 2025 04:58:31 +0800 Subject: [PATCH 3/5] Fix: Correct `Py_SET_SIZE` behavior in Python 3.12+ and rename macros * Fixed the logic of `Py_SET_SIZE` macro in Python 3.12+ * Renamed macros to improve clarity. --- charm/core/math/pairing/pairingmodule.c | 40 ++++++++++++++----------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/charm/core/math/pairing/pairingmodule.c b/charm/core/math/pairing/pairingmodule.c index 04ff11c7..e636c785 100644 --- a/charm/core/math/pairing/pairingmodule.c +++ b/charm/core/math/pairing/pairingmodule.c @@ -31,18 +31,28 @@ // PEP 757 – C API to import-export Python integers #if PY_MINOR_VERSION <= 11 - #define PyLongObj_Val(l, i) (l)->ob_digit[i] - #define PyLongObj_Size(l) Py_SIZE(l) + #define PyLong_DIGIT(l, i) (l)->ob_digit[i] + #define PyLong_SIZE(l) Py_SIZE(l) #else - #define PyLongObj_Val(l, i) (l)->long_value.ob_digit[i] + #define PyLong_DIGIT(l, i) (l)->long_value.ob_digit[i] // lv_tag sign bits: 00 = positive, 01 = zero, 10 = negative - #define PyLongObj_Size(l) ((1 - ((l)->long_value.lv_tag & _PyLong_SIGN_MASK)) \ - * ((l)->long_value.lv_tag >> _PyLong_NON_SIZE_BITS)) + #define PyLong_SIZE(l) ((1 - ((l)->long_value.lv_tag & _PyLong_SIGN_MASK)) \ + * ((l)->long_value.lv_tag >> _PyLong_NON_SIZE_BITS)) #endif -// PEP 674 – Disallow using macros as l-values #if PY_MINOR_VERSION <= 10 - #define Py_SET_SIZE(l, i) do { Py_SIZE(l) = (i); } while (0) + #define PyLong_SET_SIZE(l, i) do { Py_SIZE(l) = (i); } while (0) +#elif PY_MINOR_VERSION <= 11 + // PEP 674 – Disallow using macros as l-values + #define PyLong_SET_SIZE(l, i) Py_SET_SIZE(l, i) +#else + #define PyLong_SET_SIZE(l, i) \ + do { \ + int _signed_size = (i); \ + int _sign = _signed_size > 0 ? 0 : (_signed_size == 0 ? 1 : 2); \ + int _size = _signed_size < 0 ? -_signed_size : _signed_size; \ + (l)->long_value.lv_tag = (_size << _PyLong_NON_SIZE_BITS) | _sign; \ + } while (0) #endif int exp_rule(GroupType lhs, GroupType rhs) @@ -136,25 +146,21 @@ PyObject *mpzToLongObj (mpz_t m) mpz_init_set (temp, m); for (i = 0; i < size; i++) { - PyLongObj_Val(l, i) = (digit) (mpz_get_ui (temp) & PyLong_MASK); + PyLong_DIGIT(l, i) = (digit) (mpz_get_ui (temp) & PyLong_MASK); mpz_fdiv_q_2exp (temp, temp, PyLong_SHIFT); } i = size; - while ((i > 0) && (PyLongObj_Val(l, i - 1) == 0)) + while ((i > 0) && (PyLong_DIGIT(l, i - 1) == 0)) i--; - if(isNeg) { - Py_SET_SIZE(l, -i); - } - else { - Py_SET_SIZE(l, i); - } + + PyLong_SET_SIZE(l, isNeg ? -i : i); mpz_clear (temp); return (PyObject *) l; } void longObjToMPZ (mpz_t m, PyLongObject * p) { - int size = PyLongObj_Size(p); + int size = PyLong_SIZE(p); int isNeg = FALSE; if (size < 0) { size = -size; @@ -163,7 +169,7 @@ void longObjToMPZ (mpz_t m, PyLongObject * p) mpz_set_ui (m, 0); for (int i = size - 1; i >= 0; i--) { mpz_mul_2exp (m, m, PyLong_SHIFT); - mpz_add_ui (m, m, PyLongObj_Val(p, i)); + mpz_add_ui (m, m, PyLong_DIGIT(p, i)); } if(isNeg) mpz_neg(m, m); } From caff34d91d7fb25756b130d93ba9f4c15261636f Mon Sep 17 00:00:00 2001 From: ZhangFengyao Date: Fri, 20 Jun 2025 15:05:45 +0800 Subject: [PATCH 4/5] Fix: Update Element multiplication to use GMP for integer handling * Replaced `signed long` with `mpz_t` for large integer support * Introduced `debug_gmp()` macro to output GMP info --- charm/core/math/pairing/pairingmodule.c | 37 +++++++++++++++++-------- charm/core/math/pairing/pairingmodule.h | 2 ++ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/charm/core/math/pairing/pairingmodule.c b/charm/core/math/pairing/pairingmodule.c index e636c785..60367656 100644 --- a/charm/core/math/pairing/pairingmodule.c +++ b/charm/core/math/pairing/pairingmodule.c @@ -839,40 +839,50 @@ static PyObject *Element_sub(Element *self, Element *other) static PyObject *Element_mul(PyObject *lhs, PyObject *rhs) { Element *self = NULL, *other = NULL, *newObject = NULL; - signed long int z; + mpz_t z; int found_int = FALSE; // lhs or rhs must be an element type if(PyElement_Check(lhs)) { self = (Element *) lhs; } - else if(PyNumber_Check(lhs)) { - if(PyArg_Parse(lhs, "l", &z)) { - debug("Integer lhs: '%li'\n", z); - } + else if(PyLong_Check(lhs)) { + mpz_init(z); + longObjToMPZ(z, (PyLongObject *) lhs); + debug_gmp("Integer lhs: '%Zd'\n", z); found_int = TRUE; } + else { + debug("lhs is not an element type or long object.\n"); + PyErr_SetString(ElementError, "invalid left operand type"); + return NULL; + } if(PyElement_Check(rhs)) { other = (Element *) rhs; } - else if(PyNumber_Check(rhs)) { - if(PyArg_Parse(rhs, "l", &z)) { - debug("Integer rhs: '%li'\n", z); - } - found_int = TRUE; + else if(PyLong_Check(rhs)) { + mpz_init(z); + longObjToMPZ(z, (PyLongObject *) rhs); + debug_gmp("Integer rhs: '%Zd'\n", z); + found_int = TRUE; + } + else { + debug("rhs is not an element type or long object.\n"); + PyErr_SetString(ElementError, "invalid right operand type"); + return NULL; } debug("Starting '%s'\n", __func__); if(PyElement_Check(lhs) && found_int) { // lhs is the element type newObject = createNewElement(self->element_type, self->pairing); - element_mul_si(newObject->e, self->e, z); + element_mul_mpz(newObject->e, self->e, z); } else if(PyElement_Check(rhs) && found_int) { // rhs is the element type newObject = createNewElement(other->element_type, other->pairing); - element_mul_si(newObject->e, other->e, z); + element_mul_mpz(newObject->e, other->e, z); } else if(PyElement_Check(lhs) && PyElement_Check(rhs)) { // both are element types @@ -895,6 +905,9 @@ static PyObject *Element_mul(PyObject *lhs, PyObject *rhs) else { EXIT_IF(TRUE, "invalid types."); } + if (found_int) { + mpz_clear(z); + } #ifdef BENCHMARK_ENABLED UPDATE_BENCH(MULTIPLICATION, newObject->element_type, newObject->pairing); #endif diff --git a/charm/core/math/pairing/pairingmodule.h b/charm/core/math/pairing/pairingmodule.h index bb66ef93..5e0413fd 100644 --- a/charm/core/math/pairing/pairingmodule.h +++ b/charm/core/math/pairing/pairingmodule.h @@ -78,8 +78,10 @@ typedef enum Group GroupType; #ifdef DEBUG #define debug_e(...) element_printf("DEBUG: "__VA_ARGS__) +#define debug_gmp(...) gmp_printf("DEBUG: "__VA_ARGS__) #else #define debug_e(...) +#define debug_gmp(...) #endif #define PrintPyRef(msg, o) printf("%s:" #msg " ref cnt = '%i'\n", __FUNCTION__, (int) Py_REFCNT(o)); From 44e9f68c36c1cef5cb01e4f2b4cd92ac3238a86c Mon Sep 17 00:00:00 2001 From: ZhangFengyao Date: Fri, 20 Jun 2025 17:54:39 +0800 Subject: [PATCH 5/5] Fix: Define `MS_WIN64` for correct `PyLong_SHIFT` on Windows 64-bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Windows 64-bit with Python ≤ 3.10, pyconfig.h only sets `SIZEOF_VOID_P` to 8 when `MS_WIN64` is defined. Without this, pyport.h falls back to a 15-bit `PYLONG_BITS_IN_DIGIT`, resulting in an incorrect `PyLong_SHIFT` and bad big-integer conversions. --- charm/core/math/integer/integermodule.h | 5 +++++ charm/core/math/pairing/miracl/pairingmodule2.h | 5 +++++ charm/core/math/pairing/pairingmodule.h | 5 +++++ charm/core/math/pairing/relic/pairingmodule3.h | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/charm/core/math/integer/integermodule.h b/charm/core/math/integer/integermodule.h index fead537f..5dee0216 100644 --- a/charm/core/math/integer/integermodule.h +++ b/charm/core/math/integer/integermodule.h @@ -34,6 +34,11 @@ #define PY_SSIZE_T_CLEAN #endif +/* Define MS_WIN64 to get correct PYLONG_BITS_IN_DIGIT on Windows. */ +#if PY_MINOR_VERSION <= 10 && defined(_WIN64) && !defined(MS_WIN64) +#define MS_WIN64 +#endif + #include #include #include diff --git a/charm/core/math/pairing/miracl/pairingmodule2.h b/charm/core/math/pairing/miracl/pairingmodule2.h index f6ccd2fe..9979a394 100644 --- a/charm/core/math/pairing/miracl/pairingmodule2.h +++ b/charm/core/math/pairing/miracl/pairingmodule2.h @@ -39,6 +39,11 @@ #define PY_SSIZE_T_CLEAN #endif +/* Define MS_WIN64 to get correct PYLONG_BITS_IN_DIGIT on Windows. */ +#if PY_MINOR_VERSION <= 10 && defined(_WIN64) && !defined(MS_WIN64) +#define MS_WIN64 +#endif + #include #include #include diff --git a/charm/core/math/pairing/pairingmodule.h b/charm/core/math/pairing/pairingmodule.h index 5e0413fd..89f3a3ef 100644 --- a/charm/core/math/pairing/pairingmodule.h +++ b/charm/core/math/pairing/pairingmodule.h @@ -34,6 +34,11 @@ #define PY_SSIZE_T_CLEAN #endif +/* Define MS_WIN64 to get correct PYLONG_BITS_IN_DIGIT on Windows. */ +#if PY_MINOR_VERSION <= 10 && defined(_WIN32) && !defined(MS_WIN64) + #define MS_WIN64 +#endif + #include #include diff --git a/charm/core/math/pairing/relic/pairingmodule3.h b/charm/core/math/pairing/relic/pairingmodule3.h index 3a70507c..a659a770 100644 --- a/charm/core/math/pairing/relic/pairingmodule3.h +++ b/charm/core/math/pairing/relic/pairingmodule3.h @@ -34,6 +34,11 @@ #define PY_SSIZE_T_CLEAN #endif +/* Define MS_WIN64 to get correct PYLONG_BITS_IN_DIGIT on Windows. */ +#if PY_MINOR_VERSION <= 10 && defined(_WIN64) && !defined(MS_WIN64) +#define MS_WIN64 +#endif + #include #include #include