|
5 | 5 | #include <Python.h> |
6 | 6 | #include "CPy.h" |
7 | 7 |
|
| 8 | +#ifdef _MSC_VER |
| 9 | +#include <intrin.h> |
| 10 | +#endif |
| 11 | + |
8 | 12 | #ifndef _WIN32 |
9 | 13 | // On 64-bit Linux and macOS, ssize_t and long are both 64 bits, and |
10 | 14 | // PyLong_FromLong is faster than PyLong_FromSsize_t, so use the faster one |
|
15 | 19 | #define CPyLong_FromSsize_t PyLong_FromSsize_t |
16 | 20 | #endif |
17 | 21 |
|
| 22 | +#if defined(__GNUC__) || defined(__clang__) |
| 23 | +# if defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) || (defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8) |
| 24 | +# define CPY_CLZ(x) __builtin_clzll((unsigned long long)(x)) |
| 25 | +# define CPY_BITS 64 |
| 26 | +# else |
| 27 | +# define CPY_CLZ(x) __builtin_clz((unsigned int)(x)) |
| 28 | +# define CPY_BITS 32 |
| 29 | +# endif |
| 30 | +#endif |
| 31 | + |
| 32 | + |
18 | 33 | CPyTagged CPyTagged_FromSsize_t(Py_ssize_t value) { |
19 | 34 | // We use a Python object if the value shifted left by 1 is too |
20 | 35 | // large for Py_ssize_t |
@@ -581,3 +596,52 @@ double CPyTagged_TrueDivide(CPyTagged x, CPyTagged y) { |
581 | 596 | } |
582 | 597 | return 1.0; |
583 | 598 | } |
| 599 | + |
| 600 | +// int.bit_length() |
| 601 | +CPyTagged CPyTagged_BitLength(CPyTagged self) { |
| 602 | + // Handle zero |
| 603 | + if (self == 0) { |
| 604 | + return 0; |
| 605 | + } |
| 606 | + |
| 607 | + // Fast path for small (tagged) ints |
| 608 | + if (CPyTagged_CheckShort(self)) { |
| 609 | + Py_ssize_t val = CPyTagged_ShortAsSsize_t(self); |
| 610 | + Py_ssize_t absval = val < 0 ? -val : val; |
| 611 | + int bits = 0; |
| 612 | + if (absval) { |
| 613 | +#if defined(_MSC_VER) |
| 614 | + #if defined(_WIN64) |
| 615 | + unsigned long idx; |
| 616 | + if (_BitScanReverse64(&idx, (unsigned __int64)absval)) { |
| 617 | + bits = (int)(idx + 1); |
| 618 | + } |
| 619 | + #else |
| 620 | + unsigned long idx; |
| 621 | + if (_BitScanReverse(&idx, (unsigned long)absval)) { |
| 622 | + bits = (int)(idx + 1); |
| 623 | + } |
| 624 | + #endif |
| 625 | +#elif defined(__GNUC__) || defined(__clang__) |
| 626 | + bits = (int)(CPY_BITS - CPY_CLZ(absval)); |
| 627 | +#else |
| 628 | + // Fallback to loop if no builtin |
| 629 | + while (absval) { |
| 630 | + absval >>= 1; |
| 631 | + bits++; |
| 632 | + } |
| 633 | +#endif |
| 634 | + } |
| 635 | + return bits << 1; |
| 636 | + } |
| 637 | + |
| 638 | + // Slow path for big ints |
| 639 | + PyObject *pyint = CPyTagged_AsObject(self); |
| 640 | + int bits = _PyLong_NumBits(pyint); |
| 641 | + Py_DECREF(pyint); |
| 642 | + if (bits < 0) { |
| 643 | + // _PyLong_NumBits sets an error on failure |
| 644 | + return CPY_INT_TAG; |
| 645 | + } |
| 646 | + return bits << 1; |
| 647 | +} |
0 commit comments