Skip to content

Commit 22742e2

Browse files
committed
Bundle operands to specify denormal modes
Two new operands are now supported in the "fp.control" operand bundle: * "denorm.in=xxx" - specifies the inpot denormal mode. * "denorm.out=xxx" - specifies the output denormal mode. Here xxx must be one of the following values: * "ieee" - preserve denormals. * "zero" - flush to zero preserving sign. * "pzero" - flush to positive zero. * "dyn" - mode is dynamically read from a control register. These values align those permitted in the "denormal-fp-math" function attribute.
1 parent 1e6b6bf commit 22742e2

File tree

8 files changed

+394
-11
lines changed

8 files changed

+394
-11
lines changed

llvm/docs/LangRef.rst

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3084,7 +3084,10 @@ floating-point control modes and the treatment of status bits respectively.
30843084

30853085
An operand bundle tagged with "fp.control" contains information about the
30863086
control modes used for the operation execution. Operands specified in this
3087-
bundle represent particular options. Currently, only rounding mode is supported.
3087+
bundle represent particular options. The following modes are supported:
3088+
3089+
* rounding mode,
3090+
* denormal behavior.
30883091

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

3109+
Denormal behavior defines whether denormal values are flushed to zero during
3110+
the call's execution. This behavior is specified separately for input and
3111+
output values. Such specification is a string, which starts with
3112+
"denorm.in=" or "denorm.out=" respectively. The remainder of the string should
3113+
be one of the values:
3114+
3115+
::
3116+
3117+
``"ieee"`` - preserve denormals,
3118+
``"zero"`` - flush to +0.0 or -0.0 depending on value sign,
3119+
``"pzero"`` - flush to +0.0,
3120+
``"dyn"`` - concrete mode is read from some register.
3121+
31063122
An operand bundle tagged with "fp.except" may be associated with operations
31073123
that can read or write floating-point exception flags. It contains a single
31083124
metadata string value, which can have one of the following values:

llvm/include/llvm/ADT/FloatingPointMode.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,39 @@ void DenormalMode::print(raw_ostream &OS) const {
234234
OS << denormalModeKindName(Output) << ',' << denormalModeKindName(Input);
235235
}
236236

237+
/// If the specified string represents denormal mode as used in operand bundles,
238+
/// returns the corresponding mode.
239+
inline std::optional<DenormalMode::DenormalModeKind>
240+
parseDenormalKindFromOperandBundle(StringRef Str) {
241+
if (Str == "ieee")
242+
return DenormalMode::IEEE;
243+
if (Str == "zero")
244+
return DenormalMode::PreserveSign;
245+
if (Str == "pzero")
246+
return DenormalMode::PositiveZero;
247+
if (Str == "dyn")
248+
return DenormalMode::Dynamic;
249+
return std::nullopt;
250+
}
251+
252+
/// Converts the specified denormal mode into string suitable for use in an
253+
/// operand bundle.
254+
inline std::optional<StringRef>
255+
printDenormalForOperandBundle(DenormalMode::DenormalModeKind Mode) {
256+
switch (Mode) {
257+
case DenormalMode::IEEE:
258+
return "ieee";
259+
case DenormalMode::PreserveSign:
260+
return "zero";
261+
case DenormalMode::PositiveZero:
262+
return "pzero";
263+
case DenormalMode::Dynamic:
264+
return "dyn";
265+
default:
266+
return std::nullopt;
267+
}
268+
}
269+
237270
/// Floating-point class tests, supported by 'is_fpclass' intrinsic. Actual
238271
/// test may be an OR combination of basic tests.
239272
enum FPClassTest : unsigned {

llvm/include/llvm/IR/InstrTypes.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,12 +1092,24 @@ template <typename InputTy> class OperandBundleDefT {
10921092
using OperandBundleDef = OperandBundleDefT<Value *>;
10931093
using ConstOperandBundleDef = OperandBundleDefT<const Value *>;
10941094

1095+
std::optional<StringRef> getBundleOperandByPrefix(OperandBundleUse Bundle,
1096+
StringRef Prefix);
1097+
void addOperandToBundleTag(LLVMContext &Ctx,
1098+
SmallVectorImpl<OperandBundleDef> &Bundles,
1099+
StringRef Tag, size_t PrefixSize, StringRef Val);
1100+
10951101
void addFPRoundingBundle(LLVMContext &Ctx,
10961102
SmallVectorImpl<OperandBundleDef> &Bundles,
10971103
RoundingMode Rounding);
10981104
void addFPExceptionBundle(LLVMContext &Ctx,
10991105
SmallVectorImpl<OperandBundleDef> &Bundles,
11001106
fp::ExceptionBehavior Except);
1107+
void addFPInputDenormBundle(LLVMContext &Ctx,
1108+
SmallVectorImpl<OperandBundleDef> &Bundles,
1109+
DenormalMode::DenormalModeKind Mode);
1110+
void addFPOutputDenormBundle(LLVMContext &Ctx,
1111+
SmallVectorImpl<OperandBundleDef> &Bundles,
1112+
DenormalMode::DenormalModeKind Mode);
11011113

11021114
//===----------------------------------------------------------------------===//
11031115
// CallBase Class
@@ -2171,6 +2183,15 @@ class CallBase : public Instruction {
21712183
/// Return exception behavior specified for this call.
21722184
fp::ExceptionBehavior getExceptionBehavior() const;
21732185

2186+
/// Return input denormal mode specified by operand bundles.
2187+
DenormalMode::DenormalModeKind getInputDenormMode() const;
2188+
2189+
/// Return output denormal mode specified by operand bundles.
2190+
DenormalMode::DenormalModeKind getOutputDenormMode() const;
2191+
2192+
/// Return input and output denormal modes specified by operand bundles.
2193+
DenormalMode getDenormMode() const;
2194+
21742195
/// Used to keep track of an operand bundle. See the main comment on
21752196
/// OperandBundleUser above.
21762197
struct BundleOpInfo {

llvm/lib/Analysis/ConstantFolding.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,6 +1354,11 @@ static ConstantFP *flushDenormalConstantFP(ConstantFP *CFP,
13541354
if (!APF.isDenormal())
13551355
return CFP;
13561356

1357+
if (auto *CB = dyn_cast<CallBase>(Inst)) {
1358+
auto Mode = IsOutput ? CB->getOutputDenormMode() : CB->getInputDenormMode();
1359+
return flushDenormalConstant(CFP->getType(), APF, Mode);
1360+
}
1361+
13571362
DenormalMode Mode = getInstrDenormalMode(Inst, CFP->getType());
13581363
return flushDenormalConstant(CFP->getType(), APF,
13591364
IsOutput ? Mode.Output : Mode.Input);
@@ -2976,12 +2981,20 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
29762981
}
29772982

29782983
if (const auto *Op1 = dyn_cast<ConstantFP>(Operands[0])) {
2979-
const APFloat &Op1V = Op1->getValueAPF();
2984+
ConstantFP *Op1F =
2985+
flushDenormalConstantFP(const_cast<ConstantFP *>(Op1), Call, false);
2986+
if (!Op1F)
2987+
return nullptr;
2988+
const APFloat &Op1V = Op1F->getValueAPF();
29802989

29812990
if (const auto *Op2 = dyn_cast<ConstantFP>(Operands[1])) {
29822991
if (Op2->getType() != Op1->getType())
29832992
return nullptr;
2984-
const APFloat &Op2V = Op2->getValueAPF();
2993+
ConstantFP *Op2F =
2994+
flushDenormalConstantFP(const_cast<ConstantFP *>(Op2), Call, false);
2995+
if (!Op2F)
2996+
return nullptr;
2997+
const APFloat &Op2V = Op2F->getValueAPF();
29852998

29862999
if (const auto *ConstrIntr =
29873000
dyn_cast_if_present<ConstrainedFPIntrinsic>(Call)) {
@@ -3011,8 +3024,11 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
30113024
return evaluateCompare(Op1V, Op2V, ConstrIntr);
30123025
}
30133026
if (mayFoldConstrained(const_cast<ConstrainedFPIntrinsic *>(ConstrIntr),
3014-
St))
3015-
return ConstantFP::get(Ty->getContext(), Res);
3027+
St)) {
3028+
auto DenormOut = Call->getOutputDenormMode();
3029+
DenormalMode::DenormalModeKind Mode = DenormOut;
3030+
return flushDenormalConstant(Op2->getType(), Res, Mode);
3031+
}
30163032
return nullptr;
30173033
}
30183034

llvm/lib/IR/Instructions.cpp

Lines changed: 162 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -628,13 +628,17 @@ bool CallBase::hasClobberingOperandBundles() const {
628628
RoundingMode CallBase::getRoundingMode() const {
629629
// Try reading rounding mode from FP bundle.
630630
std::optional<RoundingMode> RM;
631-
if (auto RoundingBundle = getOperandBundle(LLVMContext::OB_fp_control)) {
632-
Value *V = RoundingBundle->Inputs.front();
633-
Metadata *MD = cast<MetadataAsValue>(V)->getMetadata();
634-
RM = convertBundleToRoundingMode(cast<MDString>(MD)->getString());
631+
if (auto ControlBundle = getOperandBundle(LLVMContext::OB_fp_control)) {
632+
for (auto &U : ControlBundle->Inputs) {
633+
Value *V = U.get();
634+
if (auto *MDV = dyn_cast<MetadataAsValue>(V)) {
635+
Metadata *MD = MDV->getMetadata();
636+
if (auto *MDS = dyn_cast<MDString>(MD))
637+
if (auto RM = convertBundleToRoundingMode(MDS->getString()))
638+
return *RM;
639+
}
640+
}
635641
}
636-
if (RM)
637-
return *RM;
638642

639643
// If this is a constrained intrinsic, get rounding mode from its metadata
640644
// arguments.
@@ -678,6 +682,71 @@ fp::ExceptionBehavior CallBase::getExceptionBehavior() const {
678682
return fp::ebIgnore;
679683
}
680684

685+
DenormalMode::DenormalModeKind CallBase::getInputDenormMode() const {
686+
if (auto InDenormBundle = getOperandBundle(LLVMContext::OB_fp_control)) {
687+
auto DenormOperand =
688+
getBundleOperandByPrefix(*InDenormBundle, "denorm.in=");
689+
if (DenormOperand) {
690+
if (auto Mode = parseDenormalKindFromOperandBundle(*DenormOperand))
691+
return *Mode;
692+
} else {
693+
return DenormalMode::IEEE;
694+
}
695+
}
696+
697+
if (!getParent())
698+
return DenormalMode::IEEE;
699+
const Function *F = getFunction();
700+
if (!F)
701+
return DenormalMode::IEEE;
702+
703+
Type *Ty = nullptr;
704+
for (auto &A : args())
705+
if (auto *T = A.get()->getType(); T->isFPOrFPVectorTy()) {
706+
Ty = T;
707+
break;
708+
}
709+
assert(Ty && "Some input argument must be of floating-point type");
710+
711+
Ty = Ty->getScalarType();
712+
return F->getDenormalMode(Ty->getFltSemantics()).Input;
713+
}
714+
715+
DenormalMode::DenormalModeKind CallBase::getOutputDenormMode() const {
716+
if (auto InDenormBundle = getOperandBundle(LLVMContext::OB_fp_control)) {
717+
auto DenormOperand =
718+
getBundleOperandByPrefix(*InDenormBundle, "denorm.out=");
719+
if (DenormOperand) {
720+
if (auto Mode = parseDenormalKindFromOperandBundle(*DenormOperand))
721+
return *Mode;
722+
} else {
723+
return DenormalMode::IEEE;
724+
}
725+
}
726+
727+
if (!getParent())
728+
return DenormalMode::IEEE;
729+
const Function *F = getFunction();
730+
if (!F)
731+
return DenormalMode::IEEE;
732+
733+
Type *Ty = getType();
734+
assert(Ty->isFPOrFPVectorTy() && "Unexpected output type");
735+
736+
Ty = Ty->getScalarType();
737+
return F->getDenormalMode(Ty->getFltSemantics()).Output;
738+
}
739+
740+
DenormalMode CallBase::getDenormMode() const {
741+
auto InputMode = getInputDenormMode();
742+
auto OutputMode = getOutputDenormMode();
743+
if (!InputMode)
744+
InputMode = DenormalMode::IEEE;
745+
if (!OutputMode)
746+
OutputMode = DenormalMode::IEEE;
747+
return DenormalMode(OutputMode, InputMode);
748+
}
749+
681750
MemoryEffects CallBase::getFloatingPointMemoryEffects() const {
682751
if (Intrinsic::ID IntrID = getIntrinsicID())
683752
if (const BasicBlock *BB = getParent())
@@ -794,6 +863,69 @@ bool CallBase::hasArgumentWithAdditionalReturnCaptureComponents() const {
794863
return false;
795864
}
796865

866+
std::optional<StringRef> llvm::getBundleOperandByPrefix(OperandBundleUse Bundle,
867+
StringRef Prefix) {
868+
for (const auto &Item : Bundle.Inputs) {
869+
Metadata *MD = cast<MetadataAsValue>(Item.get())->getMetadata();
870+
if (const auto *MDS = dyn_cast<MDString>(MD)) {
871+
StringRef Str = MDS->getString();
872+
if (Str.consume_front(Prefix))
873+
return Str;
874+
}
875+
}
876+
return std::nullopt;
877+
}
878+
879+
void llvm::addOperandToBundleTag(LLVMContext &Ctx,
880+
SmallVectorImpl<OperandBundleDef> &Bundles,
881+
StringRef Tag, size_t PrefixSize,
882+
StringRef Val) {
883+
assert(PrefixSize > 0 && "Unexpected prefix size");
884+
assert(PrefixSize < Val.size() && "Invalid prefix size");
885+
StringRef Prefix = Val.take_front(PrefixSize);
886+
887+
// Find a bundle with the specified tag.
888+
OperandBundleDef *Bundle = nullptr;
889+
for (OperandBundleDef &OB : Bundles) {
890+
if (OB.getTag() == Tag) {
891+
Bundle = &OB;
892+
break;
893+
}
894+
}
895+
896+
// If no such bundle are found, create new one with the single value.
897+
if (!Bundle) {
898+
auto *MStr = MDString::get(Ctx, Val);
899+
auto *MD = MetadataAsValue::get(Ctx, MStr);
900+
SmallVector<Value *, 1> BundleValues(1, MD);
901+
Bundles.emplace_back(Tag.str(), BundleValues);
902+
return;
903+
}
904+
905+
// If the bundle is found, search its values for those started with the given
906+
// prefix.
907+
SmallVector<Value *, 4> Values(Bundle->inputs());
908+
for (auto i = Values.begin(), e = Values.end(); i != e; ++i) {
909+
if (auto *MV = dyn_cast<MetadataAsValue>(*i)) {
910+
if (auto *MD = dyn_cast<MDString>(MV->getMetadata())) {
911+
StringRef Str = MD->getString();
912+
if (Str == Val)
913+
// Already in the values.
914+
return;
915+
if (Str.starts_with(Prefix)) {
916+
Values.erase(i);
917+
break;
918+
}
919+
}
920+
}
921+
}
922+
923+
auto *ValMD = MDString::get(Ctx, Val);
924+
auto *MD = MetadataAsValue::get(Ctx, ValMD);
925+
Values.push_back(MD);
926+
Bundles.emplace_back("fp.control", Values);
927+
}
928+
797929
void llvm::addFPRoundingBundle(LLVMContext &Ctx,
798930
SmallVectorImpl<OperandBundleDef> &Bundles,
799931
RoundingMode Rounding) {
@@ -814,6 +946,30 @@ void llvm::addFPExceptionBundle(LLVMContext &Ctx,
814946
Bundles.emplace_back("fp.except", EB);
815947
}
816948

949+
void llvm::addFPInputDenormBundle(LLVMContext &Ctx,
950+
SmallVectorImpl<OperandBundleDef> &Bundles,
951+
DenormalMode::DenormalModeKind Mode) {
952+
std::optional<StringRef> DenormValue = printDenormalForOperandBundle(Mode);
953+
if (!DenormValue)
954+
return;
955+
std::string Prefix = "denorm.in=";
956+
std::string DenormItem = Prefix + DenormValue->str();
957+
958+
addOperandToBundleTag(Ctx, Bundles, "fp.control", Prefix.size(), DenormItem);
959+
}
960+
961+
void llvm::addFPOutputDenormBundle(LLVMContext &Ctx,
962+
SmallVectorImpl<OperandBundleDef> &Bundles,
963+
DenormalMode::DenormalModeKind Mode) {
964+
std::optional<StringRef> DenormValue = printDenormalForOperandBundle(Mode);
965+
if (!DenormValue)
966+
return;
967+
std::string Prefix = "denorm.out=";
968+
std::string DenormItem = Prefix + DenormValue->str();
969+
970+
addOperandToBundleTag(Ctx, Bundles, "fp.control", Prefix.size(), DenormItem);
971+
}
972+
817973
//===----------------------------------------------------------------------===//
818974
// CallInst Implementation
819975
//===----------------------------------------------------------------------===//

llvm/lib/IR/Verifier.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3833,6 +3833,8 @@ void Verifier::visitCallBase(CallBase &Call) {
38333833
Check(!FoundFpeControlBundle, "Multiple \"fp.control\" operand bundles",
38343834
Call);
38353835
bool FoundRoundingMode = false;
3836+
bool FoundInDenormalMode = false;
3837+
bool FoundOutDenormalMode = false;
38363838
for (auto &U : BU.Inputs) {
38373839
Value *V = U.get();
38383840
Check(isa<MetadataAsValue>(V),
@@ -3847,6 +3849,18 @@ void Verifier::visitCallBase(CallBase &Call) {
38473849
Check(!FoundRoundingMode, "Rounding mode is specified more that once",
38483850
Call);
38493851
FoundRoundingMode = true;
3852+
} else if (Item.consume_front("denorm.in=")) {
3853+
Check(!FoundInDenormalMode,
3854+
"Input denormal mode is specified more that once", Call);
3855+
FoundInDenormalMode = true;
3856+
Check(parseDenormalKindFromOperandBundle(Item),
3857+
"Invalid input denormal mode", Call);
3858+
} else if (Item.consume_front("denorm.out=")) {
3859+
Check(!FoundOutDenormalMode,
3860+
"Output denormal mode is specified more that once", Call);
3861+
FoundOutDenormalMode = true;
3862+
Check(parseDenormalKindFromOperandBundle(Item),
3863+
"Invalid output denormal mode", Call);
38503864
} else {
38513865
CheckFailed("Unrecognized value in \"fp.control\" bundle operand",
38523866
Call);

0 commit comments

Comments
 (0)