Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
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
6 changes: 6 additions & 0 deletions Doc/library/dis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1872,6 +1872,12 @@ but are replaced by real opcodes or removed before bytecode is generated.
Undirected relative jump instructions which are replaced by their
directed (forward/backward) counterparts by the assembler.

.. opcode:: JUMP_IF_TRUE
.. opcode:: JUMP_IF_FALSE

Conditional jumps which do not impact the stack. Replaced by the sequence
``COPY 1``, ``TO_BOOL``, ``POP_JUMP_IF_TRUE/FALSE``.

.. opcode:: LOAD_CLOSURE (i)

Pushes a reference to the cell contained in slot ``i`` of the "fast locals"
Expand Down
3 changes: 2 additions & 1 deletion Include/internal/pycore_magic_number.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ Known values:
Python 3.14a1 3604 (Do not duplicate test at end of while statements)
Python 3.14a1 3605 (Move ENTER_EXECUTOR to opcode 255)
Python 3.14a1 3606 (Specialize CALL_KW)
Python 3.14a1 3607 (Add pseudo instructions JUMP_IF_TRUE/FALSE)

Python 3.15 will start with 3650

Expand All @@ -270,7 +271,7 @@ PC/launcher.c must also be updated.

*/

#define PYC_MAGIC_NUMBER 3606
#define PYC_MAGIC_NUMBER 3607
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
(little-endian) and then appending b'\r\n'. */
#define PYC_MAGIC_NUMBER_TOKEN \
Expand Down
51 changes: 34 additions & 17 deletions Include/internal/pycore_opcode_metadata.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 9 additions & 7 deletions Include/opcode_ids.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 9 additions & 7 deletions Lib/_opcode_metadata.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions Lib/test/test_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -1527,6 +1527,45 @@ async def name_4():
pass
[[]]

class TestBooleanExpression(unittest.TestCase):
class Value:
def __init__(self):
self.called = 0

def __bool__(self):
self.called += 1
return self.value

class Yes(Value):
value = True

class No(Value):
value = False

def test_short_circuit_and(self):
v = [self.Yes(), self.No(), self.Yes()]
res = v[0] and v[1] and v[0]
self.assertIs(res, v[1])
self.assertEqual([e.called for e in v], [1, 1, 0])

def test_short_circuit_or(self):
v = [self.No(), self.Yes(), self.No()]
res = v[0] or v[1] or v[0]
self.assertIs(res, v[1])
self.assertEqual([e.called for e in v], [1, 1, 0])

def test_compound(self):
# See gh-124285
v = [self.No(), self.Yes(), self.Yes(), self.Yes()]
res = v[0] and v[1] or v[2] or v[3]
self.assertIs(res, v[2])
self.assertEqual([e.called for e in v], [1, 0, 1, 0])

v = [self.No(), self.No(), self.Yes(), self.Yes(), self.No()]
res = v[0] or v[1] and v[2] or v[3] or v[4]
self.assertIs(res, v[3])
self.assertEqual([e.called for e in v], [1, 1, 0, 1, 0])

@requires_debug_ranges()
class TestSourcePositions(unittest.TestCase):
# Ensure that compiled code snippets have correct line and column numbers
Expand Down
30 changes: 30 additions & 0 deletions Lib/test/test_generated_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,36 @@ def test_pseudo_instruction_with_flags(self):
"""
self.run_cases_test(input, output)

def test_pseudo_instruction_as_sequence(self):
input = """
pseudo(OP, (in -- out1, out2)) = [
OP1, OP2
];

inst(OP1, (--)) {
}

inst(OP2, (--)) {
}
"""
output = """
TARGET(OP1) {
frame->instr_ptr = next_instr;
next_instr += 1;
INSTRUCTION_STATS(OP1);
DISPATCH();
}

TARGET(OP2) {
frame->instr_ptr = next_instr;
next_instr += 1;
INSTRUCTION_STATS(OP2);
DISPATCH();
}
"""
self.run_cases_test(input, output)


def test_array_input(self):
input = """
inst(OP, (below, values[oparg*2], above --)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix bug where ``bool(a)`` can be invoked more than once during the
evaluation of a compound boolean expression.
8 changes: 8 additions & 0 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2571,6 +2571,14 @@ dummy_func(
JUMP_BACKWARD_NO_INTERRUPT,
};

pseudo(JUMP_IF_FALSE, (cond -- cond)) = [
COPY, TO_BOOL, POP_JUMP_IF_FALSE,
];

pseudo(JUMP_IF_TRUE, (cond -- cond)) = [
COPY, TO_BOOL, POP_JUMP_IF_TRUE,
];

tier1 inst(ENTER_EXECUTOR, (--)) {
#ifdef _Py_TIER2
PyCodeObject *code = _PyFrame_GetCode(frame);
Expand Down
6 changes: 2 additions & 4 deletions Python/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -3140,17 +3140,15 @@ codegen_boolop(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;
jumpi = JUMP_IF_FALSE;
else
jumpi = POP_JUMP_IF_TRUE;
jumpi = JUMP_IF_TRUE;
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, TO_BOOL);
ADDOP_JUMP(c, loc, jumpi, end);
ADDOP(c, loc, POP_TOP);
}
Expand Down
Loading
Loading