https://github.com/python/cpython/commit/7afa476874b9a432ad6dbe9fb3e65d62f2999f88
commit: 7afa476874b9a432ad6dbe9fb3e65d62f2999f88
branch: main
author: Brandt Bucher <brandtbuc...@microsoft.com>
committer: brandtbucher <brandtbuc...@gmail.com>
date: 2025-03-02T13:21:34-08:00
summary:

GH-130415: Use boolean guards to narrow types to values in the JIT (GH-130659)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2025-02-27-17-05-05.gh-issue-130415.iijvfW.rst
M Include/internal/pycore_optimizer.h
M Lib/test/test_capi/test_opt.py
M Python/optimizer_analysis.c
M Python/optimizer_bytecodes.c
M Python/optimizer_cases.c.h
M Python/optimizer_symbols.c

diff --git a/Include/internal/pycore_optimizer.h 
b/Include/internal/pycore_optimizer.h
index 25c3d3e5a22442..79861167f65e63 100644
--- a/Include/internal/pycore_optimizer.h
+++ b/Include/internal/pycore_optimizer.h
@@ -172,6 +172,7 @@ typedef enum _JitSymType {
     JIT_SYM_KNOWN_CLASS_TAG = 6,
     JIT_SYM_KNOWN_VALUE_TAG = 7,
     JIT_SYM_TUPLE_TAG = 8,
+    JIT_SYM_TRUTHINESS_TAG = 9,
 } JitSymType;
 
 typedef struct _jit_opt_known_class {
@@ -198,12 +199,19 @@ typedef struct _jit_opt_tuple {
     uint16_t items[MAX_SYMBOLIC_TUPLE_SIZE];
 } JitOptTuple;
 
+typedef struct {
+    uint8_t tag;
+    bool not;
+    uint16_t value;
+} JitOptTruthiness;
+
 typedef union _jit_opt_symbol {
     uint8_t tag;
     JitOptKnownClass cls;
     JitOptKnownValue value;
     JitOptKnownVersion version;
     JitOptTuple tuple;
+    JitOptTruthiness truthiness;
 } JitOptSymbol;
 
 
@@ -245,8 +253,8 @@ typedef struct _JitOptContext {
 
 extern bool _Py_uop_sym_is_null(JitOptSymbol *sym);
 extern bool _Py_uop_sym_is_not_null(JitOptSymbol *sym);
-extern bool _Py_uop_sym_is_const(JitOptSymbol *sym);
-extern PyObject *_Py_uop_sym_get_const(JitOptSymbol *sym);
+extern bool _Py_uop_sym_is_const(JitOptContext *ctx, JitOptSymbol *sym);
+extern PyObject *_Py_uop_sym_get_const(JitOptContext *ctx, JitOptSymbol *sym);
 extern JitOptSymbol *_Py_uop_sym_new_unknown(JitOptContext *ctx);
 extern JitOptSymbol *_Py_uop_sym_new_not_null(JitOptContext *ctx);
 extern JitOptSymbol *_Py_uop_sym_new_type(
@@ -262,12 +270,13 @@ extern void _Py_uop_sym_set_type(JitOptContext *ctx, 
JitOptSymbol *sym, PyTypeOb
 extern bool _Py_uop_sym_set_type_version(JitOptContext *ctx, JitOptSymbol 
*sym, unsigned int version);
 extern void _Py_uop_sym_set_const(JitOptContext *ctx, JitOptSymbol *sym, 
PyObject *const_val);
 extern bool _Py_uop_sym_is_bottom(JitOptSymbol *sym);
-extern int _Py_uop_sym_truthiness(JitOptSymbol *sym);
+extern int _Py_uop_sym_truthiness(JitOptContext *ctx, JitOptSymbol *sym);
 extern PyTypeObject *_Py_uop_sym_get_type(JitOptSymbol *sym);
 extern bool _Py_uop_sym_is_immortal(JitOptSymbol *sym);
 extern JitOptSymbol *_Py_uop_sym_new_tuple(JitOptContext *ctx, int size, 
JitOptSymbol **args);
 extern JitOptSymbol *_Py_uop_sym_tuple_getitem(JitOptContext *ctx, 
JitOptSymbol *sym, int item);
 extern int _Py_uop_sym_tuple_length(JitOptSymbol *sym);
+extern JitOptSymbol *_Py_uop_sym_new_truthiness(JitOptContext *ctx, 
JitOptSymbol *value, bool truthy);
 
 extern void _Py_uop_abstractcontext_init(JitOptContext *ctx);
 extern void _Py_uop_abstractcontext_fini(JitOptContext *ctx);
diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index 2a9b777862c84a..b7083dbfb89db8 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -1437,6 +1437,68 @@ def crash_addition():
 
         crash_addition()
 
+    def test_narrow_type_to_constant_bool_false(self):
+        def f(n):
+            trace = []
+            for i in range(n):
+                # false is always False, but we can only prove that it's a 
bool:
+                false = i == TIER2_THRESHOLD
+                trace.append("A")
+                if not false:  # Kept.
+                    trace.append("B")
+                    if not false:  # Removed!
+                        trace.append("C")
+                    trace.append("D")
+                    if false:  # Removed!
+                        trace.append("X")
+                    trace.append("E")
+                trace.append("F")
+                if false:  # Removed!
+                    trace.append("X")
+                trace.append("G")
+            return trace
+
+        trace, ex = self._run_with_optimizer(f, TIER2_THRESHOLD)
+        self.assertEqual(trace, list("ABCDEFG") * TIER2_THRESHOLD)
+        self.assertIsNotNone(ex)
+        uops = get_opnames(ex)
+        # Only one guard remains:
+        self.assertEqual(uops.count("_GUARD_IS_FALSE_POP"), 1)
+        self.assertEqual(uops.count("_GUARD_IS_TRUE_POP"), 0)
+        # But all of the appends we care about are still there:
+        self.assertEqual(uops.count("_CALL_LIST_APPEND"), len("ABCDEFG"))
+
+    def test_narrow_type_to_constant_bool_true(self):
+        def f(n):
+            trace = []
+            for i in range(n):
+                # true always True, but we can only prove that it's a bool:
+                true = i != TIER2_THRESHOLD
+                trace.append("A")
+                if true:  # Kept.
+                    trace.append("B")
+                    if not true:  # Removed!
+                        trace.append("X")
+                    trace.append("C")
+                    if true:  # Removed!
+                        trace.append("D")
+                    trace.append("E")
+                trace.append("F")
+                if not true:  # Removed!
+                    trace.append("X")
+                trace.append("G")
+            return trace
+
+        trace, ex = self._run_with_optimizer(f, TIER2_THRESHOLD)
+        self.assertEqual(trace, list("ABCDEFG") * TIER2_THRESHOLD)
+        self.assertIsNotNone(ex)
+        uops = get_opnames(ex)
+        # Only one guard remains:
+        self.assertEqual(uops.count("_GUARD_IS_FALSE_POP"), 0)
+        self.assertEqual(uops.count("_GUARD_IS_TRUE_POP"), 1)
+        # But all of the appends we care about are still there:
+        self.assertEqual(uops.count("_CALL_LIST_APPEND"), len("ABCDEFG"))
+
 
 def global_identity(x):
     return x
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-27-17-05-05.gh-issue-130415.iijvfW.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-27-17-05-05.gh-issue-130415.iijvfW.rst
new file mode 100644
index 00000000000000..f5b6d0e9c1a06a
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-27-17-05-05.gh-issue-130415.iijvfW.rst
@@ -0,0 +1,2 @@
+Improve the experimental JIT's ability to narrow boolean values based on the
+results of truthiness tests.
diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c
index 29a05088e62c45..67bf8d11b3f9ac 100644
--- a/Python/optimizer_analysis.c
+++ b/Python/optimizer_analysis.c
@@ -136,26 +136,6 @@ incorrect_keys(_PyUOpInstruction *inst, PyObject *obj)
     return 0;
 }
 
-static int
-check_next_uop(_PyUOpInstruction *buffer, int size, int pc, uint16_t expected)
-{
-    if (pc + 1 >= size) {
-        DPRINTF(1, "Cannot rewrite %s at pc %d: buffer too small\n",
-                _PyOpcode_uop_name[buffer[pc].opcode], pc);
-        return 0;
-    }
-    uint16_t next_opcode = buffer[pc + 1].opcode;
-    if (next_opcode != expected) {
-        DPRINTF(1,
-                "Cannot rewrite %s at pc %d: unexpected next opcode %s, "
-                "expected %s\n",
-                _PyOpcode_uop_name[buffer[pc].opcode], pc,
-                _PyOpcode_uop_name[next_opcode], _PyOpcode_uop_name[expected]);
-        return 0;
-    }
-    return 1;
-}
-
 /* Returns 1 if successfully optimized
  *         0 if the trace is not suitable for optimization (yet)
  *        -1 if there was an error. */
@@ -363,6 +343,7 @@ remove_globals(_PyInterpreterFrame *frame, 
_PyUOpInstruction *buffer,
 #define sym_tuple_getitem _Py_uop_sym_tuple_getitem
 #define sym_tuple_length _Py_uop_sym_tuple_length
 #define sym_is_immortal _Py_uop_sym_is_immortal
+#define sym_new_truthiness _Py_uop_sym_new_truthiness
 
 static int
 optimize_to_bool(
@@ -376,7 +357,7 @@ optimize_to_bool(
         *result_ptr = value;
         return 1;
     }
-    int truthiness = sym_truthiness(value);
+    int truthiness = sym_truthiness(ctx, value);
     if (truthiness >= 0) {
         PyObject *load = truthiness ? Py_True : Py_False;
         REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, 
(uintptr_t)load);
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index f3625a1492c47c..c4e4b28e50d211 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -34,6 +34,7 @@ typedef struct _Py_UOpsAbstractFrame _Py_UOpsAbstractFrame;
 #define sym_tuple_getitem _Py_uop_sym_tuple_getitem
 #define sym_tuple_length _Py_uop_sym_tuple_length
 #define sym_is_immortal _Py_uop_sym_is_immortal
+#define sym_new_truthiness _Py_uop_sym_new_truthiness
 
 extern int
 optimize_to_bool(
@@ -198,11 +199,11 @@ dummy_func(void) {
                 // Case C:
                 res = sym_new_type(ctx, &PyFloat_Type);
             }
-            else if (!sym_is_const(right)) {
+            else if (!sym_is_const(ctx, right)) {
                 // Case A or B... can't know without the sign of the RHS:
                 res = sym_new_unknown(ctx);
             }
-            else if (_PyLong_IsNegative((PyLongObject *)sym_get_const(right))) 
{
+            else if (_PyLong_IsNegative((PyLongObject *)sym_get_const(ctx, 
right))) {
                 // Case B:
                 res = sym_new_type(ctx, &PyFloat_Type);
             }
@@ -223,13 +224,13 @@ dummy_func(void) {
     }
 
     op(_BINARY_OP_ADD_INT, (left, right -- res)) {
-        if (sym_is_const(left) && sym_is_const(right) &&
+        if (sym_is_const(ctx, left) && sym_is_const(ctx, right) &&
             sym_matches_type(left, &PyLong_Type) && sym_matches_type(right, 
&PyLong_Type))
         {
-            assert(PyLong_CheckExact(sym_get_const(left)));
-            assert(PyLong_CheckExact(sym_get_const(right)));
-            PyObject *temp = _PyLong_Add((PyLongObject *)sym_get_const(left),
-                                         (PyLongObject *)sym_get_const(right));
+            assert(PyLong_CheckExact(sym_get_const(ctx, left)));
+            assert(PyLong_CheckExact(sym_get_const(ctx, right)));
+            PyObject *temp = _PyLong_Add((PyLongObject *)sym_get_const(ctx, 
left),
+                                         (PyLongObject *)sym_get_const(ctx, 
right));
             if (temp == NULL) {
                 goto error;
             }
@@ -244,13 +245,13 @@ dummy_func(void) {
     }
 
     op(_BINARY_OP_SUBTRACT_INT, (left, right -- res)) {
-        if (sym_is_const(left) && sym_is_const(right) &&
+        if (sym_is_const(ctx, left) && sym_is_const(ctx, right) &&
             sym_matches_type(left, &PyLong_Type) && sym_matches_type(right, 
&PyLong_Type))
         {
-            assert(PyLong_CheckExact(sym_get_const(left)));
-            assert(PyLong_CheckExact(sym_get_const(right)));
-            PyObject *temp = _PyLong_Subtract((PyLongObject 
*)sym_get_const(left),
-                                              (PyLongObject 
*)sym_get_const(right));
+            assert(PyLong_CheckExact(sym_get_const(ctx, left)));
+            assert(PyLong_CheckExact(sym_get_const(ctx, right)));
+            PyObject *temp = _PyLong_Subtract((PyLongObject 
*)sym_get_const(ctx, left),
+                                              (PyLongObject 
*)sym_get_const(ctx, right));
             if (temp == NULL) {
                 goto error;
             }
@@ -265,13 +266,13 @@ dummy_func(void) {
     }
 
     op(_BINARY_OP_MULTIPLY_INT, (left, right -- res)) {
-        if (sym_is_const(left) && sym_is_const(right) &&
+        if (sym_is_const(ctx, left) && sym_is_const(ctx, right) &&
             sym_matches_type(left, &PyLong_Type) && sym_matches_type(right, 
&PyLong_Type))
         {
-            assert(PyLong_CheckExact(sym_get_const(left)));
-            assert(PyLong_CheckExact(sym_get_const(right)));
-            PyObject *temp = _PyLong_Multiply((PyLongObject 
*)sym_get_const(left),
-                                              (PyLongObject 
*)sym_get_const(right));
+            assert(PyLong_CheckExact(sym_get_const(ctx, left)));
+            assert(PyLong_CheckExact(sym_get_const(ctx, right)));
+            PyObject *temp = _PyLong_Multiply((PyLongObject 
*)sym_get_const(ctx, left),
+                                              (PyLongObject 
*)sym_get_const(ctx, right));
             if (temp == NULL) {
                 goto error;
             }
@@ -286,14 +287,14 @@ dummy_func(void) {
     }
 
     op(_BINARY_OP_ADD_FLOAT, (left, right -- res)) {
-        if (sym_is_const(left) && sym_is_const(right) &&
+        if (sym_is_const(ctx, left) && sym_is_const(ctx, right) &&
             sym_matches_type(left, &PyFloat_Type) && sym_matches_type(right, 
&PyFloat_Type))
         {
-            assert(PyFloat_CheckExact(sym_get_const(left)));
-            assert(PyFloat_CheckExact(sym_get_const(right)));
+            assert(PyFloat_CheckExact(sym_get_const(ctx, left)));
+            assert(PyFloat_CheckExact(sym_get_const(ctx, right)));
             PyObject *temp = PyFloat_FromDouble(
-                PyFloat_AS_DOUBLE(sym_get_const(left)) +
-                PyFloat_AS_DOUBLE(sym_get_const(right)));
+                PyFloat_AS_DOUBLE(sym_get_const(ctx, left)) +
+                PyFloat_AS_DOUBLE(sym_get_const(ctx, right)));
             if (temp == NULL) {
                 goto error;
             }
@@ -308,14 +309,14 @@ dummy_func(void) {
     }
 
     op(_BINARY_OP_SUBTRACT_FLOAT, (left, right -- res)) {
-        if (sym_is_const(left) && sym_is_const(right) &&
+        if (sym_is_const(ctx, left) && sym_is_const(ctx, right) &&
             sym_matches_type(left, &PyFloat_Type) && sym_matches_type(right, 
&PyFloat_Type))
         {
-            assert(PyFloat_CheckExact(sym_get_const(left)));
-            assert(PyFloat_CheckExact(sym_get_const(right)));
+            assert(PyFloat_CheckExact(sym_get_const(ctx, left)));
+            assert(PyFloat_CheckExact(sym_get_const(ctx, right)));
             PyObject *temp = PyFloat_FromDouble(
-                PyFloat_AS_DOUBLE(sym_get_const(left)) -
-                PyFloat_AS_DOUBLE(sym_get_const(right)));
+                PyFloat_AS_DOUBLE(sym_get_const(ctx, left)) -
+                PyFloat_AS_DOUBLE(sym_get_const(ctx, right)));
             if (temp == NULL) {
                 goto error;
             }
@@ -330,14 +331,14 @@ dummy_func(void) {
     }
 
     op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res)) {
-        if (sym_is_const(left) && sym_is_const(right) &&
+        if (sym_is_const(ctx, left) && sym_is_const(ctx, right) &&
             sym_matches_type(left, &PyFloat_Type) && sym_matches_type(right, 
&PyFloat_Type))
         {
-            assert(PyFloat_CheckExact(sym_get_const(left)));
-            assert(PyFloat_CheckExact(sym_get_const(right)));
+            assert(PyFloat_CheckExact(sym_get_const(ctx, left)));
+            assert(PyFloat_CheckExact(sym_get_const(ctx, right)));
             PyObject *temp = PyFloat_FromDouble(
-                PyFloat_AS_DOUBLE(sym_get_const(left)) *
-                PyFloat_AS_DOUBLE(sym_get_const(right)));
+                PyFloat_AS_DOUBLE(sym_get_const(ctx, left)) *
+                PyFloat_AS_DOUBLE(sym_get_const(ctx, right)));
             if (temp == NULL) {
                 goto error;
             }
@@ -352,9 +353,9 @@ dummy_func(void) {
     }
 
     op(_BINARY_OP_ADD_UNICODE, (left, right -- res)) {
-        if (sym_is_const(left) && sym_is_const(right) &&
+        if (sym_is_const(ctx, left) && sym_is_const(ctx, right) &&
             sym_matches_type(left, &PyUnicode_Type) && sym_matches_type(right, 
&PyUnicode_Type)) {
-            PyObject *temp = PyUnicode_Concat(sym_get_const(left), 
sym_get_const(right));
+            PyObject *temp = PyUnicode_Concat(sym_get_const(ctx, left), 
sym_get_const(ctx, right));
             if (temp == NULL) {
                 goto error;
             }
@@ -368,9 +369,9 @@ dummy_func(void) {
 
     op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right -- )) {
         JitOptSymbol *res;
-        if (sym_is_const(left) && sym_is_const(right) &&
+        if (sym_is_const(ctx, left) && sym_is_const(ctx, right) &&
             sym_matches_type(left, &PyUnicode_Type) && sym_matches_type(right, 
&PyUnicode_Type)) {
-            PyObject *temp = PyUnicode_Concat(sym_get_const(left), 
sym_get_const(right));
+            PyObject *temp = PyUnicode_Concat(sym_get_const(ctx, left), 
sym_get_const(ctx, right));
             if (temp == NULL) {
                 goto error;
             }
@@ -391,14 +392,14 @@ dummy_func(void) {
 
     op(_TO_BOOL, (value -- res)) {
         if (!optimize_to_bool(this_instr, ctx, value, &res)) {
-            res = sym_new_type(ctx, &PyBool_Type);
+            res = sym_new_truthiness(ctx, value, true);
         }
     }
 
     op(_TO_BOOL_BOOL, (value -- res)) {
         if (!optimize_to_bool(this_instr, ctx, value, &res)) {
             sym_set_type(value, &PyBool_Type);
-            res = value;
+            res = sym_new_truthiness(ctx, value, true);
         }
     }
 
@@ -430,6 +431,11 @@ dummy_func(void) {
         }
     }
 
+    op(_UNARY_NOT, (value -- res)) {
+        sym_set_type(value, &PyBool_Type);
+        res = sym_new_truthiness(ctx, value, false);
+    }
+
     op(_COMPARE_OP, (left, right -- res)) {
         if (oparg & 16) {
             res = sym_new_type(ctx, &PyBool_Type);
@@ -521,8 +527,8 @@ dummy_func(void) {
         (void)dict_version;
         (void)index;
         attr = NULL;
-        if (sym_is_const(owner)) {
-            PyModuleObject *mod = (PyModuleObject *)sym_get_const(owner);
+        if (sym_is_const(ctx, owner)) {
+            PyModuleObject *mod = (PyModuleObject *)sym_get_const(ctx, owner);
             if (PyModule_CheckExact(mod)) {
                 PyObject *dict = mod->md_dict;
                 uint64_t watched_mutations = get_mutations(dict);
@@ -599,19 +605,19 @@ dummy_func(void) {
     }
 
     op(_CHECK_FUNCTION_VERSION, (func_version/2, callable, self_or_null, 
unused[oparg] -- callable, self_or_null, unused[oparg])) {
-        if (sym_is_const(callable) && sym_matches_type(callable, 
&PyFunction_Type)) {
-            assert(PyFunction_Check(sym_get_const(callable)));
+        if (sym_is_const(ctx, callable) && sym_matches_type(callable, 
&PyFunction_Type)) {
+            assert(PyFunction_Check(sym_get_const(ctx, callable)));
             REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, 
func_version);
-            this_instr->operand1 = (uintptr_t)sym_get_const(callable);
+            this_instr->operand1 = (uintptr_t)sym_get_const(ctx, callable);
         }
         sym_set_type(callable, &PyFunction_Type);
     }
 
     op(_CHECK_FUNCTION_EXACT_ARGS, (callable, self_or_null, unused[oparg] -- 
callable, self_or_null, unused[oparg])) {
         assert(sym_matches_type(callable, &PyFunction_Type));
-        if (sym_is_const(callable)) {
+        if (sym_is_const(ctx, callable)) {
             if (sym_is_null(self_or_null) || sym_is_not_null(self_or_null)) {
-                PyFunctionObject *func = (PyFunctionObject 
*)sym_get_const(callable);
+                PyFunctionObject *func = (PyFunctionObject 
*)sym_get_const(ctx, callable);
                 PyCodeObject *co = (PyCodeObject *)func->func_code;
                 if (co->co_argcount == oparg + !sym_is_null(self_or_null)) {
                     REPLACE_OP(this_instr, _NOP, 0 ,0);
@@ -812,24 +818,26 @@ dummy_func(void) {
     }
 
     op(_GUARD_IS_TRUE_POP, (flag -- )) {
-        if (sym_is_const(flag)) {
-            PyObject *value = sym_get_const(flag);
+        if (sym_is_const(ctx, flag)) {
+            PyObject *value = sym_get_const(ctx, flag);
             assert(value != NULL);
             eliminate_pop_guard(this_instr, value != Py_True);
         }
+        sym_set_const(flag, Py_True);
     }
 
     op(_GUARD_IS_FALSE_POP, (flag -- )) {
-        if (sym_is_const(flag)) {
-            PyObject *value = sym_get_const(flag);
+        if (sym_is_const(ctx, flag)) {
+            PyObject *value = sym_get_const(ctx, flag);
             assert(value != NULL);
             eliminate_pop_guard(this_instr, value != Py_False);
         }
+        sym_set_const(flag, Py_False);
     }
 
     op(_GUARD_IS_NONE_POP, (flag -- )) {
-        if (sym_is_const(flag)) {
-            PyObject *value = sym_get_const(flag);
+        if (sym_is_const(ctx, flag)) {
+            PyObject *value = sym_get_const(ctx, flag);
             assert(value != NULL);
             eliminate_pop_guard(this_instr, !Py_IsNone(value));
         }
@@ -837,11 +845,12 @@ dummy_func(void) {
             assert(!sym_matches_type(flag, &_PyNone_Type));
             eliminate_pop_guard(this_instr, true);
         }
+        sym_set_const(flag, Py_None);
     }
 
     op(_GUARD_IS_NOT_NONE_POP, (flag -- )) {
-        if (sym_is_const(flag)) {
-            PyObject *value = sym_get_const(flag);
+        if (sym_is_const(ctx, flag)) {
+            PyObject *value = sym_get_const(ctx, flag);
             assert(value != NULL);
             eliminate_pop_guard(this_instr, Py_IsNone(value));
         }
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 0372870b94ec0a..397184dd87ad7d 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -140,8 +140,11 @@
         }
 
         case _UNARY_NOT: {
+            JitOptSymbol *value;
             JitOptSymbol *res;
-            res = sym_new_not_null(ctx);
+            value = stack_pointer[-1];
+            sym_set_type(value, &PyBool_Type);
+            res = sym_new_truthiness(ctx, value, false);
             stack_pointer[-1] = res;
             break;
         }
@@ -151,7 +154,7 @@
             JitOptSymbol *res;
             value = stack_pointer[-1];
             if (!optimize_to_bool(this_instr, ctx, value, &res)) {
-                res = sym_new_type(ctx, &PyBool_Type);
+                res = sym_new_truthiness(ctx, value, true);
             }
             stack_pointer[-1] = res;
             break;
@@ -163,7 +166,7 @@
             value = stack_pointer[-1];
             if (!optimize_to_bool(this_instr, ctx, value, &res)) {
                 sym_set_type(value, &PyBool_Type);
-                res = value;
+                res = sym_new_truthiness(ctx, value, true);
             }
             stack_pointer[-1] = res;
             break;
@@ -268,15 +271,15 @@
             JitOptSymbol *res;
             right = stack_pointer[-1];
             left = stack_pointer[-2];
-            if (sym_is_const(left) && sym_is_const(right) &&
+            if (sym_is_const(ctx, left) && sym_is_const(ctx, right) &&
                 sym_matches_type(left, &PyLong_Type) && 
sym_matches_type(right, &PyLong_Type))
             {
-                assert(PyLong_CheckExact(sym_get_const(left)));
-                assert(PyLong_CheckExact(sym_get_const(right)));
+                assert(PyLong_CheckExact(sym_get_const(ctx, left)));
+                assert(PyLong_CheckExact(sym_get_const(ctx, right)));
                 stack_pointer += -2;
                 assert(WITHIN_STACK_BOUNDS());
-                PyObject *temp = _PyLong_Multiply((PyLongObject 
*)sym_get_const(left),
-                    (PyLongObject *)sym_get_const(right));
+                PyObject *temp = _PyLong_Multiply((PyLongObject 
*)sym_get_const(ctx, left),
+                    (PyLongObject *)sym_get_const(ctx, right));
                 if (temp == NULL) {
                     goto error;
                 }
@@ -303,15 +306,15 @@
             JitOptSymbol *res;
             right = stack_pointer[-1];
             left = stack_pointer[-2];
-            if (sym_is_const(left) && sym_is_const(right) &&
+            if (sym_is_const(ctx, left) && sym_is_const(ctx, right) &&
                 sym_matches_type(left, &PyLong_Type) && 
sym_matches_type(right, &PyLong_Type))
             {
-                assert(PyLong_CheckExact(sym_get_const(left)));
-                assert(PyLong_CheckExact(sym_get_const(right)));
+                assert(PyLong_CheckExact(sym_get_const(ctx, left)));
+                assert(PyLong_CheckExact(sym_get_const(ctx, right)));
                 stack_pointer += -2;
                 assert(WITHIN_STACK_BOUNDS());
-                PyObject *temp = _PyLong_Add((PyLongObject 
*)sym_get_const(left),
-                    (PyLongObject *)sym_get_const(right));
+                PyObject *temp = _PyLong_Add((PyLongObject 
*)sym_get_const(ctx, left),
+                    (PyLongObject *)sym_get_const(ctx, right));
                 if (temp == NULL) {
                     goto error;
                 }
@@ -338,15 +341,15 @@
             JitOptSymbol *res;
             right = stack_pointer[-1];
             left = stack_pointer[-2];
-            if (sym_is_const(left) && sym_is_const(right) &&
+            if (sym_is_const(ctx, left) && sym_is_const(ctx, right) &&
                 sym_matches_type(left, &PyLong_Type) && 
sym_matches_type(right, &PyLong_Type))
             {
-                assert(PyLong_CheckExact(sym_get_const(left)));
-                assert(PyLong_CheckExact(sym_get_const(right)));
+                assert(PyLong_CheckExact(sym_get_const(ctx, left)));
+                assert(PyLong_CheckExact(sym_get_const(ctx, right)));
                 stack_pointer += -2;
                 assert(WITHIN_STACK_BOUNDS());
-                PyObject *temp = _PyLong_Subtract((PyLongObject 
*)sym_get_const(left),
-                    (PyLongObject *)sym_get_const(right));
+                PyObject *temp = _PyLong_Subtract((PyLongObject 
*)sym_get_const(ctx, left),
+                    (PyLongObject *)sym_get_const(ctx, right));
                 if (temp == NULL) {
                     goto error;
                 }
@@ -404,14 +407,14 @@
             JitOptSymbol *res;
             right = stack_pointer[-1];
             left = stack_pointer[-2];
-            if (sym_is_const(left) && sym_is_const(right) &&
+            if (sym_is_const(ctx, left) && sym_is_const(ctx, right) &&
                 sym_matches_type(left, &PyFloat_Type) && 
sym_matches_type(right, &PyFloat_Type))
             {
-                assert(PyFloat_CheckExact(sym_get_const(left)));
-                assert(PyFloat_CheckExact(sym_get_const(right)));
+                assert(PyFloat_CheckExact(sym_get_const(ctx, left)));
+                assert(PyFloat_CheckExact(sym_get_const(ctx, right)));
                 PyObject *temp = PyFloat_FromDouble(
-                    PyFloat_AS_DOUBLE(sym_get_const(left)) *
-                    PyFloat_AS_DOUBLE(sym_get_const(right)));
+                    PyFloat_AS_DOUBLE(sym_get_const(ctx, left)) *
+                    PyFloat_AS_DOUBLE(sym_get_const(ctx, right)));
                 if (temp == NULL) {
                     goto error;
                 }
@@ -438,14 +441,14 @@
             JitOptSymbol *res;
             right = stack_pointer[-1];
             left = stack_pointer[-2];
-            if (sym_is_const(left) && sym_is_const(right) &&
+            if (sym_is_const(ctx, left) && sym_is_const(ctx, right) &&
                 sym_matches_type(left, &PyFloat_Type) && 
sym_matches_type(right, &PyFloat_Type))
             {
-                assert(PyFloat_CheckExact(sym_get_const(left)));
-                assert(PyFloat_CheckExact(sym_get_const(right)));
+                assert(PyFloat_CheckExact(sym_get_const(ctx, left)));
+                assert(PyFloat_CheckExact(sym_get_const(ctx, right)));
                 PyObject *temp = PyFloat_FromDouble(
-                    PyFloat_AS_DOUBLE(sym_get_const(left)) +
-                    PyFloat_AS_DOUBLE(sym_get_const(right)));
+                    PyFloat_AS_DOUBLE(sym_get_const(ctx, left)) +
+                    PyFloat_AS_DOUBLE(sym_get_const(ctx, right)));
                 if (temp == NULL) {
                     goto error;
                 }
@@ -472,14 +475,14 @@
             JitOptSymbol *res;
             right = stack_pointer[-1];
             left = stack_pointer[-2];
-            if (sym_is_const(left) && sym_is_const(right) &&
+            if (sym_is_const(ctx, left) && sym_is_const(ctx, right) &&
                 sym_matches_type(left, &PyFloat_Type) && 
sym_matches_type(right, &PyFloat_Type))
             {
-                assert(PyFloat_CheckExact(sym_get_const(left)));
-                assert(PyFloat_CheckExact(sym_get_const(right)));
+                assert(PyFloat_CheckExact(sym_get_const(ctx, left)));
+                assert(PyFloat_CheckExact(sym_get_const(ctx, right)));
                 PyObject *temp = PyFloat_FromDouble(
-                    PyFloat_AS_DOUBLE(sym_get_const(left)) -
-                    PyFloat_AS_DOUBLE(sym_get_const(right)));
+                    PyFloat_AS_DOUBLE(sym_get_const(ctx, left)) -
+                    PyFloat_AS_DOUBLE(sym_get_const(ctx, right)));
                 if (temp == NULL) {
                     goto error;
                 }
@@ -520,9 +523,9 @@
             JitOptSymbol *res;
             right = stack_pointer[-1];
             left = stack_pointer[-2];
-            if (sym_is_const(left) && sym_is_const(right) &&
+            if (sym_is_const(ctx, left) && sym_is_const(ctx, right) &&
                 sym_matches_type(left, &PyUnicode_Type) && 
sym_matches_type(right, &PyUnicode_Type)) {
-                PyObject *temp = PyUnicode_Concat(sym_get_const(left), 
sym_get_const(right));
+                PyObject *temp = PyUnicode_Concat(sym_get_const(ctx, left), 
sym_get_const(ctx, right));
                 if (temp == NULL) {
                     goto error;
                 }
@@ -547,9 +550,9 @@
             right = stack_pointer[-1];
             left = stack_pointer[-2];
             JitOptSymbol *res;
-            if (sym_is_const(left) && sym_is_const(right) &&
+            if (sym_is_const(ctx, left) && sym_is_const(ctx, right) &&
                 sym_matches_type(left, &PyUnicode_Type) && 
sym_matches_type(right, &PyUnicode_Type)) {
-                PyObject *temp = PyUnicode_Concat(sym_get_const(left), 
sym_get_const(right));
+                PyObject *temp = PyUnicode_Concat(sym_get_const(ctx, left), 
sym_get_const(ctx, right));
                 if (temp == NULL) {
                     goto error;
                 }
@@ -1159,8 +1162,8 @@
             (void)dict_version;
             (void)index;
             attr = NULL;
-            if (sym_is_const(owner)) {
-                PyModuleObject *mod = (PyModuleObject *)sym_get_const(owner);
+            if (sym_is_const(ctx, owner)) {
+                PyModuleObject *mod = (PyModuleObject *)sym_get_const(ctx, 
owner);
                 if (PyModule_CheckExact(mod)) {
                     PyObject *dict = mod->md_dict;
                     stack_pointer[-1] = attr;
@@ -1655,10 +1658,10 @@
             JitOptSymbol *callable;
             callable = stack_pointer[-2 - oparg];
             uint32_t func_version = (uint32_t)this_instr->operand0;
-            if (sym_is_const(callable) && sym_matches_type(callable, 
&PyFunction_Type)) {
-                assert(PyFunction_Check(sym_get_const(callable)));
+            if (sym_is_const(ctx, callable) && sym_matches_type(callable, 
&PyFunction_Type)) {
+                assert(PyFunction_Check(sym_get_const(ctx, callable)));
                 REPLACE_OP(this_instr, _CHECK_FUNCTION_VERSION_INLINE, 0, 
func_version);
-                this_instr->operand1 = (uintptr_t)sym_get_const(callable);
+                this_instr->operand1 = (uintptr_t)sym_get_const(ctx, callable);
             }
             sym_set_type(callable, &PyFunction_Type);
             break;
@@ -1724,9 +1727,9 @@
             self_or_null = stack_pointer[-1 - oparg];
             callable = stack_pointer[-2 - oparg];
             assert(sym_matches_type(callable, &PyFunction_Type));
-            if (sym_is_const(callable)) {
+            if (sym_is_const(ctx, callable)) {
                 if (sym_is_null(self_or_null) || 
sym_is_not_null(self_or_null)) {
-                    PyFunctionObject *func = (PyFunctionObject 
*)sym_get_const(callable);
+                    PyFunctionObject *func = (PyFunctionObject 
*)sym_get_const(ctx, callable);
                     PyCodeObject *co = (PyCodeObject *)func->func_code;
                     if (co->co_argcount == oparg + !sym_is_null(self_or_null)) 
{
                         REPLACE_OP(this_instr, _NOP, 0 ,0);
@@ -2160,12 +2163,12 @@
                             res = sym_new_type(ctx, &PyFloat_Type);
                         }
                         else {
-                            if (!sym_is_const(right)) {
+                            if (!sym_is_const(ctx, right)) {
                                 // Case A or B... can't know without the sign 
of the RHS:
                                 res = sym_new_unknown(ctx);
                             }
                             else {
-                                if (_PyLong_IsNegative((PyLongObject 
*)sym_get_const(right))) {
+                                if (_PyLong_IsNegative((PyLongObject 
*)sym_get_const(ctx, right))) {
                                     // Case B:
                                     res = sym_new_type(ctx, &PyFloat_Type);
                                 }
@@ -2230,8 +2233,8 @@
         case _GUARD_IS_TRUE_POP: {
             JitOptSymbol *flag;
             flag = stack_pointer[-1];
-            if (sym_is_const(flag)) {
-                PyObject *value = sym_get_const(flag);
+            if (sym_is_const(ctx, flag)) {
+                PyObject *value = sym_get_const(ctx, flag);
                 assert(value != NULL);
                 stack_pointer += -1;
                 assert(WITHIN_STACK_BOUNDS());
@@ -2239,6 +2242,7 @@
                 stack_pointer += 1;
                 assert(WITHIN_STACK_BOUNDS());
             }
+            sym_set_const(flag, Py_True);
             stack_pointer += -1;
             assert(WITHIN_STACK_BOUNDS());
             break;
@@ -2247,8 +2251,8 @@
         case _GUARD_IS_FALSE_POP: {
             JitOptSymbol *flag;
             flag = stack_pointer[-1];
-            if (sym_is_const(flag)) {
-                PyObject *value = sym_get_const(flag);
+            if (sym_is_const(ctx, flag)) {
+                PyObject *value = sym_get_const(ctx, flag);
                 assert(value != NULL);
                 stack_pointer += -1;
                 assert(WITHIN_STACK_BOUNDS());
@@ -2256,6 +2260,7 @@
                 stack_pointer += 1;
                 assert(WITHIN_STACK_BOUNDS());
             }
+            sym_set_const(flag, Py_False);
             stack_pointer += -1;
             assert(WITHIN_STACK_BOUNDS());
             break;
@@ -2264,8 +2269,8 @@
         case _GUARD_IS_NONE_POP: {
             JitOptSymbol *flag;
             flag = stack_pointer[-1];
-            if (sym_is_const(flag)) {
-                PyObject *value = sym_get_const(flag);
+            if (sym_is_const(ctx, flag)) {
+                PyObject *value = sym_get_const(ctx, flag);
                 assert(value != NULL);
                 stack_pointer += -1;
                 assert(WITHIN_STACK_BOUNDS());
@@ -2283,14 +2288,15 @@
                 stack_pointer += -1;
                 assert(WITHIN_STACK_BOUNDS());
             }
+            sym_set_const(flag, Py_None);
             break;
         }
 
         case _GUARD_IS_NOT_NONE_POP: {
             JitOptSymbol *flag;
             flag = stack_pointer[-1];
-            if (sym_is_const(flag)) {
-                PyObject *value = sym_get_const(flag);
+            if (sym_is_const(ctx, flag)) {
+                PyObject *value = sym_get_const(ctx, flag);
                 assert(value != NULL);
                 stack_pointer += -1;
                 assert(WITHIN_STACK_BOUNDS());
diff --git a/Python/optimizer_symbols.c b/Python/optimizer_symbols.c
index dcde8e7ce81577..e8a3b918e56a94 100644
--- a/Python/optimizer_symbols.c
+++ b/Python/optimizer_symbols.c
@@ -48,6 +48,12 @@ static JitOptSymbol NO_SPACE_SYMBOL = {
     .tag = JIT_SYM_BOTTOM_TAG
 };
 
+static JitOptSymbol *
+allocation_base(JitOptContext *ctx)
+{
+    return ctx->t_arena.arena;
+}
+
 JitOptSymbol *
 out_of_space(JitOptContext *ctx)
 {
@@ -70,6 +76,12 @@ sym_new(JitOptContext *ctx)
     return self;
 }
 
+static void make_const(JitOptSymbol *sym, PyObject *val)
+{
+    sym->tag = JIT_SYM_KNOWN_VALUE_TAG;
+    sym->value.value = Py_NewRef(val);
+}
+
 static inline void
 sym_set_bottom(JitOptContext *ctx, JitOptSymbol *sym)
 {
@@ -90,9 +102,21 @@ _Py_uop_sym_is_not_null(JitOptSymbol *sym) {
 }
 
 bool
-_Py_uop_sym_is_const(JitOptSymbol *sym)
+_Py_uop_sym_is_const(JitOptContext *ctx, JitOptSymbol *sym)
 {
-    return sym->tag == JIT_SYM_KNOWN_VALUE_TAG;
+    if (sym->tag == JIT_SYM_KNOWN_VALUE_TAG) {
+        return true;
+    }
+    if (sym->tag == JIT_SYM_TRUTHINESS_TAG) {
+        JitOptSymbol *value = allocation_base(ctx) + sym->truthiness.value;
+        int truthiness = _Py_uop_sym_truthiness(ctx, value);
+        if (truthiness < 0) {
+            return false;
+        }
+        make_const(sym, (truthiness ^ sym->truthiness.not) ? Py_True : 
Py_False);
+        return true;
+    }
+    return false;
 }
 
 bool
@@ -103,11 +127,21 @@ _Py_uop_sym_is_null(JitOptSymbol *sym)
 
 
 PyObject *
-_Py_uop_sym_get_const(JitOptSymbol *sym)
+_Py_uop_sym_get_const(JitOptContext *ctx, JitOptSymbol *sym)
 {
     if (sym->tag == JIT_SYM_KNOWN_VALUE_TAG) {
         return sym->value.value;
     }
+    if (sym->tag == JIT_SYM_TRUTHINESS_TAG) {
+        JitOptSymbol *value = allocation_base(ctx) + sym->truthiness.value;
+        int truthiness = _Py_uop_sym_truthiness(ctx, value);
+        if (truthiness < 0) {
+            return NULL;
+        }
+        PyObject *res = (truthiness ^ sym->truthiness.not) ? Py_True : 
Py_False;
+        make_const(sym, res);
+        return res;
+    }
     return NULL;
 }
 
@@ -153,6 +187,11 @@ _Py_uop_sym_set_type(JitOptContext *ctx, JitOptSymbol 
*sym, PyTypeObject *typ)
             sym->cls.version = 0;
             sym->cls.type = typ;
             return;
+        case JIT_SYM_TRUTHINESS_TAG:
+            if (typ != &PyBool_Type) {
+                sym_set_bottom(ctx, sym);
+            }
+            return;
     }
 }
 
@@ -193,16 +232,16 @@ _Py_uop_sym_set_type_version(JitOptContext *ctx, 
JitOptSymbol *sym, unsigned int
             sym->tag = JIT_SYM_TYPE_VERSION_TAG;
             sym->version.version = version;
             return true;
+        case JIT_SYM_TRUTHINESS_TAG:
+            if (version != PyBool_Type.tp_version_tag) {
+                sym_set_bottom(ctx, sym);
+                return false;
+            }
+            return true;
     }
     Py_UNREACHABLE();
 }
 
-static void make_const(JitOptSymbol *sym, PyObject *val)
-{
-    sym->tag = JIT_SYM_KNOWN_VALUE_TAG;
-    sym->value.value = Py_NewRef(val);
-}
-
 void
 _Py_uop_sym_set_const(JitOptContext *ctx, JitOptSymbol *sym, PyObject 
*const_val)
 {
@@ -240,6 +279,29 @@ _Py_uop_sym_set_const(JitOptContext *ctx, JitOptSymbol 
*sym, PyObject *const_val
         case JIT_SYM_UNKNOWN_TAG:
             make_const(sym, const_val);
             return;
+        case JIT_SYM_TRUTHINESS_TAG:
+            if (!PyBool_Check(const_val) ||
+                (_Py_uop_sym_is_const(ctx, sym) &&
+                 _Py_uop_sym_get_const(ctx, sym) != const_val))
+            {
+                sym_set_bottom(ctx, sym);
+                return;
+            }
+            JitOptSymbol *value = allocation_base(ctx) + sym->truthiness.value;
+            PyTypeObject *type = _Py_uop_sym_get_type(value);
+            if (const_val == (sym->truthiness.not ? Py_False : Py_True)) {
+                // value is truthy. This is only useful for bool:
+                if (type == &PyBool_Type) {
+                    _Py_uop_sym_set_const(ctx, value, Py_True);
+                }
+            }
+            // value is falsey:
+            else if (type == &PyBool_Type) {
+                _Py_uop_sym_set_const(ctx, value, Py_False);
+            }
+            // TODO: More types (GH-130415)!
+            make_const(sym, const_val);
+            return;
     }
 }
 
@@ -339,6 +401,8 @@ _Py_uop_sym_get_type(JitOptSymbol *sym)
             return Py_TYPE(sym->value.value);
         case JIT_SYM_TUPLE_TAG:
             return &PyTuple_Type;
+        case JIT_SYM_TRUTHINESS_TAG:
+            return &PyBool_Type;
     }
     Py_UNREACHABLE();
 }
@@ -361,6 +425,8 @@ _Py_uop_sym_get_type_version(JitOptSymbol *sym)
             return Py_TYPE(sym->value.value)->tp_version_tag;
         case JIT_SYM_TUPLE_TAG:
             return PyTuple_Type.tp_version_tag;
+        case JIT_SYM_TRUTHINESS_TAG:
+            return PyBool_Type.tp_version_tag;
     }
     Py_UNREACHABLE();
 }
@@ -379,6 +445,7 @@ _Py_uop_sym_has_type(JitOptSymbol *sym)
         case JIT_SYM_KNOWN_CLASS_TAG:
         case JIT_SYM_KNOWN_VALUE_TAG:
         case JIT_SYM_TUPLE_TAG:
+        case JIT_SYM_TRUTHINESS_TAG:
             return true;
     }
     Py_UNREACHABLE();
@@ -398,7 +465,7 @@ _Py_uop_sym_matches_type_version(JitOptSymbol *sym, 
unsigned int version)
 }
 
 int
-_Py_uop_sym_truthiness(JitOptSymbol *sym)
+_Py_uop_sym_truthiness(JitOptContext *ctx, JitOptSymbol *sym)
 {
     switch(sym->tag) {
         case JIT_SYM_NULL_TAG:
@@ -416,6 +483,16 @@ _Py_uop_sym_truthiness(JitOptSymbol *sym)
             break;
         case JIT_SYM_TUPLE_TAG:
             return sym->tuple.length != 0;
+        case JIT_SYM_TRUTHINESS_TAG:
+            ;
+            JitOptSymbol *value = allocation_base(ctx) + sym->truthiness.value;
+            int truthiness = _Py_uop_sym_truthiness(ctx, value);
+            if (truthiness < 0) {
+                return truthiness;
+            }
+            truthiness ^= sym->truthiness.not;
+            make_const(sym, truthiness ? Py_True : Py_False);
+            return truthiness;
     }
     PyObject *value = sym->value.value;
     /* Only handle a few known safe types */
@@ -435,12 +512,6 @@ _Py_uop_sym_truthiness(JitOptSymbol *sym)
     return -1;
 }
 
-static JitOptSymbol *
-allocation_base(JitOptContext *ctx)
-{
-    return ctx->t_arena.arena;
-}
-
 JitOptSymbol *
 _Py_uop_sym_new_tuple(JitOptContext *ctx, int size, JitOptSymbol **args)
 {
@@ -503,9 +574,36 @@ _Py_uop_sym_is_immortal(JitOptSymbol *sym)
     if (sym->tag == JIT_SYM_KNOWN_CLASS_TAG) {
         return sym->cls.type == &PyBool_Type;
     }
+    if (sym->tag == JIT_SYM_TRUTHINESS_TAG) {
+        return true;
+    }
     return false;
 }
 
+JitOptSymbol *
+_Py_uop_sym_new_truthiness(JitOptContext *ctx, JitOptSymbol *value, bool 
truthy)
+{
+    // It's clearer to invert this in the signature:
+    bool not = !truthy;
+    if (value->tag == JIT_SYM_TRUTHINESS_TAG && value->truthiness.not == not) {
+        return value;
+    }
+    JitOptSymbol *res = sym_new(ctx);
+    if (res == NULL) {
+        return out_of_space(ctx);
+    }
+    int truthiness = _Py_uop_sym_truthiness(ctx, value);
+    if (truthiness < 0) {
+        res->tag = JIT_SYM_TRUTHINESS_TAG;
+        res->truthiness.not = not;
+        res->truthiness.value = (uint16_t)(value - allocation_base(ctx));
+    }
+    else {
+        make_const(res, (truthiness ^ not) ? Py_True : Py_False);
+    }
+    return res;
+}
+
 // 0 on success, -1 on error.
 _Py_UOpsAbstractFrame *
 _Py_uop_frame_new(
@@ -570,7 +668,7 @@ _Py_uop_abstractcontext_fini(JitOptContext *ctx)
 void
 _Py_uop_abstractcontext_init(JitOptContext *ctx)
 {
-    static_assert(sizeof(JitOptSymbol) <= 2*sizeof(uint64_t));
+    static_assert(sizeof(JitOptSymbol) <= 2 * sizeof(uint64_t), "JitOptSymbol 
has grown");
     ctx->limit = ctx->locals_and_stack + MAX_ABSTRACT_INTERP_SIZE;
     ctx->n_consumed = ctx->locals_and_stack;
 #ifdef Py_DEBUG // Aids debugging a little. There should never be NULL in the 
abstract interpreter.
@@ -625,6 +723,7 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject 
*Py_UNUSED(ignored))
     _Py_uop_abstractcontext_init(ctx);
     PyObject *val_42 = NULL;
     PyObject *val_43 = NULL;
+    PyObject *tuple = NULL;
 
     // Use a single 'sym' variable so copy-pasting tests is easier.
     JitOptSymbol *sym = _Py_uop_sym_new_unknown(ctx);
@@ -634,8 +733,8 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject 
*Py_UNUSED(ignored))
     TEST_PREDICATE(!_Py_uop_sym_is_null(sym), "top is NULL");
     TEST_PREDICATE(!_Py_uop_sym_is_not_null(sym), "top is not NULL");
     TEST_PREDICATE(!_Py_uop_sym_matches_type(sym, &PyLong_Type), "top matches 
a type");
-    TEST_PREDICATE(!_Py_uop_sym_is_const(sym), "top is a constant");
-    TEST_PREDICATE(_Py_uop_sym_get_const(sym) == NULL, "top as constant is not 
NULL");
+    TEST_PREDICATE(!_Py_uop_sym_is_const(ctx, sym), "top is a constant");
+    TEST_PREDICATE(_Py_uop_sym_get_const(ctx, sym) == NULL, "top as constant 
is not NULL");
     TEST_PREDICATE(!_Py_uop_sym_is_bottom(sym), "top is bottom");
 
     sym = make_bottom(ctx);
@@ -645,8 +744,8 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject 
*Py_UNUSED(ignored))
     TEST_PREDICATE(!_Py_uop_sym_is_null(sym), "bottom is NULL is not false");
     TEST_PREDICATE(!_Py_uop_sym_is_not_null(sym), "bottom is not NULL is not 
false");
     TEST_PREDICATE(!_Py_uop_sym_matches_type(sym, &PyLong_Type), "bottom 
matches a type");
-    TEST_PREDICATE(!_Py_uop_sym_is_const(sym), "bottom is a constant is not 
false");
-    TEST_PREDICATE(_Py_uop_sym_get_const(sym) == NULL, "bottom as constant is 
not NULL");
+    TEST_PREDICATE(!_Py_uop_sym_is_const(ctx, sym), "bottom is a constant is 
not false");
+    TEST_PREDICATE(_Py_uop_sym_get_const(ctx, sym) == NULL, "bottom as 
constant is not NULL");
     TEST_PREDICATE(_Py_uop_sym_is_bottom(sym), "bottom isn't bottom");
 
     sym = _Py_uop_sym_new_type(ctx, &PyLong_Type);
@@ -657,8 +756,8 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject 
*Py_UNUSED(ignored))
     TEST_PREDICATE(_Py_uop_sym_is_not_null(sym), "int isn't not NULL");
     TEST_PREDICATE(_Py_uop_sym_matches_type(sym, &PyLong_Type), "int isn't 
int");
     TEST_PREDICATE(!_Py_uop_sym_matches_type(sym, &PyFloat_Type), "int matches 
float");
-    TEST_PREDICATE(!_Py_uop_sym_is_const(sym), "int is a constant");
-    TEST_PREDICATE(_Py_uop_sym_get_const(sym) == NULL, "int as constant is not 
NULL");
+    TEST_PREDICATE(!_Py_uop_sym_is_const(ctx, sym), "int is a constant");
+    TEST_PREDICATE(_Py_uop_sym_get_const(ctx, sym) == NULL, "int as constant 
is not NULL");
 
     _Py_uop_sym_set_type(ctx, sym, &PyLong_Type);  // Should be a no-op
     TEST_PREDICATE(_Py_uop_sym_matches_type(sym, &PyLong_Type), "(int and int) 
isn't int");
@@ -679,19 +778,19 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject 
*Py_UNUSED(ignored))
         goto fail;
     }
     _Py_uop_sym_set_const(ctx, sym, val_42);
-    TEST_PREDICATE(_Py_uop_sym_truthiness(sym) == 1, "bool(42) is not True");
+    TEST_PREDICATE(_Py_uop_sym_truthiness(ctx, sym) == 1, "bool(42) is not 
True");
     TEST_PREDICATE(!_Py_uop_sym_is_null(sym), "42 is NULL");
     TEST_PREDICATE(_Py_uop_sym_is_not_null(sym), "42 isn't not NULL");
     TEST_PREDICATE(_Py_uop_sym_matches_type(sym, &PyLong_Type), "42 isn't an 
int");
     TEST_PREDICATE(!_Py_uop_sym_matches_type(sym, &PyFloat_Type), "42 matches 
float");
-    TEST_PREDICATE(_Py_uop_sym_is_const(sym), "42 is not a constant");
-    TEST_PREDICATE(_Py_uop_sym_get_const(sym) != NULL, "42 as constant is 
NULL");
-    TEST_PREDICATE(_Py_uop_sym_get_const(sym) == val_42, "42 as constant isn't 
42");
+    TEST_PREDICATE(_Py_uop_sym_is_const(ctx, sym), "42 is not a constant");
+    TEST_PREDICATE(_Py_uop_sym_get_const(ctx, sym) != NULL, "42 as constant is 
NULL");
+    TEST_PREDICATE(_Py_uop_sym_get_const(ctx, sym) == val_42, "42 as constant 
isn't 42");
     TEST_PREDICATE(_Py_uop_sym_is_immortal(sym), "42 is not immortal");
 
     _Py_uop_sym_set_type(ctx, sym, &PyLong_Type);  // Should be a no-op
     TEST_PREDICATE(_Py_uop_sym_matches_type(sym, &PyLong_Type), "(42 and 42) 
isn't an int");
-    TEST_PREDICATE(_Py_uop_sym_get_const(sym) == val_42, "(42 and 42) as 
constant isn't 42");
+    TEST_PREDICATE(_Py_uop_sym_get_const(ctx, sym) == val_42, "(42 and 42) as 
constant isn't 42");
 
     _Py_uop_sym_set_type(ctx, sym, &PyFloat_Type);  // Should make it bottom
     TEST_PREDICATE(_Py_uop_sym_is_bottom(sym), "(42 and float) isn't bottom");
@@ -709,11 +808,11 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject 
*Py_UNUSED(ignored))
 
 
     sym = _Py_uop_sym_new_const(ctx, Py_None);
-    TEST_PREDICATE(_Py_uop_sym_truthiness(sym) == 0, "bool(None) is not 
False");
+    TEST_PREDICATE(_Py_uop_sym_truthiness(ctx, sym) == 0, "bool(None) is not 
False");
     sym = _Py_uop_sym_new_const(ctx, Py_False);
-    TEST_PREDICATE(_Py_uop_sym_truthiness(sym) == 0, "bool(False) is not 
False");
+    TEST_PREDICATE(_Py_uop_sym_truthiness(ctx, sym) == 0, "bool(False) is not 
False");
     sym = _Py_uop_sym_new_const(ctx, PyLong_FromLong(0));
-    TEST_PREDICATE(_Py_uop_sym_truthiness(sym) == 0, "bool(0) is not False");
+    TEST_PREDICATE(_Py_uop_sym_truthiness(ctx, sym) == 0, "bool(0) is not 
False");
 
     JitOptSymbol *i1 = _Py_uop_sym_new_type(ctx, &PyFloat_Type);
     JitOptSymbol *i2 = _Py_uop_sym_new_const(ctx, val_43);
@@ -724,17 +823,31 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject 
*Py_UNUSED(ignored))
         "tuple item does not match value used to create tuple"
     );
     TEST_PREDICATE(
-        _Py_uop_sym_get_const(_Py_uop_sym_tuple_getitem(ctx, sym, 1)) == 
val_43,
+        _Py_uop_sym_get_const(ctx, _Py_uop_sym_tuple_getitem(ctx, sym, 1)) == 
val_43,
         "tuple item does not match value used to create tuple"
     );
     PyObject *pair[2] = { val_42, val_43 };
-    PyObject *tuple = _PyTuple_FromArray(pair, 2);
+    tuple = _PyTuple_FromArray(pair, 2);
     sym = _Py_uop_sym_new_const(ctx, tuple);
     TEST_PREDICATE(
-        _Py_uop_sym_get_const(_Py_uop_sym_tuple_getitem(ctx, sym, 1)) == 
val_43,
+        _Py_uop_sym_get_const(ctx, _Py_uop_sym_tuple_getitem(ctx, sym, 1)) == 
val_43,
         "tuple item does not match value used to create tuple"
     );
-
+    JitOptSymbol *value = _Py_uop_sym_new_type(ctx, &PyBool_Type);
+    sym = _Py_uop_sym_new_truthiness(ctx, value, false);
+    TEST_PREDICATE(_Py_uop_sym_matches_type(sym, &PyBool_Type), "truthiness is 
not boolean");
+    TEST_PREDICATE(_Py_uop_sym_truthiness(ctx, sym) == -1, "truthiness is not 
unknown");
+    TEST_PREDICATE(_Py_uop_sym_is_const(ctx, sym) == false, "truthiness is 
constant");
+    TEST_PREDICATE(_Py_uop_sym_get_const(ctx, sym) == NULL, "truthiness is not 
NULL");
+    TEST_PREDICATE(_Py_uop_sym_is_const(ctx, value) == false, "value is 
constant");
+    TEST_PREDICATE(_Py_uop_sym_get_const(ctx, value) == NULL, "value is not 
NULL");
+    _Py_uop_sym_set_const(ctx, sym, Py_False);
+    TEST_PREDICATE(_Py_uop_sym_matches_type(sym, &PyBool_Type), "truthiness is 
not boolean");
+    TEST_PREDICATE(_Py_uop_sym_truthiness(ctx, sym) == 0, "truthiness is not 
True");
+    TEST_PREDICATE(_Py_uop_sym_is_const(ctx, sym) == true, "truthiness is not 
constant");
+    TEST_PREDICATE(_Py_uop_sym_get_const(ctx, sym) == Py_False, "truthiness is 
not False");
+    TEST_PREDICATE(_Py_uop_sym_is_const(ctx, value) == true, "value is not 
constant");
+    TEST_PREDICATE(_Py_uop_sym_get_const(ctx, value) == Py_True, "value is not 
True");
     _Py_uop_abstractcontext_fini(ctx);
     Py_DECREF(val_42);
     Py_DECREF(val_43);

_______________________________________________
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