mirror of https://github.com/python/cpython
gh-104615: don't make unsafe swaps in apply_static_swaps (#104620)
This commit is contained in:
parent
dcdc90d384
commit
0589c6a4d3
|
@ -105,7 +105,8 @@ PyAPI_FUNC(PyObject*) _PyCompile_CodeGen(
|
|||
|
||||
PyAPI_FUNC(PyObject*) _PyCompile_OptimizeCfg(
|
||||
PyObject *instructions,
|
||||
PyObject *consts);
|
||||
PyObject *consts,
|
||||
int nlocals);
|
||||
|
||||
PyAPI_FUNC(PyCodeObject*)
|
||||
_PyCompile_Assemble(_PyCompile_CodeUnitMetadata *umd, PyObject *filename,
|
||||
|
|
|
@ -1071,6 +1071,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
|
|||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(newline));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(newlines));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(next));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(nlocals));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(node_depth));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(node_offset));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ns));
|
||||
|
|
|
@ -559,6 +559,7 @@ struct _Py_global_strings {
|
|||
STRUCT_FOR_ID(newline)
|
||||
STRUCT_FOR_ID(newlines)
|
||||
STRUCT_FOR_ID(next)
|
||||
STRUCT_FOR_ID(nlocals)
|
||||
STRUCT_FOR_ID(node_depth)
|
||||
STRUCT_FOR_ID(node_offset)
|
||||
STRUCT_FOR_ID(ns)
|
||||
|
|
|
@ -1065,6 +1065,7 @@ extern "C" {
|
|||
INIT_ID(newline), \
|
||||
INIT_ID(newlines), \
|
||||
INIT_ID(next), \
|
||||
INIT_ID(nlocals), \
|
||||
INIT_ID(node_depth), \
|
||||
INIT_ID(node_offset), \
|
||||
INIT_ID(ns), \
|
||||
|
|
|
@ -1518,6 +1518,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
|||
string = &_Py_ID(next);
|
||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||
_PyUnicode_InternInPlace(interp, &string);
|
||||
string = &_Py_ID(nlocals);
|
||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||
_PyUnicode_InternInPlace(interp, &string);
|
||||
string = &_Py_ID(node_depth);
|
||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||
_PyUnicode_InternInPlace(interp, &string);
|
||||
|
|
|
@ -130,10 +130,10 @@ class CodegenTestCase(CompilationStepTestCase):
|
|||
|
||||
class CfgOptimizationTestCase(CompilationStepTestCase):
|
||||
|
||||
def get_optimized(self, insts, consts):
|
||||
def get_optimized(self, insts, consts, nlocals=0):
|
||||
insts = self.normalize_insts(insts)
|
||||
insts = self.complete_insts_info(insts)
|
||||
insts = optimize_cfg(insts, consts)
|
||||
insts = optimize_cfg(insts, consts, nlocals)
|
||||
return insts, consts
|
||||
|
||||
class AssemblerTestCase(CompilationStepTestCase):
|
||||
|
|
|
@ -1168,6 +1168,24 @@ class TestSpecifics(unittest.TestCase):
|
|||
""")
|
||||
compile(code, "<test>", "exec")
|
||||
|
||||
def test_apply_static_swaps(self):
|
||||
def f(x, y):
|
||||
a, a = x, y
|
||||
return a
|
||||
self.assertEqual(f("x", "y"), "y")
|
||||
|
||||
def test_apply_static_swaps_2(self):
|
||||
def f(x, y, z):
|
||||
a, b, a = x, y, z
|
||||
return a
|
||||
self.assertEqual(f("x", "y", "z"), "z")
|
||||
|
||||
def test_apply_static_swaps_3(self):
|
||||
def f(x, y, z):
|
||||
a, a, b = x, y, z
|
||||
return a
|
||||
self.assertEqual(f("x", "y", "z"), "y")
|
||||
|
||||
|
||||
@requires_debug_ranges()
|
||||
class TestSourcePositions(unittest.TestCase):
|
||||
|
|
|
@ -484,6 +484,12 @@ class ListComprehensionTest(unittest.TestCase):
|
|||
"""
|
||||
self._check_in_scopes(code, {"z": 1, "out": [(3, 2, 1)]})
|
||||
|
||||
def test_assign_to_comp_iter_var_in_outer_function(self):
|
||||
code = """
|
||||
a = [1 for a in [0]]
|
||||
"""
|
||||
self._check_in_scopes(code, {"a": [1]}, scopes=["function"])
|
||||
|
||||
|
||||
__test__ = {'doctests' : doctests}
|
||||
|
||||
|
|
|
@ -971,13 +971,14 @@ class TestMarkingVariablesAsUnKnown(BytecodeTestCase):
|
|||
self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
|
||||
|
||||
|
||||
class DirectiCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
class DirectCfgOptimizerTests(CfgOptimizationTestCase):
|
||||
|
||||
def cfg_optimization_test(self, insts, expected_insts,
|
||||
consts=None, expected_consts=None):
|
||||
consts=None, expected_consts=None,
|
||||
nlocals=0):
|
||||
if expected_consts is None:
|
||||
expected_consts = consts
|
||||
opt_insts, opt_consts = self.get_optimized(insts, consts)
|
||||
opt_insts, opt_consts = self.get_optimized(insts, consts, nlocals)
|
||||
expected_insts = self.normalize_insts(expected_insts)
|
||||
self.assertInstructionsMatch(opt_insts, expected_insts)
|
||||
self.assertEqual(opt_consts, expected_consts)
|
||||
|
@ -1058,6 +1059,19 @@ class DirectiCfgOptimizerTests(CfgOptimizationTestCase):
|
|||
]
|
||||
self.cfg_optimization_test(insts, expected_insts, consts=list(range(5)))
|
||||
|
||||
def test_no_unsafe_static_swap(self):
|
||||
# We can't change order of two stores to the same location
|
||||
insts = [
|
||||
('LOAD_CONST', 0, 1),
|
||||
('LOAD_CONST', 1, 2),
|
||||
('LOAD_CONST', 2, 3),
|
||||
('SWAP', 3, 4),
|
||||
('STORE_FAST', 1, 4),
|
||||
('STORE_FAST', 1, 4),
|
||||
('POP_TOP', 0, 4),
|
||||
('RETURN_VALUE', 5)
|
||||
]
|
||||
self.cfg_optimization_test(insts, insts, consts=list(range(3)), nlocals=1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Fix wrong ordering of assignments in code like ``a, a = x, y``. Contributed by
|
||||
Carl Meyer.
|
|
@ -616,16 +616,17 @@ _testinternalcapi.optimize_cfg -> object
|
|||
|
||||
instructions: object
|
||||
consts: object
|
||||
nlocals: int
|
||||
|
||||
Apply compiler optimizations to an instruction list.
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
_testinternalcapi_optimize_cfg_impl(PyObject *module, PyObject *instructions,
|
||||
PyObject *consts)
|
||||
/*[clinic end generated code: output=5412aeafca683c8b input=7e8a3de86ebdd0f9]*/
|
||||
PyObject *consts, int nlocals)
|
||||
/*[clinic end generated code: output=57c53c3a3dfd1df0 input=6a96d1926d58d7e5]*/
|
||||
{
|
||||
return _PyCompile_OptimizeCfg(instructions, consts);
|
||||
return _PyCompile_OptimizeCfg(instructions, consts, nlocals);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -83,7 +83,7 @@ exit:
|
|||
}
|
||||
|
||||
PyDoc_STRVAR(_testinternalcapi_optimize_cfg__doc__,
|
||||
"optimize_cfg($module, /, instructions, consts)\n"
|
||||
"optimize_cfg($module, /, instructions, consts, nlocals)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Apply compiler optimizations to an instruction list.");
|
||||
|
@ -93,7 +93,7 @@ PyDoc_STRVAR(_testinternalcapi_optimize_cfg__doc__,
|
|||
|
||||
static PyObject *
|
||||
_testinternalcapi_optimize_cfg_impl(PyObject *module, PyObject *instructions,
|
||||
PyObject *consts);
|
||||
PyObject *consts, int nlocals);
|
||||
|
||||
static PyObject *
|
||||
_testinternalcapi_optimize_cfg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||
|
@ -101,14 +101,14 @@ _testinternalcapi_optimize_cfg(PyObject *module, PyObject *const *args, Py_ssize
|
|||
PyObject *return_value = NULL;
|
||||
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
|
||||
|
||||
#define NUM_KEYWORDS 2
|
||||
#define NUM_KEYWORDS 3
|
||||
static struct {
|
||||
PyGC_Head _this_is_not_used;
|
||||
PyObject_VAR_HEAD
|
||||
PyObject *ob_item[NUM_KEYWORDS];
|
||||
} _kwtuple = {
|
||||
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
|
||||
.ob_item = { &_Py_ID(instructions), &_Py_ID(consts), },
|
||||
.ob_item = { &_Py_ID(instructions), &_Py_ID(consts), &_Py_ID(nlocals), },
|
||||
};
|
||||
#undef NUM_KEYWORDS
|
||||
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
|
||||
|
@ -117,24 +117,29 @@ _testinternalcapi_optimize_cfg(PyObject *module, PyObject *const *args, Py_ssize
|
|||
# define KWTUPLE NULL
|
||||
#endif // !Py_BUILD_CORE
|
||||
|
||||
static const char * const _keywords[] = {"instructions", "consts", NULL};
|
||||
static const char * const _keywords[] = {"instructions", "consts", "nlocals", NULL};
|
||||
static _PyArg_Parser _parser = {
|
||||
.keywords = _keywords,
|
||||
.fname = "optimize_cfg",
|
||||
.kwtuple = KWTUPLE,
|
||||
};
|
||||
#undef KWTUPLE
|
||||
PyObject *argsbuf[2];
|
||||
PyObject *argsbuf[3];
|
||||
PyObject *instructions;
|
||||
PyObject *consts;
|
||||
int nlocals;
|
||||
|
||||
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf);
|
||||
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf);
|
||||
if (!args) {
|
||||
goto exit;
|
||||
}
|
||||
instructions = args[0];
|
||||
consts = args[1];
|
||||
return_value = _testinternalcapi_optimize_cfg_impl(module, instructions, consts);
|
||||
nlocals = _PyLong_AsInt(args[2]);
|
||||
if (nlocals == -1 && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
return_value = _testinternalcapi_optimize_cfg_impl(module, instructions, consts, nlocals);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
|
@ -201,4 +206,4 @@ _testinternalcapi_assemble_code_object(PyObject *module, PyObject *const *args,
|
|||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=ab661d56a14b1a1c input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=2965f1578b986218 input=a9049054013a1b77]*/
|
||||
|
|
|
@ -7996,7 +7996,7 @@ finally:
|
|||
}
|
||||
|
||||
PyObject *
|
||||
_PyCompile_OptimizeCfg(PyObject *instructions, PyObject *consts)
|
||||
_PyCompile_OptimizeCfg(PyObject *instructions, PyObject *consts, int nlocals)
|
||||
{
|
||||
PyObject *res = NULL;
|
||||
PyObject *const_cache = PyDict_New();
|
||||
|
@ -8008,7 +8008,7 @@ _PyCompile_OptimizeCfg(PyObject *instructions, PyObject *consts)
|
|||
if (instructions_to_cfg(instructions, &g) < 0) {
|
||||
goto error;
|
||||
}
|
||||
int code_flags = 0, nlocals = 0, nparams = 0, firstlineno = 1;
|
||||
int code_flags = 0, nparams = 0, firstlineno = 1;
|
||||
if (_PyCfg_OptimizeCodeUnit(&g, consts, const_cache, code_flags, nlocals,
|
||||
nparams, firstlineno) < 0) {
|
||||
goto error;
|
||||
|
|
|
@ -1293,6 +1293,11 @@ swaptimize(basicblock *block, int *ix)
|
|||
(opcode) == STORE_FAST_MAYBE_NULL || \
|
||||
(opcode) == POP_TOP)
|
||||
|
||||
#define STORES_TO(instr) \
|
||||
(((instr).i_opcode == STORE_FAST || \
|
||||
(instr).i_opcode == STORE_FAST_MAYBE_NULL) \
|
||||
? (instr).i_oparg : -1)
|
||||
|
||||
static int
|
||||
next_swappable_instruction(basicblock *block, int i, int lineno)
|
||||
{
|
||||
|
@ -1344,6 +1349,23 @@ apply_static_swaps(basicblock *block, int i)
|
|||
return;
|
||||
}
|
||||
}
|
||||
// The reordering is not safe if the two instructions to be swapped
|
||||
// store to the same location, or if any intervening instruction stores
|
||||
// to the same location as either of them.
|
||||
int store_j = STORES_TO(block->b_instr[j]);
|
||||
int store_k = STORES_TO(block->b_instr[k]);
|
||||
if (store_j >= 0 || store_k >= 0) {
|
||||
if (store_j == store_k) {
|
||||
return;
|
||||
}
|
||||
for (int idx = j + 1; idx < k; idx++) {
|
||||
int store_idx = STORES_TO(block->b_instr[idx]);
|
||||
if (store_idx >= 0 && (store_idx == store_j || store_idx == store_k)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Success!
|
||||
INSTR_SET_OP0(swap, NOP);
|
||||
cfg_instr temp = block->b_instr[j];
|
||||
|
|
Loading…
Reference in New Issue