diff -r 7f7988ea908f Lib/test/test_peepholer.py --- a/Lib/test/test_peepholer.py Sat Apr 09 09:08:26 2016 +0300 +++ b/Lib/test/test_peepholer.py Sat Apr 09 16:53:04 2016 +0600 @@ -307,6 +307,29 @@ self.assertFalse(instr.opname.startswith('BINARY_')) self.assertFalse(instr.opname.startswith('BUILD_')) + def test_folding_of_compareops_on_constants(self): + for line, elem in ( + ('a = (1 > 2)', False), + ('a = (3 < 4)', True), + ('a = (5 == 6)', False), + ('a = (6 != 7)', True), + ('a = (7 >= 8)', False), + ('a = (8 <= 9)', True), + ('a = (10 is 11)', False), + ('a = (12 is not 13)', True), + ('a = 14 in (15, 16, 17)', False), + ('a = 18 not in (19, 20, 21)', True), + # with inversion of "is", "is not", "in", "not in" + ('a = not (22 is 22)', False), + ('a = not (23 is not 24)', False), + ('a = not 25 in (26, 27, 28)', True), + ('a = not 29 not in (29, 30)', True), + ): + code = compile(line, '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', elem) + for instr in dis.get_instructions(code): + self.assertFalse(instr.opname == 'COMPARE_OP') + class TestBuglets(unittest.TestCase): diff -r 7f7988ea908f Python/peephole.c --- a/Python/peephole.c Sat Apr 09 09:08:26 2016 +0300 +++ b/Python/peephole.c Sat Apr 09 16:53:04 2016 +0600 @@ -289,6 +289,84 @@ return 1; } +static int +fold_compareops_on_constants(unsigned char *codestr, PyObject *consts, PyObject **objs, Py_ssize_t op) +{ + PyObject *newconst, *v, *w; + Py_ssize_t len_consts, size; + int cmp_res = 0; + + /* Pre-conditions */ + assert(PyList_CheckExact(consts)); + + /* Create new constant */ + v = objs[0]; + w = objs[1]; + switch (op) { + case PyCmp_IS: + newconst = (v == w) ? Py_True : Py_False; + Py_INCREF(newconst); + break; + case PyCmp_IS_NOT: + newconst = (v != w) ? Py_True : Py_False; + Py_INCREF(newconst); + break; + case PyCmp_IN: + case PyCmp_NOT_IN: + cmp_res = PySequence_Contains(w, v); + if (cmp_res < 0) { + newconst = NULL; + } else { + if (op == PyCmp_NOT_IN) + cmp_res = !cmp_res; + newconst = cmp_res ? Py_True : Py_False; + Py_INCREF(newconst); + } + break; + case PyCmp_LT: + case PyCmp_LE: + case PyCmp_EQ: + case PyCmp_NE: + case PyCmp_GT: + case PyCmp_GE: + newconst = PyObject_RichCompare(v, w, op); + break; + default: + /* Called with an unknown compare operator */ + PyErr_Format(PyExc_SystemError, + "unexpected compare operator %d on a constant", + op); + return 0; + } + + if (newconst == NULL) { + if(!PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) + PyErr_Clear(); + return 0; + } + size = PyObject_Size(newconst); + if (size == -1) { + if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) + return 0; + PyErr_Clear(); + } else if (size > 20) { + Py_DECREF(newconst); + return 0; + } + + /* Append folded constant into consts table */ + len_consts = PyList_GET_SIZE(consts); + if (PyList_Append(consts, newconst)) { + Py_DECREF(newconst); + return 0; + } + Py_DECREF(newconst); + + codestr[0] = LOAD_CONST; + SETARG(codestr, 0, len_consts); + return 1; +} + static unsigned int * markblocks(unsigned char *code, Py_ssize_t len) { @@ -449,12 +527,24 @@ */ case COMPARE_OP: j = GETARG(codestr, i); - if (j < 6 || j > 9 || - codestr[i+3] != UNARY_NOT || - !ISBASICBLOCK(blocks,i,4)) - continue; - SETARG(codestr, i, (j^1)); - codestr[i+3] = NOP; + if (j >= PyCmp_IN && j <= PyCmp_IS_NOT && + codestr[i+3] == UNARY_NOT && + ISBASICBLOCK(blocks,i,4)) { + j ^= 1; + SETARG(codestr, i, j); + codestr[i+3] = NOP; + } + h = CONST_STACK_OP_LASTN(2); + assert((h >= 0 || CONST_STACK_LEN() < 2)); + if (h >= 0 && + ISBASICBLOCK(blocks, h, i-h+1) && + j >= PyCmp_LT && j <= PyCmp_IS_NOT && + fold_compareops_on_constants(&codestr[i], consts, CONST_STACK_LASTN(2), j)) { + memset(&codestr[h], NOP, i - h); + assert(codestr[i] == LOAD_CONST); + CONST_STACK_POP(2); + CONST_STACK_PUSH_OP(i); + } break; /* Skip over LOAD_CONST trueconst