llvm/llvm/test/Transforms/InstCombine/freeze.ll

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals
; RUN: opt < %s -passes=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)
declare void @use_p32(ptr)

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
}

define <3 x i4> @partial_undef_vec() {
; CHECK-LABEL: @partial_undef_vec(
; CHECK-NEXT:    ret <3 x i4> <i4 0, i4 1, i4 0>
;
  %f = freeze <3 x i4> <i4 poison, i4 1, i4 undef>
  ret <3 x i4> %f
}

; 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(ptr %ptr) {
; CHECK-LABEL: @early_freeze_test2(
; CHECK-NEXT:    [[V1:%.*]] = load i32, ptr [[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, ptr %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:    [[V4:%.*]] = add i32 [[V2]], 3
; 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:    [[A:%.*]] = alloca i32, align 4
; CHECK-NEXT:    [[V_FR:%.*]] = freeze i32 [[V:%.*]]
; CHECK-NEXT:    call void @use_p32(ptr nonnull [[A]])
; CHECK-NEXT:    call void @use_i32(i32 [[V_FR]])
; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[V_FR]], 0
; CHECK-NEXT:    br i1 [[COND]], label [[BB0:%.*]], label [[BB1:%.*]]
; CHECK:       bb0:
; 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_FR]])
; CHECK-NEXT:    br label [[END]]
; CHECK:       end:
; CHECK-NEXT:    ret void
;
entry:
  %a = alloca i32
  call void @use_p32(ptr %a)
  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
}

declare i32 @__CxxFrameHandler3(...)

define void @freeze_dominated_uses_catchswitch(i1 %c, i32 %x) personality ptr @__CxxFrameHandler3 {
; CHECK-LABEL: @freeze_dominated_uses_catchswitch(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
; CHECK:       if.then:
; CHECK-NEXT:    invoke void @use_i32(i32 0)
; CHECK-NEXT:            to label [[CLEANUP:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
; CHECK:       if.else:
; CHECK-NEXT:    invoke void @use_i32(i32 1)
; CHECK-NEXT:            to label [[CLEANUP]] unwind label [[CATCH_DISPATCH]]
; CHECK:       catch.dispatch:
; CHECK-NEXT:    [[PHI:%.*]] = phi i32 [ 0, [[IF_THEN]] ], [ [[X:%.*]], [[IF_ELSE]] ]
; CHECK-NEXT:    [[CS:%.*]] = catchswitch within none [label [[CATCH:%.*]], label %catch2] unwind to caller
; CHECK:       catch:
; CHECK-NEXT:    [[CP:%.*]] = catchpad within [[CS]] [ptr null, i32 64, ptr null]
; CHECK-NEXT:    [[PHI_FREEZE:%.*]] = freeze i32 [[PHI]]
; CHECK-NEXT:    call void @use_i32(i32 [[PHI_FREEZE]]) [ "funclet"(token [[CP]]) ]
; CHECK-NEXT:    unreachable
; CHECK:       catch2:
; CHECK-NEXT:    [[CP2:%.*]] = catchpad within [[CS]] [ptr null, i32 64, ptr null]
; CHECK-NEXT:    call void @use_i32(i32 [[PHI]]) [ "funclet"(token [[CP2]]) ]
; CHECK-NEXT:    unreachable
; CHECK:       cleanup:
; CHECK-NEXT:    ret void
;
entry:
  br i1 %c, label %if.then, label %if.else

if.then:
  invoke void @use_i32(i32 0)
  to label %cleanup unwind label %catch.dispatch

if.else:
  invoke void @use_i32(i32 1)
  to label %cleanup unwind label %catch.dispatch

catch.dispatch:
  %phi = phi i32 [ 0, %if.then ], [ %x, %if.else ]
  %cs = catchswitch within none [label %catch, label %catch2] unwind to caller

catch:
  %cp = catchpad within %cs [ptr null, i32 64, ptr null]
  %phi.freeze = freeze i32 %phi
  call void @use_i32(i32 %phi.freeze) [ "funclet"(token %cp) ]
  unreachable

catch2:
  %cp2 = catchpad within %cs [ptr null, i32 64, ptr null]
  call void @use_i32(i32 %phi) [ "funclet"(token %cp2) ]
  unreachable

cleanup:
  ret void
}

declare i32 @get_i32()

define i32 @freeze_use_in_different_branches(i1 %c) {
; CHECK-LABEL: @freeze_use_in_different_branches(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[X:%.*]] = call i32 @get_i32()
; CHECK-NEXT:    [[FR:%.*]] = freeze i32 [[X]]
; CHECK-NEXT:    call void @use_i32(i32 0)
; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
; CHECK:       if:
; CHECK-NEXT:    call void @use_i32(i32 [[FR]])
; CHECK-NEXT:    ret i32 0
; CHECK:       else:
; CHECK-NEXT:    call void @use_i32(i32 [[FR]])
; CHECK-NEXT:    call void @use_i32(i32 [[FR]])
; CHECK-NEXT:    ret i32 1
;
entry:
  %x = call i32 @get_i32()
  call void @use_i32(i32 0)
  br i1 %c, label %if, label %else

if:
  call void @use_i32(i32 %x)
  ret i32 0

else:
  call void @use_i32(i32 %x)
  %fr = freeze i32 %x
  call void @use_i32(i32 %fr)
  ret i32 1
}

define i32 @freeze_phi_use(i1 %c) {
; CHECK-LABEL: @freeze_phi_use(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[X:%.*]] = call i32 @get_i32()
; CHECK-NEXT:    [[FR:%.*]] = freeze i32 [[X]]
; CHECK-NEXT:    call void @use_i32(i32 0)
; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF:%.*]], label [[JOIN:%.*]]
; CHECK:       if:
; CHECK-NEXT:    br label [[JOIN]]
; CHECK:       join:
; CHECK-NEXT:    [[PHI:%.*]] = phi i32 [ [[FR]], [[IF]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT:    call void @use_i32(i32 [[FR]])
; CHECK-NEXT:    ret i32 [[PHI]]
;
entry:
  %x = call i32 @get_i32()
  call void @use_i32(i32 0)
  br i1 %c, label %if, label %join

if:
  br label %join

join:
  %phi = phi i32 [ %x, %if ], [ 0, %entry ]
  %fr = freeze i32 %x
  call void @use_i32(i32 %fr)
  ret i32 %phi
}

define i32 @freeze_phi_followed_by_phi(i1 %c, i32 %y, i32 %z) {
; CHECK-LABEL: @freeze_phi_followed_by_phi(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF:%.*]], label [[JOIN:%.*]]
; CHECK:       if:
; CHECK-NEXT:    br label [[JOIN]]
; CHECK:       join:
; CHECK-NEXT:    [[X:%.*]] = phi i32 [ [[Y:%.*]], [[IF]] ], [ [[Z:%.*]], [[ENTRY:%.*]] ]
; CHECK-NEXT:    [[PHI:%.*]] = phi i32 [ [[Z]], [[IF]] ], [ [[Y]], [[ENTRY]] ]
; CHECK-NEXT:    [[FR:%.*]] = freeze i32 [[X]]
; CHECK-NEXT:    call void @use_i32(i32 [[FR]])
; CHECK-NEXT:    call void @use_i32(i32 [[FR]])
; CHECK-NEXT:    ret i32 [[PHI]]
;
entry:
  br i1 %c, label %if, label %join

if:
  br label %join

join:
  %x = phi i32 [ %y, %if ], [ %z, %entry ]
  %phi = phi i32 [ %z, %if ], [ %y, %entry ]
  call void @use_i32(i32 %x)
  %fr = freeze i32 %x
  call void @use_i32(i32 %fr)
  ret i32 %phi
}

define i32 @freeze_invoke_use_in_phi(i1 %c) personality ptr undef {
; CHECK-LABEL: @freeze_invoke_use_in_phi(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[X:%.*]] = invoke i32 @get_i32()
; CHECK-NEXT:            to label [[INVOKE_CONT:%.*]] unwind label [[INVOKE_UNWIND:%.*]]
; CHECK:       invoke.cont:
; CHECK-NEXT:    [[PHI:%.*]] = phi i32 [ [[X]], [[ENTRY:%.*]] ], [ 0, [[INVOKE_CONT]] ]
; CHECK-NEXT:    [[FR:%.*]] = freeze i32 [[X]]
; CHECK-NEXT:    call void @use_i32(i32 [[FR]])
; CHECK-NEXT:    call void @use_i32(i32 [[PHI]])
; CHECK-NEXT:    br label [[INVOKE_CONT]]
; CHECK:       invoke.unwind:
; CHECK-NEXT:    [[TMP0:%.*]] = landingpad i8
; CHECK-NEXT:            cleanup
; CHECK-NEXT:    unreachable
;
entry:
  %x = invoke i32 @get_i32()
  to label %invoke.cont unwind label %invoke.unwind

invoke.cont:
  %phi = phi i32 [ %x, %entry ], [ 0, %invoke.cont ]
  %fr = freeze i32 %x
  call void @use_i32(i32 %fr)
  call void @use_i32(i32 %phi)
  br label %invoke.cont

invoke.unwind:
  landingpad i8 cleanup
  unreachable
}

define i32 @freeze_invoke_use_after_phi(i1 %c) personality ptr undef {
; CHECK-LABEL: @freeze_invoke_use_after_phi(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[X:%.*]] = invoke i32 @get_i32()
; CHECK-NEXT:            to label [[INVOKE_CONT:%.*]] unwind label [[INVOKE_UNWIND:%.*]]
; CHECK:       invoke.cont:
; CHECK-NEXT:    [[PHI:%.*]] = phi i32 [ [[X]], [[ENTRY:%.*]] ], [ 0, [[INVOKE_CONT]] ]
; CHECK-NEXT:    [[FR:%.*]] = freeze i32 [[X]]
; CHECK-NEXT:    call void @use_i32(i32 [[FR]])
; CHECK-NEXT:    call void @use_i32(i32 [[FR]])
; CHECK-NEXT:    call void @use_i32(i32 [[PHI]])
; CHECK-NEXT:    br label [[INVOKE_CONT]]
; CHECK:       invoke.unwind:
; CHECK-NEXT:    [[TMP0:%.*]] = landingpad i8
; CHECK-NEXT:            cleanup
; CHECK-NEXT:    unreachable
;
entry:
  %x = invoke i32 @get_i32()
  to label %invoke.cont unwind label %invoke.unwind

invoke.cont:
  %phi = phi i32 [ %x, %entry ], [ 0, %invoke.cont ]
  call void @use_i32(i32 %x)
  %fr = freeze i32 %x
  call void @use_i32(i32 %fr)
  call void @use_i32(i32 %phi)
  br label %invoke.cont

invoke.unwind:
  landingpad i8 cleanup
  unreachable
}

define i32 @freeze_callbr_use_after_phi(i1 %c) {
; CHECK-LABEL: @freeze_callbr_use_after_phi(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[X:%.*]] = callbr i32 asm sideeffect "", "=r"() #[[ATTR1:[0-9]+]]
; CHECK-NEXT:            to label [[CALLBR_CONT:%.*]] []
; CHECK:       callbr.cont:
; CHECK-NEXT:    [[PHI:%.*]] = phi i32 [ [[X]], [[ENTRY:%.*]] ], [ 0, [[CALLBR_CONT]] ]
; CHECK-NEXT:    call void @use_i32(i32 [[X]])
; CHECK-NEXT:    [[FR:%.*]] = freeze i32 [[X]]
; CHECK-NEXT:    call void @use_i32(i32 [[FR]])
; CHECK-NEXT:    call void @use_i32(i32 [[PHI]])
; CHECK-NEXT:    br label [[CALLBR_CONT]]
;
entry:
  %x = callbr i32 asm sideeffect "", "=r"()
  to label %callbr.cont []

callbr.cont:
  %phi = phi i32 [ %x, %entry ], [ 0, %callbr.cont ]
  call void @use_i32(i32 %x)
  %fr = freeze i32 %x
  call void @use_i32(i32 %fr)
  call void @use_i32(i32 %phi)
  br label %callbr.cont
}

define i1 @combine_and_after_freezing_uses(i32 %x) {
; CHECK-LABEL: @combine_and_after_freezing_uses(
; CHECK-NEXT:    [[X_FR:%.*]] = freeze i32 [[X:%.*]]
; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[X_FR]], 15
; CHECK-NEXT:    [[AND:%.*]] = icmp eq i32 [[TMP1]], 15
; CHECK-NEXT:    ret i1 [[AND]]
;
  %and1 = and i32 %x, 4
  %cmp1 = icmp ne i32 %and1, 0
  %x.fr = freeze i32 %x
  %and2 = and i32 %x.fr, 11
  %cmp2 = icmp eq i32 %and2, 11
  %and = and i1 %cmp1, %cmp2
  ret i1 %and
}

declare i1 @mock_use(i64, i64)
define i1 @fully_propagate_freeze(i32 %0, i32 noundef %1) {
; CHECK-LABEL: @fully_propagate_freeze(
; CHECK-NEXT:    [[DOTFR:%.*]] = freeze i32 [[TMP0:%.*]]
; CHECK-NEXT:    [[DR:%.*]] = lshr i32 [[DOTFR]], 2
; CHECK-NEXT:    [[IDX1:%.*]] = zext nneg i32 [[DR]] to i64
; CHECK-NEXT:    [[ADD:%.*]] = add nuw nsw i32 [[DR]], 1
; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[ADD]], [[TMP1:%.*]]
; CHECK-NEXT:    [[IDX2:%.*]] = zext nneg i32 [[DR]] to i64
; CHECK-NEXT:    [[V:%.*]] = call i1 @mock_use(i64 [[IDX1]], i64 [[IDX2]])
; CHECK-NEXT:    [[RET:%.*]] = and i1 [[V]], [[CMP]]
; CHECK-NEXT:    ret i1 [[RET]]
;
  %dr = lshr i32 %0, 2
  %idx1 = zext i32 %dr to i64
  %add = add i32 %dr, 1
  %cmp = icmp slt i32 %add, %1
  %cmp.fr = freeze i1 %cmp
  %idx2 = zext i32 %dr to i64
  %v = call i1 @mock_use(i64 %idx1, i64 %idx2)
  %ret = and i1 %v, %cmp.fr
  ret i1 %ret
}

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:    [[V11:%.*]] = lshr i32 [[ARG_FR]], 1
; CHECK-NEXT:    ret i32 [[V11]]
;
  %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 ptr @propagate_drop_gep1(ptr %arg) {
; CHECK-LABEL: @propagate_drop_gep1(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze ptr [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = getelementptr i8, ptr [[ARG_FR]], i64 16
; CHECK-NEXT:    ret ptr [[V1]]
;
  %v1 = getelementptr inbounds i8, ptr %arg, i64 16
  %v1.fr = freeze ptr %v1
  ret ptr %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:    [[TMP0:%.*]] = freeze i32 [[INIT:%.*]]
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[TMP0]], [[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_drop_flags(i32 %init, i32 %n) {
; CHECK-LABEL: @fold_phi_drop_flags(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INIT_FR:%.*]] = freeze i32 [[INIT:%.*]]
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT_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 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:    [[TMP0:%.*]] = freeze i32 [[INIT:%.*]]
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[TMP0]], [[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(ptr %init, ptr %end) {
; CHECK-LABEL: @fold_phi_gep(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[TMP0:%.*]] = freeze ptr [[INIT:%.*]]
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[I:%.*]] = phi ptr [ [[TMP0]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[I_NEXT]] = getelementptr i8, ptr [[I]], i64 1
; CHECK-NEXT:    [[COND:%.*]] = icmp eq ptr [[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 ptr [ %init, %entry ], [ %i.next, %loop ]
  %i.fr = freeze ptr %i
  %i.next = getelementptr i8, ptr %i.fr, i64 1
  %cond = icmp eq ptr %i.next, %end
  br i1 %cond, label %loop, label %exit

exit:                                             ; preds = %loop
  ret void
}

define void @fold_phi_multiple_insts(i32 %init, i32 %n) {
; CHECK-LABEL: @fold_phi_multiple_insts(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INIT_FR:%.*]] = freeze i32 [[INIT:%.*]]
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT_FR]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
; CHECK-NEXT:    [[I_SQ:%.*]] = mul i32 [[I]], [[I]]
; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I_SQ]], 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.sq = mul nsw nuw i32 %i.fr, %i.fr
  %i.next = add nsw nuw i32 %i.sq, 1
  %cond = icmp eq i32 %i.next, %n
  br i1 %cond, label %loop, label %exit

exit:                                             ; preds = %loop
  ret void
}

define void @fold_phi_multiple_back_edges(i32 %init, i32 %n, i32 %m) {
; CHECK-LABEL: @fold_phi_multiple_back_edges(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INIT_FR:%.*]] = freeze i32 [[INIT:%.*]]
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT_FR]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ], [ [[I_NEXT2:%.*]], [[LOOP_LATCH2:%.*]] ]
; 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 [[LOOP_LATCH2]]
; CHECK:       loop.latch2:
; CHECK-NEXT:    [[I_NEXT2]] = add i32 [[I]], 2
; CHECK-NEXT:    [[COND2:%.*]] = icmp eq i32 [[I_NEXT]], [[M:%.*]]
; CHECK-NEXT:    br i1 [[COND2]], 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.next2, %loop.latch2 ]
  %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 %loop.latch2

loop.latch2:
  %i.next2 = add nsw nuw i32 %i.fr, 2
  %cond2 = icmp eq i32 %i.next, %m
  br i1 %cond2, label %loop, label %exit

exit:                                             ; preds = %loop
  ret void
}

define void @fold_phi_multiple_start_values(i1 %c, i32 %init, i32 %init2, i32 %n) {
; CHECK-LABEL: @fold_phi_multiple_start_values(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br i1 [[C:%.*]], label [[IF:%.*]], label [[LOOP:%.*]]
; CHECK:       if:
; CHECK-NEXT:    br label [[LOOP]]
; CHECK:       loop:
; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT:%.*]], [[ENTRY:%.*]] ], [ [[INIT2:%.*]], [[IF]] ], [ [[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 i1 %c, label %if, label %loop

if:
  br label %loop

loop:
  %i = phi i32 [ %init, %entry ], [ %init2, %if ], [ %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:
  ret void
}

define void @fold_phi_invoke_start_value(i32 %n) personality ptr undef {
; CHECK-LABEL: @fold_phi_invoke_start_value(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INIT:%.*]] = invoke i32 @get_i32()
; CHECK-NEXT:            to label [[LOOP:%.*]] unwind label [[UNWIND:%.*]]
; 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:       unwind:
; CHECK-NEXT:    [[TMP0:%.*]] = landingpad i8
; CHECK-NEXT:            cleanup
; CHECK-NEXT:    unreachable
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %init = invoke i32 @get_i32()
  to label %loop unwind label %unwind

loop:
  %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

unwind:
  landingpad i8 cleanup
  unreachable

exit:
  ret void
}

define void @fold_phi_invoke_noundef_start_value(i32 %n) personality ptr undef {
; CHECK-LABEL: @fold_phi_invoke_noundef_start_value(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[INIT:%.*]] = invoke noundef i32 @get_i32()
; CHECK-NEXT:            to label [[LOOP:%.*]] unwind label [[UNWIND:%.*]]
; 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:       unwind:
; CHECK-NEXT:    [[TMP0:%.*]] = landingpad i8
; CHECK-NEXT:            cleanup
; CHECK-NEXT:    unreachable
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %init = invoke noundef i32 @get_i32()
  to label %loop unwind label %unwind

loop:
  %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

unwind:
  landingpad i8 cleanup
  unreachable

exit:
  ret void
}

define ptr @freeze_load_noundef(ptr %ptr) {
; CHECK-LABEL: @freeze_load_noundef(
; CHECK-NEXT:    [[P:%.*]] = load ptr, ptr [[PTR:%.*]], align 8, !noundef [[META0:![0-9]+]]
; CHECK-NEXT:    ret ptr [[P]]
;
  %p = load ptr, ptr %ptr, !noundef !0
  %p.fr = freeze ptr %p
  ret ptr %p.fr
}

define ptr @freeze_load_dereferenceable(ptr %ptr) {
; CHECK-LABEL: @freeze_load_dereferenceable(
; CHECK-NEXT:    [[P:%.*]] = load ptr, ptr [[PTR:%.*]], align 8, !dereferenceable [[META1:![0-9]+]]
; CHECK-NEXT:    ret ptr [[P]]
;
  %p = load ptr, ptr %ptr, !dereferenceable !1
  %p.fr = freeze ptr %p
  ret ptr %p.fr
}

define ptr @freeze_load_dereferenceable_or_null(ptr %ptr) {
; CHECK-LABEL: @freeze_load_dereferenceable_or_null(
; CHECK-NEXT:    [[P:%.*]] = load ptr, ptr [[PTR:%.*]], align 8, !dereferenceable_or_null [[META1]]
; CHECK-NEXT:    ret ptr [[P]]
;
  %p = load ptr, ptr %ptr, !dereferenceable_or_null !1
  %p.fr = freeze ptr %p
  ret ptr %p.fr
}

define i32 @freeze_load_with_range(ptr %ptr) {
; CHECK-LABEL: @freeze_load_with_range(
; CHECK-NEXT:    [[X:%.*]] = load i32, ptr [[PTR:%.*]], align 4, !range [[RNG2:![0-9]+]]
; CHECK-NEXT:    [[X_FR:%.*]] = freeze i32 [[X]]
; CHECK-NEXT:    ret i32 [[X_FR]]
;
  %x = load i32, ptr %ptr, !range !2
  %x.fr = freeze i32 %x
  ret i32 %x.fr
}

declare i32 @foo.i32()

define i32 @freeze_call_with_range() {
; CHECK-LABEL: @freeze_call_with_range(
; CHECK-NEXT:    [[X:%.*]] = call i32 @foo.i32(), !range [[RNG2]]
; CHECK-NEXT:    [[X_FR:%.*]] = freeze i32 [[X]]
; CHECK-NEXT:    ret i32 [[X_FR]]
;
  %x = call i32 @foo.i32(), !range !2
  %x.fr = freeze i32 %x
  ret i32 %x.fr
}

declare i32 @llvm.ctpop.i32(i32)

define i32 @freeze_ctpop(i32 %x) {
; CHECK-LABEL: @freeze_ctpop(
; CHECK-NEXT:    [[Y:%.*]] = lshr i32 2047, [[X:%.*]]
; CHECK-NEXT:    [[Y_FR:%.*]] = freeze i32 [[Y]]
; CHECK-NEXT:    [[CTPOP:%.*]] = call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 [[Y_FR]])
; CHECK-NEXT:    ret i32 [[CTPOP]]
;
  %y = lshr i32 2047, %x
  %ctpop = call i32 @llvm.ctpop.i32(i32 %y)
  %fr = freeze i32 %ctpop
  ret i32 %fr
}

define i32 @freeze_zext_nneg(i8 %x) {
; CHECK-LABEL: @freeze_zext_nneg(
; CHECK-NEXT:    [[X_FR:%.*]] = freeze i8 [[X:%.*]]
; CHECK-NEXT:    [[ZEXT:%.*]] = zext i8 [[X_FR]] to i32
; CHECK-NEXT:    ret i32 [[ZEXT]]
;
  %zext = zext nneg i8 %x to i32
  %fr = freeze i32 %zext
  ret i32 %fr
}

define float @freeze_uitofp_nneg(i8 %x) {
; CHECK-LABEL: @freeze_uitofp_nneg(
; CHECK-NEXT:    [[X_FR:%.*]] = freeze i8 [[X:%.*]]
; CHECK-NEXT:    [[UITOFP:%.*]] = uitofp i8 [[X_FR]] to float
; CHECK-NEXT:    ret float [[UITOFP]]
;
  %uitofp = uitofp nneg i8 %x to float
  %fr = freeze float %uitofp
  ret float %fr
}

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

define i32 @propagate_drop_flags_trunc(i64 %arg) {
; CHECK-LABEL: @propagate_drop_flags_trunc(
; CHECK-NEXT:    [[ARG_FR:%.*]] = freeze i64 [[ARG:%.*]]
; CHECK-NEXT:    [[V1:%.*]] = trunc i64 [[ARG_FR]] to i32
; CHECK-NEXT:    ret i32 [[V1]]
;
  %v1 = trunc nsw nuw i64 %arg to i32
  %v1.fr = freeze i32 %v1
  ret i32 %v1.fr
}

define ptr @propagate_drop_flags_gep_nusw(ptr %p) {
; CHECK-LABEL: @propagate_drop_flags_gep_nusw(
; CHECK-NEXT:    [[P_FR:%.*]] = freeze ptr [[P:%.*]]
; CHECK-NEXT:    [[GEP:%.*]] = getelementptr i8, ptr [[P_FR]], i64 1
; CHECK-NEXT:    ret ptr [[GEP]]
;
  %gep = getelementptr nusw i8, ptr %p, i64 1
  %gep.fr = freeze ptr %gep
  ret ptr %gep.fr
}

define ptr @propagate_drop_flags_gep_nuw(ptr %p) {
; CHECK-LABEL: @propagate_drop_flags_gep_nuw(
; CHECK-NEXT:    [[P_FR:%.*]] = freeze ptr [[P:%.*]]
; CHECK-NEXT:    [[GEP:%.*]] = getelementptr i8, ptr [[P_FR]], i64 1
; CHECK-NEXT:    ret ptr [[GEP]]
;
  %gep = getelementptr nuw i8, ptr %p, i64 1
  %gep.fr = freeze ptr %gep
  ret ptr %gep.fr
}

declare i32 @llvm.umax.i32(i32 %a, i32 %b)

define i32 @freeze_call_with_range_attr(i32 %a) {
; CHECK-LABEL: @freeze_call_with_range_attr(
; CHECK-NEXT:    [[Y:%.*]] = lshr i32 2047, [[A:%.*]]
; CHECK-NEXT:    [[Y_FR:%.*]] = freeze i32 [[Y]]
; CHECK-NEXT:    [[X:%.*]] = call i32 @llvm.umax.i32(i32 [[Y_FR]], i32 50)
; CHECK-NEXT:    ret i32 [[X]]
;
  %y = lshr i32 2047, %a
  %x = call range(i32 0, 2048) i32 @llvm.umax.i32(i32 %y, i32 50)
  %x.fr = freeze i32 %x
  ret i32 %x.fr
}

declare ptr @llvm.ptrmask.p0.i64(ptr, i64)

define ptr @freeze_ptrmask_align(ptr %p, i64 noundef %m) {
; CHECK-LABEL: @freeze_ptrmask_align(
; CHECK-NEXT:    [[P_FR:%.*]] = freeze ptr [[P:%.*]]
; CHECK-NEXT:    [[MASK:%.*]] = call ptr @llvm.ptrmask.p0.i64(ptr [[P_FR]], i64 [[M:%.*]])
; CHECK-NEXT:    ret ptr [[MASK]]
;
  %mask = call align(4) ptr @llvm.ptrmask.p0.i64(ptr %p, i64 %m)
  %fr =  freeze ptr %mask
  ret ptr %fr
}

define ptr @freeze_ptrmask_nonnull(ptr %p, i64 noundef %m) {
; CHECK-LABEL: @freeze_ptrmask_nonnull(
; CHECK-NEXT:    [[P_FR:%.*]] = freeze ptr [[P:%.*]]
; CHECK-NEXT:    [[MASK:%.*]] = call ptr @llvm.ptrmask.p0.i64(ptr [[P_FR]], i64 [[M:%.*]])
; CHECK-NEXT:    ret ptr [[MASK]]
;
  %mask = call nonnull ptr @llvm.ptrmask.p0.i64(ptr %p, i64 %m)
  %fr =  freeze ptr %mask
  ret ptr %fr
}

!0 = !{}
!1 = !{i64 4}
!2 = !{i32 0, i32 100}
;.
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
; CHECK: attributes #[[ATTR1]] = { nounwind }
;.
; CHECK: [[META0]] = !{}
; CHECK: [[META1]] = !{i64 4}
; CHECK: [[RNG2]] = !{i32 0, i32 100}
;.