Skip to content

Commit 08d911a

Browse files
committed
add small_power
1 parent 7b4364d commit 08d911a

2 files changed

Lines changed: 77 additions & 0 deletions

File tree

Lib/test/test_opcache.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1536,6 +1536,44 @@ def binary_op_bitwise_extend():
15361536
self.assert_specialized(binary_op_bitwise_extend, "BINARY_OP_EXTEND")
15371537
self.assert_no_opcode(binary_op_bitwise_extend, "BINARY_OP")
15381538

1539+
@cpython_only
1540+
@requires_specialization
1541+
def test_binary_op_small_power(self):
1542+
def small_power():
1543+
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
1544+
a = 7
1545+
self.assertEqual(a ** 2, 49)
1546+
b = -9
1547+
self.assertEqual(b ** 3, -729)
1548+
c = 2 ** 20 - 1 # largest base accepted by the exponent-3 bound
1549+
self.assertEqual(c ** 3, (2 ** 20 - 1) ** 3)
1550+
z = 0 # result is the immortal cached 0
1551+
self.assertEqual(z ** 2, 0)
1552+
1553+
small_power()
1554+
self.assert_specialized(small_power, "BINARY_OP_EXTEND")
1555+
self.assert_no_opcode(small_power, "BINARY_OP")
1556+
1557+
def small_power_inplace():
1558+
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
1559+
a = 7
1560+
a **= 2
1561+
self.assertEqual(a, 49)
1562+
1563+
small_power_inplace()
1564+
self.assert_specialized(small_power_inplace, "BINARY_OP_EXTEND")
1565+
1566+
def power_not_extended(x):
1567+
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
1568+
self.assertEqual(x ** 4, 2401)
1569+
self.assertEqual((x + 2 ** 20) ** 3, (7 + 2 ** 20) ** 3)
1570+
self.assertEqual((x + (1 << 40)) ** 2, (7 + (1 << 40)) ** 2)
1571+
self.assertEqual(bool(x) ** 2, True)
1572+
self.assertEqual(float(x) ** 3, 343.0)
1573+
1574+
power_not_extended(7)
1575+
self.assert_no_opcode(power_not_extended, "BINARY_OP_EXTEND")
1576+
15391577
@cpython_only
15401578
@requires_specialization
15411579
def test_load_super_attr(self):

Python/specialize.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2179,6 +2179,40 @@ BITWISE_LONGS_ACTION(compactlongs_and, &)
21792179
BITWISE_LONGS_ACTION(compactlongs_xor, ^)
21802180
#undef BITWISE_LONGS_ACTION
21812181

2182+
static int
2183+
small_power_guard(PyObject *lhs, PyObject *rhs)
2184+
{
2185+
if (!is_compactlong(lhs) || !is_compactlong(rhs)) {
2186+
return false;
2187+
}
2188+
Py_ssize_t exponent = _PyLong_CompactValue((PyLongObject *)rhs);
2189+
if (exponent == 2) {
2190+
/* |x| < 2^30 (compact), so x*x < 2^60: fits a 64-bit
2191+
intermediate even where Py_ssize_t is 32 bits. */
2192+
return true;
2193+
}
2194+
if (exponent == 3) {
2195+
/* Need |x*x*x| < 2^60, so |x| < 2^20. */
2196+
Py_ssize_t x = _PyLong_CompactValue((PyLongObject *)lhs);
2197+
return x < (1 << 20) && x > -(1 << 20);
2198+
}
2199+
return false;
2200+
}
2201+
2202+
static PyObject *
2203+
small_power(PyObject *lhs, PyObject *rhs)
2204+
{
2205+
/* Compute in int64_t (not Py_ssize_t): the guard bounds keep the
2206+
product below 2^60, which overflows a 32-bit Py_ssize_t. */
2207+
int64_t x = (int64_t)_PyLong_CompactValue((PyLongObject *)lhs);
2208+
int64_t exponent = (int64_t)_PyLong_CompactValue((PyLongObject *)rhs);
2209+
assert(exponent == 2 || exponent == 3);
2210+
if (exponent == 2) {
2211+
return PyLong_FromLongLong(x * x);
2212+
}
2213+
return PyLong_FromLongLong(x * x * x);
2214+
}
2215+
21822216
/* float-long */
21832217

21842218
static inline int
@@ -2259,6 +2293,11 @@ static _PyBinaryOpSpecializationDescr binaryop_extend_descrs[] = {
22592293
{NB_INPLACE_AND, compactlongs_guard, compactlongs_and, &PyLong_Type, 1, NULL, NULL},
22602294
{NB_INPLACE_XOR, compactlongs_guard, compactlongs_xor, &PyLong_Type, 1, NULL, NULL},
22612295

2296+
/* x ** 2 / x ** 3 for compact ints; PyLong_FromLongLong may return
2297+
the cached immortal small ints, which result_unique accepts. */
2298+
{NB_POWER, small_power_guard, small_power, &PyLong_Type, 1, NULL, NULL},
2299+
{NB_INPLACE_POWER, small_power_guard, small_power, &PyLong_Type, 1, NULL, NULL},
2300+
22622301
/* float-long arithmetic: guards also check NaN and compactness. */
22632302
{NB_ADD, float_compactlong_guard, float_compactlong_add, &PyFloat_Type, 1, NULL, NULL},
22642303
{NB_SUBTRACT, float_compactlong_guard, float_compactlong_subtract, &PyFloat_Type, 1, NULL, NULL},

0 commit comments

Comments
 (0)