Skip to content

gh-133672: Allow LOAD_FAST to be optimized to LOAD_FAST_BORROW #133721

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
35 changes: 26 additions & 9 deletions Lib/test/test_peepholer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2430,14 +2430,14 @@ def test_optimized(self):
]
self.check(insts, expected)

def test_unoptimized_if_unconsumed(self):
def test_optimize_unconsumed_when_is_safe(self):
insts = [
("LOAD_FAST", 0, 1),
("LOAD_FAST", 1, 2),
("POP_TOP", None, 3),
]
expected = [
("LOAD_FAST", 0, 1),
("LOAD_FAST_BORROW", 0, 1),
("LOAD_FAST_BORROW", 1, 2),
("POP_TOP", None, 3),
]
Expand All @@ -2449,7 +2449,7 @@ def test_unoptimized_if_unconsumed(self):
("POP_TOP", None, 3),
]
expected = [
("LOAD_FAST", 0, 1),
("LOAD_FAST_BORROW", 0, 1),
("NOP", None, 2),
("NOP", None, 3),
]
Expand Down Expand Up @@ -2509,7 +2509,12 @@ def test_consume_some_inputs_no_outputs(self):
("GET_LEN", None, 2),
("LIST_APPEND", 0, 3),
]
self.check(insts, insts)
expected = [
("LOAD_FAST_BORROW", 0, 1),
("GET_LEN", None, 2),
("LIST_APPEND", 0, 3),
]
self.check(insts, expected)

def test_check_exc_match(self):
insts = [
Expand All @@ -2518,7 +2523,7 @@ def test_check_exc_match(self):
("CHECK_EXC_MATCH", None, 3)
]
expected = [
("LOAD_FAST", 0, 1),
("LOAD_FAST_BORROW", 0, 1),
("LOAD_FAST_BORROW", 1, 2),
("CHECK_EXC_MATCH", None, 3)
]
Expand All @@ -2537,7 +2542,19 @@ def test_for_iter(self):
("LOAD_CONST", 0, 7),
("RETURN_VALUE", None, 8),
]
self.cfg_optimization_test(insts, insts, consts=[None])
expected = [
("LOAD_FAST_BORROW", 0, 1),
top := self.Label(),
("FOR_ITER", end := self.Label(), 2),
("STORE_FAST", 2, 3),
("JUMP", top, 4),
end,
("END_FOR", None, 5),
("POP_TOP", None, 6),
("LOAD_CONST", 0, 7),
("RETURN_VALUE", None, 8),
]
self.cfg_optimization_test(insts, expected, consts=[None])

def test_load_attr(self):
insts = [
Expand All @@ -2556,7 +2573,7 @@ def test_load_attr(self):
("LOAD_ATTR", 1, 2),
]
expected = [
("LOAD_FAST", 0, 1),
("LOAD_FAST_BORROW", 0, 1),
("LOAD_ATTR", 1, 2),
]
self.check(insts, expected)
Expand Down Expand Up @@ -2586,7 +2603,7 @@ def test_super_attr(self):
expected = [
("LOAD_FAST_BORROW", 0, 1),
("LOAD_FAST_BORROW", 1, 2),
("LOAD_FAST", 2, 3),
("LOAD_FAST_BORROW", 2, 3),
("LOAD_SUPER_ATTR", 1, 4),
]
self.check(insts, expected)
Expand All @@ -2603,7 +2620,7 @@ def test_send(self):
("RETURN_VALUE", None, 7)
]
expected = [
("LOAD_FAST", 0, 1),
("LOAD_FAST_BORROW", 0, 1),
("LOAD_FAST_BORROW", 1, 2),
("SEND", end := self.Label(), 3),
("LOAD_CONST", 0, 4),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +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.
2 changes: 1 addition & 1 deletion Python/flowgraph.c
Original file line number Diff line number Diff line change
Expand Up @@ -2976,7 +2976,7 @@ optimize_load_fast(cfg_builder *g)

// Optimize instructions
for (int i = 0; i < block->b_iused; i++) {
if (!instr_flags[i]) {
if (!(instr_flags[i] & (SUPPORT_KILLED | STORED_AS_LOCAL))) {
cfg_instr *instr = &block->b_instr[i];
switch (instr->i_opcode) {
case LOAD_FAST:
Expand Down
Loading