https://github.com/python/cpython/commit/674dbf3b3a72bd3d17298c2ead79f32edcff774a commit: 674dbf3b3a72bd3d17298c2ead79f32edcff774a branch: main author: Yan Yanchii <yyanc...@gmail.com> committer: iritkatriel <1055913+iritkatr...@users.noreply.github.com> date: 2025-03-28T10:10:22Z summary:
gh-126835: Refine constant folding tests in `test_peepholer.py::TestTranforms` (#131826) files: M Lib/test/test_peepholer.py diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 6de89c4043d3f7..8b97e76fa346ff 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -150,7 +150,7 @@ def test_pack_unpack(self): self.assertNotInBytecode(code, 'UNPACK_SEQUENCE') self.check_lnotab(code) - def test_folding_of_tuples_of_constants(self): + def test_constant_folding_tuples_of_constants(self): for line, elem in ( ('a = 1,2,3', (1, 2, 3)), ('("a","b","c")', ('a', 'b', 'c')), @@ -191,7 +191,7 @@ def crater(): ],) self.check_lnotab(crater) - def test_folding_of_lists_of_constants(self): + def test_constant_folding_lists_of_constants(self): for line, elem in ( # in/not in constants with BUILD_LIST should be folded to a tuple: ('a in [1,2,3]', (1, 2, 3)), @@ -205,7 +205,7 @@ def test_folding_of_lists_of_constants(self): self.assertNotInBytecode(code, 'BUILD_LIST') self.check_lnotab(code) - def test_folding_of_sets_of_constants(self): + def test_constant_folding_sets_of_constants(self): for line, elem in ( # in/not in constants with BUILD_SET should be folded to a frozenset: ('a in {1,2,3}', frozenset({1, 2, 3})), @@ -235,56 +235,175 @@ def g(a): self.assertTrue(g(4)) self.check_lnotab(g) + def test_constant_folding_small_int(self): + tests = [ + ('(0, )[0]', 0), + ('(1 + 2, )[0]', 3), + ('(2 + 2 * 2, )[0]', 6), + ('(1, (1 + 2 + 3, ))[1][0]', 6), + ('1 + 2', 3), + ('2 + 2 * 2 // 2 - 2', 2), + ('(255, )[0]', 255), + ('(256, )[0]', None), + ('(1000, )[0]', None), + ('(1 - 2, )[0]', None), + ('255 + 0', 255), + ('255 + 1', None), + ('-1', None), + ('--1', 1), + ('--255', 255), + ('--256', None), + ('~1', None), + ('~~1', 1), + ('~~255', 255), + ('~~256', None), + ('++255', 255), + ('++256', None), + ] + for expr, oparg in tests: + with self.subTest(expr=expr, oparg=oparg): + code = compile(expr, '', 'single') + if oparg is not None: + self.assertInBytecode(code, 'LOAD_SMALL_INT', oparg) + else: + self.assertNotInBytecode(code, 'LOAD_SMALL_INT') + self.check_lnotab(code) - def test_folding_of_binops_on_constants(self): - for line, elem in ( - ('a = 2+3+4', 9), # chained fold - ('"@"*4', '@@@@'), # check string ops - ('a="abc" + "def"', 'abcdef'), # check string ops - ('a = 3**4', 81), # binary power - ('a = 3*4', 12), # binary multiply - ('a = 13//4', 3), # binary floor divide - ('a = 14%4', 2), # binary modulo - ('a = 2+3', 5), # binary add - ('a = 13-4', 9), # binary subtract - ('a = (12,13)[1]', 13), # binary subscr - ('a = 13 << 2', 52), # binary lshift - ('a = 13 >> 2', 3), # binary rshift - ('a = 13 & 7', 5), # binary and - ('a = 13 ^ 7', 10), # binary xor - ('a = 13 | 7', 15), # binary or - ): - with self.subTest(line=line): - code = compile(line, '', 'single') - if isinstance(elem, int): - self.assertInBytecode(code, 'LOAD_SMALL_INT', elem) + def test_constant_folding_unaryop(self): + intrinsic_positive = 5 + tests = [ + ('-0', 'UNARY_NEGATIVE', None, True, 'LOAD_SMALL_INT', 0), + ('-0.0', 'UNARY_NEGATIVE', None, True, 'LOAD_CONST', -0.0), + ('-(1.0-1.0)', 'UNARY_NEGATIVE', None, True, 'LOAD_CONST', -0.0), + ('-0.5', 'UNARY_NEGATIVE', None, True, 'LOAD_CONST', -0.5), + ('---1', 'UNARY_NEGATIVE', None, True, 'LOAD_CONST', -1), + ('---""', 'UNARY_NEGATIVE', None, False, None, None), + ('~~~1', 'UNARY_INVERT', None, True, 'LOAD_CONST', -2), + ('~~~""', 'UNARY_INVERT', None, False, None, None), + ('not not True', 'UNARY_NOT', None, True, 'LOAD_CONST', True), + ('not not x', 'UNARY_NOT', None, True, 'LOAD_NAME', 'x'), # this should be optimized regardless of constant or not + ('+++1', 'CALL_INTRINSIC_1', intrinsic_positive, True, 'LOAD_SMALL_INT', 1), + ('---x', 'UNARY_NEGATIVE', None, False, None, None), + ('~~~x', 'UNARY_INVERT', None, False, None, None), + ('+++x', 'CALL_INTRINSIC_1', intrinsic_positive, False, None, None), + ] + + for ( + expr, + original_opcode, + original_argval, + is_optimized, + optimized_opcode, + optimized_argval, + ) in tests: + with self.subTest(expr=expr, is_optimized=is_optimized): + code = compile(expr, "", "single") + if is_optimized: + self.assertNotInBytecode(code, original_opcode, argval=original_argval) + self.assertInBytecode(code, optimized_opcode, argval=optimized_argval) else: - self.assertInBytecode(code, 'LOAD_CONST', elem) - for instr in dis.get_instructions(code): - self.assertFalse(instr.opname.startswith('BINARY_')) + self.assertInBytecode(code, original_opcode, argval=original_argval) self.check_lnotab(code) - # Verify that unfoldables are skipped - code = compile('a=2+"b"', '', 'single') - self.assertInBytecode(code, 'LOAD_SMALL_INT', 2) - self.assertInBytecode(code, 'LOAD_CONST', 'b') - self.check_lnotab(code) + # Check that -0.0 works after marshaling + def negzero(): + return -(1.0-1.0) + + for instr in dis.get_instructions(negzero): + self.assertFalse(instr.opname.startswith('UNARY_')) + self.check_lnotab(negzero) + + def test_constant_folding_binop(self): + tests = [ + ('1 + 2', 'NB_ADD', True, 'LOAD_SMALL_INT', 3), + ('1 + 2 + 3', 'NB_ADD', True, 'LOAD_SMALL_INT', 6), + ('1 + ""', 'NB_ADD', False, None, None), + ('1 - 2', 'NB_SUBTRACT', True, 'LOAD_CONST', -1), + ('1 - 2 - 3', 'NB_SUBTRACT', True, 'LOAD_CONST', -4), + ('1 - ""', 'NB_SUBTRACT', False, None, None), + ('2 * 2', 'NB_MULTIPLY', True, 'LOAD_SMALL_INT', 4), + ('2 * 2 * 2', 'NB_MULTIPLY', True, 'LOAD_SMALL_INT', 8), + ('2 / 2', 'NB_TRUE_DIVIDE', True, 'LOAD_CONST', 1.0), + ('2 / 2 / 2', 'NB_TRUE_DIVIDE', True, 'LOAD_CONST', 0.5), + ('2 / ""', 'NB_TRUE_DIVIDE', False, None, None), + ('2 // 2', 'NB_FLOOR_DIVIDE', True, 'LOAD_SMALL_INT', 1), + ('2 // 2 // 2', 'NB_FLOOR_DIVIDE', True, 'LOAD_SMALL_INT', 0), + ('2 // ""', 'NB_FLOOR_DIVIDE', False, None, None), + ('2 % 2', 'NB_REMAINDER', True, 'LOAD_SMALL_INT', 0), + ('2 % 2 % 2', 'NB_REMAINDER', True, 'LOAD_SMALL_INT', 0), + ('2 % ()', 'NB_REMAINDER', False, None, None), + ('2 ** 2', 'NB_POWER', True, 'LOAD_SMALL_INT', 4), + ('2 ** 2 ** 2', 'NB_POWER', True, 'LOAD_SMALL_INT', 16), + ('2 ** ""', 'NB_POWER', False, None, None), + ('2 << 2', 'NB_LSHIFT', True, 'LOAD_SMALL_INT', 8), + ('2 << 2 << 2', 'NB_LSHIFT', True, 'LOAD_SMALL_INT', 32), + ('2 << ""', 'NB_LSHIFT', False, None, None), + ('2 >> 2', 'NB_RSHIFT', True, 'LOAD_SMALL_INT', 0), + ('2 >> 2 >> 2', 'NB_RSHIFT', True, 'LOAD_SMALL_INT', 0), + ('2 >> ""', 'NB_RSHIFT', False, None, None), + ('2 | 2', 'NB_OR', True, 'LOAD_SMALL_INT', 2), + ('2 | 2 | 2', 'NB_OR', True, 'LOAD_SMALL_INT', 2), + ('2 | ""', 'NB_OR', False, None, None), + ('2 & 2', 'NB_AND', True, 'LOAD_SMALL_INT', 2), + ('2 & 2 & 2', 'NB_AND', True, 'LOAD_SMALL_INT', 2), + ('2 & ""', 'NB_AND', False, None, None), + ('2 ^ 2', 'NB_XOR', True, 'LOAD_SMALL_INT', 0), + ('2 ^ 2 ^ 2', 'NB_XOR', True, 'LOAD_SMALL_INT', 2), + ('2 ^ ""', 'NB_XOR', False, None, None), + ('(1, )[0]', 'NB_SUBSCR', True, 'LOAD_SMALL_INT', 1), + ('(1, )[-1]', 'NB_SUBSCR', True, 'LOAD_SMALL_INT', 1), + ('(1 + 2, )[0]', 'NB_SUBSCR', True, 'LOAD_SMALL_INT', 3), + ('(1, (1, 2))[1][1]', 'NB_SUBSCR', True, 'LOAD_SMALL_INT', 2), + ('(1, 2)[2-1]', 'NB_SUBSCR', True, 'LOAD_SMALL_INT', 2), + ('(1, (1, 2))[1][2-1]', 'NB_SUBSCR', True, 'LOAD_SMALL_INT', 2), + ('(1, (1, 2))[1:6][0][2-1]', 'NB_SUBSCR', True, 'LOAD_SMALL_INT', 2), + ('"a"[0]', 'NB_SUBSCR', True, 'LOAD_CONST', 'a'), + ('("a" + "b")[1]', 'NB_SUBSCR', True, 'LOAD_CONST', 'b'), + ('("a" + "b", )[0][1]', 'NB_SUBSCR', True, 'LOAD_CONST', 'b'), + ('("a" * 10)[9]', 'NB_SUBSCR', True, 'LOAD_CONST', 'a'), + ('(1, )[1]', 'NB_SUBSCR', False, None, None), + ('(1, )[-2]', 'NB_SUBSCR', False, None, None), + ('"a"[1]', 'NB_SUBSCR', False, None, None), + ('"a"[-2]', 'NB_SUBSCR', False, None, None), + ('("a" + "b")[2]', 'NB_SUBSCR', False, None, None), + ('("a" + "b", )[0][2]', 'NB_SUBSCR', False, None, None), + ('("a" + "b", )[1][0]', 'NB_SUBSCR', False, None, None), + ('("a" * 10)[10]', 'NB_SUBSCR', False, None, None), + ('(1, (1, 2))[2:6][0][2-1]', 'NB_SUBSCR', False, None, None), + ] + + for ( + expr, + nb_op, + is_optimized, + optimized_opcode, + optimized_argval + ) in tests: + with self.subTest(expr=expr, is_optimized=is_optimized): + code = compile(expr, '', 'single') + nb_op_val = get_binop_argval(nb_op) + if is_optimized: + self.assertNotInBytecode(code, 'BINARY_OP', argval=nb_op_val) + self.assertInBytecode(code, optimized_opcode, argval=optimized_argval) + else: + self.assertInBytecode(code, 'BINARY_OP', argval=nb_op_val) + self.check_lnotab(code) # Verify that large sequences do not result from folding - code = compile('a="x"*10000', '', 'single') + code = compile('"x"*10000', '', 'single') self.assertInBytecode(code, 'LOAD_CONST', 10000) self.assertNotIn("x"*10000, code.co_consts) self.check_lnotab(code) - code = compile('a=1<<1000', '', 'single') + code = compile('1<<1000', '', 'single') self.assertInBytecode(code, 'LOAD_CONST', 1000) self.assertNotIn(1<<1000, code.co_consts) self.check_lnotab(code) - code = compile('a=2**1000', '', 'single') + code = compile('2**1000', '', 'single') self.assertInBytecode(code, 'LOAD_CONST', 1000) self.assertNotIn(2**1000, code.co_consts) self.check_lnotab(code) - def test_binary_subscr_on_unicode(self): + # Test binary subscript on unicode # valid code get optimized code = compile('"foo"[0]', '', 'single') self.assertInBytecode(code, 'LOAD_CONST', 'f') @@ -307,43 +426,83 @@ def test_binary_subscr_on_unicode(self): self.assertInBytecode(code, 'BINARY_OP') self.check_lnotab(code) - def test_folding_of_unaryops_on_constants(self): - for line, elem in ( - ('-0.5', -0.5), # unary negative - ('-0.0', -0.0), # -0.0 - ('-(1.0-1.0)', -0.0), # -0.0 after folding - ('-0', 0), # -0 - ('~-2', 1), # unary invert - ('+1', 1), # unary positive - ): - with self.subTest(line=line): - code = compile(line, '', 'single') - if isinstance(elem, int): - self.assertInBytecode(code, 'LOAD_SMALL_INT', elem) - else: - self.assertInBytecode(code, 'LOAD_CONST', elem) - for instr in dis.get_instructions(code): - self.assertFalse(instr.opname.startswith('UNARY_')) - self.check_lnotab(code) - # Check that -0.0 works after marshaling - def negzero(): - return -(1.0-1.0) + def test_constant_folding_remove_nop_location(self): + sources = [ + """ + (- + - + - + 1) + """, - for instr in dis.get_instructions(negzero): - self.assertFalse(instr.opname.startswith('UNARY_')) - self.check_lnotab(negzero) + """ + (1 + + + 2 + + + 3) + """, - # Verify that unfoldables are skipped - for line, elem, opname in ( - ('-"abc"', 'abc', 'UNARY_NEGATIVE'), - ('~"abc"', 'abc', 'UNARY_INVERT'), - ): - with self.subTest(line=line): - code = compile(line, '', 'single') - self.assertInBytecode(code, 'LOAD_CONST', elem) - self.assertInBytecode(code, opname) - self.check_lnotab(code) + """ + (1, + 2, + 3)[0] + """, + + """ + [1, + 2, + 3] + """, + + """ + {1, + 2, + 3} + """, + + """ + 1 in [ + 1, + 2, + 3 + ] + """, + + """ + 1 in { + 1, + 2, + 3 + } + """, + + """ + for _ in [1, + 2, + 3]: + pass + """, + + """ + for _ in [1, + 2, + x]: + pass + """, + + """ + for _ in {1, + 2, + 3}: + pass + """ + ] + + for source in sources: + code = compile(textwrap.dedent(source), '', 'single') + self.assertNotInBytecode(code, 'NOP') def test_elim_extra_return(self): # RETURN LOAD_CONST None RETURN --> RETURN @@ -459,232 +618,6 @@ def g()->1+1: self.assertNotInBytecode(f, 'BINARY_OP') self.check_lnotab(f) - def test_constant_folding(self): - # Issue #11244: aggressive constant folding. - exprs = [ - '3 * -5', - '-3 * 5', - '2 * (3 * 4)', - '(2 * 3) * 4', - '(-1, 2, 3)', - '(1, -2, 3)', - '(1, 2, -3)', - '(1, 2, -3) * 6', - 'lambda x: x in {(3 * -5) + (-1 - 6), (1, -2, 3) * 2, None}', - ] - for e in exprs: - with self.subTest(e=e): - code = compile(e, '', 'single') - for instr in dis.get_instructions(code): - self.assertFalse(instr.opname.startswith('UNARY_')) - self.assertFalse(instr.opname.startswith('BINARY_')) - self.assertFalse(instr.opname.startswith('BUILD_')) - self.check_lnotab(code) - - def test_constant_folding_small_int(self): - tests = [ - ('(0, )[0]', 0), - ('(1 + 2, )[0]', 3), - ('(2 + 2 * 2, )[0]', 6), - ('(1, (1 + 2 + 3, ))[1][0]', 6), - ('1 + 2', 3), - ('2 + 2 * 2 // 2 - 2', 2), - ('(255, )[0]', 255), - ('(256, )[0]', None), - ('(1000, )[0]', None), - ('(1 - 2, )[0]', None), - ('255 + 0', 255), - ('255 + 1', None), - ('-1', None), - ('--1', 1), - ('--255', 255), - ('--256', None), - ('~1', None), - ('~~1', 1), - ('~~255', 255), - ('~~256', None), - ('++255', 255), - ('++256', None), - ] - for expr, oparg in tests: - with self.subTest(expr=expr, oparg=oparg): - code = compile(expr, '', 'single') - if oparg is not None: - self.assertInBytecode(code, 'LOAD_SMALL_INT', oparg) - else: - self.assertNotInBytecode(code, 'LOAD_SMALL_INT') - self.check_lnotab(code) - - def test_folding_unaryop(self): - intrinsic_positive = 5 - tests = [ - ('---1', 'UNARY_NEGATIVE', None, True), - ('---""', 'UNARY_NEGATIVE', None, False), - ('~~~1', 'UNARY_INVERT', None, True), - ('~~~""', 'UNARY_INVERT', None, False), - ('not not True', 'UNARY_NOT', None, True), - ('not not x', 'UNARY_NOT', None, True), # this should be optimized regardless of constant or not - ('+++1', 'CALL_INTRINSIC_1', intrinsic_positive, True), - ('---x', 'UNARY_NEGATIVE', None, False), - ('~~~x', 'UNARY_INVERT', None, False), - ('+++x', 'CALL_INTRINSIC_1', intrinsic_positive, False), - ] - - for expr, opcode, oparg, optimized in tests: - with self.subTest(expr=expr, optimized=optimized): - code = compile(expr, '', 'single') - if optimized: - self.assertNotInBytecode(code, opcode, argval=oparg) - else: - self.assertInBytecode(code, opcode, argval=oparg) - self.check_lnotab(code) - - def test_folding_binop(self): - tests = [ - ('1 + 2', False, 'NB_ADD'), - ('1 + 2 + 3', False, 'NB_ADD'), - ('1 + ""', True, 'NB_ADD'), - ('1 - 2', False, 'NB_SUBTRACT'), - ('1 - 2 - 3', False, 'NB_SUBTRACT'), - ('1 - ""', True, 'NB_SUBTRACT'), - ('2 * 2', False, 'NB_MULTIPLY'), - ('2 * 2 * 2', False, 'NB_MULTIPLY'), - ('2 / 2', False, 'NB_TRUE_DIVIDE'), - ('2 / 2 / 2', False, 'NB_TRUE_DIVIDE'), - ('2 / ""', True, 'NB_TRUE_DIVIDE'), - ('2 // 2', False, 'NB_FLOOR_DIVIDE'), - ('2 // 2 // 2', False, 'NB_FLOOR_DIVIDE'), - ('2 // ""', True, 'NB_FLOOR_DIVIDE'), - ('2 % 2', False, 'NB_REMAINDER'), - ('2 % 2 % 2', False, 'NB_REMAINDER'), - ('2 % ()', True, 'NB_REMAINDER'), - ('2 ** 2', False, 'NB_POWER'), - ('2 ** 2 ** 2', False, 'NB_POWER'), - ('2 ** ""', True, 'NB_POWER'), - ('2 << 2', False, 'NB_LSHIFT'), - ('2 << 2 << 2', False, 'NB_LSHIFT'), - ('2 << ""', True, 'NB_LSHIFT'), - ('2 >> 2', False, 'NB_RSHIFT'), - ('2 >> 2 >> 2', False, 'NB_RSHIFT'), - ('2 >> ""', True, 'NB_RSHIFT'), - ('2 | 2', False, 'NB_OR'), - ('2 | 2 | 2', False, 'NB_OR'), - ('2 | ""', True, 'NB_OR'), - ('2 & 2', False, 'NB_AND'), - ('2 & 2 & 2', False, 'NB_AND'), - ('2 & ""', True, 'NB_AND'), - ('2 ^ 2', False, 'NB_XOR'), - ('2 ^ 2 ^ 2', False, 'NB_XOR'), - ('2 ^ ""', True, 'NB_XOR'), - ('(1, )[0]', False, 'NB_SUBSCR'), - ('(1, )[-1]', False, 'NB_SUBSCR'), - ('(1 + 2, )[0]', False, 'NB_SUBSCR'), - ('(1, (1, 2))[1][1]', False, 'NB_SUBSCR'), - ('(1, 2)[2-1]', False, 'NB_SUBSCR'), - ('(1, (1, 2))[1][2-1]', False, 'NB_SUBSCR'), - ('(1, (1, 2))[1:6][0][2-1]', False, 'NB_SUBSCR'), - ('"a"[0]', False, 'NB_SUBSCR'), - ('("a" + "b")[1]', False, 'NB_SUBSCR'), - ('("a" + "b", )[0][1]', False, 'NB_SUBSCR'), - ('("a" * 10)[9]', False, 'NB_SUBSCR'), - ('(1, )[1]', True, 'NB_SUBSCR'), - ('(1, )[-2]', True, 'NB_SUBSCR'), - ('"a"[1]', True, 'NB_SUBSCR'), - ('"a"[-2]', True, 'NB_SUBSCR'), - ('("a" + "b")[2]', True, 'NB_SUBSCR'), - ('("a" + "b", )[0][2]', True, 'NB_SUBSCR'), - ('("a" + "b", )[1][0]', True, 'NB_SUBSCR'), - ('("a" * 10)[10]', True, 'NB_SUBSCR'), - ('(1, (1, 2))[2:6][0][2-1]', True, 'NB_SUBSCR'), - - ] - for expr, has_error, nb_op in tests: - with self.subTest(expr=expr, has_error=has_error): - code = compile(expr, '', 'single') - nb_op_val = get_binop_argval(nb_op) - if not has_error: - self.assertNotInBytecode(code, 'BINARY_OP', argval=nb_op_val) - else: - self.assertInBytecode(code, 'BINARY_OP', argval=nb_op_val) - self.check_lnotab(code) - - def test_constant_folding_remove_nop_location(self): - sources = [ - """ - (- - - - - - 1) - """, - - """ - (1 - + - 2 - + - 3) - """, - - """ - (1, - 2, - 3)[0] - """, - - """ - [1, - 2, - 3] - """, - - """ - {1, - 2, - 3} - """, - - """ - 1 in [ - 1, - 2, - 3 - ] - """, - - """ - 1 in { - 1, - 2, - 3 - } - """, - - """ - for _ in [1, - 2, - 3]: - pass - """, - - """ - for _ in [1, - 2, - x]: - pass - """, - - """ - for _ in {1, - 2, - 3}: - pass - """ - ] - - for source in sources: - code = compile(textwrap.dedent(source), '', 'single') - self.assertNotInBytecode(code, 'NOP') - def test_in_literal_list(self): def containtest(): return x in [a, b] _______________________________________________ Python-checkins mailing list -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com