Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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