diff --git a/llvm/test/Transforms/DeadStoreElimination/MSSA/lifetime.ll b/llvm/test/Transforms/DeadStoreElimination/MSSA/lifetime.ll index 9aa3c9c1fd42..3ed68cc7227a 100644 --- a/llvm/test/Transforms/DeadStoreElimination/MSSA/lifetime.ll +++ b/llvm/test/Transforms/DeadStoreElimination/MSSA/lifetime.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -S -basic-aa -dse < %s | FileCheck %s target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128" @@ -8,28 +9,145 @@ declare void @llvm.memset.p0i8.i8(i8* nocapture, i8, i8, i1) nounwind define void @test1() { ; CHECK-LABEL: @test1( +; CHECK-NEXT: [[A:%.*]] = alloca i8, align 1 +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 1, i8* [[A]]) +; CHECK-NEXT: ret void +; %A = alloca i8 store i8 0, i8* %A ;; Written to by memset call void @llvm.lifetime.end.p0i8(i64 1, i8* %A) -; CHECK: lifetime.end call void @llvm.memset.p0i8.i8(i8* %A, i8 0, i8 -1, i1 false) -; CHECK-NOT: memset ret void -; CHECK: ret void } define void @test2(i32* %P) { -; CHECK: test2 +; CHECK-LABEL: @test2( +; CHECK-NEXT: [[Q:%.*]] = getelementptr i32, i32* [[P:%.*]], i32 1 +; CHECK-NEXT: [[R:%.*]] = bitcast i32* [[Q]] to i8* +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* [[R]]) +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* [[R]]) +; CHECK-NEXT: ret void +; %Q = getelementptr i32, i32* %P, i32 1 %R = bitcast i32* %Q to i8* call void @llvm.lifetime.start.p0i8(i64 4, i8* %R) -; CHECK: lifetime.start store i32 0, i32* %Q ;; This store is dead. -; CHECK-NOT: store call void @llvm.lifetime.end.p0i8(i64 4, i8* %R) -; CHECK: lifetime.end ret void } + +; lifetime.end only marks the first two bytes of %A as dead. Make sure +; `store i8 20, i8* %A.2 is not removed. +define void @test3_lifetime_end_partial() { +; CHECK-LABEL: @test3_lifetime_end_partial( +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[A_0:%.*]] = bitcast i32* [[A]] to i8* +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 2, i8* [[A_0]]) +; CHECK-NEXT: [[A_1:%.*]] = getelementptr i8, i8* [[A_0]], i64 1 +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 2, i8* [[A_0]]) +; CHECK-NEXT: call void @use(i8* [[A_1]]) +; CHECK-NEXT: ret void +; + %A = alloca i32 + + %A.0 = bitcast i32 * %A to i8* + call void @llvm.lifetime.start.p0i8(i64 2, i8* %A.0) + %A.1 = getelementptr i8, i8* %A.0, i64 1 + %A.2 = getelementptr i8, i8* %A.0, i64 2 + + store i8 0, i8* %A.0 + store i8 10, i8* %A.1 + store i8 20, i8* %A.2 + + call void @llvm.lifetime.end.p0i8(i64 2, i8* %A.0) + call void @use(i8* %A.1) + ret void +} + +; lifetime.end only marks the first two bytes of %A as dead. Make sure +; `store i8 20, i8* %A.2 is not removed. +define void @test4_lifetime_end_partial_loop() { +; CHECK-LABEL: @test4_lifetime_end_partial_loop( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[A_0:%.*]] = bitcast i32* [[A]] to i8* +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[IV:%.*]] = phi i8 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 2, i8* [[A_0]]) +; CHECK-NEXT: [[A_1:%.*]] = getelementptr i8, i8* [[A_0]], i64 1 +; CHECK-NEXT: [[A_2:%.*]] = getelementptr i8, i8* [[A_0]], i64 2 +; CHECK-NEXT: call void @use(i8* [[A_1]]) +; CHECK-NEXT: store i8 20, i8* [[A_2]], align 1 +; CHECK-NEXT: store i8 10, i8* [[A_1]], align 1 +; CHECK-NEXT: store i8 0, i8* [[A_0]], align 1 +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 2, i8* [[A_0]]) +; CHECK-NEXT: [[IV_NEXT]] = add i8 [[IV]], 10 +; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i8 [[IV_NEXT]], 10 +; CHECK-NEXT: br i1 [[EXITCOND]], label [[EXIT:%.*]], label [[LOOP]] +; CHECK: exit: +; CHECK-NEXT: ret void +; +entry: + %A = alloca i32 + + %A.0 = bitcast i32 * %A to i8* + br label %loop + +loop: + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + call void @llvm.lifetime.start.p0i8(i64 2, i8* %A.0) + %A.1 = getelementptr i8, i8* %A.0, i64 1 + %A.2 = getelementptr i8, i8* %A.0, i64 2 + + call void @use(i8* %A.1) + + store i8 20, i8* %A.2 + store i8 10, i8* %A.1 + store i8 0, i8* %A.0 + call void @llvm.lifetime.end.p0i8(i64 2, i8* %A.0) + + %iv.next = add i8 %iv, 10 + %exitcond = icmp eq i8 %iv.next, 10 + br i1 %exitcond, label %exit, label %loop + +exit: + ret void +} + +; lifetime.end only marks the first two bytes of %A as dead. Make sure +; `store i8 20, i8* %A.2 is not removed. +define void @test5_lifetime_end_partial(i32* %A) { +; CHECK-LABEL: @test5_lifetime_end_partial( +; CHECK-NEXT: [[A_0:%.*]] = bitcast i32* [[A:%.*]] to i8* +; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 2, i8* [[A_0]]) +; CHECK-NEXT: [[A_1:%.*]] = getelementptr i8, i8* [[A_0]], i64 1 +; CHECK-NEXT: [[A_2:%.*]] = getelementptr i8, i8* [[A_0]], i64 2 +; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 2, i8* [[A_0]]) +; CHECK-NEXT: call void @use(i8* [[A_1]]) +; CHECK-NEXT: store i8 30, i8* [[A_1]], align 1 +; CHECK-NEXT: store i8 40, i8* [[A_2]], align 1 +; CHECK-NEXT: ret void +; + + %A.0 = bitcast i32 * %A to i8* + call void @llvm.lifetime.start.p0i8(i64 2, i8* %A.0) + %A.1 = getelementptr i8, i8* %A.0, i64 1 + %A.2 = getelementptr i8, i8* %A.0, i64 2 + + store i8 0, i8* %A.0 + store i8 10, i8* %A.1 + store i8 20, i8* %A.2 + + call void @llvm.lifetime.end.p0i8(i64 2, i8* %A.0) + + call void @use(i8* %A.1) + store i8 30, i8* %A.1 + store i8 40, i8* %A.2 + ret void +} + +declare void @use(i8*) readonly