Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -905,6 +905,31 @@ static constexpr Attribute::AttrKind ID = Attribute::Dereferenceable; }; +/// An abstract interface for all align attributes. +struct AAAlign : public AbstractAttribute { + + /// See AbstractAttribute::AbstractAttribute(...). + AAAlign(Value &V, InformationCache &InfoCache) + : AbstractAttribute(V, InfoCache) {} + + /// See AbstractAttribute::AbstractAttribute(...). + AAAlign(Value *AssociatedVal, Value &AnchoredValue, + InformationCache &InfoCache) + : AbstractAttribute(AssociatedVal, AnchoredValue, 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 @@ -1974,6 +1974,214 @@ : ChangeStatus::CHANGED; } +// ------------------------ Align Argument Attribute ------------------------ + +struct AAAlignImpl : AAAlign, IntegerState { + AAAlignImpl(Value &V, InformationCache &InfoCache) + : AAAlign(V, InfoCache), IntegerState(1U << 29) {} + + AAAlignImpl(Value *AssociatedVal, Value &AnchoredValue, + InformationCache &InfoCache) + : AAAlign(AssociatedVal, AnchoredValue, 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() ? ("align<" + std::to_string(getKnownAlign()) + + "-" + 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 or argument. + if (F.getAttributes().hasAttribute(AttrIdx, ID)) + addKnownBits(F.getAttribute(AttrIdx, ID).getAlignment()); + } + + /// See AbstractAttribute::getDeducedAttributes + virtual void + getDeducedAttributes(SmallVectorImpl &Attrs) const override { + LLVMContext &Ctx = AnchoredVal.getContext(); + + 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; +}; + +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 no assumed state was used for reasoning, an + // optimistic fixpoint is reached 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; + } + + unsigned Align = + RV.getPointerAlignment(getAnchorScope().getParent()->getDataLayout()); + + // Alignment is known in IR information. + if (Align) { + takeAssumedMinimum(Align); + return true; + } + + return false; + }; + + if (!AARetValImpl->checkForallReturnedValues(Pred)) { + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } + + return (getAssumed() != BeforeState) ? ChangeStatus::CHANGED + : ChangeStatus::UNCHANGED; +} + +/// 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) { + + Function &F = getAnchorScope(); + Argument &Arg = cast(getAnchoredValue()); + + unsigned ArgNo = Arg.getArgNo(); + const DataLayout &DL = F.getParent()->getDataLayout(); + + auto BeforeState = getAssumed(); + + // Callback function + std::function CallSiteCheck = [&](CallSite CS) { + assert(CS && "Sanity check: Call site was not initialized properly!"); + + auto *AlignAA = A.getAAFor(*this, *CS.getInstruction(), ArgNo); + + // Check that AlignAA is AAAlignCallSiteArgument. + if (AlignAA) { + ImmutableCallSite ICS(&AlignAA->getAnchoredValue()); + if (ICS && CS.getInstruction() == ICS.getInstruction()) { + takeAssumedMinimum(AlignAA->getAssumedAlign()); + return isValidState(); + } + } + + Value *V = CS.getArgOperand(ArgNo); + takeAssumedMinimum(V->getPointerAlignment(DL)); + return isValidState(); + }; + + if (!A.checkForAllCallSites(F, CallSiteCheck, true)) + indicatePessimisticFixpoint(); + + return BeforeState == getAssumed() ? ChangeStatus::UNCHANGED + : ChangeStatus ::CHANGED; +} + +struct AAAlignCallSiteArgument : AAAlignImpl { + + /// See AANonNullImpl::AANonNullImpl(...). + AAAlignCallSiteArgument(CallSite CS, unsigned ArgNo, + InformationCache &InfoCache) + : AAAlignImpl(CS.getArgOperand(ArgNo), *CS.getInstruction(), InfoCache), + ArgNo(ArgNo) {} + + /// See AbstractAttribute::initialize(...). + void initialize(Attributor &A) override { + CallSite CS(&getAnchoredValue()); + takeKnownMaximum(getAssociatedValue()->getPointerAlignment( + getAnchorScope().getParent()->getDataLayout())); + } + + /// See AbstractAttribute::updateImpl(Attributor &A). + ChangeStatus updateImpl(Attributor &A) override; + + /// See AbstractAttribute::getManifestPosition(). + ManifestPosition getManifestPosition() const override { + return MP_CALL_SITE_ARGUMENT; + }; + + // Return argument index of associated value. + int getArgNo() const { return ArgNo; } + +private: + unsigned ArgNo; +}; + +ChangeStatus AAAlignCallSiteArgument::updateImpl(Attributor &A) { + // NOTE: Never look at the argument of the callee in this method. + // If we do this, "align" is always deduced because of the assumption. + + auto BeforeState = getAssumed(); + + Value &V = *getAssociatedValue(); + + auto *AlignAA = A.getAAFor(*this, V); + + if (AlignAA) + takeAssumedMinimum(AlignAA->getAssumedAlign()); + else + indicatePessimisticFixpoint(); + + return BeforeState == getAssumed() ? ChangeStatus::UNCHANGED + : ChangeStatus::CHANGED; +} + /// ---------------------------------------------------------------------------- /// Attributor /// ---------------------------------------------------------------------------- @@ -2171,6 +2379,11 @@ registerAA(*new AAReturnedValuesImpl(F, InfoCache)); if (ReturnType->isPointerTy()) { + // Every function with pointer return type might be marked align. + if (ReturnType->isPointerTy() && + (!Whitelist || Whitelist->count(AAAlignReturned::ID))) + registerAA(*new AAAlignReturned(F, InfoCache)); + // Every function with pointer return type might be marked nonnull. if (!Whitelist || Whitelist->count(AANonNullReturned::ID)) registerAA(*new AANonNullReturned(F, InfoCache)); @@ -2196,6 +2409,10 @@ // Every argument with pointer type might be marked dereferenceable. if (!Whitelist || Whitelist->count(AADereferenceableArgument::ID)) registerAA(*new AADereferenceableArgument(Arg, InfoCache)); + + // Every argument with pointer type might be marked align. + if (!Whitelist || Whitelist->count(AAAlignArgument::ID)) + registerAA(*new AAAlignArgument(Arg, InfoCache)); } } @@ -2254,6 +2471,10 @@ Whitelist->count(AADereferenceableCallSiteArgument::ID)) registerAA(*new AADereferenceableCallSiteArgument(CS, i, InfoCache), i); + + // Call site argument attribute "align". + if (!Whitelist || Whitelist->count(AAAlignCallSiteArgument::ID)) + registerAA(*new AAAlignCallSiteArgument(CS, i, InfoCache), i); } } } Index: llvm/test/Transforms/FunctionAttrs/align.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/FunctionAttrs/align.ll @@ -0,0 +1,86 @@ +; 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 +declare i32* @unknown() #0 + + +; define i32* @test5() { +; %ret = tail call i32* @align8() +; ret i32* %ret +; } + +; TEST 6 +; SCC +; ATTRIBUTOR: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @test6_1() +define i32* @test6_1() #0 { + %ret = tail call i32* @test6_2() + ret i32* %ret +} + +; ATTRIBUTOR: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @test6_2() +define i32* @test6_2() #0 { + %ret = tail call i32* @test6_1() + ret i32* %ret +} + + +; TEST 7 +; Better than IR information +; ATTRIBUTOR: define align 32 i32* @test7(i32* returned align 32 %p) +define align 4 i32* @test7(i32* align 32 %p) #0 { + ret i32* %p +} + + +; TEST 8 +define void @test8_helper() { + %ptr0 = tail call i32* @unknown() + %ptr1 = tail call align 4 i32* @unknown() + %ptr2 = tail call align 8 i32* @unknown() + tail call void @test8(i32* %ptr1, i32* %ptr1, i32* %ptr0) + tail call void @test8(i32* %ptr2, i32* %ptr1, i32* %ptr1) + tail call void @test8(i32* %ptr2, i32* %ptr1, i32* %ptr1) + ret void +} + +define internal void @test8(i32* %a, i32* %b, i32* %c) { +; ATTRIBUTOR: define internal void @test8(i32* align 4 %a, i32* align 4 %b, i32* %c) + ret void +} + + +attributes #0 = { nounwind uwtable noinline } +attributes #1 = { uwtable noinline } \ No newline at end of file Index: llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll +++ llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll @@ -88,7 +88,7 @@ ; Other arguments are possible here due to the no-return behavior. ; ; FIXME: no-return missing -; CHECK: define noalias nonnull dereferenceable(4294967295) i32* @srec16(i32* nocapture readnone %a) +; CHECK: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @srec16(i32* nocapture readnone %a) define i32* @srec16(i32* %a) #0 { entry: %call = call i32* @srec16(i32* %a) Index: llvm/test/Transforms/FunctionAttrs/noalias_returned.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/noalias_returned.ll +++ llvm/test/Transforms/FunctionAttrs/noalias_returned.ll @@ -79,13 +79,13 @@ ; TEST 5 ; Returning global pointer. Should not be noalias. -; CHECK: define nonnull dereferenceable(8) i8** @getter() +; CHECK: define nonnull align 8 dereferenceable(8) i8** @getter() define i8** @getter() { ret i8** @G } ; Returning global pointer. Should not be noalias. -; CHECK: define nonnull dereferenceable(8) i8** @calle1() +; CHECK: define nonnull align 8 dereferenceable(8) i8** @calle1() define i8** @calle1(){ %1 = call i8** @getter() ret i8** %1 Index: llvm/test/Transforms/FunctionAttrs/nonnull.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/nonnull.ll +++ llvm/test/Transforms/FunctionAttrs/nonnull.ll @@ -40,14 +40,14 @@ ; just never return period.) define i8* @test4_helper() { ; FNATTR: define noalias nonnull i8* @test4_helper -; ATTRIBUTOR: define noalias nonnull dereferenceable(4294967295) i8* @test4_helper +; ATTRIBUTOR: define noalias nonnull align 536870912 dereferenceable(4294967295) i8* @test4_helper %ret = call i8* @test4() ret i8* %ret } define i8* @test4() { ; FNATTR: define noalias nonnull i8* @test4 -; ATTRIBUTOR: define noalias nonnull dereferenceable(4294967295) i8* @test4 +; ATTRIBUTOR: define noalias nonnull align 536870912 dereferenceable(4294967295) i8* @test4 %ret = call i8* @test4_helper() ret i8* %ret }