aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorBrandt Bucher <brandtbucher@microsoft.com>2025-03-02 13:21:34 -0800
committerGitHub <noreply@github.com>2025-03-02 13:21:34 -0800
commit7afa476874b9a432ad6dbe9fb3e65d62f2999f88 (patch)
tree53e88fb36267f0bacc8d326f38dcadfb441f8c6e
parentc6513f7a627c8918a5e3f3733fa47d34a94ddff9 (diff)
downloadcpython-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.h15
-rw-r--r--Lib/test/test_capi/test_opt.py62
-rw-r--r--Misc/NEWS.d/next/Core_and_Builtins/2025-02-27-17-05-05.gh-issue-130415.iijvfW.rst2
-rw-r--r--Python/optimizer_analysis.c23
-rw-r--r--Python/optimizer_bytecodes.c115
-rw-r--r--Python/optimizer_cases.c.h114
-rw-r--r--Python/optimizer_symbols.c183
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);