https://github.com/python/cpython/commit/46151648ca8ba1edd2a2783e8e154692a13d8ea8
commit: 46151648ca8ba1edd2a2783e8e154692a13d8ea8
branch: main
author: Tomas R. <tomas.ro...@gmail.com>
committer: brandtbucher <brandtbuc...@gmail.com>
date: 2025-06-06T16:44:43-07:00
summary:

GH-131798: Optimize away type(x) in the JIT when the result is known (GH-135194)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2025-06-05-21-58-30.gh-issue-131798.nt5Ab7.rst
M Lib/test/test_capi/test_opt.py
M Python/optimizer_bytecodes.c
M Python/optimizer_cases.c.h

diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index a292ebcc7f4aed..ee8d261685d463 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -1778,11 +1778,12 @@ def testfunc(n):
         self.assertNotIn("_GUARD_TOS_UNICODE", uops)
         self.assertIn("_BINARY_OP_ADD_UNICODE", uops)
 
-    def test_call_type_1(self):
+    def test_call_type_1_guards_removed(self):
         def testfunc(n):
             x = 0
             for _ in range(n):
-                x += type(42) is int
+                foo = eval('42')
+                x += type(foo) is int
             return x
 
         res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
@@ -1793,6 +1794,25 @@ def testfunc(n):
         self.assertNotIn("_GUARD_NOS_NULL", uops)
         self.assertNotIn("_GUARD_CALLABLE_TYPE_1", uops)
 
+    def test_call_type_1_known_type(self):
+        def testfunc(n):
+            x = 0
+            for _ in range(n):
+                x += type(42) is int
+            return x
+
+        res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+        self.assertEqual(res, TIER2_THRESHOLD)
+        self.assertIsNotNone(ex)
+        uops = get_opnames(ex)
+        # When the result of type(...) is known, _CALL_TYPE_1 is replaced with
+        # _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW which is optimized away in
+        # remove_unneeded_uops.
+        self.assertNotIn("_CALL_TYPE_1", uops)
+        self.assertNotIn("_POP_CALL_ONE_LOAD_CONST_INLINE_BORROW", uops)
+        self.assertNotIn("_POP_CALL_LOAD_CONST_INLINE_BORROW", uops)
+        self.assertNotIn("_POP_TOP_LOAD_CONST_INLINE_BORROW", uops)
+
     def test_call_type_1_result_is_const(self):
         def testfunc(n):
             x = 0
@@ -1806,7 +1826,6 @@ def testfunc(n):
         self.assertEqual(res, TIER2_THRESHOLD)
         self.assertIsNotNone(ex)
         uops = get_opnames(ex)
-        self.assertIn("_CALL_TYPE_1", uops)
         self.assertNotIn("_GUARD_IS_NOT_NONE_POP", uops)
 
     def test_call_str_1(self):
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-05-21-58-30.gh-issue-131798.nt5Ab7.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-05-21-58-30.gh-issue-131798.nt5Ab7.rst
new file mode 100644
index 00000000000000..e4b5f610353f94
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-05-21-58-30.gh-issue-131798.nt5Ab7.rst
@@ -0,0 +1,2 @@
+Optimize away ``_CALL_TYPE_1`` in the JIT when the return type is known.
+Patch by Tomas Roun
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index b4220e2c627ecb..12efaacd8f0dfc 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -937,8 +937,11 @@ dummy_func(void) {
     }
 
     op(_CALL_TYPE_1, (unused, unused, arg -- res)) {
-        if (sym_has_type(arg)) {
-            res = sym_new_const(ctx, (PyObject *)sym_get_type(arg));
+        PyObject* type = (PyObject *)sym_get_type(arg);
+        if (type) {
+            res = sym_new_const(ctx, type);
+            REPLACE_OP(this_instr, _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW, 0,
+                       (uintptr_t)type);
         }
         else {
             res = sym_new_not_null(ctx);
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 960c683800455e..1a2d49973ee916 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -2056,8 +2056,11 @@
             JitOptSymbol *arg;
             JitOptSymbol *res;
             arg = stack_pointer[-1];
-            if (sym_has_type(arg)) {
-                res = sym_new_const(ctx, (PyObject *)sym_get_type(arg));
+            PyObject* type = (PyObject *)sym_get_type(arg);
+            if (type) {
+                res = sym_new_const(ctx, type);
+                REPLACE_OP(this_instr, _POP_CALL_ONE_LOAD_CONST_INLINE_BORROW, 
0,
+                       (uintptr_t)type);
             }
             else {
                 res = sym_new_not_null(ctx);

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: arch...@mail-archive.com

Reply via email to