[llvm] [PromoteMemToReg] Insert store undef when removing lifetime markers (PR #191909)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 22 13:50:25 PDT 2026
================
@@ -0,0 +1,192 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=sroa -S | FileCheck %s
+
+; SROA should use lifetime intrinsics to avoid creating recurrent PHI nodes
+; when promoting struct allocas.
+;
+; The alloca is reused across loop iterations. Without lifetime markers, SROA
+; must conservatively assume the i32 field retains its value on the skip path,
+; so it inserts a back-edge PHI at the loop header carrying the previous
+; iteration's value.
+;
+; With lifetime.start at the top of the loop, the alloca's content is
+; logically undefined at that point each iteration. SROA should propagate this
+; onto each slice alloca: PromoteMemToReg can then treat lifetime.start as an
+; implicit "store undef", breaking the back-edge dependence and eliminating
+; the recurrent PHI.
+;
+; PromoteMemToReg treats lifetime markers as implicit "store undef", breaking
+; back-edge dependences and eliminating recurrent PHIs in @with_lifetime and
+; @with_lifetime_end.
+
+%struct.S = type { i8, i32, i8 }
+
+declare void @use(i32)
+declare void @llvm.lifetime.start.p0(ptr captures(none))
+declare void @llvm.lifetime.end.p0(ptr captures(none))
+
+; Without lifetime markers a recurrent PHI at the loop header is unavoidable.
+define void @without_lifetime(i32 %val, i1 %c1, i1 %c2) {
+; CHECK-LABEL: define void @without_lifetime(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[C1:%.*]], i1 [[C2:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[S_SROA_1_0:%.*]] = phi i32 [ undef, %[[ENTRY]] ], [ [[S_SROA_1_1:%.*]], %[[CLEANUP:.*]] ]
+; CHECK-NEXT: br i1 [[C1]], label %[[INIT:.*]], label %[[SKIP:.*]]
+; CHECK: [[INIT]]:
+; CHECK-NEXT: br label %[[READ:.*]]
+; CHECK: [[SKIP]]:
+; CHECK-NEXT: br label %[[READ]]
+; CHECK: [[READ]]:
+; CHECK-NEXT: [[S_SROA_1_1]] = phi i32 [ [[VAL]], %[[INIT]] ], [ [[S_SROA_1_0]], %[[SKIP]] ]
+; CHECK-NEXT: call void @use(i32 [[S_SROA_1_1]])
+; CHECK-NEXT: br label %[[CLEANUP]]
+; CHECK: [[CLEANUP]]:
+; CHECK-NEXT: br i1 [[C2]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %s = alloca %struct.S, align 4
+ br label %loop
+
+loop:
+ br i1 %c1, label %init, label %skip
+
+init:
+ %gep0 = getelementptr inbounds %struct.S, ptr %s, i32 0, i32 0
+ store i8 65, ptr %gep0, align 4
+ %gep1 = getelementptr inbounds %struct.S, ptr %s, i32 0, i32 1
+ store i32 %val, ptr %gep1, align 4
+ %gep2 = getelementptr inbounds %struct.S, ptr %s, i32 0, i32 2
+ store i8 90, ptr %gep2, align 4
+ br label %read
+
+skip:
+ br label %read
+
+read:
+ %gep1r = getelementptr inbounds %struct.S, ptr %s, i32 0, i32 1
+ %v = load i32, ptr %gep1r, align 4
+ call void @use(i32 %v)
+ br label %cleanup
+
+cleanup:
+ br i1 %c2, label %loop, label %exit
+
+exit:
+ ret void
+}
+
+; With lifetime markers the back-edge PHI is unnecessary: lifetime.start
+; at the top of the loop makes the alloca's content undef at that point,
+; so the skip path should yield undef rather than carrying a stale value.
+define void @with_lifetime(i32 %val, i1 %c1, i1 %c2) {
+; CHECK-LABEL: define void @with_lifetime(
+; CHECK-SAME: i32 [[VAL:%.*]], i1 [[C1:%.*]], i1 [[C2:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: br i1 [[C1]], label %[[INIT:.*]], label %[[SKIP:.*]]
+; CHECK: [[INIT]]:
+; CHECK-NEXT: br label %[[READ:.*]]
+; CHECK: [[SKIP]]:
+; CHECK-NEXT: br label %[[READ]]
+; CHECK: [[READ]]:
+; CHECK-NEXT: [[S_SROA_31_0:%.*]] = phi i32 [ [[VAL]], %[[INIT]] ], [ undef, %[[SKIP]] ]
+; CHECK-NEXT: call void @use(i32 [[S_SROA_31_0]])
+; CHECK-NEXT: br label %[[CLEANUP:.*]]
+; CHECK: [[CLEANUP]]:
+; CHECK-NEXT: br i1 [[C2]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+; No recurrent PHI should be created at the loop header.
+; The only PHI is at the read block, merging %val (init) with undef (skip).
----------------
nikic wrote:
Avoid these comments inside the function, keep everything in the comment above the function.
https://github.com/llvm/llvm-project/pull/191909
More information about the llvm-commits
mailing list