[llvm] [KnownFPClass] Add sign preserving deductions to `atan2(y, x)` (PR #223176)

Eric Ross via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 12 14:32:12 PDT 2026


https://github.com/ZERICO2005 created https://github.com/llvm/llvm-project/pull/223176

`atan2(y, x)` should return the same sign as `y` (up to the denormal mode):
- If `y >= +0.0`, then `atan2(y, x) >= +0.0`
- If `y <= -0.0`, then `atan2(y, x) <= -0.0`

AI Disclosure:
I used ChatGPT Codex (5.6 sol) to help write the tests.


>From 5e73409eeb8612b39a00b316654cb8eb7a9956b3 Mon Sep 17 00:00:00 2001
From: zerico <zerico2005 at gmail.com>
Date: Sat, 12 Sep 2026 12:35:53 -0600
Subject: [PATCH] [KnownFPClass] Add sign preserving deductions to atan2(y, x)

---
 llvm/lib/Analysis/ValueTracking.cpp           |   8 +
 .../CodeGen/GlobalISel/GISelValueTracking.cpp |   8 +
 llvm/lib/Support/KnownFPClass.cpp             |  20 +-
 .../Transforms/Attributor/nofpclass-atan2.ll  | 458 ++++++++++++++++--
 .../simplify-demanded-fpclass-atan2.ll        |  25 +
 .../CodeGen/GlobalISel/KnownFPClassTest.cpp   |   6 +-
 6 files changed, 490 insertions(+), 35 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 1580571a01451..f0e86a3bf9905 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5503,6 +5503,14 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
       FPClassTest InterestedY = InterestedClasses;
       FPClassTest InterestedX = InterestedClasses;
 
+      // We can rule out negative values if y cannot have a negative value.
+      if ((InterestedClasses & fcNegFinite) != fcNone)
+        InterestedY |= fcNegative;
+
+      // We can rule out positive values if y cannot have a positive value.
+      if ((InterestedClasses & fcPosFinite) != fcNone)
+        InterestedY |= fcPositive | fcNegSubnormal;
+
       // We can rule out zero and subnormal if x cannot have a positive value.
       if ((InterestedClasses & (fcZero | fcSubnormal)) != fcNone)
         InterestedX |= fcPositive | fcNegSubnormal;
diff --git a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
index 0bbafeb6dca41..ad07e0055fc7a 100644
--- a/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/GISelValueTracking.cpp
@@ -1406,6 +1406,14 @@ void GISelValueTracking::computeKnownFPClass(Register R,
     FPClassTest InterestedY = InterestedClasses;
     FPClassTest InterestedX = InterestedClasses;
 
+    // We can rule out negative values if y cannot have a negative value.
+    if ((InterestedClasses & fcNegFinite) != fcNone)
+      InterestedY |= fcNegative;
+
+    // We can rule out positive values if y cannot have a positive value.
+    if ((InterestedClasses & fcPosFinite) != fcNone)
+      InterestedY |= fcPositive | fcNegSubnormal;
+
     // We can rule out zero and subnormal if x cannot have a positive value.
     if ((InterestedClasses & (fcZero | fcSubnormal)) != fcNone)
       InterestedX |= fcPositive | fcNegSubnormal;
diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 75636ffc73361..930d91be93045 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -798,13 +798,29 @@ KnownFPClass KnownFPClass::atan2(const KnownFPClass &KnownY,
   Known.propagateNonNaN(KnownY, KnownX);
 
   // Negative subnormals could be treated like positive zero.
-  const bool XCannotHavePositiveValue = KnownX.isKnownNever(fcPositive) &&
+  const bool XCannotHavePositiveInput = KnownX.isKnownNever(fcPositive) &&
                                         KnownX.isKnownNeverLogicalPosZero(Mode);
+  const bool YCannotHavePositiveInput = KnownY.isKnownNever(fcPositive) &&
+                                        KnownY.isKnownNeverLogicalPosZero(Mode);
 
   // If x <= -0.0, then |atan2(y, x)| >= pi/2
-  if (XCannotHavePositiveValue)
+  if (XCannotHavePositiveInput)
     Known.knownNot(fcZero | fcSubnormal);
 
+  // If y >= +0.0, then atan2(y, x) >= +0.0
+  if (KnownY.isKnownNever(fcNegative))
+    Known.knownNot(fcNegative);
+
+  // If y <= -0.0, then atan2(y, x) <= -0.0
+  // We do this deduction last in case we were able to rule out a negative
+  // subnormal result earlier.
+  if (YCannotHavePositiveInput) {
+    Known.knownNot(fcPosSubnormal | fcPosNormal | fcPosInf);
+    // Negative subnormal results can flush to +0.0.
+    if (Known.isKnownNever(fcNegSubnormal) || !Mode.outputsMayBePositiveZero())
+      Known.knownNot(fcPosZero);
+  }
+
   return Known;
 }
 
diff --git a/llvm/test/Transforms/Attributor/nofpclass-atan2.ll b/llvm/test/Transforms/Attributor/nofpclass-atan2.ll
index feb4a7075b435..ae0e7c6f34ea2 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-atan2.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-atan2.ll
@@ -7,7 +7,7 @@ declare float @llvm.atan2.f32(float, float)
 define float @ret_atan2(float %arg0, float %arg1) {
 ; CHECK-LABEL: define nofpclass(inf) float @ret_atan2(
 ; CHECK-SAME: float [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR1:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) float @llvm.atan2.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR6:[0-9]+]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) float @llvm.atan2.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR10:[0-9]+]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.atan2.f32(float %arg0, float %arg1)
@@ -17,7 +17,7 @@ define float @ret_atan2(float %arg0, float %arg1) {
 define float @ret_atan2_nonan(float nofpclass(nan) %arg0, float nofpclass(nan) %arg1) {
 ; CHECK-LABEL: define nofpclass(nan inf) float @ret_atan2_nonan(
 ; CHECK-SAME: float nofpclass(nan) [[ARG0:%.*]], float nofpclass(nan) [[ARG1:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf) float @llvm.atan2.f32(float nofpclass(nan) [[ARG0]], float nofpclass(nan) [[ARG1]]) #[[ATTR6]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf) float @llvm.atan2.f32(float nofpclass(nan) [[ARG0]], float nofpclass(nan) [[ARG1]]) #[[ATTR10]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.atan2.f32(float %arg0, float %arg1)
@@ -27,18 +27,180 @@ define float @ret_atan2_nonan(float nofpclass(nan) %arg0, float nofpclass(nan) %
 define float @ret_atan2_nosnan(float nofpclass(snan) %arg0, float nofpclass(snan) %arg1) {
 ; CHECK-LABEL: define nofpclass(snan inf) float @ret_atan2_nosnan(
 ; CHECK-SAME: float nofpclass(snan) [[ARG0:%.*]], float nofpclass(snan) [[ARG1:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan inf) float @llvm.atan2.f32(float nofpclass(snan) [[ARG0]], float nofpclass(snan) [[ARG1]]) #[[ATTR6]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan inf) float @llvm.atan2.f32(float nofpclass(snan) [[ARG0]], float nofpclass(snan) [[ARG1]]) #[[ATTR10]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.atan2.f32(float %arg0, float %arg1)
   ret float %call
 }
 
+; Quadrant tests
+
+define float @ret_atan2_normal_quad_1(float nofpclass(nan inf zero sub nnorm) %y, float nofpclass(nan inf zero sub nnorm) %x) {
+; CHECK-LABEL: define nofpclass(nan inf nzero nsub nnorm) float @ret_atan2_normal_quad_1(
+; CHECK-SAME: float nofpclass(nan inf zero sub nnorm) [[Y:%.*]], float nofpclass(nan inf zero sub nnorm) [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf nzero nsub nnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero sub nnorm) [[Y]], float nofpclass(nan inf zero sub nnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_normal_quad_2(float nofpclass(nan inf zero sub nnorm) %y, float nofpclass(nan inf zero sub pnorm) %x) {
+; CHECK-LABEL: define nofpclass(nan inf zero sub nnorm) float @ret_atan2_normal_quad_2(
+; CHECK-SAME: float nofpclass(nan inf zero sub nnorm) [[Y:%.*]], float nofpclass(nan inf zero sub pnorm) [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf zero sub nnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero sub nnorm) [[Y]], float nofpclass(nan inf zero sub pnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_normal_quad_3(float nofpclass(nan inf zero sub pnorm) %y, float nofpclass(nan inf zero sub pnorm) %x) {
+; CHECK-LABEL: define nofpclass(nan inf zero sub pnorm) float @ret_atan2_normal_quad_3(
+; CHECK-SAME: float nofpclass(nan inf zero sub pnorm) [[Y:%.*]], float nofpclass(nan inf zero sub pnorm) [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf zero sub pnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero sub pnorm) [[Y]], float nofpclass(nan inf zero sub pnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_normal_quad_4(float nofpclass(nan inf zero sub pnorm) %y, float nofpclass(nan inf zero sub nnorm) %x) {
+; CHECK-LABEL: define nofpclass(nan inf pzero psub pnorm) float @ret_atan2_normal_quad_4(
+; CHECK-SAME: float nofpclass(nan inf zero sub pnorm) [[Y:%.*]], float nofpclass(nan inf zero sub nnorm) [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf pzero psub pnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero sub pnorm) [[Y]], float nofpclass(nan inf zero sub nnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_normal_subnormal_quad_1(float nofpclass(nan inf zero nsub nnorm) %y, float nofpclass(nan inf zero nsub nnorm) %x) {
+; CHECK-LABEL: define nofpclass(nan inf nzero nsub nnorm) float @ret_atan2_normal_subnormal_quad_1(
+; CHECK-SAME: float nofpclass(nan inf zero nsub nnorm) [[Y:%.*]], float nofpclass(nan inf zero nsub nnorm) [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf nzero nsub nnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero nsub nnorm) [[Y]], float nofpclass(nan inf zero nsub nnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_normal_subnormal_quad_2(float nofpclass(nan inf zero nsub nnorm) %y, float nofpclass(nan inf zero psub pnorm) %x) {
+; CHECK-LABEL: define nofpclass(nan inf zero sub nnorm) float @ret_atan2_normal_subnormal_quad_2(
+; CHECK-SAME: float nofpclass(nan inf zero nsub nnorm) [[Y:%.*]], float nofpclass(nan inf zero psub pnorm) [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf zero sub nnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero nsub nnorm) [[Y]], float nofpclass(nan inf zero psub pnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_normal_subnormal_quad_3(float nofpclass(nan inf zero psub pnorm) %y, float nofpclass(nan inf zero psub pnorm) %x) {
+; CHECK-LABEL: define nofpclass(nan inf zero sub pnorm) float @ret_atan2_normal_subnormal_quad_3(
+; CHECK-SAME: float nofpclass(nan inf zero psub pnorm) [[Y:%.*]], float nofpclass(nan inf zero psub pnorm) [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf zero sub pnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero psub pnorm) [[Y]], float nofpclass(nan inf zero psub pnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_normal_subnormal_quad_4(float nofpclass(nan inf zero psub pnorm) %y, float nofpclass(nan inf zero nsub nnorm) %x) {
+; CHECK-LABEL: define nofpclass(nan inf pzero psub pnorm) float @ret_atan2_normal_subnormal_quad_4(
+; CHECK-SAME: float nofpclass(nan inf zero psub pnorm) [[Y:%.*]], float nofpclass(nan inf zero nsub nnorm) [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf pzero psub pnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero psub pnorm) [[Y]], float nofpclass(nan inf zero nsub nnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_normal_subnormal_quad_1_mode_dynamic_dynamic(float nofpclass(nan inf zero nsub nnorm) %y, float nofpclass(nan inf zero nsub nnorm) %x) denormal_fpenv(float: dynamic|dynamic) {
+; CHECK-LABEL: define nofpclass(nan inf nzero nsub nnorm) float @ret_atan2_normal_subnormal_quad_1_mode_dynamic_dynamic(
+; CHECK-SAME: float nofpclass(nan inf zero nsub nnorm) [[Y:%.*]], float nofpclass(nan inf zero nsub nnorm) [[X:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf nzero nsub nnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero nsub nnorm) [[Y]], float nofpclass(nan inf zero nsub nnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_normal_subnormal_quad_2_mode_dynamic_dynamic(float nofpclass(nan inf zero nsub nnorm) %y, float nofpclass(nan inf zero psub pnorm) %x) denormal_fpenv(float: dynamic|dynamic) {
+; CHECK-LABEL: define nofpclass(nan inf nzero nsub nnorm) float @ret_atan2_normal_subnormal_quad_2_mode_dynamic_dynamic(
+; CHECK-SAME: float nofpclass(nan inf zero nsub nnorm) [[Y:%.*]], float nofpclass(nan inf zero psub pnorm) [[X:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf nzero nsub nnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero nsub nnorm) [[Y]], float nofpclass(nan inf zero psub pnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_normal_subnormal_quad_3_mode_dynamic_dynamic(float nofpclass(nan inf zero psub pnorm) %y, float nofpclass(nan inf zero psub pnorm) %x) denormal_fpenv(float: dynamic|dynamic) {
+; CHECK-LABEL: define nofpclass(nan inf) float @ret_atan2_normal_subnormal_quad_3_mode_dynamic_dynamic(
+; CHECK-SAME: float nofpclass(nan inf zero psub pnorm) [[Y:%.*]], float nofpclass(nan inf zero psub pnorm) [[X:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf) float @llvm.atan2.f32(float nofpclass(nan inf zero psub pnorm) [[Y]], float nofpclass(nan inf zero psub pnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_normal_subnormal_quad_4_mode_dynamic_dynamic(float nofpclass(nan inf zero psub pnorm) %y, float nofpclass(nan inf zero nsub nnorm) %x) denormal_fpenv(float: dynamic|dynamic) {
+; CHECK-LABEL: define nofpclass(nan inf) float @ret_atan2_normal_subnormal_quad_4_mode_dynamic_dynamic(
+; CHECK-SAME: float nofpclass(nan inf zero psub pnorm) [[Y:%.*]], float nofpclass(nan inf zero nsub nnorm) [[X:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf) float @llvm.atan2.f32(float nofpclass(nan inf zero psub pnorm) [[Y]], float nofpclass(nan inf zero nsub nnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_normal_subnormal_quad_1_mode_ftpz_dapz(float nofpclass(nan inf zero nsub nnorm) %y, float nofpclass(nan inf zero nsub nnorm) %x) denormal_fpenv(float: positivezero|positivezero) {
+; CHECK-LABEL: define nofpclass(nan inf nzero nsub nnorm) float @ret_atan2_normal_subnormal_quad_1_mode_ftpz_dapz(
+; CHECK-SAME: float nofpclass(nan inf zero nsub nnorm) [[Y:%.*]], float nofpclass(nan inf zero nsub nnorm) [[X:%.*]]) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf nzero nsub nnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero nsub nnorm) [[Y]], float nofpclass(nan inf zero nsub nnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_normal_subnormal_quad_2_mode_ftpz_dapz(float nofpclass(nan inf zero nsub nnorm) %y, float nofpclass(nan inf zero psub pnorm) %x) denormal_fpenv(float: positivezero|positivezero) {
+; CHECK-LABEL: define nofpclass(nan inf nzero nsub nnorm) float @ret_atan2_normal_subnormal_quad_2_mode_ftpz_dapz(
+; CHECK-SAME: float nofpclass(nan inf zero nsub nnorm) [[Y:%.*]], float nofpclass(nan inf zero psub pnorm) [[X:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf nzero nsub nnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero nsub nnorm) [[Y]], float nofpclass(nan inf zero psub pnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_normal_subnormal_quad_3_mode_ftpz_dapz(float nofpclass(nan inf zero psub pnorm) %y, float nofpclass(nan inf zero psub pnorm) %x) denormal_fpenv(float: positivezero|positivezero) {
+; CHECK-LABEL: define nofpclass(nan inf) float @ret_atan2_normal_subnormal_quad_3_mode_ftpz_dapz(
+; CHECK-SAME: float nofpclass(nan inf zero psub pnorm) [[Y:%.*]], float nofpclass(nan inf zero psub pnorm) [[X:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf) float @llvm.atan2.f32(float nofpclass(nan inf zero psub pnorm) [[Y]], float nofpclass(nan inf zero psub pnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_normal_subnormal_quad_4_mode_ftpz_dapz(float nofpclass(nan inf zero psub pnorm) %y, float nofpclass(nan inf zero nsub nnorm) %x) denormal_fpenv(float: positivezero|positivezero) {
+; CHECK-LABEL: define nofpclass(nan inf) float @ret_atan2_normal_subnormal_quad_4_mode_ftpz_dapz(
+; CHECK-SAME: float nofpclass(nan inf zero psub pnorm) [[Y:%.*]], float nofpclass(nan inf zero nsub nnorm) [[X:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf) float @llvm.atan2.f32(float nofpclass(nan inf zero psub pnorm) [[Y]], float nofpclass(nan inf zero nsub nnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
 ; |atan2(y, x)| >= pi/2 when x is a negative normal or negative infinity.
-define float @ret_atan2_neg_normal_x(float %y, float nofpclass(nan pinf zero sub pnorm) %x) {
-; CHECK-LABEL: define nofpclass(inf zero sub) float @ret_atan2_neg_normal_x(
+define float @ret_atan2_negnormal_neginf_x(float %y, float nofpclass(nan pinf zero sub pnorm) %x) {
+; CHECK-LABEL: define nofpclass(inf zero sub) float @ret_atan2_negnormal_neginf_x(
 ; CHECK-SAME: float [[Y:%.*]], float nofpclass(nan pinf zero sub pnorm) [[X:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf zero sub) float @llvm.atan2.f32(float [[Y]], float nofpclass(nan pinf zero sub pnorm) [[X]]) #[[ATTR6]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf zero sub) float @llvm.atan2.f32(float [[Y]], float nofpclass(nan pinf zero sub pnorm) [[X]]) #[[ATTR10]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.atan2.f32(float %y, float %x)
@@ -49,50 +211,286 @@ define float @ret_atan2_neg_normal_x(float %y, float nofpclass(nan pinf zero sub
 ; be zero or subnormal. However, a negative subnormal may be treated as a
 ; logical positive zero depending on how the floating point environment handles
 ; denormals. Here we make sure to test every input denormal-mode combination.
-define float @ret_atan2_neg_subnormal_x_ieee_ieee(
-; CHECK-LABEL: define nofpclass(inf zero sub) float @ret_atan2_neg_subnormal_x_ieee_ieee(
-; CHECK-SAME: float [[Y:%.*]], float nofpclass(nan inf zero psub norm) [[X:%.*]]) #[[ATTR2:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf zero sub) float @llvm.atan2.f32(float [[Y]], float nofpclass(nan inf zero psub norm) [[X]]) #[[ATTR6]]
+define float @ret_atan2_negsubnormal_x_mode_ieee_ieee(float %y, float nofpclass(nan inf zero psub norm) %x) denormal_fpenv(float: ieee|ieee) {
+; CHECK-LABEL: define nofpclass(inf zero sub) float @ret_atan2_negsubnormal_x_mode_ieee_ieee(
+; CHECK-SAME: float [[Y:%.*]], float nofpclass(nan inf zero psub norm) [[X:%.*]]) #[[ATTR4:[0-9]+]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf zero sub) float @llvm.atan2.f32(float [[Y]], float nofpclass(nan inf zero psub norm) [[X]]) #[[ATTR10]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
-  float %y, float nofpclass(nan inf zero norm psub) %x)
-  denormal_fpenv(float: ieee|ieee) {
   %call = call float @llvm.atan2.f32(float %y, float %x)
   ret float %call
 }
 
-define float @ret_atan2_neg_subnormal_x_ieee_preservesign(
-; CHECK-LABEL: define nofpclass(inf zero sub) float @ret_atan2_neg_subnormal_x_ieee_preservesign(
-; CHECK-SAME: float [[Y:%.*]], float nofpclass(nan inf zero psub norm) [[X:%.*]]) #[[ATTR3:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf zero sub) float @llvm.atan2.f32(float [[Y]], float nofpclass(nan inf zero psub norm) [[X]]) #[[ATTR6]]
+define float @ret_atan2_negsubnormal_x_mode_ieee_daz(float %y, float nofpclass(nan inf zero psub norm) %x) denormal_fpenv(float: ieee|preservesign) {
+; CHECK-LABEL: define nofpclass(inf zero sub) float @ret_atan2_negsubnormal_x_mode_ieee_daz(
+; CHECK-SAME: float [[Y:%.*]], float nofpclass(nan inf zero psub norm) [[X:%.*]]) #[[ATTR5:[0-9]+]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf zero sub) float @llvm.atan2.f32(float [[Y]], float nofpclass(nan inf zero psub norm) [[X]]) #[[ATTR10]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
-  float %y, float nofpclass(nan inf zero norm psub) %x)
-  denormal_fpenv(float: ieee|preservesign) {
   %call = call float @llvm.atan2.f32(float %y, float %x)
   ret float %call
 }
 
-define float @ret_atan2_neg_subnormal_x_ieee_positivezero(
-; CHECK-LABEL: define nofpclass(inf) float @ret_atan2_neg_subnormal_x_ieee_positivezero(
-; CHECK-SAME: float [[Y:%.*]], float nofpclass(nan inf zero psub norm) [[X:%.*]]) #[[ATTR4:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) float @llvm.atan2.f32(float [[Y]], float nofpclass(nan inf zero psub norm) [[X]]) #[[ATTR6]]
+define float @ret_atan2_negsubnormal_x_mode_ieee_dapz(float %y, float nofpclass(nan inf zero psub norm) %x) denormal_fpenv(float: ieee|positivezero) {
+; CHECK-LABEL: define nofpclass(inf) float @ret_atan2_negsubnormal_x_mode_ieee_dapz(
+; CHECK-SAME: float [[Y:%.*]], float nofpclass(nan inf zero psub norm) [[X:%.*]]) #[[ATTR6:[0-9]+]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) float @llvm.atan2.f32(float [[Y]], float nofpclass(nan inf zero psub norm) [[X]]) #[[ATTR10]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
-  float %y, float nofpclass(nan inf zero norm psub) %x)
-  denormal_fpenv(float: ieee|positivezero) {
   %call = call float @llvm.atan2.f32(float %y, float %x)
   ret float %call
 }
 
-define float @ret_atan2_neg_subnormal_x_ieee_dynamic(
-; CHECK-LABEL: define nofpclass(inf) float @ret_atan2_neg_subnormal_x_ieee_dynamic(
-; CHECK-SAME: float [[Y:%.*]], float nofpclass(nan inf zero psub norm) [[X:%.*]]) #[[ATTR5:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) float @llvm.atan2.f32(float [[Y]], float nofpclass(nan inf zero psub norm) [[X]]) #[[ATTR6]]
+define float @ret_atan2_negsubnormal_x_mode_ieee_dynamic(float %y, float nofpclass(nan inf zero psub norm) %x) denormal_fpenv(float: ieee|dynamic) {
+; CHECK-LABEL: define nofpclass(inf) float @ret_atan2_negsubnormal_x_mode_ieee_dynamic(
+; CHECK-SAME: float [[Y:%.*]], float nofpclass(nan inf zero psub norm) [[X:%.*]]) #[[ATTR7:[0-9]+]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) float @llvm.atan2.f32(float [[Y]], float nofpclass(nan inf zero psub norm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+; If y >= +0.0, then atan2(y, x) >= +0.0
+
+define float @ret_atan2_positive_y(float nofpclass(nan ninf nzero nsub nnorm) %y, float %x) {
+; CHECK-LABEL: define nofpclass(inf nzero nsub nnorm) float @ret_atan2_positive_y(
+; CHECK-SAME: float nofpclass(nan ninf nzero nsub nnorm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf nzero nsub nnorm) float @llvm.atan2.f32(float nofpclass(nan ninf nzero nsub nnorm) [[Y]], float [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_positive_y_mode_dynamic_dynamic(float nofpclass(nan ninf nzero nsub nnorm) %y, float %x) denormal_fpenv(float: dynamic|dynamic) {
+; CHECK-LABEL: define nofpclass(inf nzero nsub nnorm) float @ret_atan2_positive_y_mode_dynamic_dynamic(
+; CHECK-SAME: float nofpclass(nan ninf nzero nsub nnorm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf nzero nsub nnorm) float @llvm.atan2.f32(float nofpclass(nan ninf nzero nsub nnorm) [[Y]], float [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_positive_y_mode_ftpz_dapz(float nofpclass(nan ninf nzero nsub nnorm) %y, float %x) denormal_fpenv(float: positivezero|positivezero) {
+; CHECK-LABEL: define nofpclass(inf nzero nsub nnorm) float @ret_atan2_positive_y_mode_ftpz_dapz(
+; CHECK-SAME: float nofpclass(nan ninf nzero nsub nnorm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf nzero nsub nnorm) float @llvm.atan2.f32(float nofpclass(nan ninf nzero nsub nnorm) [[Y]], float [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_positive_or_negzero_y(float nofpclass(nan ninf nsub nnorm) %y, float %x) {
+; CHECK-LABEL: define nofpclass(inf) float @ret_atan2_positive_or_negzero_y(
+; CHECK-SAME: float nofpclass(nan ninf nsub nnorm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) float @llvm.atan2.f32(float nofpclass(nan ninf nsub nnorm) [[Y]], float [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_positive_or_negsubnormal_y(float nofpclass(nan ninf nzero nnorm) %y, float %x) {
+; CHECK-LABEL: define nofpclass(inf) float @ret_atan2_positive_or_negsubnormal_y(
+; CHECK-SAME: float nofpclass(nan ninf nzero nnorm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) float @llvm.atan2.f32(float nofpclass(nan ninf nzero nnorm) [[Y]], float [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+; TODO: If we can guarantee that negative subnormals flush to positive zero,
+; then we should be able to rule out y being negative.
+define float @ret_atan2_positive_or_negsubnormal_y_mode_ieee_dapz(float nofpclass(nan ninf nzero nnorm) %y, float %x) denormal_fpenv(float: ieee|positivezero) {
+; CHECK-LABEL: define nofpclass(inf) float @ret_atan2_positive_or_negsubnormal_y_mode_ieee_dapz(
+; CHECK-SAME: float nofpclass(nan ninf nzero nnorm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR6]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) float @llvm.atan2.f32(float nofpclass(nan ninf nzero nnorm) [[Y]], float [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+; If y <= -0.0, then atan2(y, x) <= -0.0
+
+define float @ret_atan2_negnormal_neginf_y(float nofpclass(nan pinf zero sub pnorm) %y, float %x) {
+; CHECK-LABEL: define nofpclass(inf pzero psub pnorm) float @ret_atan2_negnormal_neginf_y(
+; CHECK-SAME: float nofpclass(nan pinf zero sub pnorm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf pzero psub pnorm) float @llvm.atan2.f32(float nofpclass(nan pinf zero sub pnorm) [[Y]], float [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_negsubnormal_y_mode_ieee_ieee(float nofpclass(nan inf zero psub norm) %y, float %x) denormal_fpenv(float: ieee|ieee) {
+; CHECK-LABEL: define nofpclass(inf pzero psub pnorm) float @ret_atan2_negsubnormal_y_mode_ieee_ieee(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf pzero psub pnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero psub norm) [[Y]], float [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_negsubnormal_y_mode_ieee_daz(float nofpclass(nan inf zero psub norm) %y, float %x) denormal_fpenv(float: ieee|preservesign) {
+; CHECK-LABEL: define nofpclass(inf pzero psub pnorm) float @ret_atan2_negsubnormal_y_mode_ieee_daz(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf pzero psub pnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero psub norm) [[Y]], float [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_negsubnormal_y_mode_ieee_dapz(float nofpclass(nan inf zero psub norm) %y, float %x) denormal_fpenv(float: ieee|positivezero) {
+; CHECK-LABEL: define nofpclass(inf) float @ret_atan2_negsubnormal_y_mode_ieee_dapz(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR6]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) float @llvm.atan2.f32(float nofpclass(nan inf zero psub norm) [[Y]], float [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_negsubnormal_y_mode_ieee_dynamic(float nofpclass(nan inf zero psub norm) %y, float %x) denormal_fpenv(float: ieee|dynamic) {
+; CHECK-LABEL: define nofpclass(inf) float @ret_atan2_negsubnormal_y_mode_ieee_dynamic(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR7]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf) float @llvm.atan2.f32(float nofpclass(nan inf zero psub norm) [[Y]], float [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+; If the output could flush to positive zero, then we cannot rule out positive
+; zero.
+
+define float @ret_atan2_negsubnormal_y_mode_dynamic_ieee(float nofpclass(nan inf zero psub norm) %y, float %x) denormal_fpenv(float: dynamic|ieee) {
+; CHECK-LABEL: define nofpclass(inf psub pnorm) float @ret_atan2_negsubnormal_y_mode_dynamic_ieee(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR8:[0-9]+]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf psub pnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero psub norm) [[Y]], float [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_negsubnormal_y_mode_ftpz_ieee(float nofpclass(nan inf zero psub norm) %y, float %x) denormal_fpenv(float: positivezero|ieee) {
+; CHECK-LABEL: define nofpclass(inf psub pnorm) float @ret_atan2_negsubnormal_y_mode_ftpz_ieee(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR9:[0-9]+]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf psub pnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero psub norm) [[Y]], float [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+; A preserve-sign output mode may flush a negative subnormal result to -0.0,
+; but cannot produce +0.0.
+define float @ret_atan2_negsubnormal_y_mode_preservesign_ieee(float nofpclass(nan inf zero psub norm) %y, float %x) denormal_fpenv(float: preservesign|ieee) {
+; CHECK-LABEL: define nofpclass(inf pzero psub pnorm) float @ret_atan2_negsubnormal_y_mode_preservesign_ieee(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR11:[0-9]+]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf pzero psub pnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero psub norm) [[Y]], float [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_negnormal_y_mode_dynamic_ieee(float nofpclass(nan inf zero sub pnorm) %y, float %x) denormal_fpenv(float: dynamic|ieee) {
+; CHECK-LABEL: define nofpclass(inf psub pnorm) float @ret_atan2_negnormal_y_mode_dynamic_ieee(
+; CHECK-SAME: float nofpclass(nan inf zero sub pnorm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR8]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf psub pnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero sub pnorm) [[Y]], float [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_negnormal_y_mode_ftpz_ieee(float nofpclass(nan inf zero sub pnorm) %y, float %x) denormal_fpenv(float: positivezero|ieee) {
+; CHECK-LABEL: define nofpclass(inf psub pnorm) float @ret_atan2_negnormal_y_mode_ftpz_ieee(
+; CHECK-SAME: float nofpclass(nan inf zero sub pnorm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR9]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf psub pnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero sub pnorm) [[Y]], float [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+; We can still rule out positive zero if negative subnormals were ruled out of
+; the possible results at an earlier deduction.
+
+define float @ret_atan2_negsubnormal_y_negnormal_x_mode_ieee_dynamic(float nofpclass(nan inf zero psub norm) %y, float nofpclass(nan inf zero sub pnorm) %x) denormal_fpenv(float: ieee|dynamic) {
+; CHECK-LABEL: define nofpclass(nan inf zero sub) float @ret_atan2_negsubnormal_y_negnormal_x_mode_ieee_dynamic(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[Y:%.*]], float nofpclass(nan inf zero sub pnorm) [[X:%.*]]) #[[ATTR7]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf zero sub) float @llvm.atan2.f32(float nofpclass(nan inf zero psub norm) [[Y]], float nofpclass(nan inf zero sub pnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_negsubnormal_y_negnormal_x_mode_ieee_dapz(float nofpclass(nan inf zero psub norm) %y, float nofpclass(nan inf zero sub pnorm) %x) denormal_fpenv(float: ieee|positivezero) {
+; CHECK-LABEL: define nofpclass(nan inf zero sub) float @ret_atan2_negsubnormal_y_negnormal_x_mode_ieee_dapz(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[Y:%.*]], float nofpclass(nan inf zero sub pnorm) [[X:%.*]]) #[[ATTR6]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf zero sub) float @llvm.atan2.f32(float nofpclass(nan inf zero psub norm) [[Y]], float nofpclass(nan inf zero sub pnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_negsubnormal_y_negnormal_x_mode_dynamic_ieee(float nofpclass(nan inf zero psub norm) %y, float nofpclass(nan inf zero sub pnorm) %x) denormal_fpenv(float: dynamic|ieee) {
+; CHECK-LABEL: define nofpclass(nan inf zero sub pnorm) float @ret_atan2_negsubnormal_y_negnormal_x_mode_dynamic_ieee(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[Y:%.*]], float nofpclass(nan inf zero sub pnorm) [[X:%.*]]) #[[ATTR8]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf zero sub pnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero psub norm) [[Y]], float nofpclass(nan inf zero sub pnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_negsubnormal_y_negnormal_x_mode_ftpz_ieee(float nofpclass(nan inf zero psub norm) %y, float nofpclass(nan inf zero sub pnorm) %x) denormal_fpenv(float: positivezero|ieee) {
+; CHECK-LABEL: define nofpclass(nan inf zero sub pnorm) float @ret_atan2_negsubnormal_y_negnormal_x_mode_ftpz_ieee(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[Y:%.*]], float nofpclass(nan inf zero sub pnorm) [[X:%.*]]) #[[ATTR9]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf zero sub pnorm) float @llvm.atan2.f32(float nofpclass(nan inf zero psub norm) [[Y]], float nofpclass(nan inf zero sub pnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_negsubnormal_y_negnormal_x_mode_dynamic_dynamic(float nofpclass(nan inf zero psub norm) %y, float nofpclass(nan inf zero sub pnorm) %x) denormal_fpenv(float: dynamic|dynamic) {
+; CHECK-LABEL: define nofpclass(nan inf zero sub) float @ret_atan2_negsubnormal_y_negnormal_x_mode_dynamic_dynamic(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[Y:%.*]], float nofpclass(nan inf zero sub pnorm) [[X:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf zero sub) float @llvm.atan2.f32(float nofpclass(nan inf zero psub norm) [[Y]], float nofpclass(nan inf zero sub pnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_negsubnormal_y_negnormal_x_mode_ftpz_dapz(float nofpclass(nan inf zero psub norm) %y, float nofpclass(nan inf zero sub pnorm) %x) denormal_fpenv(float: positivezero|positivezero) {
+; CHECK-LABEL: define nofpclass(nan inf zero sub) float @ret_atan2_negsubnormal_y_negnormal_x_mode_ftpz_dapz(
+; CHECK-SAME: float nofpclass(nan inf zero psub norm) [[Y:%.*]], float nofpclass(nan inf zero sub pnorm) [[X:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan inf zero sub) float @llvm.atan2.f32(float nofpclass(nan inf zero psub norm) [[Y]], float nofpclass(nan inf zero sub pnorm) [[X]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.atan2.f32(float %y, float %x)
+  ret float %call
+}
+
+define float @ret_atan2_nan_y_mode_dynamic_dynamic(float nofpclass(inf zero sub norm) %y, float %x) denormal_fpenv(float: dynamic|dynamic) {
+; CHECK-LABEL: define nofpclass(inf zero sub norm) float @ret_atan2_nan_y_mode_dynamic_dynamic(
+; CHECK-SAME: float nofpclass(inf zero sub norm) [[Y:%.*]], float [[X:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf zero sub norm) float @llvm.atan2.f32(float nofpclass(inf zero sub norm) [[Y]], float [[X]]) #[[ATTR10]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
-  float %y, float nofpclass(nan inf zero norm psub) %x)
-  denormal_fpenv(float: ieee|dynamic) {
   %call = call float @llvm.atan2.f32(float %y, float %x)
   ret float %call
 }
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-atan2.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-atan2.ll
index 47a6dd2b0d4a0..6af207c88f7c7 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-atan2.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-atan2.ll
@@ -28,3 +28,28 @@ define i1 @atan2_negative_x_is_subnormal(float %y, float %x) {
   %class = call i1 @llvm.is.fpclass.f32(float %atan2, i32 144)
   ret i1 %class
 }
+
+define i1 @atan2_positive_y_is_negzero(float %y, float %x) {
+; CHECK-LABEL: define i1 @atan2_positive_y_is_negzero(
+; CHECK-SAME: float [[Y:%.*]], float [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %abs_y = call float @llvm.fabs.f32(float %y)
+  %positive_y = fadd float %abs_y, 1.000000e+00
+  %atan2 = call float @llvm.atan2.f32(float %positive_y, float %x)
+  %class = call i1 @llvm.is.fpclass.f32(float %atan2, i32 32)
+  ret i1 %class
+}
+
+define i1 @atan2_negative_y_is_poszero(float %y, float %x) {
+; CHECK-LABEL: define i1 @atan2_negative_y_is_poszero(
+; CHECK-SAME: float [[Y:%.*]], float [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %abs_y = call float @llvm.fabs.f32(float %y)
+  %positive_y = fadd float %abs_y, 1.000000e+00
+  %negative_y = fneg float %positive_y
+  %atan2 = call float @llvm.atan2.f32(float %negative_y, float %x)
+  %class = call i1 @llvm.is.fpclass.f32(float %atan2, i32 64)
+  ret i1 %class
+}
diff --git a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
index 8ff84820276d5..4b50789e7e613 100644
--- a/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
+++ b/llvm/unittests/CodeGen/GlobalISel/KnownFPClassTest.cpp
@@ -1804,7 +1804,7 @@ TEST_F(AArch64GISelMITest, TestFPClassFAtan2) {
 }
 
 TEST_F(AArch64GISelMITest, TestFPClassFAtan2NNaN) {
-  // atan2 with two non-NaN inputs is non-NaN and non-Inf.
+  // atan2 with two non-negative finite inputs is non-negative and finite.
   StringRef MIRString = R"(
     %ptr:_(p0) = G_IMPLICIT_DEF
     %y:_(s32) = G_LOAD %ptr(p0) :: (load (s32))
@@ -1822,8 +1822,8 @@ TEST_F(AArch64GISelMITest, TestFPClassFAtan2NNaN) {
   Register SrcReg = FinalCopy->getOperand(1).getReg();
   GISelValueTracking Info(*MF);
   KnownFPClass Known = Info.computeKnownFPClass(SrcReg);
-  EXPECT_EQ(fcFinite, Known.getKnownFPClasses());
-  EXPECT_EQ(std::nullopt, Known.getSignBit());
+  EXPECT_EQ(fcPosFinite, Known.getKnownFPClasses());
+  EXPECT_EQ(false, Known.getSignBit());
 }
 
 // isAbsoluteValueULEOne: x - floor(x) is in [0, 1), so multiplying a known-



More information about the llvm-commits mailing list