; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 ; RUN: opt -S -passes=dse < %s | FileCheck %s ; We conservative choose to prevent dead store elimination ; across release or stronger fences. It's not required ; (since the must still be a race on %addd.i), but ; it is conservatively correct. A legal optimization ; could hoist the second store above the fence, and then ; DSE one of them. define void @test1(ptr %addr.i) { ; CHECK-LABEL: define void @test1( ; CHECK-SAME: ptr [[ADDR_I:%.*]]) { ; CHECK-NEXT: store i32 5, ptr [[ADDR_I]], align 4 ; CHECK-NEXT: fence release ; CHECK-NEXT: store i32 5, ptr [[ADDR_I]], align 4 ; CHECK-NEXT: ret void ; store i32 5, ptr %addr.i, align 4 fence release store i32 5, ptr %addr.i, align 4 ret void } ; Same as previous, but with different values. If we ever optimize ; this more aggressively, this allows us to check that the correct ; store is retained (the 'i32 1' store in this case) define void @test1b(ptr %addr.i) { ; CHECK-LABEL: define void @test1b( ; CHECK-SAME: ptr [[ADDR_I:%.*]]) { ; CHECK-NEXT: store i32 42, ptr [[ADDR_I]], align 4 ; CHECK-NEXT: fence release ; CHECK-NEXT: store i32 1, ptr [[ADDR_I]], align 4 ; CHECK-NEXT: ret void ; store i32 42, ptr %addr.i, align 4 fence release store i32 1, ptr %addr.i, align 4 ret void } ; We *could* DSE across this fence, but don't. No other thread can ; observe the order of the acquire fence and the store. define void @test2(ptr %addr.i) { ; CHECK-LABEL: define void @test2( ; CHECK-SAME: ptr [[ADDR_I:%.*]]) { ; CHECK-NEXT: store i32 5, ptr [[ADDR_I]], align 4 ; CHECK-NEXT: fence acquire ; CHECK-NEXT: store i32 5, ptr [[ADDR_I]], align 4 ; CHECK-NEXT: ret void ; store i32 5, ptr %addr.i, align 4 fence acquire store i32 5, ptr %addr.i, align 4 ret void } ; TODO: ; We DSE stack alloc'ed and byval locations, in the presence of fences. ; Fence does not make an otherwise thread local store visible. ; Right now the DSE in presence of fence is only done in end blocks (with no successors), ; but the same logic applies to other basic blocks as well. ; The store to %addr.i can be removed since it is a byval attribute define void @test3(ptr byval(i32) %addr.i) { ; CHECK-LABEL: define void @test3( ; CHECK-SAME: ptr byval(i32) [[ADDR_I:%.*]]) { ; CHECK-NEXT: store i32 5, ptr [[ADDR_I]], align 4 ; CHECK-NEXT: fence release ; CHECK-NEXT: ret void ; store i32 5, ptr %addr.i, align 4 fence release ret void } declare void @foo(ptr nocapture %p) declare noalias ptr @malloc(i32) ; TODO: ; DSE of stores in locations allocated through library calls. define void @test_nocapture() { ; CHECK-LABEL: define void @test_nocapture() { ; CHECK-NEXT: [[M:%.*]] = call ptr @malloc(i32 24) ; CHECK-NEXT: call void @foo(ptr [[M]]) ; CHECK-NEXT: store i8 4, ptr [[M]], align 1 ; CHECK-NEXT: fence release ; CHECK-NEXT: ret void ; %m = call ptr @malloc(i32 24) call void @foo(ptr %m) store i8 4, ptr %m fence release ret void } ; TODO: ; This is a full fence, but it does not make a thread local store visible. ; We can DSE the store in presence of the fence. define void @fence_seq_cst() { ; CHECK-LABEL: define void @fence_seq_cst() { ; CHECK-NEXT: [[P1:%.*]] = alloca i32, align 4 ; CHECK-NEXT: store i32 0, ptr [[P1]], align 4 ; CHECK-NEXT: fence seq_cst ; CHECK-NEXT: ret void ; %P1 = alloca i32 store i32 0, ptr %P1, align 4 fence seq_cst store i32 4, ptr %P1, align 4 ret void }