From 529abf210b6d1929ab609b443de83f5362c87ebc Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Wed, 8 Oct 2025 21:42:45 +0000 Subject: [PATCH 1/4] [mypyc] feat: implement try_getting_literal in preallocate helper During the rtuple boxing stage, if any of the values are a known Literal type we can skip the struct access and just load the static literal --- mypyc/irbuild/for_helpers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 20440d4a26f4..26a8853cf7de 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -9,6 +9,7 @@ from typing import Callable, ClassVar +from mypy.checkexpr import try_getting_literal from mypy.nodes import ( ARG_POS, BytesExpr, @@ -255,7 +256,7 @@ def sequence_from_generator_preallocate_helper( if isinstance(typ, LiteralType) else TupleGet(sequence, i, line) ) - for i, typ in enumerate(get_proper_types(proper_type.items)) + for i, typ in enumerate(map(try_getting_literal, proper_type.items)) ] items = list(map(builder.add, get_item_ops)) sequence = builder.new_tuple(items, line) From c474ce59fde5cf1cdff739e76c2144130794ca69 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Wed, 8 Oct 2025 21:42:45 +0000 Subject: [PATCH 2/4] Update irbuild-tuple.test --- mypyc/irbuild/for_helpers.py | 2 +- mypyc/test-data/irbuild-tuple.test | 111 ++++++++++++++++++++--------- 2 files changed, 77 insertions(+), 36 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 26a8853cf7de..a705ca006207 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -29,7 +29,7 @@ TypeAlias, Var, ) -from mypy.types import LiteralType, TupleType, get_proper_type, get_proper_types +from mypy.types import LiteralType, TupleType, get_proper_type from mypyc.ir.ops import ( ERR_NEVER, BasicBlock, diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 3613c5f0101d..023e6151825b 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -744,14 +744,24 @@ L4: [case testTupleBuiltFromFinalFixedLengthTuple] from typing import Final -source: Final = (1, 2, 3) +def varint() -> int: + # this helper lets us break constant folding for this test + return 2 + +source_final: Final = (1, varint(), 3) +source_var = (1, varint(), 3) def f(val: int) -> bool: return val % 2 == 0 -def test() -> None: - a = tuple(f(x) for x in source) +def test_final_can_fold() -> None: + a = tuple(f(x) for x in source_final) +def test_var_can_not_fold() -> None: + a = tuple(f(x) for x in source_var) [out] +def varint(): +L0: + return 4 def f(val): val, r0 :: int r1 :: bit @@ -759,58 +769,89 @@ L0: r0 = CPyTagged_Remainder(val, 4) r1 = int_eq r0, 0 return r1 -def test(): +def test_final_can_fold(): r0 :: tuple[int, int, int] r1 :: bool r2 :: object r3 :: native_int r4 :: bit - r5, r6, r7 :: int - r8, r9, r10 :: object - r11, r12 :: tuple - r13 :: native_int - r14 :: bit - r15 :: object - r16, x :: int - r17 :: bool - r18 :: object - r19 :: native_int + r5 :: object + r6 :: int + r7, r8 :: object + r9, r10 :: tuple + r11 :: native_int + r12 :: bit + r13 :: object + r14, x :: int + r15 :: bool + r16 :: object + r17 :: native_int a :: tuple L0: - r0 = __main__.source :: static + r0 = __main__.source_final :: static if is_error(r0) goto L1 else goto L2 L1: - r1 = raise NameError('value for final name "source" was not set') + r1 = raise NameError('value for final name "source_final" was not set') unreachable L2: r2 = box(tuple[int, int, int], r0) r3 = PyObject_Size(r2) r4 = r3 >= 0 :: signed - r5 = r0[0] + r5 = object 1 r6 = r0[1] - r7 = r0[2] - r8 = box(int, r5) - r9 = box(int, r6) - r10 = box(int, r7) - r11 = PyTuple_Pack(3, r8, r9, r10) - r12 = PyTuple_New(r3) - r13 = 0 + r7 = object 3 + r8 = box(int, r6) + r9 = PyTuple_Pack(3, r5, r8, r7) + r10 = PyTuple_New(r3) + r11 = 0 L3: - r14 = r13 < r3 :: signed - if r14 goto L4 else goto L6 :: bool + r12 = r11 < r3 :: signed + if r12 goto L4 else goto L6 :: bool L4: - r15 = CPySequenceTuple_GetItemUnsafe(r11, r13) - r16 = unbox(int, r15) - x = r16 - r17 = f(x) - r18 = box(bool, r17) - CPySequenceTuple_SetItemUnsafe(r12, r13, r18) + r13 = CPySequenceTuple_GetItemUnsafe(r9, r11) + r14 = unbox(int, r13) + x = r14 + r15 = f(x) + r16 = box(bool, r15) + CPySequenceTuple_SetItemUnsafe(r10, r11, r16) L5: - r19 = r13 + 1 - r13 = r19 + r17 = r11 + 1 + r11 = r17 goto L3 L6: - a = r12 + a = r10 + return 1 +def test_var_can_not_fold(): + source :: tuple + r0 :: native_int + r1 :: tuple + r2 :: native_int + r3 :: bit + r4 :: object + r5, x, r6 :: bool + r7 :: object + r8 :: native_int + a :: tuple +L0: + r0 = var_object_size source + r1 = PyTuple_New(r0) + r2 = 0 +L1: + r3 = r2 < r0 :: signed + if r3 goto L2 else goto L4 :: bool +L2: + r4 = CPySequenceTuple_GetItemUnsafe(source, r2) + r5 = unbox(bool, r4) + x = r5 + r6 = f(x) + r7 = box(bool, r6) + CPySequenceTuple_SetItemUnsafe(r1, r2, r7) +L3: + r8 = r2 + 1 + r2 = r8 + goto L1 +L4: + a = r1 return 1 [case testTupleBuiltFromVariableLengthTuple] From 5a0cb426ff461798f219843c1bf3f67edf972d5d Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Wed, 8 Oct 2025 21:42:45 +0000 Subject: [PATCH 3/4] change test name --- mypyc/test-data/irbuild-tuple.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 023e6151825b..985d145d9004 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -741,7 +741,7 @@ L4: a = r11 return 1 -[case testTupleBuiltFromFinalFixedLengthTuple] +[case testTupleBuiltFromModLevelFixedLengthTuple] from typing import Final def varint() -> int: From c8d6cde155c83b9af3c871fd1467e2e5a6be9ae8 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Wed, 8 Oct 2025 21:43:58 +0000 Subject: [PATCH 4/4] update ir --- mypyc/test-data/irbuild-tuple.test | 65 ++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 985d145d9004..d0adc3f01708 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -822,36 +822,57 @@ L6: a = r10 return 1 def test_var_can_not_fold(): - source :: tuple - r0 :: native_int - r1 :: tuple - r2 :: native_int - r3 :: bit + r0 :: dict + r1 :: str + r2 :: object + r3 :: tuple[int, int, int] r4 :: object - r5, x, r6 :: bool - r7 :: object - r8 :: native_int + r5 :: native_int + r6 :: bit + r7, r8, r9 :: int + r10, r11, r12 :: object + r13, r14 :: tuple + r15 :: native_int + r16 :: bit + r17 :: object + r18, x :: int + r19 :: bool + r20 :: object + r21 :: native_int a :: tuple L0: - r0 = var_object_size source - r1 = PyTuple_New(r0) - r2 = 0 + r0 = __main__.globals :: static + r1 = 'source_var' + r2 = CPyDict_GetItem(r0, r1) + r3 = unbox(tuple[int, int, int], r2) + r4 = box(tuple[int, int, int], r3) + r5 = PyObject_Size(r4) + r6 = r5 >= 0 :: signed + r7 = r3[0] + r8 = r3[1] + r9 = r3[2] + r10 = box(int, r7) + r11 = box(int, r8) + r12 = box(int, r9) + r13 = PyTuple_Pack(3, r10, r11, r12) + r14 = PyTuple_New(r5) + r15 = 0 L1: - r3 = r2 < r0 :: signed - if r3 goto L2 else goto L4 :: bool + r16 = r15 < r5 :: signed + if r16 goto L2 else goto L4 :: bool L2: - r4 = CPySequenceTuple_GetItemUnsafe(source, r2) - r5 = unbox(bool, r4) - x = r5 - r6 = f(x) - r7 = box(bool, r6) - CPySequenceTuple_SetItemUnsafe(r1, r2, r7) + r17 = CPySequenceTuple_GetItemUnsafe(r13, r15) + r18 = unbox(int, r17) + x = r18 + r19 = f(x) + r20 = box(bool, r19) + CPySequenceTuple_SetItemUnsafe(r14, r15, r20) L3: - r8 = r2 + 1 - r2 = r8 + r21 = r15 + 1 + r15 = r21 goto L1 L4: - a = r1 + a = r14 return 1 [case testTupleBuiltFromVariableLengthTuple]