-
-
Notifications
You must be signed in to change notification settings - Fork 31.7k
bpo-29882: Fix portability bug introduced in GH-30774 #30794
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Can you please add a test for ref: #30774 (comment) |
Sure, will do. Though note that there's really nothing at all special about that value: any input value larger than |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding (uint32_t)
cast and the added test LGTM. I didn't review the long comment ;-)
@@ -115,17 +115,27 @@ _Py_popcount32(uint32_t x) | |||
const uint32_t M2 = 0x33333333; | |||
// Binary: 0000 1111 0000 1111 ... | |||
const uint32_t M4 = 0x0F0F0F0F; | |||
// 256**4 + 256**3 + 256**2 + 256**1 | |||
const uint32_t SUM = 0x01010101; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why removing this constant? I added it to not have to use the macro to get an uint32_t literal number. UINT32_C() if I recall correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vstinner This is already explained in the long comment that you didn't review. :-)
The problem here isn't the constant; it's the type declaration.
For the multiplication in the last line of the function to be portable, we need at least one of the unsigned operands in that multiplication to not be promoted to int
. An inline constant 0x01010101U
satisfies that criterion: by C99 §6.4.4.1, together with C's guarantees about the minimum precision of long
, it has type either unsigned int
or unsigned long
. A uint32_t
constant with the same value does not satisfy that criterion, for reasons already explained.
And there isn't a type declaration that works here. I just explained why uint32_t
won't work. If we declare SUM
as unsigned int
instead of uint32_t
, we have portability issues on machines where int
isn't large enough to represent the value 0x01010101
. If we declare it as unsigned long
, we're back to doing a 64-bit-by-64-bit multiply on almost all current Linux and macOS boxes.
If you really want to keep the constant, another option is to leave this line exactly as-is and change the multiplication in the last line to x * (SUM + 0U)
; that + 0U
effectively forces the second multiplicand to have type with rank greater than or equal to that of int
, making it immune to further integer promotion.
But as Tim observed in the #30774 discussion, none of this prevents us from potentially doing a 512-bit-by-512-bit multiply on a box that has 512-bit integers. But short of relying on compiler-specific intrinsics, that's inescapable anyway: standard C simply isn't capable of doing arithmetic on anything smaller than an int
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks fine to me!
@mdickinson: Please replace |
This PR fixes a portability bug in
_Py_popcount32
that was introduced in GH-30774, and adds a comment explaining why the final line of the function is delicate.Prior discussions:
https://bugs.python.org/issue29882