Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Make boolean conversions in branches explicit
  • Loading branch information
brandtbucher committed Jun 6, 2023
commit 69781b4244a9485837b5eda473533e5c280068c0
3 changes: 2 additions & 1 deletion Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.12b1 3530 (Shrink the LOAD_SUPER_ATTR caches)
# Python 3.12b1 3531 (Add PEP 695 changes)
# Python 3.13a1 3550 (Plugin optimizer support)
# Python 3.13a1 3551 (Make the conversion to boolean in jumps explicit)

# Python 3.14 will start with 3600

Expand All @@ -462,7 +463,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.

MAGIC_NUMBER = (3550).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3551).to_bytes(2, 'little') + b'\r\n'

_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c

Expand Down
8 changes: 4 additions & 4 deletions Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -1292,19 +1292,19 @@ def test_multiline_boolean_expression(self):
""")
compiled_code, _ = self.check_positions_against_ast(snippet)
# jump if a is true:
self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_TRUE',
self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_FALSE',
line=1, end_line=1, column=4, end_column=5, occurrence=1)
# jump if b is false:
self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_FALSE',
self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_TRUE',
line=2, end_line=2, column=5, end_column=6, occurrence=1)
# jump if c is false:
self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_FALSE',
self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_TRUE',
line=2, end_line=2, column=15, end_column=16, occurrence=2)
# compare d and 0
self.assertOpcodeSourcePositionIs(compiled_code, 'COMPARE_OP',
line=4, end_line=4, column=8, end_column=13, occurrence=1)
# jump if comparison it True
self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_TRUE',
self.assertOpcodeSourcePositionIs(compiled_code, 'POP_JUMP_IF_FALSE',
line=4, end_line=4, column=8, end_column=13, occurrence=2)

def test_multiline_assert(self):
Expand Down
3 changes: 2 additions & 1 deletion Lib/test/test_compiler_codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def test_if_expression(self):
expected = [
('RESUME', 0, 0),
('LOAD_CONST', 0, 1),
('POP_JUMP_IF_FALSE', false_lbl := self.Label(), 1),
('UNARY_NOT', 0, 1),
('POP_JUMP_IF_TRUE', false_lbl := self.Label(), 1),
('LOAD_CONST', 1, 1),
('JUMP', exit_lbl := self.Label()),
false_lbl,
Expand Down
212 changes: 110 additions & 102 deletions Lib/test/test_dis.py

Large diffs are not rendered by default.

9 changes: 4 additions & 5 deletions Lib/test/test_peepholer.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,8 @@ def test_unot(self):
def unot(x):
if not x == 2:
del x
self.assertNotInBytecode(unot, 'UNARY_NOT')
self.assertNotInBytecode(unot, 'POP_JUMP_IF_FALSE')
self.assertInBytecode(unot, 'POP_JUMP_IF_TRUE')
self.assertNotInBytecode(unot, 'POP_JUMP_IF_TRUE')
self.assertInBytecode(unot, 'POP_JUMP_IF_FALSE')
self.check_lnotab(unot)

def test_elim_inversion_of_is_or_in(self):
Expand Down Expand Up @@ -386,14 +385,14 @@ def f(a, b, c):
and c)
self.check_jump_targets(f)
self.check_lnotab(f)
self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 2)
self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 2)
# POP_JUMP_IF_TRUE to POP_JUMP_IF_TRUE --> POP_JUMP_IF_TRUE to non-jump
def f(a, b, c):
return ((a or b)
or c)
self.check_jump_targets(f)
self.check_lnotab(f)
self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 2)
self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 2)
# JUMP_IF_FALSE_OR_POP to JUMP_IF_TRUE_OR_POP --> POP_JUMP_IF_FALSE to non-jump
def f(a, b, c):
return ((a and b)
Expand Down
26 changes: 4 additions & 22 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2146,35 +2146,17 @@ dummy_func(
}

inst(POP_JUMP_IF_FALSE, (cond -- )) {
assert(PyBool_Check(cond));
if (Py_IsFalse(cond)) {
JUMPBY(oparg);
}
else if (!Py_IsTrue(cond)) {
int err = PyObject_IsTrue(cond);
DECREF_INPUTS();
if (err == 0) {
JUMPBY(oparg);
}
else {
ERROR_IF(err < 0, error);
}
}
}

inst(POP_JUMP_IF_TRUE, (cond -- )) {
assert(PyBool_Check(cond));
if (Py_IsTrue(cond)) {
JUMPBY(oparg);
}
else if (!Py_IsFalse(cond)) {
int err = PyObject_IsTrue(cond);
DECREF_INPUTS();
if (err > 0) {
JUMPBY(oparg);
}
else {
ERROR_IF(err < 0, error);
}
}
}

inst(POP_JUMP_IF_NOT_NONE, (value -- )) {
Expand Down Expand Up @@ -3401,7 +3383,7 @@ dummy_func(

inst(INSTRUMENTED_POP_JUMP_IF_TRUE, ( -- )) {
PyObject *cond = POP();
int err = PyObject_IsTrue(cond);
int err = PyObject_IsTrue(cond); // XXX
Py_DECREF(cond);
ERROR_IF(err < 0, error);
_Py_CODEUNIT *here = next_instr-1;
Expand All @@ -3412,7 +3394,7 @@ dummy_func(

inst(INSTRUMENTED_POP_JUMP_IF_FALSE, ( -- )) {
PyObject *cond = POP();
int err = PyObject_IsTrue(cond);
int err = PyObject_IsTrue(cond); // XXX
Py_DECREF(cond);
ERROR_IF(err < 0, error);
_Py_CODEUNIT *here = next_instr-1;
Expand Down
23 changes: 15 additions & 8 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -2823,11 +2823,13 @@ compiler_jump_if(struct compiler *c, location loc,
ADDOP_I(c, LOC(e), SWAP, 2);
ADDOP_I(c, LOC(e), COPY, 2);
ADDOP_COMPARE(c, LOC(e), asdl_seq_GET(e->v.Compare.ops, i));
ADDOP_JUMP(c, LOC(e), POP_JUMP_IF_FALSE, cleanup);
ADDOP(c, LOC(e), UNARY_NOT);
ADDOP_JUMP(c, LOC(e), POP_JUMP_IF_TRUE, cleanup);
}
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, n));
ADDOP_COMPARE(c, LOC(e), asdl_seq_GET(e->v.Compare.ops, n));
ADDOP_JUMP(c, LOC(e), cond ? POP_JUMP_IF_TRUE : POP_JUMP_IF_FALSE, next);
ADDOP(c, LOC(e), UNARY_NOT);
ADDOP_JUMP(c, LOC(e), cond ? POP_JUMP_IF_FALSE : POP_JUMP_IF_TRUE, next);
NEW_JUMP_TARGET_LABEL(c, end);
ADDOP_JUMP(c, NO_LOCATION, JUMP, end);

Expand All @@ -2850,7 +2852,8 @@ compiler_jump_if(struct compiler *c, location loc,

/* general implementation */
VISIT(c, expr, e);
ADDOP_JUMP(c, LOC(e), cond ? POP_JUMP_IF_TRUE : POP_JUMP_IF_FALSE, next);
ADDOP(c, LOC(e), UNARY_NOT);
ADDOP_JUMP(c, LOC(e), cond ? POP_JUMP_IF_FALSE : POP_JUMP_IF_TRUE, next);
return SUCCESS;
}

Expand Down Expand Up @@ -4202,16 +4205,17 @@ compiler_boolop(struct compiler *c, expr_ty e)
location loc = LOC(e);
assert(e->kind == BoolOp_kind);
if (e->v.BoolOp.op == And)
jumpi = POP_JUMP_IF_FALSE;
else
jumpi = POP_JUMP_IF_TRUE;
else
jumpi = POP_JUMP_IF_FALSE;
NEW_JUMP_TARGET_LABEL(c, end);
s = e->v.BoolOp.values;
n = asdl_seq_LEN(s) - 1;
assert(n >= 0);
for (i = 0; i < n; ++i) {
VISIT(c, expr, (expr_ty)asdl_seq_GET(s, i));
ADDOP_I(c, loc, COPY, 1);
ADDOP(c, loc, UNARY_NOT);
ADDOP_JUMP(c, loc, jumpi, end);
ADDOP(c, loc, POP_TOP);
}
Expand Down Expand Up @@ -4519,7 +4523,8 @@ compiler_compare(struct compiler *c, expr_ty e)
ADDOP_I(c, loc, COPY, 2);
ADDOP_COMPARE(c, loc, asdl_seq_GET(e->v.Compare.ops, i));
ADDOP_I(c, loc, COPY, 1);
ADDOP_JUMP(c, loc, POP_JUMP_IF_FALSE, cleanup);
ADDOP(c, loc, UNARY_NOT);
ADDOP_JUMP(c, loc, POP_JUMP_IF_TRUE, cleanup);
ADDOP(c, loc, POP_TOP);
}
VISIT(c, expr, (expr_ty)asdl_seq_GET(e->v.Compare.comparators, n));
Expand Down Expand Up @@ -5754,7 +5759,8 @@ compiler_visit_keyword(struct compiler *c, keyword_ty k)
static int
compiler_with_except_finish(struct compiler *c, jump_target_label cleanup) {
NEW_JUMP_TARGET_LABEL(c, suppress);
ADDOP_JUMP(c, NO_LOCATION, POP_JUMP_IF_TRUE, suppress);
ADDOP(c, NO_LOCATION, UNARY_NOT);
ADDOP_JUMP(c, NO_LOCATION, POP_JUMP_IF_FALSE, suppress);
ADDOP_I(c, NO_LOCATION, RERAISE, 2);

USE_LABEL(c, suppress);
Expand Down Expand Up @@ -7162,7 +7168,8 @@ compiler_pattern_value(struct compiler *c, pattern_ty p, pattern_context *pc)
}
VISIT(c, expr, value);
ADDOP_COMPARE(c, LOC(p), Eq);
RETURN_IF_ERROR(jump_to_fail_pop(c, LOC(p), pc, POP_JUMP_IF_FALSE));
ADDOP(c, LOC(p), UNARY_NOT);
RETURN_IF_ERROR(jump_to_fail_pop(c, LOC(p), pc, POP_JUMP_IF_TRUE));
return SUCCESS;
}

Expand Down
89 changes: 65 additions & 24 deletions Python/flowgraph.c
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,36 @@ get_const_value(int opcode, int oparg, PyObject *co_consts)
return Py_NewRef(constant);
}

// Steals a reference to newconst.
static int
add_const(PyObject *newconst, PyObject *consts, PyObject *const_cache)
{
if (_PyCompile_ConstCacheMergeOne(const_cache, &newconst) < 0) {
Py_DECREF(newconst);
return -1;
}

Py_ssize_t index;
for (index = 0; index < PyList_GET_SIZE(consts); index++) {
if (PyList_GET_ITEM(consts, index) == newconst) {
break;
}
}
if (index == PyList_GET_SIZE(consts)) {
if ((size_t)index >= (size_t)INT_MAX - 1) {
PyErr_SetString(PyExc_OverflowError, "too many constants");
Py_DECREF(newconst);
return -1;
}
if (PyList_Append(consts, newconst)) {
Py_DECREF(newconst);
return -1;
}
}
Py_DECREF(newconst);
return index;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

windows compiler warning here, implicit cast from Py_ssize_t to int

}

/* Replace LOAD_CONST c1, LOAD_CONST c2 ... LOAD_CONST cn, BUILD_TUPLE n
with LOAD_CONST (c1, c2, ... cn).
The consts table must still be in list form so that the
Expand Down Expand Up @@ -1153,33 +1183,14 @@ fold_tuple_on_constants(PyObject *const_cache,
}
PyTuple_SET_ITEM(newconst, i, constant);
}
if (_PyCompile_ConstCacheMergeOne(const_cache, &newconst) < 0) {
Py_DECREF(newconst);
int index = add_const(newconst, consts, const_cache);
if (index < 0) {
return ERROR;
}

Py_ssize_t index;
for (index = 0; index < PyList_GET_SIZE(consts); index++) {
if (PyList_GET_ITEM(consts, index) == newconst) {
break;
}
}
if (index == PyList_GET_SIZE(consts)) {
if ((size_t)index >= (size_t)INT_MAX - 1) {
Py_DECREF(newconst);
PyErr_SetString(PyExc_OverflowError, "too many constants");
return ERROR;
}
if (PyList_Append(consts, newconst)) {
Py_DECREF(newconst);
return ERROR;
}
}
Py_DECREF(newconst);
for (int i = 0; i < n; i++) {
INSTR_SET_OP0(&inst[i], NOP);
}
INSTR_SET_OP1(&inst[n], LOAD_CONST, (int)index);
INSTR_SET_OP1(&inst[n], LOAD_CONST, index);
return SUCCESS;
}

Expand Down Expand Up @@ -1432,17 +1443,39 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts)
INSTR_SET_OP0(&bb->b_instr[i + 1], NOP);
}
break;
case UNARY_NOT:
cnt = get_const_value(opcode, oparg, consts);
if (cnt == NULL) {
goto error;
}
is_true = PyObject_IsTrue(cnt);
Py_DECREF(cnt);
if (is_true == -1) {
goto error;
}
cnt = PyBool_FromLong(!is_true);
int index = add_const(cnt, consts, const_cache);
if (index < 0) {
return ERROR;
}
INSTR_SET_OP0(inst, NOP);
INSTR_SET_OP1(&bb->b_instr[i + 1], LOAD_CONST, index);
break;
case IS_OP:
cnt = get_const_value(opcode, oparg, consts);
if (cnt == NULL) {
goto error;
}
int jump_op = i+2 < bb->b_iused ? bb->b_instr[i+2].i_opcode : 0;
int invert = i + 2 < bb->b_iused && bb->b_instr[i + 2].i_opcode == UNARY_NOT;
int jump_op = i + invert + 2 < bb->b_iused ? bb->b_instr[i + invert + 2].i_opcode : 0;
if (Py_IsNone(cnt) && (jump_op == POP_JUMP_IF_FALSE || jump_op == POP_JUMP_IF_TRUE)) {
unsigned char nextarg = bb->b_instr[i+1].i_oparg;
INSTR_SET_OP0(inst, NOP);
INSTR_SET_OP0(&bb->b_instr[i + 1], NOP);
bb->b_instr[i+2].i_opcode = nextarg ^ (jump_op == POP_JUMP_IF_FALSE) ?
if (invert) {
INSTR_SET_OP0(&bb->b_instr[i + 2], NOP);
}
bb->b_instr[i + invert + 2].i_opcode = nextarg ^ invert ^ (jump_op == POP_JUMP_IF_FALSE) ?
POP_JUMP_IF_NOT_NONE : POP_JUMP_IF_NONE;
}
Py_DECREF(cnt);
Expand All @@ -1454,6 +1487,14 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts)
}
break;
}
case IS_OP:
case CONTAINS_OP:
if (nextop == UNARY_NOT) {
INSTR_SET_OP0(inst, NOP);
INSTR_SET_OP1(&bb->b_instr[i + 1], opcode, !oparg);
continue;
}
break;
/* Try to fold tuples of constants.
Skip over BUILD_TUPLE(1) UNPACK_SEQUENCE(1).
Replace BUILD_TUPLE(2) UNPACK_SEQUENCE(2) with SWAP(2).
Expand Down
Loading