Files
llvm-project/llvm/lib/Support/FloatingPointMode.cpp
Matt Arsenault 2502e3b7ba IR: Promote "denormal-fp-math" to a first class attribute (#174293)
Convert "denormal-fp-math" and "denormal-fp-math-f32" into a first
class denormal_fpenv attribute. Previously the query for the effective
denormal mode involved two string attribute queries with parsing. I'm
introducing more uses of this, so it makes sense to convert this
to a more efficient encoding. The old representation was also awkward
since it was split across two separate attributes. The new encoding
just stores the default and float modes as bitfields, largely avoiding
the need to consider if the other mode is set.

The syntax in the common cases looks like this:
  `denormal_fpenv(preservesign,preservesign)`
  `denormal_fpenv(float: preservesign,preservesign)`
  `denormal_fpenv(dynamic,dynamic float: preservesign,preservesign)`

I wasn't sure about reusing the float type name instead of adding a
new keyword. It's parsed as a type but only accepts float. I'm also
debating switching the name to subnormal to match the current
preferred IEEE terminology (also used by nofpclass and other
contexts).

This has a behavior change when using the command flag debug
options to set the denormal mode. The behavior of the flag
ignored functions with an explicit attribute set, per
the default and f32 version. Now that these are one attribute,
the flag logic can't distinguish which of the two components
were explicitly set on the function. Only one test appeared to
rely on this behavior, so I just avoided using the flags in it.

This also does not perform all the code cleanups this enables.
In particular the attributor handling could be cleaned up.

I also guessed at how to support this in MLIR. I followed
MemoryEffects as a reference; it appears bitfields are expanded
into arguments to attributes, so the representation there is
a bit uglier with the 2 2-element fields flattened into 4 arguments.
2026-02-05 13:31:26 +00:00

176 lines
4.9 KiB
C++

//===- FloatingPointMode.cpp ------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/FloatingPointMode.h"
#include "llvm/ADT/StringExtras.h"
using namespace llvm;
FPClassTest llvm::fneg(FPClassTest Mask) {
FPClassTest NewMask = Mask & fcNan;
if (Mask & fcNegInf)
NewMask |= fcPosInf;
if (Mask & fcNegNormal)
NewMask |= fcPosNormal;
if (Mask & fcNegSubnormal)
NewMask |= fcPosSubnormal;
if (Mask & fcNegZero)
NewMask |= fcPosZero;
if (Mask & fcPosZero)
NewMask |= fcNegZero;
if (Mask & fcPosSubnormal)
NewMask |= fcNegSubnormal;
if (Mask & fcPosNormal)
NewMask |= fcNegNormal;
if (Mask & fcPosInf)
NewMask |= fcNegInf;
return NewMask;
}
FPClassTest llvm::inverse_fabs(FPClassTest Mask) {
FPClassTest NewMask = Mask & fcNan;
if (Mask & fcPosZero)
NewMask |= fcZero;
if (Mask & fcPosSubnormal)
NewMask |= fcSubnormal;
if (Mask & fcPosNormal)
NewMask |= fcNormal;
if (Mask & fcPosInf)
NewMask |= fcInf;
return NewMask;
}
FPClassTest llvm::unknown_sign(FPClassTest Mask) {
FPClassTest NewMask = Mask & fcNan;
if (Mask & fcZero)
NewMask |= fcZero;
if (Mask & fcSubnormal)
NewMask |= fcSubnormal;
if (Mask & fcNormal)
NewMask |= fcNormal;
if (Mask & fcInf)
NewMask |= fcInf;
return NewMask;
}
// Every bitfield has a unique name and one or more aliasing names that cover
// multiple bits. Names should be listed in order of preference, with higher
// popcounts listed first.
//
// Bits are consumed as printed. Each field should only be represented in one
// printed field.
static constexpr std::pair<FPClassTest, StringLiteral> NoFPClassName[] = {
{fcAllFlags, "all"},
{fcNan, "nan"},
{fcSNan, "snan"},
{fcQNan, "qnan"},
{fcInf, "inf"},
{fcNegInf, "ninf"},
{fcPosInf, "pinf"},
{fcZero, "zero"},
{fcNegZero, "nzero"},
{fcPosZero, "pzero"},
{fcSubnormal, "sub"},
{fcNegSubnormal, "nsub"},
{fcPosSubnormal, "psub"},
{fcNormal, "norm"},
{fcNegNormal, "nnorm"},
{fcPosNormal, "pnorm"}
};
raw_ostream &llvm::operator<<(raw_ostream &OS, FPClassTest Mask) {
OS << '(';
if (Mask == fcNone) {
OS << "none)";
return OS;
}
ListSeparator LS(" ");
for (auto [BitTest, Name] : NoFPClassName) {
if ((Mask & BitTest) == BitTest) {
OS << LS << Name;
// Clear the bits so we don't print any aliased names later.
Mask &= ~BitTest;
}
}
assert(Mask == 0 && "didn't print some mask bits");
OS << ')';
return OS;
}
void DenormalFPEnv::print(raw_ostream &OS, bool OmitIfSame) const {
if (F32Mode == DefaultMode) {
DefaultMode.print(OS, /*Legacy=*/false, OmitIfSame);
return;
}
// Omit printing the base mode if only the f32 mode isn't the default.
if (DefaultMode != DenormalMode::getDefault()) {
DefaultMode.print(OS, /*Legacy=*/false, OmitIfSame);
OS << ", ";
}
OS << "float: ";
F32Mode.print(OS, /*Legacy=*/false, OmitIfSame);
}
static bool cannotOrderStrictlyGreaterImpl(FPClassTest LHS, FPClassTest RHS,
bool OrEqual, bool OrderedZero) {
LHS &= ~fcNan;
RHS &= ~fcNan;
if (LHS == fcNone || RHS == fcNone)
return true;
FPClassTest LowestBitRHS = static_cast<FPClassTest>(RHS & -RHS);
FPClassTest HighestBitLHS = static_cast<FPClassTest>(1 << Log2_32(LHS));
if (!OrderedZero) {
// Introduce conflict in zero bits if we're treating them as equal.
if (LowestBitRHS == fcNegZero)
LowestBitRHS = fcPosZero;
if (HighestBitLHS == fcNegZero)
HighestBitLHS = fcPosZero;
}
if (LowestBitRHS > HighestBitLHS) {
assert((LHS & RHS) == fcNone && "no bits should intersect");
return true;
}
if (LowestBitRHS < HighestBitLHS)
return false;
constexpr FPClassTest ExactValuesMask = fcZero | fcInf;
return !OrEqual && (LowestBitRHS & ExactValuesMask) != fcNone;
}
bool llvm::cannotOrderStrictlyGreater(FPClassTest LHS, FPClassTest RHS,
bool OrderedZeroSign) {
return cannotOrderStrictlyGreaterImpl(LHS, RHS, false, OrderedZeroSign);
}
bool llvm::cannotOrderStrictlyGreaterEq(FPClassTest LHS, FPClassTest RHS,
bool OrderedZeroSign) {
return cannotOrderStrictlyGreaterImpl(LHS, RHS, true, OrderedZeroSign);
}
bool llvm::cannotOrderStrictlyLess(FPClassTest LHS, FPClassTest RHS,
bool OrderedZeroSign) {
return cannotOrderStrictlyGreaterImpl(RHS, LHS, false, OrderedZeroSign);
}
bool llvm::cannotOrderStrictlyLessEq(FPClassTest LHS, FPClassTest RHS,
bool OrderedZeroSign) {
return cannotOrderStrictlyGreaterImpl(RHS, LHS, true, OrderedZeroSign);
}