-- Test cases for copy propagation optimization. This also tests IR transforms in general, -- as copy propagation was the first IR transform that was implemented. [case testCopyPropagationSimple] def g() -> int: return 1 def f() -> int: y = g() return y [out] def g(): L0: return 2 def f(): r0 :: int L0: r0 = g() return r0 [case testCopyPropagationChain] def f(x: int) -> int: y = x z = y return z [out] def f(x): x :: int L0: return x [case testCopyPropagationChainPartial] def f(x: int) -> int: y = x z = y x = 2 return z [out] def f(x): x, y :: int L0: y = x x = 4 return y [case testCopyPropagationChainBad] def f(x: int) -> int: y = x z = y y = 2 return z [out] def f(x): x, y, z :: int L0: y = x z = y y = 4 return z [case testCopyPropagationMutatedSource1] def f(x: int) -> int: y = x x = 1 return y [out] def f(x): x, y :: int L0: y = x x = 2 return y [case testCopyPropagationMutatedSource2] def f() -> int: z = 1 y = z z = 2 return y [out] def f(): z, y :: int L0: z = 2 y = z z = 4 return y [case testCopyPropagationTooComplex] def f(b: bool, x: int) -> int: if b: y = x return y else: y = 1 return y [out] def f(b, x): b :: bool x, y :: int L0: if b goto L1 else goto L2 :: bool L1: y = x return y L2: y = 2 return y [case testCopyPropagationArg] def f(x: int) -> int: x = 2 return x [out] def f(x): x :: int L0: x = 4 return x [case testCopyPropagationPartiallyDefined1] def f(b: bool) -> int: if b: x = 1 y = x return y [out] def f(b): b :: bool r0, x :: int r1 :: bool y :: int L0: r0 = :: int x = r0 if b goto L1 else goto L2 :: bool L1: x = 2 L2: if is_error(x) goto L3 else goto L4 L3: r1 = raise UnboundLocalError('local variable "x" referenced before assignment') unreachable L4: y = x return y -- The remaining test cases test basic IRTransform functionality and are not -- all needed for testing copy propagation as such. [case testIRTransformBranch] from mypy_extensions import i64 def f(x: bool) -> int: y = x if y: return 1 else: return 2 [out] def f(x): x :: bool L0: if x goto L1 else goto L2 :: bool L1: return 2 L2: return 4 [case testIRTransformAssignment] def f(b: bool, x: int) -> int: y = x if b: return y else: return 1 [out] def f(b, x): b :: bool x :: int L0: if b goto L1 else goto L2 :: bool L1: return x L2: return 2 [case testIRTransformRegisterOps1] from __future__ import annotations from typing import cast class C: a: int def m(self, x: int) -> None: pass def get_attr(x: C) -> int: y = x return y.a def set_attr(x: C) -> None: y = x y.a = 1 def tuple_get(x: tuple[int, int]) -> int: y = x return y[0] def tuple_set(x: int, xx: int) -> tuple[int, int]: y = x z = xx return y, z def call(x: int) -> int: y = x return call(y) def method_call(c: C, x: int) -> None: y = x c.m(y) def cast_op(x: object) -> str: y = x return cast(str, y) def box(x: int) -> object: y = x return y def unbox(x: object) -> int: y = x return cast(int, y) def call_c(x: list[str]) -> None: y = x y.append("x") def keep_alive(x: C) -> int: y = x return y.a + 1 [out] def C.m(self, x): self :: __main__.C x :: int L0: return 1 def get_attr(x): x :: __main__.C r0 :: int L0: r0 = x.a return r0 def set_attr(x): x :: __main__.C r0 :: bool L0: x.a = 2; r0 = is_error return 1 def tuple_get(x): x :: tuple[int, int] r0 :: int L0: r0 = x[0] return r0 def tuple_set(x, xx): x, xx :: int r0 :: tuple[int, int] L0: r0 = (x, xx) return r0 def call(x): x, r0 :: int L0: r0 = call(x) return r0 def method_call(c, x): c :: __main__.C x :: int r0 :: None L0: r0 = c.m(x) return 1 def cast_op(x): x :: object r0 :: str L0: r0 = cast(str, x) return r0 def box(x): x :: int r0 :: object L0: r0 = box(int, x) return r0 def unbox(x): x :: object r0 :: int L0: r0 = unbox(int, x) return r0 def call_c(x): x :: list r0 :: str r1 :: i32 r2 :: bit L0: r0 = 'x' r1 = PyList_Append(x, r0) r2 = r1 >= 0 :: signed return 1 def keep_alive(x): x :: __main__.C r0, r1 :: int L0: r0 = borrow x.a r1 = CPyTagged_Add(r0, 2) keep_alive x return r1 [case testIRTransformRegisterOps2] from mypy_extensions import i32, i64 def truncate(x: i64) -> i32: y = x return i32(y) def extend(x: i32) -> i64: y = x return i64(y) def int_op(x: i64, xx: i64) -> i64: y = x z = xx return y + z def comparison_op(x: i64, xx: i64) -> bool: y = x z = xx return y == z def float_op(x: float, xx: float) -> float: y = x z = xx return y + z def float_neg(x: float) -> float: y = x return -y def float_comparison_op(x: float, xx: float) -> bool: y = x z = xx return y == z [out] def truncate(x): x :: i64 r0 :: i32 L0: r0 = truncate x: i64 to i32 return r0 def extend(x): x :: i32 r0 :: i64 L0: r0 = extend signed x: i32 to i64 return r0 def int_op(x, xx): x, xx, r0 :: i64 L0: r0 = x + xx return r0 def comparison_op(x, xx): x, xx :: i64 r0 :: bit L0: r0 = x == xx return r0 def float_op(x, xx): x, xx, r0 :: float L0: r0 = x + xx return r0 def float_neg(x): x, r0 :: float L0: r0 = -x return r0 def float_comparison_op(x, xx): x, xx :: float r0 :: bit L0: r0 = x == xx return r0 -- Note that transforms of these ops aren't tested here: -- * LoadMem -- * SetMem -- * GetElementPtr -- * LoadAddress -- * Unborrow