Skip to content

Commit f7c380e

Browse files
GH-132732: Use pure op machinery to optimize COMPARE_OP_INT/FLOAT/STR (#137062)
Co-authored-by: Ken Jin <kenjin4096@gmail.com>
1 parent 1e69cd1 commit f7c380e

File tree

6 files changed

+237
-39
lines changed

6 files changed

+237
-39
lines changed

Lib/test/test_capi/test_opt.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,8 @@ def testfunc(n):
862862
uops = get_opnames(ex)
863863
self.assertNotIn("_GUARD_TOS_INT", uops)
864864
self.assertNotIn("_GUARD_NOS_INT", uops)
865-
self.assertIn("_BINARY_OP_ADD_INT", uops)
865+
self.assertNotIn("_BINARY_OP_ADD_INT", uops)
866+
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
866867
# Try again, but between the runs, set the global to a float.
867868
# This should result in no executor the second time.
868869
ns = {}
@@ -1462,7 +1463,8 @@ def testfunc(n):
14621463
self.assertEqual(res, 3)
14631464
self.assertIsNotNone(ex)
14641465
uops = get_opnames(ex)
1465-
self.assertIn("_BINARY_OP_ADD_INT", uops)
1466+
self.assertNotIn("_BINARY_OP_ADD_INT", uops)
1467+
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
14661468
self.assertNotIn("_GUARD_NOS_INT", uops)
14671469
self.assertNotIn("_GUARD_TOS_INT", uops)
14681470

@@ -1612,7 +1614,7 @@ def f(n):
16121614
# But all of the appends we care about are still there:
16131615
self.assertEqual(uops.count("_CALL_LIST_APPEND"), len("ABCDEFG"))
16141616

1615-
def test_compare_pop_two_load_const_inline_borrow(self):
1617+
def test_compare_op_int_pop_two_load_const_inline_borrow(self):
16161618
def testfunc(n):
16171619
x = 0
16181620
for _ in range(n):
@@ -1629,6 +1631,40 @@ def testfunc(n):
16291631
self.assertNotIn("_COMPARE_OP_INT", uops)
16301632
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
16311633

1634+
def test_compare_op_str_pop_two_load_const_inline_borrow(self):
1635+
def testfunc(n):
1636+
x = 0
1637+
for _ in range(n):
1638+
a = "foo"
1639+
b = "foo"
1640+
if a == b:
1641+
x += 1
1642+
return x
1643+
1644+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
1645+
self.assertEqual(res, TIER2_THRESHOLD)
1646+
self.assertIsNotNone(ex)
1647+
uops = get_opnames(ex)
1648+
self.assertNotIn("_COMPARE_OP_STR", uops)
1649+
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
1650+
1651+
def test_compare_op_float_pop_two_load_const_inline_borrow(self):
1652+
def testfunc(n):
1653+
x = 0
1654+
for _ in range(n):
1655+
a = 1.0
1656+
b = 1.0
1657+
if a == b:
1658+
x += 1
1659+
return x
1660+
1661+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
1662+
self.assertEqual(res, TIER2_THRESHOLD)
1663+
self.assertIsNotNone(ex)
1664+
uops = get_opnames(ex)
1665+
self.assertNotIn("_COMPARE_OP_FLOAT", uops)
1666+
self.assertNotIn("_POP_TWO_LOAD_CONST_INLINE_BORROW", uops)
1667+
16321668
def test_to_bool_bool_contains_op_set(self):
16331669
"""
16341670
Test that _TO_BOOL_BOOL is removed from code like:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Optimize constant comparison for ``_COMPARE_OP_INT``, ``_COMPARE_OP_FLOAT`` and ``_COMPARE_OP_STR`` in JIT builds

Python/optimizer_analysis.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "pycore_range.h"
2929
#include "pycore_unicodeobject.h"
3030
#include "pycore_ceval.h"
31+
#include "pycore_floatobject.h"
3132

3233
#include <stdarg.h>
3334
#include <stdbool.h>

Python/optimizer_bytecodes.c

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -430,31 +430,17 @@ dummy_func(void) {
430430
}
431431

432432
op(_COMPARE_OP_INT, (left, right -- res)) {
433-
if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) {
434-
assert(PyLong_CheckExact(sym_get_const(ctx, left)));
435-
assert(PyLong_CheckExact(sym_get_const(ctx, right)));
436-
PyObject *tmp = PyObject_RichCompare(sym_get_const(ctx, left),
437-
sym_get_const(ctx, right),
438-
oparg >> 5);
439-
if (tmp == NULL) {
440-
goto error;
441-
}
442-
assert(PyBool_Check(tmp));
443-
assert(_Py_IsImmortal(tmp));
444-
REPLACE_OP(this_instr, _POP_TWO_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)tmp);
445-
res = sym_new_const(ctx, tmp);
446-
Py_DECREF(tmp);
447-
}
448-
else {
449-
res = sym_new_type(ctx, &PyBool_Type);
450-
}
433+
REPLACE_OPCODE_IF_EVALUATES_PURE(left, right);
434+
res = sym_new_type(ctx, &PyBool_Type);
451435
}
452436

453437
op(_COMPARE_OP_FLOAT, (left, right -- res)) {
438+
REPLACE_OPCODE_IF_EVALUATES_PURE(left, right);
454439
res = sym_new_type(ctx, &PyBool_Type);
455440
}
456441

457442
op(_COMPARE_OP_STR, (left, right -- res)) {
443+
REPLACE_OPCODE_IF_EVALUATES_PURE(left, right);
458444
res = sym_new_type(ctx, &PyBool_Type);
459445
}
460446

Python/optimizer_cases.c.h

Lines changed: 179 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)