]> granicus.if.org Git - python/commitdiff
SF patch #1031667: Fold tuples of constants into a single constant
authorRaymond Hettinger <python@rcn.com>
Wed, 22 Sep 2004 18:44:21 +0000 (18:44 +0000)
committerRaymond Hettinger <python@rcn.com>
Wed, 22 Sep 2004 18:44:21 +0000 (18:44 +0000)
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
Misc/NEWS
Python/compile.c
Python/import.c

index 33eb0f57574504685bb7335dd3161a2f137eceb5..913f805e12a73e4c2fcba76b77184666fe9a3690 100644 (file)
@@ -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):
index b87f1281f61a68f86a5e7fdff4fd3086d4f06e71..3c2734b2473aa9bbdbed65e4232ef17b2ba1f1a9 100644 (file)
--- 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
index e8f4d67d173de4061a477958ac977665a958ea15..9beb7da7994f348f1f28503528f360964f7e277f 100644 (file)
@@ -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<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 *
 markblocks(unsigned char *code, int len)
 {
@@ -423,6 +475,21 @@ markblocks(unsigned char *code, int len)
        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 *
 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<codelen ; i += CODESIZE(codestr[i])) {
                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));
                        if (name == NULL  ||  strcmp(name, "None") != 0)
                                continue;
-                       for (j=0 ; j < PyTuple_GET_SIZE(consts) ; j++) {
-                               if (PyTuple_GET_ITEM(consts, j) == Py_None) {
+                       for (j=0 ; j < PyList_GET_SIZE(consts) ; j++) {
+                               if (PyList_GET_ITEM(consts, j) == Py_None) {
                                        codestr[i] = LOAD_CONST;
                                        SETARG(codestr, i, j);
                                        break;
@@ -525,17 +592,28 @@ optimize_code(PyObject *code, PyObject* consts, PyObject *names, PyObject *linen
                        j = GETARG(codestr, i);
                        if (codestr[i+3] != JUMP_IF_FALSE  ||
                            codestr[i+6] != POP_TOP  ||
-                           !ISBASICBLOCK(blocks,i,7) ||
-                           !PyObject_IsTrue(PyTuple_GET_ITEM(consts, j)))
+                           !ISBASICBLOCK(blocks,i,7)  ||
+                           !PyObject_IsTrue(PyList_GET_ITEM(consts, j)))
                                continue;
                        memset(codestr+i, NOP, 7);
                        nops += 7;
                        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 3 UNPACK_SEQN 3 with ROT3 ROT2. */
                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:
                        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,
index 8bbd31b5cc31605aa7246deae269fc83e01e42a6..61e4313fbb49f8be7fff3bfc46a8e73f7a6bd057 100644 (file)
@@ -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