Skip to content
Merged
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
75 changes: 36 additions & 39 deletions Lib/test/test_dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -1817,26 +1817,25 @@ def _prepare_test_cases():
make_inst(opname='MAKE_CELL', arg=1, argval='b', argrepr='b', offset=2, start_offset=2, starts_line=False, line_number=None),
make_inst(opname='RESUME', arg=0, argval=0, argrepr='', offset=4, start_offset=4, starts_line=True, line_number=1, cache_info=[('counter', 1, b'\x00\x00')]),
make_inst(opname='LOAD_CONST', arg=3, argval=(3, 4), argrepr='(3, 4)', offset=8, start_offset=8, starts_line=True, line_number=2),
make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='a', argrepr='a', offset=10, start_offset=10, starts_line=False, line_number=2),
make_inst(opname='LOAD_FAST_BORROW', arg=1, argval='b', argrepr='b', offset=12, start_offset=12, starts_line=False, line_number=2),
make_inst(opname='BUILD_TUPLE', arg=2, argval=2, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=2),
make_inst(opname='LOAD_CONST', arg=1, argval=code_object_f, argrepr=repr(code_object_f), offset=16, start_offset=16, starts_line=False, line_number=2),
make_inst(opname='MAKE_FUNCTION', arg=None, argval=None, argrepr='', offset=18, start_offset=18, starts_line=False, line_number=2),
make_inst(opname='SET_FUNCTION_ATTRIBUTE', arg=8, argval=8, argrepr='closure', offset=20, start_offset=20, starts_line=False, line_number=2),
make_inst(opname='SET_FUNCTION_ATTRIBUTE', arg=1, argval=1, argrepr='defaults', offset=22, start_offset=22, starts_line=False, line_number=2),
make_inst(opname='STORE_FAST', arg=2, argval='f', argrepr='f', offset=24, start_offset=24, starts_line=False, line_number=2),
make_inst(opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print + NULL', offset=26, start_offset=26, starts_line=True, line_number=7, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
make_inst(opname='LOAD_DEREF', arg=0, argval='a', argrepr='a', offset=36, start_offset=36, starts_line=False, line_number=7),
make_inst(opname='LOAD_DEREF', arg=1, argval='b', argrepr='b', offset=38, start_offset=38, starts_line=False, line_number=7),
make_inst(opname='LOAD_COMMON_CONSTANT', arg=8, argval='', argrepr="''", offset=40, start_offset=40, starts_line=False, line_number=7),
make_inst(opname='LOAD_SMALL_INT', arg=1, argval=1, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7),
make_inst(opname='BUILD_LIST', arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7),
make_inst(opname='BUILD_MAP', arg=0, argval=0, argrepr='', offset=46, start_offset=46, starts_line=False, line_number=7),
make_inst(opname='LOAD_CONST', arg=2, argval='Hello world!', argrepr="'Hello world!'", offset=48, start_offset=48, starts_line=False, line_number=7),
make_inst(opname='CALL', arg=7, argval=7, argrepr='', offset=50, start_offset=50, starts_line=False, line_number=7, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=58, start_offset=58, starts_line=False, line_number=7),
make_inst(opname='LOAD_FAST_BORROW', arg=2, argval='f', argrepr='f', offset=60, start_offset=60, starts_line=True, line_number=8),
make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=62, start_offset=62, starts_line=False, line_number=8),
make_inst(opname='LOAD_FAST_BORROW_LOAD_FAST_BORROW', arg=1, argval=('a', 'b'), argrepr='a, b', offset=10, start_offset=10, starts_line=False, line_number=2),
make_inst(opname='BUILD_TUPLE', arg=2, argval=2, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=2),
make_inst(opname='LOAD_CONST', arg=1, argval=code_object_f, argrepr=repr(code_object_f), offset=14, start_offset=14, starts_line=False, line_number=2),
make_inst(opname='MAKE_FUNCTION', arg=None, argval=None, argrepr='', offset=16, start_offset=16, starts_line=False, line_number=2),
make_inst(opname='SET_FUNCTION_ATTRIBUTE', arg=8, argval=8, argrepr='closure', offset=18, start_offset=18, starts_line=False, line_number=2),
make_inst(opname='SET_FUNCTION_ATTRIBUTE', arg=1, argval=1, argrepr='defaults', offset=20, start_offset=20, starts_line=False, line_number=2),
make_inst(opname='STORE_FAST', arg=2, argval='f', argrepr='f', offset=22, start_offset=22, starts_line=False, line_number=2),
make_inst(opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print + NULL', offset=24, start_offset=24, starts_line=True, line_number=7, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
make_inst(opname='LOAD_DEREF', arg=0, argval='a', argrepr='a', offset=34, start_offset=34, starts_line=False, line_number=7),
make_inst(opname='LOAD_DEREF', arg=1, argval='b', argrepr='b', offset=36, start_offset=36, starts_line=False, line_number=7),
make_inst(opname='LOAD_COMMON_CONSTANT', arg=8, argval='', argrepr="''", offset=38, start_offset=38, starts_line=False, line_number=7),
make_inst(opname='LOAD_SMALL_INT', arg=1, argval=1, argrepr='', offset=40, start_offset=40, starts_line=False, line_number=7),
make_inst(opname='BUILD_LIST', arg=0, argval=0, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7),
make_inst(opname='BUILD_MAP', arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7),
make_inst(opname='LOAD_CONST', arg=2, argval='Hello world!', argrepr="'Hello world!'", offset=46, start_offset=46, starts_line=False, line_number=7),
make_inst(opname='CALL', arg=7, argval=7, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=7, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=7),
make_inst(opname='LOAD_FAST_BORROW', arg=2, argval='f', argrepr='f', offset=58, start_offset=58, starts_line=True, line_number=8),
make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=60, start_offset=60, starts_line=False, line_number=8),
]

expected_opinfo_f = [
Expand All @@ -1845,25 +1844,23 @@ def _prepare_test_cases():
make_inst(opname='MAKE_CELL', arg=1, argval='d', argrepr='d', offset=4, start_offset=4, starts_line=False, line_number=None),
make_inst(opname='RESUME', arg=0, argval=0, argrepr='', offset=6, start_offset=6, starts_line=True, line_number=2, cache_info=[('counter', 1, b'\x00\x00')]),
make_inst(opname='LOAD_CONST', arg=2, argval=(5, 6), argrepr='(5, 6)', offset=10, start_offset=10, starts_line=True, line_number=3),
make_inst(opname='LOAD_FAST_BORROW', arg=3, argval='a', argrepr='a', offset=12, start_offset=12, starts_line=False, line_number=3),
make_inst(opname='LOAD_FAST_BORROW', arg=4, argval='b', argrepr='b', offset=14, start_offset=14, starts_line=False, line_number=3),
make_inst(opname='LOAD_FAST_BORROW', arg=0, argval='c', argrepr='c', offset=16, start_offset=16, starts_line=False, line_number=3),
make_inst(opname='LOAD_FAST_BORROW', arg=1, argval='d', argrepr='d', offset=18, start_offset=18, starts_line=False, line_number=3),
make_inst(opname='BUILD_TUPLE', arg=4, argval=4, argrepr='', offset=20, start_offset=20, starts_line=False, line_number=3),
make_inst(opname='LOAD_CONST', arg=1, argval=code_object_inner, argrepr=repr(code_object_inner), offset=22, start_offset=22, starts_line=False, line_number=3),
make_inst(opname='MAKE_FUNCTION', arg=None, argval=None, argrepr='', offset=24, start_offset=24, starts_line=False, line_number=3),
make_inst(opname='SET_FUNCTION_ATTRIBUTE', arg=8, argval=8, argrepr='closure', offset=26, start_offset=26, starts_line=False, line_number=3),
make_inst(opname='SET_FUNCTION_ATTRIBUTE', arg=1, argval=1, argrepr='defaults', offset=28, start_offset=28, starts_line=False, line_number=3),
make_inst(opname='STORE_FAST', arg=2, argval='inner', argrepr='inner', offset=30, start_offset=30, starts_line=False, line_number=3),
make_inst(opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print + NULL', offset=32, start_offset=32, starts_line=True, line_number=5, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
make_inst(opname='LOAD_DEREF', arg=3, argval='a', argrepr='a', offset=42, start_offset=42, starts_line=False, line_number=5),
make_inst(opname='LOAD_DEREF', arg=4, argval='b', argrepr='b', offset=44, start_offset=44, starts_line=False, line_number=5),
make_inst(opname='LOAD_DEREF', arg=0, argval='c', argrepr='c', offset=46, start_offset=46, starts_line=False, line_number=5),
make_inst(opname='LOAD_DEREF', arg=1, argval='d', argrepr='d', offset=48, start_offset=48, starts_line=False, line_number=5),
make_inst(opname='CALL', arg=4, argval=4, argrepr='', offset=50, start_offset=50, starts_line=False, line_number=5, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=58, start_offset=58, starts_line=False, line_number=5),
make_inst(opname='LOAD_FAST_BORROW', arg=2, argval='inner', argrepr='inner', offset=60, start_offset=60, starts_line=True, line_number=6),
make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=62, start_offset=62, starts_line=False, line_number=6),
make_inst(opname='LOAD_FAST_BORROW_LOAD_FAST_BORROW', arg=52, argval=('a', 'b'), argrepr='a, b', offset=12, start_offset=12, starts_line=False, line_number=3),
make_inst(opname='LOAD_FAST_BORROW_LOAD_FAST_BORROW', arg=1, argval=('c', 'd'), argrepr='c, d', offset=14, start_offset=14, starts_line=False, line_number=3),
make_inst(opname='BUILD_TUPLE', arg=4, argval=4, argrepr='', offset=16, start_offset=16, starts_line=False, line_number=3),
make_inst(opname='LOAD_CONST', arg=1, argval=code_object_inner, argrepr=repr(code_object_inner), offset=18, start_offset=18, starts_line=False, line_number=3),
make_inst(opname='MAKE_FUNCTION', arg=None, argval=None, argrepr='', offset=20, start_offset=20, starts_line=False, line_number=3),
make_inst(opname='SET_FUNCTION_ATTRIBUTE', arg=8, argval=8, argrepr='closure', offset=22, start_offset=22, starts_line=False, line_number=3),
make_inst(opname='SET_FUNCTION_ATTRIBUTE', arg=1, argval=1, argrepr='defaults', offset=24, start_offset=24, starts_line=False, line_number=3),
make_inst(opname='STORE_FAST', arg=2, argval='inner', argrepr='inner', offset=26, start_offset=26, starts_line=False, line_number=3),
make_inst(opname='LOAD_GLOBAL', arg=1, argval='print', argrepr='print + NULL', offset=28, start_offset=28, starts_line=True, line_number=5, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
make_inst(opname='LOAD_DEREF', arg=3, argval='a', argrepr='a', offset=38, start_offset=38, starts_line=False, line_number=5),
make_inst(opname='LOAD_DEREF', arg=4, argval='b', argrepr='b', offset=40, start_offset=40, starts_line=False, line_number=5),
make_inst(opname='LOAD_DEREF', arg=0, argval='c', argrepr='c', offset=42, start_offset=42, starts_line=False, line_number=5),
make_inst(opname='LOAD_DEREF', arg=1, argval='d', argrepr='d', offset=44, start_offset=44, starts_line=False, line_number=5),
make_inst(opname='CALL', arg=4, argval=4, argrepr='', offset=46, start_offset=46, starts_line=False, line_number=5, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
make_inst(opname='POP_TOP', arg=None, argval=None, argrepr='', offset=54, start_offset=54, starts_line=False, line_number=5),
make_inst(opname='LOAD_FAST_BORROW', arg=2, argval='inner', argrepr='inner', offset=56, start_offset=56, starts_line=True, line_number=6),
make_inst(opname='RETURN_VALUE', arg=None, argval=None, argrepr='', offset=58, start_offset=58, starts_line=False, line_number=6),
]

expected_opinfo_inner = [
Expand Down
25 changes: 25 additions & 0 deletions Lib/test/test_peepholer.py
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,31 @@ def f():
y = x + x
self.assertInBytecode(f, 'LOAD_FAST_BORROW_LOAD_FAST_BORROW')

def test_store_fast_load_fast_borrow(self):
# gh-159493: a LOAD_FAST that can borrow must not be fused into
# STORE_FAST_LOAD_FAST, regardless of how the source is laid out.
def f(shared, n):
for i in range(n): shared

def g(shared, n):
for i in range(n):
shared

for func in (f, g):
with self.subTest(func=func.__name__):
self.assertNotInBytecode(func, 'STORE_FAST_LOAD_FAST')
self.assertInBytecode(func, 'LOAD_FAST_BORROW', 'shared')
self.assertEqual([i.opname for i in dis.get_instructions(f)],
[i.opname for i in dis.get_instructions(g)])

def test_store_fast_load_fast_not_borrowable(self):
def f(x):
y = x; z = y
return z
# The load of y is stored into z, so it needs a strong reference and
# can still be fused.
self.assertInBytecode(f, 'STORE_FAST_LOAD_FAST', ('y', 'y'))

def test_load_fast_unknown_simple(self):
def f():
if condition():
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Superinstructions are now formed after the compiler decides which
:opcode:`LOAD_FAST` instructions can borrow their reference.
This fixes reference count contention slowdown on the free-threaded build for single-line loops and comprehensions.
29 changes: 23 additions & 6 deletions Python/flowgraph.c
Original file line number Diff line number Diff line change
Expand Up @@ -2719,6 +2719,12 @@ insert_superinstructions(cfg_builder *g)
make_super_instruction(inst, &b->b_instr[i + 1], LOAD_FAST_LOAD_FAST);
Comment thread
kumaraditya303 marked this conversation as resolved.
}
break;
case LOAD_FAST_BORROW:
if (nextop == LOAD_FAST_BORROW) {
make_super_instruction(inst, &b->b_instr[i + 1],
LOAD_FAST_BORROW_LOAD_FAST_BORROW);
}
break;
case STORE_FAST:
switch (nextop) {
case LOAD_FAST:
Expand All @@ -2732,9 +2738,9 @@ insert_superinstructions(cfg_builder *g)
}
}
}
int res = remove_redundant_nops(g);
RETURN_IF_ERROR(remove_redundant_nops(g));
assert(no_redundant_nops(g));
return res;
return SUCCESS;
}

#define NOT_LOCAL -1
Expand Down Expand Up @@ -3834,8 +3840,6 @@ _PyCfg_OptimizeCodeUnit(cfg_builder *g, PyObject *consts, PyObject *const_cache,
RETURN_IF_ERROR(
add_checks_for_loads_of_uninitialized_variables(
g->g_entryblock, nlocals, nparams));
RETURN_IF_ERROR(insert_superinstructions(g));

RETURN_IF_ERROR(push_cold_blocks_to_end(g));
RETURN_IF_ERROR(resolve_line_numbers(g, firstlineno));
// temporarily remove assert. See https://github.com/python/cpython/issues/125845
Expand Down Expand Up @@ -4157,11 +4161,19 @@ _PyCfg_OptimizedCfgToInstructionSequence(cfg_builder *g,
RETURN_IF_ERROR(normalize_jumps(g));
assert(no_redundant_jumps(g));

/* Can't modify the bytecode after inserting instructions that produce
* borrowed references.
/* After this point the bytecode may only be modified in ways that
* preserve the borrowed references computed here.
*
* NOTE: keep the passes below in sync with _PyCompile_OptimizeCfg().
*/
RETURN_IF_ERROR(optimize_load_fast(g));

/* Superinstructions are inserted after optimize_load_fast() so that a
* LOAD_FAST which can be borrowed is converted to LOAD_FAST_BORROW
* instead of being fused into a STORE_FAST_LOAD_FAST. Fusing adjacent
* instructions does not change which references are borrowed. */
RETURN_IF_ERROR(insert_superinstructions(g));

/* Can't modify the bytecode after computing jump offsets. */
if (_PyCfg_ToInstructionSequence(g, seq) < 0) {
return ERROR;
Expand Down Expand Up @@ -4254,10 +4266,15 @@ _PyCompile_OptimizeCfg(PyObject *seq, PyObject *consts, int nlocals)
goto error;
}

/* NOTE: keep in sync with _PyCfg_OptimizedCfgToInstructionSequence(). */
if (optimize_load_fast(g) != SUCCESS) {
goto error;
}

if (insert_superinstructions(g) < 0) {
Comment thread
kumaraditya303 marked this conversation as resolved.
goto error;
}

res = cfg_to_instruction_sequence(g);
error:
Py_DECREF(const_cache);
Expand Down
Loading