diff options
author | Brandt Bucher <brandtbucher@microsoft.com> | 2025-03-02 13:21:34 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-03-02 13:21:34 -0800 |
commit | 7afa476874b9a432ad6dbe9fb3e65d62f2999f88 (patch) | |
tree | 53e88fb36267f0bacc8d326f38dcadfb441f8c6e | |
parent | c6513f7a627c8918a5e3f3733fa47d34a94ddff9 (diff) | |
download | cpython-7afa476874b9a432ad6dbe9fb3e65d62f2999f88.tar.gz cpython-7afa476874b9a432ad6dbe9fb3e65d62f2999f88.zip |
GH-130415: Use boolean guards to narrow types to values in the JIT (GH-130659)
-rw-r--r-- | Include/internal/pycore_optimizer.h | 15 | ||||
-rw-r--r-- | Lib/test/test_capi/test_opt.py | 62 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core_and_Builtins/2025-02-27-17-05-05.gh-issue-130415.iijvfW.rst | 2 | ||||
-rw-r--r-- | Python/optimizer_analysis.c | 23 | ||||
-rw-r--r-- | Python/optimizer_bytecodes.c | 115 | ||||
-rw-r--r-- | Python/optimizer_cases.c.h | 114 | ||||
-rw-r--r-- | Python/optimizer_symbols.c | 183 |
7 files changed, 348 insertions, 166 deletions
diff --git a/Include/internal/pycore_optimizer.h b/Include/internal/pycore_optimizer.h index 25c3d3e5a22..79861167f65 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 2a9b777862c..b7083dbfb89 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -1437,6 +1437,68 @@ class TestUopsOptimization(unittest.TestCase): 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 00000000000..f5b6d0e9c1a --- /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 29a05088e62..67bf8d11b3f 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 f3625a1492c..c4e4b28e50d 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 0372870b94e..397184dd87a 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 dcde8e7ce81..e8a3b918e56 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); |