[llvm] InstCombine: Add baseline tests for improved copysign handling (PR #175026)
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Thu Jan 8 09:03:57 PST 2026
https://github.com/arsenm created https://github.com/llvm/llvm-project/pull/175026
InstCombine: Add baseline tests for improved copysign handling
InstCombine: Improve SimplifyDemandedFPClass copysign handling
Recognize cases where the signbit is known.
>From e7fd5d7c11969ab5dbdc3bae6a69e2bd9963323d Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Thu, 8 Jan 2026 14:38:25 +0100
Subject: [PATCH 1/2] InstCombine: Add baseline tests for improved copysign
handling
---
.../InstCombine/simplify-demanded-fpclass.ll | 64 +++++++++++++++++++
1 file changed, 64 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass.ll
index 33931ab29fbd9..f6dc13ef409ac 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass.ll
@@ -849,6 +849,70 @@ define nofpclass(nan pinf pnorm psub pzero) float @ret_nofpclass_no_positives_no
ret float %copysign
}
+define nofpclass(snan) float @copysign_nnan_sign_known_positive_or_nan(float %x, float nofpclass(ninf nnorm nsub nzero) %always.positive.or.nan) {
+; CHECK-LABEL: define nofpclass(snan) float @copysign_nnan_sign_known_positive_or_nan
+; CHECK-SAME: (float [[X:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ALWAYS_POSITIVE_OR_NAN:%.*]]) {
+; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.copysign.f32(float [[X]], float [[ALWAYS_POSITIVE_OR_NAN]])
+; CHECK-NEXT: ret float [[COPYSIGN]]
+;
+ %copysign = call nnan float @llvm.copysign.f32(float %x, float %always.positive.or.nan)
+ ret float %copysign
+}
+
+define nofpclass(snan) float @copysign_nnan_sign_known_negative_or_nan(float %x, float nofpclass(pinf pnorm psub pzero) %always.negative.or.nan) {
+; CHECK-LABEL: define nofpclass(snan) float @copysign_nnan_sign_known_negative_or_nan
+; CHECK-SAME: (float [[X:%.*]], float nofpclass(pinf pzero psub pnorm) [[ALWAYS_NEGATIVE_OR_NAN:%.*]]) {
+; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.copysign.f32(float [[X]], float [[ALWAYS_NEGATIVE_OR_NAN]])
+; CHECK-NEXT: ret float [[COPYSIGN]]
+;
+ %copysign = call nnan float @llvm.copysign.f32(float %x, float %always.negative.or.nan)
+ ret float %copysign
+}
+
+; Fold out copysign
+define nofpclass(snan) float @copysign_nnan__known_positive__sign_known_positive_or_nan(float nofpclass(ninf nnorm nsub nzero) %known.positive, float nofpclass(ninf nnorm nsub nzero) %always.positive.or.nan) {
+; CHECK-LABEL: define nofpclass(snan) float @copysign_nnan__known_positive__sign_known_positive_or_nan
+; CHECK-SAME: (float nofpclass(ninf nzero nsub nnorm) [[KNOWN_POSITIVE:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ALWAYS_POSITIVE_OR_NAN:%.*]]) {
+; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.copysign.f32(float [[KNOWN_POSITIVE]], float [[ALWAYS_POSITIVE_OR_NAN]])
+; CHECK-NEXT: ret float [[COPYSIGN]]
+;
+ %copysign = call nnan float @llvm.copysign.f32(float %known.positive, float %always.positive.or.nan)
+ ret float %copysign
+}
+
+; Fold out copysign
+define nofpclass(snan) float @copysign_nnan__known_negative__sign_known_negative_or_nan(float nofpclass(pinf pnorm psub pzero) %known.negative, float nofpclass(pinf pnorm psub pzero) %always.negative.or.nan) {
+; CHECK-LABEL: define nofpclass(snan) float @copysign_nnan__known_negative__sign_known_negative_or_nan
+; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[KNOWN_NEGATIVE:%.*]], float nofpclass(pinf pzero psub pnorm) [[ALWAYS_NEGATIVE_OR_NAN:%.*]]) {
+; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.copysign.f32(float [[KNOWN_NEGATIVE]], float [[ALWAYS_NEGATIVE_OR_NAN]])
+; CHECK-NEXT: ret float [[COPYSIGN]]
+;
+ %copysign = call nnan float @llvm.copysign.f32(float %known.negative, float %always.negative.or.nan)
+ ret float %copysign
+}
+
+; Convert to fabs
+define nofpclass(snan) float @copysign_nnan__known_negative__sign_known_positive_or_nan(float nofpclass(pinf pnorm psub pzero) %known.negative, float nofpclass(ninf nnorm nsub nzero) %always.positive.or.nan) {
+; CHECK-LABEL: define nofpclass(snan) float @copysign_nnan__known_negative__sign_known_positive_or_nan
+; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[KNOWN_NEGATIVE:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ALWAYS_POSITIVE_OR_NAN:%.*]]) {
+; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.copysign.f32(float [[KNOWN_NEGATIVE]], float [[ALWAYS_POSITIVE_OR_NAN]])
+; CHECK-NEXT: ret float [[COPYSIGN]]
+;
+ %copysign = call nnan float @llvm.copysign.f32(float %known.negative, float %always.positive.or.nan)
+ ret float %copysign
+}
+
+; Convert to fneg(fabs)
+define nofpclass(snan) float @copysign_nnan__known_positive__sign_known_negative_or_nan(float nofpclass(ninf nnorm nsub nzero) %known.positive, float nofpclass(pinf pnorm psub pzero) %always.negative.or.nan) {
+; CHECK-LABEL: define nofpclass(snan) float @copysign_nnan__known_positive__sign_known_negative_or_nan
+; CHECK-SAME: (float nofpclass(ninf nzero nsub nnorm) [[KNOWN_POSITIVE:%.*]], float nofpclass(pinf pzero psub pnorm) [[ALWAYS_NEGATIVE_OR_NAN:%.*]]) {
+; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.copysign.f32(float [[KNOWN_POSITIVE]], float [[ALWAYS_NEGATIVE_OR_NAN]])
+; CHECK-NEXT: ret float [[COPYSIGN]]
+;
+ %copysign = call nnan float @llvm.copysign.f32(float %known.positive, float %always.negative.or.nan)
+ ret float %copysign
+}
+
; Do nothing
define nofpclass(ninf nnorm nsub nzero) float @ret_nofpclass_negatives__select_clamp_neg_to_zero(float %x) {
; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_nofpclass_negatives__select_clamp_neg_to_zero
>From 7026c73f507938f8055f9ba5ce0ee6bd3df92c84 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Thu, 8 Jan 2026 16:06:30 +0100
Subject: [PATCH 2/2] InstCombine: Improve SimplifyDemandedFPClass copysign
handling
Recognize cases where the signbit is known.
---
.../InstCombineSimplifyDemanded.cpp | 22 +++++++++++++++++++
.../InstCombine/simplify-demanded-fpclass.ll | 15 ++++++++-----
2 files changed, 31 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index 2662c168dc90f..3ee26b502652f 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -2252,6 +2252,28 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Instruction *I,
KnownFPClass KnownSign =
computeKnownFPClass(I->getOperand(1), fcAllFlags, CxtI, Depth + 1);
+
+ if (Known.SignBit && KnownSign.SignBit &&
+ *Known.SignBit == *KnownSign.SignBit)
+ return I->getOperand(0);
+
+ // TODO: Call argument attribute not considered
+ // Input implied not-nan from flag.
+ if (FMF.noNaNs())
+ KnownSign.knownNot(fcNan);
+
+ if (KnownSign.SignBit == false ||
+ KnownSign.isKnownNever(fcNegative | fcNan)) {
+ I->setOperand(1, ConstantFP::getZero(VTy));
+ return I;
+ }
+
+ if (KnownSign.SignBit == true ||
+ KnownSign.isKnownNever(fcPositive | fcNan)) {
+ I->setOperand(1, ConstantFP::get(VTy, -1.0));
+ return I;
+ }
+
Known.copysign(KnownSign);
break;
}
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass.ll
index f6dc13ef409ac..82b1d63d3c673 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass.ll
@@ -852,7 +852,7 @@ define nofpclass(nan pinf pnorm psub pzero) float @ret_nofpclass_no_positives_no
define nofpclass(snan) float @copysign_nnan_sign_known_positive_or_nan(float %x, float nofpclass(ninf nnorm nsub nzero) %always.positive.or.nan) {
; CHECK-LABEL: define nofpclass(snan) float @copysign_nnan_sign_known_positive_or_nan
; CHECK-SAME: (float [[X:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ALWAYS_POSITIVE_OR_NAN:%.*]]) {
-; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.copysign.f32(float [[X]], float [[ALWAYS_POSITIVE_OR_NAN]])
+; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.fabs.f32(float [[X]])
; CHECK-NEXT: ret float [[COPYSIGN]]
;
%copysign = call nnan float @llvm.copysign.f32(float %x, float %always.positive.or.nan)
@@ -862,7 +862,8 @@ define nofpclass(snan) float @copysign_nnan_sign_known_positive_or_nan(float %x,
define nofpclass(snan) float @copysign_nnan_sign_known_negative_or_nan(float %x, float nofpclass(pinf pnorm psub pzero) %always.negative.or.nan) {
; CHECK-LABEL: define nofpclass(snan) float @copysign_nnan_sign_known_negative_or_nan
; CHECK-SAME: (float [[X:%.*]], float nofpclass(pinf pzero psub pnorm) [[ALWAYS_NEGATIVE_OR_NAN:%.*]]) {
-; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.copysign.f32(float [[X]], float [[ALWAYS_NEGATIVE_OR_NAN]])
+; CHECK-NEXT: [[TMP1:%.*]] = call nnan float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT: [[COPYSIGN:%.*]] = fneg nnan float [[TMP1]]
; CHECK-NEXT: ret float [[COPYSIGN]]
;
%copysign = call nnan float @llvm.copysign.f32(float %x, float %always.negative.or.nan)
@@ -873,7 +874,7 @@ define nofpclass(snan) float @copysign_nnan_sign_known_negative_or_nan(float %x,
define nofpclass(snan) float @copysign_nnan__known_positive__sign_known_positive_or_nan(float nofpclass(ninf nnorm nsub nzero) %known.positive, float nofpclass(ninf nnorm nsub nzero) %always.positive.or.nan) {
; CHECK-LABEL: define nofpclass(snan) float @copysign_nnan__known_positive__sign_known_positive_or_nan
; CHECK-SAME: (float nofpclass(ninf nzero nsub nnorm) [[KNOWN_POSITIVE:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ALWAYS_POSITIVE_OR_NAN:%.*]]) {
-; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.copysign.f32(float [[KNOWN_POSITIVE]], float [[ALWAYS_POSITIVE_OR_NAN]])
+; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.fabs.f32(float [[KNOWN_POSITIVE]])
; CHECK-NEXT: ret float [[COPYSIGN]]
;
%copysign = call nnan float @llvm.copysign.f32(float %known.positive, float %always.positive.or.nan)
@@ -884,7 +885,8 @@ define nofpclass(snan) float @copysign_nnan__known_positive__sign_known_positive
define nofpclass(snan) float @copysign_nnan__known_negative__sign_known_negative_or_nan(float nofpclass(pinf pnorm psub pzero) %known.negative, float nofpclass(pinf pnorm psub pzero) %always.negative.or.nan) {
; CHECK-LABEL: define nofpclass(snan) float @copysign_nnan__known_negative__sign_known_negative_or_nan
; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[KNOWN_NEGATIVE:%.*]], float nofpclass(pinf pzero psub pnorm) [[ALWAYS_NEGATIVE_OR_NAN:%.*]]) {
-; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.copysign.f32(float [[KNOWN_NEGATIVE]], float [[ALWAYS_NEGATIVE_OR_NAN]])
+; CHECK-NEXT: [[TMP1:%.*]] = call nnan float @llvm.fabs.f32(float [[KNOWN_NEGATIVE]])
+; CHECK-NEXT: [[COPYSIGN:%.*]] = fneg nnan float [[TMP1]]
; CHECK-NEXT: ret float [[COPYSIGN]]
;
%copysign = call nnan float @llvm.copysign.f32(float %known.negative, float %always.negative.or.nan)
@@ -895,7 +897,7 @@ define nofpclass(snan) float @copysign_nnan__known_negative__sign_known_negative
define nofpclass(snan) float @copysign_nnan__known_negative__sign_known_positive_or_nan(float nofpclass(pinf pnorm psub pzero) %known.negative, float nofpclass(ninf nnorm nsub nzero) %always.positive.or.nan) {
; CHECK-LABEL: define nofpclass(snan) float @copysign_nnan__known_negative__sign_known_positive_or_nan
; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[KNOWN_NEGATIVE:%.*]], float nofpclass(ninf nzero nsub nnorm) [[ALWAYS_POSITIVE_OR_NAN:%.*]]) {
-; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.copysign.f32(float [[KNOWN_NEGATIVE]], float [[ALWAYS_POSITIVE_OR_NAN]])
+; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.fabs.f32(float [[KNOWN_NEGATIVE]])
; CHECK-NEXT: ret float [[COPYSIGN]]
;
%copysign = call nnan float @llvm.copysign.f32(float %known.negative, float %always.positive.or.nan)
@@ -906,7 +908,8 @@ define nofpclass(snan) float @copysign_nnan__known_negative__sign_known_positive
define nofpclass(snan) float @copysign_nnan__known_positive__sign_known_negative_or_nan(float nofpclass(ninf nnorm nsub nzero) %known.positive, float nofpclass(pinf pnorm psub pzero) %always.negative.or.nan) {
; CHECK-LABEL: define nofpclass(snan) float @copysign_nnan__known_positive__sign_known_negative_or_nan
; CHECK-SAME: (float nofpclass(ninf nzero nsub nnorm) [[KNOWN_POSITIVE:%.*]], float nofpclass(pinf pzero psub pnorm) [[ALWAYS_NEGATIVE_OR_NAN:%.*]]) {
-; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.copysign.f32(float [[KNOWN_POSITIVE]], float [[ALWAYS_NEGATIVE_OR_NAN]])
+; CHECK-NEXT: [[TMP1:%.*]] = call nnan float @llvm.fabs.f32(float [[KNOWN_POSITIVE]])
+; CHECK-NEXT: [[COPYSIGN:%.*]] = fneg nnan float [[TMP1]]
; CHECK-NEXT: ret float [[COPYSIGN]]
;
%copysign = call nnan float @llvm.copysign.f32(float %known.positive, float %always.negative.or.nan)
More information about the llvm-commits
mailing list