[llvm-branch-commits] [llvm] InstCombine: Handle multiple use copysign (PR #176917)
Matt Arsenault via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Jan 30 06:30:42 PST 2026
https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/176917
>From d0144874fc665842e469799678feefe462d8a1f9 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Tue, 20 Jan 2026 13:20:09 +0100
Subject: [PATCH 1/2] InstCombine: Handle multiple use copysign
Handle multiple use copysign in SimplifyDemandedFPClass
---
llvm/include/llvm/Support/KnownFPClass.h | 7 ++++
.../InstCombineSimplifyDemanded.cpp | 39 +++++++++++++++++--
.../InstCombine/simplify-demanded-fpclass.ll | 14 +++----
3 files changed, 50 insertions(+), 10 deletions(-)
diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h
index 2ce4fcbe34bba..6409a7e44a116 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -327,6 +327,13 @@ struct KnownFPClass {
KnownFPClasses &= (fcPositive | fcNan);
}
+ static KnownFPClass copysign(const KnownFPClass &KnownMag,
+ const KnownFPClass &KnownSign) {
+ KnownFPClass Known = KnownMag;
+ Known.copysign(KnownSign);
+ return Known;
+ }
+
// Propagate knowledge that a non-NaN source implies the result can also not
// be a NaN. For unconstrained operations, signaling nans are not guaranteed
// to be quieted but cannot be introduced.
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index 630016fe58a19..5e14c47cf14c5 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -2775,7 +2775,9 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Instruction *I,
case Intrinsic::copysign: {
// Flip on more potentially demanded classes
const FPClassTest DemandedMaskAnySign = llvm::unknown_sign(DemandedMask);
- if (SimplifyDemandedFPClass(I, 0, DemandedMaskAnySign, Known, Depth + 1))
+ KnownFPClass KnownMag;
+ if (SimplifyDemandedFPClass(CI, 0, DemandedMaskAnySign, KnownMag,
+ Depth + 1))
return I;
if ((DemandedMask & fcNegative) == DemandedMask) {
@@ -2791,7 +2793,8 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Instruction *I,
}
if (Value *Simplified = simplifyDemandedFPClassCopysignMag(
- CI->getArgOperand(0), DemandedMask, Known, FMF.noSignedZeros()))
+ CI->getArgOperand(0), DemandedMask, KnownMag,
+ FMF.noSignedZeros()))
return Simplified;
KnownFPClass KnownSign = computeKnownFPClass(CI->getArgOperand(1),
@@ -2818,7 +2821,7 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Instruction *I,
return I;
}
- Known.copysign(KnownSign);
+ Known = KnownFPClass::copysign(KnownMag, KnownSign);
Known.knownNot(~DemandedMask);
break;
}
@@ -3432,6 +3435,36 @@ Value *InstCombinerImpl::SimplifyMultipleUseDemandedFPClass(
return Simplified;
break;
}
+ case Intrinsic::copysign: {
+ Value *Mag = CI->getArgOperand(0);
+ Value *Sign = CI->getArgOperand(1);
+ KnownFPClass KnownMag =
+ computeKnownFPClass(Mag, fcAllFlags, CxtI, Depth + 1);
+
+ // Rule out some cases by magnitude, which may help prove the sign bit is
+ // one direction or the other.
+ KnownMag.knownNot(~llvm::unknown_sign(DemandedMask));
+
+ // Cannot use nsz in the multiple use case.
+ if (Value *Simplified = simplifyDemandedFPClassCopysignMag(
+ Mag, DemandedMask, KnownMag, /*NSZ=*/false))
+ return Simplified;
+
+ KnownFPClass KnownSign =
+ computeKnownFPClass(Sign, fcAllFlags, CxtI, Depth + 1);
+
+ if (FMF.noInfs())
+ KnownSign.knownNot(fcInf);
+ if (FMF.noNaNs())
+ KnownSign.knownNot(fcNan);
+
+ if (KnownSign.SignBit && KnownMag.SignBit &&
+ *KnownSign.SignBit == *KnownMag.SignBit)
+ return Mag;
+
+ Known = KnownFPClass::copysign(KnownMag, KnownSign);
+ break;
+ }
case Intrinsic::maximum:
case Intrinsic::minimum:
case Intrinsic::maximumnum:
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass.ll
index 7825d380d6e50..1c3ccf734b6a3 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass.ll
@@ -2181,7 +2181,7 @@ define nofpclass(nan ninf nnorm nsub nzero) float @ret_only_positive__copysign_s
; CHECK-SAME: (float nofpclass(nan ninf nzero nsub nnorm) [[ALWAYS_POSITIVE:%.*]], float [[UNKNOWN:%.*]], ptr [[PTR:%.*]]) {
; CHECK-NEXT: [[COPYSIGN:%.*]] = call float @llvm.copysign.f32(float [[ALWAYS_POSITIVE]], float [[UNKNOWN]])
; CHECK-NEXT: store float [[COPYSIGN]], ptr [[PTR]], align 4
-; CHECK-NEXT: ret float [[COPYSIGN]]
+; CHECK-NEXT: ret float [[ALWAYS_POSITIVE]]
;
%copysign = call float @llvm.copysign.f32(float %always.positive, float %unknown)
store float %copysign, ptr %ptr
@@ -2204,7 +2204,7 @@ define nofpclass(nan pinf pnorm psub pzero) float @ret_only_negative__copysign_s
; CHECK-SAME: (float nofpclass(nan pinf pzero psub pnorm) [[ALWAYS_NEGATIVE:%.*]], float [[UNKNOWN:%.*]], ptr [[PTR:%.*]]) {
; CHECK-NEXT: [[COPYSIGN:%.*]] = call float @llvm.copysign.f32(float [[ALWAYS_NEGATIVE]], float [[UNKNOWN]])
; CHECK-NEXT: store float [[COPYSIGN]], ptr [[PTR]], align 4
-; CHECK-NEXT: ret float [[COPYSIGN]]
+; CHECK-NEXT: ret float [[ALWAYS_NEGATIVE]]
;
%copysign = call float @llvm.copysign.f32(float %always.negative, float %unknown)
store float %copysign, ptr %ptr
@@ -2273,7 +2273,7 @@ define nofpclass(nan nnorm nsub nzero) float @ret_only_positive_or_ninf__copysig
; CHECK-SAME: (float nofpclass(nan ninf nzero nsub nnorm) [[ALWAYS_POSITIVE:%.*]], float [[UNKNOWN:%.*]], ptr [[PTR:%.*]]) {
; CHECK-NEXT: [[COPYSIGN:%.*]] = call ninf float @llvm.copysign.f32(float [[ALWAYS_POSITIVE]], float [[UNKNOWN]])
; CHECK-NEXT: store float [[COPYSIGN]], ptr [[PTR]], align 4
-; CHECK-NEXT: ret float [[COPYSIGN]]
+; CHECK-NEXT: ret float [[ALWAYS_POSITIVE]]
;
%copysign = call ninf float @llvm.copysign.f32(float %always.positive, float %unknown)
store float %copysign, ptr %ptr
@@ -2431,7 +2431,7 @@ define nofpclass(pinf pnorm psub pzero) float @ret_only_negative_or_nan__copysig
; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ALWAYS_NEGATIVE_OR_NAN:%.*]], float [[UNKNOWN:%.*]], ptr [[PTR:%.*]]) {
; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.copysign.f32(float [[ALWAYS_NEGATIVE_OR_NAN]], float [[UNKNOWN]])
; CHECK-NEXT: store float [[COPYSIGN]], ptr [[PTR]], align 4
-; CHECK-NEXT: ret float [[COPYSIGN]]
+; CHECK-NEXT: ret float [[ALWAYS_NEGATIVE_OR_NAN]]
;
%copysign = call nnan float @llvm.copysign.f32(float %always.negative.or.nan, float %unknown)
store float %copysign, ptr %ptr
@@ -2500,7 +2500,7 @@ define nofpclass(pinf pnorm psub pzero) float @ret_only_positive_or_nan__copysig
; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ALWAYS_NEGATIVE_OR_NAN:%.*]], float [[UNKNOWN:%.*]], ptr [[PTR:%.*]]) {
; CHECK-NEXT: [[COPYSIGN:%.*]] = call nnan float @llvm.copysign.f32(float [[ALWAYS_NEGATIVE_OR_NAN]], float [[UNKNOWN]])
; CHECK-NEXT: store float [[COPYSIGN]], ptr [[PTR]], align 4
-; CHECK-NEXT: ret float [[COPYSIGN]]
+; CHECK-NEXT: ret float [[ALWAYS_NEGATIVE_OR_NAN]]
;
%copysign = call nnan float @llvm.copysign.f32(float %always.negative.or.nan, float %unknown)
store float %copysign, ptr %ptr
@@ -2524,7 +2524,7 @@ define nofpclass(nan) float @ret_no_nan__copysign_ninf__src_known_negative_or_na
; CHECK-SAME: (float nofpclass(pinf pzero psub pnorm) [[ALWAYS_NEGATIVE_OR_NAN:%.*]], float nofpclass(nan pzero psub pnorm) [[ALWAYS_NEGATIVE_OR_PINF:%.*]], ptr [[PTR:%.*]]) {
; CHECK-NEXT: [[COPYSIGN:%.*]] = call ninf float @llvm.copysign.f32(float [[ALWAYS_NEGATIVE_OR_NAN]], float [[ALWAYS_NEGATIVE_OR_PINF]])
; CHECK-NEXT: store float [[COPYSIGN]], ptr [[PTR]], align 4
-; CHECK-NEXT: ret float [[COPYSIGN]]
+; CHECK-NEXT: ret float [[ALWAYS_NEGATIVE_OR_NAN]]
;
%copysign = call ninf float @llvm.copysign.f32(float %always.negative.or.nan, float %always.negative.or.pinf)
store float %copysign, ptr %ptr
@@ -2548,7 +2548,7 @@ define nofpclass(nan) float @ret_no_nan__copysign_ninf__src_known_negative_or_na
; CHECK-SAME: (float nofpclass(ninf nzero nsub nnorm) [[ALWAYS_POSITIVE_OR_NAN:%.*]], float nofpclass(nan nzero nsub nnorm) [[ALWAYS_NEGATIVE_OR_PINF:%.*]], ptr [[PTR:%.*]]) {
; CHECK-NEXT: [[COPYSIGN:%.*]] = call ninf float @llvm.copysign.f32(float [[ALWAYS_POSITIVE_OR_NAN]], float [[ALWAYS_NEGATIVE_OR_PINF]])
; CHECK-NEXT: store float [[COPYSIGN]], ptr [[PTR]], align 4
-; CHECK-NEXT: ret float [[COPYSIGN]]
+; CHECK-NEXT: ret float [[ALWAYS_POSITIVE_OR_NAN]]
;
%copysign = call ninf float @llvm.copysign.f32(float %always.positive.or.nan, float %always.negative.or.pinf)
store float %copysign, ptr %ptr
>From 995b3400e5978fabcd2970fab65b73e90006e93d Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Fri, 30 Jan 2026 14:26:52 +0100
Subject: [PATCH 2/2] Fix using Known as input
---
.../Transforms/InstCombine/InstCombineSimplifyDemanded.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index 5e14c47cf14c5..f6c3198cd6d5e 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
@@ -2799,9 +2799,8 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Instruction *I,
KnownFPClass KnownSign = computeKnownFPClass(CI->getArgOperand(1),
fcAllFlags, CxtI, Depth + 1);
-
- if (Known.SignBit && KnownSign.SignBit &&
- *Known.SignBit == *KnownSign.SignBit)
+ if (KnownMag.SignBit && KnownSign.SignBit &&
+ *KnownMag.SignBit == *KnownSign.SignBit)
return CI->getOperand(0);
// TODO: Call argument attribute not considered
More information about the llvm-branch-commits
mailing list