mirror of
https://github.com/python/cpython.git
synced 2025-07-23 03:05:38 +00:00
gh-87092: Make jump target label equal to the offset of the target in the instructions sequence (#102093)
This commit is contained in:
parent
6b2d7c0ddb
commit
9f799ab020
4 changed files with 116 additions and 113 deletions
|
@ -50,18 +50,13 @@ class CompilationStepTestCase(unittest.TestCase):
|
||||||
HAS_TARGET = set(dis.hasjrel + dis.hasjabs + dis.hasexc)
|
HAS_TARGET = set(dis.hasjrel + dis.hasjabs + dis.hasexc)
|
||||||
HAS_ARG_OR_TARGET = HAS_ARG.union(HAS_TARGET)
|
HAS_ARG_OR_TARGET = HAS_ARG.union(HAS_TARGET)
|
||||||
|
|
||||||
def setUp(self):
|
class Label:
|
||||||
self.last_label = 0
|
pass
|
||||||
|
|
||||||
def Label(self):
|
|
||||||
self.last_label += 1
|
|
||||||
return self.last_label
|
|
||||||
|
|
||||||
def assertInstructionsMatch(self, actual_, expected_):
|
def assertInstructionsMatch(self, actual_, expected_):
|
||||||
# get two lists where each entry is a label or
|
# get two lists where each entry is a label or
|
||||||
# an instruction tuple. Compare them, while mapping
|
# an instruction tuple. Normalize the labels to the
|
||||||
# each actual label to a corresponding expected label
|
# instruction count of the target, and compare the lists.
|
||||||
# based on their locations.
|
|
||||||
|
|
||||||
self.assertIsInstance(actual_, list)
|
self.assertIsInstance(actual_, list)
|
||||||
self.assertIsInstance(expected_, list)
|
self.assertIsInstance(expected_, list)
|
||||||
|
@ -82,39 +77,35 @@ class CompilationStepTestCase(unittest.TestCase):
|
||||||
act = act[:len(exp)]
|
act = act[:len(exp)]
|
||||||
self.assertEqual(exp, act)
|
self.assertEqual(exp, act)
|
||||||
|
|
||||||
def normalize_insts(self, insts):
|
def resolveAndRemoveLabels(self, insts):
|
||||||
""" Map labels to instruction index.
|
idx = 0
|
||||||
Remove labels which are not used as jump targets.
|
|
||||||
Map opcodes to opnames.
|
|
||||||
"""
|
|
||||||
labels_map = {}
|
|
||||||
targets = set()
|
|
||||||
idx = 1
|
|
||||||
for item in insts:
|
|
||||||
assert isinstance(item, (int, tuple))
|
|
||||||
if isinstance(item, tuple):
|
|
||||||
opcode, oparg, *_ = item
|
|
||||||
if dis.opmap.get(opcode, opcode) in self.HAS_TARGET:
|
|
||||||
targets.add(oparg)
|
|
||||||
idx += 1
|
|
||||||
elif isinstance(item, int):
|
|
||||||
assert item not in labels_map, "label reused"
|
|
||||||
labels_map[item] = idx
|
|
||||||
|
|
||||||
res = []
|
res = []
|
||||||
for item in insts:
|
for item in insts:
|
||||||
if isinstance(item, int) and item in targets:
|
assert isinstance(item, (self.Label, tuple))
|
||||||
if not res or labels_map[item] != res[-1]:
|
if isinstance(item, self.Label):
|
||||||
res.append(labels_map[item])
|
item.value = idx
|
||||||
elif isinstance(item, tuple):
|
else:
|
||||||
opcode, oparg, *loc = item
|
idx += 1
|
||||||
opcode = dis.opmap.get(opcode, opcode)
|
res.append(item)
|
||||||
if opcode in self.HAS_TARGET:
|
|
||||||
arg = labels_map[oparg]
|
return res
|
||||||
else:
|
|
||||||
arg = oparg if opcode in self.HAS_TARGET else None
|
def normalize_insts(self, insts):
|
||||||
opcode = dis.opname[opcode]
|
""" Map labels to instruction index.
|
||||||
res.append((opcode, arg, *loc))
|
Map opcodes to opnames.
|
||||||
|
"""
|
||||||
|
insts = self.resolveAndRemoveLabels(insts)
|
||||||
|
res = []
|
||||||
|
for item in insts:
|
||||||
|
assert isinstance(item, tuple)
|
||||||
|
opcode, oparg, *loc = item
|
||||||
|
opcode = dis.opmap.get(opcode, opcode)
|
||||||
|
if isinstance(oparg, self.Label):
|
||||||
|
arg = oparg.value
|
||||||
|
else:
|
||||||
|
arg = oparg if opcode in self.HAS_ARG else None
|
||||||
|
opcode = dis.opname[opcode]
|
||||||
|
res.append((opcode, arg, *loc))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,20 +120,18 @@ class CfgOptimizationTestCase(CompilationStepTestCase):
|
||||||
|
|
||||||
def complete_insts_info(self, insts):
|
def complete_insts_info(self, insts):
|
||||||
# fill in omitted fields in location, and oparg 0 for ops with no arg.
|
# fill in omitted fields in location, and oparg 0 for ops with no arg.
|
||||||
instructions = []
|
res = []
|
||||||
for item in insts:
|
for item in insts:
|
||||||
if isinstance(item, int):
|
assert isinstance(item, tuple)
|
||||||
instructions.append(item)
|
inst = list(reversed(item))
|
||||||
else:
|
opcode = dis.opmap[inst.pop()]
|
||||||
assert isinstance(item, tuple)
|
oparg = inst.pop() if opcode in self.HAS_ARG_OR_TARGET else 0
|
||||||
inst = list(reversed(item))
|
loc = inst + [-1] * (4 - len(inst))
|
||||||
opcode = dis.opmap[inst.pop()]
|
res.append((opcode, oparg, *loc))
|
||||||
oparg = inst.pop() if opcode in self.HAS_ARG_OR_TARGET else 0
|
return res
|
||||||
loc = inst + [-1] * (4 - len(inst))
|
|
||||||
instructions.append((opcode, oparg, *loc))
|
|
||||||
return instructions
|
|
||||||
|
|
||||||
def get_optimized(self, insts, consts):
|
def get_optimized(self, insts, consts):
|
||||||
|
insts = self.normalize_insts(insts)
|
||||||
insts = self.complete_insts_info(insts)
|
insts = self.complete_insts_info(insts)
|
||||||
insts = optimize_cfg(insts, consts)
|
insts = optimize_cfg(insts, consts)
|
||||||
return insts, consts
|
return insts, consts
|
||||||
|
|
|
@ -37,11 +37,11 @@ class IsolatedCodeGenTests(CodegenTestCase):
|
||||||
('GET_ITER', None, 1),
|
('GET_ITER', None, 1),
|
||||||
loop_lbl := self.Label(),
|
loop_lbl := self.Label(),
|
||||||
('FOR_ITER', exit_lbl := self.Label(), 1),
|
('FOR_ITER', exit_lbl := self.Label(), 1),
|
||||||
('STORE_NAME', None, 1),
|
('STORE_NAME', 1, 1),
|
||||||
('PUSH_NULL', None, 2),
|
('PUSH_NULL', None, 2),
|
||||||
('LOAD_NAME', None, 2),
|
('LOAD_NAME', 2, 2),
|
||||||
('LOAD_NAME', None, 2),
|
('LOAD_NAME', 1, 2),
|
||||||
('CALL', None, 2),
|
('CALL', 1, 2),
|
||||||
('POP_TOP', None),
|
('POP_TOP', None),
|
||||||
('JUMP', loop_lbl),
|
('JUMP', loop_lbl),
|
||||||
exit_lbl,
|
exit_lbl,
|
||||||
|
|
|
@ -984,6 +984,7 @@ class DirectiCfgOptimizerTests(CfgOptimizationTestCase):
|
||||||
if expected_consts is None:
|
if expected_consts is None:
|
||||||
expected_consts = consts
|
expected_consts = consts
|
||||||
opt_insts, opt_consts = self.get_optimized(insts, consts)
|
opt_insts, opt_consts = self.get_optimized(insts, consts)
|
||||||
|
expected_insts = self.normalize_insts(expected_insts)
|
||||||
self.assertInstructionsMatch(opt_insts, expected_insts)
|
self.assertInstructionsMatch(opt_insts, expected_insts)
|
||||||
self.assertEqual(opt_consts, expected_consts)
|
self.assertEqual(opt_consts, expected_consts)
|
||||||
|
|
||||||
|
@ -996,11 +997,11 @@ class DirectiCfgOptimizerTests(CfgOptimizationTestCase):
|
||||||
('LOAD_CONST', 3, 14),
|
('LOAD_CONST', 3, 14),
|
||||||
]
|
]
|
||||||
expected = [
|
expected = [
|
||||||
('LOAD_NAME', '1', 11),
|
('LOAD_NAME', 1, 11),
|
||||||
('POP_JUMP_IF_TRUE', lbl := self.Label(), 12),
|
('POP_JUMP_IF_TRUE', lbl := self.Label(), 12),
|
||||||
('LOAD_CONST', '2', 13),
|
('LOAD_CONST', 2, 13),
|
||||||
lbl,
|
lbl,
|
||||||
('LOAD_CONST', '3', 14)
|
('LOAD_CONST', 3, 14)
|
||||||
]
|
]
|
||||||
self.cfg_optimization_test(insts, expected, consts=list(range(5)))
|
self.cfg_optimization_test(insts, expected, consts=list(range(5)))
|
||||||
|
|
||||||
|
@ -1018,7 +1019,7 @@ class DirectiCfgOptimizerTests(CfgOptimizationTestCase):
|
||||||
expected = [
|
expected = [
|
||||||
('NOP', None, 11),
|
('NOP', None, 11),
|
||||||
('NOP', None, 12),
|
('NOP', None, 12),
|
||||||
('LOAD_CONST', '3', 14)
|
('LOAD_CONST', 3, 14)
|
||||||
]
|
]
|
||||||
self.cfg_optimization_test(insts, expected, consts=list(range(5)))
|
self.cfg_optimization_test(insts, expected, consts=list(range(5)))
|
||||||
|
|
||||||
|
@ -1031,9 +1032,9 @@ class DirectiCfgOptimizerTests(CfgOptimizationTestCase):
|
||||||
]
|
]
|
||||||
expected = [
|
expected = [
|
||||||
lbl := self.Label(),
|
lbl := self.Label(),
|
||||||
('LOAD_NAME', '1', 11),
|
('LOAD_NAME', 1, 11),
|
||||||
('POP_JUMP_IF_TRUE', lbl, 12),
|
('POP_JUMP_IF_TRUE', lbl, 12),
|
||||||
('LOAD_CONST', '2', 13)
|
('LOAD_CONST', 2, 13)
|
||||||
]
|
]
|
||||||
self.cfg_optimization_test(insts, expected, consts=list(range(5)))
|
self.cfg_optimization_test(insts, expected, consts=list(range(5)))
|
||||||
|
|
||||||
|
|
117
Python/compile.c
117
Python/compile.c
|
@ -9704,56 +9704,77 @@ instructions_to_cfg(PyObject *instructions, cfg_builder *g)
|
||||||
{
|
{
|
||||||
assert(PyList_Check(instructions));
|
assert(PyList_Check(instructions));
|
||||||
|
|
||||||
Py_ssize_t instr_size = PyList_GET_SIZE(instructions);
|
Py_ssize_t num_insts = PyList_GET_SIZE(instructions);
|
||||||
for (Py_ssize_t i = 0; i < instr_size; i++) {
|
bool *is_target = PyMem_Calloc(num_insts, sizeof(bool));
|
||||||
|
if (is_target == NULL) {
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
for (Py_ssize_t i = 0; i < num_insts; i++) {
|
||||||
PyObject *item = PyList_GET_ITEM(instructions, i);
|
PyObject *item = PyList_GET_ITEM(instructions, i);
|
||||||
if (PyLong_Check(item)) {
|
if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 6) {
|
||||||
int lbl_id = PyLong_AsLong(item);
|
PyErr_SetString(PyExc_ValueError, "expected a 6-tuple");
|
||||||
if (PyErr_Occurred()) {
|
goto error;
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
if (lbl_id <= 0 || lbl_id > instr_size) {
|
|
||||||
/* expect label in a reasonable range */
|
|
||||||
PyErr_SetString(PyExc_ValueError, "label out of range");
|
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
jump_target_label lbl = {lbl_id};
|
|
||||||
RETURN_IF_ERROR(cfg_builder_use_label(g, lbl));
|
|
||||||
}
|
}
|
||||||
else {
|
int opcode = PyLong_AsLong(PyTuple_GET_ITEM(item, 0));
|
||||||
if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 6) {
|
if (PyErr_Occurred()) {
|
||||||
PyErr_SetString(PyExc_ValueError, "expected a 6-tuple");
|
goto error;
|
||||||
return ERROR;
|
}
|
||||||
}
|
if (HAS_TARGET(opcode)) {
|
||||||
int opcode = PyLong_AsLong(PyTuple_GET_ITEM(item, 0));
|
|
||||||
if (PyErr_Occurred()) {
|
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
int oparg = PyLong_AsLong(PyTuple_GET_ITEM(item, 1));
|
int oparg = PyLong_AsLong(PyTuple_GET_ITEM(item, 1));
|
||||||
if (PyErr_Occurred()) {
|
if (PyErr_Occurred()) {
|
||||||
return ERROR;
|
goto error;
|
||||||
}
|
}
|
||||||
location loc;
|
if (oparg < 0 || oparg >= num_insts) {
|
||||||
loc.lineno = PyLong_AsLong(PyTuple_GET_ITEM(item, 2));
|
PyErr_SetString(PyExc_ValueError, "label out of range");
|
||||||
if (PyErr_Occurred()) {
|
goto error;
|
||||||
return ERROR;
|
|
||||||
}
|
}
|
||||||
loc.end_lineno = PyLong_AsLong(PyTuple_GET_ITEM(item, 3));
|
is_target[oparg] = true;
|
||||||
if (PyErr_Occurred()) {
|
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
loc.col_offset = PyLong_AsLong(PyTuple_GET_ITEM(item, 4));
|
|
||||||
if (PyErr_Occurred()) {
|
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
loc.end_col_offset = PyLong_AsLong(PyTuple_GET_ITEM(item, 5));
|
|
||||||
if (PyErr_Occurred()) {
|
|
||||||
return ERROR;
|
|
||||||
}
|
|
||||||
RETURN_IF_ERROR(cfg_builder_addop(g, opcode, oparg, loc));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < num_insts; i++) {
|
||||||
|
if (is_target[i]) {
|
||||||
|
jump_target_label lbl = {i};
|
||||||
|
RETURN_IF_ERROR(cfg_builder_use_label(g, lbl));
|
||||||
|
}
|
||||||
|
PyObject *item = PyList_GET_ITEM(instructions, i);
|
||||||
|
if (!PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 6) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "expected a 6-tuple");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
int opcode = PyLong_AsLong(PyTuple_GET_ITEM(item, 0));
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
int oparg = PyLong_AsLong(PyTuple_GET_ITEM(item, 1));
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
location loc;
|
||||||
|
loc.lineno = PyLong_AsLong(PyTuple_GET_ITEM(item, 2));
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
loc.end_lineno = PyLong_AsLong(PyTuple_GET_ITEM(item, 3));
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
loc.col_offset = PyLong_AsLong(PyTuple_GET_ITEM(item, 4));
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
loc.end_col_offset = PyLong_AsLong(PyTuple_GET_ITEM(item, 5));
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
RETURN_IF_ERROR(cfg_builder_addop(g, opcode, oparg, loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
PyMem_Free(is_target);
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
|
error:
|
||||||
|
PyMem_Free(is_target);
|
||||||
|
return ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -9763,20 +9784,12 @@ cfg_to_instructions(cfg_builder *g)
|
||||||
if (instructions == NULL) {
|
if (instructions == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
int lbl = 1;
|
int lbl = 0;
|
||||||
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
|
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
|
||||||
b->b_label = lbl++;
|
b->b_label = lbl;
|
||||||
|
lbl += b->b_iused;
|
||||||
}
|
}
|
||||||
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
|
for (basicblock *b = g->g_entryblock; b != NULL; b = b->b_next) {
|
||||||
PyObject *lbl = PyLong_FromLong(b->b_label);
|
|
||||||
if (lbl == NULL) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (PyList_Append(instructions, lbl) != 0) {
|
|
||||||
Py_DECREF(lbl);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
Py_DECREF(lbl);
|
|
||||||
for (int i = 0; i < b->b_iused; i++) {
|
for (int i = 0; i < b->b_iused; i++) {
|
||||||
struct instr *instr = &b->b_instr[i];
|
struct instr *instr = &b->b_instr[i];
|
||||||
location loc = instr->i_loc;
|
location loc = instr->i_loc;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue