From c03252cf232fbed8e9b7bbf3df4061637e4c609c Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Thu, 27 Mar 2025 13:29:58 +0100 Subject: [PATCH 1/5] merge unaryop folding tests --- Lib/test/test_peepholer.py | 94 +++++++++++++++----------------------- 1 file changed, 38 insertions(+), 56 deletions(-) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 6de89c4043d3f7..2c881617499518 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -307,44 +307,6 @@ 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) - - for instr in dis.get_instructions(negzero): - self.assertFalse(instr.opname.startswith('UNARY_')) - self.check_lnotab(negzero) - - # 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) - def test_elim_extra_return(self): # RETURN LOAD_CONST None RETURN --> RETURN def f(x): @@ -518,27 +480,47 @@ def test_constant_folding_small_int(self): 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) + ('-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, opcode, argval=oparg) + self.assertInBytecode(code, original_opcode, argval=original_argval) 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_folding_binop(self): tests = [ ('1 + 2', False, 'NB_ADD'), From 2c7b4014f3f0a53d9e8e49084058cbcf0a832d72 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Fri, 28 Mar 2025 09:18:47 +0100 Subject: [PATCH 2/5] merge binaryop folding tests --- Lib/test/test_peepholer.py | 236 ++++++++++++++++--------------------- 1 file changed, 104 insertions(+), 132 deletions(-) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 2c881617499518..10267a11919717 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -235,78 +235,6 @@ def g(a): self.assertTrue(g(4)) self.check_lnotab(g) - - 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) - else: - self.assertInBytecode(code, 'LOAD_CONST', elem) - for instr in dis.get_instructions(code): - self.assertFalse(instr.opname.startswith('BINARY_')) - 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) - - # Verify that large sequences do not result from folding - code = compile('a="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') - self.assertInBytecode(code, 'LOAD_CONST', 1000) - self.assertNotIn(1<<1000, code.co_consts) - self.check_lnotab(code) - code = compile('a=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): - # valid code get optimized - code = compile('"foo"[0]', '', 'single') - self.assertInBytecode(code, 'LOAD_CONST', 'f') - self.assertNotInBytecode(code, 'BINARY_OP') - self.check_lnotab(code) - code = compile('"\u0061\uffff"[1]', '', 'single') - self.assertInBytecode(code, 'LOAD_CONST', '\uffff') - self.assertNotInBytecode(code,'BINARY_OP') - self.check_lnotab(code) - - # With PEP 393, non-BMP char get optimized - code = compile('"\U00012345"[0]', '', 'single') - self.assertInBytecode(code, 'LOAD_CONST', '\U00012345') - self.assertNotInBytecode(code, 'BINARY_OP') - self.check_lnotab(code) - - # invalid code doesn't get optimized - # out of range - code = compile('"fuu"[10]', '', 'single') - self.assertInBytecode(code, 'BINARY_OP') - self.check_lnotab(code) - def test_elim_extra_return(self): # RETURN LOAD_CONST None RETURN --> RETURN def f(x): @@ -523,73 +451,117 @@ def negzero(): 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): + ('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 not has_error: + 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('"x"*10000', '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', 10000) + self.assertNotIn("x"*10000, code.co_consts) + self.check_lnotab(code) + code = compile('1<<1000', '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', 1000) + self.assertNotIn(1<<1000, code.co_consts) + self.check_lnotab(code) + code = compile('2**1000', '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', 1000) + self.assertNotIn(2**1000, code.co_consts) + self.check_lnotab(code) + + # Test binary subscript on unicode + # valid code get optimized + code = compile('"foo"[0]', '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', 'f') + self.assertNotInBytecode(code, 'BINARY_OP') + self.check_lnotab(code) + code = compile('"\u0061\uffff"[1]', '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', '\uffff') + self.assertNotInBytecode(code,'BINARY_OP') + self.check_lnotab(code) + + # With PEP 393, non-BMP char get optimized + code = compile('"\U00012345"[0]', '', 'single') + self.assertInBytecode(code, 'LOAD_CONST', '\U00012345') + self.assertNotInBytecode(code, 'BINARY_OP') + self.check_lnotab(code) + + # invalid code doesn't get optimized + # out of range + code = compile('"fuu"[10]', '', 'single') + self.assertInBytecode(code, 'BINARY_OP') + self.check_lnotab(code) + + def test_constant_folding_remove_nop_location(self): sources = [ """ From 9a421662c11f9bd12cce33915a184d93d4c4d247 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Fri, 28 Mar 2025 09:34:37 +0100 Subject: [PATCH 3/5] remove `test_constant_folding` `test_constant_folding` scenarios are covered in other more specific tests --- Lib/test/test_peepholer.py | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 10267a11919717..393ccd8fa484e8 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -349,28 +349,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), From 7b94894ab40082f5b1e26a823132ea063b78c779 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Fri, 28 Mar 2025 09:53:50 +0100 Subject: [PATCH 4/5] use explicit naming for constant folding tests in TestTransforms --- Lib/test/test_peepholer.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index 393ccd8fa484e8..d40f15e9052990 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})), @@ -383,7 +383,7 @@ def test_constant_folding_small_int(self): self.assertNotInBytecode(code, 'LOAD_SMALL_INT') self.check_lnotab(code) - def test_folding_unaryop(self): + def test_constant_folding_unaryop(self): intrinsic_positive = 5 tests = [ ('-0', 'UNARY_NEGATIVE', None, True, 'LOAD_SMALL_INT', 0), @@ -427,7 +427,7 @@ def negzero(): self.assertFalse(instr.opname.startswith('UNARY_')) self.check_lnotab(negzero) - def test_folding_binop(self): + 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), From d5ce5ecd9ab2666671603542242a80f93eb05196 Mon Sep 17 00:00:00 2001 From: Yan Yanchii Date: Fri, 28 Mar 2025 10:06:41 +0100 Subject: [PATCH 5/5] group constant folding related tests together in TestTransforms --- Lib/test/test_peepholer.py | 229 +++++++++++++++++++------------------ 1 file changed, 115 insertions(+), 114 deletions(-) diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py index d40f15e9052990..8b97e76fa346ff 100644 --- a/Lib/test/test_peepholer.py +++ b/Lib/test/test_peepholer.py @@ -235,120 +235,6 @@ def g(a): self.assertTrue(g(4)) self.check_lnotab(g) - def test_elim_extra_return(self): - # RETURN LOAD_CONST None RETURN --> RETURN - def f(x): - return x - self.assertNotInBytecode(f, 'LOAD_CONST', None) - returns = [instr for instr in dis.get_instructions(f) - if instr.opname == 'RETURN_VALUE'] - self.assertEqual(len(returns), 1) - self.check_lnotab(f) - - def test_elim_jump_to_return(self): - # JUMP_FORWARD to RETURN --> RETURN - def f(cond, true_value, false_value): - # Intentionally use two-line expression to test issue37213. - return (true_value if cond - else false_value) - self.check_jump_targets(f) - self.assertNotInBytecode(f, 'JUMP_FORWARD') - self.assertNotInBytecode(f, 'JUMP_BACKWARD') - returns = [instr for instr in dis.get_instructions(f) - if instr.opname == 'RETURN_VALUE'] - self.assertEqual(len(returns), 2) - self.check_lnotab(f) - - def test_elim_jump_to_uncond_jump(self): - # POP_JUMP_IF_FALSE to JUMP_FORWARD --> POP_JUMP_IF_FALSE to non-jump - def f(): - if a: - # Intentionally use two-line expression to test issue37213. - if (c - or d): - foo() - else: - baz() - self.check_jump_targets(f) - self.check_lnotab(f) - - def test_elim_jump_to_uncond_jump2(self): - # POP_JUMP_IF_FALSE to JUMP_BACKWARD --> POP_JUMP_IF_FALSE to non-jump - def f(): - while a: - # Intentionally use two-line expression to test issue37213. - if (c - or d): - a = foo() - self.check_jump_targets(f) - self.check_lnotab(f) - - def test_elim_jump_to_uncond_jump3(self): - # Intentionally use two-line expressions to test issue37213. - # POP_JUMP_IF_FALSE to POP_JUMP_IF_FALSE --> POP_JUMP_IF_FALSE to non-jump - def f(a, b, c): - return ((a and b) - and c) - self.check_jump_targets(f) - self.check_lnotab(f) - self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 2) - # POP_JUMP_IF_TRUE to POP_JUMP_IF_TRUE --> POP_JUMP_IF_TRUE to non-jump - def f(a, b, c): - return ((a or b) - or c) - self.check_jump_targets(f) - self.check_lnotab(f) - self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 2) - # JUMP_IF_FALSE_OR_POP to JUMP_IF_TRUE_OR_POP --> POP_JUMP_IF_FALSE to non-jump - def f(a, b, c): - return ((a and b) - or c) - self.check_jump_targets(f) - self.check_lnotab(f) - self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 1) - self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 1) - # POP_JUMP_IF_TRUE to POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE to non-jump - def f(a, b, c): - return ((a or b) - and c) - self.check_jump_targets(f) - self.check_lnotab(f) - self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 1) - self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 1) - - def test_elim_jump_to_uncond_jump4(self): - def f(): - for i in range(5): - if i > 3: - print(i) - self.check_jump_targets(f) - - def test_elim_jump_after_return1(self): - # Eliminate dead code: jumps immediately after returns can't be reached - def f(cond1, cond2): - if cond1: return 1 - if cond2: return 2 - while 1: - return 3 - while 1: - if cond1: return 4 - return 5 - return 6 - self.assertNotInBytecode(f, 'JUMP_FORWARD') - self.assertNotInBytecode(f, 'JUMP_BACKWARD') - returns = [instr for instr in dis.get_instructions(f) - if instr.opname == 'RETURN_VALUE'] - self.assertLessEqual(len(returns), 6) - self.check_lnotab(f) - - def test_make_function_doesnt_bail(self): - def f(): - def g()->1+1: - pass - return g - self.assertNotInBytecode(f, 'BINARY_OP') - self.check_lnotab(f) - def test_constant_folding_small_int(self): tests = [ ('(0, )[0]', 0), @@ -485,6 +371,7 @@ def test_constant_folding_binop(self): ('("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, @@ -617,6 +504,120 @@ def test_constant_folding_remove_nop_location(self): code = compile(textwrap.dedent(source), '', 'single') self.assertNotInBytecode(code, 'NOP') + def test_elim_extra_return(self): + # RETURN LOAD_CONST None RETURN --> RETURN + def f(x): + return x + self.assertNotInBytecode(f, 'LOAD_CONST', None) + returns = [instr for instr in dis.get_instructions(f) + if instr.opname == 'RETURN_VALUE'] + self.assertEqual(len(returns), 1) + self.check_lnotab(f) + + def test_elim_jump_to_return(self): + # JUMP_FORWARD to RETURN --> RETURN + def f(cond, true_value, false_value): + # Intentionally use two-line expression to test issue37213. + return (true_value if cond + else false_value) + self.check_jump_targets(f) + self.assertNotInBytecode(f, 'JUMP_FORWARD') + self.assertNotInBytecode(f, 'JUMP_BACKWARD') + returns = [instr for instr in dis.get_instructions(f) + if instr.opname == 'RETURN_VALUE'] + self.assertEqual(len(returns), 2) + self.check_lnotab(f) + + def test_elim_jump_to_uncond_jump(self): + # POP_JUMP_IF_FALSE to JUMP_FORWARD --> POP_JUMP_IF_FALSE to non-jump + def f(): + if a: + # Intentionally use two-line expression to test issue37213. + if (c + or d): + foo() + else: + baz() + self.check_jump_targets(f) + self.check_lnotab(f) + + def test_elim_jump_to_uncond_jump2(self): + # POP_JUMP_IF_FALSE to JUMP_BACKWARD --> POP_JUMP_IF_FALSE to non-jump + def f(): + while a: + # Intentionally use two-line expression to test issue37213. + if (c + or d): + a = foo() + self.check_jump_targets(f) + self.check_lnotab(f) + + def test_elim_jump_to_uncond_jump3(self): + # Intentionally use two-line expressions to test issue37213. + # POP_JUMP_IF_FALSE to POP_JUMP_IF_FALSE --> POP_JUMP_IF_FALSE to non-jump + def f(a, b, c): + return ((a and b) + and c) + self.check_jump_targets(f) + self.check_lnotab(f) + self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 2) + # POP_JUMP_IF_TRUE to POP_JUMP_IF_TRUE --> POP_JUMP_IF_TRUE to non-jump + def f(a, b, c): + return ((a or b) + or c) + self.check_jump_targets(f) + self.check_lnotab(f) + self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 2) + # JUMP_IF_FALSE_OR_POP to JUMP_IF_TRUE_OR_POP --> POP_JUMP_IF_FALSE to non-jump + def f(a, b, c): + return ((a and b) + or c) + self.check_jump_targets(f) + self.check_lnotab(f) + self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 1) + self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 1) + # POP_JUMP_IF_TRUE to POP_JUMP_IF_FALSE --> POP_JUMP_IF_TRUE to non-jump + def f(a, b, c): + return ((a or b) + and c) + self.check_jump_targets(f) + self.check_lnotab(f) + self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_FALSE'), 1) + self.assertEqual(count_instr_recursively(f, 'POP_JUMP_IF_TRUE'), 1) + + def test_elim_jump_to_uncond_jump4(self): + def f(): + for i in range(5): + if i > 3: + print(i) + self.check_jump_targets(f) + + def test_elim_jump_after_return1(self): + # Eliminate dead code: jumps immediately after returns can't be reached + def f(cond1, cond2): + if cond1: return 1 + if cond2: return 2 + while 1: + return 3 + while 1: + if cond1: return 4 + return 5 + return 6 + self.assertNotInBytecode(f, 'JUMP_FORWARD') + self.assertNotInBytecode(f, 'JUMP_BACKWARD') + returns = [instr for instr in dis.get_instructions(f) + if instr.opname == 'RETURN_VALUE'] + self.assertLessEqual(len(returns), 6) + self.check_lnotab(f) + + def test_make_function_doesnt_bail(self): + def f(): + def g()->1+1: + pass + return g + self.assertNotInBytecode(f, 'BINARY_OP') + self.check_lnotab(f) + def test_in_literal_list(self): def containtest(): return x in [a, b]