diff --git a/llvm/test/Transforms/InstCombine/nanless-canonicalize-combine.ll b/llvm/test/Transforms/InstCombine/nanless-canonicalize-combine.ll new file mode 100644 index 0000000000000..56e0808c650ff --- /dev/null +++ b/llvm/test/Transforms/InstCombine/nanless-canonicalize-combine.ll @@ -0,0 +1,832 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt -S -p=instcombine < %s | FileCheck %s + +; Test a special pattern wrapping llvm.canonicalize which has weaker +; requirements for nan behavior. + +; Base pattern with no denormal flushing, should fold to direct use of +; %x +define float @canonicalize_ieee_0(float %x) #0 { +; CHECK-LABEL: define float @canonicalize_ieee_0( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[SOFT_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 1.0, %x + %ord = fcmp ord float %x, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %soft.canonical + ret float %x.canon +} + +; Commuted pattern, should fold to direct use of %x +define float @canonicalize_ieee_1(float %x) #0 { +; CHECK-LABEL: define float @canonicalize_ieee_1( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[X]] +; CHECK-NEXT: [[UNO:%.*]] = fcmp uno float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[UNO]], float [[SOFT_CANONICAL]], float [[HARD_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 1.0, %x + %uno = fcmp uno float %x, 0.0 + %x.canon = select i1 %uno, float %soft.canonical, float %hard.canonical + ret float %x.canon +} + +; Would be OK, but instcombine will delete the no-op fmul before +; reaching the select. +define float @canonicalize_ieee_0_fmul(float %x) #0 { +; CHECK-LABEL: define float @canonicalize_ieee_0_fmul( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[X]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fmul float %x, 1.0 + %ord = fcmp ord float %x, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %soft.canonical + ret float %x.canon +} + +; Commuted fdiv would be OK, but instcombine will delete the no-op +; fmul before reaching the select. +define float @canonicalize_ieee_0_fdiv_commute(float %x) #0 { +; CHECK-LABEL: define float @canonicalize_ieee_0_fdiv_commute( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[X]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float %x, 1.0 + %ord = fcmp ord float %x, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %soft.canonical + ret float %x.canon +} + +; Base pattern with denormal flushing, should fold to single +; canonicalize call. +define float @canonicalize_daz_0(float %x) #1 { +; CHECK-LABEL: define float @canonicalize_daz_0( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[SOFT_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 1.0, %x + %ord = fcmp ord float %x, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %soft.canonical + ret float %x.canon +} + +; Commuted pattern with denormal flushing, should fold to single +; canonicalize call. +define float @canonicalize_daz_1(float %x) #1 { +; CHECK-LABEL: define float @canonicalize_daz_1( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[X]] +; CHECK-NEXT: [[UNO:%.*]] = fcmp uno float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[UNO]], float [[SOFT_CANONICAL]], float [[HARD_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 1.0, %x + %uno = fcmp uno float %x, 0.0 + %x.canon = select i1 %uno, float %soft.canonical, float %hard.canonical + ret float %x.canon +} + +; Unknown denormal mode can fold to canonicalize call +define float @canonicalize_dynamic_0(float %x) #2 { +; CHECK-LABEL: define float @canonicalize_dynamic_0( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[SOFT_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 1.0, %x + %ord = fcmp ord float %x, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %soft.canonical + ret float %x.canon +} + +; Unknown denormal mode can fold to canonicalize call +define float @canonicalize_dynamic_1(float %x) #2 { +; CHECK-LABEL: define float @canonicalize_dynamic_1( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[X]] +; CHECK-NEXT: [[UNO:%.*]] = fcmp uno float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[UNO]], float [[SOFT_CANONICAL]], float [[HARD_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 1.0, %x + %uno = fcmp uno float %x, 0.0 + %x.canon = select i1 %uno, float %soft.canonical, float %hard.canonical + ret float %x.canon +} + +define <2 x float> @canonicalize_ieee_0_vec(<2 x float> %x) #0 { +; CHECK-LABEL: define <2 x float> @canonicalize_ieee_0_vec( +; CHECK-SAME: <2 x float> [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call <2 x float> @llvm.canonicalize.v2f32(<2 x float> [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv <2 x float> splat (float 1.000000e+00), [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord <2 x float> [[X]], zeroinitializer +; CHECK-NEXT: [[X_CANON:%.*]] = select <2 x i1> [[ORD]], <2 x float> [[HARD_CANONICAL]], <2 x float> [[SOFT_CANONICAL]] +; CHECK-NEXT: ret <2 x float> [[X_CANON]] +; + %hard.canonical = call <2 x float> @llvm.canonicalize.v2f32(<2 x float> %x) + %soft.canonical = fdiv <2 x float> splat (float 1.0), %x + %ord = fcmp ord <2 x float> %x, zeroinitializer + %x.canon = select <2 x i1> %ord, <2 x float> %hard.canonical, <2 x float> %soft.canonical + ret <2 x float> %x.canon +} + +define <2 x float> @canonicalize_ieee_1_vec(<2 x float> %x) #0 { +; CHECK-LABEL: define <2 x float> @canonicalize_ieee_1_vec( +; CHECK-SAME: <2 x float> [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call <2 x float> @llvm.canonicalize.v2f32(<2 x float> [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv <2 x float> splat (float 1.000000e+00), [[X]] +; CHECK-NEXT: [[UNO:%.*]] = fcmp uno <2 x float> [[X]], zeroinitializer +; CHECK-NEXT: [[X_CANON:%.*]] = select <2 x i1> [[UNO]], <2 x float> [[SOFT_CANONICAL]], <2 x float> [[HARD_CANONICAL]] +; CHECK-NEXT: ret <2 x float> [[X_CANON]] +; + %hard.canonical = call <2 x float> @llvm.canonicalize.v2f32(<2 x float> %x) + %soft.canonical = fdiv <2 x float> splat (float 1.0), %x + %uno = fcmp uno <2 x float> %x, zeroinitializer + %x.canon = select <2 x i1> %uno, <2 x float> %soft.canonical, <2 x float> %hard.canonical + ret <2 x float> %x.canon +} + +define <2 x float> @canonicalize_ieee_0_vec_poison_elt(<2 x float> %x) #0 { +; CHECK-LABEL: define <2 x float> @canonicalize_ieee_0_vec_poison_elt( +; CHECK-SAME: <2 x float> [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call <2 x float> @llvm.canonicalize.v2f32(<2 x float> [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv <2 x float> , [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord <2 x float> [[X]], zeroinitializer +; CHECK-NEXT: [[X_CANON:%.*]] = select <2 x i1> [[ORD]], <2 x float> [[HARD_CANONICAL]], <2 x float> [[SOFT_CANONICAL]] +; CHECK-NEXT: ret <2 x float> [[X_CANON]] +; + %hard.canonical = call <2 x float> @llvm.canonicalize.v2f32(<2 x float> %x) + %soft.canonical = fdiv <2 x float> , %x + %ord = fcmp ord <2 x float> %x, zeroinitializer + %x.canon = select <2 x i1> %ord, <2 x float> %hard.canonical, <2 x float> %soft.canonical + ret <2 x float> %x.canon +} + +define <2 x float> @canonicalize_daz_0_vec(<2 x float> %x) #1 { +; CHECK-LABEL: define <2 x float> @canonicalize_daz_0_vec( +; CHECK-SAME: <2 x float> [[X:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call <2 x float> @llvm.canonicalize.v2f32(<2 x float> [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv <2 x float> splat (float 1.000000e+00), [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord <2 x float> [[X]], zeroinitializer +; CHECK-NEXT: [[X_CANON:%.*]] = select <2 x i1> [[ORD]], <2 x float> [[HARD_CANONICAL]], <2 x float> [[SOFT_CANONICAL]] +; CHECK-NEXT: ret <2 x float> [[X_CANON]] +; + %hard.canonical = call <2 x float> @llvm.canonicalize.v2f32(<2 x float> %x) + %soft.canonical = fdiv <2 x float> splat (float 1.0), %x + %ord = fcmp ord <2 x float> %x, zeroinitializer + %x.canon = select <2 x i1> %ord, <2 x float> %hard.canonical, <2 x float> %soft.canonical + ret <2 x float> %x.canon +} + +define <2 x float> @canonicalize_daz_1_vec(<2 x float> %x) #1 { +; CHECK-LABEL: define <2 x float> @canonicalize_daz_1_vec( +; CHECK-SAME: <2 x float> [[X:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call <2 x float> @llvm.canonicalize.v2f32(<2 x float> [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv <2 x float> splat (float 1.000000e+00), [[X]] +; CHECK-NEXT: [[UNO:%.*]] = fcmp uno <2 x float> [[X]], zeroinitializer +; CHECK-NEXT: [[X_CANON:%.*]] = select <2 x i1> [[UNO]], <2 x float> [[SOFT_CANONICAL]], <2 x float> [[HARD_CANONICAL]] +; CHECK-NEXT: ret <2 x float> [[X_CANON]] +; + %hard.canonical = call <2 x float> @llvm.canonicalize.v2f32(<2 x float> %x) + %soft.canonical = fdiv <2 x float> splat (float 1.0), %x + %uno = fcmp uno <2 x float> %x, zeroinitializer + %x.canon = select <2 x i1> %uno, <2 x float> %soft.canonical, <2 x float> %hard.canonical + ret <2 x float> %x.canon +} + +define bfloat @canonicalize_ieee_bf16(bfloat %x) #0 { +; CHECK-LABEL: define bfloat @canonicalize_ieee_bf16( +; CHECK-SAME: bfloat [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call bfloat @llvm.canonicalize.bf16(bfloat [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv bfloat 0xR3F80, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord bfloat [[X]], 0xR0000 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], bfloat [[HARD_CANONICAL]], bfloat [[SOFT_CANONICAL]] +; CHECK-NEXT: ret bfloat [[X_CANON]] +; + %hard.canonical = call bfloat @llvm.canonicalize.bf16(bfloat %x) + %soft.canonical = fdiv bfloat 1.0, %x + %ord = fcmp ord bfloat %x, 0.0 + %x.canon = select i1 %ord, bfloat %hard.canonical, bfloat %soft.canonical + ret bfloat %x.canon +} + +define half @canonicalize_ieee_f16(half %x) #0 { +; CHECK-LABEL: define half @canonicalize_ieee_f16( +; CHECK-SAME: half [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call half @llvm.canonicalize.f16(half [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv half 0xH3C00, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord half [[X]], 0xH0000 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], half [[HARD_CANONICAL]], half [[SOFT_CANONICAL]] +; CHECK-NEXT: ret half [[X_CANON]] +; + %hard.canonical = call half @llvm.canonicalize.f16(half %x) + %soft.canonical = fdiv half 1.0, %x + %ord = fcmp ord half %x, 0.0 + %x.canon = select i1 %ord, half %hard.canonical, half %soft.canonical + ret half %x.canon +} + +define double @canonicalize_ieee_f64(double %x) #0 { +; CHECK-LABEL: define double @canonicalize_ieee_f64( +; CHECK-SAME: double [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call double @llvm.canonicalize.f64(double [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv double 1.000000e+00, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord double [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], double [[HARD_CANONICAL]], double [[SOFT_CANONICAL]] +; CHECK-NEXT: ret double [[X_CANON]] +; + %hard.canonical = call double @llvm.canonicalize.f64(double %x) + %soft.canonical = fdiv double 1.0, %x + %ord = fcmp ord double %x, 0.0 + %x.canon = select i1 %ord, double %hard.canonical, double %soft.canonical + ret double %x.canon +} + +define fp128 @canonicalize_ieee_f128(fp128 %x) #0 { +; CHECK-LABEL: define fp128 @canonicalize_ieee_f128( +; CHECK-SAME: fp128 [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call fp128 @llvm.canonicalize.f128(fp128 [[X]]) +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord fp128 [[X]], 0xL00000000000000000000000000000000 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], fp128 [[HARD_CANONICAL]], fp128 [[X]] +; CHECK-NEXT: ret fp128 [[X_CANON]] +; + %hard.canonical = call fp128 @llvm.canonicalize.f128(fp128 %x) + %ord = fcmp ord fp128 %x, 0xL00000000000000000000000000000000 + %x.canon = select i1 %ord, fp128 %hard.canonical, fp128 %x + ret fp128 %x.canon +} + +; Negative test +define float @div_not_one(float %x) #0 { +; CHECK-LABEL: define float @div_not_one( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 2.000000e+00, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[SOFT_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 2.0, %x + %ord = fcmp ord float %x, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %soft.canonical + ret float %x.canon +} + +; Negative test +define float @not_fdiv(float %x) #0 { +; CHECK-LABEL: define float @not_fdiv( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fadd float [[X]], 1.000000e+00 +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[SOFT_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fadd float %x, 1.0 + %ord = fcmp ord float %x, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %soft.canonical + ret float %x.canon +} + +; Negative test +define float @fcmp_not_ord(float %x) #0 { +; CHECK-LABEL: define float @fcmp_not_ord( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp olt float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[SOFT_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 1.0, %x + %ord = fcmp olt float %x, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %soft.canonical + ret float %x.canon +} + +declare float @func(float) + +; Negative test +define float @not_canonicalize(float %x) #0 { +; CHECK-LABEL: define float @not_canonicalize( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[NOT_CANONICAL:%.*]] = call noundef float @func(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[NOT_CANONICAL]], float [[SOFT_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %not.canonical = call noundef float @func(float %x) + %soft.canonical = fdiv float 1.0, %x + %ord = fcmp ord float %x, 0.0 + %x.canon = select i1 %ord, float %not.canonical, float %soft.canonical + ret float %x.canon +} + +; Negative test +define float @compared_value_different(float %x, float %y) #0 { +; CHECK-LABEL: define float @compared_value_different( +; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[Y]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[SOFT_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 1.0, %x + %ord = fcmp ord float %y, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %soft.canonical + ret float %x.canon +} + +; Negative test +define float @fdiv_value_different_ieee(float %x, float %y) #0 { +; CHECK-LABEL: define float @fdiv_value_different_ieee( +; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[Y]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[SOFT_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 1.0, %y + %ord = fcmp ord float %x, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %soft.canonical + ret float %x.canon +} + +; Negative test +define float @fdiv_value_different_ieee_commute(float %x, float %y) #0 { +; CHECK-LABEL: define float @fdiv_value_different_ieee_commute( +; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[Y]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp uno float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[SOFT_CANONICAL]], float [[HARD_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 1.0, %y + %ord = fcmp uno float %x, 0.0 + %x.canon = select i1 %ord, float %soft.canonical, float %hard.canonical + ret float %x.canon +} + +; Negative test +define float @fdiv_value_different_daz(float %x, float %y) #1 { +; CHECK-LABEL: define float @fdiv_value_different_daz( +; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[Y]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[SOFT_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 1.0, %y + %ord = fcmp ord float %x, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %soft.canonical + ret float %x.canon +} + +; Negative test +define float @fdiv_value_different_daz_commute(float %x, float %y) #1 { +; CHECK-LABEL: define float @fdiv_value_different_daz_commute( +; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[Y]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp uno float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[SOFT_CANONICAL]], float [[HARD_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 1.0, %y + %ord = fcmp uno float %x, 0.0 + %x.canon = select i1 %ord, float %soft.canonical, float %hard.canonical + ret float %x.canon +} + +; Negative test +define float @fcmp_ord_not_constant(float %x, float %y) #0 { +; CHECK-LABEL: define float @fcmp_ord_not_constant( +; CHECK-SAME: float [[X:%.*]], float [[Y:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], [[Y]] +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[SOFT_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 1.0, %x + %ord = fcmp ord float %x, %y + %x.canon = select i1 %ord, float %hard.canonical, float %soft.canonical + ret float %x.canon +} + +; Negative test +define float @fcmp_ord_nan_constant(float %x) #0 { +; CHECK-LABEL: define float @fcmp_ord_nan_constant( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[X]] +; CHECK-NEXT: ret float [[SOFT_CANONICAL]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 1.0, %x + %ord = fcmp ord float %x, 0x7FF8000000000000 + %x.canon = select i1 %ord, float %hard.canonical, float %soft.canonical + ret float %x.canon +} + +define x86_fp80 @ignore_x86_fp80(x86_fp80 %x) #0 { +; CHECK-LABEL: define x86_fp80 @ignore_x86_fp80( +; CHECK-SAME: x86_fp80 [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call x86_fp80 @llvm.canonicalize.f80(x86_fp80 [[X]]) +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord x86_fp80 [[X]], 0xK00000000000000000000 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], x86_fp80 [[HARD_CANONICAL]], x86_fp80 [[X]] +; CHECK-NEXT: ret x86_fp80 [[X_CANON]] +; + %hard.canonical = call x86_fp80 @llvm.canonicalize.f80(x86_fp80 %x) + %ord = fcmp ord x86_fp80 %x, 0xK00000000000000000000 + %x.canon = select i1 %ord, x86_fp80 %hard.canonical, x86_fp80 %x + ret x86_fp80 %x.canon +} + +define ppc_fp128 @ignore_ppc_fp128(ppc_fp128 %x) #0 { +; CHECK-LABEL: define ppc_fp128 @ignore_ppc_fp128( +; CHECK-SAME: ppc_fp128 [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call ppc_fp128 @llvm.canonicalize.ppcf128(ppc_fp128 [[X]]) +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord ppc_fp128 [[X]], 0xM00000000000000000000000000000000 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], ppc_fp128 [[HARD_CANONICAL]], ppc_fp128 [[X]] +; CHECK-NEXT: ret ppc_fp128 [[X_CANON]] +; + %hard.canonical = call ppc_fp128 @llvm.canonicalize.ppcf128(ppc_fp128 %x) + %ord = fcmp ord ppc_fp128 %x, 0xM00000000000000000000000000000000 + %x.canon = select i1 %ord, ppc_fp128 %hard.canonical, ppc_fp128 %x + ret ppc_fp128 %x.canon +} + +; In IEEE mode there are no non-canonical values, we can drop the canonicalize. +define float @canonicalize_ieee_0_missing_noop(float %x) #0 { +; CHECK-LABEL: define float @canonicalize_ieee_0_missing_noop( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[X]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %ord = fcmp ord float %x, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %x + ret float %x.canon +} + +; In IEEE mode there are no non-canonical values, we can drop the +; canonicalize. +define float @canonicalize_ieee_1_missing_noop(float %x) #0 { +; CHECK-LABEL: define float @canonicalize_ieee_1_missing_noop( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[UNO:%.*]] = fcmp uno float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[UNO]], float [[X]], float [[HARD_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %uno = fcmp uno float %x, 0.0 + %x.canon = select i1 %uno, float %x, float %hard.canonical + ret float %x.canon +} + +; Negative test. This cannot fold to the canonicalize call, as it may +; change the payload and sign bit of %x in the nan case. +define float @canonicalize_da_0_missing_noop(float %x) #1 { +; CHECK-LABEL: define float @canonicalize_da_0_missing_noop( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[X]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %ord = fcmp ord float %x, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %x + ret float %x.canon +} + +; Negative test. This cannot fold to the canonicalize call, as it may +; change the payload and sign bit of %x in the nan case. +define float @canonicalize_daz_1_missing_noop(float %x) #1 { +; CHECK-LABEL: define float @canonicalize_daz_1_missing_noop( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[UNO:%.*]] = fcmp uno float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[UNO]], float [[X]], float [[HARD_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %uno = fcmp uno float %x, 0.0 + %x.canon = select i1 %uno, float %x, float %hard.canonical + ret float %x.canon +} + +; Either input or output is sufficient to require canonicalize +define float @canonicalize_only_ftz(float %x) "denormal-fp-math"="preserve-sign,ieee" { +; CHECK-LABEL: define float @canonicalize_only_ftz( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR3:[0-9]+]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[SOFT_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 1.0, %x + %ord = fcmp ord float %x, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %soft.canonical + ret float %x.canon +} + +; Either input or output is sufficient to require canonicalize +define float @canonicalize_only_daz(float %x) "denormal-fp-math"="ieee,preserve-sign" { +; CHECK-LABEL: define float @canonicalize_only_daz( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR4:[0-9]+]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[SOFT_CANONICAL:%.*]] = fdiv float 1.000000e+00, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[SOFT_CANONICAL]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %soft.canonical = fdiv float 1.0, %x + %ord = fcmp ord float %x, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %soft.canonical + ret float %x.canon +} + +; Both components must be IEEE to fully drop the canonicalize +define float @canonicalize_missing_noop_only_ftz(float %x) "denormal-fp-math"="preserve-sign,ieee" { +; CHECK-LABEL: define float @canonicalize_missing_noop_only_ftz( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR3]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[X]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %ord = fcmp ord float %x, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %x + ret float %x.canon +} + +; Both components must be IEEE to fully drop the canonicalize +define float @canonicalize_missing_noop_only_daz(float %x) "denormal-fp-math"="ieee,preserve-sign" { +; CHECK-LABEL: define float @canonicalize_missing_noop_only_daz( +; CHECK-SAME: float [[X:%.*]]) #[[ATTR4]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call float @llvm.canonicalize.f32(float [[X]]) +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord float [[X]], 0.000000e+00 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], float [[HARD_CANONICAL]], float [[X]] +; CHECK-NEXT: ret float [[X_CANON]] +; + %hard.canonical = call float @llvm.canonicalize.f32(float %x) + %ord = fcmp ord float %x, 0.0 + %x.canon = select i1 %ord, float %hard.canonical, float %x + ret float %x.canon +} + +define half @independent_hands_canonicalize_ieee_0(half noundef %x, half %y) #0 { +; CHECK-LABEL: define half @independent_hands_canonicalize_ieee_0( +; CHECK-SAME: half noundef [[X:%.*]], half [[Y:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call half @llvm.canonicalize.f16(half [[X]]) +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord half [[X]], 0xH0000 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], half [[HARD_CANONICAL]], half [[Y]] +; CHECK-NEXT: ret half [[X_CANON]] +; + %hard.canonical = call half @llvm.canonicalize.f16(half %x) + %ord = fcmp ord half %x, 0.0 + %x.canon = select i1 %ord, half %hard.canonical, half %y + ret half %x.canon +} + +define half @independent_hands_canonicalize_ieee_1(half noundef %x, half %y) #0 { +; CHECK-LABEL: define half @independent_hands_canonicalize_ieee_1( +; CHECK-SAME: half noundef [[X:%.*]], half [[Y:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call half @llvm.canonicalize.f16(half [[X]]) +; CHECK-NEXT: [[UNO:%.*]] = fcmp uno half [[X]], 0xH0000 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[UNO]], half [[Y]], half [[HARD_CANONICAL]] +; CHECK-NEXT: ret half [[X_CANON]] +; + %hard.canonical = call half @llvm.canonicalize.f16(half %x) + %uno = fcmp uno half %x, 0.0 + %x.canon = select i1 %uno, half %y, half %hard.canonical + ret half %x.canon +} + +define half @independent_hands_fdiv_ieee_0(half noundef %x, half %y) #0 { +; CHECK-LABEL: define half @independent_hands_fdiv_ieee_0( +; CHECK-SAME: half noundef [[X:%.*]], half [[Y:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[RCP:%.*]] = fdiv half 0xH3C00, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord half [[X]], 0xH0000 +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[ORD]], half [[Y]], half [[RCP]] +; CHECK-NEXT: ret half [[SEL]] +; + %rcp = fdiv half 1.0, %x + %ord = fcmp ord half %x, 0.0 + %sel = select i1 %ord, half %y, half %rcp + ret half %sel +} + +define half @independent_hands_fdiv_ieee_1(half noundef %x, half %y) #0 { +; CHECK-LABEL: define half @independent_hands_fdiv_ieee_1( +; CHECK-SAME: half noundef [[X:%.*]], half [[Y:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[RCP:%.*]] = fdiv half 0xH3C00, [[X]] +; CHECK-NEXT: [[UNO:%.*]] = fcmp uno half [[X]], 0xH0000 +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[UNO]], half [[RCP]], half [[Y]] +; CHECK-NEXT: ret half [[SEL]] +; + %rcp = fdiv half 1.0, %x + %uno = fcmp uno half %x, 0.0 + %sel = select i1 %uno, half %rcp, half %y + ret half %sel +} + +define half @independent_hands_canonicalize_daz_0(half noundef %x, half %y) #1 { +; CHECK-LABEL: define half @independent_hands_canonicalize_daz_0( +; CHECK-SAME: half noundef [[X:%.*]], half [[Y:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call half @llvm.canonicalize.f16(half [[X]]) +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord half [[X]], 0xH0000 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], half [[HARD_CANONICAL]], half [[Y]] +; CHECK-NEXT: ret half [[X_CANON]] +; + %hard.canonical = call half @llvm.canonicalize.f16(half %x) + %ord = fcmp ord half %x, 0.0 + %x.canon = select i1 %ord, half %hard.canonical, half %y + ret half %x.canon +} + +define half @independent_hands_canonicalize_daz_1(half noundef %x, half %y) #1 { +; CHECK-LABEL: define half @independent_hands_canonicalize_daz_1( +; CHECK-SAME: half noundef [[X:%.*]], half [[Y:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call half @llvm.canonicalize.f16(half [[X]]) +; CHECK-NEXT: [[UNO:%.*]] = fcmp uno half [[X]], 0xH0000 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[UNO]], half [[Y]], half [[HARD_CANONICAL]] +; CHECK-NEXT: ret half [[X_CANON]] +; + %hard.canonical = call half @llvm.canonicalize.f16(half %x) + %uno = fcmp uno half %x, 0.0 + %x.canon = select i1 %uno, half %y, half %hard.canonical + ret half %x.canon +} + +define half @independent_hands_fdiv_daz_0(half noundef %x, half %y) #1 { +; CHECK-LABEL: define half @independent_hands_fdiv_daz_0( +; CHECK-SAME: half noundef [[X:%.*]], half [[Y:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[RCP:%.*]] = fdiv half 0xH3C00, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord half [[X]], 0xH0000 +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[ORD]], half [[Y]], half [[RCP]] +; CHECK-NEXT: ret half [[SEL]] +; + %rcp = fdiv half 1.0, %x + %ord = fcmp ord half %x, 0.0 + %sel = select i1 %ord, half %y, half %rcp + ret half %sel +} + +define half @independent_hands_fdiv_daz_1(half noundef %x, half %y) #1 { +; CHECK-LABEL: define half @independent_hands_fdiv_daz_1( +; CHECK-SAME: half noundef [[X:%.*]], half [[Y:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[RCP:%.*]] = fdiv half 0xH3C00, [[X]] +; CHECK-NEXT: [[UNO:%.*]] = fcmp uno half [[X]], 0xH0000 +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[UNO]], half [[RCP]], half [[Y]] +; CHECK-NEXT: ret half [[SEL]] +; + %rcp = fdiv half 1.0, %x + %uno = fcmp uno half %x, 0.0 + %sel = select i1 %uno, half %rcp, half %y + ret half %sel +} + +define x86_fp80 @independent_hands_canonicalize_ieee_0_x86_fp80(x86_fp80 noundef %x, x86_fp80 %y) #0 { +; CHECK-LABEL: define x86_fp80 @independent_hands_canonicalize_ieee_0_x86_fp80( +; CHECK-SAME: x86_fp80 noundef [[X:%.*]], x86_fp80 [[Y:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call x86_fp80 @llvm.canonicalize.f80(x86_fp80 [[X]]) +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord x86_fp80 [[X]], 0xK00000000000000000000 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], x86_fp80 [[HARD_CANONICAL]], x86_fp80 [[Y]] +; CHECK-NEXT: ret x86_fp80 [[X_CANON]] +; + %hard.canonical = call x86_fp80 @llvm.canonicalize.f80(x86_fp80 %x) + %ord = fcmp ord x86_fp80 %x, 0xK00000000000000000000 + %x.canon = select i1 %ord, x86_fp80 %hard.canonical, x86_fp80 %y + ret x86_fp80 %x.canon +} + +define x86_fp80 @independent_hands_fdiv_ieee_0_x86_fp80(x86_fp80 noundef %x, x86_fp80 %y) #0 { +; CHECK-LABEL: define x86_fp80 @independent_hands_fdiv_ieee_0_x86_fp80( +; CHECK-SAME: x86_fp80 noundef [[X:%.*]], x86_fp80 [[Y:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[RCP:%.*]] = fdiv x86_fp80 0xK3FFF8000000000000000, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord x86_fp80 [[X]], 0xK00000000000000000000 +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[ORD]], x86_fp80 [[Y]], x86_fp80 [[RCP]] +; CHECK-NEXT: ret x86_fp80 [[SEL]] +; + %rcp = fdiv x86_fp80 0xK3FFF8000000000000000, %x + %ord = fcmp ord x86_fp80 %x, 0xK00000000000000000000 + %sel = select i1 %ord, x86_fp80 %y, x86_fp80 %rcp + ret x86_fp80 %sel +} + + +; Negative test, needs noundef +define half @independent_hands_canonicalize_ieee_0_maybe_undef(half %x, half %y) #0 { +; CHECK-LABEL: define half @independent_hands_canonicalize_ieee_0_maybe_undef( +; CHECK-SAME: half [[X:%.*]], half [[Y:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call half @llvm.canonicalize.f16(half [[X]]) +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord half [[X]], 0xH0000 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[ORD]], half [[HARD_CANONICAL]], half [[Y]] +; CHECK-NEXT: ret half [[X_CANON]] +; + %hard.canonical = call half @llvm.canonicalize.f16(half %x) + %ord = fcmp ord half %x, 0.0 + %x.canon = select i1 %ord, half %hard.canonical, half %y + ret half %x.canon +} + +; Negative test, needs noundef +define half @independent_hands_canonicalize_ieee_1_maybe_undef(half %x, half %y) #0 { +; CHECK-LABEL: define half @independent_hands_canonicalize_ieee_1_maybe_undef( +; CHECK-SAME: half [[X:%.*]], half [[Y:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[HARD_CANONICAL:%.*]] = call half @llvm.canonicalize.f16(half [[X]]) +; CHECK-NEXT: [[UNO:%.*]] = fcmp uno half [[X]], 0xH0000 +; CHECK-NEXT: [[X_CANON:%.*]] = select i1 [[UNO]], half [[Y]], half [[HARD_CANONICAL]] +; CHECK-NEXT: ret half [[X_CANON]] +; + %hard.canonical = call half @llvm.canonicalize.f16(half %x) + %uno = fcmp uno half %x, 0.0 + %x.canon = select i1 %uno, half %y, half %hard.canonical + ret half %x.canon +} + +; Negative test, needs noundef +define half @independent_hands_fdiv_ieee_0_maybe_undef(half %x, half %y) #0 { +; CHECK-LABEL: define half @independent_hands_fdiv_ieee_0_maybe_undef( +; CHECK-SAME: half [[X:%.*]], half [[Y:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[RCP:%.*]] = fdiv half 0xH3C00, [[X]] +; CHECK-NEXT: [[ORD:%.*]] = fcmp ord half [[X]], 0xH0000 +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[ORD]], half [[Y]], half [[RCP]] +; CHECK-NEXT: ret half [[SEL]] +; + %rcp = fdiv half 1.0, %x + %ord = fcmp ord half %x, 0.0 + %sel = select i1 %ord, half %y, half %rcp + ret half %sel +} + +; Negative test, needs noundef +define half @independent_hands_fdiv_ieee_1_maybe_undef(half %x, half %y) #0 { +; CHECK-LABEL: define half @independent_hands_fdiv_ieee_1_maybe_undef( +; CHECK-SAME: half [[X:%.*]], half [[Y:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[RCP:%.*]] = fdiv half 0xH3C00, [[X]] +; CHECK-NEXT: [[UNO:%.*]] = fcmp uno half [[X]], 0xH0000 +; CHECK-NEXT: [[SEL:%.*]] = select i1 [[UNO]], half [[RCP]], half [[Y]] +; CHECK-NEXT: ret half [[SEL]] +; + %rcp = fdiv half 1.0, %x + %uno = fcmp uno half %x, 0.0 + %sel = select i1 %uno, half %rcp, half %y + ret half %sel +} + +attributes #0 = { "denormal-fp-math"="ieee,ieee" } +attributes #1 = { "denormal-fp-math"="preserve-sign,preserve-sign" } +attributes #2 = { "denormal-fp-math"="dynamic,dynamic" }