mirror of https://github.com/python/cpython
gh-124285: Fix bug where bool() is called multiple times for the same part of a boolean expression (#124394)
This commit is contained in:
parent
c58c572a65
commit
78aeb38f7d
|
@ -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
|
Undirected relative jump instructions which are replaced by their
|
||||||
directed (forward/backward) counterparts by the assembler.
|
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)
|
.. opcode:: LOAD_CLOSURE (i)
|
||||||
|
|
||||||
Pushes a reference to the cell contained in slot ``i`` of the "fast locals"
|
Pushes a reference to the cell contained in slot ``i`` of the "fast locals"
|
||||||
|
|
|
@ -258,6 +258,7 @@ Known values:
|
||||||
Python 3.14a1 3604 (Do not duplicate test at end of while statements)
|
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 3605 (Move ENTER_EXECUTOR to opcode 255)
|
||||||
Python 3.14a1 3606 (Specialize CALL_KW)
|
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
|
Python 3.15 will start with 3650
|
||||||
|
|
||||||
|
@ -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
|
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
|
||||||
(little-endian) and then appending b'\r\n'. */
|
(little-endian) and then appending b'\r\n'. */
|
||||||
#define PYC_MAGIC_NUMBER_TOKEN \
|
#define PYC_MAGIC_NUMBER_TOKEN \
|
||||||
|
|
|
@ -22,6 +22,8 @@ extern "C" {
|
||||||
((OP) == STORE_FAST_MAYBE_NULL) || \
|
((OP) == STORE_FAST_MAYBE_NULL) || \
|
||||||
((OP) == JUMP) || \
|
((OP) == JUMP) || \
|
||||||
((OP) == JUMP_NO_INTERRUPT) || \
|
((OP) == JUMP_NO_INTERRUPT) || \
|
||||||
|
((OP) == JUMP_IF_FALSE) || \
|
||||||
|
((OP) == JUMP_IF_TRUE) || \
|
||||||
((OP) == SETUP_FINALLY) || \
|
((OP) == SETUP_FINALLY) || \
|
||||||
((OP) == SETUP_CLEANUP) || \
|
((OP) == SETUP_CLEANUP) || \
|
||||||
((OP) == SETUP_WITH) || \
|
((OP) == SETUP_WITH) || \
|
||||||
|
@ -269,6 +271,10 @@ int _PyOpcode_num_popped(int opcode, int oparg) {
|
||||||
return 0;
|
return 0;
|
||||||
case JUMP_FORWARD:
|
case JUMP_FORWARD:
|
||||||
return 0;
|
return 0;
|
||||||
|
case JUMP_IF_FALSE:
|
||||||
|
return 1;
|
||||||
|
case JUMP_IF_TRUE:
|
||||||
|
return 1;
|
||||||
case JUMP_NO_INTERRUPT:
|
case JUMP_NO_INTERRUPT:
|
||||||
return 0;
|
return 0;
|
||||||
case LIST_APPEND:
|
case LIST_APPEND:
|
||||||
|
@ -726,6 +732,10 @@ int _PyOpcode_num_pushed(int opcode, int oparg) {
|
||||||
return 0;
|
return 0;
|
||||||
case JUMP_FORWARD:
|
case JUMP_FORWARD:
|
||||||
return 0;
|
return 0;
|
||||||
|
case JUMP_IF_FALSE:
|
||||||
|
return 1;
|
||||||
|
case JUMP_IF_TRUE:
|
||||||
|
return 1;
|
||||||
case JUMP_NO_INTERRUPT:
|
case JUMP_NO_INTERRUPT:
|
||||||
return 0;
|
return 0;
|
||||||
case LIST_APPEND:
|
case LIST_APPEND:
|
||||||
|
@ -956,7 +966,7 @@ enum InstructionFormat {
|
||||||
};
|
};
|
||||||
|
|
||||||
#define IS_VALID_OPCODE(OP) \
|
#define IS_VALID_OPCODE(OP) \
|
||||||
(((OP) >= 0) && ((OP) < 264) && \
|
(((OP) >= 0) && ((OP) < 266) && \
|
||||||
(_PyOpcode_opcode_metadata[(OP)].valid_entry))
|
(_PyOpcode_opcode_metadata[(OP)].valid_entry))
|
||||||
|
|
||||||
#define HAS_ARG_FLAG (1)
|
#define HAS_ARG_FLAG (1)
|
||||||
|
@ -1005,9 +1015,9 @@ struct opcode_metadata {
|
||||||
int16_t flags;
|
int16_t flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const struct opcode_metadata _PyOpcode_opcode_metadata[264];
|
extern const struct opcode_metadata _PyOpcode_opcode_metadata[266];
|
||||||
#ifdef NEED_OPCODE_METADATA
|
#ifdef NEED_OPCODE_METADATA
|
||||||
const struct opcode_metadata _PyOpcode_opcode_metadata[264] = {
|
const struct opcode_metadata _PyOpcode_opcode_metadata[266] = {
|
||||||
[BINARY_OP] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
[BINARY_OP] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
||||||
[BINARY_OP_ADD_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG },
|
[BINARY_OP_ADD_FLOAT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG },
|
||||||
[BINARY_OP_ADD_INT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG },
|
[BINARY_OP_ADD_INT] = { true, INSTR_FMT_IXC, HAS_EXIT_FLAG | HAS_ERROR_FLAG },
|
||||||
|
@ -1224,6 +1234,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[264] = {
|
||||||
[YIELD_VALUE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ESCAPES_FLAG },
|
[YIELD_VALUE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ESCAPES_FLAG },
|
||||||
[_DO_CALL_FUNCTION_EX] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
|
[_DO_CALL_FUNCTION_EX] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
|
||||||
[JUMP] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
[JUMP] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
||||||
|
[JUMP_IF_FALSE] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
||||||
|
[JUMP_IF_TRUE] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
|
||||||
[JUMP_NO_INTERRUPT] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG },
|
[JUMP_NO_INTERRUPT] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG },
|
||||||
[LOAD_CLOSURE] = { true, -1, HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_PURE_FLAG },
|
[LOAD_CLOSURE] = { true, -1, HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_PURE_FLAG },
|
||||||
[POP_BLOCK] = { true, -1, HAS_PURE_FLAG },
|
[POP_BLOCK] = { true, -1, HAS_PURE_FLAG },
|
||||||
|
@ -1422,9 +1434,9 @@ _PyOpcode_macro_expansion[256] = {
|
||||||
};
|
};
|
||||||
#endif // NEED_OPCODE_METADATA
|
#endif // NEED_OPCODE_METADATA
|
||||||
|
|
||||||
extern const char *_PyOpcode_OpName[264];
|
extern const char *_PyOpcode_OpName[266];
|
||||||
#ifdef NEED_OPCODE_METADATA
|
#ifdef NEED_OPCODE_METADATA
|
||||||
const char *_PyOpcode_OpName[264] = {
|
const char *_PyOpcode_OpName[266] = {
|
||||||
[BINARY_OP] = "BINARY_OP",
|
[BINARY_OP] = "BINARY_OP",
|
||||||
[BINARY_OP_ADD_FLOAT] = "BINARY_OP_ADD_FLOAT",
|
[BINARY_OP_ADD_FLOAT] = "BINARY_OP_ADD_FLOAT",
|
||||||
[BINARY_OP_ADD_INT] = "BINARY_OP_ADD_INT",
|
[BINARY_OP_ADD_INT] = "BINARY_OP_ADD_INT",
|
||||||
|
@ -1543,6 +1555,8 @@ const char *_PyOpcode_OpName[264] = {
|
||||||
[JUMP_BACKWARD] = "JUMP_BACKWARD",
|
[JUMP_BACKWARD] = "JUMP_BACKWARD",
|
||||||
[JUMP_BACKWARD_NO_INTERRUPT] = "JUMP_BACKWARD_NO_INTERRUPT",
|
[JUMP_BACKWARD_NO_INTERRUPT] = "JUMP_BACKWARD_NO_INTERRUPT",
|
||||||
[JUMP_FORWARD] = "JUMP_FORWARD",
|
[JUMP_FORWARD] = "JUMP_FORWARD",
|
||||||
|
[JUMP_IF_FALSE] = "JUMP_IF_FALSE",
|
||||||
|
[JUMP_IF_TRUE] = "JUMP_IF_TRUE",
|
||||||
[JUMP_NO_INTERRUPT] = "JUMP_NO_INTERRUPT",
|
[JUMP_NO_INTERRUPT] = "JUMP_NO_INTERRUPT",
|
||||||
[LIST_APPEND] = "LIST_APPEND",
|
[LIST_APPEND] = "LIST_APPEND",
|
||||||
[LIST_EXTEND] = "LIST_EXTEND",
|
[LIST_EXTEND] = "LIST_EXTEND",
|
||||||
|
@ -1943,25 +1957,28 @@ const uint8_t _PyOpcode_Deopt[256] = {
|
||||||
case 235: \
|
case 235: \
|
||||||
;
|
;
|
||||||
struct pseudo_targets {
|
struct pseudo_targets {
|
||||||
uint8_t targets[3];
|
uint8_t as_sequence;
|
||||||
|
uint8_t targets[4];
|
||||||
};
|
};
|
||||||
extern const struct pseudo_targets _PyOpcode_PseudoTargets[8];
|
extern const struct pseudo_targets _PyOpcode_PseudoTargets[10];
|
||||||
#ifdef NEED_OPCODE_METADATA
|
#ifdef NEED_OPCODE_METADATA
|
||||||
const struct pseudo_targets _PyOpcode_PseudoTargets[8] = {
|
const struct pseudo_targets _PyOpcode_PseudoTargets[10] = {
|
||||||
[LOAD_CLOSURE-256] = { { LOAD_FAST, 0, 0 } },
|
[LOAD_CLOSURE-256] = { 0, { LOAD_FAST, 0, 0, 0 } },
|
||||||
[STORE_FAST_MAYBE_NULL-256] = { { STORE_FAST, 0, 0 } },
|
[STORE_FAST_MAYBE_NULL-256] = { 0, { STORE_FAST, 0, 0, 0 } },
|
||||||
[JUMP-256] = { { JUMP_FORWARD, JUMP_BACKWARD, 0 } },
|
[JUMP-256] = { 0, { JUMP_FORWARD, JUMP_BACKWARD, 0, 0 } },
|
||||||
[JUMP_NO_INTERRUPT-256] = { { JUMP_FORWARD, JUMP_BACKWARD_NO_INTERRUPT, 0 } },
|
[JUMP_NO_INTERRUPT-256] = { 0, { JUMP_FORWARD, JUMP_BACKWARD_NO_INTERRUPT, 0, 0 } },
|
||||||
[SETUP_FINALLY-256] = { { NOP, 0, 0 } },
|
[JUMP_IF_FALSE-256] = { 1, { COPY, TO_BOOL, POP_JUMP_IF_FALSE, 0 } },
|
||||||
[SETUP_CLEANUP-256] = { { NOP, 0, 0 } },
|
[JUMP_IF_TRUE-256] = { 1, { COPY, TO_BOOL, POP_JUMP_IF_TRUE, 0 } },
|
||||||
[SETUP_WITH-256] = { { NOP, 0, 0 } },
|
[SETUP_FINALLY-256] = { 0, { NOP, 0, 0, 0 } },
|
||||||
[POP_BLOCK-256] = { { NOP, 0, 0 } },
|
[SETUP_CLEANUP-256] = { 0, { NOP, 0, 0, 0 } },
|
||||||
|
[SETUP_WITH-256] = { 0, { NOP, 0, 0, 0 } },
|
||||||
|
[POP_BLOCK-256] = { 0, { NOP, 0, 0, 0 } },
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // NEED_OPCODE_METADATA
|
#endif // NEED_OPCODE_METADATA
|
||||||
static inline bool
|
static inline bool
|
||||||
is_pseudo_target(int pseudo, int target) {
|
is_pseudo_target(int pseudo, int target) {
|
||||||
if (pseudo < 256 || pseudo >= 264) {
|
if (pseudo < 256 || pseudo >= 266) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (int i = 0; _PyOpcode_PseudoTargets[pseudo-256].targets[i]; i++) {
|
for (int i = 0; _PyOpcode_PseudoTargets[pseudo-256].targets[i]; i++) {
|
||||||
|
|
|
@ -226,13 +226,15 @@ extern "C" {
|
||||||
#define INSTRUMENTED_LINE 254
|
#define INSTRUMENTED_LINE 254
|
||||||
#define ENTER_EXECUTOR 255
|
#define ENTER_EXECUTOR 255
|
||||||
#define JUMP 256
|
#define JUMP 256
|
||||||
#define JUMP_NO_INTERRUPT 257
|
#define JUMP_IF_FALSE 257
|
||||||
#define LOAD_CLOSURE 258
|
#define JUMP_IF_TRUE 258
|
||||||
#define POP_BLOCK 259
|
#define JUMP_NO_INTERRUPT 259
|
||||||
#define SETUP_CLEANUP 260
|
#define LOAD_CLOSURE 260
|
||||||
#define SETUP_FINALLY 261
|
#define POP_BLOCK 261
|
||||||
#define SETUP_WITH 262
|
#define SETUP_CLEANUP 262
|
||||||
#define STORE_FAST_MAYBE_NULL 263
|
#define SETUP_FINALLY 263
|
||||||
|
#define SETUP_WITH 264
|
||||||
|
#define STORE_FAST_MAYBE_NULL 265
|
||||||
|
|
||||||
#define HAVE_ARGUMENT 41
|
#define HAVE_ARGUMENT 41
|
||||||
#define MIN_SPECIALIZED_OPCODE 150
|
#define MIN_SPECIALIZED_OPCODE 150
|
||||||
|
|
|
@ -335,13 +335,15 @@ opmap = {
|
||||||
'INSTRUMENTED_CALL': 252,
|
'INSTRUMENTED_CALL': 252,
|
||||||
'INSTRUMENTED_JUMP_BACKWARD': 253,
|
'INSTRUMENTED_JUMP_BACKWARD': 253,
|
||||||
'JUMP': 256,
|
'JUMP': 256,
|
||||||
'JUMP_NO_INTERRUPT': 257,
|
'JUMP_IF_FALSE': 257,
|
||||||
'LOAD_CLOSURE': 258,
|
'JUMP_IF_TRUE': 258,
|
||||||
'POP_BLOCK': 259,
|
'JUMP_NO_INTERRUPT': 259,
|
||||||
'SETUP_CLEANUP': 260,
|
'LOAD_CLOSURE': 260,
|
||||||
'SETUP_FINALLY': 261,
|
'POP_BLOCK': 261,
|
||||||
'SETUP_WITH': 262,
|
'SETUP_CLEANUP': 262,
|
||||||
'STORE_FAST_MAYBE_NULL': 263,
|
'SETUP_FINALLY': 263,
|
||||||
|
'SETUP_WITH': 264,
|
||||||
|
'STORE_FAST_MAYBE_NULL': 265,
|
||||||
}
|
}
|
||||||
|
|
||||||
HAVE_ARGUMENT = 41
|
HAVE_ARGUMENT = 41
|
||||||
|
|
|
@ -1527,6 +1527,45 @@ class TestSpecifics(unittest.TestCase):
|
||||||
pass
|
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()
|
@requires_debug_ranges()
|
||||||
class TestSourcePositions(unittest.TestCase):
|
class TestSourcePositions(unittest.TestCase):
|
||||||
# Ensure that compiled code snippets have correct line and column numbers
|
# Ensure that compiled code snippets have correct line and column numbers
|
||||||
|
|
|
@ -523,6 +523,36 @@ class TestGeneratedCases(unittest.TestCase):
|
||||||
"""
|
"""
|
||||||
self.run_cases_test(input, output)
|
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):
|
def test_array_input(self):
|
||||||
input = """
|
input = """
|
||||||
inst(OP, (below, values[oparg*2], above --)) {
|
inst(OP, (below, values[oparg*2], above --)) {
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix bug where ``bool(a)`` can be invoked more than once during the
|
||||||
|
evaluation of a compound boolean expression.
|
|
@ -2570,6 +2570,14 @@ dummy_func(
|
||||||
JUMP_BACKWARD_NO_INTERRUPT,
|
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, (--)) {
|
tier1 inst(ENTER_EXECUTOR, (--)) {
|
||||||
#ifdef _Py_TIER2
|
#ifdef _Py_TIER2
|
||||||
PyCodeObject *code = _PyFrame_GetCode(frame);
|
PyCodeObject *code = _PyFrame_GetCode(frame);
|
||||||
|
|
|
@ -3140,17 +3140,15 @@ codegen_boolop(compiler *c, expr_ty e)
|
||||||
location loc = LOC(e);
|
location loc = LOC(e);
|
||||||
assert(e->kind == BoolOp_kind);
|
assert(e->kind == BoolOp_kind);
|
||||||
if (e->v.BoolOp.op == And)
|
if (e->v.BoolOp.op == And)
|
||||||
jumpi = POP_JUMP_IF_FALSE;
|
jumpi = JUMP_IF_FALSE;
|
||||||
else
|
else
|
||||||
jumpi = POP_JUMP_IF_TRUE;
|
jumpi = JUMP_IF_TRUE;
|
||||||
NEW_JUMP_TARGET_LABEL(c, end);
|
NEW_JUMP_TARGET_LABEL(c, end);
|
||||||
s = e->v.BoolOp.values;
|
s = e->v.BoolOp.values;
|
||||||
n = asdl_seq_LEN(s) - 1;
|
n = asdl_seq_LEN(s) - 1;
|
||||||
assert(n >= 0);
|
assert(n >= 0);
|
||||||
for (i = 0; i < n; ++i) {
|
for (i = 0; i < n; ++i) {
|
||||||
VISIT(c, expr, (expr_ty)asdl_seq_GET(s, 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_JUMP(c, loc, jumpi, end);
|
||||||
ADDOP(c, loc, POP_TOP);
|
ADDOP(c, loc, POP_TOP);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1589,6 +1589,8 @@ basicblock_optimize_load_const(PyObject *const_cache, basicblock *bb, PyObject *
|
||||||
switch(nextop) {
|
switch(nextop) {
|
||||||
case POP_JUMP_IF_FALSE:
|
case POP_JUMP_IF_FALSE:
|
||||||
case POP_JUMP_IF_TRUE:
|
case POP_JUMP_IF_TRUE:
|
||||||
|
case JUMP_IF_FALSE:
|
||||||
|
case JUMP_IF_TRUE:
|
||||||
{
|
{
|
||||||
/* Remove LOAD_CONST const; conditional jump */
|
/* Remove LOAD_CONST const; conditional jump */
|
||||||
PyObject* cnt = get_const_value(opcode, oparg, consts);
|
PyObject* cnt = get_const_value(opcode, oparg, consts);
|
||||||
|
@ -1600,8 +1602,11 @@ basicblock_optimize_load_const(PyObject *const_cache, basicblock *bb, PyObject *
|
||||||
if (is_true == -1) {
|
if (is_true == -1) {
|
||||||
return ERROR;
|
return ERROR;
|
||||||
}
|
}
|
||||||
INSTR_SET_OP0(inst, NOP);
|
if (PyCompile_OpcodeStackEffect(nextop, 0) == -1) {
|
||||||
int jump_if_true = nextop == POP_JUMP_IF_TRUE;
|
/* POP_JUMP_IF_FALSE or POP_JUMP_IF_TRUE */
|
||||||
|
INSTR_SET_OP0(inst, NOP);
|
||||||
|
}
|
||||||
|
int jump_if_true = (nextop == POP_JUMP_IF_TRUE || nextop == JUMP_IF_TRUE);
|
||||||
if (is_true == jump_if_true) {
|
if (is_true == jump_if_true) {
|
||||||
bb->b_instr[i+1].i_opcode = JUMP;
|
bb->b_instr[i+1].i_opcode = JUMP;
|
||||||
}
|
}
|
||||||
|
@ -1761,6 +1766,36 @@ optimize_basic_block(PyObject *const_cache, basicblock *bb, PyObject *consts)
|
||||||
i -= jump_thread(bb, inst, target, POP_JUMP_IF_TRUE);
|
i -= jump_thread(bb, inst, target, POP_JUMP_IF_TRUE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case JUMP_IF_FALSE:
|
||||||
|
switch (target->i_opcode) {
|
||||||
|
case JUMP:
|
||||||
|
case JUMP_IF_FALSE:
|
||||||
|
i -= jump_thread(bb, inst, target, JUMP_IF_FALSE);
|
||||||
|
continue;
|
||||||
|
case JUMP_IF_TRUE:
|
||||||
|
// No need to check for loops here, a block's b_next
|
||||||
|
// cannot point to itself.
|
||||||
|
assert(inst->i_target != inst->i_target->b_next);
|
||||||
|
inst->i_target = inst->i_target->b_next;
|
||||||
|
i--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JUMP_IF_TRUE:
|
||||||
|
switch (target->i_opcode) {
|
||||||
|
case JUMP:
|
||||||
|
case JUMP_IF_TRUE:
|
||||||
|
i -= jump_thread(bb, inst, target, JUMP_IF_TRUE);
|
||||||
|
continue;
|
||||||
|
case JUMP_IF_FALSE:
|
||||||
|
// No need to check for loops here, a block's b_next
|
||||||
|
// cannot point to itself.
|
||||||
|
assert(inst->i_target != inst->i_target->b_next);
|
||||||
|
inst->i_target = inst->i_target->b_next;
|
||||||
|
i--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case JUMP:
|
case JUMP:
|
||||||
case JUMP_NO_INTERRUPT:
|
case JUMP_NO_INTERRUPT:
|
||||||
switch (target->i_opcode) {
|
switch (target->i_opcode) {
|
||||||
|
@ -2367,6 +2402,38 @@ push_cold_blocks_to_end(cfg_builder *g) {
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
convert_pseudo_conditional_jumps(cfg_builder *g)
|
||||||
|
{
|
||||||
|
basicblock *entryblock = g->g_entryblock;
|
||||||
|
for (basicblock *b = entryblock; b != NULL; b = b->b_next) {
|
||||||
|
for (int i = 0; i < b->b_iused; i++) {
|
||||||
|
cfg_instr *instr = &b->b_instr[i];
|
||||||
|
if (instr->i_opcode == JUMP_IF_FALSE || instr->i_opcode == JUMP_IF_TRUE) {
|
||||||
|
assert(i == b->b_iused - 1);
|
||||||
|
instr->i_opcode = instr->i_opcode == JUMP_IF_FALSE ?
|
||||||
|
POP_JUMP_IF_FALSE : POP_JUMP_IF_TRUE;
|
||||||
|
location loc = instr->i_loc;
|
||||||
|
cfg_instr copy = {
|
||||||
|
.i_opcode = COPY,
|
||||||
|
.i_oparg = 1,
|
||||||
|
.i_loc = loc,
|
||||||
|
.i_target = NULL,
|
||||||
|
};
|
||||||
|
RETURN_IF_ERROR(basicblock_insert_instruction(b, i++, ©));
|
||||||
|
cfg_instr to_bool = {
|
||||||
|
.i_opcode = TO_BOOL,
|
||||||
|
.i_oparg = 0,
|
||||||
|
.i_loc = loc,
|
||||||
|
.i_target = NULL,
|
||||||
|
};
|
||||||
|
RETURN_IF_ERROR(basicblock_insert_instruction(b, i++, &to_bool));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
convert_pseudo_ops(cfg_builder *g)
|
convert_pseudo_ops(cfg_builder *g)
|
||||||
{
|
{
|
||||||
|
@ -2826,6 +2893,8 @@ _PyCfg_OptimizedCfgToInstructionSequence(cfg_builder *g,
|
||||||
int *stackdepth, int *nlocalsplus,
|
int *stackdepth, int *nlocalsplus,
|
||||||
_PyInstructionSequence *seq)
|
_PyInstructionSequence *seq)
|
||||||
{
|
{
|
||||||
|
RETURN_IF_ERROR(convert_pseudo_conditional_jumps(g));
|
||||||
|
|
||||||
*stackdepth = calculate_stackdepth(g);
|
*stackdepth = calculate_stackdepth(g);
|
||||||
if (*stackdepth < 0) {
|
if (*stackdepth < 0) {
|
||||||
return ERROR;
|
return ERROR;
|
||||||
|
|
|
@ -248,6 +248,7 @@ class PseudoInstruction:
|
||||||
name: str
|
name: str
|
||||||
stack: StackEffect
|
stack: StackEffect
|
||||||
targets: list[Instruction]
|
targets: list[Instruction]
|
||||||
|
as_sequence: bool
|
||||||
flags: list[str]
|
flags: list[str]
|
||||||
opcode: int = -1
|
opcode: int = -1
|
||||||
|
|
||||||
|
@ -852,6 +853,7 @@ def add_pseudo(
|
||||||
pseudo.name,
|
pseudo.name,
|
||||||
analyze_stack(pseudo),
|
analyze_stack(pseudo),
|
||||||
[instructions[target] for target in pseudo.targets],
|
[instructions[target] for target in pseudo.targets],
|
||||||
|
pseudo.as_sequence,
|
||||||
pseudo.flags,
|
pseudo.flags,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -305,6 +305,7 @@ def generate_pseudo_targets(analysis: Analysis, out: CWriter) -> None:
|
||||||
table_size = len(analysis.pseudos)
|
table_size = len(analysis.pseudos)
|
||||||
max_targets = max(len(pseudo.targets) for pseudo in analysis.pseudos.values())
|
max_targets = max(len(pseudo.targets) for pseudo in analysis.pseudos.values())
|
||||||
out.emit("struct pseudo_targets {\n")
|
out.emit("struct pseudo_targets {\n")
|
||||||
|
out.emit(f"uint8_t as_sequence;\n")
|
||||||
out.emit(f"uint8_t targets[{max_targets + 1}];\n")
|
out.emit(f"uint8_t targets[{max_targets + 1}];\n")
|
||||||
out.emit("};\n")
|
out.emit("};\n")
|
||||||
out.emit(
|
out.emit(
|
||||||
|
@ -315,10 +316,11 @@ def generate_pseudo_targets(analysis: Analysis, out: CWriter) -> None:
|
||||||
f"const struct pseudo_targets _PyOpcode_PseudoTargets[{table_size}] = {{\n"
|
f"const struct pseudo_targets _PyOpcode_PseudoTargets[{table_size}] = {{\n"
|
||||||
)
|
)
|
||||||
for pseudo in analysis.pseudos.values():
|
for pseudo in analysis.pseudos.values():
|
||||||
|
as_sequence = "1" if pseudo.as_sequence else "0"
|
||||||
targets = ["0"] * (max_targets + 1)
|
targets = ["0"] * (max_targets + 1)
|
||||||
for i, target in enumerate(pseudo.targets):
|
for i, target in enumerate(pseudo.targets):
|
||||||
targets[i] = target.name
|
targets[i] = target.name
|
||||||
out.emit(f"[{pseudo.name}-256] = {{ {{ {', '.join(targets)} }} }},\n")
|
out.emit(f"[{pseudo.name}-256] = {{ {as_sequence}, {{ {', '.join(targets)} }} }},\n")
|
||||||
out.emit("};\n\n")
|
out.emit("};\n\n")
|
||||||
out.emit("#endif // NEED_OPCODE_METADATA\n")
|
out.emit("#endif // NEED_OPCODE_METADATA\n")
|
||||||
out.emit("static inline bool\n")
|
out.emit("static inline bool\n")
|
||||||
|
|
|
@ -148,6 +148,7 @@ class Pseudo(Node):
|
||||||
outputs: list[OutputEffect]
|
outputs: list[OutputEffect]
|
||||||
flags: list[str] # instr flags to set on the pseudo instruction
|
flags: list[str] # instr flags to set on the pseudo instruction
|
||||||
targets: list[str] # opcodes this can be replaced by
|
targets: list[str] # opcodes this can be replaced by
|
||||||
|
as_sequence: bool
|
||||||
|
|
||||||
|
|
||||||
AstNode = InstDef | Macro | Pseudo | Family
|
AstNode = InstDef | Macro | Pseudo | Family
|
||||||
|
@ -423,16 +424,22 @@ class Parser(PLexer):
|
||||||
flags = []
|
flags = []
|
||||||
if self.expect(lx.RPAREN):
|
if self.expect(lx.RPAREN):
|
||||||
if self.expect(lx.EQUALS):
|
if self.expect(lx.EQUALS):
|
||||||
if not self.expect(lx.LBRACE):
|
if self.expect(lx.LBRACE):
|
||||||
raise self.make_syntax_error("Expected {")
|
as_sequence = False
|
||||||
if members := self.members():
|
closing = lx.RBRACE
|
||||||
if self.expect(lx.RBRACE) and self.expect(lx.SEMI):
|
elif self.expect(lx.LBRACKET):
|
||||||
|
as_sequence = True
|
||||||
|
closing = lx.RBRACKET
|
||||||
|
else:
|
||||||
|
raise self.make_syntax_error("Expected { or [")
|
||||||
|
if members := self.members(allow_sequence=True):
|
||||||
|
if self.expect(closing) and self.expect(lx.SEMI):
|
||||||
return Pseudo(
|
return Pseudo(
|
||||||
tkn.text, inp, outp, flags, members
|
tkn.text, inp, outp, flags, members, as_sequence
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def members(self) -> list[str] | None:
|
def members(self, allow_sequence : bool=False) -> list[str] | None:
|
||||||
here = self.getpos()
|
here = self.getpos()
|
||||||
if tkn := self.expect(lx.IDENTIFIER):
|
if tkn := self.expect(lx.IDENTIFIER):
|
||||||
members = [tkn.text]
|
members = [tkn.text]
|
||||||
|
@ -442,8 +449,10 @@ class Parser(PLexer):
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
peek = self.peek()
|
peek = self.peek()
|
||||||
if not peek or peek.kind != lx.RBRACE:
|
kinds = [lx.RBRACE, lx.RBRACKET] if allow_sequence else [lx.RBRACE]
|
||||||
raise self.make_syntax_error("Expected comma or right paren")
|
if not peek or peek.kind not in kinds:
|
||||||
|
raise self.make_syntax_error(
|
||||||
|
f"Expected comma or right paren{'/bracket' if allow_sequence else ''}")
|
||||||
return members
|
return members
|
||||||
self.setpos(here)
|
self.setpos(here)
|
||||||
return None
|
return None
|
||||||
|
|
Loading…
Reference in New Issue