Index: llvm/include/llvm/ADT/FloatingPointMode.h =================================================================== --- llvm/include/llvm/ADT/FloatingPointMode.h +++ llvm/include/llvm/ADT/FloatingPointMode.h @@ -96,9 +96,11 @@ DenormalModeKind Input = DenormalModeKind::Invalid; constexpr DenormalMode() = default; + constexpr DenormalMode(const DenormalMode &) = default; constexpr DenormalMode(DenormalModeKind Out, DenormalModeKind In) : Output(Out), Input(In) {} + DenormalMode &operator=(const DenormalMode &) = default; static constexpr DenormalMode getInvalid() { return DenormalMode(DenormalModeKind::Invalid, DenormalModeKind::Invalid); Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -1918,6 +1918,7 @@ /// Remove all \p AttrKinds attached to \p IRP. ChangeStatus removeAttrs(const IRPosition &IRP, const ArrayRef &AttrKinds); + ChangeStatus removeAttrs(const IRPosition &IRP, ArrayRef Attrs); /// Attach \p DeducedAttrs to \p IRP, if \p ForceReplace is set we do this /// even if the same attribute kind was already present. @@ -5101,6 +5102,98 @@ bool UndefIsContained; }; +struct DenormalFPMathState : public AbstractState { + struct DenormalState { + DenormalMode Mode = DenormalMode::getInvalid(); + DenormalMode ModeF32 = DenormalMode::getInvalid(); + + bool operator==(const DenormalState Other) const { + return Mode == Other.Mode && ModeF32 == Other.ModeF32; + } + + bool operator!=(const DenormalState Other) const { + return Mode != Other.Mode || ModeF32 != Other.ModeF32; + } + + bool isValid() const { + return Mode.isValid() && ModeF32.isValid(); + } + + static DenormalMode::DenormalModeKind + unionDenormalKind(DenormalMode::DenormalModeKind Callee, + DenormalMode::DenormalModeKind Caller) { + if (Caller == Callee) + return Caller; + if (Callee == DenormalMode::Dynamic) + return Caller; + if (Caller == DenormalMode::Dynamic) + return Callee; + return DenormalMode::Invalid; + } + + static DenormalMode unionAssumed(DenormalMode Callee, DenormalMode Caller) { + return DenormalMode{unionDenormalKind(Callee.Output, Caller.Output), + unionDenormalKind(Callee.Input, Caller.Input)}; + } + + DenormalState unionWith(DenormalState Caller) const { + DenormalState Callee(*this); + Callee.Mode = unionAssumed(Callee.Mode, Caller.Mode); + Callee.ModeF32 = unionAssumed(Callee.ModeF32, Caller.ModeF32); + return Callee; + } + }; + + DenormalState Known; + + /// Explicitly track whether we've hit a fixed point. + bool IsAtFixedpoint = false; + + DenormalFPMathState() = default; + + DenormalState getKnown() const { return Known; } + + // There's only really known or unknown, there's no speculatively assumable + // state. + DenormalState getAssumed() const { return Known; } + + bool isValidState() const override { + return Known.isValid(); + } + + /// Return true if there are no dynamic components to the denormal mode worth + /// specializing. + bool isModeFixed() const { + return Known.Mode.Input != DenormalMode::Dynamic && + Known.Mode.Output != DenormalMode::Dynamic && + Known.ModeF32.Input != DenormalMode::Dynamic && + Known.ModeF32.Output != DenormalMode::Dynamic; + } + + bool isAtFixpoint() const override { + return IsAtFixedpoint; + } + + ChangeStatus indicateFixpoint() { + bool Changed = !IsAtFixedpoint; + IsAtFixedpoint = true; + return Changed ? ChangeStatus::CHANGED : ChangeStatus::UNCHANGED; + } + + ChangeStatus indicateOptimisticFixpoint() override { + return indicateFixpoint(); + } + + ChangeStatus indicatePessimisticFixpoint() override { + return indicateFixpoint(); + } + + DenormalFPMathState operator^=(const DenormalFPMathState &Caller) { + Known = Known.unionWith(Caller.getKnown()); + return *this; + } +}; + using PotentialConstantIntValuesState = PotentialValuesState; using PotentialLLVMValuesState = PotentialValuesState>; @@ -6029,6 +6122,8 @@ static const char ID; }; +raw_ostream &operator<<(raw_ostream &, const AAPointerInfo::Access &); + /// An abstract attribute for getting assumption information. struct AAAssumptionInfo : public StateWrapper, AbstractAttribute, @@ -6221,6 +6316,36 @@ /// This function should return true if the type of the \p AA is /// AAIndirectCallInfo + /// This function should return true if the type of the \p AA is + /// AADenormalFPMath. + static bool classof(const AbstractAttribute *AA) { + return (AA->getIdAddr() == &ID); + } + + /// Unique ID (due to the unique address) + static const char ID; +}; + +/// An abstract Attribute for specializing "dynamic" components of +/// "denormal-fp-math" and "denormal-fp-math-f32" to a known denormal mode. +struct AADenormalFPMath + : public StateWrapper { + using Base = StateWrapper; + + AADenormalFPMath(const IRPosition &IRP, Attributor &A) : Base(IRP) {} + + /// Create an abstract attribute view for the position \p IRP. + static AADenormalFPMath &createForPosition(const IRPosition &IRP, + Attributor &A); + + /// See AbstractAttribute::getName() + const std::string getName() const override { return "AADenormalFPMath"; } + + /// See AbstractAttribute::getIdAddr() + const char *getIdAddr() const override { return &ID; } + + /// This function should return true if the type of the \p AA is + /// AADenormalFPMath. static bool classof(const AbstractAttribute *AA) { return (AA->getIdAddr() == &ID); } Index: llvm/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/lib/Transforms/IPO/Attributor.cpp +++ llvm/lib/Transforms/IPO/Attributor.cpp @@ -1229,6 +1229,19 @@ return updateAttrMap(IRP, AttrKinds, RemoveAttrCB); } +ChangeStatus Attributor::removeAttrs(const IRPosition &IRP, + ArrayRef Attrs) { + auto RemoveAttrCB = [&](StringRef Attr, AttributeSet AttrSet, + AttributeMask &AM, AttrBuilder &) -> bool { + if (!AttrSet.hasAttribute(Attr)) + return false; + AM.addAttribute(Attr); + return true; + }; + + return updateAttrMap(IRP, Attrs, RemoveAttrCB); +} + ChangeStatus Attributor::manifestAttrs(const IRPosition &IRP, const ArrayRef &Attrs, bool ForceReplace) { @@ -3389,6 +3402,14 @@ // Every function can track active assumptions. getOrCreateAAFor(FPos); + // If we're not using a dynamic mode for float, there's nothing worthwhile + // to infer. This misses the edge case denormal-fp-math="dynamic" and + // denormal-fp-math-f32=something, but that likely has no real world use. + DenormalMode Mode = F.getDenormalMode(APFloat::IEEEsingle()); + if (Mode.Input == DenormalMode::Dynamic || + Mode.Output == DenormalMode::Dynamic) + getOrCreateAAFor(FPos); + // Return attributes are only appropriate if the return type is non void. Type *ReturnType = F.getReturnType(); if (!ReturnType->isVoidTy()) { Index: llvm/lib/Transforms/IPO/AttributorAttributes.cpp =================================================================== --- llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -194,6 +194,7 @@ PIPE_OPERATOR(AAAddressSpace) PIPE_OPERATOR(AAIndirectCallInfo) PIPE_OPERATOR(AAGlobalValueInfo) +PIPE_OPERATOR(AADenormalFPMath) #undef PIPE_OPERATOR @@ -8951,6 +8952,108 @@ }; } // namespace +/// ------------------ denormal-fp-math Attribute ------------------------- + +namespace { +struct AADenormalFPMathImpl : public AADenormalFPMath { + AADenormalFPMathImpl(const IRPosition &IRP, Attributor &A) + : AADenormalFPMath(IRP, A) {} + + const std::string getAsStr(Attributor *A) const override { + std::string Str("AADenormalFPMath["); + raw_string_ostream OS(Str); + + DenormalState Known = getKnown(); + if (Known.Mode.isValid()) + OS << "denormal-fp-math=" << Known.Mode; + else + OS << "invalid"; + + if (Known.ModeF32.isValid()) + OS << " denormal-fp-math-f32=" << Known.ModeF32; + OS << ']'; + return OS.str(); + } +}; + +struct AADenormalFPMathFunction final : AADenormalFPMathImpl { + AADenormalFPMathFunction(const IRPosition &IRP, Attributor &A) + : AADenormalFPMathImpl(IRP, A) {} + + void initialize(Attributor &A) override { + const Function *F = getAnchorScope(); + DenormalMode Mode = F->getDenormalModeRaw(); + DenormalMode ModeF32 = F->getDenormalModeF32Raw(); + + // TODO: Handling this here prevents handling the case where a caller has a + // fixed denormal-fp-math with dynamic denormal-fp-math-f32, but called from + // a function with a fully fixed mode. + if (ModeF32 == DenormalMode::getInvalid()) + ModeF32 = Mode; + Known = DenormalState{Mode, ModeF32}; + if (isModeFixed()) + indicateFixpoint(); + } + + ChangeStatus updateImpl(Attributor &A) override { + ChangeStatus Change = ChangeStatus::UNCHANGED; + + auto CheckCallSite = [=, &Change, &A](AbstractCallSite CS) { + Function *Caller = CS.getInstruction()->getFunction(); + LLVM_DEBUG(dbgs() << "[AADenormalFPMath] Call " << Caller->getName() + << "->" << getAssociatedFunction()->getName() << '\n'); + + const auto *CallerInfo = A.getAAFor( + *this, IRPosition::function(*Caller), DepClassTy::REQUIRED); + if (!CallerInfo) + return false; + + Change = Change | clampStateAndIndicateChange(this->getState(), + CallerInfo->getState()); + return true; + }; + + bool AllCallSitesKnown = true; + if (!A.checkForAllCallSites(CheckCallSite, *this, true, AllCallSitesKnown)) + return indicatePessimisticFixpoint(); + + if (Change == ChangeStatus::CHANGED && isModeFixed()) + indicateFixpoint(); + return Change; + } + + ChangeStatus manifest(Attributor &A) override { + LLVMContext &Ctx = getAssociatedFunction()->getContext(); + + SmallVector AttrToAdd; + SmallVector AttrToRemove; + if (Known.Mode == DenormalMode::getDefault()) { + AttrToRemove.push_back("denormal-fp-math"); + } else { + AttrToAdd.push_back( + Attribute::get(Ctx, "denormal-fp-math", Known.Mode.str())); + } + + if (Known.ModeF32 != Known.Mode) { + AttrToAdd.push_back( + Attribute::get(Ctx, "denormal-fp-math-f32", Known.ModeF32.str())); + } else { + AttrToRemove.push_back("denormal-fp-math-f32"); + } + + auto &IRP = getIRPosition(); + + // TODO: There should be a combined add and remove API. + return A.removeAttrs(IRP, AttrToRemove) | + A.manifestAttrs(IRP, AttrToAdd, /*ForceReplace=*/true); + } + + void trackStatistics() const override { + STATS_DECLTRACK_FN_ATTR(denormal_fp_math) + } +}; +} // namespace + /// ------------------ Value Constant Range Attribute ------------------------- namespace { @@ -12705,6 +12808,7 @@ const char AAAddressSpace::ID = 0; const char AAIndirectCallInfo::ID = 0; const char AAGlobalValueInfo::ID = 0; +const char AADenormalFPMath::ID = 0; // Macro magic to create the static generator function for attributes that // follow the naming scheme. @@ -12851,6 +12955,7 @@ CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANonConvergent) CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAIntraFnReachability) CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAInterFnReachability) +CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AADenormalFPMath) CREATE_NON_RET_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAMemoryBehavior) Index: llvm/test/Transforms/Attributor/denormal-fp-math.ll =================================================================== --- llvm/test/Transforms/Attributor/denormal-fp-math.ll +++ llvm/test/Transforms/Attributor/denormal-fp-math.ll @@ -6,8 +6,7 @@ ; Should infer to ieee,ieee/default define internal void @leaf_dynamic_dynamic_from_ieee_ieee() #0 { -; CHECK-LABEL: define internal void @leaf_dynamic_dynamic_from_ieee_ieee -; CHECK-SAME: () #[[ATTR0:[0-9]+]] { +; CHECK-LABEL: define internal void @leaf_dynamic_dynamic_from_ieee_ieee() { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: ret void ; @@ -17,8 +16,7 @@ ; Should infer to ieee,ieee/default define internal void @leaf_recursive_dynamic_dynamic_from_ieee_ieee() #0 { -; CHECK-LABEL: define internal void @leaf_recursive_dynamic_dynamic_from_ieee_ieee -; CHECK-SAME: () #[[ATTR0]] { +; CHECK-LABEL: define internal void @leaf_recursive_dynamic_dynamic_from_ieee_ieee() { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: call void @leaf_recursive_dynamic_dynamic_from_ieee_ieee() ; CHECK-NEXT: ret void @@ -30,8 +28,7 @@ ; Should strip denormal-fp-math for default ieee define internal void @leaf_f64_ieee_f64_ieee__f32_dynamic_f32_dynamic__dynamic_dynamic_from_ieee_ieee() #1 { -; CHECK-LABEL: define internal void @leaf_f64_ieee_f64_ieee__f32_dynamic_f32_dynamic__dynamic_dynamic_from_ieee_ieee -; CHECK-SAME: () #[[ATTR1:[0-9]+]] { +; CHECK-LABEL: define internal void @leaf_f64_ieee_f64_ieee__f32_dynamic_f32_dynamic__dynamic_dynamic_from_ieee_ieee() { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: ret void ; @@ -42,7 +39,7 @@ ; Should infer to preserve-sign,preserve-sign define internal void @leaf_dynamic_dynamic_from_daz_daz() #0 { ; CHECK-LABEL: define internal void @leaf_dynamic_dynamic_from_daz_daz -; CHECK-SAME: () #[[ATTR0]] { +; CHECK-SAME: () #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: ret void ; @@ -53,7 +50,7 @@ ; Should strip denormal-fp-math for default ieee, and have denormal-fp-math-f32=preserve-sign,preserve-sign define internal void @leaf_f64_ieee_f64_ieee__f32_dynamic_f32_dynamic__dynamic_dynamic_from_daz_daz() #1 { ; CHECK-LABEL: define internal void @leaf_f64_ieee_f64_ieee__f32_dynamic_f32_dynamic__dynamic_dynamic_from_daz_daz -; CHECK-SAME: () #[[ATTR1]] { +; CHECK-SAME: () #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: ret void ; @@ -64,7 +61,7 @@ ; Leave this alone, must stay dynamic,dynamic define internal void @leaf_dynamic_dynamic_from_daz_daz_and_ieee_ieee() #0 { ; CHECK-LABEL: define internal void @leaf_dynamic_dynamic_from_daz_daz_and_ieee_ieee -; CHECK-SAME: () #[[ATTR0]] { +; CHECK-SAME: () #[[ATTR2:[0-9]+]] { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: ret void ; @@ -86,7 +83,7 @@ ; Leave as dynamic,dynamic define void @externally_visible_dynamic_dynamic_from_ieee_ieee() #0 { ; CHECK-LABEL: define void @externally_visible_dynamic_dynamic_from_ieee_ieee -; CHECK-SAME: () #[[ATTR0]] { +; CHECK-SAME: () #[[ATTR2]] { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: ret void ; @@ -97,7 +94,7 @@ ; Should infer to positive-zero,positive-zero define internal void @leaf_dynamic_dynamic_from_dapz_dapz() #0 { ; CHECK-LABEL: define internal void @leaf_dynamic_dynamic_from_dapz_dapz -; CHECK-SAME: () #[[ATTR0]] { +; CHECK-SAME: () #[[ATTR3:[0-9]+]] { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: ret void ; @@ -107,8 +104,7 @@ ; ieee,ieee entry point define void @func_ieee_ieee() #2 { -; CHECK-LABEL: define void @func_ieee_ieee -; CHECK-SAME: () #[[ATTR2:[0-9]+]] { +; CHECK-LABEL: define void @func_ieee_ieee() { ; CHECK-NEXT: call void @leaf_dynamic_dynamic_from_ieee_ieee() ; CHECK-NEXT: call void @leaf_f64_ieee_f64_ieee__f32_dynamic_f32_dynamic__dynamic_dynamic_from_ieee_ieee() ; CHECK-NEXT: call void @leaf_dynamic_dynamic_from_daz_daz_and_ieee_ieee() @@ -157,7 +153,7 @@ ; preserve-sign,preserve-sign entry point define void @func_daz_daz() #3 { ; CHECK-LABEL: define void @func_daz_daz -; CHECK-SAME: () #[[ATTR3:[0-9]+]] { +; CHECK-SAME: () #[[ATTR0]] { ; CHECK-NEXT: call void @leaf_dynamic_dynamic_from_daz_daz() ; CHECK-NEXT: call void @leaf_dynamic_dynamic_from_daz_daz_and_ieee_ieee() ; CHECK-NEXT: call void @leaf_f64_ieee_f64_ieee__f32_dynamic_f32_dynamic__dynamic_dynamic_from_daz_daz_and_ieee_ieee() @@ -176,7 +172,7 @@ ; positive-zero,positive-zero entry point define void @func_dapz_dapz() #4 { ; CHECK-LABEL: define void @func_dapz_dapz -; CHECK-SAME: () #[[ATTR4:[0-9]+]] { +; CHECK-SAME: () #[[ATTR3]] { ; CHECK-NEXT: call void @leaf_dynamic_dynamic_from_dapz_dapz() ; CHECK-NEXT: ret void ; @@ -188,7 +184,7 @@ ; realistic case and we don't bother trying to handle it. define internal void @leaf_f64_dynamic_f64_dynamic__f32_daz_f32_daz_from__daz_daz() #5 { ; CHECK-LABEL: define internal void @leaf_f64_dynamic_f64_dynamic__f32_daz_f32_daz_from__daz_daz -; CHECK-SAME: () #[[ATTR5:[0-9]+]] { +; CHECK-SAME: () #[[ATTR4:[0-9]+]] { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: ret void ; @@ -198,8 +194,7 @@ ; -> ieee,ieee define internal void @leaf_dynamic_ieee_from_ieee_ieee() #6 { -; CHECK-LABEL: define internal void @leaf_dynamic_ieee_from_ieee_ieee -; CHECK-SAME: () #[[ATTR6:[0-9]+]] { +; CHECK-LABEL: define internal void @leaf_dynamic_ieee_from_ieee_ieee() { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: ret void ; @@ -209,8 +204,7 @@ ; -> ieee,ieee define internal void @leaf_ieee_dynamic_from_ieee_ieee() #7 { -; CHECK-LABEL: define internal void @leaf_ieee_dynamic_from_ieee_ieee -; CHECK-SAME: () #[[ATTR7:[0-9]+]] { +; CHECK-LABEL: define internal void @leaf_ieee_dynamic_from_ieee_ieee() { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: ret void ; @@ -221,7 +215,7 @@ ; Specialize the f64 mode to ieee,ieee but leave f32 as dynamic,dynamic define internal void @leaf_dynamic_dynamic_from_f64_ieee_f32_dynamic() #0 { ; CHECK-LABEL: define internal void @leaf_dynamic_dynamic_from_f64_ieee_f32_dynamic -; CHECK-SAME: () #[[ATTR0]] { +; CHECK-SAME: () #[[ATTR1]] { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: ret void ; @@ -242,7 +236,7 @@ ; -> preserve-sign,ieee. define internal void @leaf_daz_dynamic_from_dynamic_ieee() #8 { ; CHECK-LABEL: define internal void @leaf_daz_dynamic_from_dynamic_ieee -; CHECK-SAME: () #[[ATTR8:[0-9]+]] { +; CHECK-SAME: () #[[ATTR5:[0-9]+]] { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: ret void ; @@ -252,7 +246,7 @@ define void @dynamic_ieee() #6 { ; CHECK-LABEL: define void @dynamic_ieee -; CHECK-SAME: () #[[ATTR6]] { +; CHECK-SAME: () #[[ATTR6:[0-9]+]] { ; CHECK-NEXT: call void @leaf_daz_dynamic_from_dynamic_ieee() ; CHECK-NEXT: ret void ; @@ -283,7 +277,7 @@ ; Leave unchanged as preserve-sign,preserve-sign define internal void @leaf_daz_daz_from_dynamic() #3 { ; CHECK-LABEL: define internal void @leaf_daz_daz_from_dynamic -; CHECK-SAME: () #[[ATTR3]] { +; CHECK-SAME: () #[[ATTR0]] { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: ret void ; @@ -293,7 +287,7 @@ define void @dynamic_dynamic() #0 { ; CHECK-LABEL: define void @dynamic_dynamic -; CHECK-SAME: () #[[ATTR0]] { +; CHECK-SAME: () #[[ATTR2]] { ; CHECK-NEXT: call void @leaf_ieee_ieee_from_dynamic() ; CHECK-NEXT: call void @leaf_daz_daz_from_dynamic() ; CHECK-NEXT: call void @leaf_dynamic_from_dynamic() @@ -307,7 +301,7 @@ define internal void @leaf_ieee_f64_daz_f32() #9 { ; CHECK-LABEL: define internal void @leaf_ieee_f64_daz_f32 -; CHECK-SAME: () #[[ATTR9:[0-9]+]] { +; CHECK-SAME: () #[[ATTR7:[0-9]+]] { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: ret void ; @@ -317,7 +311,7 @@ define internal void @leaf_ieee_f64_daz_f32_from_ieee_f64_dynamic_f32() #10 { ; CHECK-LABEL: define internal void @leaf_ieee_f64_daz_f32_from_ieee_f64_dynamic_f32 -; CHECK-SAME: () #[[ATTR10:[0-9]+]] { +; CHECK-SAME: () #[[ATTR8:[0-9]+]] { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: ret void ; @@ -340,7 +334,7 @@ ; => "preserve-sign,positive-zero" + "denormal-fp-math-f32"="ieee,positive-zero" define internal void @leaf_daz_dynamic_dynamic_dapz_from_daz_dapz_ieee_dapz() #11 { ; CHECK-LABEL: define internal void @leaf_daz_dynamic_dynamic_dapz_from_daz_dapz_ieee_dapz -; CHECK-SAME: () #[[ATTR11:[0-9]+]] { +; CHECK-SAME: () #[[ATTR9:[0-9]+]] { ; CHECK-NEXT: call void @call_of_mystery() ; CHECK-NEXT: ret void ; @@ -350,7 +344,7 @@ define void @daz_dapz_ieee_dapz() #12 { ; CHECK-LABEL: define void @daz_dapz_ieee_dapz -; CHECK-SAME: () #[[ATTR12:[0-9]+]] { +; CHECK-SAME: () #[[ATTR9]] { ; CHECK-NEXT: call void @leaf_daz_dynamic_dynamic_dapz_from_daz_dapz_ieee_dapz() ; CHECK-NEXT: ret void ; @@ -372,17 +366,14 @@ attributes #11 = { "denormal-fp-math"="preserve-sign,dynamic" "denormal-fp-math-f32"="dynamic,positive-zero" } attributes #12 = { "denormal-fp-math"="preserve-sign,positive-zero" "denormal-fp-math-f32"="ieee,positive-zero" } ;. -; CHECK: attributes #[[ATTR0]] = { "denormal-fp-math"="dynamic,dynamic" } +; CHECK: attributes #[[ATTR0]] = { "denormal-fp-math"="preserve-sign,preserve-sign" } ; CHECK: attributes #[[ATTR1]] = { "denormal-fp-math-f32"="dynamic,dynamic" } -; CHECK: attributes #[[ATTR2]] = { "denormal-fp-math"="ieee,ieee" } -; CHECK: attributes #[[ATTR3]] = { "denormal-fp-math"="preserve-sign,preserve-sign" } -; CHECK: attributes #[[ATTR4]] = { "denormal-fp-math"="positive-zero,positive-zero" } -; CHECK: attributes #[[ATTR5]] = { "denormal-fp-math"="dynamic,dynamic" "denormal-fp-math-f32"="preserve-sign,preserve-sign" } +; CHECK: attributes #[[ATTR2]] = { "denormal-fp-math"="dynamic,dynamic" } +; CHECK: attributes #[[ATTR3]] = { "denormal-fp-math"="positive-zero,positive-zero" } +; CHECK: attributes #[[ATTR4]] = { "denormal-fp-math"="dynamic,dynamic" "denormal-fp-math-f32"="preserve-sign,preserve-sign" } +; CHECK: attributes #[[ATTR5]] = { "denormal-fp-math"="preserve-sign,ieee" } ; CHECK: attributes #[[ATTR6]] = { "denormal-fp-math"="dynamic,ieee" } -; CHECK: attributes #[[ATTR7]] = { "denormal-fp-math"="ieee,dynamic" } -; CHECK: attributes #[[ATTR8]] = { "denormal-fp-math"="preserve-sign,dynamic" } -; CHECK: attributes #[[ATTR9]] = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" } -; CHECK: attributes #[[ATTR10]] = { "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee" } -; CHECK: attributes #[[ATTR11]] = { "denormal-fp-math"="preserve-sign,dynamic" "denormal-fp-math-f32"="dynamic,positive-zero" } -; CHECK: attributes #[[ATTR12]] = { "denormal-fp-math"="preserve-sign,positive-zero" "denormal-fp-math-f32"="ieee,positive-zero" } +; CHECK: attributes #[[ATTR7]] = { "denormal-fp-math-f32"="preserve-sign,preserve-sign" } +; CHECK: attributes #[[ATTR8]] = { "denormal-fp-math"="preserve-sign,preserve-sign" "denormal-fp-math-f32"="ieee,ieee" } +; CHECK: attributes #[[ATTR9]] = { "denormal-fp-math"="preserve-sign,positive-zero" "denormal-fp-math-f32"="ieee,positive-zero" } ;.