@@ -212,14 +212,14 @@ basicblock_add_jump(basicblock *b, int opcode, basicblock *target, location loc)
212
212
}
213
213
214
214
static inline int
215
- basicblock_append_instructions (basicblock * target , basicblock * source )
215
+ basicblock_append_instructions (basicblock * to , basicblock * from )
216
216
{
217
- for (int i = 0 ; i < source -> b_iused ; i ++ ) {
218
- int n = basicblock_next_instr (target );
217
+ for (int i = 0 ; i < from -> b_iused ; i ++ ) {
218
+ int n = basicblock_next_instr (to );
219
219
if (n < 0 ) {
220
220
return ERROR ;
221
221
}
222
- target -> b_instr [n ] = source -> b_instr [i ];
222
+ to -> b_instr [n ] = from -> b_instr [i ];
223
223
}
224
224
return SUCCESS ;
225
225
}
@@ -292,9 +292,9 @@ static void
292
292
dump_basicblock (const basicblock * b )
293
293
{
294
294
const char * b_return = basicblock_returns (b ) ? "return " : "" ;
295
- fprintf (stderr , "%d: [EH=%d CLD=%d WRM=%d NO_FT=%d %p] used: %d, depth: %d, %s\n" ,
295
+ fprintf (stderr , "%d: [EH=%d CLD=%d WRM=%d NO_FT=%d %p] used: %d, depth: %d, preds: %d %s\n" ,
296
296
b -> b_label .id , b -> b_except_handler , b -> b_cold , b -> b_warm , BB_NO_FALLTHROUGH (b ), b , b -> b_iused ,
297
- b -> b_startdepth , b_return );
297
+ b -> b_startdepth , b -> b_predecessors , b_return );
298
298
if (b -> b_instr ) {
299
299
int i ;
300
300
for (i = 0 ; i < b -> b_iused ; i ++ ) {
@@ -1165,15 +1165,26 @@ remove_redundant_jumps(cfg_builder *g) {
1165
1165
return changes ;
1166
1166
}
1167
1167
1168
+ static inline bool
1169
+ basicblock_has_no_lineno (basicblock * b ) {
1170
+ for (int i = 0 ; i < b -> b_iused ; i ++ ) {
1171
+ if (b -> b_instr [i ].i_loc .lineno >= 0 ) {
1172
+ return false;
1173
+ }
1174
+ }
1175
+ return true;
1176
+ }
1177
+
1168
1178
/* Maximum size of basic block that should be copied in optimizer */
1169
1179
#define MAX_COPY_SIZE 4
1170
1180
1171
- /* If this block ends with an unconditional jump to a small exit block, then
1181
+ /* If this block ends with an unconditional jump to a small exit block or
1182
+ * a block that has no line numbers (and no fallthrough), then
1172
1183
* remove the jump and extend this block with the target.
1173
1184
* Returns 1 if extended, 0 if no change, and -1 on error.
1174
1185
*/
1175
1186
static int
1176
- inline_small_exit_blocks (basicblock * bb ) {
1187
+ basicblock_inline_small_or_no_lineno_blocks (basicblock * bb ) {
1177
1188
cfg_instr * last = basicblock_last_instr (bb );
1178
1189
if (last == NULL ) {
1179
1190
return 0 ;
@@ -1182,14 +1193,46 @@ inline_small_exit_blocks(basicblock *bb) {
1182
1193
return 0 ;
1183
1194
}
1184
1195
basicblock * target = last -> i_target ;
1185
- if (basicblock_exits_scope (target ) && target -> b_iused <= MAX_COPY_SIZE ) {
1196
+ bool small_exit_block = (basicblock_exits_scope (target ) &&
1197
+ target -> b_iused <= MAX_COPY_SIZE );
1198
+ bool no_lineno_no_fallthrough = (basicblock_has_no_lineno (target ) &&
1199
+ !BB_HAS_FALLTHROUGH (target ));
1200
+ if (small_exit_block || no_lineno_no_fallthrough ) {
1201
+ assert (is_jump (last ));
1202
+ int removed_jump_opcode = last -> i_opcode ;
1186
1203
INSTR_SET_OP0 (last , NOP );
1187
1204
RETURN_IF_ERROR (basicblock_append_instructions (bb , target ));
1205
+ if (no_lineno_no_fallthrough ) {
1206
+ last = basicblock_last_instr (bb );
1207
+ if (IS_UNCONDITIONAL_JUMP_OPCODE (last -> i_opcode ) &&
1208
+ removed_jump_opcode == JUMP )
1209
+ {
1210
+ /* Make sure we don't lose eval breaker checks */
1211
+ last -> i_opcode = JUMP ;
1212
+ }
1213
+ }
1214
+ target -> b_predecessors -- ;
1188
1215
return 1 ;
1189
1216
}
1190
1217
return 0 ;
1191
1218
}
1192
1219
1220
+ static int
1221
+ inline_small_or_no_lineno_blocks (basicblock * entryblock ) {
1222
+ bool changes ;
1223
+ do {
1224
+ changes = false;
1225
+ for (basicblock * b = entryblock ; b != NULL ; b = b -> b_next ) {
1226
+ int res = basicblock_inline_small_or_no_lineno_blocks (b );
1227
+ RETURN_IF_ERROR (res );
1228
+ if (res ) {
1229
+ changes = true;
1230
+ }
1231
+ }
1232
+ } while (changes ); /* every change removes a jump, ensuring convergence */
1233
+ return changes ;
1234
+ }
1235
+
1193
1236
// Attempt to eliminate jumps to jumps by updating inst to jump to
1194
1237
// target->i_target using the provided opcode. Return whether or not the
1195
1238
// optimization was successful.
@@ -1804,19 +1847,14 @@ optimize_cfg(cfg_builder *g, PyObject *consts, PyObject *const_cache, int firstl
1804
1847
{
1805
1848
assert (PyDict_CheckExact (const_cache ));
1806
1849
RETURN_IF_ERROR (check_cfg (g ));
1807
- for (basicblock * b = g -> g_entryblock ; b != NULL ; b = b -> b_next ) {
1808
- RETURN_IF_ERROR (inline_small_exit_blocks (b ));
1809
- }
1850
+ RETURN_IF_ERROR (inline_small_or_no_lineno_blocks (g -> g_entryblock ));
1810
1851
RETURN_IF_ERROR (remove_unreachable (g -> g_entryblock ));
1811
1852
RETURN_IF_ERROR (resolve_line_numbers (g , firstlineno ));
1812
1853
RETURN_IF_ERROR (optimize_load_const (const_cache , g , consts ));
1813
1854
for (basicblock * b = g -> g_entryblock ; b != NULL ; b = b -> b_next ) {
1814
1855
RETURN_IF_ERROR (optimize_basic_block (const_cache , b , consts ));
1815
1856
}
1816
1857
RETURN_IF_ERROR (remove_redundant_nops_and_pairs (g -> g_entryblock ));
1817
- for (basicblock * b = g -> g_entryblock ; b != NULL ; b = b -> b_next ) {
1818
- RETURN_IF_ERROR (inline_small_exit_blocks (b ));
1819
- }
1820
1858
RETURN_IF_ERROR (remove_unreachable (g -> g_entryblock ));
1821
1859
1822
1860
int removed_nops , removed_jumps ;
@@ -2333,12 +2371,7 @@ convert_pseudo_ops(cfg_builder *g)
2333
2371
static inline bool
2334
2372
is_exit_or_eval_check_without_lineno (basicblock * b ) {
2335
2373
if (basicblock_exits_scope (b ) || basicblock_has_eval_break (b )) {
2336
- for (int i = 0 ; i < b -> b_iused ; i ++ ) {
2337
- if (b -> b_instr [i ].i_loc .lineno >= 0 ) {
2338
- return false;
2339
- }
2340
- }
2341
- return true;
2374
+ return basicblock_has_no_lineno (b );
2342
2375
}
2343
2376
else {
2344
2377
return false;
0 commit comments