; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt < %s -passes=gvn -enable-load-pre -S | FileCheck %s --check-prefixes=CHECK,MDEP
; RUN: opt < %s -passes='gvn<memoryssa>' -enable-load-pre -S | FileCheck %s --check-prefixes=CHECK,MSSA
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 @llvm.experimental.guard(i1, ...)

; This is a motivating example on why we prohibit hoisting through guards.
; In the bottom block, we check that the index is within bounds and only access
; the element in this case and deoptimize otherwise. If we hoist the load to a
; place above the guard, it will may lead to out-of-bound array access.
define i32 @test_motivation(ptr %p, ptr %q, i1 %C, i32 %index, i32 %len) {
; CHECK-LABEL: define i32 @test_motivation(
; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]], i1 [[C:%.*]], i32 [[INDEX:%.*]], i32 [[LEN:%.*]]) {
; CHECK-NEXT:  [[BLOCK1:.*:]]
; CHECK-NEXT:    [[EL1:%.*]] = getelementptr inbounds i32, ptr [[Q]], i32 [[INDEX]]
; CHECK-NEXT:    [[EL2:%.*]] = getelementptr inbounds i32, ptr [[P]], i32 [[INDEX]]
; CHECK-NEXT:    br i1 [[C]], label %[[BLOCK2:.*]], label %[[BLOCK3:.*]]
; CHECK:       [[BLOCK2]]:
; CHECK-NEXT:    br label %[[BLOCK4:.*]]
; CHECK:       [[BLOCK3]]:
; CHECK-NEXT:    store i32 0, ptr [[EL1]], align 4
; CHECK-NEXT:    br label %[[BLOCK4]]
; CHECK:       [[BLOCK4]]:
; CHECK-NEXT:    [[P2:%.*]] = phi ptr [ [[EL2]], %[[BLOCK3]] ], [ [[EL1]], %[[BLOCK2]] ]
; CHECK-NEXT:    [[COND1:%.*]] = icmp sge i32 [[INDEX]], 0
; CHECK-NEXT:    [[COND2:%.*]] = icmp slt i32 [[INDEX]], [[LEN]]
; CHECK-NEXT:    [[IN_BOUNDS:%.*]] = and i1 [[COND1]], [[COND2]]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[IN_BOUNDS]]) [ "deopt"() ]
; CHECK-NEXT:    [[PRE:%.*]] = load i32, ptr [[P2]], align 4
; CHECK-NEXT:    ret i32 [[PRE]]
;
block1:
  %el1 = getelementptr inbounds i32, ptr %q, i32 %index
  %el2 = getelementptr inbounds i32, ptr %p, i32 %index
  br i1 %C, label %block2, label %block3

block2:

  br label %block4

block3:
  store i32 0, ptr %el1
  br label %block4

block4:


  %P2 = phi ptr [%el2, %block3], [%el1, %block2]
  %cond1 = icmp sge i32 %index, 0
  %cond2 = icmp slt i32 %index, %len
  %in.bounds = and i1 %cond1, %cond2
  call void (i1, ...) @llvm.experimental.guard(i1 %in.bounds) [ "deopt"() ]
  %PRE = load i32, ptr %P2
  ret i32 %PRE
}

; Guard in load's block that is above the load should prohibit the PRE.
define i32 @test_guard_01(ptr %p, ptr %q, i1 %C, i1 %G) {
; CHECK-LABEL: define i32 @test_guard_01(
; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]], i1 [[C:%.*]], i1 [[G:%.*]]) {
; CHECK-NEXT:  [[BLOCK1:.*:]]
; CHECK-NEXT:    br i1 [[C]], label %[[BLOCK2:.*]], label %[[BLOCK3:.*]]
; CHECK:       [[BLOCK2]]:
; CHECK-NEXT:    br label %[[BLOCK4:.*]]
; CHECK:       [[BLOCK3]]:
; CHECK-NEXT:    store i32 0, ptr [[P]], align 4
; CHECK-NEXT:    br label %[[BLOCK4]]
; CHECK:       [[BLOCK4]]:
; CHECK-NEXT:    [[P2:%.*]] = phi ptr [ [[P]], %[[BLOCK3]] ], [ [[Q]], %[[BLOCK2]] ]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[G]]) [ "deopt"() ]
; CHECK-NEXT:    [[PRE:%.*]] = load i32, ptr [[P2]], align 4
; CHECK-NEXT:    ret i32 [[PRE]]
;
block1:
  br i1 %C, label %block2, label %block3

block2:


  br label %block4

block3:
  store i32 0, ptr %p
  br label %block4

block4:


  %P2 = phi ptr [%p, %block3], [%q, %block2]
  call void (i1, ...) @llvm.experimental.guard(i1 %G) [ "deopt"() ]
  %PRE = load i32, ptr %P2
  ret i32 %PRE
}

; Guard in load's block that is below the load should not prohibit the PRE.
define i32 @test_guard_02(ptr %p, ptr %q, i1 %C, i1 %G) {
; MDEP-LABEL: define i32 @test_guard_02(
; MDEP-SAME: ptr [[P:%.*]], ptr [[Q:%.*]], i1 [[C:%.*]], i1 [[G:%.*]]) {
; MDEP-NEXT:  [[BLOCK1:.*:]]
; MDEP-NEXT:    br i1 [[C]], label %[[BLOCK2:.*]], label %[[BLOCK3:.*]]
; MDEP:       [[BLOCK2]]:
; MDEP-NEXT:    [[PRE_PRE:%.*]] = load i32, ptr [[Q]], align 4
; MDEP-NEXT:    br label %[[BLOCK4:.*]]
; MDEP:       [[BLOCK3]]:
; MDEP-NEXT:    store i32 0, ptr [[P]], align 4
; MDEP-NEXT:    br label %[[BLOCK4]]
; MDEP:       [[BLOCK4]]:
; MDEP-NEXT:    [[PRE:%.*]] = phi i32 [ 0, %[[BLOCK3]] ], [ [[PRE_PRE]], %[[BLOCK2]] ]
; MDEP-NEXT:    [[P2:%.*]] = phi ptr [ [[P]], %[[BLOCK3]] ], [ [[Q]], %[[BLOCK2]] ]
; MDEP-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[G]]) [ "deopt"() ]
; MDEP-NEXT:    ret i32 [[PRE]]
;
; MSSA-LABEL: define i32 @test_guard_02(
; MSSA-SAME: ptr [[P:%.*]], ptr [[Q:%.*]], i1 [[C:%.*]], i1 [[G:%.*]]) {
; MSSA-NEXT:  [[BLOCK1:.*:]]
; MSSA-NEXT:    br i1 [[C]], label %[[BLOCK2:.*]], label %[[BLOCK3:.*]]
; MSSA:       [[BLOCK2]]:
; MSSA-NEXT:    br label %[[BLOCK4:.*]]
; MSSA:       [[BLOCK3]]:
; MSSA-NEXT:    store i32 0, ptr [[P]], align 4
; MSSA-NEXT:    br label %[[BLOCK4]]
; MSSA:       [[BLOCK4]]:
; MSSA-NEXT:    [[P2:%.*]] = phi ptr [ [[P]], %[[BLOCK3]] ], [ [[Q]], %[[BLOCK2]] ]
; MSSA-NEXT:    [[PRE:%.*]] = load i32, ptr [[P2]], align 4
; MSSA-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[G]]) [ "deopt"() ]
; MSSA-NEXT:    ret i32 [[PRE]]
;
block1:
  br i1 %C, label %block2, label %block3

block2:


  br label %block4

block3:
  store i32 0, ptr %p
  br label %block4

block4:


  %P2 = phi ptr [%p, %block3], [%q, %block2]
  %PRE = load i32, ptr %P2
  call void (i1, ...) @llvm.experimental.guard(i1 %G) [ "deopt"() ]
  ret i32 %PRE
}

; Guard above the load's block should prevent PRE from hoisting through it.
define i32 @test_guard_03(ptr %p, ptr %q, i1 %C, i1 %G) {
; CHECK-LABEL: define i32 @test_guard_03(
; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]], i1 [[C:%.*]], i1 [[G:%.*]]) {
; CHECK-NEXT:  [[BLOCK1:.*:]]
; CHECK-NEXT:    br i1 [[C]], label %[[BLOCK2:.*]], label %[[BLOCK3:.*]]
; CHECK:       [[BLOCK2]]:
; CHECK-NEXT:    br label %[[BLOCK4:.*]]
; CHECK:       [[BLOCK3]]:
; CHECK-NEXT:    store i32 0, ptr [[P]], align 4
; CHECK-NEXT:    br label %[[BLOCK4]]
; CHECK:       [[BLOCK4]]:
; CHECK-NEXT:    [[P2:%.*]] = phi ptr [ [[P]], %[[BLOCK3]] ], [ [[Q]], %[[BLOCK2]] ]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[G]]) [ "deopt"() ]
; CHECK-NEXT:    [[PRE:%.*]] = load i32, ptr [[P2]], align 4
; CHECK-NEXT:    ret i32 [[PRE]]
;
block1:
  br i1 %C, label %block2, label %block3

block2:


  br label %block4

block3:
  store i32 0, ptr %p
  br label %block4

block4:


  %P2 = phi ptr [%p, %block3], [%q, %block2]
  call void (i1, ...) @llvm.experimental.guard(i1 %G) [ "deopt"() ]
  br label %block5

block5:
  %PRE = load i32, ptr %P2
  ret i32 %PRE
}
