Skip to content

Remove FiniteMathOnly and use only NoHonorINFs and NoHonorNANs. #97342

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jul 26, 2024

Conversation

zahiraam
Copy link
Contributor

@zahiraam zahiraam commented Jul 1, 2024

Currently __FINITE_MATH_ONLY__ is set when FiniteMathOnly. And FiniteMathOnly is set when NoHonorInfs && NoHonorNans is true. But what happens one of the latter flags is false?
To avoid potential inconsistencies, the internal option FiniteMathOnly is removed option and the macro __FINITE_MATH_ONLY__ is set when NoHonorInfs && NoHonorNans.

@zahiraam zahiraam changed the title [NFC] Add assertion to ensure that FiniteMathOnly toggles with [NFC] Add assertion to ensure FiniteMathOnly syncs with HonorINFs and HonorNANs. Jul 2, 2024
@zahiraam zahiraam changed the title [NFC] Add assertion to ensure FiniteMathOnly syncs with HonorINFs and HonorNANs. [NFC] Add assertion to ensure FiniteMathOnly is in sync with HonorINFs and HonorNANs. Jul 2, 2024
@zahiraam zahiraam requested a review from AaronBallman July 2, 2024 12:02
Comment on lines 822 to 823
if (!LO.NoHonorInfs || !LO.NoHonorInfs)
assert(!LO.FiniteMathOnly && "FiniteMathOnly implies NoHonorInfs");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about:

assert((LO.FiniteMathOnly == (LO.NoHonorInfs && LO.NoHonorNans)) && "inf/nan inconsistent internal state");

to make it more clear that the state has to be sane no matter which options are set? Also, this corrects the duplicate condition in your if statement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this condition, this command line will assert:
clang.exe -Xclang -menable-no-infs -Xclang -menable-no-nans foo.c
FiniteMathonly is 0 by default.
Do we want that?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds like a logic bug to me -- if infs and nans are disabled, then it's definitely finite math only mode. If infs OR nans are disabled... I think it's also finite math only mode.

Ideally, I think we should get rid of the internal notion of FiniteMathOnly and replace it with the honor infs/nans flags explicitly so we don't get into these sort of situations to begin with. We'd still have to answer how to define __FINITE_MATH_ONLY__, but that seems doable.

CC @andykaylor @jcranmer-intel

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is quite a bit of LIT tests that have that "logic bug" then. They are using -Xclang -menable-no-infs -Xclang -menable-no-nan without the -ffinite-math-only option.

Getting rid of FiniteMathOnly is fine by me.
FiniteMathOnly = !HonorINFs && !HonorNaNs which would mean -menable-no-infs -menable-non-nans. I would think using one or the other of these options could still mean that input values may be either INF or NAN or we can produce them, in which case we wouldn't be in finite math mode.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is quite a bit of LIT tests that have that "logic bug" then. They are using -Xclang -menable-no-infs -Xclang -menable-no-nan without the -ffinite-math-only option.

I didn't mean the logic bug was on the user end; I meant that our internal state has the logic bug because !FiniteMathOnly implies that infinities are supported and so it logically conflicts with !HonorInfs.

Getting rid of FiniteMathOnly is fine by me.
FiniteMathOnly = !HonorINFs && !HonorNaNs which would mean -menable-no-infs -menable-non-nans. I would think using one or the other of these options could still mean that input values may be either INF or NAN or we can produce them, in which case we wouldn't be in finite math mode.

I can see the logic there and I think that's defensible, but because FiniteMathOnly determines the value __FINITE_MATH_ONLY__ expands to, we should probably see how that flag is being used in the wild to see whether folks expect it to be 1 or 0 when infinity is enabled but NAN is disabled (for example).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with Aaron that we should get rid of the internal state that combines these two modes. I expect we'll only want to define __FINITE_MATH_ONLY__ when we aren't honoring either. You could create code that made incorrect assumptions either way, but the state described by the symbol is that we aren't honoring either.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way I see it, nnan and ninf collectively define four modes for math: full types, no-nan, finite-math (nnan + ninf), and the extremely questionable (to me at least) ninf-without-nnan. I don't think FiniteMathOnly as a concept independent of the nnan/ninf flags makes much sense.

There is perhaps room for disagreement as to whether __FINITE_MATH_ONLY__ should be nnan || ninf or nnan && ninf; I lean towards && myself. Looking at sourcegraph, there seems to be essentially three uses of these macros: two to indicate that they don't care about min(NaN) results, and one to avoid using fpclassify.

Copy link

github-actions bot commented Jul 8, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@zahiraam zahiraam requested a review from AaronBallman July 9, 2024 19:18
@zahiraam zahiraam marked this pull request as ready for review July 10, 2024 12:58
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jul 10, 2024
@llvmbot
Copy link
Member

llvmbot commented Jul 10, 2024

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-driver

Author: Zahira Ammarguellat (zahiraam)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/97342.diff

6 Files Affected:

  • (modified) clang/include/clang/Basic/LangOptions.h (+5)
  • (modified) clang/lib/Driver/ToolChains/Clang.cpp (+11-1)
  • (modified) clang/lib/Frontend/InitPreprocessor.cpp (+1-1)
  • (modified) clang/test/CodeGen/fp-floatcontrol-stack.cpp (+1-1)
  • (modified) clang/test/Sema/warn-infinity-nan-disabled-lnx.cpp (+5-4)
  • (modified) clang/test/Sema/warn-infinity-nan-disabled-win.cpp (+4-4)
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 91f1c2f2e6239..e1adcb3a95f18 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -816,6 +816,11 @@ class FPOptions {
     setAllowFPReassociate(LO.AllowFPReassoc);
     setNoHonorNaNs(LO.NoHonorNaNs);
     setNoHonorInfs(LO.NoHonorInfs);
+    // Ensure that if FiniteMathOnly is enabled, NoHonorNaNs and NoHonorInfs are
+    // also enabled. This is because FiniteMathOnly mode assumes no NaNs or Infs
+    // are present in computations.
+    assert((LO.FiniteMathOnly == (LO.NoHonorInfs && LO.NoHonorNaNs)) &&
+           "inf/nan inconsistent internal state");
     setNoSignedZero(LO.NoSignedZero);
     setAllowReciprocal(LO.AllowRecip);
     setAllowApproxFunc(LO.ApproxFunc);
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index aa285c39f14b4..65257e3c4c9d9 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -3295,7 +3295,17 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
   }
 
   // Handle __FINITE_MATH_ONLY__ similarly.
-  if (!HonorINFs && !HonorNaNs)
+  bool InfValues, NanValues = true;
+  auto processArg = [&](const auto *Arg) {
+    if (StringRef(Arg->getValue()) == "-menable-no-nans")
+      NanValues = false;
+    if (StringRef(Arg->getValue()) == "-menable-no-infs")
+      InfValues = false;
+  };
+  for (auto *Arg : Args.filtered(options::OPT_Xclang)) {
+    processArg(Arg);
+  }
+  if ((!HonorINFs && !HonorNaNs) || (!NanValues && !InfValues))
     CmdArgs.push_back("-ffinite-math-only");
 
   if (const Arg *A = Args.getLastArg(options::OPT_mfpmath_EQ)) {
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 5e52555c6fee9..e7e02e9ddf96e 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -1311,7 +1311,7 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
   if (!LangOpts.MathErrno)
     Builder.defineMacro("__NO_MATH_ERRNO__");
 
-  if (LangOpts.FastMath || LangOpts.FiniteMathOnly)
+  if (LangOpts.FastMath || (LangOpts.NoHonorInfs && LangOpts.NoHonorNaNs))
     Builder.defineMacro("__FINITE_MATH_ONLY__", "1");
   else
     Builder.defineMacro("__FINITE_MATH_ONLY__", "0");
diff --git a/clang/test/CodeGen/fp-floatcontrol-stack.cpp b/clang/test/CodeGen/fp-floatcontrol-stack.cpp
index 090da25d21207..237c9d4f9a37e 100644
--- a/clang/test/CodeGen/fp-floatcontrol-stack.cpp
+++ b/clang/test/CodeGen/fp-floatcontrol-stack.cpp
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -triple x86_64-linux-gnu -ffp-contract=on -DDEFAULT=1 -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-DDEFAULT %s
 // RUN: %clang_cc1 -triple x86_64-linux-gnu -ffp-contract=on -DEBSTRICT=1 -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-DEBSTRICT %s
 // RUN: %clang_cc1 -triple x86_64-linux-gnu -DFAST=1 -ffast-math -ffp-contract=fast -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-FAST %s
-// RUN: %clang_cc1 -triple x86_64-linux-gnu -ffp-contract=on -DNOHONOR=1 -menable-no-infs -menable-no-nans -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-NOHONOR %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -ffp-contract=on -DNOHONOR=1 -ffinite-math-only -menable-no-infs -menable-no-nans -emit-llvm -o - %s | FileCheck --check-prefix=CHECK-NOHONOR %s
 
 #define FUN(n) \
   (float z) { return n * z + n; }
diff --git a/clang/test/Sema/warn-infinity-nan-disabled-lnx.cpp b/clang/test/Sema/warn-infinity-nan-disabled-lnx.cpp
index 03a432e05851d..cb719c3a519da 100644
--- a/clang/test/Sema/warn-infinity-nan-disabled-lnx.cpp
+++ b/clang/test/Sema/warn-infinity-nan-disabled-lnx.cpp
@@ -1,10 +1,11 @@
 // RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan \
-// RUN: -triple powerpc64le-unknown-unknown %s -menable-no-infs \
-// RUN: -menable-no-nans -std=c++23
+// RUN: -triple powerpc64le-unknown-unknown %s \
+// RUN: -ffinite-math-only -std=c++23
 
 // RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan \
-// RUN: -triple powerpc64le-unknown-unknown %s -menable-no-infs \
-// RUN: -menable-no-nans -funsafe-math-optimizations -std=c++23
+// RUN: -triple powerpc64le-unknown-unknown %s \
+// RUN: -ffinite-math-only -funsafe-math-optimizations \
+// RUN: -std=c++23
 
 // RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown \
 // RUN: %s -std=c++23
diff --git a/clang/test/Sema/warn-infinity-nan-disabled-win.cpp b/clang/test/Sema/warn-infinity-nan-disabled-win.cpp
index 51f9d325619ba..4bf3355926634 100644
--- a/clang/test/Sema/warn-infinity-nan-disabled-win.cpp
+++ b/clang/test/Sema/warn-infinity-nan-disabled-win.cpp
@@ -2,12 +2,12 @@
 // on Windows the NAN macro is defined using INFINITY. See below.
 
 // RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan \
-// RUN: -triple powerpc64le-unknown-unknown %s -menable-no-infs \
-// RUN: -menable-no-nans -std=c++23
+// RUN: -triple powerpc64le-unknown-unknown %s -ffinite-math-only -std=c++23
 
 // RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan \
-// RUN: -triple powerpc64le-unknown-unknown %s -menable-no-infs \
-// RUN: -menable-no-nans -funsafe-math-optimizations -std=c++23
+// RUN: -triple powerpc64le-unknown-unknown %s \
+// RUN: -ffinite-math-only -funsafe-math-optimizations \
+// RUN: -std=c++23
 
 // RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown \
 // RUN: %s -std=c++23

// Ensure that if FiniteMathOnly is enabled, NoHonorNaNs and NoHonorInfs are
// also enabled. This is because FiniteMathOnly mode assumes no NaNs or Infs
// are present in computations.
assert((LO.FiniteMathOnly == (LO.NoHonorInfs && LO.NoHonorNaNs)) &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FiniteMathOnly variable should be removed from LangOptions entirely at this point; the plan is to only rely on NoHonorInfs and NoHonorNans instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need for the assertion then. I suppose CLFiniteMathOnly should be removed too, right?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, that assertion can go away (don't forget to update the PR title and summary accordingly as well).

I suppose CLFiniteMathOnly should be removed too, right?

CC @AnastasiaStulova as OpenCL code owner.

I think CLFiniteMathOnly is a combination of NoHonorInfs, NoHonorNaNs, and OpenCL language mode. However, I don't see CLFiniteMathOnly being used by anything when I grep over the source. We set the flag from Options.td via marshaling, but we don't seem to ever check the value of the LangOpt anywhere. So it seems like CLFiniteMathOnly can be removed and maybe -cl-finite-math-only could be deprecated and eventually removed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw this option is defined in OpenCL spec so it would be better to keep this at least as a driver option.
https://registry.khronos.org/OpenCL/specs/3.0-unified/html/OpenCL_API.html#optimization-options

I think mapping it to NoHonorInfs, NoHonorNaNs frontend options seems reasonable.

Can the description of this change be elaborated to describe what the intension is.

Tagging @svenvh for any additional feedback.

But mapping

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AnastasiaStulova thanks for the review.
The driver option -cl-finite-math-only can still be used.
Updated the PR's description.

@zahiraam zahiraam changed the title [NFC] Add assertion to ensure FiniteMathOnly is in sync with HonorINFs and HonorNANs. Remove FiniteMathOnly and use only NoHonorINFs and NoHonorNANs. Jul 11, 2024
@@ -3295,7 +3295,17 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
}

// Handle __FINITE_MATH_ONLY__ similarly.
if (!HonorINFs && !HonorNaNs)
bool InfValues, NanValues = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InfValues isn't initialized here

Comment on lines 3305 to 3307
for (auto *Arg : Args.filtered(options::OPT_Xclang)) {
processArg(Arg);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (auto *Arg : Args.filtered(options::OPT_Xclang)) {
processArg(Arg);
}
for (auto *Arg : Args.filtered(options::OPT_Xclang))
processArg(Arg);

Comment on lines 37 to 38
NoHonorNaNs = 0;
NoHonorInfs = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need this code at all because LangOptions.def defines both of these with a default value of 0 anyway, so the x macros above should already do this automatically, right?

Comment on lines 3309 to 3310
for (auto *Arg : Args.filtered(options::OPT_Xclang))
processArg(Arg);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of filtering all arguments to cc1 and looping over all of them, can we use Args.getLastArg()? And can we skip that processing if we already know we're doing to pass -ffinite-math-only because of (!HonorINFs && !HonorNaNs)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't use Args.getLastArg() with arguments to Xclang. Args.filtered() needs to be used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you thinking of something like this?
// Handle __FINITE_MATH_ONLY__ similarly.
if (!HonorINFs && !HonorNaNs)
CmdArgs.push_back("-ffinite-math-only");
else {
bool InfValues = true;
bool NanValues = true;
auto processArg = [&](const auto *Arg) {
if (StringRef(Arg->getValue()) == "-menable-no-nans")
NanValues = false;
if (StringRef(Arg->getValue()) == "-menable-no-infs")
InfValues = false;
};
for (auto *Arg : Args.filtered(options::OPT_Xclang))
processArg(Arg);

if (!NanValues && !InfValues)
CmdArgs.push_back("-ffinite-math-only");
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That could work, I was thinking along these lines:

  // Explanation of why we need to do this dance goes here.
  auto NaNsAndInfs = [&] {
    bool InfValues = true;
    bool NanValues = true;
    auto processArg = [&](const auto *Arg) {
      if (StringRef(Arg->getValue()) == "-menable-no-nans")
        NanValues = false;
      if (StringRef(Arg->getValue()) == "-menable-no-infs")
        InfValues = false;
    };

    for (auto *Arg : Args.filtered(options::OPT_Xclang))
      processArg(Arg);

    return InfValues && NanValues;
  };

  if ((!HonorINFs && !HonorNaNs) || !NaNsAndInfs())
    CmdArgs.push_back("-ffinite-math-only");

I leave it to driver folks to say what they'd prefer if they have a strong preference.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mdtoguchi WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No strong preference - singular addition of -ffinite-math-only is easier to maintain.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I could write something like this as a compromise?
bool shouldAddFiniteMathOnly = false;

if (!HonorINFs && !HonorNaNs) {
shouldAddFiniteMathOnly = true;
} else {
bool InfValues = true;
bool NanValues = true;

for (const auto *Arg : Args.filtered(options::OPT_Xclang)) {
StringRef ArgValue = Arg->getValue();
if (ArgValue == "-menable-no-nans")
NanValues = false;
else if (ArgValue == "-menable-no-infs")
InfValues = false;
}

if (!NanValues && !InfValues)
shouldAddFiniteMathOnly = true;
}

if (shouldAddFiniteMathOnly) {
CmdArgs.push_back("-ffinite-math-only");
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be okay with that approach.

Opts.FastMath || Opts.FiniteMathOnly || Opts.UnsafeFPMath ||
Opts.AllowFPReassoc || Opts.NoHonorNaNs || Opts.NoHonorInfs ||
Opts.NoSignedZero || Opts.AllowRecip || Opts.ApproxFunc;
Opts.FastMath || (Opts.NoHonorInfs && Opts.NoHonorNaNs) ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that NoHonorInfs and NoHonorNaNs are both individually checked below, you don't need this entry here.

@AaronBallman
Copy link
Collaborator

Ping @AnastasiaStulova for OpenCL feedback (this PR impacts an OpenCL command line option).

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes LGTM, I've added @MaskRay and @jansvoboda11 in case they can help answer the OpenCL questions, as it seems @AnastasiaStulova may be unavailable at the moment.

If we don't hear back from anyone in the next two days, I think it's fine to land.

@zahiraam zahiraam requested a review from svenvh July 24, 2024 20:16
@zahiraam zahiraam merged commit c9c91f5 into llvm:main Jul 26, 2024
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants