; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -instcombine -S | FileCheck %s

define i32 @fold(i32 %x) {
; CHECK-LABEL: @fold(
; CHECK-NEXT:    [[Y:%.*]] = freeze i32 [[X:%.*]]
; CHECK-NEXT:    ret i32 [[Y]]
;
  %y = freeze i32 %x
  %z = freeze i32 %y
  ret i32 %z
}

define i32 @make_const() {
; CHECK-LABEL: @make_const(
; CHECK-NEXT:    ret i32 10
;
  %x = freeze i32 10
  ret i32 %x
}

define i32 @and_freeze_undef(i32 %x) {
; CHECK-LABEL: @and_freeze_undef(
; CHECK-NEXT:    ret i32 0
;
  %f = freeze i32 undef
  %res = and i32 %x, %f
  ret i32 %res
}

declare void @use_i32(i32)

define i32 @and_freeze_undef_multipleuses(i32 %x) {
; CHECK-LABEL: @and_freeze_undef_multipleuses(
; CHECK-NEXT:    call void @use_i32(i32 0)
; CHECK-NEXT:    ret i32 0
;
  %f = freeze i32 undef
  %res = and i32 %x, %f
  call void @use_i32(i32 %f)
  ret i32 %res
}

define i32 @or_freeze_undef(i32 %x) {
; CHECK-LABEL: @or_freeze_undef(
; CHECK-NEXT:    ret i32 -1
;
  %f = freeze i32 undef
  %res = or i32 %x, %f
  ret i32 %res
}

define i32 @or_freeze_undef_multipleuses(i32 %x) {
; CHECK-LABEL: @or_freeze_undef_multipleuses(
; CHECK-NEXT:    call void @use_i32(i32 0)
; CHECK-NEXT:    ret i32 [[X:%.*]]
;
  %f = freeze i32 undef
  %res = or i32 %x, %f
  call void @use_i32(i32 %f)
  ret i32 %res
}

declare void @use_i32_i1(i32, i1)

define void @or_select_multipleuses(i32 %x, i1 %y) {
; CHECK-LABEL: @or_select_multipleuses(
; CHECK-NEXT:    call void @use_i32_i1(i32 32, i1 [[Y:%.*]])
; CHECK-NEXT:    ret void
;
  %f = freeze i1 undef
  %a = select i1 %f, i32 %x, i32 32 ; prefers %f to be false
  %b = or i1 %f, %y ; prefers %f to be true
  call void @use_i32_i1(i32 %a, i1 %b)
  ret void
}

define void @or_select_multipleuses_logical(i32 %x, i1 %y) {
; CHECK-LABEL: @or_select_multipleuses_logical(
; CHECK-NEXT:    call void @use_i32_i1(i32 32, i1 [[Y:%.*]])
; CHECK-NEXT:    ret void
;
  %f = freeze i1 undef
  %a = select i1 %f, i32 %x, i32 32 ; prefers %f to be false
  %b = select i1 %f, i1 true, i1 %y ; prefers %f to be true
  call void @use_i32_i1(i32 %a, i1 %b)
  ret void
}

; Move the freeze forward to prevent poison from spreading.

define i32 @early_freeze_test1(i32 %x, i32 %y) {
; CHECK-LABEL: @early_freeze_test1(
; CHECK-NEXT:    [[V1:%.*]] = add i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT:    [[V1_FR:%.*]] = freeze i32 [[V1]]
; CHECK-NEXT:    [[V2:%.*]] = shl i32 [[V1_FR]], 1
; CHECK-NEXT:    [[V3:%.*]] = and i32 [[V2]], 2
; CHECK-NEXT:    ret i32 [[V3]]
;
  %v1 = add i32 %x, %y
  %v2 = shl i32 %v1, 1
  %v3 = and i32 %v2, 2
  %v3.fr = freeze i32 %v3
  ret i32 %v3.fr
}

define i1 @early_freeze_test2(i32* %ptr) {
; CHECK-LABEL: @early_freeze_test2(
; CHECK-NEXT:    [[V1:%.*]] = load i32, i32* [[PTR:%.*]], align 4
; CHECK-NEXT:    [[V1_FR:%.*]] = freeze i32 [[V1]]
; CHECK-NEXT:    [[V2:%.*]] = and i32 [[V1_FR]], 1
; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[V2]], 0
; CHECK-NEXT:    ret i1 [[COND]]
;
  %v1 = load i32, i32* %ptr
  %v2 = and i32 %v1, 1
  %cond = icmp eq i32 %v2, 0
  %cond.fr = freeze i1 %cond
  ret i1 %cond.fr
}

define i32 @early_freeze_test3(i32 %v1) {
; CHECK-LABEL: @early_freeze_test3(
; CHECK-NEXT:    [[V1_FR:%.*]] = freeze i32 [[V1:%.*]]
; CHECK-NEXT:    [[V2:%.*]] = shl i32 [[V1_FR]], 1
; CHECK-NEXT:    [[V3:%.*]] = add i32 [[V2]], 2
; CHECK-NEXT:    [[V4:%.*]] = or i32 [[V3]], 1
; CHECK-NEXT:    ret i32 [[V4]]
;
  %v2 = shl i32 %v1, 1
  %v3 = add nuw i32 %v2, 2
  %v4 = or i32 %v3, 1
  %v4.fr = freeze i32 %v4
  ret i32 %v4.fr
}

; If replace all dominated uses of v to freeze(v).

define void @freeze_dominated_uses_test1(i32 %v) {
; CHECK-LABEL: @freeze_dominated_uses_test1(
; CHECK-NEXT:    [[V_FR:%.*]] = freeze i32 [[V:%.*]]
; CHECK-NEXT:    call void @use_i32(i32 [[V_FR]])
; CHECK-NEXT:    call void @use_i32(i32 [[V_FR]])
; CHECK-NEXT:    ret void
;
  %v.fr = freeze i32 %v
  call void @use_i32(i32 %v)
  call void @use_i32(i32 %v.fr)
  ret void
}

define void @freeze_dominated_uses_test2(i32 %v) {
; CHECK-LABEL: @freeze_dominated_uses_test2(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    call void @use_i32(i32 [[V:%.*]])
; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[V]], 0
; CHECK-NEXT:    br i1 [[COND]], label [[BB0:%.*]], label [[BB1:%.*]]
; CHECK:       bb0:
; CHECK-NEXT:    [[V_FR:%.*]] = freeze i32 [[V]]
; CHECK-NEXT:    call void @use_i32(i32 [[V_FR]])
; CHECK-NEXT:    call void @use_i32(i32 [[V_FR]])
; CHECK-NEXT:    br label [[END:%.*]]
; CHECK:       bb1:
; CHECK-NEXT:    call void @use_i32(i32 [[V]])
; CHECK-NEXT:    br label [[END]]
; CHECK:       end:
; CHECK-NEXT:    ret void
;
entry:
  call void @use_i32(i32 %v)
  %cond = icmp eq i32 %v, 0
  br i1 %cond, label %bb0, label %bb1

bb0:
  %v.fr = freeze i32 %v
  call void @use_i32(i32 %v.fr)
  call void @use_i32(i32 %v)
  br label %end

bb1:
  call void @use_i32(i32 %v)
  br label %end

end:
  ret void
}

; If there is a duplicate freeze, it will be removed.

define void @freeze_dominated_uses_test3(i32 %v, i1 %cond) {
; CHECK-LABEL: @freeze_dominated_uses_test3(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[V_FR1:%.*]] = freeze i32 [[V:%.*]]
; CHECK-NEXT:    call void @use_i32(i32 [[V_FR1]])
; CHECK-NEXT:    br i1 [[COND:%.*]], label [[BB0:%.*]], label [[BB1:%.*]]
; CHECK:       bb0:
; CHECK-NEXT:    call void @use_i32(i32 [[V_FR1]])
; CHECK-NEXT:    br label [[END:%.*]]
; CHECK:       bb1:
; CHECK-NEXT:    call void @use_i32(i32 [[V_FR1]])
; CHECK-NEXT:    br label [[END]]
; CHECK:       end:
; CHECK-NEXT:    ret void
;
entry:
  %v.fr1 = freeze i32 %v
  call void @use_i32(i32 %v.fr1)
  br i1 %cond, label %bb0, label %bb1

bb0:
  %v.fr2 = freeze i32 %v
  call void @use_i32(i32 %v.fr2)
  br label %end

bb1:
  call void @use_i32(i32 %v)
  br label %end

end:
  ret void
}


define i32 @propagate_drop_flags_add(i32 %arg) {
; CHECK-LABEL: @propagate_drop_flags_add(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze i32 [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = add i32 [[ARG_FR]], 2
; CHECK-NEXT:    ret i32 [[V1]]
;
  %v1 = add nsw nuw i32 %arg, 2
  %v1.fr = freeze i32 %v1
  ret i32 %v1.fr
}

define i32 @propagate_drop_flags_add_foldaway(i32 noundef %arg) {
; CHECK-LABEL: @propagate_drop_flags_add_foldaway(
; CHECK-NEXT:    [[V1:%.*]] = add i32 [[ARG:%.*]], 2
; CHECK-NEXT:    ret i32 [[V1]]
;
  %v1 = add nsw nuw i32 %arg, 2
  %v1.fr = freeze i32 %v1
  ret i32 %v1.fr
}

define i32 @propagate_drop_flags_sub(i32 %arg) {
; CHECK-LABEL: @propagate_drop_flags_sub(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze i32 [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = add i32 [[ARG_FR]], -2
; CHECK-NEXT:    ret i32 [[V1]]
;
  %v1 = sub nsw nuw i32 %arg, 2
  %v1.fr = freeze i32 %v1
  ret i32 %v1.fr
}

define i32 @propagate_drop_flags_mul(i32 %arg) {
; CHECK-LABEL: @propagate_drop_flags_mul(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze i32 [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = shl i32 [[ARG_FR]], 1
; CHECK-NEXT:    ret i32 [[V1]]
;
  %v1 = mul nsw nuw i32 %arg, 2
  %v1.fr = freeze i32 %v1
  ret i32 %v1.fr
}

define i32 @propagate_drop_flags_udiv(i32 %arg) {
; CHECK-LABEL: @propagate_drop_flags_udiv(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze i32 [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = lshr i32 [[ARG_FR]], 1
; CHECK-NEXT:    ret i32 [[V1]]
;
  %v1 = udiv exact i32 %arg, 2
  %v1.fr = freeze i32 %v1
  ret i32 %v1.fr
}

define i32 @propagate_drop_flags_sdiv(i32 %arg) {
; CHECK-LABEL: @propagate_drop_flags_sdiv(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze i32 [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = ashr i32 [[ARG_FR]], 1
; CHECK-NEXT:    ret i32 [[V1]]
;
  %v1 = sdiv exact i32 %arg, 2
  %v1.fr = freeze i32 %v1
  ret i32 %v1.fr
}

define i32 @propagate_drop_shl1(i32 %arg) {
; CHECK-LABEL: @propagate_drop_shl1(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze i32 [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = shl i32 [[ARG_FR]], 2
; CHECK-NEXT:    ret i32 [[V1]]
;
  %v1 = shl nsw nuw i32 %arg, 2
  %v1.fr = freeze i32 %v1
  ret i32 %v1.fr
}

define i32 @propagate_drop_shl2(i32 %arg, i32 %unknown) {
; CHECK-LABEL: @propagate_drop_shl2(
; CHECK-NEXT:    [[V1:%.*]] = shl nuw nsw i32 [[ARG:%.*]], [[UNKNOWN:%.*]]
; CHECK-NEXT:    [[V1_FR:%.*]] = freeze i32 [[V1]]
; CHECK-NEXT:    ret i32 [[V1_FR]]
;
  %v1 = shl nsw nuw i32 %arg, %unknown
  %v1.fr = freeze i32 %v1
  ret i32 %v1.fr
}

define i32 @propagate_drop_ashr1(i32 %arg) {
; CHECK-LABEL: @propagate_drop_ashr1(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze i32 [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = ashr i32 [[ARG_FR]], 2
; CHECK-NEXT:    ret i32 [[V1]]
;
  %v1 = ashr exact i32 %arg, 2
  %v1.fr = freeze i32 %v1
  ret i32 %v1.fr
}

define i32 @propagate_drop_ashr2(i32 %arg, i32 %unknown) {
; CHECK-LABEL: @propagate_drop_ashr2(
; CHECK-NEXT:    [[V1:%.*]] = ashr exact i32 [[ARG:%.*]], [[UNKNOWN:%.*]]
; CHECK-NEXT:    [[V1_FR:%.*]] = freeze i32 [[V1]]
; CHECK-NEXT:    ret i32 [[V1_FR]]
;
  %v1 = ashr exact i32 %arg, %unknown
  %v1.fr = freeze i32 %v1
  ret i32 %v1.fr
}

define i32 @propagate_drop_lshr1(i32 %arg) {
; CHECK-LABEL: @propagate_drop_lshr1(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze i32 [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = lshr i32 [[ARG_FR]], 2
; CHECK-NEXT:    ret i32 [[V1]]
;
  %v1 = lshr exact i32 %arg, 2
  %v1.fr = freeze i32 %v1
  ret i32 %v1.fr
}

define i32 @propagate_drop_lshr2(i32 %arg, i32 %unknown) {
; CHECK-LABEL: @propagate_drop_lshr2(
; CHECK-NEXT:    [[V1:%.*]] = lshr exact i32 [[ARG:%.*]], [[UNKNOWN:%.*]]
; CHECK-NEXT:    [[V1_FR:%.*]] = freeze i32 [[V1]]
; CHECK-NEXT:    ret i32 [[V1_FR]]
;
  %v1 = lshr exact i32 %arg, %unknown
  %v1.fr = freeze i32 %v1
  ret i32 %v1.fr
}

define i8* @propagate_drop_gep1(i8* %arg) {
; CHECK-LABEL: @propagate_drop_gep1(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze i8* [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = getelementptr i8, i8* [[ARG_FR]], i64 16
; CHECK-NEXT:    ret i8* [[V1]]
;
  %v1 = getelementptr inbounds i8, i8* %arg, i64 16
  %v1.fr = freeze i8* %v1
  ret i8* %v1.fr
}

define float @propagate_drop_fneg(float %arg) {
; CHECK-LABEL: @propagate_drop_fneg(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze float [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = fneg float [[ARG_FR]]
; CHECK-NEXT:    ret float [[V1]]
;
  %v1 = fneg ninf nnan float %arg
  %v1.fr = freeze float %v1
  ret float %v1.fr
}


define float @propagate_drop_fadd(float %arg) {
; CHECK-LABEL: @propagate_drop_fadd(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze float [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = fadd float [[ARG_FR]], 2.000000e+00
; CHECK-NEXT:    ret float [[V1]]
;
  %v1 = fadd ninf nnan float %arg, 2.0
  %v1.fr = freeze float %v1
  ret float %v1.fr
}

define float @propagate_drop_fsub(float %arg) {
; CHECK-LABEL: @propagate_drop_fsub(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze float [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = fadd float [[ARG_FR]], -2.000000e+00
; CHECK-NEXT:    ret float [[V1]]
;
  %v1 = fsub ninf nnan float %arg, 2.0
  %v1.fr = freeze float %v1
  ret float %v1.fr
}

define float @propagate_drop_fmul(float %arg) {
; CHECK-LABEL: @propagate_drop_fmul(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze float [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = fmul float [[ARG_FR]], 2.000000e+00
; CHECK-NEXT:    ret float [[V1]]
;
  %v1 = fmul ninf nnan float %arg, 2.0
  %v1.fr = freeze float %v1
  ret float %v1.fr
}

define float @propagate_drop_fdiv(float %arg) {
; CHECK-LABEL: @propagate_drop_fdiv(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze float [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = fmul float [[ARG_FR]], 5.000000e-01
; CHECK-NEXT:    ret float [[V1]]
;
  %v1 = fdiv ninf nnan float %arg, 2.0
  %v1.fr = freeze float %v1
  ret float %v1.fr
}

define float @propagate_drop_frem(float %arg) {
; CHECK-LABEL: @propagate_drop_frem(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze float [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = frem float [[ARG_FR]], 2.000000e+00
; CHECK-NEXT:    ret float [[V1]]
;
  %v1 = frem ninf nnan float %arg, 2.0
  %v1.fr = freeze float %v1
  ret float %v1.fr
}

define i1 @propagate_drop_fcmp(float %arg) {
; CHECK-LABEL: @propagate_drop_fcmp(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze float [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = fcmp une float [[ARG_FR]], 2.000000e+00
; CHECK-NEXT:    ret i1 [[V1]]
;
  %v1 = fcmp ninf nnan une float %arg, 2.0
  %v1.fr = freeze i1 %v1
  ret i1 %v1.fr
}

define float @propagate_drop_fmath_select(i1 %arg) {
; CHECK-LABEL: @propagate_drop_fmath_select(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze i1 [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = select i1 [[ARG_FR]], float 1.000000e+00, float -1.000000e+00
; CHECK-NEXT:    ret float [[V1]]
;
  %v1 = select ninf nnan i1 %arg, float 1.0, float -1.0
  %v1.fr = freeze float %v1
  ret float %v1.fr
}

define void @fold_phi_noop(i32 noundef %init, i32 %n) {
; CHECK-LABEL: @fold_phi_noop(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT:%.*]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %i = phi i32 [ %init, %entry ], [ %i.next, %loop ]
  %i.fr = freeze i32 %i
  %i.next = add i32 %i.fr, 1
  %cond = icmp eq i32 %i.next, %n
  br i1 %cond, label %loop, label %exit

exit:                                             ; preds = %loop
  ret void
}

define void @fold_phi_through(i32 %init, i32 %n) {
; CHECK-LABEL: @fold_phi_through(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[PHI_FR:%.*]] = freeze i32 [[INIT:%.*]]
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[PHI_FR]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %i = phi i32 [ %init, %entry ], [ %i.next, %loop ]
  %i.fr = freeze i32 %i
  %i.next = add i32 %i.fr, 1
  %cond = icmp eq i32 %i.next, %n
  br i1 %cond, label %loop, label %exit

exit:                                             ; preds = %loop
  ret void
}

define void @fold_phi_neg_flags(i32 %init, i32 %n) {
; CHECK-LABEL: @fold_phi_neg_flags(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT:%.*]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[I_FR:%.*]] = freeze i32 [[I]]
; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I_FR]], 1
; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %i = phi i32 [ %init, %entry ], [ %i.next, %loop ]
  %i.fr = freeze i32 %i
  %i.next = add nsw nuw i32 %i.fr, 1
  %cond = icmp eq i32 %i.next, %n
  br i1 %cond, label %loop, label %exit

exit:                                             ; preds = %loop
  ret void
}

define void @fold_phi_non_add(i32 %init, i32 %n) {
; CHECK-LABEL: @fold_phi_non_add(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[PHI_FR:%.*]] = freeze i32 [[INIT:%.*]]
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[PHI_FR]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[I_NEXT]] = shl i32 [[I]], 1
; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %i = phi i32 [ %init, %entry ], [ %i.next, %loop ]
  %i.fr = freeze i32 %i
  %i.next = shl i32 %i.fr, 1
  %cond = icmp eq i32 %i.next, %n
  br i1 %cond, label %loop, label %exit

exit:                                             ; preds = %loop
  ret void
}

define void @fold_phi_gep(i8* %init, i8* %end) {
; CHECK-LABEL: @fold_phi_gep(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[PHI_FR:%.*]] = freeze i8* [[INIT:%.*]]
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[I:%.*]] = phi i8* [ [[PHI_FR]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[I_NEXT]] = getelementptr i8, i8* [[I]], i64 1
; CHECK-NEXT:    [[COND:%.*]] = icmp eq i8* [[I_NEXT]], [[END:%.*]]
; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %i = phi i8* [ %init, %entry ], [ %i.next, %loop ]
  %i.fr = freeze i8* %i
  %i.next = getelementptr i8, i8* %i.fr, i64 1
  %cond = icmp eq i8* %i.next, %end
  br i1 %cond, label %loop, label %exit

exit:                                             ; preds = %loop
  ret void
}



