mirror of
https://github.com/python/cpython.git
synced 2025-08-03 00:23:06 +00:00
SF patch #1031667: Fold tuples of constants into a single constant
Example: >>> import dis >>> dis.dis(compile('1,2,3', '', 'eval')) 0 0 LOAD_CONST 3 ((1, 2, 3)) 3 RETURN_VALUE
This commit is contained in:
parent
0318a939dd
commit
2c31a058eb
4 changed files with 109 additions and 15 deletions
|
@ -64,15 +64,25 @@ class TestTranforms(unittest.TestCase):
|
||||||
|
|
||||||
def test_pack_unpack(self):
|
def test_pack_unpack(self):
|
||||||
for line, elem in (
|
for line, elem in (
|
||||||
('a, = 1,', 'LOAD_CONST',),
|
('a, = a,', 'LOAD_CONST',),
|
||||||
('a, b = 1, 2', 'ROT_TWO',),
|
('a, b = a, b', 'ROT_TWO',),
|
||||||
('a, b, c = 1, 2, 3', 'ROT_THREE',),
|
('a, b, c = a, b, c', 'ROT_THREE',),
|
||||||
):
|
):
|
||||||
asm = dis_single(line)
|
asm = dis_single(line)
|
||||||
self.assert_(elem in asm)
|
self.assert_(elem in asm)
|
||||||
self.assert_('BUILD_TUPLE' not in asm)
|
self.assert_('BUILD_TUPLE' not in asm)
|
||||||
self.assert_('UNPACK_TUPLE' not in asm)
|
self.assert_('UNPACK_TUPLE' not in asm)
|
||||||
|
|
||||||
|
def test_folding_of_tuples_of_constants(self):
|
||||||
|
for line, elem in (
|
||||||
|
('a = 1,2,3', '((1, 2, 3))',),
|
||||||
|
('("a","b","c")', "(('a', 'b', 'c'))",),
|
||||||
|
('a,b,c = 1,2,3', '((1, 2, 3))',),
|
||||||
|
):
|
||||||
|
asm = dis_single(line)
|
||||||
|
self.assert_(elem in asm)
|
||||||
|
self.assert_('BUILD_TUPLE' not in asm)
|
||||||
|
|
||||||
def test_elim_extra_return(self):
|
def test_elim_extra_return(self):
|
||||||
# RETURN LOAD_CONST None RETURN --> RETURN
|
# RETURN LOAD_CONST None RETURN --> RETURN
|
||||||
def f(x):
|
def f(x):
|
||||||
|
|
|
@ -12,6 +12,9 @@ What's New in Python 2.4 beta 1?
|
||||||
Core and builtins
|
Core and builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- The bytecode optimizer now folds tuples of constants into a single
|
||||||
|
constant.
|
||||||
|
|
||||||
- PyLong_AsUnsignedLong[Mask] now support int objects as well.
|
- PyLong_AsUnsignedLong[Mask] now support int objects as well.
|
||||||
|
|
||||||
Extension modules
|
Extension modules
|
||||||
|
|
102
Python/compile.c
102
Python/compile.c
|
@ -380,6 +380,8 @@ intern_strings(PyObject *tuple)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Begin: Peephole optimizations ----------------------------------------- */
|
||||||
|
|
||||||
#define GETARG(arr, i) ((int)((arr[i+2]<<8) + arr[i+1]))
|
#define GETARG(arr, i) ((int)((arr[i+2]<<8) + arr[i+1]))
|
||||||
#define UNCONDITIONAL_JUMP(op) (op==JUMP_ABSOLUTE || op==JUMP_FORWARD)
|
#define UNCONDITIONAL_JUMP(op) (op==JUMP_ABSOLUTE || op==JUMP_FORWARD)
|
||||||
#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE || op==CONTINUE_LOOP)
|
#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE || op==CONTINUE_LOOP)
|
||||||
|
@ -388,6 +390,56 @@ intern_strings(PyObject *tuple)
|
||||||
#define CODESIZE(op) (HAS_ARG(op) ? 3 : 1)
|
#define CODESIZE(op) (HAS_ARG(op) ? 3 : 1)
|
||||||
#define ISBASICBLOCK(blocks, start, bytes) (blocks[start]==blocks[start+bytes-1])
|
#define ISBASICBLOCK(blocks, start, bytes) (blocks[start]==blocks[start+bytes-1])
|
||||||
|
|
||||||
|
/* Replace LOAD_CONST c1. LOAD_CONST c2 ... LOAD_CONST cn BUILD_TUPLE n
|
||||||
|
with LOAD_CONST (c1, c2, ... cn).
|
||||||
|
The consts table must still be in list form so that the
|
||||||
|
new constant (c1, c2, ... cn) can be appended.
|
||||||
|
Called with codestr pointing to the first LOAD_CONST.
|
||||||
|
Bails out with no change if one or more of the LOAD_CONSTs is missing. */
|
||||||
|
static int
|
||||||
|
tuple_of_constants(unsigned char *codestr, int n, PyObject *consts)
|
||||||
|
{
|
||||||
|
PyObject *newconst, *constant;
|
||||||
|
int i, arg, len_consts;
|
||||||
|
|
||||||
|
/* Pre-conditions */
|
||||||
|
assert(PyList_CheckExact(consts));
|
||||||
|
assert(codestr[0] == LOAD_CONST);
|
||||||
|
assert(codestr[n*3] == BUILD_TUPLE);
|
||||||
|
assert(GETARG(codestr, (n*3)) == n);
|
||||||
|
|
||||||
|
/* Verify chain of n load_constants */
|
||||||
|
for (i=0 ; i<n ; i++)
|
||||||
|
if (codestr[i*3] != LOAD_CONST)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Buildup new tuple of constants */
|
||||||
|
newconst = PyTuple_New(n);
|
||||||
|
if (newconst == NULL)
|
||||||
|
return 0;
|
||||||
|
for (i=0 ; i<n ; i++) {
|
||||||
|
arg = GETARG(codestr, (i*3));
|
||||||
|
constant = PyList_GET_ITEM(consts, arg);
|
||||||
|
Py_INCREF(constant);
|
||||||
|
PyTuple_SET_ITEM(newconst, i, constant);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append folded constant onto consts */
|
||||||
|
len_consts = PyList_GET_SIZE(consts);
|
||||||
|
if (PyList_Append(consts, newconst)) {
|
||||||
|
Py_DECREF(newconst);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Py_DECREF(newconst);
|
||||||
|
|
||||||
|
/* Write NOPs over old LOAD_CONSTS and
|
||||||
|
add a new LOAD_CONST newconst on top of the BUILD_TUPLE n */
|
||||||
|
memset(codestr, NOP, n*3);
|
||||||
|
codestr[n*3] = LOAD_CONST;
|
||||||
|
SETARG(codestr, (n*3), len_consts);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned int *
|
static unsigned int *
|
||||||
markblocks(unsigned char *code, int len)
|
markblocks(unsigned char *code, int len)
|
||||||
{
|
{
|
||||||
|
@ -423,6 +475,21 @@ markblocks(unsigned char *code, int len)
|
||||||
return blocks;
|
return blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Perform basic peephole optimizations to components of a code object.
|
||||||
|
The consts object should still be in list form to allow new constants
|
||||||
|
to be appended.
|
||||||
|
|
||||||
|
To keep the optimizer simple, it bails out (does nothing) for code
|
||||||
|
containing extended arguments or that has a length over 32,700. That
|
||||||
|
allows us to avoid overflow and sign issues. Likewise, it bails when
|
||||||
|
the lineno table has complex encoding for gaps >= 255.
|
||||||
|
|
||||||
|
Optimizations are restricted to simple transformations occuring within a
|
||||||
|
single basic block. All transformations keep the code size the same or
|
||||||
|
smaller. For those that reduce size, the gaps are initially filled with
|
||||||
|
NOPs. Later those NOPs are removed and the jump addresses retargeted in
|
||||||
|
a single pass. Line numbering is adjusted accordingly. */
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
optimize_code(PyObject *code, PyObject* consts, PyObject *names, PyObject *lineno_obj)
|
optimize_code(PyObject *code, PyObject* consts, PyObject *names, PyObject *lineno_obj)
|
||||||
{
|
{
|
||||||
|
@ -447,7 +514,7 @@ optimize_code(PyObject *code, PyObject* consts, PyObject *names, PyObject *linen
|
||||||
|
|
||||||
/* Avoid situations where jump retargeting could overflow */
|
/* Avoid situations where jump retargeting could overflow */
|
||||||
codelen = PyString_Size(code);
|
codelen = PyString_Size(code);
|
||||||
if (codelen > 32000)
|
if (codelen > 32700)
|
||||||
goto exitUnchanged;
|
goto exitUnchanged;
|
||||||
|
|
||||||
/* Make a modifiable copy of the code string */
|
/* Make a modifiable copy of the code string */
|
||||||
|
@ -464,7 +531,7 @@ optimize_code(PyObject *code, PyObject* consts, PyObject *names, PyObject *linen
|
||||||
blocks = markblocks(codestr, codelen);
|
blocks = markblocks(codestr, codelen);
|
||||||
if (blocks == NULL)
|
if (blocks == NULL)
|
||||||
goto exitUnchanged;
|
goto exitUnchanged;
|
||||||
assert(PyTuple_Check(consts));
|
assert(PyList_Check(consts));
|
||||||
|
|
||||||
for (i=0, nops=0 ; i<codelen ; i += CODESIZE(codestr[i])) {
|
for (i=0, nops=0 ; i<codelen ; i += CODESIZE(codestr[i])) {
|
||||||
addrmap[i] = i - nops;
|
addrmap[i] = i - nops;
|
||||||
|
@ -511,8 +578,8 @@ optimize_code(PyObject *code, PyObject* consts, PyObject *names, PyObject *linen
|
||||||
name = PyString_AsString(PyTuple_GET_ITEM(names, j));
|
name = PyString_AsString(PyTuple_GET_ITEM(names, j));
|
||||||
if (name == NULL || strcmp(name, "None") != 0)
|
if (name == NULL || strcmp(name, "None") != 0)
|
||||||
continue;
|
continue;
|
||||||
for (j=0 ; j < PyTuple_GET_SIZE(consts) ; j++) {
|
for (j=0 ; j < PyList_GET_SIZE(consts) ; j++) {
|
||||||
if (PyTuple_GET_ITEM(consts, j) == Py_None) {
|
if (PyList_GET_ITEM(consts, j) == Py_None) {
|
||||||
codestr[i] = LOAD_CONST;
|
codestr[i] = LOAD_CONST;
|
||||||
SETARG(codestr, i, j);
|
SETARG(codestr, i, j);
|
||||||
break;
|
break;
|
||||||
|
@ -525,17 +592,28 @@ optimize_code(PyObject *code, PyObject* consts, PyObject *names, PyObject *linen
|
||||||
j = GETARG(codestr, i);
|
j = GETARG(codestr, i);
|
||||||
if (codestr[i+3] != JUMP_IF_FALSE ||
|
if (codestr[i+3] != JUMP_IF_FALSE ||
|
||||||
codestr[i+6] != POP_TOP ||
|
codestr[i+6] != POP_TOP ||
|
||||||
!ISBASICBLOCK(blocks,i,7) ||
|
!ISBASICBLOCK(blocks,i,7) ||
|
||||||
!PyObject_IsTrue(PyTuple_GET_ITEM(consts, j)))
|
!PyObject_IsTrue(PyList_GET_ITEM(consts, j)))
|
||||||
continue;
|
continue;
|
||||||
memset(codestr+i, NOP, 7);
|
memset(codestr+i, NOP, 7);
|
||||||
nops += 7;
|
nops += 7;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Skip over BUILD_SEQN 1 UNPACK_SEQN 1.
|
/* Try to fold tuples of constants.
|
||||||
|
Skip over BUILD_SEQN 1 UNPACK_SEQN 1.
|
||||||
Replace BUILD_SEQN 2 UNPACK_SEQN 2 with ROT2.
|
Replace BUILD_SEQN 2 UNPACK_SEQN 2 with ROT2.
|
||||||
Replace BUILD_SEQN 3 UNPACK_SEQN 3 with ROT3 ROT2. */
|
Replace BUILD_SEQN 3 UNPACK_SEQN 3 with ROT3 ROT2. */
|
||||||
case BUILD_TUPLE:
|
case BUILD_TUPLE:
|
||||||
|
j = GETARG(codestr, i);
|
||||||
|
h = i - 3 * j;
|
||||||
|
if (h >= 0 &&
|
||||||
|
codestr[h] == LOAD_CONST &&
|
||||||
|
ISBASICBLOCK(blocks, h, 3*(j+1)) &&
|
||||||
|
tuple_of_constants(&codestr[h], j, consts)) {
|
||||||
|
nops += 3 * j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Intentional fallthrough */
|
||||||
case BUILD_LIST:
|
case BUILD_LIST:
|
||||||
j = GETARG(codestr, i);
|
j = GETARG(codestr, i);
|
||||||
if (codestr[i+3] != UNPACK_SEQUENCE ||
|
if (codestr[i+3] != UNPACK_SEQUENCE ||
|
||||||
|
@ -610,8 +688,8 @@ optimize_code(PyObject *code, PyObject* consts, PyObject *names, PyObject *linen
|
||||||
|
|
||||||
/* Replace RETURN LOAD_CONST None RETURN with just RETURN */
|
/* Replace RETURN LOAD_CONST None RETURN with just RETURN */
|
||||||
case RETURN_VALUE:
|
case RETURN_VALUE:
|
||||||
if (i+4 >= codelen ||
|
if (i+4 >= codelen ||
|
||||||
codestr[i+4] != RETURN_VALUE ||
|
codestr[i+4] != RETURN_VALUE ||
|
||||||
!ISBASICBLOCK(blocks,i,5))
|
!ISBASICBLOCK(blocks,i,5))
|
||||||
continue;
|
continue;
|
||||||
memset(codestr+i+1, NOP, 4);
|
memset(codestr+i+1, NOP, 4);
|
||||||
|
@ -677,6 +755,8 @@ exitUnchanged:
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* End: Peephole optimizations ----------------------------------------- */
|
||||||
|
|
||||||
PyCodeObject *
|
PyCodeObject *
|
||||||
PyCode_New(int argcount, int nlocals, int stacksize, int flags,
|
PyCode_New(int argcount, int nlocals, int stacksize, int flags,
|
||||||
PyObject *code, PyObject *consts, PyObject *names,
|
PyObject *code, PyObject *consts, PyObject *names,
|
||||||
|
@ -4899,7 +4979,6 @@ jcompile(node *n, const char *filename, struct compiling *base,
|
||||||
if (sc.c_errors == 0) {
|
if (sc.c_errors == 0) {
|
||||||
PyObject *consts, *names, *varnames, *filename, *name,
|
PyObject *consts, *names, *varnames, *filename, *name,
|
||||||
*freevars, *cellvars, *code;
|
*freevars, *cellvars, *code;
|
||||||
consts = PyList_AsTuple(sc.c_consts);
|
|
||||||
names = PyList_AsTuple(sc.c_names);
|
names = PyList_AsTuple(sc.c_names);
|
||||||
varnames = PyList_AsTuple(sc.c_varnames);
|
varnames = PyList_AsTuple(sc.c_varnames);
|
||||||
cellvars = dict_keys_inorder(sc.c_cellvars, 0);
|
cellvars = dict_keys_inorder(sc.c_cellvars, 0);
|
||||||
|
@ -4907,7 +4986,8 @@ jcompile(node *n, const char *filename, struct compiling *base,
|
||||||
PyTuple_GET_SIZE(cellvars));
|
PyTuple_GET_SIZE(cellvars));
|
||||||
filename = PyString_InternFromString(sc.c_filename);
|
filename = PyString_InternFromString(sc.c_filename);
|
||||||
name = PyString_InternFromString(sc.c_name);
|
name = PyString_InternFromString(sc.c_name);
|
||||||
code = optimize_code(sc.c_code, consts, names, sc.c_lnotab);
|
code = optimize_code(sc.c_code, sc.c_consts, names, sc.c_lnotab);
|
||||||
|
consts = PyList_AsTuple(sc.c_consts);
|
||||||
if (!PyErr_Occurred())
|
if (!PyErr_Occurred())
|
||||||
co = PyCode_New(sc.c_argcount,
|
co = PyCode_New(sc.c_argcount,
|
||||||
sc.c_nlocals,
|
sc.c_nlocals,
|
||||||
|
|
|
@ -49,8 +49,9 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *);
|
||||||
Python 2.3a0: 62011 (!)
|
Python 2.3a0: 62011 (!)
|
||||||
Python 2.4a0: 62041
|
Python 2.4a0: 62041
|
||||||
Python 2.4a3: 62051
|
Python 2.4a3: 62051
|
||||||
|
Python 2.4b1: 62061
|
||||||
*/
|
*/
|
||||||
#define MAGIC (62051 | ((long)'\r'<<16) | ((long)'\n'<<24))
|
#define MAGIC (62061 | ((long)'\r'<<16) | ((long)'\n'<<24))
|
||||||
|
|
||||||
/* Magic word as global; note that _PyImport_Init() can change the
|
/* Magic word as global; note that _PyImport_Init() can change the
|
||||||
value of this global to accommodate for alterations of how the
|
value of this global to accommodate for alterations of how the
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue