| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1104,6 +1104,17 @@ async def test(aseq): | |||
| 1104 | 1104 | code_lines = self.get_code_lines(test.__code__) | |
| 1105 | 1105 | self.assertEqual(expected_lines, code_lines) | |
| 1106 | 1106 | ||
| 1107 | + def check_line_numbers(self, code, opnames=None): | ||
| 1108 | + # Check that all instructions whose op matches opnames | ||
| 1109 | + # have a line number. opnames can be a single name, or | ||
| 1110 | + # a sequence of names. If it is None, match all ops. | ||
| 1111 | + | ||
| 1112 | + if isinstance(opnames, str): | ||
| 1113 | + opnames = (opnames, ) | ||
| 1114 | + for inst in dis.Bytecode(code): | ||
| 1115 | + if opnames and inst.opname in opnames: | ||
| 1116 | + self.assertIsNotNone(inst.positions.lineno) | ||
| 1117 | + | ||
| 1107 | 1118 | def test_line_number_synthetic_jump_multiple_predecessors(self): | |
| 1108 | 1119 | def f(): | |
| 1109 | 1120 | for x in it: | |
@@ -1113,25 +1124,52 @@ def f(): | |||
| 1113 | 1124 | except OSError: | |
| 1114 | 1125 | pass | |
| 1115 | 1126 | ||
| 1116 | - # Ensure that all JUMP_BACKWARDs have line number | ||
| 1117 | - code = f.__code__ | ||
| 1118 | - for inst in dis.Bytecode(code): | ||
| 1119 | - if inst.opname == 'JUMP_BACKWARD': | ||
| 1120 | - self.assertIsNotNone(inst.positions.lineno) | ||
| 1127 | + self.check_line_numbers(f.__code__, 'JUMP_BACKWARD') | ||
| 1121 | 1128 | ||
| 1122 | - def test_lineno_of_backward_jump(self): | ||
| 1129 | + def test_line_number_synthetic_jump_multiple_predecessors_nested(self): | ||
| 1130 | + def f(): | ||
| 1131 | + for x in it: | ||
| 1132 | + try: | ||
| 1133 | + X = 3 | ||
| 1134 | + except OSError: | ||
| 1135 | + try: | ||
| 1136 | + if C3: | ||
| 1137 | + X = 4 | ||
| 1138 | + except OSError: | ||
| 1139 | + pass | ||
| 1140 | + return 42 | ||
| 1141 | + | ||
| 1142 | + self.check_line_numbers(f.__code__, 'JUMP_BACKWARD') | ||
| 1143 | + | ||
| 1144 | + def test_line_number_synthetic_jump_multiple_predecessors_more_nested(self): | ||
| 1145 | + def f(): | ||
| 1146 | + for x in it: | ||
| 1147 | + try: | ||
| 1148 | + X = 3 | ||
| 1149 | + except OSError: | ||
| 1150 | + try: | ||
| 1151 | + if C3: | ||
| 1152 | + if C4: | ||
| 1153 | + X = 4 | ||
| 1154 | + except OSError: | ||
| 1155 | + try: | ||
| 1156 | + if C3: | ||
| 1157 | + if C4: | ||
| 1158 | + X = 5 | ||
| 1159 | + except OSError: | ||
| 1160 | + pass | ||
| 1161 | + return 42 | ||
| 1162 | + | ||
| 1163 | + self.check_line_numbers(f.__code__, 'JUMP_BACKWARD') | ||
| 1164 | + | ||
| 1165 | + def test_lineno_of_backward_jump_conditional_in_loop(self): | ||
| 1123 | 1166 | # Issue gh-107901 | |
| 1124 | 1167 | def f(): | |
| 1125 | 1168 | for i in x: | |
| 1126 | 1169 | if y: | |
| 1127 | 1170 | pass | |
| 1128 | 1171 | ||
| 1129 | - linenos = list(inst.positions.lineno | ||
| 1130 | - for inst in dis.get_instructions(f.__code__) | ||
| 1131 | - if inst.opname == 'JUMP_BACKWARD') | ||
| 1132 | - | ||
| 1133 | - self.assertTrue(len(linenos) > 0) | ||
| 1134 | - self.assertTrue(all(l is not None for l in linenos)) | ||
| 1172 | + self.check_line_numbers(f.__code__, 'JUMP_BACKWARD') | ||
| 1135 | 1173 | ||
| 1136 | 1174 | def test_big_dict_literal(self): | |
| 1137 | 1175 | # The compiler has a flushing point in "compiler_dict" that calls compiles | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1466,9 +1466,8 @@ def func(): | |||
| 1466 | 1466 | ('branch', 'func', 4, 4), | |
| 1467 | 1467 | ('line', 'func', 5), | |
| 1468 | 1468 | ('line', 'meth', 1), | |
| 1469 | - ('jump', 'func', 5, 5), | ||
| 1470 | - ('jump', 'func', 5, '[offset=114]'), | ||
| 1471 | - ('branch', 'func', '[offset=120]', '[offset=124]'), | ||
| 1469 | + ('jump', 'func', 5, '[offset=118]'), | ||
| 1470 | + ('branch', 'func', '[offset=122]', '[offset=126]'), | ||
| 1472 | 1471 | ('line', 'get_events', 11)]) | |
| 1473 | 1472 | ||
| 1474 | 1473 | self.check_events(func, recorders = FLOW_AND_LINE_RECORDERS, expected = [ | |
@@ -1482,9 +1481,8 @@ def func(): | |||
| 1482 | 1481 | ('line', 'func', 5), | |
| 1483 | 1482 | ('line', 'meth', 1), | |
| 1484 | 1483 | ('return', 'meth', None), | |
| 1485 | - ('jump', 'func', 5, 5), | ||
| 1486 | - ('jump', 'func', 5, '[offset=114]'), | ||
| 1487 | - ('branch', 'func', '[offset=120]', '[offset=124]'), | ||
| 1484 | + ('jump', 'func', 5, '[offset=118]'), | ||
| 1485 | + ('branch', 'func', '[offset=122]', '[offset=126]'), | ||
| 1488 | 1486 | ('return', 'func', None), | |
| 1489 | 1487 | ('line', 'get_events', 11)]) | |
| 1490 | 1488 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -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; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments