323 changes: 323 additions & 0 deletions llvm/test/Transforms/DeadStoreElimination/MSSA/multiblock-captures.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,323 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -basicaa -dse -enable-dse-memoryssa -S | FileCheck %s

target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"

declare noalias i8* @malloc(i64)

declare void @foo()
declare void @capture(i8*)

; Check that we do not remove the second store, as %m is returned.
define i8* @test_return_captures_1() {
; CHECK-LABEL: @test_return_captures_1(
; CHECK-NEXT: [[M:%.*]] = call i8* @malloc(i64 24)
; CHECK-NEXT: store i8 1, i8* [[M]]
; CHECK-NEXT: ret i8* [[M]]
;
%m = call i8* @malloc(i64 24)
store i8 0, i8* %m
store i8 1, i8* %m
ret i8* %m
}

; Same as @test_return_captures_1, but across BBs.
define i8* @test_return_captures_2() {
; CHECK-LABEL: @test_return_captures_2(
; CHECK-NEXT: [[M:%.*]] = call i8* @malloc(i64 24)
; CHECK-NEXT: store i8 0, i8* [[M]]
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: store i8 1, i8* [[M]]
; CHECK-NEXT: ret i8* [[M]]
;
%m = call i8* @malloc(i64 24)
store i8 0, i8* %m
br label %exit

exit:
store i8 1, i8* %m
ret i8* %m
}


%S1 = type { i8 * }

; We cannot remove the last store to %m, because it escapes by storing it to %E.
define void @test_malloc_capture_1(%S1* %E) {
; CHECK-LABEL: @test_malloc_capture_1(
; CHECK-NEXT: [[M:%.*]] = call i8* @malloc(i64 24)
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: [[F_PTR:%.*]] = getelementptr [[S1:%.*]], %S1* [[E:%.*]], i32 0, i32 0
; CHECK-NEXT: store i8* [[M]], i8** [[F_PTR]]
; CHECK-NEXT: store i8 1, i8* [[M]]
; CHECK-NEXT: ret void
;
%m = call i8* @malloc(i64 24)
br label %exit

exit:
%f.ptr = getelementptr %S1, %S1* %E, i32 0, i32 0
store i8* %m, i8** %f.ptr
store i8 1, i8* %m
ret void
}

; Check we do not eliminate either store. The first one cannot be eliminated,
; due to the call of @capture. The second one because %m escapes.
define i8* @test_malloc_capture_2() {
; CHECK-LABEL: @test_malloc_capture_2(
; CHECK-NEXT: [[M:%.*]] = call i8* @malloc(i64 24)
; CHECK-NEXT: store i8 0, i8* [[M]]
; CHECK-NEXT: call void @capture(i8* [[M]])
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: store i8 1, i8* [[M]]
; CHECK-NEXT: ret i8* [[M]]
;
%m = call i8* @malloc(i64 24)
store i8 0, i8* %m
call void @capture(i8* %m)
br label %exit

exit:
store i8 1, i8* %m
ret i8* %m
}

; We can remove the first store store i8 0, i8* %m because there are no throwing
; instructions between the 2 stores and also %m escapes after the killing store.
define i8* @test_malloc_capture_3() {
; CHECK-LABEL: @test_malloc_capture_3(
; CHECK-NEXT: [[M:%.*]] = call i8* @malloc(i64 24)
; CHECK-NEXT: store i8 0, i8* [[M]]
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: store i8 1, i8* [[M]]
; CHECK-NEXT: call void @capture(i8* [[M]])
; CHECK-NEXT: ret i8* [[M]]
;
%m = call i8* @malloc(i64 24)
store i8 0, i8* %m
br label %exit

exit:
store i8 1, i8* %m
call void @capture(i8* %m)
ret i8* %m
}

; TODO: We could remove the first store store i8 0, i8* %m because %m escapes
; after the killing store.
define i8* @test_malloc_capture_4() {
; CHECK-LABEL: @test_malloc_capture_4(
; CHECK-NEXT: [[M:%.*]] = call i8* @malloc(i64 24)
; CHECK-NEXT: store i8 0, i8* [[M]]
; CHECK-NEXT: call void @may_throw_readnone()
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: store i8 1, i8* [[M]]
; CHECK-NEXT: call void @capture(i8* [[M]])
; CHECK-NEXT: ret i8* [[M]]
;

%m = call i8* @malloc(i64 24)
store i8 0, i8* %m
call void @may_throw_readnone()
br label %exit

exit:
store i8 1, i8* %m
call void @capture(i8* %m)
ret i8* %m
}


; We cannot remove the first store store i8 0, i8* %m because %m escapes
; before the killing store and we may throw in between.
define i8* @test_malloc_capture_5() {
; CHECK-LABEL: @test_malloc_capture_5(
; CHECK-NEXT: [[M:%.*]] = call i8* @malloc(i64 24)
; CHECK-NEXT: call void @capture(i8* [[M]])
; CHECK-NEXT: store i8 0, i8* [[M]]
; CHECK-NEXT: call void @may_throw_readnone()
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: store i8 1, i8* [[M]]
; CHECK-NEXT: ret i8* [[M]]
;

%m = call i8* @malloc(i64 24)
call void @capture(i8* %m)
store i8 0, i8* %m
call void @may_throw_readnone()
br label %exit

exit:
store i8 1, i8* %m
ret i8* %m
}


; TODO: We could remove the first store 'store i8 0, i8* %m' even though there
; is a throwing instruction between them, because %m escapes after the killing
; store.
define i8* @test_malloc_capture_6() {
; CHECK-LABEL: @test_malloc_capture_6(
; CHECK-NEXT: [[M:%.*]] = call i8* @malloc(i64 24)
; CHECK-NEXT: store i8 0, i8* [[M]]
; CHECK-NEXT: call void @may_throw_readnone()
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: store i8 1, i8* [[M]]
; CHECK-NEXT: call void @capture(i8* [[M]])
; CHECK-NEXT: ret i8* [[M]]
;

%m = call i8* @malloc(i64 24)
store i8 0, i8* %m
call void @may_throw_readnone()
br label %exit

exit:
store i8 1, i8* %m
call void @capture(i8* %m)
ret i8* %m
}

; We can remove the first store 'store i8 0, i8* %m' even though there is a
; throwing instruction between them, because %m escapes after the killing store.
define i8* @test_malloc_capture_7() {
; CHECK-LABEL: @test_malloc_capture_7(
; CHECK-NEXT: [[M:%.*]] = call i8* @malloc(i64 24)
; CHECK-NEXT: store i8 0, i8* [[M]]
; CHECK-NEXT: call void @may_throw()
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: store i8 1, i8* [[M]]
; CHECK-NEXT: call void @capture(i8* [[M]])
; CHECK-NEXT: ret i8* [[M]]
;

%m = call i8* @malloc(i64 24)
store i8 0, i8* %m
call void @may_throw()
br label %exit

exit:
store i8 1, i8* %m
call void @capture(i8* %m)
ret i8* %m
}
; TODO: Remove store in exit.
; Stores to stack objects can be eliminated if they are not captured inside the function.
define void @test_alloca_nocapture_1() {
; CHECK-LABEL: @test_alloca_nocapture_1(
; CHECK-NEXT: [[M:%.*]] = alloca i8
; CHECK-NEXT: store i8 0, i8* [[M]]
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
%m = alloca i8
store i8 0, i8* %m
call void @foo()
br label %exit

exit:
store i8 1, i8* %m
ret void
}

; TODO: Remove store in exit.
; Cannot remove first store i8 0, i8* %m, as the call to @capture captures the object.
define void @test_alloca_capture_1() {
; CHECK-LABEL: @test_alloca_capture_1(
; CHECK-NEXT: [[M:%.*]] = alloca i8
; CHECK-NEXT: store i8 0, i8* [[M]]
; CHECK-NEXT: call void @capture(i8* [[M]])
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
%m = alloca i8
store i8 0, i8* %m
call void @capture(i8* %m)
br label %exit

exit:
store i8 1, i8* %m
ret void
}

; TODO: Remove store at exit.
; We can remove the last store to %m, even though it escapes because the alloca
; becomes invalid after the function returns.
define void @test_alloca_capture_2(%S1* %E) {
; CHECK-LABEL: @test_alloca_capture_2(
; CHECK-NEXT: [[M:%.*]] = alloca i8
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: exit:
; CHECK-NEXT: [[F_PTR:%.*]] = getelementptr [[S1:%.*]], %S1* [[E:%.*]], i32 0, i32 0
; CHECK-NEXT: store i8* [[M]], i8** [[F_PTR]]
; CHECK-NEXT: ret void
;
%m = alloca i8
br label %exit

exit:
%f.ptr = getelementptr %S1, %S1* %E, i32 0, i32 0
store i8* %m, i8** %f.ptr
store i8 1, i8* %m
ret void
}

; Readnone functions are not modeled in MemorySSA, but could throw.
; Make sure we do not eliminate the first store 'store i8 2, i8* %call'
define void @malloc_capture_throw_1() {
; CHECK-LABEL: @malloc_capture_throw_1(
; CHECK-NEXT: [[CALL:%.*]] = call i8* @malloc(i64 1)
; CHECK-NEXT: call void @may_capture(i8* [[CALL]])
; CHECK-NEXT: store i8 2, i8* [[CALL]], align 1
; CHECK-NEXT: call void @may_throw_readnone()
; CHECK-NEXT: store i8 3, i8* [[CALL]], align 1
; CHECK-NEXT: ret void
;
%call = call i8* @malloc(i64 1)
call void @may_capture(i8* %call)
store i8 2, i8* %call, align 1
call void @may_throw_readnone()
store i8 3, i8* %call, align 1
ret void
}

; Readnone functions are not modeled in MemorySSA, but could throw.
; Make sure we do not eliminate the first store 'store i8 2, i8* %call'
define void @malloc_capture_throw_2() {
; CHECK-LABEL: @malloc_capture_throw_2(
; CHECK-NEXT: [[CALL:%.*]] = call i8* @malloc(i64 1)
; CHECK-NEXT: call void @may_capture(i8* [[CALL]])
; CHECK-NEXT: store i8 2, i8* [[CALL]], align 1
; CHECK-NEXT: br label [[BB:%.*]]
; CHECK: bb:
; CHECK-NEXT: call void @may_throw_readnone()
; CHECK-NEXT: store i8 3, i8* [[CALL]], align 1
; CHECK-NEXT: ret void
;
%call = call i8* @malloc(i64 1)
call void @may_capture(i8* %call)
store i8 2, i8* %call, align 1
br label %bb

bb:
call void @may_throw_readnone()
store i8 3, i8* %call, align 1
ret void
}


declare void @may_capture(i8*)
declare void @may_throw_readnone() readnone
declare void @may_throw()
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -basicaa -dse -enable-dse-memoryssa -S | FileCheck %s
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"

declare void @f()
declare i32 @__CxxFrameHandler3(...)


; Make sure we do not eliminate `store i32 20, i32* %sv`. Even though it is a store
; to a stack object, we can read it in the landing/catchpad.
define void @test12(i32* %p) personality i32 (...)* @__CxxFrameHandler3 {
; CHECK-LABEL: @test12(
; CHECK-NEXT: block1:
; CHECK-NEXT: [[SV:%.*]] = alloca i32
; CHECK-NEXT: br label [[BLOCK2:%.*]]
; CHECK: block2:
; CHECK-NEXT: store i32 20, i32* [[SV]]
; CHECK-NEXT: invoke void @f()
; CHECK-NEXT: to label [[BLOCK3:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
; CHECK: block3:
; CHECK-NEXT: store i32 30, i32* [[SV]]
; CHECK-NEXT: br label [[EXIT:%.*]]
; CHECK: catch.dispatch:
; CHECK-NEXT: [[CS1:%.*]] = catchswitch within none [label %catch] unwind label [[CLEANUP:%.*]]
; CHECK: catch:
; CHECK-NEXT: [[C:%.*]] = catchpad within [[CS1]] []
; CHECK-NEXT: [[LV:%.*]] = load i32, i32* [[SV]]
; CHECK-NEXT: br label [[EXIT]]
; CHECK: cleanup:
; CHECK-NEXT: [[C1:%.*]] = cleanuppad within none []
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
block1:
%sv = alloca i32
br label %block2

block2:
store i32 20, i32* %sv
invoke void @f()
to label %block3 unwind label %catch.dispatch

block3:
store i32 30, i32* %sv
br label %exit

catch.dispatch:
%cs1 = catchswitch within none [label %catch] unwind label %cleanup

catch:
%c = catchpad within %cs1 []
%lv = load i32, i32* %sv
br label %exit

cleanup:
%c1 = cleanuppad within none []
br label %exit

exit:
store i32 40, i32* %sv
ret void
}
172 changes: 172 additions & 0 deletions llvm/test/Transforms/DeadStoreElimination/MSSA/multiblock-loops.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -basicaa -dse -enable-dse-memoryssa -S | FileCheck %s

target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
declare void @llvm.memset.p0i8.i64(i8* nocapture, i8, i64, i32, i1) nounwind

define void @test13(i32* noalias %P) {
; CHECK-LABEL: @test13(
; CHECK-NEXT: entry:
; CHECK-NEXT: br label [[FOR:%.*]]
; CHECK: for:
; CHECK-NEXT: store i32 0, i32* [[P:%.*]]
; CHECK-NEXT: br i1 false, label [[FOR]], label [[END:%.*]]
; CHECK: end:
; CHECK-NEXT: ret void
;
entry:
br label %for
for:
store i32 0, i32* %P
br i1 false, label %for, label %end
end:
ret void
}


define void @test14(i32* noalias %P) {
; CHECK-LABEL: @test14(
; CHECK-NEXT: entry:
; CHECK-NEXT: store i32 1, i32* [[P:%.*]]
; CHECK-NEXT: br label [[FOR:%.*]]
; CHECK: for:
; CHECK-NEXT: store i32 0, i32* [[P]]
; CHECK-NEXT: br i1 false, label [[FOR]], label [[END:%.*]]
; CHECK: end:
; CHECK-NEXT: ret void
;
entry:
store i32 1, i32* %P
br label %for
for:
store i32 0, i32* %P
br i1 false, label %for, label %end
end:
ret void
}

define void @test18(i32* noalias %P) {
; CHECK-LABEL: @test18(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[P2:%.*]] = bitcast i32* [[P:%.*]] to i8*
; CHECK-NEXT: store i32 0, i32* [[P]]
; CHECK-NEXT: br label [[FOR:%.*]]
; CHECK: for:
; CHECK-NEXT: store i8 1, i8* [[P2]]
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P]]
; CHECK-NEXT: store i8 2, i8* [[P2]]
; CHECK-NEXT: br i1 false, label [[FOR]], label [[END:%.*]]
; CHECK: end:
; CHECK-NEXT: ret void
;
entry:
%P2 = bitcast i32* %P to i8*
store i32 0, i32* %P
br label %for
for:
store i8 1, i8* %P2
%x = load i32, i32* %P
store i8 2, i8* %P2
br i1 false, label %for, label %end
end:
ret void
}

define void @test21(i32* noalias %P) {
; CHECK-LABEL: @test21(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ARRAYIDX0:%.*]] = getelementptr inbounds i32, i32* [[P:%.*]], i64 1
; CHECK-NEXT: [[P3:%.*]] = bitcast i32* [[ARRAYIDX0]] to i8*
; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* align 4 [[P3]], i8 0, i64 28, i1 false)
; CHECK-NEXT: br label [[FOR:%.*]]
; CHECK: for:
; CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds i32, i32* [[P]], i64 1
; CHECK-NEXT: store i32 1, i32* [[ARRAYIDX1]], align 4
; CHECK-NEXT: br i1 false, label [[FOR]], label [[END:%.*]]
; CHECK: end:
; CHECK-NEXT: ret void
;
entry:
%arrayidx0 = getelementptr inbounds i32, i32* %P, i64 1
%p3 = bitcast i32* %arrayidx0 to i8*
call void @llvm.memset.p0i8.i64(i8* %p3, i8 0, i64 28, i32 4, i1 false)
br label %for
for:
%arrayidx1 = getelementptr inbounds i32, i32* %P, i64 1
store i32 1, i32* %arrayidx1, align 4
br i1 false, label %for, label %end
end:
ret void
}

define void @test_loop(i32 %N, i32* noalias nocapture readonly %A, i32* noalias nocapture readonly %x, i32* noalias nocapture %b) local_unnamed_addr {
; CHECK-LABEL: @test_loop(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CMP27:%.*]] = icmp sgt i32 [[N:%.*]], 0
; CHECK-NEXT: br i1 [[CMP27]], label [[FOR_BODY4_LR_PH_PREHEADER:%.*]], label [[FOR_COND_CLEANUP:%.*]]
; CHECK: for.body4.lr.ph.preheader:
; CHECK-NEXT: br label [[FOR_BODY4_LR_PH:%.*]]
; CHECK: for.cond.cleanup:
; CHECK-NEXT: ret void
; CHECK: for.body4.lr.ph:
; CHECK-NEXT: [[I_028:%.*]] = phi i32 [ [[INC11:%.*]], [[FOR_COND_CLEANUP3:%.*]] ], [ 0, [[FOR_BODY4_LR_PH_PREHEADER]] ]
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, i32* [[B:%.*]], i32 [[I_028]]
; CHECK-NEXT: store i32 0, i32* [[ARRAYIDX]], align 4
; CHECK-NEXT: [[MUL:%.*]] = mul nsw i32 [[I_028]], [[N]]
; CHECK-NEXT: br label [[FOR_BODY4:%.*]]
; CHECK: for.body4:
; CHECK-NEXT: [[TMP0:%.*]] = phi i32 [ 0, [[FOR_BODY4_LR_PH]] ], [ [[ADD9:%.*]], [[FOR_BODY4]] ]
; CHECK-NEXT: [[J_026:%.*]] = phi i32 [ 0, [[FOR_BODY4_LR_PH]] ], [ [[INC:%.*]], [[FOR_BODY4]] ]
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[J_026]], [[MUL]]
; CHECK-NEXT: [[ARRAYIDX5:%.*]] = getelementptr inbounds i32, i32* [[A:%.*]], i32 [[ADD]]
; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[ARRAYIDX5]], align 4
; CHECK-NEXT: [[ARRAYIDX6:%.*]] = getelementptr inbounds i32, i32* [[X:%.*]], i32 [[J_026]]
; CHECK-NEXT: [[TMP2:%.*]] = load i32, i32* [[ARRAYIDX6]], align 4
; CHECK-NEXT: [[MUL7:%.*]] = mul nsw i32 [[TMP2]], [[TMP1]]
; CHECK-NEXT: [[ADD9]] = add nsw i32 [[MUL7]], [[TMP0]]
; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[J_026]], 1
; CHECK-NEXT: [[EXITCOND:%.*]] = icmp eq i32 [[INC]], [[N]]
; CHECK-NEXT: br i1 [[EXITCOND]], label [[FOR_COND_CLEANUP3]], label [[FOR_BODY4]]
; CHECK: for.cond.cleanup3:
; CHECK-NEXT: store i32 [[ADD9]], i32* [[ARRAYIDX]], align 4
; CHECK-NEXT: [[INC11]] = add nuw nsw i32 [[I_028]], 1
; CHECK-NEXT: [[EXITCOND29:%.*]] = icmp eq i32 [[INC11]], [[N]]
; CHECK-NEXT: br i1 [[EXITCOND29]], label [[FOR_COND_CLEANUP]], label [[FOR_BODY4_LR_PH]]
;
entry:
%cmp27 = icmp sgt i32 %N, 0
br i1 %cmp27, label %for.body4.lr.ph.preheader, label %for.cond.cleanup

for.body4.lr.ph.preheader: ; preds = %entry
br label %for.body4.lr.ph

for.cond.cleanup: ; preds = %for.cond.cleanup3, %entry
ret void

for.body4.lr.ph: ; preds = %for.body4.lr.ph.preheader, %for.cond.cleanup3
%i.028 = phi i32 [ %inc11, %for.cond.cleanup3 ], [ 0, %for.body4.lr.ph.preheader ]
%arrayidx = getelementptr inbounds i32, i32* %b, i32 %i.028
store i32 0, i32* %arrayidx, align 4
%mul = mul nsw i32 %i.028, %N
br label %for.body4

for.body4: ; preds = %for.body4, %for.body4.lr.ph
%0 = phi i32 [ 0, %for.body4.lr.ph ], [ %add9, %for.body4 ]
%j.026 = phi i32 [ 0, %for.body4.lr.ph ], [ %inc, %for.body4 ]
%add = add nsw i32 %j.026, %mul
%arrayidx5 = getelementptr inbounds i32, i32* %A, i32 %add
%1 = load i32, i32* %arrayidx5, align 4
%arrayidx6 = getelementptr inbounds i32, i32* %x, i32 %j.026
%2 = load i32, i32* %arrayidx6, align 4
%mul7 = mul nsw i32 %2, %1
%add9 = add nsw i32 %mul7, %0
%inc = add nuw nsw i32 %j.026, 1
%exitcond = icmp eq i32 %inc, %N
br i1 %exitcond, label %for.cond.cleanup3, label %for.body4

for.cond.cleanup3: ; preds = %for.body4
store i32 %add9, i32* %arrayidx, align 4
%inc11 = add nuw nsw i32 %i.028, 1
%exitcond29 = icmp eq i32 %inc11, %N
br i1 %exitcond29, label %for.cond.cleanup, label %for.body4.lr.ph
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -basicaa -dse -enable-dse-memoryssa -S | FileCheck %s

target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
declare void @unknown_func()
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture, i64, i1) nounwind
declare void @llvm.memset.p0i8.i64(i8* nocapture, i8, i64, i32, i1) nounwind

define void @test19(i32* noalias %P) {
; CHECK-LABEL: @test19(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ARRAYIDX0:%.*]] = getelementptr inbounds i32, i32* [[P:%.*]], i64 1
; CHECK-NEXT: [[P3:%.*]] = bitcast i32* [[ARRAYIDX0]] to i8*
; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* align 4 [[P3]], i8 0, i64 28, i1 false)
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds i32, i32* [[P]], i64 1
; CHECK-NEXT: store i32 1, i32* [[ARRAYIDX1]], align 4
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: ret void
;
entry:
%arrayidx0 = getelementptr inbounds i32, i32* %P, i64 1
%p3 = bitcast i32* %arrayidx0 to i8*
call void @llvm.memset.p0i8.i64(i8* %p3, i8 0, i64 28, i32 4, i1 false)
br i1 true, label %bb1, label %bb2
bb1:
br label %bb3
bb2:
%arrayidx1 = getelementptr inbounds i32, i32* %P, i64 1
store i32 1, i32* %arrayidx1, align 4
br label %bb3
bb3:
ret void
}


define void @test20(i32* noalias %P) {
; CHECK-LABEL: @test20(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[ARRAYIDX0:%.*]] = getelementptr inbounds i32, i32* [[P:%.*]], i64 1
; CHECK-NEXT: [[P3:%.*]] = bitcast i32* [[ARRAYIDX0]] to i8*
; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* align 4 [[P3]], i8 0, i64 28, i1 false)
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds i32, i32* [[P]], i64 1
; CHECK-NEXT: store i32 1, i32* [[ARRAYIDX1]], align 4
; CHECK-NEXT: ret void
;
entry:
%arrayidx0 = getelementptr inbounds i32, i32* %P, i64 1
%p3 = bitcast i32* %arrayidx0 to i8*
call void @llvm.memset.p0i8.i64(i8* %p3, i8 0, i64 28, i32 4, i1 false)
br i1 true, label %bb1, label %bb2
bb1:
br label %bb3
bb2:
br label %bb3
bb3:
%arrayidx1 = getelementptr inbounds i32, i32* %P, i64 1
store i32 1, i32* %arrayidx1, align 4
ret void
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -basicaa -dse -enable-dse-memoryssa -S | FileCheck %s

target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"


define void @test4(i32* noalias %P) {
; CHECK-LABEL: @test4(
; CHECK-NEXT: store i32 0, i32* [[P:%.*]]
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: [[X:%.*]] = load i32, i32* [[P]]
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: store i32 0, i32* [[P]]
; CHECK-NEXT: ret void
;
store i32 0, i32* %P
br i1 true, label %bb1, label %bb2
bb1:
br label %bb3
bb2:
%x = load i32, i32* %P
br label %bb3
bb3:
store i32 0, i32* %P
ret void
}

define void @test5(i32* noalias %P) {
; CHECK-LABEL: @test5(
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
; CHECK-NEXT: store i32 1, i32* [[P:%.*]]
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: store i32 1, i32* [[P]]
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: store i32 0, i32* [[P]]
; CHECK-NEXT: ret void
;
br i1 true, label %bb1, label %bb2
bb1:
store i32 1, i32* %P
br label %bb3
bb2:
store i32 1, i32* %P
br label %bb3
bb3:
store i32 0, i32* %P
ret void
}

define void @test8(i32* %P, i32* %Q) {
; CHECK-LABEL: @test8(
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
; CHECK-NEXT: store i32 1, i32* [[P:%.*]]
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: store i32 1, i32* [[Q:%.*]]
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: store i32 0, i32* [[P]]
; CHECK-NEXT: ret void
;
br i1 true, label %bb1, label %bb2
bb1:
store i32 1, i32* %P
br label %bb3
bb2:
store i32 1, i32* %Q
br label %bb3
bb3:
store i32 0, i32* %P
ret void
}

define void @test10(i32* noalias %P) {
; CHECK-LABEL: @test10(
; CHECK-NEXT: [[P2:%.*]] = bitcast i32* [[P:%.*]] to i8*
; CHECK-NEXT: store i32 0, i32* [[P]]
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: store i8 1, i8* [[P2]]
; CHECK-NEXT: ret void
;
%P2 = bitcast i32* %P to i8*
store i32 0, i32* %P
br i1 true, label %bb1, label %bb2
bb1:
br label %bb3
bb2:
br label %bb3
bb3:
store i8 1, i8* %P2
ret void
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -basicaa -dse -enable-dse-memoryssa -S | FileCheck %s

target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"


define void @second_store_smaller(i32* noalias %P) {
; CHECK-LABEL: @second_store_smaller(
; CHECK-NEXT: store i32 1, i32* [[P:%.*]]
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: [[P_I16:%.*]] = bitcast i32* [[P]] to i16*
; CHECK-NEXT: store i16 0, i16* [[P_I16]]
; CHECK-NEXT: ret void
;
store i32 1, i32* %P
br i1 true, label %bb1, label %bb2
bb1:
br label %bb3
bb2:
br label %bb3
bb3:
%P.i16 = bitcast i32* %P to i16*
store i16 0, i16* %P.i16
ret void
}


define void @second_store_bigger(i32* noalias %P) {
; CHECK-LABEL: @second_store_bigger(
; CHECK-NEXT: store i32 1, i32* [[P:%.*]]
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: [[P_I64:%.*]] = bitcast i32* [[P]] to i64*
; CHECK-NEXT: store i64 0, i64* [[P_I64]]
; CHECK-NEXT: ret void
;
store i32 1, i32* %P
br i1 true, label %bb1, label %bb2
bb1:
br label %bb3
bb2:
br label %bb3
bb3:
%P.i64 = bitcast i32* %P to i64*
store i64 0, i64* %P.i64
ret void
}

116 changes: 116 additions & 0 deletions llvm/test/Transforms/DeadStoreElimination/MSSA/multiblock-simple.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -basicaa -dse -enable-dse-memoryssa -S | FileCheck %s

target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"


define void @test2(i32* noalias %P) {
; CHECK-LABEL: @test2(
; CHECK-NEXT: store i32 1, i32* [[P:%.*]]
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: store i32 0, i32* [[P]]
; CHECK-NEXT: ret void
;
store i32 1, i32* %P
br i1 true, label %bb1, label %bb2
bb1:
br label %bb3
bb2:
br label %bb3
bb3:
store i32 0, i32* %P
ret void
}

define void @test3(i32* noalias %P) {
; CHECK-LABEL: @test3(
; CHECK-NEXT: store i32 0, i32* [[P:%.*]]
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: store i32 0, i32* [[P]]
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: ret void
;
store i32 0, i32* %P
br i1 true, label %bb1, label %bb2
bb1:
br label %bb3
bb2:
store i32 0, i32* %P
br label %bb3
bb3:
ret void
}


define void @test7(i32* noalias %P, i32* noalias %Q) {
; CHECK-LABEL: @test7(
; CHECK-NEXT: store i32 1, i32* [[Q:%.*]]
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[P:%.*]]
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: store i32 0, i32* [[Q]]
; CHECK-NEXT: store i32 0, i32* [[P]]
; CHECK-NEXT: ret void
;
store i32 1, i32* %Q
br i1 true, label %bb1, label %bb2
bb1:
load i32, i32* %P
br label %bb3
bb2:
br label %bb3
bb3:
store i32 0, i32* %Q
store i32 0, i32* %P
ret void
}

define i32 @test22(i32* %P, i32* noalias %Q, i32* %R) {
; CHECK-LABEL: @test22(
; CHECK-NEXT: store i32 2, i32* [[P:%.*]]
; CHECK-NEXT: store i32 3, i32* [[Q:%.*]]
; CHECK-NEXT: [[L:%.*]] = load i32, i32* [[R:%.*]]
; CHECK-NEXT: ret i32 [[L]]
;
store i32 1, i32* %Q
store i32 2, i32* %P
store i32 3, i32* %Q
%l = load i32, i32* %R
ret i32 %l
}

define void @test9(i32* noalias %P) {
; CHECK-LABEL: @test9(
; CHECK-NEXT: store i32 0, i32* [[P:%.*]]
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: ret void
; CHECK: bb3:
; CHECK-NEXT: store i32 0, i32* [[P]]
; CHECK-NEXT: ret void
;
store i32 0, i32* %P
br i1 true, label %bb1, label %bb2
bb1:
br label %bb3
bb2:
ret void
bb3:
store i32 0, i32* %P
ret void
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; XFAIL: *
; RUN: opt < %s -basicaa -dse -enable-dse-memoryssa -S | FileCheck %s

target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
declare void @unknown_func()

define void @test6(i32* noalias %P) {
; CHECK-LABEL: @test6(
; CHECK-NEXT: store i32 0, i32* [[P:%.*]]
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: call void @unknown_func()
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: store i32 0, i32* [[P]]
; CHECK-NEXT: ret void
;
store i32 0, i32* %P
br i1 true, label %bb1, label %bb2
bb1:
br label %bb3
bb2:
call void @unknown_func()
br label %bb3
bb3:
store i32 0, i32* %P
ret void
}

define i32 @test22(i32* %P, i32* noalias %Q, i32* %R) {
; CHECK-LABEL: @test22(
; CHECK-NEXT: store i32 2, i32* [[P:%.*]]
; CHECK-NEXT: store i32 3, i32* [[Q:%.*]]
; CHECK-NEXT: [[L:%.*]] = load i32, i32* [[R:%.*]]
; CHECK-NEXT: ret i32 [[L]]
;
store i32 1, i32* %Q
store i32 2, i32* %P
store i32 3, i32* %Q
%l = load i32, i32* %R
ret i32 %l
}


define void @test23(i32* noalias %P) {
; CHECK-LABEL: @test23(
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: call void @unknown_func()
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: store i32 0, i32* [[P:%.*]]
; CHECK-NEXT: ret void
;
br i1 true, label %bb1, label %bb2
bb1:
store i32 0, i32* %P
br label %bb3
bb2:
call void @unknown_func()
br label %bb3
bb3:
store i32 0, i32* %P
ret void
}


define void @test24(i32* noalias %P) {
; CHECK-LABEL: @test24(
; CHECK-NEXT: br i1 true, label [[BB2:%.*]], label [[BB1:%.*]]
; CHECK: bb1:
; CHECK-NEXT: br label [[BB3:%.*]]
; CHECK: bb2:
; CHECK-NEXT: call void @unknown_func()
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: store i32 0, i32* [[P:%.*]]
; CHECK-NEXT: ret void
;
br i1 true, label %bb2, label %bb1
bb1:
store i32 0, i32* %P
br label %bb3
bb2:
call void @unknown_func()
br label %bb3
bb3:
store i32 0, i32* %P
ret void
}
21 changes: 21 additions & 0 deletions llvm/test/Transforms/DeadStoreElimination/MSSA/no-targetdata.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
; RUN: opt -basicaa -dse -enable-dse-memoryssa -S < %s | FileCheck %s

declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture, i64, i1) nounwind

define void @fn(i8* nocapture %buf) #0 {
entry:

; We would not eliminate the first memcpy with data layout, and we should not
; eliminate it without data layout.
; CHECK-LABEL: @fn
; CHECK: tail call void @llvm.memcpy.p0i8.p0i8.i64
; CHECK: tail call void @llvm.memcpy.p0i8.p0i8.i64
; CHECK: ret void

%arrayidx = getelementptr i8, i8* %buf, i64 18
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %arrayidx, i8* %buf, i64 18, i1 false)
store i8 1, i8* %arrayidx, align 1
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %buf, i8* %arrayidx, i64 18, i1 false)
ret void
}

55 changes: 55 additions & 0 deletions llvm/test/Transforms/DeadStoreElimination/MSSA/operand-bundles.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
; RUN: opt < %s -basicaa -dse -enable-dse-memoryssa -S | FileCheck %s

declare noalias i8* @malloc(i64) "malloc-like"

declare void @foo()
declare void @bar(i8*)

define void @test() {
%obj = call i8* @malloc(i64 8)
store i8 0, i8* %obj
; don't remove store. %obj should be treated like it will be read by the @foo.
; CHECK: store i8 0, i8* %obj
call void @foo() ["deopt" (i8* %obj)]
ret void
}

define void @test1() {
%obj = call i8* @malloc(i64 8)
store i8 0, i8* %obj
; CHECK: store i8 0, i8* %obj
call void @bar(i8* nocapture %obj)
ret void
}

define void @test2() {
%obj = call i8* @malloc(i64 8)
store i8 0, i8* %obj
; CHECK-NOT: store i8 0, i8* %obj
call void @foo()
ret void
}

define void @test3() {
; CHECK-LABEL: @test3(
%s = alloca i64
; Verify that this first store is not considered killed by the second one
; since it could be observed from the deopt continuation.
; CHECK: store i64 1, i64* %s
store i64 1, i64* %s
call void @foo() [ "deopt"(i64* %s) ]
store i64 0, i64* %s
ret void
}

declare noalias i8* @calloc(i64, i64)

define void @test4() {
; CHECK-LABEL: @test4
%local_obj = call i8* @calloc(i64 1, i64 4)
call void @foo() ["deopt" (i8* %local_obj)]
store i8 0, i8* %local_obj, align 4
; CHECK-NOT: store i8 0, i8* %local_obj, align 4
call void @bar(i8* nocapture %local_obj)
ret void
}
68 changes: 68 additions & 0 deletions llvm/test/Transforms/DeadStoreElimination/MSSA/overlap.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -basicaa -dse -enable-dse-memoryssa -S | FileCheck %s
; RUN: opt < %s -aa-pipeline=basic-aa -passes=dse -enable-dse-memoryssa -S | FileCheck %s

declare void @use(i64*)

define void @test1() {
; CHECK-LABEL: @test1(
; CHECK-NEXT: [[A:%.*]] = alloca i64
; CHECK-NEXT: call void @use(i64* [[A]])
; CHECK-NEXT: [[PTR1:%.*]] = bitcast i64* [[A]] to i8*
; CHECK-NEXT: [[PTR2:%.*]] = getelementptr i8, i8* [[PTR1]], i32 1
; CHECK-NEXT: store i8 10, i8* [[PTR1]]
; CHECK-NEXT: store i8 20, i8* [[PTR2]]
; CHECK-NEXT: [[LV:%.*]] = load i64, i64* [[A]]
; CHECK-NEXT: store i8 0, i8* [[PTR1]]
; CHECK-NEXT: call void @use(i64* [[A]])
; CHECK-NEXT: ret void
;
%a = alloca i64
call void @use(i64* %a)
%ptr1 = bitcast i64* %a to i8*
%ptr2 = getelementptr i8, i8* %ptr1, i32 1

store i8 10, i8* %ptr1
store i8 20, i8* %ptr2
%lv = load i64, i64* %a
store i8 0, i8* %ptr1

call void @use(i64* %a)
ret void
}

define void @test2() {
; CHECK-LABEL: @test2(
; CHECK-NEXT: [[A:%.*]] = alloca i64
; CHECK-NEXT: call void @use(i64* [[A]])
; CHECK-NEXT: [[PTR1:%.*]] = bitcast i64* [[A]] to i8*
; CHECK-NEXT: [[PTR2:%.*]] = getelementptr i8, i8* [[PTR1]], i32 1
; CHECK-NEXT: store i8 10, i8* [[PTR1]]
; CHECK-NEXT: store i8 20, i8* [[PTR2]]
; CHECK-NEXT: br i1 undef, label [[BB1:%.*]], label [[END:%.*]]
; CHECK: bb1:
; CHECK-NEXT: [[LV:%.*]] = load i64, i64* [[A]]
; CHECK-NEXT: br label [[END]]
; CHECK: end:
; CHECK-NEXT: store i8 0, i8* [[PTR1]]
; CHECK-NEXT: call void @use(i64* [[A]])
; CHECK-NEXT: ret void
;
%a = alloca i64
call void @use(i64* %a)
%ptr1 = bitcast i64* %a to i8*
%ptr2 = getelementptr i8, i8* %ptr1, i32 1

store i8 10, i8* %ptr1
store i8 20, i8* %ptr2
br i1 undef, label %bb1, label %end

bb1:
%lv = load i64, i64* %a
br label %end

end:
store i8 0, i8* %ptr1
call void @use(i64* %a)
ret void
}
38 changes: 38 additions & 0 deletions llvm/test/Transforms/DeadStoreElimination/MSSA/pr11390.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
; RUN: opt -basicaa -dse -enable-dse-memoryssa -S < %s | FileCheck %s
; PR11390
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

define fastcc void @cat_domain(i8* nocapture %name, i8* nocapture %domain, i8**
nocapture %s) nounwind uwtable {
entry:
%call = tail call i64 @strlen(i8* %name) nounwind readonly
%call1 = tail call i64 @strlen(i8* %domain) nounwind readonly
%add = add i64 %call, 1
%add2 = add i64 %add, %call1
%add3 = add i64 %add2, 1
%call4 = tail call noalias i8* @malloc(i64 %add3) nounwind
store i8* %call4, i8** %s, align 8
%tobool = icmp eq i8* %call4, null
br i1 %tobool, label %return, label %if.end

if.end: ; preds = %entry
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %call4, i8* %name, i64 %call, i1 false)
%arrayidx = getelementptr inbounds i8, i8* %call4, i64 %call
store i8 46, i8* %arrayidx, align 1
; CHECK: store i8 46
%add.ptr5 = getelementptr inbounds i8, i8* %call4, i64 %add
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %add.ptr5, i8* %domain, i64 %call1, i1 false)
%arrayidx8 = getelementptr inbounds i8, i8* %call4, i64 %add2
store i8 0, i8* %arrayidx8, align 1
br label %return

return: ; preds = %if.end, %entry
ret void
}

declare i64 @strlen(i8* nocapture) nounwind readonly

declare noalias i8* @malloc(i64) nounwind

declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture, i64, i1) nounwind
418 changes: 418 additions & 0 deletions llvm/test/Transforms/DeadStoreElimination/MSSA/simple-todo.ll

Large diffs are not rendered by default.

422 changes: 422 additions & 0 deletions llvm/test/Transforms/DeadStoreElimination/MSSA/simple.ll

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions llvm/test/Transforms/DeadStoreElimination/MSSA/tail-byval.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
; RUN: opt -dse -enable-dse-memoryssa -S < %s | FileCheck %s

; Don't eliminate stores to allocas before tail calls to functions that use
; byval. It's correct to mark calls like these as 'tail'. To implement this tail
; call, the backend should copy the bytes from the alloca into the argument area
; before clearing the stack.

target datalayout = "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128"
target triple = "i386-unknown-linux-gnu"

declare void @g(i32* byval %p)

define void @f(i32* byval %x) {
entry:
%p = alloca i32
%v = load i32, i32* %x
store i32 %v, i32* %p
tail call void @g(i32* byval %p)
ret void
}
; CHECK-LABEL: define void @f(i32* byval %x)
; CHECK: store i32 %v, i32* %p
; CHECK: tail call void @g(i32* byval %p)