Skip to content

Bundle operands to specify denormal modes #136501

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

Open
wants to merge 3 commits into
base: users/spavloff/fadd
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3084,7 +3084,10 @@ floating-point control modes and the treatment of status bits respectively.

An operand bundle tagged with "fp.control" contains information about the
control modes used for the operation execution. Operands specified in this
bundle represent particular options. Currently, only rounding mode is supported.
bundle represent particular options. The following modes are supported:

* rounding mode,
* denormal behavior.

Rounding mode is represented by a metadata string value, which specifies the
the mode used for the operation evaluation. Possible values are:
Expand All @@ -3103,6 +3106,34 @@ rounding rounding mode is taken from the control register (dynamic rounding).
In the particular case of :ref:`default floating-point environment <floatenv>`,
the operation uses rounding to nearest, ties to even.

Denormal behavior defines whether denormal values are flushed to zero during
the call's execution. This behavior is specified separately for input and
output values. Such specification is a string, which starts with
"denorm.in=" or "denorm.out=" respectively. The remainder of the string should
be one of the values:

::

"ieee" - preserve denormals,
"zero" - flush to +0.0 or -0.0 depending on value sign,
"pzero" - flush to +0.0,
"dyn" - concrete mode is read from some register.

Such bundle operand specifies denormal behavior for all floating-point types.
It is possible to override denormal behavior for specific type, if the target
supports that. Now only type "f32" allows such overriding, if a bundle operand
has prefix "denorm.f32.in=" or "denormal.f32.out=", its specifies denormal mode
for ``float`` values in the affected instruction. For example:

.. code-block:: llvm

; denormal arguments are flushed to zero preserving sign.
call float @llvm.trunc.f32(float %x) [ "fp.control"(metadata !"denorm.in=ieee", metadata !"denorm.f32.in=zero") ]

; denormal arguments are flushed to +0.0.
call float @llvm.trunc.f32(float %x) [ "fp.control"(metadata !"denorm.in=pzero") ]


An operand bundle tagged with "fp.except" may be associated with operations
that can read or write floating-point exception flags. It contains a single
metadata string value, which can have one of the following values:
Expand Down
33 changes: 33 additions & 0 deletions llvm/include/llvm/ADT/FloatingPointMode.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,39 @@ void DenormalMode::print(raw_ostream &OS) const {
OS << denormalModeKindName(Output) << ',' << denormalModeKindName(Input);
}

/// If the specified string represents denormal mode as used in operand bundles,
/// returns the corresponding mode.
inline std::optional<DenormalMode::DenormalModeKind>
parseDenormalKindFromOperandBundle(StringRef Str) {
if (Str == "ieee")
return DenormalMode::IEEE;
if (Str == "zero")
return DenormalMode::PreserveSign;
if (Str == "pzero")
return DenormalMode::PositiveZero;
if (Str == "dyn")
return DenormalMode::Dynamic;
return std::nullopt;
}

/// Converts the specified denormal mode into string suitable for use in an
/// operand bundle.
inline std::optional<StringRef>
printDenormalForOperandBundle(DenormalMode::DenormalModeKind Mode) {
switch (Mode) {
case DenormalMode::IEEE:
return "ieee";
case DenormalMode::PreserveSign:
return "zero";
case DenormalMode::PositiveZero:
return "pzero";
case DenormalMode::Dynamic:
return "dyn";
default:
return std::nullopt;
}
}

/// Floating-point class tests, supported by 'is_fpclass' intrinsic. Actual
/// test may be an OR combination of basic tests.
enum FPClassTest : unsigned {
Expand Down
31 changes: 31 additions & 0 deletions llvm/include/llvm/IR/InstrTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1092,12 +1092,27 @@ template <typename InputTy> class OperandBundleDefT {
using OperandBundleDef = OperandBundleDefT<Value *>;
using ConstOperandBundleDef = OperandBundleDefT<const Value *>;

std::optional<StringRef> getBundleOperandByPrefix(OperandBundleUse Bundle,
StringRef Prefix);
void addOperandToBundleTag(LLVMContext &Ctx,
SmallVectorImpl<OperandBundleDef> &Bundles,
StringRef Tag, size_t PrefixSize, StringRef Val);

void addFPRoundingBundle(LLVMContext &Ctx,
SmallVectorImpl<OperandBundleDef> &Bundles,
RoundingMode Rounding);
void addFPExceptionBundle(LLVMContext &Ctx,
SmallVectorImpl<OperandBundleDef> &Bundles,
fp::ExceptionBehavior Except);
std::optional<DenormalMode::DenormalModeKind>
getDenormModeBundle(const OperandBundleUse &Control, bool Unput,
const fltSemantics *FPSem);
void addInputDenormBundle(LLVMContext &Ctx,
SmallVectorImpl<OperandBundleDef> &Bundles,
DenormalMode::DenormalModeKind Mode);
void addOutputDenormBundle(LLVMContext &Ctx,
SmallVectorImpl<OperandBundleDef> &Bundles,
DenormalMode::DenormalModeKind Mode);

//===----------------------------------------------------------------------===//
// CallBase Class
Expand Down Expand Up @@ -2171,6 +2186,22 @@ class CallBase : public Instruction {
/// Return exception behavior specified for this call.
fp::ExceptionBehavior getExceptionBehavior() const;

/// Return input denormal mode specified by operand bundles.
std::optional<DenormalMode::DenormalModeKind>
getInputDenormMode(const fltSemantics *FPSem = nullptr) const;

/// Return output denormal mode specified by operand bundles.
std::optional<DenormalMode::DenormalModeKind>
getOutputDenormMode(const fltSemantics *FPSem = nullptr) const;

/// Return input denormal mode specified by operand bundles.
std::optional<DenormalMode::DenormalModeKind>
getInputDenormModeFromBundle(const fltSemantics *FPSem = nullptr) const;

/// Return output denormal mode specified by operand bundles.
std::optional<DenormalMode::DenormalModeKind>
getOutputDenormModeFromBundle(const fltSemantics *FPSem = nullptr) const;

/// Used to keep track of an operand bundle. See the main comment on
/// OperandBundleUser above.
struct BundleOpInfo {
Expand Down
23 changes: 19 additions & 4 deletions llvm/lib/Analysis/ConstantFolding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,11 @@ static ConstantFP *flushDenormalConstantFP(ConstantFP *CFP,
if (!APF.isDenormal())
return CFP;

if (auto *CB = dyn_cast_or_null<CallBase>(Inst)) {
auto Mode = IsOutput ? CB->getOutputDenormMode() : CB->getInputDenormMode();
return flushDenormalConstant(CFP->getType(), APF, *Mode);
}

DenormalMode Mode = getInstrDenormalMode(Inst, CFP->getType());
return flushDenormalConstant(CFP->getType(), APF,
IsOutput ? Mode.Output : Mode.Input);
Expand Down Expand Up @@ -2976,12 +2981,20 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
}

if (const auto *Op1 = dyn_cast<ConstantFP>(Operands[0])) {
const APFloat &Op1V = Op1->getValueAPF();
ConstantFP *Op1F =
flushDenormalConstantFP(const_cast<ConstantFP *>(Op1), Call, false);
if (!Op1F)
return nullptr;
const APFloat &Op1V = Op1F->getValueAPF();

if (const auto *Op2 = dyn_cast<ConstantFP>(Operands[1])) {
if (Op2->getType() != Op1->getType())
return nullptr;
const APFloat &Op2V = Op2->getValueAPF();
ConstantFP *Op2F =
flushDenormalConstantFP(const_cast<ConstantFP *>(Op2), Call, false);
if (!Op2F)
return nullptr;
const APFloat &Op2V = Op2F->getValueAPF();

if (const auto *ConstrIntr =
dyn_cast_if_present<ConstrainedFPIntrinsic>(Call)) {
Expand Down Expand Up @@ -3011,8 +3024,10 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
return evaluateCompare(Op1V, Op2V, ConstrIntr);
}
if (mayFoldConstrained(const_cast<ConstrainedFPIntrinsic *>(ConstrIntr),
St))
return ConstantFP::get(Ty->getContext(), Res);
St)) {
DenormalMode::DenormalModeKind Mode = *Call->getOutputDenormMode();
return flushDenormalConstant(Op2->getType(), Res, Mode);
}
return nullptr;
}

Expand Down
Loading
Loading