Index: Python/peephole.c =================================================================== --- Python/peephole.c (revision 61073) +++ Python/peephole.c (working copy) @@ -313,19 +313,19 @@ goto exitUnchanged; /* Make a modifiable copy of the code string */ - codestr = (unsigned char *)PyMem_Malloc(codelen); + codestr = (unsigned char *)PyMem_Malloc(codelen + 1); if (codestr == NULL) goto exitUnchanged; codestr = (unsigned char *)memcpy(codestr, PyString_AS_STRING(code), codelen); - /* Verify that RETURN_VALUE terminates the codestring. This allows + /* Ensure that RETURN_VALUE terminates the codestring. This allows the various transformation patterns to look ahead several instructions without additional checks to make sure they are not looking beyond the end of the code string. */ if (codestr[codelen-1] != RETURN_VALUE) - goto exitUnchanged; + codestr[codelen] = RETURN_VALUE; /* Mapping to new jump targets after NOPs are removed */ addrmap = (int *)PyMem_Malloc(codelen * sizeof(int)); @@ -550,17 +550,16 @@ case EXTENDED_ARG: goto exitUnchanged; - /* Replace RETURN LOAD_CONST None RETURN with just RETURN */ - /* Remove unreachable JUMPs after RETURN */ + /* Remove unreachable code completely */ + case RAISE_VARARGS: + case BREAK_LOOP: case RETURN_VALUE: - if (i+4 >= codelen) - continue; - if (codestr[i+4] == RETURN_VALUE && - ISBASICBLOCK(blocks,i,5)) - memset(codestr+i+1, NOP, 4); - else if (UNCONDITIONAL_JUMP(codestr[i+1]) && - ISBASICBLOCK(blocks,i,4)) - memset(codestr+i+1, NOP, 3); + j = i + CODESIZE(codestr[i]); + tgt = j; + while (tgt < codelen && blocks[tgt] == blocks[i]) + tgt += CODESIZE(codestr[tgt]); + if (tgt > j) + memset(codestr+j, NOP, tgt-j); break; } } Index: Lib/test/test_peepholer.py =================================================================== --- Lib/test/test_peepholer.py (revision 61073) +++ Lib/test/test_peepholer.py (working copy) @@ -202,7 +202,43 @@ self.assertEqual(asm.split().count('JUMP_ABSOLUTE'), 1) self.assertEqual(asm.split().count('RETURN_VALUE'), 2) + def test_elim_unreachable(self): + def f(): + return 1 + return 2 + asm = disassemble(f) + self.assertEqual(asm.split().count('RETURN_VALUE'), 1) + def f(): + while 1: + break + return + asm = disassemble(f) + self.assertEqual(asm.split().count('RETURN_VALUE'), 1) + def f(): + raise RuntimeError + asm = disassemble(f) + self.assert_('RETURN_VALUE' not in asm) + def f(): + return 1 + x+y + asm = disassemble(f) + self.assert_('LOAD_GLOBAL' not in asm) + # unreachable code that is not eliminated + def f(): + while 1: + continue + x + asm = disassemble(f) + self.assert_('LOAD_GLOBAL' in asm) + def f(): + while 1: + return + x + asm = disassemble(f) + self.assert_('LOAD_GLOBAL' in asm) + + def test_main(verbose=None): import sys from test import test_support