Update the EarlyCSE tests to use opaque pointers. Worth noting that this leaves some bitcast ptr to ptr instructions in the input IR behind which are no longer necessary. This is because these use numbered instructions, so it's hard to drop them in an automated fashion (as it would require renumbering all other instructions as well). I'm leaving that as a problem for another day. The test updates have been performed using https://gist.github.com/nikic/98357b71fd67756b0f064c9517b62a34. Differential Revision: https://reviews.llvm.org/D127278
307 lines
9.6 KiB
LLVM
307 lines
9.6 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt < %s -S -early-cse -earlycse-debug-hash | FileCheck %s
|
|
; RUN: opt < %s -S -basic-aa -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
|
|
}
|