| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py |
| ; RUN: opt < %s -S -passes=early-cse -earlycse-debug-hash | FileCheck %s |
| ; RUN: opt < %s -S -passes='early-cse<memssa>' | FileCheck %s |
| |
| define i32 @test12(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @test12( |
| ; CHECK-NEXT: [[LOAD0:%.*]] = load i32, ptr [[P1:%.*]], align 4 |
| ; CHECK-NEXT: [[TMP1:%.*]] = load atomic i32, ptr [[P2:%.*]] seq_cst, align 4 |
| ; CHECK-NEXT: [[LOAD1:%.*]] = load i32, ptr [[P1]], align 4 |
| ; CHECK-NEXT: [[SEL:%.*]] = select i1 [[B:%.*]], i32 [[LOAD0]], i32 [[LOAD1]] |
| ; CHECK-NEXT: ret i32 [[SEL]] |
| ; |
| %load0 = load i32, ptr %P1 |
| %1 = load atomic i32, ptr %P2 seq_cst, align 4 |
| %load1 = load i32, ptr %P1 |
| %sel = select i1 %B, i32 %load0, i32 %load1 |
| ret i32 %sel |
| } |
| |
| ; atomic to non-atomic forwarding is legal |
| define i32 @test13(i1 %B, ptr %P1) { |
| ; CHECK-LABEL: @test13( |
| ; CHECK-NEXT: [[A:%.*]] = load atomic i32, ptr [[P1:%.*]] seq_cst, align 4 |
| ; CHECK-NEXT: ret i32 0 |
| ; |
| %a = load atomic i32, ptr %P1 seq_cst, align 4 |
| %b = load i32, ptr %P1 |
| %res = sub i32 %a, %b |
| ret i32 %res |
| } |
| |
| ; atomic to unordered atomic forwarding is legal |
| define i32 @test14(i1 %B, ptr %P1) { |
| ; CHECK-LABEL: @test14( |
| ; CHECK-NEXT: [[A:%.*]] = load atomic i32, ptr [[P1:%.*]] seq_cst, align 4 |
| ; CHECK-NEXT: ret i32 0 |
| ; |
| %a = load atomic i32, ptr %P1 seq_cst, align 4 |
| %b = load atomic i32, ptr %P1 unordered, align 4 |
| %res = sub i32 %a, %b |
| ret i32 %res |
| } |
| |
| ; implementation restriction: can't forward to stonger |
| ; than unordered |
| define i32 @test15(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @test15( |
| ; CHECK-NEXT: [[A:%.*]] = load atomic i32, ptr [[P1:%.*]] seq_cst, align 4 |
| ; CHECK-NEXT: [[B:%.*]] = load atomic i32, ptr [[P1]] seq_cst, align 4 |
| ; CHECK-NEXT: [[RES:%.*]] = sub i32 [[A]], [[B]] |
| ; CHECK-NEXT: ret i32 [[RES]] |
| ; |
| %a = load atomic i32, ptr %P1 seq_cst, align 4 |
| %b = load atomic i32, ptr %P1 seq_cst, align 4 |
| %res = sub i32 %a, %b |
| ret i32 %res |
| } |
| |
| ; forwarding non-atomic to atomic is wrong! (However, |
| ; it would be legal to use the later value in place of the |
| ; former in this particular example. We just don't |
| ; do that right now.) |
| define i32 @test16(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @test16( |
| ; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[P1:%.*]], align 4 |
| ; CHECK-NEXT: [[B:%.*]] = load atomic i32, ptr [[P1]] unordered, align 4 |
| ; CHECK-NEXT: [[RES:%.*]] = sub i32 [[A]], [[B]] |
| ; CHECK-NEXT: ret i32 [[RES]] |
| ; |
| %a = load i32, ptr %P1, align 4 |
| %b = load atomic i32, ptr %P1 unordered, align 4 |
| %res = sub i32 %a, %b |
| ret i32 %res |
| } |
| |
| ; Can't DSE across a full fence |
| define void @fence_seq_cst_store(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @fence_seq_cst_store( |
| ; CHECK-NEXT: store i32 0, ptr [[P1:%.*]], align 4 |
| ; CHECK-NEXT: store atomic i32 0, ptr [[P2:%.*]] seq_cst, align 4 |
| ; CHECK-NEXT: store i32 0, ptr [[P1]], align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| store i32 0, ptr %P1, align 4 |
| store atomic i32 0, ptr %P2 seq_cst, align 4 |
| store i32 0, ptr %P1, align 4 |
| ret void |
| } |
| |
| ; Can't DSE across a full fence |
| define void @fence_seq_cst(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @fence_seq_cst( |
| ; CHECK-NEXT: store i32 0, ptr [[P1:%.*]], align 4 |
| ; CHECK-NEXT: fence seq_cst |
| ; CHECK-NEXT: store i32 0, ptr [[P1]], align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| store i32 0, ptr %P1, align 4 |
| fence seq_cst |
| store i32 0, ptr %P1, align 4 |
| ret void |
| } |
| |
| ; Can't DSE across a full fence |
| define void @fence_asm_sideeffect(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @fence_asm_sideeffect( |
| ; CHECK-NEXT: store i32 0, ptr [[P1:%.*]], align 4 |
| ; CHECK-NEXT: call void asm sideeffect "", ""() |
| ; CHECK-NEXT: store i32 0, ptr [[P1]], align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| store i32 0, ptr %P1, align 4 |
| call void asm sideeffect "", ""() |
| store i32 0, ptr %P1, align 4 |
| ret void |
| } |
| |
| ; Can't DSE across a full fence |
| define void @fence_asm_memory(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @fence_asm_memory( |
| ; CHECK-NEXT: store i32 0, ptr [[P1:%.*]], align 4 |
| ; CHECK-NEXT: call void asm "", "~{memory}"() |
| ; CHECK-NEXT: store i32 0, ptr [[P1]], align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| store i32 0, ptr %P1, align 4 |
| call void asm "", "~{memory}"() |
| store i32 0, ptr %P1, align 4 |
| ret void |
| } |
| |
| ; Can't remove a volatile load |
| define i32 @volatile_load(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @volatile_load( |
| ; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[P1:%.*]], align 4 |
| ; CHECK-NEXT: [[B:%.*]] = load volatile i32, ptr [[P1]], align 4 |
| ; CHECK-NEXT: [[RES:%.*]] = sub i32 [[A]], [[B]] |
| ; CHECK-NEXT: ret i32 [[RES]] |
| ; |
| %a = load i32, ptr %P1, align 4 |
| %b = load volatile i32, ptr %P1, align 4 |
| %res = sub i32 %a, %b |
| ret i32 %res |
| } |
| |
| ; Can't remove redundant volatile loads |
| define i32 @redundant_volatile_load(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @redundant_volatile_load( |
| ; CHECK-NEXT: [[A:%.*]] = load volatile i32, ptr [[P1:%.*]], align 4 |
| ; CHECK-NEXT: [[B:%.*]] = load volatile i32, ptr [[P1]], align 4 |
| ; CHECK-NEXT: [[RES:%.*]] = sub i32 [[A]], [[B]] |
| ; CHECK-NEXT: ret i32 [[RES]] |
| ; |
| %a = load volatile i32, ptr %P1, align 4 |
| %b = load volatile i32, ptr %P1, align 4 |
| %res = sub i32 %a, %b |
| ret i32 %res |
| } |
| |
| ; Can't DSE a volatile store |
| define void @volatile_store(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @volatile_store( |
| ; CHECK-NEXT: store volatile i32 0, ptr [[P1:%.*]], align 4 |
| ; CHECK-NEXT: store i32 3, ptr [[P1]], align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| store volatile i32 0, ptr %P1, align 4 |
| store i32 3, ptr %P1, align 4 |
| ret void |
| } |
| |
| ; Can't DSE a redundant volatile store |
| define void @redundant_volatile_store(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @redundant_volatile_store( |
| ; CHECK-NEXT: store volatile i32 0, ptr [[P1:%.*]], align 4 |
| ; CHECK-NEXT: store volatile i32 0, ptr [[P1]], align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| store volatile i32 0, ptr %P1, align 4 |
| store volatile i32 0, ptr %P1, align 4 |
| ret void |
| } |
| |
| ; Can value forward from volatiles |
| define i32 @test20(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @test20( |
| ; CHECK-NEXT: [[A:%.*]] = load volatile i32, ptr [[P1:%.*]], align 4 |
| ; CHECK-NEXT: ret i32 0 |
| ; |
| %a = load volatile i32, ptr %P1, align 4 |
| %b = load i32, ptr %P1, align 4 |
| %res = sub i32 %a, %b |
| ret i32 %res |
| } |
| |
| ; Can DSE a non-volatile store in favor of a volatile one |
| ; currently a missed optimization |
| define void @test21(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @test21( |
| ; CHECK-NEXT: store i32 0, ptr [[P1:%.*]], align 4 |
| ; CHECK-NEXT: store volatile i32 3, ptr [[P1]], align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| store i32 0, ptr %P1, align 4 |
| store volatile i32 3, ptr %P1, align 4 |
| ret void |
| } |
| |
| ; Can DSE a normal store in favor of a unordered one |
| define void @test22(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @test22( |
| ; CHECK-NEXT: store atomic i32 3, ptr [[P1:%.*]] unordered, align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| store i32 0, ptr %P1, align 4 |
| store atomic i32 3, ptr %P1 unordered, align 4 |
| ret void |
| } |
| |
| ; Can also DSE a unordered store in favor of a normal one |
| define void @test23(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @test23( |
| ; CHECK-NEXT: store i32 0, ptr [[P1:%.*]], align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| store atomic i32 3, ptr %P1 unordered, align 4 |
| store i32 0, ptr %P1, align 4 |
| ret void |
| } |
| |
| ; As an implementation limitation, can't remove ordered stores |
| ; Note that we could remove the earlier store if we could |
| ; represent the required ordering. |
| define void @test24(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @test24( |
| ; CHECK-NEXT: store atomic i32 3, ptr [[P1:%.*]] release, align 4 |
| ; CHECK-NEXT: store i32 0, ptr [[P1]], align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| store atomic i32 3, ptr %P1 release, align 4 |
| store i32 0, ptr %P1, align 4 |
| ret void |
| } |
| |
| ; Can't remove volatile stores - each is independently observable and |
| ; the count of such stores is an observable program side effect. |
| define void @test25(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @test25( |
| ; CHECK-NEXT: store volatile i32 3, ptr [[P1:%.*]], align 4 |
| ; CHECK-NEXT: store volatile i32 0, ptr [[P1]], align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| store volatile i32 3, ptr %P1, align 4 |
| store volatile i32 0, ptr %P1, align 4 |
| ret void |
| } |
| |
| ; Can DSE a unordered store in favor of a unordered one |
| define void @test26(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @test26( |
| ; CHECK-NEXT: store atomic i32 3, ptr [[P1:%.*]] unordered, align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| store atomic i32 0, ptr %P1 unordered, align 4 |
| store atomic i32 3, ptr %P1 unordered, align 4 |
| ret void |
| } |
| |
| ; Can DSE a unordered store in favor of a ordered one, |
| ; but current don't due to implementation limits |
| define void @test27(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @test27( |
| ; CHECK-NEXT: store atomic i32 0, ptr [[P1:%.*]] unordered, align 4 |
| ; CHECK-NEXT: store atomic i32 3, ptr [[P1]] release, align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| store atomic i32 0, ptr %P1 unordered, align 4 |
| store atomic i32 3, ptr %P1 release, align 4 |
| ret void |
| } |
| |
| ; Can DSE an unordered atomic store in favor of an |
| ; ordered one, but current don't due to implementation limits |
| define void @test28(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @test28( |
| ; CHECK-NEXT: store atomic i32 0, ptr [[P1:%.*]] unordered, align 4 |
| ; CHECK-NEXT: store atomic i32 3, ptr [[P1]] release, align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| store atomic i32 0, ptr %P1 unordered, align 4 |
| store atomic i32 3, ptr %P1 release, align 4 |
| ret void |
| } |
| |
| ; As an implementation limitation, can't remove ordered stores |
| ; see also: @test24 |
| define void @test29(i1 %B, ptr %P1, ptr %P2) { |
| ; CHECK-LABEL: @test29( |
| ; CHECK-NEXT: store atomic i32 3, ptr [[P1:%.*]] release, align 4 |
| ; CHECK-NEXT: store atomic i32 0, ptr [[P1]] unordered, align 4 |
| ; CHECK-NEXT: ret void |
| ; |
| store atomic i32 3, ptr %P1 release, align 4 |
| store atomic i32 0, ptr %P1 unordered, align 4 |
| ret void |
| } |