mirror of
https://github.com/python/cpython.git
synced 2025-09-27 10:50:04 +00:00
GH-115685: Optimize TO_BOOL
and variants based on truthiness of input. (GH-116311)
This commit is contained in:
parent
a29998a06b
commit
cbf3d38cbe
5 changed files with 150 additions and 55 deletions
|
@ -96,6 +96,7 @@ extern bool _Py_uop_sym_set_non_null(_Py_UopsSymbol *sym);
|
||||||
extern bool _Py_uop_sym_set_type(_Py_UopsSymbol *sym, PyTypeObject *typ);
|
extern bool _Py_uop_sym_set_type(_Py_UopsSymbol *sym, PyTypeObject *typ);
|
||||||
extern bool _Py_uop_sym_set_const(_Py_UopsSymbol *sym, PyObject *const_val);
|
extern bool _Py_uop_sym_set_const(_Py_UopsSymbol *sym, PyObject *const_val);
|
||||||
extern bool _Py_uop_sym_is_bottom(_Py_UopsSymbol *sym);
|
extern bool _Py_uop_sym_is_bottom(_Py_UopsSymbol *sym);
|
||||||
|
extern int _Py_uop_sym_truthiness(_Py_UopsSymbol *sym);
|
||||||
|
|
||||||
|
|
||||||
extern int _Py_uop_abstractcontext_init(_Py_UOpsContext *ctx);
|
extern int _Py_uop_abstractcontext_init(_Py_UOpsContext *ctx);
|
||||||
|
|
|
@ -298,9 +298,31 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer,
|
||||||
#define sym_set_type _Py_uop_sym_set_type
|
#define sym_set_type _Py_uop_sym_set_type
|
||||||
#define sym_set_const _Py_uop_sym_set_const
|
#define sym_set_const _Py_uop_sym_set_const
|
||||||
#define sym_is_bottom _Py_uop_sym_is_bottom
|
#define sym_is_bottom _Py_uop_sym_is_bottom
|
||||||
|
#define sym_truthiness _Py_uop_sym_truthiness
|
||||||
#define frame_new _Py_uop_frame_new
|
#define frame_new _Py_uop_frame_new
|
||||||
#define frame_pop _Py_uop_frame_pop
|
#define frame_pop _Py_uop_frame_pop
|
||||||
|
|
||||||
|
static int
|
||||||
|
optimize_to_bool(
|
||||||
|
_PyUOpInstruction *this_instr,
|
||||||
|
_Py_UOpsContext *ctx,
|
||||||
|
_Py_UopsSymbol *value,
|
||||||
|
_Py_UopsSymbol **result_ptr)
|
||||||
|
{
|
||||||
|
if (sym_matches_type(value, &PyBool_Type)) {
|
||||||
|
REPLACE_OP(this_instr, _NOP, 0, 0);
|
||||||
|
*result_ptr = value;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int truthiness = sym_truthiness(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);
|
||||||
|
*result_ptr = sym_new_const(ctx, load);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* 1 for success, 0 for not ready, cannot error at the moment. */
|
/* 1 for success, 0 for not ready, cannot error at the moment. */
|
||||||
static int
|
static int
|
||||||
|
|
|
@ -29,6 +29,14 @@ typedef struct _Py_UOpsAbstractFrame _Py_UOpsAbstractFrame;
|
||||||
#define frame_new _Py_uop_frame_new
|
#define frame_new _Py_uop_frame_new
|
||||||
#define frame_pop _Py_uop_frame_pop
|
#define frame_pop _Py_uop_frame_pop
|
||||||
|
|
||||||
|
extern int
|
||||||
|
optimize_to_bool(
|
||||||
|
_PyUOpInstruction *this_instr,
|
||||||
|
_Py_UOpsContext *ctx,
|
||||||
|
_Py_UopsSymbol *value,
|
||||||
|
_Py_UopsSymbol **result_ptr);
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
dummy_func(void) {
|
dummy_func(void) {
|
||||||
|
|
||||||
|
@ -271,63 +279,72 @@ dummy_func(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
op(_TO_BOOL, (value -- res)) {
|
op(_TO_BOOL, (value -- res)) {
|
||||||
(void)value;
|
if (optimize_to_bool(this_instr, ctx, value, &res)) {
|
||||||
res = sym_new_type(ctx, &PyBool_Type);
|
OUT_OF_SPACE_IF_NULL(res);
|
||||||
OUT_OF_SPACE_IF_NULL(res);
|
}
|
||||||
|
else {
|
||||||
|
res = sym_new_type(ctx, &PyBool_Type);
|
||||||
|
OUT_OF_SPACE_IF_NULL(res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
op(_TO_BOOL_BOOL, (value -- value)) {
|
op(_TO_BOOL_BOOL, (value -- res)) {
|
||||||
if (sym_matches_type(value, &PyBool_Type)) {
|
if (optimize_to_bool(this_instr, ctx, value, &res)) {
|
||||||
REPLACE_OP(this_instr, _NOP, 0, 0);
|
OUT_OF_SPACE_IF_NULL(res);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(!sym_set_type(value, &PyBool_Type)) {
|
if(!sym_set_type(value, &PyBool_Type)) {
|
||||||
goto hit_bottom;
|
goto hit_bottom;
|
||||||
}
|
}
|
||||||
|
res = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
op(_TO_BOOL_INT, (value -- res)) {
|
op(_TO_BOOL_INT, (value -- res)) {
|
||||||
if (sym_is_const(value) && sym_matches_type(value, &PyLong_Type)) {
|
if (optimize_to_bool(this_instr, ctx, value, &res)) {
|
||||||
PyObject *load = _PyLong_IsZero((PyLongObject *)sym_get_const(value))
|
OUT_OF_SPACE_IF_NULL(res);
|
||||||
? Py_False : Py_True;
|
|
||||||
REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)load);
|
|
||||||
OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, load));
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if(!sym_set_type(value, &PyLong_Type)) {
|
||||||
|
goto hit_bottom;
|
||||||
|
}
|
||||||
OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyBool_Type));
|
OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyBool_Type));
|
||||||
}
|
}
|
||||||
if(!sym_set_type(value, &PyLong_Type)) {
|
|
||||||
goto hit_bottom;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
op(_TO_BOOL_LIST, (value -- res)) {
|
op(_TO_BOOL_LIST, (value -- res)) {
|
||||||
if(!sym_set_type(value, &PyList_Type)) {
|
if (optimize_to_bool(this_instr, ctx, value, &res)) {
|
||||||
goto hit_bottom;
|
OUT_OF_SPACE_IF_NULL(res);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(!sym_set_type(value, &PyList_Type)) {
|
||||||
|
goto hit_bottom;
|
||||||
|
}
|
||||||
|
OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyBool_Type));
|
||||||
}
|
}
|
||||||
OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyBool_Type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
op(_TO_BOOL_NONE, (value -- res)) {
|
op(_TO_BOOL_NONE, (value -- res)) {
|
||||||
if (sym_get_const(value) == Py_None) {
|
if (optimize_to_bool(this_instr, ctx, value, &res)) {
|
||||||
REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)Py_False);
|
OUT_OF_SPACE_IF_NULL(res);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!sym_set_const(value, Py_None)) {
|
||||||
|
goto hit_bottom;
|
||||||
|
}
|
||||||
|
OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, Py_False));
|
||||||
}
|
}
|
||||||
sym_set_const(value, Py_None);
|
|
||||||
OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, Py_False));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
op(_TO_BOOL_STR, (value -- res)) {
|
op(_TO_BOOL_STR, (value -- res)) {
|
||||||
if (sym_is_const(value) && sym_matches_type(value, &PyUnicode_Type)) {
|
if (optimize_to_bool(this_instr, ctx, value, &res)) {
|
||||||
PyObject *load = sym_get_const(value) == &_Py_STR(empty) ? Py_False : Py_True;
|
OUT_OF_SPACE_IF_NULL(res);
|
||||||
REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)load);
|
|
||||||
OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, load));
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyBool_Type));
|
OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyBool_Type));
|
||||||
}
|
if(!sym_set_type(value, &PyUnicode_Type)) {
|
||||||
if(!sym_set_type(value, &PyUnicode_Type)) {
|
goto hit_bottom;
|
||||||
goto hit_bottom;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
65
Python/optimizer_cases.c.h
generated
65
Python/optimizer_cases.c.h
generated
|
@ -107,24 +107,31 @@
|
||||||
_Py_UopsSymbol *value;
|
_Py_UopsSymbol *value;
|
||||||
_Py_UopsSymbol *res;
|
_Py_UopsSymbol *res;
|
||||||
value = stack_pointer[-1];
|
value = stack_pointer[-1];
|
||||||
(void)value;
|
if (optimize_to_bool(this_instr, ctx, value, &res)) {
|
||||||
res = sym_new_type(ctx, &PyBool_Type);
|
OUT_OF_SPACE_IF_NULL(res);
|
||||||
OUT_OF_SPACE_IF_NULL(res);
|
}
|
||||||
|
else {
|
||||||
|
res = sym_new_type(ctx, &PyBool_Type);
|
||||||
|
OUT_OF_SPACE_IF_NULL(res);
|
||||||
|
}
|
||||||
stack_pointer[-1] = res;
|
stack_pointer[-1] = res;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case _TO_BOOL_BOOL: {
|
case _TO_BOOL_BOOL: {
|
||||||
_Py_UopsSymbol *value;
|
_Py_UopsSymbol *value;
|
||||||
|
_Py_UopsSymbol *res;
|
||||||
value = stack_pointer[-1];
|
value = stack_pointer[-1];
|
||||||
if (sym_matches_type(value, &PyBool_Type)) {
|
if (optimize_to_bool(this_instr, ctx, value, &res)) {
|
||||||
REPLACE_OP(this_instr, _NOP, 0, 0);
|
OUT_OF_SPACE_IF_NULL(res);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(!sym_set_type(value, &PyBool_Type)) {
|
if(!sym_set_type(value, &PyBool_Type)) {
|
||||||
goto hit_bottom;
|
goto hit_bottom;
|
||||||
}
|
}
|
||||||
|
res = value;
|
||||||
}
|
}
|
||||||
|
stack_pointer[-1] = res;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,18 +139,15 @@
|
||||||
_Py_UopsSymbol *value;
|
_Py_UopsSymbol *value;
|
||||||
_Py_UopsSymbol *res;
|
_Py_UopsSymbol *res;
|
||||||
value = stack_pointer[-1];
|
value = stack_pointer[-1];
|
||||||
if (sym_is_const(value) && sym_matches_type(value, &PyLong_Type)) {
|
if (optimize_to_bool(this_instr, ctx, value, &res)) {
|
||||||
PyObject *load = _PyLong_IsZero((PyLongObject *)sym_get_const(value))
|
OUT_OF_SPACE_IF_NULL(res);
|
||||||
? Py_False : Py_True;
|
|
||||||
REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)load);
|
|
||||||
OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, load));
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if(!sym_set_type(value, &PyLong_Type)) {
|
||||||
|
goto hit_bottom;
|
||||||
|
}
|
||||||
OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyBool_Type));
|
OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyBool_Type));
|
||||||
}
|
}
|
||||||
if(!sym_set_type(value, &PyLong_Type)) {
|
|
||||||
goto hit_bottom;
|
|
||||||
}
|
|
||||||
stack_pointer[-1] = res;
|
stack_pointer[-1] = res;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -152,10 +156,15 @@
|
||||||
_Py_UopsSymbol *value;
|
_Py_UopsSymbol *value;
|
||||||
_Py_UopsSymbol *res;
|
_Py_UopsSymbol *res;
|
||||||
value = stack_pointer[-1];
|
value = stack_pointer[-1];
|
||||||
if(!sym_set_type(value, &PyList_Type)) {
|
if (optimize_to_bool(this_instr, ctx, value, &res)) {
|
||||||
goto hit_bottom;
|
OUT_OF_SPACE_IF_NULL(res);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(!sym_set_type(value, &PyList_Type)) {
|
||||||
|
goto hit_bottom;
|
||||||
|
}
|
||||||
|
OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyBool_Type));
|
||||||
}
|
}
|
||||||
OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyBool_Type));
|
|
||||||
stack_pointer[-1] = res;
|
stack_pointer[-1] = res;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -164,11 +173,15 @@
|
||||||
_Py_UopsSymbol *value;
|
_Py_UopsSymbol *value;
|
||||||
_Py_UopsSymbol *res;
|
_Py_UopsSymbol *res;
|
||||||
value = stack_pointer[-1];
|
value = stack_pointer[-1];
|
||||||
if (sym_get_const(value) == Py_None) {
|
if (optimize_to_bool(this_instr, ctx, value, &res)) {
|
||||||
REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)Py_False);
|
OUT_OF_SPACE_IF_NULL(res);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!sym_set_const(value, Py_None)) {
|
||||||
|
goto hit_bottom;
|
||||||
|
}
|
||||||
|
OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, Py_False));
|
||||||
}
|
}
|
||||||
sym_set_const(value, Py_None);
|
|
||||||
OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, Py_False));
|
|
||||||
stack_pointer[-1] = res;
|
stack_pointer[-1] = res;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -177,16 +190,14 @@
|
||||||
_Py_UopsSymbol *value;
|
_Py_UopsSymbol *value;
|
||||||
_Py_UopsSymbol *res;
|
_Py_UopsSymbol *res;
|
||||||
value = stack_pointer[-1];
|
value = stack_pointer[-1];
|
||||||
if (sym_is_const(value) && sym_matches_type(value, &PyUnicode_Type)) {
|
if (optimize_to_bool(this_instr, ctx, value, &res)) {
|
||||||
PyObject *load = sym_get_const(value) == &_Py_STR(empty) ? Py_False : Py_True;
|
OUT_OF_SPACE_IF_NULL(res);
|
||||||
REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)load);
|
|
||||||
OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, load));
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyBool_Type));
|
OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyBool_Type));
|
||||||
}
|
if(!sym_set_type(value, &PyUnicode_Type)) {
|
||||||
if(!sym_set_type(value, &PyUnicode_Type)) {
|
goto hit_bottom;
|
||||||
goto hit_bottom;
|
}
|
||||||
}
|
}
|
||||||
stack_pointer[-1] = res;
|
stack_pointer[-1] = res;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "cpython/optimizer.h"
|
#include "cpython/optimizer.h"
|
||||||
#include "pycore_code.h"
|
#include "pycore_code.h"
|
||||||
#include "pycore_frame.h"
|
#include "pycore_frame.h"
|
||||||
|
#include "pycore_long.h"
|
||||||
#include "pycore_optimizer.h"
|
#include "pycore_optimizer.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
@ -240,6 +241,40 @@ _Py_uop_sym_matches_type(_Py_UopsSymbol *sym, PyTypeObject *typ)
|
||||||
return sym->typ == typ;
|
return sym->typ == typ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
_Py_uop_sym_truthiness(_Py_UopsSymbol *sym)
|
||||||
|
{
|
||||||
|
/* There are some non-constant values for
|
||||||
|
* which `bool(val)` always evaluates to
|
||||||
|
* True or False, such as tuples with known
|
||||||
|
* length, but unknown contents, or bound-methods.
|
||||||
|
* This function will need updating
|
||||||
|
* should we support those values.
|
||||||
|
*/
|
||||||
|
if (_Py_uop_sym_is_bottom(sym)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!_Py_uop_sym_is_const(sym)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
PyObject *value = _Py_uop_sym_get_const(sym);
|
||||||
|
if (value == Py_None) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Only handle a few known safe types */
|
||||||
|
PyTypeObject *tp = Py_TYPE(value);
|
||||||
|
if (tp == &PyLong_Type) {
|
||||||
|
return !_PyLong_IsZero((PyLongObject *)value);
|
||||||
|
}
|
||||||
|
if (tp == &PyUnicode_Type) {
|
||||||
|
return value != &_Py_STR(empty);
|
||||||
|
}
|
||||||
|
if (tp == &PyBool_Type) {
|
||||||
|
return value == Py_True;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// 0 on success, -1 on error.
|
// 0 on success, -1 on error.
|
||||||
_Py_UOpsAbstractFrame *
|
_Py_UOpsAbstractFrame *
|
||||||
_Py_uop_frame_new(
|
_Py_uop_frame_new(
|
||||||
|
@ -413,6 +448,7 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored))
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
_Py_uop_sym_set_const(sym, val_42);
|
_Py_uop_sym_set_const(sym, val_42);
|
||||||
|
TEST_PREDICATE(_Py_uop_sym_truthiness(sym) == 1, "bool(42) is not True");
|
||||||
TEST_PREDICATE(!_Py_uop_sym_is_null(sym), "42 is NULL");
|
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_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, &PyLong_Type), "42 isn't an int");
|
||||||
|
@ -436,6 +472,14 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored))
|
||||||
_Py_uop_sym_set_const(sym, val_43); // Should make it bottom
|
_Py_uop_sym_set_const(sym, val_43); // Should make it bottom
|
||||||
TEST_PREDICATE(_Py_uop_sym_is_bottom(sym), "(42 and 43) isn't bottom");
|
TEST_PREDICATE(_Py_uop_sym_is_bottom(sym), "(42 and 43) isn't bottom");
|
||||||
|
|
||||||
|
|
||||||
|
sym = _Py_uop_sym_new_const(ctx, Py_None);
|
||||||
|
TEST_PREDICATE(_Py_uop_sym_truthiness(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");
|
||||||
|
sym = _Py_uop_sym_new_const(ctx, PyLong_FromLong(0));
|
||||||
|
TEST_PREDICATE(_Py_uop_sym_truthiness(sym) == 0, "bool(0) is not False");
|
||||||
|
|
||||||
_Py_uop_abstractcontext_fini(ctx);
|
_Py_uop_abstractcontext_fini(ctx);
|
||||||
Py_DECREF(val_42);
|
Py_DECREF(val_42);
|
||||||
Py_DECREF(val_43);
|
Py_DECREF(val_43);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue