Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -2567,7 +2567,10 @@ base_ty WorstState = 0> struct BitIntegerState : public IntegerStateBase { + using super = IntegerStateBase; using base_t = base_ty; + BitIntegerState() = default; + BitIntegerState(base_t Assumed) : super(Assumed) {} /// Return true if the bits set in \p BitsEncoding are "known bits". bool isKnown(base_t BitsEncoding) const { @@ -4840,6 +4843,39 @@ static const char ID; }; +struct AANoFPClass + : public IRAttribute< + Attribute::NoFPClass, + StateWrapper, + AbstractAttribute>> { + using Base = StateWrapper, + AbstractAttribute>; + + AANoFPClass(const IRPosition &IRP, Attributor &A) : IRAttribute(IRP) {} + + /// Return true if we assume that the underlying value is noundef. + FPClassTest getAssumedNoFPClass() const { + return static_cast(getAssumed()); + } + + /// Create an abstract attribute view for the position \p IRP. + static AANoFPClass &createForPosition(const IRPosition &IRP, Attributor &A); + + /// See AbstractAttribute::getName() + const std::string getName() const override { return "AANoFPClass"; } + + /// See AbstractAttribute::getIdAddr() + const char *getIdAddr() const override { return &ID; } + + /// This function should return true if the type of the \p AA is AANoFPClass + static bool classof(const AbstractAttribute *AA) { + return (AA->getIdAddr() == &ID); + } + + /// Unique ID (due to the unique address) + static const char ID; +}; + struct AACallGraphNode; struct AACallEdges; Index: llvm/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/lib/Transforms/IPO/Attributor.cpp +++ llvm/lib/Transforms/IPO/Attributor.cpp @@ -3240,6 +3240,8 @@ // Every function with pointer return type might be marked // dereferenceable. getOrCreateAAFor(RetPos); + } else if (AttributeFuncs::isNoFPClassCompatibleType(ReturnType)) { + getOrCreateAAFor(RetPos); } } @@ -3284,6 +3286,8 @@ // Every argument with pointer type might be privatizable (or promotable) getOrCreateAAFor(ArgPos); + } else if (AttributeFuncs::isNoFPClassCompatibleType(Arg.getType())) { + getOrCreateAAFor(ArgPos); } } @@ -3312,11 +3316,13 @@ return true; if (!Callee->getReturnType()->isVoidTy() && !CB.use_empty()) { - IRPosition CBRetPos = IRPosition::callsite_returned(CB); bool UsedAssumedInformation = false; getAssumedSimplified(CBRetPos, nullptr, UsedAssumedInformation, AA::Intraprocedural); + + if (AttributeFuncs::isNoFPClassCompatibleType(Callee->getReturnType())) + getOrCreateAAFor(CBInstPos); } for (int I = 0, E = CB.arg_size(); I < E; ++I) { @@ -3336,8 +3342,14 @@ // Every call site argument might be marked "noundef". getOrCreateAAFor(CBArgPos); - if (!CB.getArgOperand(I)->getType()->isPointerTy()) + Type *ArgTy = CB.getArgOperand(I)->getType(); + + if (!ArgTy->isPointerTy()) { + if (AttributeFuncs::isNoFPClassCompatibleType(ArgTy)) + getOrCreateAAFor(CBArgPos); + continue; + } // Call site argument attribute "non-null". getOrCreateAAFor(CBArgPos); Index: llvm/lib/Transforms/IPO/AttributorAttributes.cpp =================================================================== --- llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -177,6 +177,7 @@ PIPE_OPERATOR(AAPotentialConstantValues) PIPE_OPERATOR(AAPotentialValues) PIPE_OPERATOR(AANoUndef) +PIPE_OPERATOR(AANoFPClass) PIPE_OPERATOR(AACallEdges) PIPE_OPERATOR(AAInterFnReachability) PIPE_OPERATOR(AAPointerInfo) @@ -10205,6 +10206,123 @@ void trackStatistics() const override { STATS_DECLTRACK_CSRET_ATTR(noundef) } }; +/// ------------------------ NoFPClass Attribute ------------------------------- + +struct AANoFPClassImpl : AANoFPClass { + AANoFPClassImpl(const IRPosition &IRP, Attributor &A) : AANoFPClass(IRP, A) {} + + void initialize(Attributor &A) override { + const IRPosition &IRP = getIRPosition(); + + Value &V = IRP.getAssociatedValue(); + if (isa(V)) { + indicateOptimisticFixpoint(); + return; + } + + SmallVector Attrs; + IRP.getAttrs({Attribute::NoFPClass}, Attrs, false, &A); + if (!Attrs.empty()) { + addKnownBits(Attrs[0].getNoFPClass()); + return; + } + + const DataLayout &DL = A.getDataLayout(); + if (getPositionKind() != IRPosition::IRP_RETURNED) { + KnownFPClass KnownFPClass = computeKnownFPClass(&V, DL); + addKnownBits(~KnownFPClass.KnownFPClasses); + } + } + + const std::string getAsStr() const override { + std::string Result = "nofpclass"; + raw_string_ostream OS(Result); + OS << getAssumedNoFPClass(); + return Result; + } + + ChangeStatus manifest(Attributor &A) override { + // We don't manifest nofpclass attribute for dead positions because the + // associated values with dead positions would be replaced with undef + // values. + bool UsedAssumedInformation = false; + if (A.isAssumedDead(getIRPosition(), nullptr, nullptr, + UsedAssumedInformation)) + return ChangeStatus::UNCHANGED; + + // A position whose simplified value does not have any value is + // considered to be dead. We don't manifest noundef in such positions for + // the same reason above. + if (!A.getAssumedSimplified(getIRPosition(), *this, UsedAssumedInformation, + AA::Interprocedural) + .has_value()) + return ChangeStatus::UNCHANGED; + + return AANoFPClass::manifest(A); + } + + void getDeducedAttributes(LLVMContext &Ctx, + SmallVectorImpl &Attrs) const override { + Attrs.emplace_back(Attribute::getWithNoFPClass(Ctx, getAssumedNoFPClass())); + } +}; + +struct AANoFPClassFloating : public AANoFPClassImpl { + AANoFPClassFloating(const IRPosition &IRP, Attributor &A) + : AANoFPClassImpl(IRP, A) {} + + /// See AbstractAttribute::updateImpl(...). + ChangeStatus updateImpl(Attributor &A) override { + return indicatePessimisticFixpoint(); + } + + /// See AbstractAttribute::trackStatistics() + void trackStatistics() const override { + STATS_DECLTRACK_FNRET_ATTR(nofpclass) + } +}; + +struct AANoFPClassReturned final + : AAReturnedFromReturnedValues { + AANoFPClassReturned(const IRPosition &IRP, Attributor &A) + : AAReturnedFromReturnedValues(IRP, A) {} + + /// See AbstractAttribute::trackStatistics() + void trackStatistics() const override { + STATS_DECLTRACK_FNRET_ATTR(nofpclass) + } +}; + +struct AANoFPClassArgument final + : AAArgumentFromCallSiteArguments { + AANoFPClassArgument(const IRPosition &IRP, Attributor &A) + : AAArgumentFromCallSiteArguments(IRP, A) {} + + /// See AbstractAttribute::trackStatistics() + void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(nofpclass) } +}; + +struct AANoFPClassCallSiteArgument final : AANoFPClassFloating { + AANoFPClassCallSiteArgument(const IRPosition &IRP, Attributor &A) + : AANoFPClassFloating(IRP, A) {} + + /// See AbstractAttribute::trackStatistics() + void trackStatistics() const override { + STATS_DECLTRACK_CSARG_ATTR(nofpclass) + } +}; + +struct AANoFPClassCallSiteReturned final + : AACallSiteReturnedFromReturned { + AANoFPClassCallSiteReturned(const IRPosition &IRP, Attributor &A) + : AACallSiteReturnedFromReturned(IRP, A) {} + + /// See AbstractAttribute::trackStatistics() + void trackStatistics() const override { + STATS_DECLTRACK_CSRET_ATTR(nofpclass) + } +}; + struct AACallEdgesImpl : public AACallEdges { AACallEdgesImpl(const IRPosition &IRP, Attributor &A) : AACallEdges(IRP, A) {} @@ -11629,6 +11747,7 @@ const char AAPotentialConstantValues::ID = 0; const char AAPotentialValues::ID = 0; const char AANoUndef::ID = 0; +const char AANoFPClass::ID = 0; const char AACallEdges::ID = 0; const char AAInterFnReachability::ID = 0; const char AAPointerInfo::ID = 0; @@ -11749,6 +11868,7 @@ CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAPotentialConstantValues) CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAPotentialValues) CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoUndef) +CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoFPClass) CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAPointerInfo) CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAValueSimplify) Index: llvm/test/Transforms/Attributor/nofpclass.ll =================================================================== --- llvm/test/Transforms/Attributor/nofpclass.ll +++ llvm/test/Transforms/Attributor/nofpclass.ll @@ -14,7 +14,7 @@ declare i1 @llvm.is.fpclass.f32(float, i32 immarg) define float @returned_0() { -; CHECK-LABEL: define noundef float @returned_0() { +; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub norm) float @returned_0() { ; CHECK-NEXT: call void @unknown() ; CHECK-NEXT: ret float 0.000000e+00 ; @@ -23,7 +23,7 @@ } define float @returned_neg0() { -; CHECK-LABEL: define noundef float @returned_neg0() { +; CHECK-LABEL: define noundef nofpclass(nan inf pzero sub norm) float @returned_neg0() { ; CHECK-NEXT: call void @unknown() ; CHECK-NEXT: ret float -0.000000e+00 ; @@ -32,7 +32,7 @@ } define float @returned_undef() { -; CHECK-LABEL: define float @returned_undef() { +; CHECK-LABEL: define nofpclass(all) float @returned_undef() { ; CHECK-NEXT: call void @unknown() ; CHECK-NEXT: ret float undef ; @@ -41,7 +41,7 @@ } define float @returned_poison() { -; CHECK-LABEL: define float @returned_poison() { +; CHECK-LABEL: define nofpclass(all) float @returned_poison() { ; CHECK-NEXT: call void @unknown() ; CHECK-NEXT: ret float poison ; @@ -50,7 +50,7 @@ } define double @returned_snan() { -; CHECK-LABEL: define noundef double @returned_snan() { +; CHECK-LABEL: define noundef nofpclass(qnan inf zero sub norm) double @returned_snan() { ; CHECK-NEXT: call void @unknown() ; CHECK-NEXT: ret double 0x7FF0000000000001 ; @@ -59,7 +59,7 @@ } define double @returned_qnan() { -; CHECK-LABEL: define noundef double @returned_qnan() { +; CHECK-LABEL: define noundef nofpclass(snan inf zero sub norm) double @returned_qnan() { ; CHECK-NEXT: call void @unknown() ; CHECK-NEXT: ret double 0x7FF8000000000000 ; @@ -68,7 +68,7 @@ } define <2 x double> @returned_zero_vector() { -; CHECK-LABEL: define noundef <2 x double> @returned_zero_vector() { +; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub norm) <2 x double> @returned_zero_vector() { ; CHECK-NEXT: call void @unknown() ; CHECK-NEXT: ret <2 x double> zeroinitializer ; @@ -77,7 +77,7 @@ } define <2 x double> @returned_negzero_vector() { -; CHECK-LABEL: define noundef <2 x double> @returned_negzero_vector() { +; CHECK-LABEL: define noundef nofpclass(nan inf pzero sub norm) <2 x double> @returned_negzero_vector() { ; CHECK-NEXT: call void @unknown() ; CHECK-NEXT: ret <2 x double> ; @@ -96,8 +96,8 @@ ; Return a float trivially nofpclass(nan) (call return attribute) define float @return_nofpclass_nan_decl_return() { -; CHECK-LABEL: define float @return_nofpclass_nan_decl_return() { -; CHECK-NEXT: [[RET:%.*]] = call float @ret_nofpclass_nan() +; CHECK-LABEL: define nofpclass(nan) float @return_nofpclass_nan_decl_return() { +; CHECK-NEXT: [[RET:%.*]] = call nofpclass(nan) float @ret_nofpclass_nan() ; CHECK-NEXT: ret float [[RET]] ; %ret = call float @ret_nofpclass_nan() @@ -107,7 +107,7 @@ ; Return a float trivially nofpclass(nan) (argument attribute) define float @return_nofpclass_nan_arg(float returned nofpclass(nan) %p) { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @return_nofpclass_nan_arg +; CHECK-LABEL: define nofpclass(nan) float @return_nofpclass_nan_arg ; CHECK-SAME: (float returned nofpclass(nan) [[P:%.*]]) #[[ATTR2:[0-9]+]] { ; CHECK-NEXT: ret float [[P]] ; @@ -115,7 +115,7 @@ } define [2 x [3 x float]] @return_nofpclass_inf_ret_array() { -; CHECK-LABEL: define [2 x [3 x float]] @return_nofpclass_inf_ret_array() { +; CHECK-LABEL: define nofpclass(inf) [2 x [3 x float]] @return_nofpclass_inf_ret_array() { ; CHECK-NEXT: [[RET:%.*]] = call nofpclass(inf) [2 x [3 x float]] @ret_array() ; CHECK-NEXT: ret [2 x [3 x float]] [[RET]] ; @@ -125,7 +125,7 @@ define float @returned_nnan_fadd(float %arg0, float %arg1) { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CHECK-LABEL: define float @returned_nnan_fadd +; CHECK-LABEL: define nofpclass(nan) float @returned_nnan_fadd ; CHECK-SAME: (float [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: [[FADD:%.*]] = fadd nnan float [[ARG0]], [[ARG1]] ; CHECK-NEXT: ret float [[FADD]] @@ -135,7 +135,7 @@ } define float @return_nofpclass_nan_callsite() { -; CHECK-LABEL: define float @return_nofpclass_nan_callsite() { +; CHECK-LABEL: define nofpclass(nan) float @return_nofpclass_nan_callsite() { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan) float @extern() ; CHECK-NEXT: ret float [[CALL]] ; @@ -145,7 +145,7 @@ ; Can union the return classes define nofpclass(inf) float @return_ninf_nofpclass_nan_callsite() { -; CHECK-LABEL: define nofpclass(inf) float @return_ninf_nofpclass_nan_callsite() { +; CHECK-LABEL: define nofpclass(nan inf) float @return_ninf_nofpclass_nan_callsite() { ; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan) float @extern() ; CHECK-NEXT: ret float [[CALL]] ; @@ -177,7 +177,7 @@ define void @ninf_arg_used_by_callsite_array([2 x [3 x float]] nofpclass(inf) %arg) { ; CHECK-LABEL: define void @ninf_arg_used_by_callsite_array ; CHECK-SAME: ([2 x [3 x float]] nofpclass(inf) [[ARG:%.*]]) { -; CHECK-NEXT: call void @extern.use.array([2 x [3 x float]] [[ARG]]) +; CHECK-NEXT: call void @extern.use.array([2 x [3 x float]] nofpclass(inf) [[ARG]]) ; CHECK-NEXT: ret void ; call void @extern.use.array([2 x [3 x float]] %arg) @@ -191,7 +191,7 @@ ; TUNIT-NEXT: ret float undef ; ; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CGSCC-LABEL: define float @mutually_recursive0 +; CGSCC-LABEL: define nofpclass(all) float @mutually_recursive0 ; CGSCC-SAME: (float [[ARG:%.*]]) #[[ATTR2]] { ; CGSCC-NEXT: ret float undef ; @@ -206,7 +206,7 @@ ; TUNIT-NEXT: ret float undef ; ; CGSCC: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; CGSCC-LABEL: define float @mutually_recursive1 +; CGSCC-LABEL: define nofpclass(all) float @mutually_recursive1 ; CGSCC-SAME: (float [[ARG:%.*]]) #[[ATTR2]] { ; CGSCC-NEXT: ret float undef ; @@ -215,10 +215,10 @@ } define float @recursive_phi(ptr %ptr) { -; CHECK-LABEL: define float @recursive_phi +; CHECK-LABEL: define nofpclass(nan) float @recursive_phi ; CHECK-SAME: (ptr nofree [[PTR:%.*]]) { ; CHECK-NEXT: entry: -; CHECK-NEXT: [[RET:%.*]] = call float @ret_nofpclass_nan() +; CHECK-NEXT: [[RET:%.*]] = call nofpclass(nan) float @ret_nofpclass_nan() ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[PHI:%.*]] = phi float [ [[RET]], [[ENTRY:%.*]] ], [ [[RET]], [[LOOP]] ] @@ -330,7 +330,7 @@ define internal float @only_nofpclass_inf_callers(float %arg) { ; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) ; CHECK-LABEL: define internal float @only_nofpclass_inf_callers -; CHECK-SAME: (float [[ARG:%.*]]) #[[ATTR2]] { +; CHECK-SAME: (float nofpclass(inf) [[ARG:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: [[ADD:%.*]] = fadd float [[ARG]], [[ARG]] ; CHECK-NEXT: ret float [[ADD]] ; @@ -342,13 +342,13 @@ ; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) ; TUNIT-LABEL: define float @call_noinf_0 ; TUNIT-SAME: (float nofpclass(inf) [[ARG:%.*]]) #[[ATTR2]] { -; TUNIT-NEXT: [[RESULT:%.*]] = call float @only_nofpclass_inf_callers(float [[ARG]]) #[[ATTR5:[0-9]+]] +; TUNIT-NEXT: [[RESULT:%.*]] = call float @only_nofpclass_inf_callers(float nofpclass(inf) [[ARG]]) #[[ATTR5:[0-9]+]] ; TUNIT-NEXT: ret float [[RESULT]] ; ; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define float @call_noinf_0 ; CGSCC-SAME: (float nofpclass(inf) [[ARG:%.*]]) #[[ATTR3:[0-9]+]] { -; CGSCC-NEXT: [[RESULT:%.*]] = call float @only_nofpclass_inf_callers(float [[ARG]]) #[[ATTR4]] +; CGSCC-NEXT: [[RESULT:%.*]] = call float @only_nofpclass_inf_callers(float nofpclass(inf) [[ARG]]) #[[ATTR4]] ; CGSCC-NEXT: ret float [[RESULT]] ; %result = call float @only_nofpclass_inf_callers(float %arg) @@ -386,13 +386,13 @@ define float @call_noinf_return_0(float %arg) { ; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; TUNIT-LABEL: define float @call_noinf_return_0 +; TUNIT-LABEL: define nofpclass(inf) float @call_noinf_return_0 ; TUNIT-SAME: (float [[ARG:%.*]]) #[[ATTR2]] { ; TUNIT-NEXT: [[RESULT:%.*]] = call nofpclass(inf) float @only_nofpclass_inf_return_users(float [[ARG]]) #[[ATTR5]] ; TUNIT-NEXT: ret float [[RESULT]] ; ; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(none) -; CGSCC-LABEL: define float @call_noinf_return_0 +; CGSCC-LABEL: define nofpclass(inf) float @call_noinf_return_0 ; CGSCC-SAME: (float [[ARG:%.*]]) #[[ATTR3]] { ; CGSCC-NEXT: [[RESULT:%.*]] = call nofpclass(inf) float @only_nofpclass_inf_return_users(float [[ARG]]) #[[ATTR4]] ; CGSCC-NEXT: ret float [[RESULT]] @@ -403,13 +403,13 @@ define float @call_noinf_return_1(float %arg) { ; TUNIT: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none) -; TUNIT-LABEL: define float @call_noinf_return_1 +; TUNIT-LABEL: define nofpclass(inf) float @call_noinf_return_1 ; TUNIT-SAME: (float [[ARG:%.*]]) #[[ATTR2]] { ; TUNIT-NEXT: [[RESULT:%.*]] = call nofpclass(inf) float @only_nofpclass_inf_return_users(float [[ARG]]) #[[ATTR5]] ; TUNIT-NEXT: ret float [[RESULT]] ; ; CGSCC: Function Attrs: nofree nosync nounwind willreturn memory(none) -; CGSCC-LABEL: define float @call_noinf_return_1 +; CGSCC-LABEL: define nofpclass(inf) float @call_noinf_return_1 ; CGSCC-SAME: (float [[ARG:%.*]]) #[[ATTR3]] { ; CGSCC-NEXT: [[RESULT:%.*]] = call nofpclass(inf) float @only_nofpclass_inf_return_users(float [[ARG]]) #[[ATTR4]] ; CGSCC-NEXT: ret float [[RESULT]] @@ -594,11 +594,11 @@ ; CHECK-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]] ; CHECK: A: ; CHECK-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR4]] [ "nofpclass"(float [[RET]], i32 3) ] -; CHECK-NEXT: call void @extern.use(float [[RET]]) +; CHECK-NEXT: call void @extern.use(float nofpclass(nan) [[RET]]) ; CHECK-NEXT: ret float [[RET]] ; CHECK: B: ; CHECK-NEXT: call void @llvm.assume(i1 noundef true) [ "nofpclass"(float [[RET]], i32 12) ] -; CHECK-NEXT: call void @extern.use(float [[RET]]) +; CHECK-NEXT: call void @extern.use(float nofpclass(ninf nnorm) [[RET]]) ; CHECK-NEXT: ret float [[RET]] ; entry: