Skip to content

Conversation

@arsenm
Copy link
Contributor

@arsenm arsenm commented Dec 21, 2025

No description provided.

@arsenm arsenm added the floating-point Floating-point math label Dec 21, 2025 — with Graphite App
Copy link
Contributor Author

arsenm commented Dec 21, 2025

@arsenm arsenm added the llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes label Dec 21, 2025 — with Graphite App
@arsenm arsenm marked this pull request as ready for review December 21, 2025 13:21
@llvmbot
Copy link
Member

llvmbot commented Dec 21, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Matt Arsenault (arsenm)

Changes

Patch is 29.80 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/173188.diff

1 Files Affected:

  • (added) llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-canonicalize.ll (+609)
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-canonicalize.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-canonicalize.ll
new file mode 100644
index 0000000000000..20de837d14367
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-canonicalize.ll
@@ -0,0 +1,609 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+declare nofpclass(norm inf nan nzero nsub) float @returns_pzero_psub()
+declare nofpclass(norm inf nan zero nsub) float @returns_psub()
+declare nofpclass(norm inf nan zero psub) float @returns_nsub()
+declare nofpclass(norm inf nan zero) float @returns_sub()
+declare nofpclass(norm inf nan zero) <2 x float> @returns_sub_vec()
+declare nofpclass(norm inf nan) float @returns_sub_zero()
+declare nofpclass(qnan) float @returns_maybe_snan()
+declare nofpclass(qnan zero norm sub inf) float @returns_snan()
+declare nofpclass(inf nan zero) float @returns_sub_norm()
+declare nofpclass(nnorm inf nan zero nsub) float @returns_psub_pnorm()
+declare nofpclass(pnorm inf nan zero psub) float @returns_nsub_nnorm()
+
+; Does not require special handling of canonicalize.
+define nofpclass(inf) float @ret_nofpclass_inf__canonicalize_select_pinf_rhs(i1 %cond, float %x) {
+; CHECK-LABEL: define nofpclass(inf) float @ret_nofpclass_inf__canonicalize_select_pinf_rhs(
+; CHECK-SAME: i1 [[COND:%.*]], float [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.canonicalize.f32(float [[X]])
+; CHECK-NEXT:    ret float [[TMP1]]
+;
+  %select = select i1 %cond, float %x, float 0x7FF0000000000000
+  %canon = call float @llvm.canonicalize.f32(float %select)
+  ret float %canon
+}
+
+define nofpclass(nan pzero) float @ret_nofpclass_nan_pzero__canonicalize_select_psub_pnorm_daz(float %x, i1 %cond) #0 {
+; CHECK-LABEL: define nofpclass(nan pzero) float @ret_nofpclass_nan_pzero__canonicalize_select_psub_pnorm_daz(
+; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:    [[PSUB:%.*]] = call float @returns_psub_pnorm()
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[X]], float [[PSUB]]
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[SELECT]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %psub = call float @returns_psub_pnorm()
+  %select = select i1 %cond, float %x, float %psub
+  %canon = call float @llvm.canonicalize.f32(float %select)
+  ret float %canon
+}
+
+define nofpclass(nan nzero) float @ret_nofpclass_nan_nzero__canonicalize_select_nsub_nnorm_daz(float %x, i1 %cond) #0 {
+; CHECK-LABEL: define nofpclass(nan nzero) float @ret_nofpclass_nan_nzero__canonicalize_select_nsub_nnorm_daz(
+; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[NSUB:%.*]] = call float @returns_nsub_nnorm()
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[X]], float [[NSUB]]
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[SELECT]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %nsub = call float @returns_nsub_nnorm()
+  %select = select i1 %cond, float %x, float %nsub
+  %canon = call float @llvm.canonicalize.f32(float %select)
+  ret float %canon
+}
+
+define nofpclass(nan zero) float @ret_nofpclass_nan_zero__canonicalize_select_sub_norm_daz(float %x, i1 %cond) #0 {
+; CHECK-LABEL: define nofpclass(nan zero) float @ret_nofpclass_nan_zero__canonicalize_select_sub_norm_daz(
+; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[SUB:%.*]] = call float @returns_sub_norm()
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[X]], float [[SUB]]
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[SELECT]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %sub = call float @returns_sub_norm()
+  %select = select i1 %cond, float %x, float %sub
+  %canon = call float @llvm.canonicalize.f32(float %select)
+  ret float %canon
+}
+
+define nofpclass(nan pzero) float @ret_nofpclass_nan_pzero__canonicalize_select_psub_daz(float %x, i1 %cond) #0 {
+; CHECK-LABEL: define nofpclass(nan pzero) float @ret_nofpclass_nan_pzero__canonicalize_select_psub_daz(
+; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[PSUB:%.*]] = call float @returns_psub()
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[X]], float [[PSUB]]
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[SELECT]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %psub = call float @returns_psub()
+  %select = select i1 %cond, float %x, float %psub
+  %canon = call float @llvm.canonicalize.f32(float %select)
+  ret float %canon
+}
+
+define nofpclass(nan nzero) float @ret_nofpclass_nan_nzero__canonicalize_select_nsub_daz(float %x, i1 %cond) #0 {
+; CHECK-LABEL: define nofpclass(nan nzero) float @ret_nofpclass_nan_nzero__canonicalize_select_nsub_daz(
+; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[NSUB:%.*]] = call float @returns_nsub()
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[X]], float [[NSUB]]
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[SELECT]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %nsub = call float @returns_nsub()
+  %select = select i1 %cond, float %x, float %nsub
+  %canon = call float @llvm.canonicalize.f32(float %select)
+  ret float %canon
+}
+
+define nofpclass(nan zero) float @ret_nofpclass_nan_zero__canonicalize_select_sub_daz(float %x, i1 %cond) #0 {
+; CHECK-LABEL: define nofpclass(nan zero) float @ret_nofpclass_nan_zero__canonicalize_select_sub_daz(
+; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[SUB:%.*]] = call float @returns_sub()
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[X]], float [[SUB]]
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[SELECT]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %sub = call float @returns_sub()
+  %select = select i1 %cond, float %x, float %sub
+  %canon = call float @llvm.canonicalize.f32(float %select)
+  ret float %canon
+}
+
+define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_only_psub_ieee() {
+; CHECK-LABEL: define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_only_psub_ieee() {
+; CHECK-NEXT:    [[PSUB:%.*]] = call float @returns_psub()
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[PSUB]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %psub = call float @returns_psub()
+  %canon = call float @llvm.canonicalize.f32(float %psub)
+  ret float %canon
+}
+
+define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_only_nsub_ieee() {
+; CHECK-LABEL: define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_only_nsub_ieee() {
+; CHECK-NEXT:    [[NSUB:%.*]] = call float @returns_nsub()
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[NSUB]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %nsub = call float @returns_nsub()
+  %canon = call float @llvm.canonicalize.f32(float %nsub)
+  ret float %canon
+}
+
+define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_only_sub_ieee() {
+; CHECK-LABEL: define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_only_sub_ieee() {
+; CHECK-NEXT:    [[SUB:%.*]] = call float @returns_sub()
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[SUB]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %sub = call float @returns_sub()
+  %canon = call float @llvm.canonicalize.f32(float %sub)
+  ret float %canon
+}
+
+define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_only_psub_daz() #0 {
+; CHECK-LABEL: define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_only_psub_daz(
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT:    [[PSUB:%.*]] = call float @returns_psub()
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[PSUB]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %psub = call float @returns_psub()
+  %canon = call float @llvm.canonicalize.f32(float %psub)
+  ret float %canon
+}
+
+define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_only_nsub_daz() #0 {
+; CHECK-LABEL: define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_only_nsub_daz(
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT:    [[NSUB:%.*]] = call float @returns_nsub()
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[NSUB]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %nsub = call float @returns_nsub()
+  %canon = call float @llvm.canonicalize.f32(float %nsub)
+  ret float %canon
+}
+
+define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_only_sub_daz() #0 {
+; CHECK-LABEL: define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_only_sub_daz(
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT:    [[SUB:%.*]] = call float @returns_sub()
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[SUB]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %sub = call float @returns_sub()
+  %canon = call float @llvm.canonicalize.f32(float %sub)
+  ret float %canon
+}
+
+define nofpclass(zero) <2 x float> @ret_nofpclass_zero__canonicalize_daz_vec(<2 x float> %x, <2 x i1> %cond) #0 {
+; CHECK-LABEL: define nofpclass(zero) <2 x float> @ret_nofpclass_zero__canonicalize_daz_vec(
+; CHECK-SAME: <2 x float> [[X:%.*]], <2 x i1> [[COND:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[SUB:%.*]] = call <2 x float> @returns_sub_vec()
+; CHECK-NEXT:    [[SELECT:%.*]] = select <2 x i1> [[COND]], <2 x float> [[X]], <2 x float> [[SUB]]
+; CHECK-NEXT:    [[CANON:%.*]] = call <2 x float> @llvm.canonicalize.v2f32(<2 x float> [[SELECT]])
+; CHECK-NEXT:    ret <2 x float> [[CANON]]
+;
+  %sub = call <2 x float> @returns_sub_vec()
+  %select = select <2 x i1> %cond, <2 x float> %x, <2 x float> %sub
+  %canon = call <2 x float> @llvm.canonicalize.v2f32(<2 x float> %select)
+  ret <2 x float> %canon
+}
+
+define nofpclass(zero sub) float @ret_nofpclass_sub_zero__canonicalize_daz(float %x, i1 %cond) #0 {
+; CHECK-LABEL: define nofpclass(zero sub) float @ret_nofpclass_sub_zero__canonicalize_daz(
+; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[SUB_OR_ZERO:%.*]] = call float @returns_sub_zero()
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[X]], float [[SUB_OR_ZERO]]
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[SELECT]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %sub_or_zero = call float @returns_sub_zero()
+  %select = select i1 %cond, float %x, float %sub_or_zero
+  %canon = call float @llvm.canonicalize.f32(float %select)
+  ret float %canon
+}
+
+define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_ieee(float %unknown) {
+; CHECK-LABEL: define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_ieee(
+; CHECK-SAME: float [[UNKNOWN:%.*]]) {
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[UNKNOWN]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %canon = call float @llvm.canonicalize.f32(float %unknown)
+  ret float %canon
+}
+
+define nofpclass(qnan) float @ret_nofpclass_qnan__canonicalize_ieee(float %unknown) {
+; CHECK-LABEL: define nofpclass(qnan) float @ret_nofpclass_qnan__canonicalize_ieee(
+; CHECK-SAME: float [[UNKNOWN:%.*]]) {
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[UNKNOWN]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %canon = call float @llvm.canonicalize.f32(float %unknown)
+  ret float %canon
+}
+
+define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_daz(float %unknown) #0 {
+; CHECK-LABEL: define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_daz(
+; CHECK-SAME: float [[UNKNOWN:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[UNKNOWN]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %canon = call float @llvm.canonicalize.f32(float %unknown)
+  ret float %canon
+}
+
+define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_dynamic(float %unknown) #1 {
+; CHECK-LABEL: define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_dynamic(
+; CHECK-SAME: float [[UNKNOWN:%.*]]) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[UNKNOWN]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %canon = call float @llvm.canonicalize.f32(float %unknown)
+  ret float %canon
+}
+
+; Only snan not sufficent to strip canonicalize
+define nofpclass(snan) float @ret_nofpclass_snan__canonicalize_ieee(float %unknown) {
+; CHECK-LABEL: define nofpclass(snan) float @ret_nofpclass_snan__canonicalize_ieee(
+; CHECK-SAME: float [[UNKNOWN:%.*]]) {
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[UNKNOWN]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %canon = call float @llvm.canonicalize.f32(float %unknown)
+  ret float %canon
+}
+
+; Only qnan not sufficent to strip canonicalize
+define nofpclass(qnan) float @ret_nofpclass_anan__canonicalize_ieee(float %unknown) {
+; CHECK-LABEL: define nofpclass(qnan) float @ret_nofpclass_anan__canonicalize_ieee(
+; CHECK-SAME: float [[UNKNOWN:%.*]]) {
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[UNKNOWN]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %canon = call float @llvm.canonicalize.f32(float %unknown)
+  ret float %canon
+}
+
+; Ignore fp80, leave canonicalize.
+define nofpclass(nan) x86_fp80 @ret_nofpclass_zero_sub_canonicalize_fp80_daz(x86_fp80 %unknown) {
+; CHECK-LABEL: define nofpclass(nan) x86_fp80 @ret_nofpclass_zero_sub_canonicalize_fp80_daz(
+; CHECK-SAME: x86_fp80 [[UNKNOWN:%.*]]) {
+; CHECK-NEXT:    [[CANON:%.*]] = call x86_fp80 @llvm.canonicalize.f80(x86_fp80 [[UNKNOWN]])
+; CHECK-NEXT:    ret x86_fp80 [[CANON]]
+;
+  %canon = call x86_fp80 @llvm.canonicalize.f80(x86_fp80 %unknown)
+  ret x86_fp80 %canon
+}
+
+define nofpclass(nan sub) float @ret_nofpclass_nan_sub__canonicalize_dynamic(float %unknown) #1 {
+; CHECK-LABEL: define nofpclass(nan sub) float @ret_nofpclass_nan_sub__canonicalize_dynamic(
+; CHECK-SAME: float [[UNKNOWN:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[UNKNOWN]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %canon = call float @llvm.canonicalize.f32(float %unknown)
+  ret float %canon
+}
+
+; Cannot do anything. No signaling nan result doesn't tell us anything.
+define nofpclass(snan) float @ret_nofpclass_snan__canonicalize_select_unknown_or_snan(float %x, i1 %cond) {
+; CHECK-LABEL: define nofpclass(snan) float @ret_nofpclass_snan__canonicalize_select_unknown_or_snan(
+; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[SNAN:%.*]] = call float @returns_snan()
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[X]], float [[SNAN]]
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[SELECT]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %snan = call float @returns_snan()
+  %select = select i1 %cond, float %x, float %snan
+  %canon = call float @llvm.canonicalize.f32(float %select)
+  ret float %canon
+}
+
+; Fold to canonicalize of %x. The result cannot be a qnan, so it
+; cannot be the signaling nan select result.
+define nofpclass(qnan) float @ret_nofpclass_qnan__canonicalize_select_unknown_or_snan(float %x, i1 %cond) {
+; CHECK-LABEL: define nofpclass(qnan) float @ret_nofpclass_qnan__canonicalize_select_unknown_or_snan(
+; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[SNAN:%.*]] = call float @returns_snan()
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[X]], float [[SNAN]]
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[SELECT]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %snan = call float @returns_snan()
+  %select = select i1 %cond, float %x, float %snan
+  %canon = call float @llvm.canonicalize.f32(float %select)
+  ret float %canon
+}
+
+; Can fold to direct use of %x
+define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_select_unknown_or_snan(float %x, i1 %cond) {
+; CHECK-LABEL: define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_select_unknown_or_snan(
+; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[SNAN:%.*]] = call float @returns_snan()
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[X]], float [[SNAN]]
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[SELECT]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %snan = call float @returns_snan()
+  %select = select i1 %cond, float %x, float %snan
+  %canon = call float @llvm.canonicalize.f32(float %select)
+  ret float %canon
+}
+
+define nofpclass(zero) float @ret_nofpclass_zero_nnan_flag__canonicalize_select_unknown_or_snan(float %x, i1 %cond) {
+; CHECK-LABEL: define nofpclass(zero) float @ret_nofpclass_zero_nnan_flag__canonicalize_select_unknown_or_snan(
+; CHECK-SAME: float [[X:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[SNAN:%.*]] = call float @returns_snan()
+; CHECK-NEXT:    [[SELECT:%.*]] = select i1 [[COND]], float [[X]], float [[SNAN]]
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[SELECT]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %snan = call float @returns_snan()
+  %select = select i1 %cond, float %x, float %snan
+  %canon = call float @llvm.canonicalize.f32(float %select)
+  ret float %canon
+}
+
+; With IEEE handling the canonicalize can be dropped.
+;
+; FIXME: This doesn't really require the nofpclass on the return, but
+; we don't attempt SimplifyDemandedFPClass without it.
+define nofpclass(zero) float @ret_nofpclass_zero__canonicalize_nnan_src_ieee(float nofpclass(nan) %x, i1 %cond) {
+; CHECK-LABEL: define nofpclass(zero) float @ret_nofpclass_zero__canonicalize_nnan_src_ieee(
+; CHECK-SAME: float nofpclass(nan) [[X:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[X]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %canon = call float @llvm.canonicalize.f32(float %x)
+  ret float %canon
+}
+
+; Must preserve canonicalize with DAZ
+define nofpclass(zero) float @ret_nofpclass_zero__canonicalize_nnan_src_daz(float nofpclass(nan) %x, i1 %cond) #0 {
+; CHECK-LABEL: define nofpclass(zero) float @ret_nofpclass_zero__canonicalize_nnan_src_daz(
+; CHECK-SAME: float nofpclass(nan) [[X:%.*]], i1 [[COND:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[X]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %canon = call float @llvm.canonicalize.f32(float %x)
+  ret float %canon
+}
+
+define nofpclass(zero) float @ret_nofpclass_zero__canonicalize_no_sub_src_daz(float nofpclass(sub) %x, i1 %cond) #0 {
+; CHECK-LABEL: define nofpclass(zero) float @ret_nofpclass_zero__canonicalize_no_sub_src_daz(
+; CHECK-SAME: float nofpclass(sub) [[X:%.*]], i1 [[COND:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[X]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %canon = call float @llvm.canonicalize.f32(float %x)
+  ret float %canon
+}
+
+define nofpclass(zero) float @ret_nofpclass_zero__canonicalize_no_sub_no_nan_src_daz(float nofpclass(nan sub) %x, i1 %cond) #0 {
+; CHECK-LABEL: define nofpclass(zero) float @ret_nofpclass_zero__canonicalize_no_sub_no_nan_src_daz(
+; CHECK-SAME: float nofpclass(nan sub) [[X:%.*]], i1 [[COND:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[X]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %canon = call float @llvm.canonicalize.f32(float %x)
+  ret float %canon
+}
+
+define nofpclass(zero) float @ret_nofpclass_zero__canonicalize_no_sub_no_nan_src_dynamic(float nofpclass(nan sub) %x, i1 %cond) #1 {
+; CHECK-LABEL: define nofpclass(zero) float @ret_nofpclass_zero__canonicalize_no_sub_no_nan_src_dynamic(
+; CHECK-SAME: float nofpclass(nan sub) [[X:%.*]], i1 [[COND:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CANON:%.*]] = call float @llvm.canonicalize.f32(float [[X]])
+; CHECK-NEXT:    ret float [[CANON]]
+;
+  %canon = call float @llvm.canonicalize.f32(float %x)
+  ret float %canon
+}
+
+; Input cannot be subnormal and result cannot be nan, so canonicalize is droppable
+define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_no_sub_src_daz(float nofpclass(sub) %x, i1 %cond) #0 {
+; CHECK-LABEL: define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_no_sub_src_daz(
+; CHECK-SAME: fl...
[truncated]

@github-actions
Copy link

github-actions bot commented Dec 21, 2025

🐧 Linux x64 Test Results

  • 187750 tests passed
  • 4985 tests skipped

✅ The build succeeded and all tests passed.

@github-actions
Copy link

github-actions bot commented Dec 21, 2025

🪟 Windows x64 Test Results

  • 128844 tests passed
  • 2838 tests skipped

✅ The build succeeded and all tests passed.

@arsenm arsenm force-pushed the users/arsenm/instcombine/add-baseline-test-simplify-demanded-fpclass-canonicalize branch from 06ef509 to fa6f411 Compare December 21, 2025 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

floating-point Floating-point math llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants