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

This commit is contained in:
Brandt Bucher 2025-03-02 13:21:34 -08:00 committed by GitHub
parent c6513f7a62
commit 7afa476874
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 348 additions and 166 deletions

View file

@ -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);

View file

@ -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));
}

View file

@ -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());

View file

@ -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);