From 2c31a058eb719da8908ea9d9c2f1b748ecc7a4ca Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 22 Sep 2004 18:44:21 +0000 Subject: 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 --- Lib/test/test_peepholer.py | 16 +++++-- Misc/NEWS | 3 ++ Python/compile.c | 102 ++++++++++++++++++++++++++++++++++++++++----- Python/import.c | 3 +- 4 files changed, 109 insertions(+), 15 deletions(-) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 33eb0f5..913f805 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -64,15 +64,25 @@ class TestTranforms(unittest.TestCase): def test_pack_unpack(self): for line, elem in ( - ('a, = 1,', 'LOAD_CONST',), - ('a, b = 1, 2', 'ROT_TWO',), - ('a, b, c = 1, 2, 3', 'ROT_THREE',), + ('a, = a,', 'LOAD_CONST',), + ('a, b = a, b', 'ROT_TWO',), + ('a, b, c = a, b, c', 'ROT_THREE',), ): asm = dis_single(line) self.assert_(elem in asm) self.assert_('BUILD_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): # RETURN LOAD_CONST None RETURN --> RETURN def f(x): diff --git a/Misc/NEWS b/Misc/NEWS index b87f128..3c2734b 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,9 @@ What's New in Python 2.4 beta 1? Core and builtins ----------------- +- The bytecode optimizer now folds tuples of constants into a single + constant. + - PyLong_AsUnsignedLong[Mask] now support int objects as well. Extension modules diff --git a/Python/compile.c b/Python/compile.c index e8f4d67..9beb7da 100644 --- a/Python/compile.c +++ b/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 UNCONDITIONAL_JUMP(op) (op==JUMP_ABSOLUTE || op==JUMP_FORWARD) #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 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= 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 * 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 */ codelen = PyString_Size(code); - if (codelen > 32000) + if (codelen > 32700) goto exitUnchanged; /* 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); if (blocks == NULL) goto exitUnchanged; - assert(PyTuple_Check(consts)); + assert(PyList_Check(consts)); for (i=0, nops=0 ; i= 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: j = GETARG(codestr, i); 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 */ case RETURN_VALUE: - if (i+4 >= codelen || - codestr[i+4] != RETURN_VALUE || + if (i+4 >= codelen || + codestr[i+4] != RETURN_VALUE || !ISBASICBLOCK(blocks,i,5)) continue; memset(codestr+i+1, NOP, 4); @@ -677,6 +755,8 @@ exitUnchanged: return code; } +/* End: Peephole optimizations ----------------------------------------- */ + PyCodeObject * PyCode_New(int argcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, @@ -4899,7 +4979,6 @@ jcompile(node *n, const char *filename, struct compiling *base, if (sc.c_errors == 0) { PyObject *consts, *names, *varnames, *filename, *name, *freevars, *cellvars, *code; - consts = PyList_AsTuple(sc.c_consts); names = PyList_AsTuple(sc.c_names); varnames = PyList_AsTuple(sc.c_varnames); 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)); filename = PyString_InternFromString(sc.c_filename); 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()) co = PyCode_New(sc.c_argcount, sc.c_nlocals, diff --git a/Python/import.c b/Python/import.c index 8bbd31b..61e4313 100644 --- a/Python/import.c +++ b/Python/import.c @@ -49,8 +49,9 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *); Python 2.3a0: 62011 (!) Python 2.4a0: 62041 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 value of this global to accommodate for alterations of how the -- cgit v0.12