PageRenderTime 36ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/Lib/test/test_peepholer.py

https://github.com/albertz/CPython
Python | 336 lines | 324 code | 7 blank | 5 comment | 16 complexity | 218ad5e20a3d37c1cde186e1e0fa54f8 MD5 | raw file
  1. import dis
  2. import unittest
  3. from test.bytecode_helper import BytecodeTestCase
  4. class TestTranforms(BytecodeTestCase):
  5. def test_unot(self):
  6. # UNARY_NOT POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE'
  7. def unot(x):
  8. if not x == 2:
  9. del x
  10. self.assertNotInBytecode(unot, 'UNARY_NOT')
  11. self.assertNotInBytecode(unot, 'POP_JUMP_IF_FALSE')
  12. self.assertInBytecode(unot, 'POP_JUMP_IF_TRUE')
  13. def test_elim_inversion_of_is_or_in(self):
  14. for line, cmp_op in (
  15. ('not a is b', 'is not',),
  16. ('not a in b', 'not in',),
  17. ('not a is not b', 'is',),
  18. ('not a not in b', 'in',),
  19. ):
  20. code = compile(line, '', 'single')
  21. self.assertInBytecode(code, 'COMPARE_OP', cmp_op)
  22. def test_global_as_constant(self):
  23. # LOAD_GLOBAL None/True/False --> LOAD_CONST None/True/False
  24. def f():
  25. x = None
  26. x = None
  27. return x
  28. def g():
  29. x = True
  30. return x
  31. def h():
  32. x = False
  33. return x
  34. for func, elem in ((f, None), (g, True), (h, False)):
  35. self.assertNotInBytecode(func, 'LOAD_GLOBAL')
  36. self.assertInBytecode(func, 'LOAD_CONST', elem)
  37. def f():
  38. 'Adding a docstring made this test fail in Py2.5.0'
  39. return None
  40. self.assertNotInBytecode(f, 'LOAD_GLOBAL')
  41. self.assertInBytecode(f, 'LOAD_CONST', None)
  42. def test_while_one(self):
  43. # Skip over: LOAD_CONST trueconst POP_JUMP_IF_FALSE xx
  44. def f():
  45. while 1:
  46. pass
  47. return list
  48. for elem in ('LOAD_CONST', 'POP_JUMP_IF_FALSE'):
  49. self.assertNotInBytecode(f, elem)
  50. for elem in ('JUMP_ABSOLUTE',):
  51. self.assertInBytecode(f, elem)
  52. def test_pack_unpack(self):
  53. for line, elem in (
  54. ('a, = a,', 'LOAD_CONST',),
  55. ('a, b = a, b', 'ROT_TWO',),
  56. ('a, b, c = a, b, c', 'ROT_THREE',),
  57. ):
  58. code = compile(line,'','single')
  59. self.assertInBytecode(code, elem)
  60. self.assertNotInBytecode(code, 'BUILD_TUPLE')
  61. self.assertNotInBytecode(code, 'UNPACK_TUPLE')
  62. def test_folding_of_tuples_of_constants(self):
  63. for line, elem in (
  64. ('a = 1,2,3', (1, 2, 3)),
  65. ('("a","b","c")', ('a', 'b', 'c')),
  66. ('a,b,c = 1,2,3', (1, 2, 3)),
  67. ('(None, 1, None)', (None, 1, None)),
  68. ('((1, 2), 3, 4)', ((1, 2), 3, 4)),
  69. ):
  70. code = compile(line,'','single')
  71. self.assertInBytecode(code, 'LOAD_CONST', elem)
  72. self.assertNotInBytecode(code, 'BUILD_TUPLE')
  73. # Long tuples should be folded too.
  74. code = compile(repr(tuple(range(10000))),'','single')
  75. self.assertNotInBytecode(code, 'BUILD_TUPLE')
  76. # One LOAD_CONST for the tuple, one for the None return value
  77. load_consts = [instr for instr in dis.get_instructions(code)
  78. if instr.opname == 'LOAD_CONST']
  79. self.assertEqual(len(load_consts), 2)
  80. # Bug 1053819: Tuple of constants misidentified when presented with:
  81. # . . . opcode_with_arg 100 unary_opcode BUILD_TUPLE 1 . . .
  82. # The following would segfault upon compilation
  83. def crater():
  84. (~[
  85. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  86. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  87. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  88. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  89. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  90. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  91. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  92. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  93. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  94. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  95. ],)
  96. def test_folding_of_lists_of_constants(self):
  97. for line, elem in (
  98. # in/not in constants with BUILD_LIST should be folded to a tuple:
  99. ('a in [1,2,3]', (1, 2, 3)),
  100. ('a not in ["a","b","c"]', ('a', 'b', 'c')),
  101. ('a in [None, 1, None]', (None, 1, None)),
  102. ('a not in [(1, 2), 3, 4]', ((1, 2), 3, 4)),
  103. ):
  104. code = compile(line, '', 'single')
  105. self.assertInBytecode(code, 'LOAD_CONST', elem)
  106. self.assertNotInBytecode(code, 'BUILD_LIST')
  107. def test_folding_of_sets_of_constants(self):
  108. for line, elem in (
  109. # in/not in constants with BUILD_SET should be folded to a frozenset:
  110. ('a in {1,2,3}', frozenset({1, 2, 3})),
  111. ('a not in {"a","b","c"}', frozenset({'a', 'c', 'b'})),
  112. ('a in {None, 1, None}', frozenset({1, None})),
  113. ('a not in {(1, 2), 3, 4}', frozenset({(1, 2), 3, 4})),
  114. ('a in {1, 2, 3, 3, 2, 1}', frozenset({1, 2, 3})),
  115. ):
  116. code = compile(line, '', 'single')
  117. self.assertNotInBytecode(code, 'BUILD_SET')
  118. self.assertInBytecode(code, 'LOAD_CONST', elem)
  119. # Ensure that the resulting code actually works:
  120. def f(a):
  121. return a in {1, 2, 3}
  122. def g(a):
  123. return a not in {1, 2, 3}
  124. self.assertTrue(f(3))
  125. self.assertTrue(not f(4))
  126. self.assertTrue(not g(3))
  127. self.assertTrue(g(4))
  128. def test_folding_of_binops_on_constants(self):
  129. for line, elem in (
  130. ('a = 2+3+4', 9), # chained fold
  131. ('"@"*4', '@@@@'), # check string ops
  132. ('a="abc" + "def"', 'abcdef'), # check string ops
  133. ('a = 3**4', 81), # binary power
  134. ('a = 3*4', 12), # binary multiply
  135. ('a = 13//4', 3), # binary floor divide
  136. ('a = 14%4', 2), # binary modulo
  137. ('a = 2+3', 5), # binary add
  138. ('a = 13-4', 9), # binary subtract
  139. ('a = (12,13)[1]', 13), # binary subscr
  140. ('a = 13 << 2', 52), # binary lshift
  141. ('a = 13 >> 2', 3), # binary rshift
  142. ('a = 13 & 7', 5), # binary and
  143. ('a = 13 ^ 7', 10), # binary xor
  144. ('a = 13 | 7', 15), # binary or
  145. ):
  146. code = compile(line, '', 'single')
  147. self.assertInBytecode(code, 'LOAD_CONST', elem)
  148. for instr in dis.get_instructions(code):
  149. self.assertFalse(instr.opname.startswith('BINARY_'))
  150. # Verify that unfoldables are skipped
  151. code = compile('a=2+"b"', '', 'single')
  152. self.assertInBytecode(code, 'LOAD_CONST', 2)
  153. self.assertInBytecode(code, 'LOAD_CONST', 'b')
  154. # Verify that large sequences do not result from folding
  155. code = compile('a="x"*10000', '', 'single')
  156. self.assertInBytecode(code, 'LOAD_CONST', 10000)
  157. self.assertNotIn("x"*10000, code.co_consts)
  158. code = compile('a=1<<1000', '', 'single')
  159. self.assertInBytecode(code, 'LOAD_CONST', 1000)
  160. self.assertNotIn(1<<1000, code.co_consts)
  161. code = compile('a=2**1000', '', 'single')
  162. self.assertInBytecode(code, 'LOAD_CONST', 1000)
  163. self.assertNotIn(2**1000, code.co_consts)
  164. def test_binary_subscr_on_unicode(self):
  165. # valid code get optimized
  166. code = compile('"foo"[0]', '', 'single')
  167. self.assertInBytecode(code, 'LOAD_CONST', 'f')
  168. self.assertNotInBytecode(code, 'BINARY_SUBSCR')
  169. code = compile('"\u0061\uffff"[1]', '', 'single')
  170. self.assertInBytecode(code, 'LOAD_CONST', '\uffff')
  171. self.assertNotInBytecode(code,'BINARY_SUBSCR')
  172. # With PEP 393, non-BMP char get optimized
  173. code = compile('"\U00012345"[0]', '', 'single')
  174. self.assertInBytecode(code, 'LOAD_CONST', '\U00012345')
  175. self.assertNotInBytecode(code, 'BINARY_SUBSCR')
  176. # invalid code doesn't get optimized
  177. # out of range
  178. code = compile('"fuu"[10]', '', 'single')
  179. self.assertInBytecode(code, 'BINARY_SUBSCR')
  180. def test_folding_of_unaryops_on_constants(self):
  181. for line, elem in (
  182. ('-0.5', -0.5), # unary negative
  183. ('-0.0', -0.0), # -0.0
  184. ('-(1.0-1.0)', -0.0), # -0.0 after folding
  185. ('-0', 0), # -0
  186. ('~-2', 1), # unary invert
  187. ('+1', 1), # unary positive
  188. ):
  189. code = compile(line, '', 'single')
  190. self.assertInBytecode(code, 'LOAD_CONST', elem)
  191. for instr in dis.get_instructions(code):
  192. self.assertFalse(instr.opname.startswith('UNARY_'))
  193. # Check that -0.0 works after marshaling
  194. def negzero():
  195. return -(1.0-1.0)
  196. for instr in dis.get_instructions(code):
  197. self.assertFalse(instr.opname.startswith('UNARY_'))
  198. # Verify that unfoldables are skipped
  199. for line, elem, opname in (
  200. ('-"abc"', 'abc', 'UNARY_NEGATIVE'),
  201. ('~"abc"', 'abc', 'UNARY_INVERT'),
  202. ):
  203. code = compile(line, '', 'single')
  204. self.assertInBytecode(code, 'LOAD_CONST', elem)
  205. self.assertInBytecode(code, opname)
  206. def test_elim_extra_return(self):
  207. # RETURN LOAD_CONST None RETURN --> RETURN
  208. def f(x):
  209. return x
  210. self.assertNotInBytecode(f, 'LOAD_CONST', None)
  211. returns = [instr for instr in dis.get_instructions(f)
  212. if instr.opname == 'RETURN_VALUE']
  213. self.assertEqual(len(returns), 1)
  214. def test_elim_jump_to_return(self):
  215. # JUMP_FORWARD to RETURN --> RETURN
  216. def f(cond, true_value, false_value):
  217. return true_value if cond else false_value
  218. self.assertNotInBytecode(f, 'JUMP_FORWARD')
  219. self.assertNotInBytecode(f, 'JUMP_ABSOLUTE')
  220. returns = [instr for instr in dis.get_instructions(f)
  221. if instr.opname == 'RETURN_VALUE']
  222. self.assertEqual(len(returns), 2)
  223. def test_elim_jump_after_return1(self):
  224. # Eliminate dead code: jumps immediately after returns can't be reached
  225. def f(cond1, cond2):
  226. if cond1: return 1
  227. if cond2: return 2
  228. while 1:
  229. return 3
  230. while 1:
  231. if cond1: return 4
  232. return 5
  233. return 6
  234. self.assertNotInBytecode(f, 'JUMP_FORWARD')
  235. self.assertNotInBytecode(f, 'JUMP_ABSOLUTE')
  236. returns = [instr for instr in dis.get_instructions(f)
  237. if instr.opname == 'RETURN_VALUE']
  238. self.assertEqual(len(returns), 6)
  239. def test_elim_jump_after_return2(self):
  240. # Eliminate dead code: jumps immediately after returns can't be reached
  241. def f(cond1, cond2):
  242. while 1:
  243. if cond1: return 4
  244. self.assertNotInBytecode(f, 'JUMP_FORWARD')
  245. # There should be one jump for the while loop.
  246. returns = [instr for instr in dis.get_instructions(f)
  247. if instr.opname == 'JUMP_ABSOLUTE']
  248. self.assertEqual(len(returns), 1)
  249. returns = [instr for instr in dis.get_instructions(f)
  250. if instr.opname == 'RETURN_VALUE']
  251. self.assertEqual(len(returns), 2)
  252. def test_make_function_doesnt_bail(self):
  253. def f():
  254. def g()->1+1:
  255. pass
  256. return g
  257. self.assertNotInBytecode(f, 'BINARY_ADD')
  258. def test_constant_folding(self):
  259. # Issue #11244: aggressive constant folding.
  260. exprs = [
  261. '3 * -5',
  262. '-3 * 5',
  263. '2 * (3 * 4)',
  264. '(2 * 3) * 4',
  265. '(-1, 2, 3)',
  266. '(1, -2, 3)',
  267. '(1, 2, -3)',
  268. '(1, 2, -3) * 6',
  269. 'lambda x: x in {(3 * -5) + (-1 - 6), (1, -2, 3) * 2, None}',
  270. ]
  271. for e in exprs:
  272. code = compile(e, '', 'single')
  273. for instr in dis.get_instructions(code):
  274. self.assertFalse(instr.opname.startswith('UNARY_'))
  275. self.assertFalse(instr.opname.startswith('BINARY_'))
  276. self.assertFalse(instr.opname.startswith('BUILD_'))
  277. def test_condition_with_binop_with_bools(self):
  278. def f():
  279. if True or False:
  280. return 1
  281. return 0
  282. self.assertEqual(f(), 1)
  283. class TestBuglets(unittest.TestCase):
  284. def test_bug_11510(self):
  285. # folded constant set optimization was commingled with the tuple
  286. # unpacking optimization which would fail if the set had duplicate
  287. # elements so that the set length was unexpected
  288. def f():
  289. x, y = {1, 1}
  290. return x, y
  291. with self.assertRaises(ValueError):
  292. f()
  293. if __name__ == "__main__":
  294. unittest.main()