llvm/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-nested-loops.ll

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3
; RUN: opt -p constraint-elimination -S %s | FileCheck %s

declare void @use(i1)

define void @start_value_of_inner_add_rec_is_add_rec_condition_can_be_simplified(i32 noundef %len) {
; CHECK-LABEL: define void @start_value_of_inner_add_rec_is_add_rec_condition_can_be_simplified(
; CHECK-SAME: i32 noundef [[LEN:%.*]]) {
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[OUTER_HEADER:%.*]]
; CHECK:       outer.header:
; CHECK-NEXT:    [[I_0:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ [[I_INC:%.*]], [[OUTER_LATCH:%.*]] ]
; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[I_0]], [[LEN]]
; CHECK-NEXT:    br i1 [[CMP]], label [[INNER_HEADER:%.*]], label [[EXIT:%.*]]
; CHECK:       inner.header:
; CHECK-NEXT:    [[K_0:%.*]] = phi i32 [ [[I_0]], [[OUTER_HEADER]] ], [ [[K_INC:%.*]], [[INNER_LATCH:%.*]] ]
; CHECK-NEXT:    [[CMP2_NOT:%.*]] = icmp eq i32 [[K_0]], [[LEN]]
; CHECK-NEXT:    br i1 [[CMP2_NOT]], label [[OUTER_LATCH]], label [[INNER_LATCH]]
; CHECK:       inner.latch:
; CHECK-NEXT:    call void @use(i1 true)
; CHECK-NEXT:    [[K_INC]] = add i32 [[K_0]], 1
; CHECK-NEXT:    br label [[INNER_HEADER]]
; CHECK:       outer.latch:
; CHECK-NEXT:    [[I_INC]] = add i32 [[I_0]], 1
; CHECK-NEXT:    br label [[OUTER_HEADER]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %outer.header

outer.header:
  %i.0 = phi i32 [ 1, %entry ], [ %i.inc, %outer.latch ]
  %cmp = icmp ult i32 %i.0, %len
  br i1 %cmp, label %inner.header, label %exit

inner.header:
  %k.0 = phi i32 [ %i.0, %outer.header ], [ %k.inc, %inner.latch ]
  %cmp2.not = icmp eq i32 %k.0, %len
  br i1 %cmp2.not, label %outer.latch, label %inner.latch

inner.latch:
  %cmp.not.i = icmp ult i32 %k.0, %len
  call void @use(i1 %cmp.not.i)
  %k.inc = add i32 %k.0, 1
  br label %inner.header

outer.latch:
  %i.inc = add i32 %i.0, 1
  br label %outer.header

exit:
  ret void
}

define void @start_value_of_inner_add_rec_is_add_rec_condition_cannot_be_simplified(i32 noundef %len) {
; CHECK-LABEL: define void @start_value_of_inner_add_rec_is_add_rec_condition_cannot_be_simplified(
; CHECK-SAME: i32 noundef [[LEN:%.*]]) {
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[OUTER_HEADER:%.*]]
; CHECK:       outer.header:
; CHECK-NEXT:    [[I_0:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ [[I_INC:%.*]], [[OUTER_LATCH:%.*]] ]
; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[I_0]], [[LEN]]
; CHECK-NEXT:    br i1 [[CMP]], label [[INNER_HEADER:%.*]], label [[EXIT:%.*]]
; CHECK:       inner.header:
; CHECK-NEXT:    [[K_0:%.*]] = phi i32 [ [[I_0]], [[OUTER_HEADER]] ], [ [[K_INC:%.*]], [[INNER_LATCH:%.*]] ]
; CHECK-NEXT:    [[CMP2_NOT:%.*]] = icmp eq i32 [[K_0]], [[LEN]]
; CHECK-NEXT:    br i1 [[CMP2_NOT]], label [[OUTER_LATCH]], label [[INNER_LATCH]]
; CHECK:       inner.latch:
; CHECK-NEXT:    [[CMP_NOT_I:%.*]] = icmp ult i32 [[K_0]], 3
; CHECK-NEXT:    call void @use(i1 [[CMP_NOT_I]])
; CHECK-NEXT:    [[K_INC]] = add i32 [[K_0]], 1
; CHECK-NEXT:    br label [[INNER_HEADER]]
; CHECK:       outer.latch:
; CHECK-NEXT:    [[I_INC]] = add i32 [[I_0]], 1
; CHECK-NEXT:    br label [[OUTER_HEADER]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %outer.header

outer.header:
  %i.0 = phi i32 [ 1, %entry ], [ %i.inc, %outer.latch ]
  %cmp = icmp ult i32 %i.0, %len
  br i1 %cmp, label %inner.header, label %exit

inner.header:
  %k.0 = phi i32 [ %i.0, %outer.header ], [ %k.inc, %inner.latch ]
  %cmp2.not = icmp eq i32 %k.0, %len
  br i1 %cmp2.not, label %outer.latch, label %inner.latch

inner.latch:
  %cmp.not.i = icmp ult i32 %k.0, 3
  call void @use(i1 %cmp.not.i)
  %k.inc = add i32 %k.0, 1
  br label %inner.header

outer.latch:
  %i.inc = add i32 %i.0, 1
  br label %outer.header

exit:
  ret void
}
define void @inner_add_rec_decreasing(i32 noundef %len) {
; CHECK-LABEL: define void @inner_add_rec_decreasing(
; CHECK-SAME: i32 noundef [[LEN:%.*]]) {
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[OUTER_HEADER:%.*]]
; CHECK:       outer.header:
; CHECK-NEXT:    [[I_0:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ [[I_INC:%.*]], [[OUTER_LATCH:%.*]] ]
; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[I_0]], [[LEN]]
; CHECK-NEXT:    br i1 [[CMP]], label [[INNER_HEADER:%.*]], label [[EXIT:%.*]]
; CHECK:       inner.header:
; CHECK-NEXT:    [[K_0:%.*]] = phi i32 [ [[I_0]], [[OUTER_HEADER]] ], [ [[K_DEC:%.*]], [[INNER_LATCH:%.*]] ]
; CHECK-NEXT:    [[CMP2_NOT:%.*]] = icmp eq i32 [[K_0]], 0
; CHECK-NEXT:    br i1 [[CMP2_NOT]], label [[OUTER_LATCH]], label [[INNER_LATCH]]
; CHECK:       inner.latch:
; CHECK-NEXT:    call void @use(i1 true)
; CHECK-NEXT:    [[K_DEC]] = add i32 [[K_0]], -1
; CHECK-NEXT:    br label [[INNER_HEADER]]
; CHECK:       outer.latch:
; CHECK-NEXT:    [[I_INC]] = add i32 [[I_0]], 1
; CHECK-NEXT:    br label [[OUTER_HEADER]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %outer.header

outer.header:
  %i.0 = phi i32 [ 1, %entry ], [ %i.inc, %outer.latch ]
  %cmp = icmp ult i32 %i.0, %len
  br i1 %cmp, label %inner.header, label %exit

inner.header:
  %k.0 = phi i32 [ %i.0, %outer.header ], [ %k.dec, %inner.latch ]
  %cmp2.not = icmp eq i32 %k.0, 0
  br i1 %cmp2.not, label %outer.latch, label %inner.latch

inner.latch:
  %cmp.not.i = icmp ult i32 %k.0, %len
  call void @use(i1 %cmp.not.i)
  %k.dec = add i32 %k.0, -1
  br label %inner.header

outer.latch:
  %i.inc = add i32 %i.0, 1
  br label %outer.header

exit:
  ret void
}