Skip to content

Commit bb9166b

Browse files
ljfpCopilot
andcommitted
pythongh-133672: Allow LOAD_FAST to be optimized to LOAD_FAST_BORROW
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 85d3bcd commit bb9166b

4 files changed

Lines changed: 542 additions & 11 deletions

File tree

Lib/test/test_dis.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ def _with(c):
542542
dis_with = """\
543543
%4d RESUME 0
544544
545-
%4d LOAD_FAST_BORROW 0 (c)
545+
%4d LOAD_FAST 0 (c)
546546
COPY 1
547547
LOAD_SPECIAL 1 (__exit__)
548548
SWAP 2
@@ -1922,7 +1922,7 @@ def _prepare_test_cases():
19221922
make_inst(opname='LOAD_SMALL_INT', arg=0, argval=0, argrepr='', offset=244, start_offset=244, starts_line=False, line_number=21),
19231923
make_inst(opname='BINARY_OP', arg=11, argval=11, argrepr='/', offset=246, start_offset=246, starts_line=False, line_number=21, cache_info=[('counter', 1, b'\x00\x00'), ('descr', 4, b'\x00\x00\x00\x00\x00\x00\x00\x00')]),
19241924
make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=258, start_offset=258, starts_line=False, line_number=21),
1925-
make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=260, start_offset=260, starts_line=True, line_number=25),
1925+
make_inst(opname='LOAD_FAST', arg=0, argval='i', argrepr='i', offset=260, start_offset=260, starts_line=True, line_number=25),
19261926
make_inst(opname='COPY', arg=1, argval=1, argrepr='', offset=262, start_offset=262, starts_line=False, line_number=25),
19271927
make_inst(opname='LOAD_SPECIAL', arg=1, argval=1, argrepr='__exit__', offset=264, start_offset=264, starts_line=False, line_number=25),
19281928
make_inst(opname='SWAP', arg=2, argval=2, argrepr='', offset=266, start_offset=266, starts_line=False, line_number=25),

Lib/test/test_peepholer.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2598,6 +2598,36 @@ def test_for_iter(self):
25982598
("LOAD_CONST", 0, 7),
25992599
("RETURN_VALUE", None, 8),
26002600
]
2601+
expected = [
2602+
("LOAD_FAST_BORROW", 0, 1),
2603+
top := self.Label(),
2604+
("FOR_ITER", end := self.Label(), 2),
2605+
("STORE_FAST", 2, 3),
2606+
("JUMP", top, 4),
2607+
end,
2608+
("END_FOR", None, 5),
2609+
("POP_TOP", None, 6),
2610+
("LOAD_CONST", 0, 7),
2611+
("RETURN_VALUE", None, 8),
2612+
]
2613+
self.cfg_optimization_test(insts, expected, consts=[None])
2614+
2615+
def test_for_iter_checks_fallthrough(self):
2616+
insts = [
2617+
("LOAD_FAST", 0, 1),
2618+
("GET_ITER", 0, 2),
2619+
top := self.Label(),
2620+
("FOR_ITER", end := self.Label(), 3),
2621+
("STORE_FAST", 1, 4),
2622+
("LOAD_CONST", 0, 5),
2623+
("STORE_FAST", 0, 6),
2624+
("JUMP", top, 7),
2625+
end,
2626+
("END_FOR", None, 8),
2627+
("POP_ITER", None, 9),
2628+
("LOAD_CONST", 0, 10),
2629+
("RETURN_VALUE", None, 11),
2630+
]
26012631
self.cfg_optimization_test(insts, insts, consts=[None])
26022632

26032633
def test_load_attr(self):
@@ -2675,6 +2705,69 @@ def test_send(self):
26752705
]
26762706
self.cfg_optimization_test(insts, expected, consts=[None])
26772707

2708+
def test_borrow_checks_exception_handler_edges(self):
2709+
insts = [
2710+
("LOAD_FAST", 0, 1),
2711+
("JUMP", start := self.Label(), 2),
2712+
start,
2713+
("SETUP_FINALLY", handler := self.Label(), 3),
2714+
("LOAD_CONST", 0, 4),
2715+
("BINARY_OP", 0, 5),
2716+
("POP_BLOCK", None, 6),
2717+
("RETURN_VALUE", None, 7),
2718+
handler,
2719+
("STORE_FAST", 1, 8),
2720+
("LOAD_CONST", 0, 9),
2721+
("STORE_FAST", 0, 10),
2722+
("POP_TOP", None, 11),
2723+
("LOAD_CONST", 0, 12),
2724+
("RETURN_VALUE", None, 13),
2725+
]
2726+
expected = [
2727+
("LOAD_FAST", 0, 1),
2728+
("NOP", None, 2),
2729+
("SETUP_FINALLY", handler := self.Label(), 3),
2730+
("LOAD_CONST", 0, 4),
2731+
("BINARY_OP", 0, 5),
2732+
("NOP", None, 6),
2733+
("RETURN_VALUE", None, 7),
2734+
handler,
2735+
("STORE_FAST", 1, 8),
2736+
("LOAD_CONST", 0, 9),
2737+
("STORE_FAST", 0, 10),
2738+
("POP_TOP", None, 11),
2739+
("LOAD_CONST", 0, 12),
2740+
("RETURN_VALUE", None, 13),
2741+
]
2742+
self.cfg_optimization_test(insts, expected, consts=[None])
2743+
2744+
def test_copied_reference_is_not_borrowed(self):
2745+
insts = [
2746+
("LOAD_FAST", 0, 1),
2747+
("COPY", 1, 1),
2748+
("TO_BOOL", None, 1),
2749+
("POP_JUMP_IF_TRUE", target := self.Label(), 1),
2750+
("POP_TOP", None, 1),
2751+
("LOAD_FAST", 1, 1),
2752+
target,
2753+
("STORE_FAST", 0, 1),
2754+
("LOAD_CONST", 0, 2),
2755+
("RETURN_VALUE", None, 2),
2756+
]
2757+
expected = [
2758+
("LOAD_FAST", 0, 1),
2759+
("COPY", 1, 1),
2760+
("TO_BOOL", None, 1),
2761+
("POP_JUMP_IF_TRUE", target := self.Label(), 1),
2762+
("POP_TOP", None, 1),
2763+
("LOAD_FAST", 1, 1),
2764+
target,
2765+
("STORE_FAST", 0, 1),
2766+
("LOAD_CONST", 0, 2),
2767+
("RETURN_VALUE", None, 2),
2768+
]
2769+
self.cfg_optimization_test(insts, expected, consts=[None])
2770+
26782771
def test_format_simple(self):
26792772
# FORMAT_SIMPLE will leave its operand on the stack if it's a unicode
26802773
# object. We treat it conservatively and assume that it always leaves
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The peephole optimizer now converts LOAD_FAST to LOAD_FAST_BORROW more aggressively. This optimization is applied even if the loaded variable remains live on the stack at the end of a basic block, as long as the borrow is otherwise determined to be safe. This can reduce reference counting overhead in certain code patterns, such as with iterables in loops.

0 commit comments

Comments
 (0)