; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt < %s -passes=instcombine -S | FileCheck %s define i32 @fold(i32 %x) { ; CHECK-LABEL: define i32 @fold( ; CHECK-SAME: i32 [[X:%.*]]) { ; 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: define i32 @make_const() { ; CHECK-NEXT: ret i32 10 ; %x = freeze i32 10 ret i32 %x } define i32 @and_freeze_undef(i32 %x) { ; CHECK-LABEL: define i32 @and_freeze_undef( ; CHECK-SAME: i32 [[X:%.*]]) { ; 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: define i32 @and_freeze_undef_multipleuses( ; CHECK-SAME: i32 [[X:%.*]]) { ; 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: define i32 @or_freeze_undef( ; CHECK-SAME: i32 [[X:%.*]]) { ; 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: define i32 @or_freeze_undef_multipleuses( ; CHECK-SAME: i32 [[X:%.*]]) { ; 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: define void @or_select_multipleuses( ; CHECK-SAME: i32 [[X:%.*]], i1 [[Y:%.*]]) { ; 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: define void @or_select_multipleuses_logical( ; CHECK-SAME: i32 [[X:%.*]], i1 [[Y:%.*]]) { ; 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: define <3 x i4> @partial_undef_vec() { ; CHECK-NEXT: ret <3 x i4> splat (i4 1) ; %f = freeze <3 x i4> 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: define i32 @early_freeze_test1( ; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) { ; CHECK-NEXT: [[X_FR:%.*]] = freeze i32 [[X]] ; CHECK-NEXT: [[Y_FR:%.*]] = freeze i32 [[Y]] ; CHECK-NEXT: [[V1:%.*]] = add i32 [[X_FR]], [[Y_FR]] ; CHECK-NEXT: [[V2:%.*]] = shl i32 [[V1]], 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: define i1 @early_freeze_test2( ; CHECK-SAME: ptr [[PTR:%.*]]) { ; 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: define i32 @early_freeze_test3( ; CHECK-SAME: i32 [[V1:%.*]]) { ; 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 } define i32 @early_freeze_test4(i32 %v1) { ; CHECK-LABEL: define i32 @early_freeze_test4( ; CHECK-SAME: i32 [[V1:%.*]]) { ; CHECK-NEXT: [[V1_FR:%.*]] = freeze i32 [[V1]] ; CHECK-NEXT: [[V2:%.*]] = mul i32 [[V1_FR]], [[V1_FR]] ; CHECK-NEXT: ret i32 [[V2]] ; %v2 = mul i32 %v1, %v1 %v2.fr = freeze i32 %v2 ret i32 %v2.fr } ; If replace all dominated uses of v to freeze(v). define void @freeze_dominated_uses_test1(i32 %v) { ; CHECK-LABEL: define void @freeze_dominated_uses_test1( ; CHECK-SAME: i32 [[V:%.*]]) { ; 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: define void @freeze_dominated_uses_test2( ; CHECK-SAME: i32 [[V:%.*]]) { ; 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 %[[BB_0:.*]], label %[[BB_1:.*]] ; CHECK: [[BB_0]]: ; CHECK-NEXT: call void @use_i32(i32 [[V_FR]]) ; CHECK-NEXT: call void @use_i32(i32 [[V_FR]]) ; CHECK-NEXT: br label %[[END:.*]] ; CHECK: [[BB_1]]: ; 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 %bb.0, label %bb.1 bb.0: %v.fr = freeze i32 %v call void @use_i32(i32 %v.fr) call void @use_i32(i32 %v) br label %end bb.1: 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: define void @freeze_dominated_uses_test3( ; CHECK-SAME: i32 [[V:%.*]], i1 [[COND:%.*]]) { ; 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 %[[BB_0:.*]], label %[[BB_1:.*]] ; CHECK: [[BB_0]]: ; CHECK-NEXT: call void @use_i32(i32 [[V_FR1]]) ; CHECK-NEXT: br label %[[END:.*]] ; CHECK: [[BB_1]]: ; 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 %bb.0, label %bb.1 bb.0: %v.fr2 = freeze i32 %v call void @use_i32(i32 %v.fr2) br label %end bb.1: 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: define void @freeze_dominated_uses_catchswitch( ; CHECK-SAME: i1 [[C:%.*]], i32 [[X:%.*]]) personality ptr @__CxxFrameHandler3 { ; 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: define i32 @freeze_use_in_different_branches( ; CHECK-SAME: i1 [[C:%.*]]) { ; 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: define i32 @freeze_phi_use( ; CHECK-SAME: i1 [[C:%.*]]) { ; 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: define i32 @freeze_phi_followed_by_phi( ; CHECK-SAME: i1 [[C:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) { ; 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: define i32 @freeze_invoke_use_in_phi( ; CHECK-SAME: i1 [[C:%.*]]) personality ptr undef { ; 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: define i32 @freeze_invoke_use_after_phi( ; CHECK-SAME: i1 [[C:%.*]]) personality ptr undef { ; 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: define i32 @freeze_callbr_use_after_phi( ; CHECK-SAME: i1 [[C:%.*]]) { ; 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: define i1 @combine_and_after_freezing_uses( ; CHECK-SAME: i32 [[X:%.*]]) { ; 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: define i1 @fully_propagate_freeze( ; CHECK-SAME: i32 [[TMP0:%.*]], i32 noundef [[TMP1:%.*]]) { ; 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: define i32 @propagate_drop_flags_add( ; CHECK-SAME: i32 [[ARG:%.*]]) { ; 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: define i32 @propagate_drop_flags_add_foldaway( ; CHECK-SAME: i32 noundef [[ARG:%.*]]) { ; 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: define i32 @propagate_drop_flags_sub( ; CHECK-SAME: i32 [[ARG:%.*]]) { ; 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: define i32 @propagate_drop_flags_mul( ; CHECK-SAME: i32 [[ARG:%.*]]) { ; 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: define i32 @propagate_drop_flags_udiv( ; CHECK-SAME: i32 [[ARG:%.*]]) { ; 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: define i32 @propagate_drop_flags_sdiv( ; CHECK-SAME: i32 [[ARG:%.*]]) { ; 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: define i32 @propagate_drop_shl1( ; CHECK-SAME: i32 [[ARG:%.*]]) { ; 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: define i32 @propagate_drop_shl2( ; CHECK-SAME: i32 [[ARG:%.*]], i32 [[UNKNOWN:%.*]]) { ; 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: define i32 @propagate_drop_ashr1( ; CHECK-SAME: i32 [[ARG:%.*]]) { ; 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: define i32 @propagate_drop_ashr2( ; CHECK-SAME: i32 [[ARG:%.*]], i32 [[UNKNOWN:%.*]]) { ; 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: define i32 @propagate_drop_lshr1( ; CHECK-SAME: i32 [[ARG:%.*]]) { ; 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: define i32 @propagate_drop_lshr2( ; CHECK-SAME: i32 [[ARG:%.*]], i32 [[UNKNOWN:%.*]]) { ; 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: define ptr @propagate_drop_gep1( ; CHECK-SAME: ptr [[ARG:%.*]]) { ; 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: define float @propagate_drop_fneg( ; CHECK-SAME: float [[ARG:%.*]]) { ; 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: define float @propagate_drop_fadd( ; CHECK-SAME: float [[ARG:%.*]]) { ; 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: define float @propagate_drop_fsub( ; CHECK-SAME: float [[ARG:%.*]]) { ; 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: define float @propagate_drop_fmul( ; CHECK-SAME: float [[ARG:%.*]]) { ; 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: define float @propagate_drop_fdiv( ; CHECK-SAME: float [[ARG:%.*]]) { ; 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: define float @propagate_drop_frem( ; CHECK-SAME: float [[ARG:%.*]]) { ; 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: define i1 @propagate_drop_fcmp( ; CHECK-SAME: float [[ARG:%.*]]) { ; 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: define float @propagate_drop_fmath_select( ; CHECK-SAME: i1 [[ARG:%.*]]) { ; 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: define void @fold_phi_noop( ; CHECK-SAME: i32 noundef [[INIT:%.*]], i32 [[N:%.*]]) { ; 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: define void @fold_phi_through( ; CHECK-SAME: i32 [[INIT:%.*]], i32 [[N:%.*]]) { ; 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: define void @fold_phi_drop_flags( ; CHECK-SAME: i32 [[INIT:%.*]], i32 [[N:%.*]]) { ; 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: define void @fold_phi_non_add( ; CHECK-SAME: i32 [[INIT:%.*]], i32 [[N:%.*]]) { ; 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: define void @fold_phi_gep( ; CHECK-SAME: ptr [[INIT:%.*]], ptr [[END:%.*]]) { ; 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 } ; The recurrence for the GEP offset can't produce poison so the freeze should ; be pushed through to the ptr. define void @fold_phi_gep_phi_offset(ptr %init, ptr %end, i64 noundef %n) { ; CHECK-LABEL: define void @fold_phi_gep_phi_offset( ; CHECK-SAME: ptr [[INIT:%.*]], ptr [[END:%.*]], i64 noundef [[N:%.*]]) { ; 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: [[OFF:%.*]] = phi i64 [ [[N]], %[[ENTRY]] ], [ [[OFF_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: [[OFF_NEXT]] = shl i64 [[OFF]], 3 ; CHECK-NEXT: [[I_NEXT]] = getelementptr i8, ptr [[I]], i64 [[OFF_NEXT]] ; 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.fr, %loop ] %off = phi i64 [ %n, %entry ], [ %off.next, %loop ] %off.next = shl i64 %off, 3 %i.next = getelementptr i8, ptr %i, i64 %off.next %i.next.fr = freeze ptr %i.next %cond = icmp eq ptr %i.next.fr, %end br i1 %cond, label %loop, label %exit exit: ; preds = %loop ret void } ; Offset is still guaranteed not to be poison, so the freeze can be moved ; here if we strip inbounds from the GEP. define void @fold_phi_gep_inbounds_phi_offset(ptr %init, ptr %end, i64 noundef %n) { ; CHECK-LABEL: define void @fold_phi_gep_inbounds_phi_offset( ; CHECK-SAME: ptr [[INIT:%.*]], ptr [[END:%.*]], i64 noundef [[N:%.*]]) { ; 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: [[OFF:%.*]] = phi i64 [ [[N]], %[[ENTRY]] ], [ [[OFF_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: [[OFF_NEXT]] = shl i64 [[OFF]], 3 ; CHECK-NEXT: [[I_NEXT]] = getelementptr i8, ptr [[I]], i64 [[OFF_NEXT]] ; 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.fr, %loop ] %off = phi i64 [ %n, %entry ], [ %off.next, %loop ] %off.next = shl i64 %off, 3 %i.next = getelementptr inbounds i8, ptr %i, i64 %off.next %i.next.fr = freeze ptr %i.next %cond = icmp eq ptr %i.next.fr, %end br i1 %cond, label %loop, label %exit exit: ; preds = %loop ret void } ; Same as previous, but also requires freezing %n. define void @fold_phi_gep_phi_offset_multiple(ptr %init, ptr %end, i64 %n) { ; CHECK-LABEL: define void @fold_phi_gep_phi_offset_multiple( ; CHECK-SAME: ptr [[INIT:%.*]], ptr [[END:%.*]], i64 [[N:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: [[TMP0:%.*]] = freeze ptr [[INIT]] ; CHECK-NEXT: [[TMP1:%.*]] = freeze i64 [[N]] ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[I:%.*]] = phi ptr [ [[TMP0]], %[[ENTRY]] ], [ [[I_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: [[OFF:%.*]] = phi i64 [ [[TMP1]], %[[ENTRY]] ], [ [[OFF_NEXT:%.*]], %[[LOOP]] ] ; CHECK-NEXT: [[OFF_NEXT]] = shl i64 [[OFF]], 3 ; CHECK-NEXT: [[I_NEXT]] = getelementptr i8, ptr [[I]], i64 [[OFF_NEXT]] ; 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.fr, %loop ] %off = phi i64 [ %n, %entry ], [ %off.next, %loop ] %off.next = shl i64 %off, 3 %i.next = getelementptr inbounds i8, ptr %i, i64 %off.next %i.next.fr = freeze ptr %i.next %cond = icmp eq ptr %i.next.fr, %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: define void @fold_phi_multiple_insts( ; CHECK-SAME: i32 [[INIT:%.*]], i32 [[N:%.*]]) { ; 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: define void @fold_phi_multiple_back_edges( ; CHECK-SAME: i32 [[INIT:%.*]], i32 [[N:%.*]], i32 [[M:%.*]]) { ; 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: define void @fold_phi_multiple_start_values( ; CHECK-SAME: i1 [[C:%.*]], i32 [[INIT:%.*]], i32 [[INIT2:%.*]], i32 [[N:%.*]]) { ; 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 } ; We can remove this freeze as the incoming values to the PHI have the same ; well-defined start value and the GEP can't produce poison, but this is ; currently unsupported. define void @fold_phi_noundef_start_value(ptr noundef %init, i1 %cond.0, i1 %cond.1, i1 %cond.2) { ; CHECK-LABEL: define void @fold_phi_noundef_start_value( ; CHECK-SAME: ptr noundef [[INIT:%.*]], i1 [[COND_0:%.*]], i1 [[COND_1:%.*]], i1 [[COND_2:%.*]]) { ; CHECK-NEXT: [[ENTRY:.*]]: ; CHECK-NEXT: br label %[[LOOP:.*]] ; CHECK: [[LOOP]]: ; CHECK-NEXT: [[IV_0:%.*]] = phi ptr [ [[INIT]], %[[ENTRY]] ], [ [[IV_0_NEXT:%.*]], %[[LOOP_LATCH:.*]] ] ; CHECK-NEXT: br i1 [[COND_0]], label %[[LOOP_LATCH]], label %[[IF_ELSE:.*]] ; CHECK: [[IF_ELSE]]: ; CHECK-NEXT: [[IV_1:%.*]] = getelementptr i8, ptr [[IV_0]], i64 -8 ; CHECK-NEXT: br label %[[LOOP_LATCH]] ; CHECK: [[LOOP_LATCH]]: ; CHECK-NEXT: [[IV_2:%.*]] = phi ptr [ [[IV_0]], %[[LOOP]] ], [ [[IV_1]], %[[IF_ELSE]] ] ; CHECK-NEXT: [[IV_2_FR:%.*]] = freeze ptr [[IV_2]] ; CHECK-NEXT: [[IV_2_FR_INT:%.*]] = ptrtoint ptr [[IV_2_FR]] to i64 ; CHECK-NEXT: [[IV_0_INT:%.*]] = ptrtoint ptr [[IV_0]] to i64 ; CHECK-NEXT: [[IDX:%.*]] = sub i64 [[IV_0_INT]], [[IV_2_FR_INT]] ; CHECK-NEXT: [[IV_0_NEXT]] = getelementptr i8, ptr [[IV_0]], i64 [[IDX]] ; CHECK-NEXT: br i1 [[COND_2]], label %[[EXIT:.*]], label %[[LOOP]] ; CHECK: [[EXIT]]: ; CHECK-NEXT: ret void ; entry: br label %loop loop: %iv.0 = phi ptr [ %init, %entry ], [ %iv.0.next, %loop.latch ] br i1 %cond.0, label %loop.latch, label %if.else if.else: %iv.1 = getelementptr i8, ptr %iv.0, i64 -8 br label %loop.latch loop.latch: %iv.2 = phi ptr [ %iv.0, %loop ], [ %iv.1, %if.else ] %iv.2.fr = freeze ptr %iv.2 %iv.2.fr.int = ptrtoint ptr %iv.2.fr to i64 %iv.0.int = ptrtoint ptr %iv.0 to i64 %idx = sub i64 %iv.0.int, %iv.2.fr.int %iv.0.next = getelementptr i8, ptr %iv.0, i64 %idx br i1 %cond.2, label %exit, label %loop exit: ret void } define void @fold_phi_invoke_start_value(i32 %n) personality ptr undef { ; CHECK-LABEL: define void @fold_phi_invoke_start_value( ; CHECK-SAME: i32 [[N:%.*]]) personality ptr undef { ; 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: define void @fold_phi_invoke_noundef_start_value( ; CHECK-SAME: i32 [[N:%.*]]) personality ptr undef { ; 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: define ptr @freeze_load_noundef( ; CHECK-SAME: ptr [[PTR:%.*]]) { ; 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: define ptr @freeze_load_dereferenceable( ; CHECK-SAME: ptr [[PTR:%.*]]) { ; 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: define ptr @freeze_load_dereferenceable_or_null( ; CHECK-SAME: ptr [[PTR:%.*]]) { ; 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: define i32 @freeze_load_with_range( ; CHECK-SAME: ptr [[PTR:%.*]]) { ; 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: define i32 @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: define i32 @freeze_ctpop( ; CHECK-SAME: i32 [[X:%.*]]) { ; 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: define i32 @freeze_zext_nneg( ; CHECK-SAME: i8 [[X:%.*]]) { ; 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: define float @freeze_uitofp_nneg( ; CHECK-SAME: i8 [[X:%.*]]) { ; 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: define i32 @propagate_drop_flags_or( ; CHECK-SAME: i32 [[ARG:%.*]]) { ; 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: define i32 @propagate_drop_flags_trunc( ; CHECK-SAME: i64 [[ARG:%.*]]) { ; 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: define ptr @propagate_drop_flags_gep_nusw( ; CHECK-SAME: ptr [[P:%.*]]) { ; 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: define ptr @propagate_drop_flags_gep_nuw( ; CHECK-SAME: ptr [[P:%.*]]) { ; 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 } define i1 @propagate_drop_flags_icmp(i32 %a, i32 %b) { ; CHECK-LABEL: define i1 @propagate_drop_flags_icmp( ; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) { ; CHECK-NEXT: [[A_FR:%.*]] = freeze i32 [[A]] ; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[A_FR]], 3 ; CHECK-NEXT: ret i1 [[RET]] ; %ret = icmp samesign ult i32 %a, 3 %ret.fr = freeze i1 %ret ret i1 %ret.fr } declare i32 @llvm.umax.i32(i32 %a, i32 %b) define i32 @freeze_call_with_range_attr(i32 %a) { ; CHECK-LABEL: define i32 @freeze_call_with_range_attr( ; CHECK-SAME: i32 [[A:%.*]]) { ; 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: define ptr @freeze_ptrmask_align( ; CHECK-SAME: ptr [[P:%.*]], i64 noundef [[M:%.*]]) { ; 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: define ptr @freeze_ptrmask_nonnull( ; CHECK-SAME: ptr [[P:%.*]], i64 noundef [[M:%.*]]) { ; 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: [[META0]] = !{} ; CHECK: [[META1]] = !{i64 4} ; CHECK: [[RNG2]] = !{i32 0, i32 100} ;.