Skip to content

Commit 850ec7b

Browse files
committed
Attributor: Try to propagate concrete denormal-fp-math{-f32}
Allow specialization of functions with "dynamic" denormal modes to a known IEEE or DAZ mode based on callers. This should make it possible to implement a is-denormal-flushing-enabled test using llvm.canonicalize and have it be free after LTO. https://reviews.llvm.org/D156129
1 parent 099410b commit 850ec7b

File tree

5 files changed

+284
-40
lines changed

5 files changed

+284
-40
lines changed

llvm/include/llvm/ADT/FloatingPointMode.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,11 @@ struct DenormalMode {
9696
DenormalModeKind Input = DenormalModeKind::Invalid;
9797

9898
constexpr DenormalMode() = default;
99+
constexpr DenormalMode(const DenormalMode &) = default;
99100
constexpr DenormalMode(DenormalModeKind Out, DenormalModeKind In) :
100101
Output(Out), Input(In) {}
101102

103+
DenormalMode &operator=(const DenormalMode &) = default;
102104

103105
static constexpr DenormalMode getInvalid() {
104106
return DenormalMode(DenormalModeKind::Invalid, DenormalModeKind::Invalid);

llvm/include/llvm/Transforms/IPO/Attributor.h

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1918,6 +1918,7 @@ struct Attributor {
19181918
/// Remove all \p AttrKinds attached to \p IRP.
19191919
ChangeStatus removeAttrs(const IRPosition &IRP,
19201920
const ArrayRef<Attribute::AttrKind> &AttrKinds);
1921+
ChangeStatus removeAttrs(const IRPosition &IRP, ArrayRef<StringRef> Attrs);
19211922

19221923
/// Attach \p DeducedAttrs to \p IRP, if \p ForceReplace is set we do this
19231924
/// even if the same attribute kind was already present.
@@ -5101,6 +5102,98 @@ template <typename MemberTy> struct PotentialValuesState : AbstractState {
51015102
bool UndefIsContained;
51025103
};
51035104

5105+
struct DenormalFPMathState : public AbstractState {
5106+
struct DenormalState {
5107+
DenormalMode Mode = DenormalMode::getInvalid();
5108+
DenormalMode ModeF32 = DenormalMode::getInvalid();
5109+
5110+
bool operator==(const DenormalState Other) const {
5111+
return Mode == Other.Mode && ModeF32 == Other.ModeF32;
5112+
}
5113+
5114+
bool operator!=(const DenormalState Other) const {
5115+
return Mode != Other.Mode || ModeF32 != Other.ModeF32;
5116+
}
5117+
5118+
bool isValid() const {
5119+
return Mode.isValid() && ModeF32.isValid();
5120+
}
5121+
5122+
static DenormalMode::DenormalModeKind
5123+
unionDenormalKind(DenormalMode::DenormalModeKind Callee,
5124+
DenormalMode::DenormalModeKind Caller) {
5125+
if (Caller == Callee)
5126+
return Caller;
5127+
if (Callee == DenormalMode::Dynamic)
5128+
return Caller;
5129+
if (Caller == DenormalMode::Dynamic)
5130+
return Callee;
5131+
return DenormalMode::Invalid;
5132+
}
5133+
5134+
static DenormalMode unionAssumed(DenormalMode Callee, DenormalMode Caller) {
5135+
return DenormalMode{unionDenormalKind(Callee.Output, Caller.Output),
5136+
unionDenormalKind(Callee.Input, Caller.Input)};
5137+
}
5138+
5139+
DenormalState unionWith(DenormalState Caller) const {
5140+
DenormalState Callee(*this);
5141+
Callee.Mode = unionAssumed(Callee.Mode, Caller.Mode);
5142+
Callee.ModeF32 = unionAssumed(Callee.ModeF32, Caller.ModeF32);
5143+
return Callee;
5144+
}
5145+
};
5146+
5147+
DenormalState Known;
5148+
5149+
/// Explicitly track whether we've hit a fixed point.
5150+
bool IsAtFixedpoint = false;
5151+
5152+
DenormalFPMathState() = default;
5153+
5154+
DenormalState getKnown() const { return Known; }
5155+
5156+
// There's only really known or unknown, there's no speculatively assumable
5157+
// state.
5158+
DenormalState getAssumed() const { return Known; }
5159+
5160+
bool isValidState() const override {
5161+
return Known.isValid();
5162+
}
5163+
5164+
/// Return true if there are no dynamic components to the denormal mode worth
5165+
/// specializing.
5166+
bool isModeFixed() const {
5167+
return Known.Mode.Input != DenormalMode::Dynamic &&
5168+
Known.Mode.Output != DenormalMode::Dynamic &&
5169+
Known.ModeF32.Input != DenormalMode::Dynamic &&
5170+
Known.ModeF32.Output != DenormalMode::Dynamic;
5171+
}
5172+
5173+
bool isAtFixpoint() const override {
5174+
return IsAtFixedpoint;
5175+
}
5176+
5177+
ChangeStatus indicateFixpoint() {
5178+
bool Changed = !IsAtFixedpoint;
5179+
IsAtFixedpoint = true;
5180+
return Changed ? ChangeStatus::CHANGED : ChangeStatus::UNCHANGED;
5181+
}
5182+
5183+
ChangeStatus indicateOptimisticFixpoint() override {
5184+
return indicateFixpoint();
5185+
}
5186+
5187+
ChangeStatus indicatePessimisticFixpoint() override {
5188+
return indicateFixpoint();
5189+
}
5190+
5191+
DenormalFPMathState operator^=(const DenormalFPMathState &Caller) {
5192+
Known = Known.unionWith(Caller.getKnown());
5193+
return *this;
5194+
}
5195+
};
5196+
51045197
using PotentialConstantIntValuesState = PotentialValuesState<APInt>;
51055198
using PotentialLLVMValuesState =
51065199
PotentialValuesState<std::pair<AA::ValueAndContext, AA::ValueScope>>;
@@ -6029,6 +6122,8 @@ struct AAPointerInfo : public AbstractAttribute {
60296122
static const char ID;
60306123
};
60316124

6125+
raw_ostream &operator<<(raw_ostream &, const AAPointerInfo::Access &);
6126+
60326127
/// An abstract attribute for getting assumption information.
60336128
struct AAAssumptionInfo
60346129
: public StateWrapper<SetState<StringRef>, AbstractAttribute,
@@ -6221,6 +6316,36 @@ struct AAIndirectCallInfo
62216316

62226317
/// This function should return true if the type of the \p AA is
62236318
/// AAIndirectCallInfo
6319+
/// This function should return true if the type of the \p AA is
6320+
/// AADenormalFPMath.
6321+
static bool classof(const AbstractAttribute *AA) {
6322+
return (AA->getIdAddr() == &ID);
6323+
}
6324+
6325+
/// Unique ID (due to the unique address)
6326+
static const char ID;
6327+
};
6328+
6329+
/// An abstract Attribute for specializing "dynamic" components of
6330+
/// "denormal-fp-math" and "denormal-fp-math-f32" to a known denormal mode.
6331+
struct AADenormalFPMath
6332+
: public StateWrapper<DenormalFPMathState, AbstractAttribute> {
6333+
using Base = StateWrapper<DenormalFPMathState, AbstractAttribute>;
6334+
6335+
AADenormalFPMath(const IRPosition &IRP, Attributor &A) : Base(IRP) {}
6336+
6337+
/// Create an abstract attribute view for the position \p IRP.
6338+
static AADenormalFPMath &createForPosition(const IRPosition &IRP,
6339+
Attributor &A);
6340+
6341+
/// See AbstractAttribute::getName()
6342+
const std::string getName() const override { return "AADenormalFPMath"; }
6343+
6344+
/// See AbstractAttribute::getIdAddr()
6345+
const char *getIdAddr() const override { return &ID; }
6346+
6347+
/// This function should return true if the type of the \p AA is
6348+
/// AADenormalFPMath.
62246349
static bool classof(const AbstractAttribute *AA) {
62256350
return (AA->getIdAddr() == &ID);
62266351
}

llvm/lib/Transforms/IPO/Attributor.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,6 +1229,19 @@ Attributor::removeAttrs(const IRPosition &IRP,
12291229
return updateAttrMap<Attribute::AttrKind>(IRP, AttrKinds, RemoveAttrCB);
12301230
}
12311231

1232+
ChangeStatus Attributor::removeAttrs(const IRPosition &IRP,
1233+
ArrayRef<StringRef> Attrs) {
1234+
auto RemoveAttrCB = [&](StringRef Attr, AttributeSet AttrSet,
1235+
AttributeMask &AM, AttrBuilder &) -> bool {
1236+
if (!AttrSet.hasAttribute(Attr))
1237+
return false;
1238+
AM.addAttribute(Attr);
1239+
return true;
1240+
};
1241+
1242+
return updateAttrMap<StringRef>(IRP, Attrs, RemoveAttrCB);
1243+
}
1244+
12321245
ChangeStatus Attributor::manifestAttrs(const IRPosition &IRP,
12331246
const ArrayRef<Attribute> &Attrs,
12341247
bool ForceReplace) {
@@ -3389,6 +3402,14 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
33893402
// Every function can track active assumptions.
33903403
getOrCreateAAFor<AAAssumptionInfo>(FPos);
33913404

3405+
// If we're not using a dynamic mode for float, there's nothing worthwhile
3406+
// to infer. This misses the edge case denormal-fp-math="dynamic" and
3407+
// denormal-fp-math-f32=something, but that likely has no real world use.
3408+
DenormalMode Mode = F.getDenormalMode(APFloat::IEEEsingle());
3409+
if (Mode.Input == DenormalMode::Dynamic ||
3410+
Mode.Output == DenormalMode::Dynamic)
3411+
getOrCreateAAFor<AADenormalFPMath>(FPos);
3412+
33923413
// Return attributes are only appropriate if the return type is non void.
33933414
Type *ReturnType = F.getReturnType();
33943415
if (!ReturnType->isVoidTy()) {

llvm/lib/Transforms/IPO/AttributorAttributes.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ PIPE_OPERATOR(AAUnderlyingObjects)
194194
PIPE_OPERATOR(AAAddressSpace)
195195
PIPE_OPERATOR(AAIndirectCallInfo)
196196
PIPE_OPERATOR(AAGlobalValueInfo)
197+
PIPE_OPERATOR(AADenormalFPMath)
197198

198199
#undef PIPE_OPERATOR
199200

@@ -8951,6 +8952,108 @@ struct AAMemoryLocationCallSite final : AAMemoryLocationImpl {
89518952
};
89528953
} // namespace
89538954

8955+
/// ------------------ denormal-fp-math Attribute -------------------------
8956+
8957+
namespace {
8958+
struct AADenormalFPMathImpl : public AADenormalFPMath {
8959+
AADenormalFPMathImpl(const IRPosition &IRP, Attributor &A)
8960+
: AADenormalFPMath(IRP, A) {}
8961+
8962+
const std::string getAsStr(Attributor *A) const override {
8963+
std::string Str("AADenormalFPMath[");
8964+
raw_string_ostream OS(Str);
8965+
8966+
DenormalState Known = getKnown();
8967+
if (Known.Mode.isValid())
8968+
OS << "denormal-fp-math=" << Known.Mode;
8969+
else
8970+
OS << "invalid";
8971+
8972+
if (Known.ModeF32.isValid())
8973+
OS << " denormal-fp-math-f32=" << Known.ModeF32;
8974+
OS << ']';
8975+
return OS.str();
8976+
}
8977+
};
8978+
8979+
struct AADenormalFPMathFunction final : AADenormalFPMathImpl {
8980+
AADenormalFPMathFunction(const IRPosition &IRP, Attributor &A)
8981+
: AADenormalFPMathImpl(IRP, A) {}
8982+
8983+
void initialize(Attributor &A) override {
8984+
const Function *F = getAnchorScope();
8985+
DenormalMode Mode = F->getDenormalModeRaw();
8986+
DenormalMode ModeF32 = F->getDenormalModeF32Raw();
8987+
8988+
// TODO: Handling this here prevents handling the case where a callee has a
8989+
// fixed denormal-fp-math with dynamic denormal-fp-math-f32, but called from
8990+
// a function with a fully fixed mode.
8991+
if (ModeF32 == DenormalMode::getInvalid())
8992+
ModeF32 = Mode;
8993+
Known = DenormalState{Mode, ModeF32};
8994+
if (isModeFixed())
8995+
indicateFixpoint();
8996+
}
8997+
8998+
ChangeStatus updateImpl(Attributor &A) override {
8999+
ChangeStatus Change = ChangeStatus::UNCHANGED;
9000+
9001+
auto CheckCallSite = [=, &Change, &A](AbstractCallSite CS) {
9002+
Function *Caller = CS.getInstruction()->getFunction();
9003+
LLVM_DEBUG(dbgs() << "[AADenormalFPMath] Call " << Caller->getName()
9004+
<< "->" << getAssociatedFunction()->getName() << '\n');
9005+
9006+
const auto *CallerInfo = A.getAAFor<AADenormalFPMath>(
9007+
*this, IRPosition::function(*Caller), DepClassTy::REQUIRED);
9008+
if (!CallerInfo)
9009+
return false;
9010+
9011+
Change = Change | clampStateAndIndicateChange(this->getState(),
9012+
CallerInfo->getState());
9013+
return true;
9014+
};
9015+
9016+
bool AllCallSitesKnown = true;
9017+
if (!A.checkForAllCallSites(CheckCallSite, *this, true, AllCallSitesKnown))
9018+
return indicatePessimisticFixpoint();
9019+
9020+
if (Change == ChangeStatus::CHANGED && isModeFixed())
9021+
indicateFixpoint();
9022+
return Change;
9023+
}
9024+
9025+
ChangeStatus manifest(Attributor &A) override {
9026+
LLVMContext &Ctx = getAssociatedFunction()->getContext();
9027+
9028+
SmallVector<Attribute, 2> AttrToAdd;
9029+
SmallVector<StringRef, 2> AttrToRemove;
9030+
if (Known.Mode == DenormalMode::getDefault()) {
9031+
AttrToRemove.push_back("denormal-fp-math");
9032+
} else {
9033+
AttrToAdd.push_back(
9034+
Attribute::get(Ctx, "denormal-fp-math", Known.Mode.str()));
9035+
}
9036+
9037+
if (Known.ModeF32 != Known.Mode) {
9038+
AttrToAdd.push_back(
9039+
Attribute::get(Ctx, "denormal-fp-math-f32", Known.ModeF32.str()));
9040+
} else {
9041+
AttrToRemove.push_back("denormal-fp-math-f32");
9042+
}
9043+
9044+
auto &IRP = getIRPosition();
9045+
9046+
// TODO: There should be a combined add and remove API.
9047+
return A.removeAttrs(IRP, AttrToRemove) |
9048+
A.manifestAttrs(IRP, AttrToAdd, /*ForceReplace=*/true);
9049+
}
9050+
9051+
void trackStatistics() const override {
9052+
STATS_DECLTRACK_FN_ATTR(denormal_fp_math)
9053+
}
9054+
};
9055+
} // namespace
9056+
89549057
/// ------------------ Value Constant Range Attribute -------------------------
89559058

89569059
namespace {
@@ -12705,6 +12808,7 @@ const char AAUnderlyingObjects::ID = 0;
1270512808
const char AAAddressSpace::ID = 0;
1270612809
const char AAIndirectCallInfo::ID = 0;
1270712810
const char AAGlobalValueInfo::ID = 0;
12811+
const char AADenormalFPMath::ID = 0;
1270812812

1270912813
// Macro magic to create the static generator function for attributes that
1271012814
// follow the naming scheme.
@@ -12851,6 +12955,7 @@ CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAUndefinedBehavior)
1285112955
CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANonConvergent)
1285212956
CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAIntraFnReachability)
1285312957
CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAInterFnReachability)
12958+
CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AADenormalFPMath)
1285412959

1285512960
CREATE_NON_RET_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAMemoryBehavior)
1285612961

0 commit comments

Comments
 (0)