Skip to content

Commit a17507a

Browse files
committed
pythongh-93444: remove redundant fields from basicblock: b_nofallthrough, b_exit, b_return
1 parent be41de1 commit a17507a

File tree

1 file changed

+74
-73
lines changed

1 file changed

+74
-73
lines changed

Python/compile.c

+74-73
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@
101101
(opcode) == POP_JUMP_IF_FALSE || \
102102
(opcode) == POP_JUMP_IF_TRUE)
103103

104+
#define IS_JUMP_OPCODE(opcode) \
105+
(IS_VIRTUAL_JUMP_OPCODE(opcode) || \
106+
is_bit_set_in_table(_PyOpcode_Jump, opcode))
107+
104108
/* opcodes which are not emitted in codegen stage, only by the assembler */
105109
#define IS_ASSEMBLER_OPCODE(opcode) \
106110
((opcode) == JUMP_FORWARD || \
@@ -124,6 +128,17 @@
124128
(opcode) == POP_JUMP_BACKWARD_IF_TRUE || \
125129
(opcode) == POP_JUMP_BACKWARD_IF_FALSE)
126130

131+
#define IS_UNCONDITIONAL_JUMP_OPCODE(opcode) \
132+
((opcode) == JUMP || \
133+
(opcode) == JUMP_NO_INTERRUPT || \
134+
(opcode) == JUMP_FORWARD || \
135+
(opcode) == JUMP_BACKWARD || \
136+
(opcode) == JUMP_BACKWARD_NO_INTERRUPT)
137+
138+
#define IS_SCOPE_EXIT_OPCODE(opcode) \
139+
((opcode) == RETURN_VALUE || \
140+
(opcode) == RAISE_VARARGS || \
141+
(opcode) == RERAISE)
127142

128143
#define IS_TOP_LEVEL_AWAIT(c) ( \
129144
(c->c_flags->cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT) \
@@ -182,8 +197,7 @@ is_block_push(struct instr *instr)
182197
static inline int
183198
is_jump(struct instr *i)
184199
{
185-
return IS_VIRTUAL_JUMP_OPCODE(i->i_opcode) ||
186-
is_bit_set_in_table(_PyOpcode_Jump, i->i_opcode);
200+
return IS_JUMP_OPCODE(i->i_opcode);
187201
}
188202

189203
static int
@@ -249,16 +263,10 @@ typedef struct basicblock_ {
249263
int b_startdepth;
250264
/* instruction offset for block, computed by assemble_jump_offsets() */
251265
int b_offset;
252-
/* Basic block has no fall through (it ends with a return, raise or jump) */
253-
unsigned b_nofallthrough : 1;
254266
/* Basic block is an exception handler that preserves lasti */
255267
unsigned b_preserve_lasti : 1;
256268
/* Used by compiler passes to mark whether they have visited a basic block. */
257269
unsigned b_visited : 1;
258-
/* Basic block exits scope (it ends with a return or raise) */
259-
unsigned b_exit : 1;
260-
/* b_return is true if a RETURN_VALUE opcode is inserted. */
261-
unsigned b_return : 1;
262270
/* b_cold is true if this block is not perf critical (like an exception handler) */
263271
unsigned b_cold : 1;
264272
/* b_warm is used by the cold-detection algorithm to mark blocks which are definitely not cold */
@@ -274,6 +282,29 @@ basicblock_last_instr(basicblock *b) {
274282
return NULL;
275283
}
276284

285+
static inline int
286+
basicblock_returns(basicblock *b) {
287+
struct instr *last = basicblock_last_instr(b);
288+
return last && last->i_opcode == RETURN_VALUE;
289+
}
290+
291+
static inline int
292+
basicblock_exits_scope(basicblock *b) {
293+
struct instr *last = basicblock_last_instr(b);
294+
return last && IS_SCOPE_EXIT_OPCODE(last->i_opcode);
295+
}
296+
297+
static inline int
298+
basicblock_nofallthrough(basicblock *b) {
299+
struct instr *last = basicblock_last_instr(b);
300+
return (last &&
301+
(IS_SCOPE_EXIT_OPCODE(last->i_opcode) ||
302+
IS_UNCONDITIONAL_JUMP_OPCODE(last->i_opcode)));
303+
}
304+
305+
#define BB_NO_FALLTHROUGH(B) (basicblock_nofallthrough(B))
306+
#define BB_HAS_FALLTHROUGH(B) (!basicblock_nofallthrough(B))
307+
277308
/* fblockinfo tracks the current frame block.
278309
279310
A frame block is used to handle loops, try/except, and try/finally.
@@ -847,7 +878,7 @@ compiler_copy_block(struct compiler *c, basicblock *block)
847878
/* Cannot copy a block if it has a fallthrough, since
848879
* a block can only have one fallthrough predecessor.
849880
*/
850-
assert(block->b_nofallthrough);
881+
assert(BB_NO_FALLTHROUGH(block));
851882
basicblock *result = compiler_new_block(c);
852883
if (result == NULL) {
853884
return NULL;
@@ -859,8 +890,6 @@ compiler_copy_block(struct compiler *c, basicblock *block)
859890
}
860891
result->b_instr[n] = block->b_instr[i];
861892
}
862-
result->b_exit = block->b_exit;
863-
result->b_nofallthrough = 1;
864893
return result;
865894
}
866895

@@ -1218,11 +1247,7 @@ static int
12181247
is_end_of_basic_block(struct instr *instr)
12191248
{
12201249
int opcode = instr->i_opcode;
1221-
1222-
return is_jump(instr) ||
1223-
opcode == RETURN_VALUE ||
1224-
opcode == RAISE_VARARGS ||
1225-
opcode == RERAISE;
1250+
return is_jump(instr) || IS_SCOPE_EXIT_OPCODE(opcode);
12261251
}
12271252

12281253
static int
@@ -1258,9 +1283,6 @@ basicblock_addop_line(basicblock *b, int opcode, int line,
12581283
struct instr *i = &b->b_instr[off];
12591284
i->i_opcode = opcode;
12601285
i->i_oparg = 0;
1261-
if (opcode == RETURN_VALUE) {
1262-
b->b_return = 1;
1263-
}
12641286
i->i_lineno = line;
12651287
i->i_end_lineno = end_line;
12661288
i->i_col_offset = col_offset;
@@ -7139,11 +7161,8 @@ stackdepth(struct compiler *c, basicblock *entry)
71397161
}
71407162
depth = new_depth;
71417163
assert(!IS_ASSEMBLER_OPCODE(instr->i_opcode));
7142-
if (instr->i_opcode == JUMP_NO_INTERRUPT ||
7143-
instr->i_opcode == JUMP ||
7144-
instr->i_opcode == RETURN_VALUE ||
7145-
instr->i_opcode == RAISE_VARARGS ||
7146-
instr->i_opcode == RERAISE)
7164+
if (IS_UNCONDITIONAL_JUMP_OPCODE(instr->i_opcode) ||
7165+
IS_SCOPE_EXIT_OPCODE(instr->i_opcode))
71477166
{
71487167
/* remaining code is dead */
71497168
next = NULL;
@@ -7154,7 +7173,7 @@ stackdepth(struct compiler *c, basicblock *entry)
71547173
}
71557174
}
71567175
if (next != NULL) {
7157-
assert(b->b_nofallthrough == 0);
7176+
assert(BB_HAS_FALLTHROUGH(b));
71587177
stackdepth_push(&sp, next, depth);
71597178
}
71607179
}
@@ -7309,7 +7328,7 @@ label_exception_targets(basicblock *entry) {
73097328
instr->i_except = handler;
73107329
assert(i == b->b_iused -1);
73117330
if (!instr->i_target->b_visited) {
7312-
if (b->b_nofallthrough == 0) {
7331+
if (BB_HAS_FALLTHROUGH(b)) {
73137332
ExceptStack *copy = copy_except_stack(except_stack);
73147333
if (copy == NULL) {
73157334
goto error;
@@ -7329,7 +7348,7 @@ label_exception_targets(basicblock *entry) {
73297348
instr->i_except = handler;
73307349
}
73317350
}
7332-
if (b->b_nofallthrough == 0 && !b->b_next->b_visited) {
7351+
if (BB_HAS_FALLTHROUGH(b) && !b->b_next->b_visited) {
73337352
assert(except_stack != NULL);
73347353
b->b_next->b_exceptstack = except_stack;
73357354
todo[0] = b->b_next;
@@ -7368,7 +7387,7 @@ mark_warm(basicblock *entry) {
73687387
assert(!b->b_except_predecessors);
73697388
b->b_warm = 1;
73707389
basicblock *next = b->b_next;
7371-
if (next && !b->b_nofallthrough && !next->b_visited) {
7390+
if (next && BB_HAS_FALLTHROUGH(b) && !next->b_visited) {
73727391
*sp++ = next;
73737392
next->b_visited = 1;
73747393
}
@@ -7412,7 +7431,7 @@ mark_cold(basicblock *entry) {
74127431
basicblock *b = *(--sp);
74137432
b->b_cold = 1;
74147433
basicblock *next = b->b_next;
7415-
if (next && !b->b_nofallthrough) {
7434+
if (next && BB_HAS_FALLTHROUGH(b)) {
74167435
if (!next->b_warm && !next->b_visited) {
74177436
*sp++ = next;
74187437
next->b_visited = 1;
@@ -7447,15 +7466,14 @@ push_cold_blocks_to_end(struct compiler *c, basicblock *entry, int code_flags) {
74477466
/* If we have a cold block with fallthrough to a warm block, add */
74487467
/* an explicit jump instead of fallthrough */
74497468
for (basicblock *b = entry; b != NULL; b = b->b_next) {
7450-
if (b->b_cold && !b->b_nofallthrough && b->b_next && b->b_next->b_warm) {
7469+
if (b->b_cold && BB_HAS_FALLTHROUGH(b) && b->b_next && b->b_next->b_warm) {
74517470
basicblock *explicit_jump = compiler_new_block(c);
74527471
if (explicit_jump == NULL) {
74537472
return -1;
74547473
}
74557474
basicblock_add_jump(explicit_jump, JUMP, -1, 0, 0, 0, b->b_next);
74567475

74577476
explicit_jump->b_cold = 1;
7458-
explicit_jump->b_nofallthrough = 1;
74597477
explicit_jump->b_next = b->b_next;
74607478
b->b_next = explicit_jump;
74617479
}
@@ -7948,7 +7966,7 @@ scan_block_for_local(int target, basicblock *b, bool unsafe_to_start,
79487966
if (unsafe) {
79497967
// unsafe at end of this block,
79507968
// so unsafe at start of next blocks
7951-
if (b->b_next && !b->b_nofallthrough) {
7969+
if (b->b_next && BB_HAS_FALLTHROUGH(b)) {
79527970
MAYBE_PUSH(b->b_next);
79537971
}
79547972
if (b->b_iused > 0) {
@@ -8276,9 +8294,10 @@ dump_instr(struct instr *i)
82768294
static void
82778295
dump_basicblock(const basicblock *b)
82788296
{
8279-
const char *b_return = b->b_return ? "return " : "";
8297+
const char *b_return = basicblock_returns(b) ? "return " : "";
82808298
fprintf(stderr, "[%d %d %d %p] used: %d, depth: %d, offset: %d %s\n",
8281-
b->b_cold, b->b_warm, b->b_nofallthrough, b, b->b_iused, b->b_startdepth, b->b_offset, b_return);
8299+
b->b_cold, b->b_warm, BB_NO_FALLTHROUGH(b), b, b->b_iused,
8300+
b->b_startdepth, b->b_offset, b_return);
82828301
if (b->b_instr) {
82838302
int i;
82848303
for (i = 0; i < b->b_iused; i++) {
@@ -8540,7 +8559,6 @@ remove_redundant_jumps(basicblock *entry) {
85408559
b_last_instr->i_opcode == JUMP_NO_INTERRUPT) {
85418560
if (b_last_instr->i_target == b->b_next) {
85428561
assert(b->b_next->b_iused);
8543-
b->b_nofallthrough = 0;
85448562
b_last_instr->i_opcode = NOP;
85458563
removed++;
85468564
}
@@ -8567,7 +8585,7 @@ assemble(struct compiler *c, int addNone)
85678585
}
85688586

85698587
/* Make sure every block that falls off the end returns None. */
8570-
if (!c->u->u_curblock->b_return) {
8588+
if (!basicblock_returns(c->u->u_curblock)) {
85718589
UNSET_LOC(c);
85728590
if (addNone)
85738591
ADDOP_LOAD_CONST(c, Py_None);
@@ -9059,7 +9077,6 @@ optimize_basic_block(struct compiler *c, basicblock *bb, PyObject *consts)
90599077
jump_if_true = nextop == POP_JUMP_IF_TRUE;
90609078
if (is_true == jump_if_true) {
90619079
bb->b_instr[i+1].i_opcode = JUMP;
9062-
bb->b_nofallthrough = 1;
90639080
}
90649081
else {
90659082
bb->b_instr[i+1].i_opcode = NOP;
@@ -9079,7 +9096,6 @@ optimize_basic_block(struct compiler *c, basicblock *bb, PyObject *consts)
90799096
jump_if_true = nextop == JUMP_IF_TRUE_OR_POP;
90809097
if (is_true == jump_if_true) {
90819098
bb->b_instr[i+1].i_opcode = JUMP;
9082-
bb->b_nofallthrough = 1;
90839099
}
90849100
else {
90859101
inst->i_opcode = NOP;
@@ -9268,7 +9284,7 @@ extend_block(basicblock *bb) {
92689284
last->i_opcode != JUMP_BACKWARD) {
92699285
return 0;
92709286
}
9271-
if (last->i_target->b_exit && last->i_target->b_iused <= MAX_COPY_SIZE) {
9287+
if (basicblock_exits_scope(last->i_target) && last->i_target->b_iused <= MAX_COPY_SIZE) {
92729288
basicblock *to_copy = last->i_target;
92739289
last->i_opcode = NOP;
92749290
for (int i = 0; i < to_copy->b_iused; i++) {
@@ -9278,7 +9294,6 @@ extend_block(basicblock *bb) {
92789294
}
92799295
bb->b_instr[index] = to_copy->b_instr[i];
92809296
}
9281-
bb->b_exit = 1;
92829297
}
92839298
return 0;
92849299
}
@@ -9336,34 +9351,21 @@ normalize_basic_block(basicblock *bb) {
93369351
/* Mark blocks as exit and/or nofallthrough.
93379352
Raise SystemError if CFG is malformed. */
93389353
for (int i = 0; i < bb->b_iused; i++) {
9339-
assert(!IS_ASSEMBLER_OPCODE(bb->b_instr[i].i_opcode));
9340-
switch(bb->b_instr[i].i_opcode) {
9341-
case RETURN_VALUE:
9342-
case RAISE_VARARGS:
9343-
case RERAISE:
9344-
bb->b_exit = 1;
9345-
bb->b_nofallthrough = 1;
9346-
break;
9347-
case JUMP:
9348-
case JUMP_NO_INTERRUPT:
9349-
bb->b_nofallthrough = 1;
9350-
/* fall through */
9351-
case POP_JUMP_IF_NOT_NONE:
9352-
case POP_JUMP_IF_NONE:
9353-
case POP_JUMP_IF_FALSE:
9354-
case POP_JUMP_IF_TRUE:
9355-
case JUMP_IF_FALSE_OR_POP:
9356-
case JUMP_IF_TRUE_OR_POP:
9357-
case FOR_ITER:
9358-
if (i != bb->b_iused-1) {
9359-
PyErr_SetString(PyExc_SystemError, "malformed control flow graph.");
9360-
return -1;
9361-
}
9362-
/* Skip over empty basic blocks. */
9363-
while (bb->b_instr[i].i_target->b_iused == 0) {
9364-
bb->b_instr[i].i_target = bb->b_instr[i].i_target->b_next;
9365-
}
9366-
9354+
int opcode = bb->b_instr[i].i_opcode;
9355+
assert(!IS_ASSEMBLER_OPCODE(opcode));
9356+
int is_jump = IS_JUMP_OPCODE(opcode);
9357+
int is_exit = IS_SCOPE_EXIT_OPCODE(opcode);
9358+
if (is_exit || is_jump) {
9359+
if (i != bb->b_iused-1) {
9360+
PyErr_SetString(PyExc_SystemError, "malformed control flow graph.");
9361+
return -1;
9362+
}
9363+
}
9364+
if (is_jump) {
9365+
/* Skip over empty basic blocks. */
9366+
while (bb->b_instr[i].i_target->b_iused == 0) {
9367+
bb->b_instr[i].i_target = bb->b_instr[i].i_target->b_next;
9368+
}
93679369
}
93689370
}
93699371
return 0;
@@ -9381,7 +9383,7 @@ mark_reachable(struct assembler *a) {
93819383
while (sp > stack) {
93829384
basicblock *b = *(--sp);
93839385
b->b_visited = 1;
9384-
if (b->b_next && !b->b_nofallthrough) {
9386+
if (b->b_next && BB_HAS_FALLTHROUGH(b)) {
93859387
if (!b->b_next->b_visited) {
93869388
assert(b->b_next->b_predecessors == 0);
93879389
*sp++ = b->b_next;
@@ -9470,7 +9472,7 @@ propagate_line_numbers(struct assembler *a) {
94709472
COPY_INSTR_LOC(b->b_instr[i], prev_instr);
94719473
}
94729474
}
9473-
if (!b->b_nofallthrough && b->b_next->b_predecessors == 1) {
9475+
if (BB_HAS_FALLTHROUGH(b) && b->b_next->b_predecessors == 1) {
94749476
assert(b->b_next->b_iused);
94759477
if (b->b_next->b_instr[0].i_lineno < 0) {
94769478
COPY_INSTR_LOC(prev_instr, b->b_next->b_instr[0]);
@@ -9518,7 +9520,6 @@ optimize_cfg(struct compiler *c, struct assembler *a, PyObject *consts)
95189520
for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
95199521
if (b->b_predecessors == 0) {
95209522
b->b_iused = 0;
9521-
b->b_nofallthrough = 0;
95229523
}
95239524
}
95249525
eliminate_empty_basic_blocks(a->a_entry);
@@ -9558,7 +9559,7 @@ trim_unused_consts(struct assembler *a, PyObject *consts)
95589559

95599560
static inline int
95609561
is_exit_without_lineno(basicblock *b) {
9561-
if (!b->b_exit) {
9562+
if (!basicblock_exits_scope(b)) {
95629563
return 0;
95639564
}
95649565
for (int i = 0; i < b->b_iused; i++) {
@@ -9609,7 +9610,7 @@ duplicate_exits_without_lineno(struct compiler *c)
96099610
/* Any remaining reachable exit blocks without line number can only be reached by
96109611
* fall through, and thus can only have a single predecessor */
96119612
for (basicblock *b = c->u->u_blocks; b != NULL; b = b->b_list) {
9612-
if (!b->b_nofallthrough && b->b_next && b->b_iused > 0) {
9613+
if (BB_HAS_FALLTHROUGH(b) && b->b_next && b->b_iused > 0) {
96139614
if (is_exit_without_lineno(b->b_next)) {
96149615
assert(b->b_next->b_iused > 0);
96159616
COPY_INSTR_LOC(b->b_instr[b->b_iused-1], b->b_next->b_instr[0]);

0 commit comments

Comments
 (0)