llvm/llvm/test/Transforms/GVN/PRE/volatile.ll

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; Tests that check our handling of volatile instructions encountered
; when scanning for dependencies
; RUN: opt -passes=gvn -enable-split-backedge-in-load-pre -S < %s | FileCheck %s

; Check that we can bypass a volatile load when searching
; for dependencies of a non-volatile load
define i32 @test1(ptr nocapture %p, ptr nocapture %q) {
; CHECK-LABEL: @test1(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[TMP0:%.*]] = load volatile i32, ptr [[Q:%.*]], align 4
; CHECK-NEXT:    ret i32 0
;
entry:
  %x = load i32, ptr %p
  load volatile i32, ptr %q
  %y = load i32, ptr %p
  %add = sub i32 %y, %x
  ret i32 %add
}

; We can not value forward if the query instruction is
; volatile, this would be (in effect) removing the volatile load
define i32 @test2(ptr nocapture %p, ptr nocapture %q) {
; CHECK-LABEL: @test2(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[X:%.*]] = load i32, ptr [[P:%.*]], align 4
; CHECK-NEXT:    [[Y:%.*]] = load volatile i32, ptr [[P]], align 4
; CHECK-NEXT:    [[ADD:%.*]] = sub i32 [[Y]], [[X]]
; CHECK-NEXT:    ret i32 [[ADD]]
;
entry:
  %x = load i32, ptr %p
  %y = load volatile i32, ptr %p
  %add = sub i32 %y, %x
  ret i32 %add
}

; If the query instruction is itself volatile, we *cannot*
; reorder it even if p and q are noalias
define i32 @test3(ptr noalias nocapture %p, ptr noalias nocapture %q) {
; CHECK-LABEL: @test3(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[X:%.*]] = load i32, ptr [[P:%.*]], align 4
; CHECK-NEXT:    [[TMP0:%.*]] = load volatile i32, ptr [[Q:%.*]], align 4
; CHECK-NEXT:    [[Y:%.*]] = load volatile i32, ptr [[P]], align 4
; CHECK-NEXT:    [[ADD:%.*]] = sub i32 [[Y]], [[X]]
; CHECK-NEXT:    ret i32 [[ADD]]
;
entry:
  %x = load i32, ptr %p
  load volatile i32, ptr %q
  %y = load volatile i32, ptr %p
  %add = sub i32 %y, %x
  ret i32 %add
}

; If an encountered instruction is both volatile and ordered,
; we need to use the strictest ordering of either.  In this
; case, the ordering prevents forwarding.
define i32 @test4(ptr noalias nocapture %p, ptr noalias nocapture %q) {
; CHECK-LABEL: @test4(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[X:%.*]] = load i32, ptr [[P:%.*]], align 4
; CHECK-NEXT:    [[TMP0:%.*]] = load atomic volatile i32, ptr [[Q:%.*]] seq_cst, align 4
; CHECK-NEXT:    [[Y:%.*]] = load atomic i32, ptr [[P]] seq_cst, align 4
; CHECK-NEXT:    [[ADD:%.*]] = sub i32 [[Y]], [[X]]
; CHECK-NEXT:    ret i32 [[ADD]]
;
entry:
  %x = load i32, ptr %p
  load atomic volatile i32, ptr %q seq_cst, align 4
  %y = load atomic i32, ptr %p seq_cst, align 4
  %add = sub i32 %y, %x
  ret i32 %add
}

; Value forwarding from a volatile load is perfectly legal
define i32 @test5(ptr nocapture %p, ptr nocapture %q) {
; CHECK-LABEL: @test5(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[X:%.*]] = load volatile i32, ptr [[P:%.*]], align 4
; CHECK-NEXT:    ret i32 0
;
entry:
  %x = load volatile i32, ptr %p
  %y = load i32, ptr %p
  %add = sub i32 %y, %x
  ret i32 %add
}

; Does cross block redundancy elimination work with volatiles?
define i32 @test6(ptr noalias nocapture %p, ptr noalias nocapture %q) {
; CHECK-LABEL: @test6(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[Y1:%.*]] = load i32, ptr [[P:%.*]], align 4
; CHECK-NEXT:    call void @use(i32 [[Y1]])
; CHECK-NEXT:    br label [[HEADER:%.*]]
; CHECK:       header:
; CHECK-NEXT:    [[X:%.*]] = load volatile i32, ptr [[Q:%.*]], align 4
; CHECK-NEXT:    [[ADD:%.*]] = sub i32 [[Y1]], [[X]]
; CHECK-NEXT:    [[CND:%.*]] = icmp eq i32 [[ADD]], 0
; CHECK-NEXT:    br i1 [[CND]], label [[EXIT:%.*]], label [[HEADER]]
; CHECK:       exit:
; CHECK-NEXT:    ret i32 0
;
entry:
  %y1 = load i32, ptr %p
  call void @use(i32 %y1)
  br label %header
header:
  %x = load volatile i32, ptr %q
  %y = load i32, ptr %p
  %add = sub i32 %y, %x
  %cnd = icmp eq i32 %add, 0
  br i1 %cnd, label %exit, label %header
exit:
  ret i32 %add
}

; Does cross block PRE work with volatiles?
define i32 @test7(i1 %c, ptr noalias nocapture %p, ptr noalias nocapture %q) {
; CHECK-LABEL: @test7(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[Y_PRE:%.*]] = load i32, ptr [[P:%.*]], align 4
; CHECK-NEXT:    br i1 [[C:%.*]], label [[HEADER:%.*]], label [[SKIP:%.*]]
; CHECK:       skip:
; CHECK-NEXT:    call void @use(i32 [[Y_PRE]])
; CHECK-NEXT:    br label [[HEADER]]
; CHECK:       header:
; CHECK-NEXT:    [[X:%.*]] = load volatile i32, ptr [[Q:%.*]], align 4
; CHECK-NEXT:    [[ADD:%.*]] = sub i32 [[Y_PRE]], [[X]]
; CHECK-NEXT:    [[CND:%.*]] = icmp eq i32 [[ADD]], 0
; CHECK-NEXT:    br i1 [[CND]], label [[EXIT:%.*]], label [[HEADER]]
; CHECK:       exit:
; CHECK-NEXT:    ret i32 0
;
entry:
  br i1 %c, label %header, label %skip
skip:
  %y1 = load i32, ptr %p
  call void @use(i32 %y1)
  br label %header
header:
  %x = load volatile i32, ptr %q
  %y = load i32, ptr %p
  %add = sub i32 %y, %x
  %cnd = icmp eq i32 %add, 0
  br i1 %cnd, label %exit, label %header
exit:
  ret i32 %add
}

; Another volatile PRE case - two paths through a loop
; load in preheader, one path read only, one not
define i32 @test8(i1 %b, i1 %c, ptr noalias %p, ptr noalias %q) {
; CHECK-LABEL: @test8(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[Y1:%.*]] = load i32, ptr [[P:%.*]], align 4
; CHECK-NEXT:    call void @use(i32 [[Y1]])
; CHECK-NEXT:    br label [[HEADER:%.*]]
; CHECK:       header:
; CHECK-NEXT:    [[Y:%.*]] = phi i32 [ [[Y_PRE:%.*]], [[SKIP_HEADER_CRIT_EDGE:%.*]] ], [ [[Y]], [[HEADER]] ], [ [[Y1]], [[ENTRY:%.*]] ]
; CHECK-NEXT:    [[X:%.*]] = load volatile i32, ptr [[Q:%.*]], align 4
; CHECK-NEXT:    call void @use(i32 [[Y]])
; CHECK-NEXT:    br i1 [[B:%.*]], label [[SKIP:%.*]], label [[HEADER]]
; CHECK:       skip:
; CHECK-NEXT:    call void @clobber(ptr [[P]], ptr [[Q]])
; CHECK-NEXT:    br i1 [[C:%.*]], label [[SKIP_HEADER_CRIT_EDGE]], label [[EXIT:%.*]]
; CHECK:       skip.header_crit_edge:
; CHECK-NEXT:    [[Y_PRE]] = load i32, ptr [[P]], align 4
; CHECK-NEXT:    br label [[HEADER]]
; CHECK:       exit:
; CHECK-NEXT:    [[ADD:%.*]] = sub i32 [[Y]], [[X]]
; CHECK-NEXT:    ret i32 [[ADD]]
;
entry:
  %y1 = load i32, ptr %p
  call void @use(i32 %y1)
  br label %header
header:
  %x = load volatile i32, ptr %q
  %y = load i32, ptr %p
  call void @use(i32 %y)
  br i1 %b, label %skip, label %header
skip:
  ; escaping the arguments is explicitly required since we marked
  ; them noalias
  call void @clobber(ptr %p, ptr %q)
  br i1 %c, label %header, label %exit
exit:
  %add = sub i32 %y, %x
  ret i32 %add
}

; This test checks that we don't optimize away instructions that are
; simplified by SimplifyInstruction(), but are not trivially dead.

define i32 @test9(ptr %V) {
; CHECK-LABEL: @test9(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[LOAD:%.*]] = call i32 undef()
; CHECK-NEXT:    ret i32 poison
;
entry:
  %load = call i32 undef()
  ret i32 %load
}

declare void @use(i32) readonly
declare void @clobber(ptr %p, ptr %q)

!0 = !{ i32 0, i32 1 }