Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -680,6 +680,20 @@ 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; } + + /// 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,16 @@ 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!"); } ChangeStatus AbstractAttribute::update(Attributor &A) { @@ -719,7 +728,163 @@ return Changed; } +/// ------------------------ Align Argument Attribute ------------------------ + +struct AAAlignImpl : AAAlign, BooleanState { + AAAlignImpl(Value &V, InformationCache &InfoCache) : AAAlign(V, InfoCache) {} + + Optional AlignValue; + + /// See AbstractAttribute::getState() + /// { + AbstractState &getState() override { return *this; } + const AbstractState &getState() const override { return *this; } + /// } + + virtual const std::string getAsStr() const override { + return getAssumed() + ? (getKnown() + ? "align<" + std::to_string(AlignValue.getValue()) + ">" + : "maybe-align") + : "unknown-align"; + } + + + /// See AAAlign:isAssumedAlign(). + bool isAssumedAlign() const { return getAssumed(); } + + /// See AAAlign:isKnownAlign(). + bool isKnownAlign() const { return getKnown(); } + + /// Return deduced align value. + unsigned getKnownAlign() const { return AlignValue.getValue(); } + + /// See AbstractAttribute::getDeducedAttributes + virtual void getDeducedAttributes(SmallVectorImpl &Attrs) const { + LLVMContext &Ctx = AnchoredVal.getContext(); + + if (AlignValue.hasValue()) { + Attrs.emplace_back( + Attribute::getWithAlignment(Ctx, AlignValue.getValue())); + } + } +}; + +/// 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 AbstractAttriubute::initialize(...). + void initialize(Attributor &A) override { + Function &F = getAnchorScope(); + + // Already the function has align attribute on return value. + if (F.getAttributes().hasAttribute(AttributeList::ReturnIndex, + Attribute::Alignment)) { + AlignValue = F.getAttributes().getRetAlignment(); + indicateOptimisticFixpoint(); + } + } + + /// 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 AbstractAttriubute::initialize(...). + void initialize(Attributor &A) override { + Argument *Arg = cast(getAssociatedValue()); + if (Arg->hasAttribute(Attribute::Alignment)) { + AlignValue = Arg->getAttribute(Attribute::Alignment).getAlignment(); + indicateOptimisticFixpoint(); + } + } + + /// 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 all the return value is known as + // align. We reach optimistic fixpoint if all return's align are known and + // they are same. On the other hand, we reach pessimistic fixpoint if (i) Any + // of return value would't have align. (ii) There are two return values whose + // align value is not same. + + bool ExistsAssume = false; + auto CompareOrSet = [&](uint32_t Value) -> bool { + if (AlignValue.hasValue()) + return AlignValue.getValue() == Value; + else { + AlignValue = Value; + return true; + } + }; + + std::function Pred = [&](Value &RV) -> bool { + auto *AlignAA = A.getAAFor(*this, RV); + + if (AlignAA && AlignAA->isValidState()) { + if (AlignAA->isKnownAlign()) + return CompareOrSet(AlignAA->getKnownAlign()); + if (AlignAA->isAssumedAlign()) { + ExistsAssume = true; + return true; + } + } + ImmutableCallSite ICS(&RV); + + if (ICS && ICS.hasRetAttr(Attribute::Alignment)) { + dbgs() << RV << " " << ICS.getAttributes().getRetAlignment() << "\n"; + for (auto s : ICS.getAttributes().getRetAttributes()) { + dbgs() << s.getAsString() << "\n"; + } + return CompareOrSet(ICS.getRetAlignment()); + } + + return false; + }; + + if (AARetValImpl->checkForallReturnedValues(Pred) && AlignValue.hasValue()) { + indicateOptimisticFixpoint(); + return ChangeStatus::CHANGED; + } else if (ExistsAssume) { + return ChangeStatus::UNCHANGED; + } else { + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } +} /// ---------------------------------------------------------------------------- /// Attributor @@ -873,8 +1038,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 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 } +