From a6965ff6516712972c1214861e0c51a7efff8891 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Fri, 18 Sep 2026 20:43:44 +0200 Subject: [PATCH] Let compilers vectorize v_complement() The two's complement of a digit vector is ~a + 1, and the +1 only carries through the trailing zero digits: they stay zero, the first nonzero digit is negated, and the digits above it are just inverted. Written that way no carry is passed between loop iterations, so the loop can be vectorized. v_complement() runs for every bitwise operation with a negative multi-digit operand, up to three times per operation. Co-Authored-By: Claude Fable 5.1 --- Objects/longobject.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 6454565aebf6a11..33a8b277214a211 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5552,14 +5552,20 @@ _PyLong_Lshift(PyObject *a, int64_t shiftby) static void v_complement(digit *z, digit *a, Py_ssize_t m) { - Py_ssize_t i; - digit carry = 1; - for (i = 0; i < m; ++i) { - carry += a[i] ^ PyLong_MASK; - z[i] = carry & PyLong_MASK; - carry >>= PyLong_SHIFT; + Py_ssize_t i = 0; + + /* -a is ~a + 1, and the +1 only carries through the trailing zero + digits: they stay zero, the first nonzero digit is negated, and the + digits above it are just inverted. No carry is passed between + iterations of the last loop, so it can be vectorized. */ + while (i < m && a[i] == 0) { + z[i++] = 0; + } + assert(i < m); + z[i] = (0U - a[i]) & PyLong_MASK; + for (++i; i < m; ++i) { + z[i] = a[i] ^ PyLong_MASK; } - assert(carry == 0); } /* Bitwise and/xor/or operations */