Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions Lib/test/test_dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ def _with(c):
dis_with = """\
%4d RESUME 0

%4d LOAD_FAST_BORROW 0 (c)
%4d LOAD_FAST 0 (c)
COPY 1
LOAD_SPECIAL 1 (__exit__)
SWAP 2
Expand Down Expand Up @@ -1922,7 +1922,7 @@ def _prepare_test_cases():
make_inst(opname='LOAD_SMALL_INT', arg=0, argval=0, argrepr='', offset=244, start_offset=244, starts_line=False, line_number=21),
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')]),
make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=258, start_offset=258, starts_line=False, line_number=21),
make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='i', argrepr='i', offset=260, start_offset=260, starts_line=True, line_number=25),
make_inst(opname='LOAD_FAST', arg=0, argval='i', argrepr='i', offset=260, start_offset=260, starts_line=True, line_number=25),
make_inst(opname='COPY', arg=1, argval=1, argrepr='', offset=262, start_offset=262, starts_line=False, line_number=25),
make_inst(opname='LOAD_SPECIAL', arg=1, argval=1, argrepr='__exit__', offset=264, start_offset=264, starts_line=False, line_number=25),
make_inst(opname='SWAP', arg=2, argval=2, argrepr='', offset=266, start_offset=266, starts_line=False, line_number=25),
Expand Down
93 changes: 93 additions & 0 deletions Lib/test/test_peepholer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2598,6 +2598,36 @@ def test_for_iter(self):
("LOAD_CONST", 0, 7),
("RETURN_VALUE", None, 8),
]
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_for_iter_checks_fallthrough(self):
insts = [
("LOAD_FAST", 0, 1),
("GET_ITER", 0, 2),
top := self.Label(),
("FOR_ITER", end := self.Label(), 3),
("STORE_FAST", 1, 4),
("LOAD_CONST", 0, 5),
("STORE_FAST", 0, 6),
("JUMP", top, 7),
end,
("END_FOR", None, 8),
("POP_ITER", None, 9),
("LOAD_CONST", 0, 10),
("RETURN_VALUE", None, 11),
]
self.cfg_optimization_test(insts, insts, consts=[None])

def test_load_attr(self):
Expand Down Expand Up @@ -2675,6 +2705,69 @@ def test_send(self):
]
self.cfg_optimization_test(insts, expected, consts=[None])

def test_borrow_checks_exception_handler_edges(self):
insts = [
("LOAD_FAST", 0, 1),
("JUMP", start := self.Label(), 2),
start,
("SETUP_FINALLY", handler := self.Label(), 3),
("LOAD_CONST", 0, 4),
("BINARY_OP", 0, 5),
("POP_BLOCK", None, 6),
("RETURN_VALUE", None, 7),
handler,
("STORE_FAST", 1, 8),
("LOAD_CONST", 0, 9),
("STORE_FAST", 0, 10),
("POP_TOP", None, 11),
("LOAD_CONST", 0, 12),
("RETURN_VALUE", None, 13),
]
expected = [
("LOAD_FAST", 0, 1),
("NOP", None, 2),
("SETUP_FINALLY", handler := self.Label(), 3),
("LOAD_CONST", 0, 4),
("BINARY_OP", 0, 5),
("NOP", None, 6),
("RETURN_VALUE", None, 7),
handler,
("STORE_FAST", 1, 8),
("LOAD_CONST", 0, 9),
("STORE_FAST", 0, 10),
("POP_TOP", None, 11),
("LOAD_CONST", 0, 12),
("RETURN_VALUE", None, 13),
]
self.cfg_optimization_test(insts, expected, consts=[None])

def test_copied_reference_is_not_borrowed(self):
insts = [
("LOAD_FAST", 0, 1),
("COPY", 1, 1),
("TO_BOOL", None, 1),
("POP_JUMP_IF_TRUE", target := self.Label(), 1),
("POP_TOP", None, 1),
("LOAD_FAST", 1, 1),
target,
("STORE_FAST", 0, 1),
("LOAD_CONST", 0, 2),
("RETURN_VALUE", None, 2),
]
expected = [
("LOAD_FAST", 0, 1),
("COPY", 1, 1),
("TO_BOOL", None, 1),
("POP_JUMP_IF_TRUE", target := self.Label(), 1),
("POP_TOP", None, 1),
("LOAD_FAST", 1, 1),
target,
("STORE_FAST", 0, 1),
("LOAD_CONST", 0, 2),
("RETURN_VALUE", None, 2),
]
self.cfg_optimization_test(insts, expected, consts=[None])

def test_format_simple(self):
# FORMAT_SIMPLE will leave its operand on the stack if it's a unicode
# object. We treat it conservatively and assume that it always leaves
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.
Loading
Loading