Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -437,6 +437,13 @@ return *this; } + /// Take minimum of assumed and \p Value. + IntegerState &takeAssumedMinimum(base_t Value) { + // Make sure we never loose "known value". + Assumed = std::max(std::min(Assumed, Value), Known); + return *this; + } + private: /// The known state encoding in an integer of type base_t. base_t Known = getWorstState(); @@ -680,6 +687,26 @@ static constexpr Attribute::AttrKind ID = Attribute::Returned; }; +/// An abstract interface for all align attributes. +struct AAAlign : public AbstractAttribute { + + /// See AbstractAttribute::AbstractAttribute(...). + AAAlign(Value &V, InformationCache &InfoCache) + : AbstractAttribute(V, InfoCache) {} + + /// See AbastractState::getAttrKind(). + Attribute::AttrKind getAttrKind() const override { return ID; } + + /// Return assumed alignment. + virtual unsigned getAssumedAlign() const = 0; + + /// Return known alignemnt. + virtual unsigned getKnownAlign() const = 0; + + /// The identifier used by the Attributor for this class of attributes. + static constexpr Attribute::AttrKind ID = Attribute::Alignment; +}; + } // end namespace llvm #endif // LLVM_TRANSFORMS_IPO_FUNCTIONATTRS_H Index: llvm/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/lib/Transforms/IPO/Attributor.cpp +++ llvm/lib/Transforms/IPO/Attributor.cpp @@ -237,7 +237,24 @@ return true; } - llvm_unreachable("Expected enum or string attribute!"); + if (Attr.isIntAttribute()) { + Attribute::AttrKind Kind = Attr.getKindAsEnum(); + if (Attrs.hasAttribute(AttrIdx, Kind)) + if (isEqualOrWorse(Attr, Attrs.getAttribute(AttrIdx, Kind))) + return false; + Attrs = Attrs.addAttribute(Ctx, AttrIdx, Attr); + return true; + } + + llvm_unreachable("Expected enum, string or int attribute!"); +} + +// Helper function that returns argument index of value. +// If the value is not an argument, this returns 0. +static int getArgNo(Value &V) { + if (auto *Arg = dyn_cast(&V)) + return Arg->getArgNo(); + return 0; } ChangeStatus AbstractAttribute::update(Attributor &A) { @@ -719,7 +736,132 @@ return Changed; } +/// ------------------------ Align Argument Attribute ------------------------ + +struct AAAlignImpl : AAAlign, IntegerState { + AAAlignImpl(Value &V, InformationCache &InfoCache) : AAAlign(V, InfoCache) {} + + /// See AbstractAttribute::getState() + /// { + AbstractState &getState() override { return *this; } + const AbstractState &getState() const override { return *this; } + /// } + + virtual const std::string getAsStr() const override { + return getAssumedAlign() + ? (getKnownAlign() + ? "align<" + std::to_string(getKnownAlign()) + ">" + : "maybe-align<" + std::to_string(getAssumedAlign()) + + ">") + : "unknown-align"; + } + + /// See AAAlign::getAssumedAlign(). + unsigned getAssumedAlign() const override { return getAssumed(); } + + /// See AAAlign::getKnownAlign(). + unsigned getKnownAlign() const override { return getKnown(); } + + /// See AbstractAttriubute::initialize(...). + void initialize(Attributor &A) override { + Function &F = getAnchorScope(); + + unsigned AttrIdx = + getAttrIndex(getManifestPosition(), getArgNo(getAnchoredValue())); + + // Already the function has align attribute on return value. + if (F.getAttributes().hasAttribute(AttrIdx, Attribute::Alignment)) { + takeAssumedMinimum( + F.getAttribute(AttrIdx, Attribute::Alignment).getAlignment()); + indicateOptimisticFixpoint(); + } + } + + /// See AbstractAttribute::getDeducedAttributes + virtual void + getDeducedAttributes(SmallVectorImpl &Attrs) const override { + LLVMContext &Ctx = AnchoredVal.getContext(); + if (getKnownAlign() <= (1U << 29)) { + Attrs.emplace_back(Attribute::getWithAlignment(Ctx, getKnownAlign())); + } + } +}; + +/// Align attribute for function return value. +struct AAAlignReturned : AAAlignImpl { + + AAAlignReturned(Function &F, InformationCache &InfoCache) + : AAAlignImpl(F, InfoCache) {} + + /// See AbstractAttribute::getManifestPosition(). + virtual ManifestPosition getManifestPosition() const override { + return MP_RETURNED; + } + + /// See AbstractAttribute::updateImpl(...). + virtual ChangeStatus updateImpl(Attributor &A) override; +}; + +/// Align attribute for function argument. +struct AAAlignArgument : AAAlignImpl { + + AAAlignArgument(Argument &A, InformationCache &InfoCache) + : AAAlignImpl(A, InfoCache) {} + + /// See AbstractAttribute::getManifestPosition(). + virtual ManifestPosition getManifestPosition() const override { + return MP_ARGUMENT; + } + + /// See AbstractAttribute::updateImpl(...). + virtual ChangeStatus updateImpl(Attributor &A) override; +}; + +ChangeStatus AAAlignArgument::updateImpl(Attributor &A) { + // TODO: Currently, we have no deduction for argument align attribute. + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; +} + +ChangeStatus AAAlignReturned::updateImpl(Attributor &A) { + Function &F = getAnchorScope(); + auto *AARetValImpl = A.getAAFor(*this, F); + if (!AARetValImpl) { + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } + + // Currently, align is deduced if alignments in return values are known as + // greater than n. We reach pessimistic fixpoint if any of the return value + // wouldn't have align. If there is no top state, we can reach optimistic + // fixpoint earlier. + + base_t BeforeState = getAssumed(); + std::function Pred = [&](Value &RV) -> bool { + auto *AlignAA = A.getAAFor(*this, RV); + + if (AlignAA && AlignAA->isValidState()) { + takeAssumedMinimum(AlignAA->getAssumedAlign()); + return true; + } + ImmutableCallSite ICS(&RV); + + if (ICS && ICS.hasRetAttr(Attribute::Alignment)) { + takeAssumedMinimum(ICS.getRetAlignment()); + return true; + } + + return false; + }; + if (!AARetValImpl->checkForallReturnedValues(Pred)) { + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } + + return (getAssumed() != BeforeState) ? ChangeStatus::CHANGED + : ChangeStatus::UNCHANGED; +} /// ---------------------------------------------------------------------------- /// Attributor @@ -873,8 +1015,18 @@ // though it is an argument attribute. if (!Whitelist || Whitelist->count(AAReturnedValues::ID)) registerAA(*new AAReturnedValuesImpl(F, InfoCache)); + + // Every function with pointer return type might be marked align. + if (ReturnType->isPointerTy() && + (!Whitelist || Whitelist->count(AAAlignReturned::ID))) + registerAA(*new AAAlignReturned(F, InfoCache)); } + // Every argument with pointer type might be marked align. + for (Argument &Arg : F.args()) { + if (Arg.getType()->isPointerTy()) + registerAA(*new AAAlignArgument(Arg, InfoCache)); + } // Walk all instructions to find more attribute opportunities and also // interesting instructions that might be queried by abstract attributes // during their initialization or update. Index: llvm/test/Transforms/FunctionAttrs/align.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/FunctionAttrs/align.ll @@ -0,0 +1,46 @@ +; RUN: opt -attributor -attributor-disable=false -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +; Test cases specifically designed for "align" attribute. +; We use FIXME's to indicate problems and missing attributes. + + +; TEST 1 +; ATTRIBUTOR: define align 8 i32* @test1(i32* returned align 8) +define i32* @test1(i32* align 8) #0 { + ret i32* %0 +} + +; TEST 2 +; ATTRIBUTOR: define i32* @test2(i32* returned) +define i32* @test2(i32*) #0 { + ret i32* %0 +} + +; TEST 3 +; ATTRIBUTOR: define align 4 i32* @test3(i32* align 8, i32* align 4, i1) +define i32* @test3(i32* align 8, i32* align 4, i1) #0 { + %ret = select i1 %2, i32* %0, i32* %1 + ret i32* %ret +} + +; TEST 4 +; ATTRIBUTOR: define align 32 i32* @test4(i32* align 32, i32* align 32, i1) +define i32* @test4(i32* align 32, i32* align 32, i1) #0 { + %ret = select i1 %2, i32* %0, i32* %1 + ret i32* %ret +} + +; TEST 5 +; declare align 8 i32* @align8() #0 + +; define i32* @test5() { +; %ret = tail call i32* @align8() +; ret i32* %ret +; } + + +attributes #0 = { nounwind uwtable noinline } +attributes #1 = { uwtable noinline } +