mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 21:51:50 +00:00 
			
		
		
		
	gh-113603: Compiler no longer tries to maintain the no-empty-block invariant (#113636)
This commit is contained in:
		
							parent
							
								
									0c3455a969
								
							
						
					
					
						commit
						7d01fb4808
					
				
					 3 changed files with 52 additions and 78 deletions
				
			
		|  | @ -449,6 +449,15 @@ _PyCfgBuilder_Addop(cfg_builder *g, int opcode, int oparg, location loc) | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| static basicblock * | ||||
| next_nonempty_block(basicblock *b) | ||||
| { | ||||
|     while (b && b->b_iused == 0) { | ||||
|         b = b->b_next; | ||||
|     } | ||||
|     return b; | ||||
| } | ||||
| 
 | ||||
| /***** debugging helpers *****/ | ||||
| 
 | ||||
| #ifndef NDEBUG | ||||
|  | @ -464,24 +473,16 @@ no_redundant_nops(cfg_builder *g) { | |||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
| no_empty_basic_blocks(cfg_builder *g) { | ||||
|     for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { | ||||
|         if (b->b_iused == 0) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
| no_redundant_jumps(cfg_builder *g) { | ||||
|     for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { | ||||
|         cfg_instr *last = basicblock_last_instr(b); | ||||
|         if (last != NULL) { | ||||
|             if (IS_UNCONDITIONAL_JUMP_OPCODE(last->i_opcode)) { | ||||
|                 assert(last->i_target != b->b_next); | ||||
|                 if (last->i_target == b->b_next) { | ||||
|                 basicblock *next = next_nonempty_block(b->b_next); | ||||
|                 basicblock *jump_target = next_nonempty_block(last->i_target); | ||||
|                 assert(jump_target != next); | ||||
|                 if (jump_target == next) { | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|  | @ -961,42 +962,6 @@ mark_reachable(basicblock *entryblock) { | |||
|     return SUCCESS; | ||||
| } | ||||
| 
 | ||||
| static void | ||||
| eliminate_empty_basic_blocks(cfg_builder *g) { | ||||
|     /* Eliminate empty blocks */ | ||||
|     for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { | ||||
|         basicblock *next = b->b_next; | ||||
|         while (next && next->b_iused == 0) { | ||||
|             next = next->b_next; | ||||
|         } | ||||
|         b->b_next = next; | ||||
|     } | ||||
|     while(g->g_entryblock && g->g_entryblock->b_iused == 0) { | ||||
|         g->g_entryblock = g->g_entryblock->b_next; | ||||
|     } | ||||
|     int next_lbl = get_max_label(g->g_entryblock) + 1; | ||||
|     for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { | ||||
|         assert(b->b_iused > 0); | ||||
|         for (int i = 0; i < b->b_iused; i++) { | ||||
|             cfg_instr *instr = &b->b_instr[i]; | ||||
|             if (HAS_TARGET(instr->i_opcode)) { | ||||
|                 basicblock *target = instr->i_target; | ||||
|                 while (target->b_iused == 0) { | ||||
|                     target = target->b_next; | ||||
|                 } | ||||
|                 if (instr->i_target != target) { | ||||
|                     if (!IS_LABEL(target->b_label)) { | ||||
|                         target->b_label.id = next_lbl++; | ||||
|                     } | ||||
|                     instr->i_target = target; | ||||
|                     instr->i_oparg = target->b_label.id; | ||||
|                 } | ||||
|                 assert(instr->i_target && instr->i_target->b_iused > 0); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static int | ||||
| remove_redundant_nops(basicblock *bb) { | ||||
|     /* Remove NOPs when legal to do so. */ | ||||
|  | @ -1025,10 +990,7 @@ remove_redundant_nops(basicblock *bb) { | |||
|                 } | ||||
|             } | ||||
|             else { | ||||
|                 basicblock* next = bb->b_next; | ||||
|                 while (next && next->b_iused == 0) { | ||||
|                     next = next->b_next; | ||||
|                 } | ||||
|                 basicblock *next = next_nonempty_block(bb->b_next); | ||||
|                 /* or if last instruction in BB and next BB has same line number */ | ||||
|                 if (next) { | ||||
|                     location next_loc = NO_LOCATION; | ||||
|  | @ -1112,25 +1074,22 @@ remove_redundant_jumps(cfg_builder *g) { | |||
|      * can be deleted. | ||||
|      */ | ||||
| 
 | ||||
|     assert(no_empty_basic_blocks(g)); | ||||
| 
 | ||||
|     bool remove_empty_blocks = false; | ||||
|     for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { | ||||
|         cfg_instr *last = basicblock_last_instr(b); | ||||
|         assert(last != NULL); | ||||
|         if (last == NULL) { | ||||
|             continue; | ||||
|         } | ||||
|         assert(!IS_ASSEMBLER_OPCODE(last->i_opcode)); | ||||
|         if (IS_UNCONDITIONAL_JUMP_OPCODE(last->i_opcode)) { | ||||
|             if (last->i_target == NULL) { | ||||
|             basicblock* jump_target = next_nonempty_block(last->i_target); | ||||
|             if (jump_target == NULL) { | ||||
|                 PyErr_SetString(PyExc_SystemError, "jump with NULL target"); | ||||
|                 return ERROR; | ||||
|             } | ||||
|             if (last->i_target == b->b_next) { | ||||
|                 assert(b->b_next->b_iused); | ||||
|             basicblock *next = next_nonempty_block(b->b_next); | ||||
|             if (jump_target == next) { | ||||
|                 if (last->i_loc.lineno == NO_LOCATION.lineno) { | ||||
|                     b->b_iused--; | ||||
|                     if (b->b_iused == 0) { | ||||
|                         remove_empty_blocks = true; | ||||
|                     } | ||||
|                 } | ||||
|                 else { | ||||
|                     INSTR_SET_OP0(last, NOP); | ||||
|  | @ -1138,10 +1097,6 @@ remove_redundant_jumps(cfg_builder *g) { | |||
|             } | ||||
|         } | ||||
|     } | ||||
|     if (remove_empty_blocks) { | ||||
|         eliminate_empty_basic_blocks(g); | ||||
|     } | ||||
|     assert(no_empty_basic_blocks(g)); | ||||
|     return SUCCESS; | ||||
| } | ||||
| 
 | ||||
|  | @ -1749,11 +1704,9 @@ optimize_cfg(cfg_builder *g, PyObject *consts, PyObject *const_cache) | |||
| { | ||||
|     assert(PyDict_CheckExact(const_cache)); | ||||
|     RETURN_IF_ERROR(check_cfg(g)); | ||||
|     eliminate_empty_basic_blocks(g); | ||||
|     for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { | ||||
|         RETURN_IF_ERROR(inline_small_exit_blocks(b)); | ||||
|     } | ||||
|     assert(no_empty_basic_blocks(g)); | ||||
|     for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { | ||||
|         RETURN_IF_ERROR(optimize_basic_block(const_cache, b, consts)); | ||||
|         assert(b->b_predecessors == 0); | ||||
|  | @ -1768,14 +1721,21 @@ optimize_cfg(cfg_builder *g, PyObject *consts, PyObject *const_cache) | |||
|     for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { | ||||
|        if (b->b_predecessors == 0) { | ||||
|             b->b_iused = 0; | ||||
|             b->b_except_handler = 0; | ||||
|        } | ||||
|     } | ||||
|     for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { | ||||
|         remove_redundant_nops(b); | ||||
|     } | ||||
|     eliminate_empty_basic_blocks(g); | ||||
|     assert(no_redundant_nops(g)); | ||||
|     RETURN_IF_ERROR(remove_redundant_jumps(g)); | ||||
| 
 | ||||
|     for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { | ||||
|         remove_redundant_nops(b); | ||||
|     } | ||||
| 
 | ||||
|     RETURN_IF_ERROR(remove_redundant_jumps(g)); | ||||
| 
 | ||||
|     assert(no_redundant_jumps(g)); | ||||
|     return SUCCESS; | ||||
| } | ||||
| 
 | ||||
|  | @ -1825,7 +1785,6 @@ insert_superinstructions(cfg_builder *g) | |||
|     for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) { | ||||
|         remove_redundant_nops(b); | ||||
|     } | ||||
|     eliminate_empty_basic_blocks(g); | ||||
|     assert(no_redundant_nops(g)); | ||||
| } | ||||
| 
 | ||||
|  | @ -2299,8 +2258,6 @@ is_exit_without_lineno(basicblock *b) { | |||
| static int | ||||
| duplicate_exits_without_lineno(cfg_builder *g) | ||||
| { | ||||
|     assert(no_empty_basic_blocks(g)); | ||||
| 
 | ||||
|     int next_lbl = get_max_label(g->g_entryblock) + 1; | ||||
| 
 | ||||
|     /* Copy all exit blocks without line number that are targets of a jump.
 | ||||
|  | @ -2308,9 +2265,11 @@ duplicate_exits_without_lineno(cfg_builder *g) | |||
|     basicblock *entryblock = g->g_entryblock; | ||||
|     for (basicblock *b = entryblock; b != NULL; b = b->b_next) { | ||||
|         cfg_instr *last = basicblock_last_instr(b); | ||||
|         assert(last != NULL); | ||||
|         if (last == NULL) { | ||||
|             continue; | ||||
|         } | ||||
|         if (is_jump(last)) { | ||||
|             basicblock *target = last->i_target; | ||||
|             basicblock *target = next_nonempty_block(last->i_target); | ||||
|             if (is_exit_without_lineno(target) && target->b_predecessors > 1) { | ||||
|                 basicblock *new_target = copy_basicblock(g, target); | ||||
|                 if (new_target == NULL) { | ||||
|  | @ -2367,9 +2326,10 @@ propagate_line_numbers(basicblock *entryblock) { | |||
|             } | ||||
|         } | ||||
|         if (BB_HAS_FALLTHROUGH(b) && b->b_next->b_predecessors == 1) { | ||||
|             assert(b->b_next->b_iused); | ||||
|             if (b->b_next->b_instr[0].i_loc.lineno < 0) { | ||||
|                 b->b_next->b_instr[0].i_loc = prev_location; | ||||
|             if (b->b_next->b_iused > 0) { | ||||
|                 if (b->b_next->b_instr[0].i_loc.lineno < 0) { | ||||
|                     b->b_next->b_instr[0].i_loc = prev_location; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (is_jump(last)) { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Irit Katriel
						Irit Katriel