Skip to content

Commit b66dfb7

Browse files
markshannondiegorusso
authored andcommitted
pythonGH-115685: Optimize TO_BOOL and variants based on truthiness of input. (pythonGH-116311)
1 parent a88d6d5 commit b66dfb7

File tree

5 files changed

+150
-55
lines changed

5 files changed

+150
-55
lines changed

Include/internal/pycore_optimizer.h

+1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ extern bool _Py_uop_sym_set_non_null(_Py_UopsSymbol *sym);
9696
extern bool _Py_uop_sym_set_type(_Py_UopsSymbol *sym, PyTypeObject *typ);
9797
extern bool _Py_uop_sym_set_const(_Py_UopsSymbol *sym, PyObject *const_val);
9898
extern bool _Py_uop_sym_is_bottom(_Py_UopsSymbol *sym);
99+
extern int _Py_uop_sym_truthiness(_Py_UopsSymbol *sym);
99100

100101

101102
extern int _Py_uop_abstractcontext_init(_Py_UOpsContext *ctx);

Python/optimizer_analysis.c

+22
Original file line numberDiff line numberDiff line change
@@ -298,9 +298,31 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer,
298298
#define sym_set_type _Py_uop_sym_set_type
299299
#define sym_set_const _Py_uop_sym_set_const
300300
#define sym_is_bottom _Py_uop_sym_is_bottom
301+
#define sym_truthiness _Py_uop_sym_truthiness
301302
#define frame_new _Py_uop_frame_new
302303
#define frame_pop _Py_uop_frame_pop
303304

305+
static int
306+
optimize_to_bool(
307+
_PyUOpInstruction *this_instr,
308+
_Py_UOpsContext *ctx,
309+
_Py_UopsSymbol *value,
310+
_Py_UopsSymbol **result_ptr)
311+
{
312+
if (sym_matches_type(value, &PyBool_Type)) {
313+
REPLACE_OP(this_instr, _NOP, 0, 0);
314+
*result_ptr = value;
315+
return 1;
316+
}
317+
int truthiness = sym_truthiness(value);
318+
if (truthiness >= 0) {
319+
PyObject *load = truthiness ? Py_True : Py_False;
320+
REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)load);
321+
*result_ptr = sym_new_const(ctx, load);
322+
return 1;
323+
}
324+
return 0;
325+
}
304326

305327
/* 1 for success, 0 for not ready, cannot error at the moment. */
306328
static int

Python/optimizer_bytecodes.c

+45-28
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ typedef struct _Py_UOpsAbstractFrame _Py_UOpsAbstractFrame;
2929
#define frame_new _Py_uop_frame_new
3030
#define frame_pop _Py_uop_frame_pop
3131

32+
extern int
33+
optimize_to_bool(
34+
_PyUOpInstruction *this_instr,
35+
_Py_UOpsContext *ctx,
36+
_Py_UopsSymbol *value,
37+
_Py_UopsSymbol **result_ptr);
38+
39+
3240
static int
3341
dummy_func(void) {
3442

@@ -271,63 +279,72 @@ dummy_func(void) {
271279
}
272280

273281
op(_TO_BOOL, (value -- res)) {
274-
(void)value;
275-
res = sym_new_type(ctx, &PyBool_Type);
276-
OUT_OF_SPACE_IF_NULL(res);
282+
if (optimize_to_bool(this_instr, ctx, value, &res)) {
283+
OUT_OF_SPACE_IF_NULL(res);
284+
}
285+
else {
286+
res = sym_new_type(ctx, &PyBool_Type);
287+
OUT_OF_SPACE_IF_NULL(res);
288+
}
277289
}
278290

279-
op(_TO_BOOL_BOOL, (value -- value)) {
280-
if (sym_matches_type(value, &PyBool_Type)) {
281-
REPLACE_OP(this_instr, _NOP, 0, 0);
291+
op(_TO_BOOL_BOOL, (value -- res)) {
292+
if (optimize_to_bool(this_instr, ctx, value, &res)) {
293+
OUT_OF_SPACE_IF_NULL(res);
282294
}
283295
else {
284296
if(!sym_set_type(value, &PyBool_Type)) {
285297
goto hit_bottom;
286298
}
299+
res = value;
287300
}
288301
}
289302

290303
op(_TO_BOOL_INT, (value -- res)) {
291-
if (sym_is_const(value) && sym_matches_type(value, &PyLong_Type)) {
292-
PyObject *load = _PyLong_IsZero((PyLongObject *)sym_get_const(value))
293-
? Py_False : Py_True;
294-
REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)load);
295-
OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, load));
304+
if (optimize_to_bool(this_instr, ctx, value, &res)) {
305+
OUT_OF_SPACE_IF_NULL(res);
296306
}
297307
else {
308+
if(!sym_set_type(value, &PyLong_Type)) {
309+
goto hit_bottom;
310+
}
298311
OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyBool_Type));
299312
}
300-
if(!sym_set_type(value, &PyLong_Type)) {
301-
goto hit_bottom;
302-
}
303313
}
304314

305315
op(_TO_BOOL_LIST, (value -- res)) {
306-
if(!sym_set_type(value, &PyList_Type)) {
307-
goto hit_bottom;
316+
if (optimize_to_bool(this_instr, ctx, value, &res)) {
317+
OUT_OF_SPACE_IF_NULL(res);
318+
}
319+
else {
320+
if(!sym_set_type(value, &PyList_Type)) {
321+
goto hit_bottom;
322+
}
323+
OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyBool_Type));
308324
}
309-
OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyBool_Type));
310325
}
311326

312327
op(_TO_BOOL_NONE, (value -- res)) {
313-
if (sym_get_const(value) == Py_None) {
314-
REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)Py_False);
328+
if (optimize_to_bool(this_instr, ctx, value, &res)) {
329+
OUT_OF_SPACE_IF_NULL(res);
330+
}
331+
else {
332+
if (!sym_set_const(value, Py_None)) {
333+
goto hit_bottom;
334+
}
335+
OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, Py_False));
315336
}
316-
sym_set_const(value, Py_None);
317-
OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, Py_False));
318337
}
319338

320339
op(_TO_BOOL_STR, (value -- res)) {
321-
if (sym_is_const(value) && sym_matches_type(value, &PyUnicode_Type)) {
322-
PyObject *load = sym_get_const(value) == &_Py_STR(empty) ? Py_False : Py_True;
323-
REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)load);
324-
OUT_OF_SPACE_IF_NULL(res = sym_new_const(ctx, load));
340+
if (optimize_to_bool(this_instr, ctx, value, &res)) {
341+
OUT_OF_SPACE_IF_NULL(res);
325342
}
326343
else {
327344
OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyBool_Type));
328-
}
329-
if(!sym_set_type(value, &PyUnicode_Type)) {
330-
goto hit_bottom;
345+
if(!sym_set_type(value, &PyUnicode_Type)) {
346+
goto hit_bottom;
347+
}
331348
}
332349
}
333350

Python/optimizer_cases.c.h

+38-27
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/optimizer_symbols.c

+44
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "cpython/optimizer.h"
55
#include "pycore_code.h"
66
#include "pycore_frame.h"
7+
#include "pycore_long.h"
78
#include "pycore_optimizer.h"
89

910
#include <stdbool.h>
@@ -240,6 +241,40 @@ _Py_uop_sym_matches_type(_Py_UopsSymbol *sym, PyTypeObject *typ)
240241
return sym->typ == typ;
241242
}
242243

244+
int
245+
_Py_uop_sym_truthiness(_Py_UopsSymbol *sym)
246+
{
247+
/* There are some non-constant values for
248+
* which `bool(val)` always evaluates to
249+
* True or False, such as tuples with known
250+
* length, but unknown contents, or bound-methods.
251+
* This function will need updating
252+
* should we support those values.
253+
*/
254+
if (_Py_uop_sym_is_bottom(sym)) {
255+
return -1;
256+
}
257+
if (!_Py_uop_sym_is_const(sym)) {
258+
return -1;
259+
}
260+
PyObject *value = _Py_uop_sym_get_const(sym);
261+
if (value == Py_None) {
262+
return 0;
263+
}
264+
/* Only handle a few known safe types */
265+
PyTypeObject *tp = Py_TYPE(value);
266+
if (tp == &PyLong_Type) {
267+
return !_PyLong_IsZero((PyLongObject *)value);
268+
}
269+
if (tp == &PyUnicode_Type) {
270+
return value != &_Py_STR(empty);
271+
}
272+
if (tp == &PyBool_Type) {
273+
return value == Py_True;
274+
}
275+
return -1;
276+
}
277+
243278
// 0 on success, -1 on error.
244279
_Py_UOpsAbstractFrame *
245280
_Py_uop_frame_new(
@@ -413,6 +448,7 @@ _Py_uop_symbols_test(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(ignored))
413448
goto fail;
414449
}
415450
_Py_uop_sym_set_const(sym, val_42);
451+
TEST_PREDICATE(_Py_uop_sym_truthiness(sym) == 1, "bool(42) is not True");
416452
TEST_PREDICATE(!_Py_uop_sym_is_null(sym), "42 is NULL");
417453
TEST_PREDICATE(_Py_uop_sym_is_not_null(sym), "42 isn't not NULL");
418454
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))
436472
_Py_uop_sym_set_const(sym, val_43); // Should make it bottom
437473
TEST_PREDICATE(_Py_uop_sym_is_bottom(sym), "(42 and 43) isn't bottom");
438474

475+
476+
sym = _Py_uop_sym_new_const(ctx, Py_None);
477+
TEST_PREDICATE(_Py_uop_sym_truthiness(sym) == 0, "bool(None) is not False");
478+
sym = _Py_uop_sym_new_const(ctx, Py_False);
479+
TEST_PREDICATE(_Py_uop_sym_truthiness(sym) == 0, "bool(False) is not False");
480+
sym = _Py_uop_sym_new_const(ctx, PyLong_FromLong(0));
481+
TEST_PREDICATE(_Py_uop_sym_truthiness(sym) == 0, "bool(0) is not False");
482+
439483
_Py_uop_abstractcontext_fini(ctx);
440484
Py_DECREF(val_42);
441485
Py_DECREF(val_43);

0 commit comments

Comments
 (0)