[Attributor] Deduce "align" attribute

Summary:
Deduce "align" attribute in attributor.

Reviewers: jdoerfert, sstefan1

Reviewed By: jdoerfert

Subscribers: hiraditya, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D64152

llvm-svn: 367187
This commit is contained in:
Hideto Ueno 2019-07-28 07:04:01 +00:00
parent bd68a052f2
commit e7bea9b73a
6 changed files with 434 additions and 5 deletions

View File

@ -905,6 +905,31 @@ struct AADereferenceable : public AbstractAttribute {
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

View File

@ -72,6 +72,9 @@ STATISTIC(NumFnArgumentDereferenceable,
"Number of function arguments marked dereferenceable");
STATISTIC(NumCSArgumentDereferenceable,
"Number of call site arguments marked dereferenceable");
STATISTIC(NumFnReturnedAlign, "Number of function return values marked align");
STATISTIC(NumFnArgumentAlign, "Number of function arguments marked align");
STATISTIC(NumCSArgumentAlign, "Number of call site arguments marked align");
// TODO: Determine a good default value.
//
@ -115,6 +118,21 @@ static void bookkeeping(AbstractAttribute::ManifestPosition MP,
return;
switch (Attr.getKindAsEnum()) {
case Attribute::Alignment:
switch (MP) {
case AbstractAttribute::MP_RETURNED:
NumFnReturnedAlign++;
break;
case AbstractAttribute::MP_ARGUMENT:
NumFnArgumentAlign++;
break;
case AbstractAttribute::MP_CALL_SITE_ARGUMENT:
NumCSArgumentAlign++;
break;
default:
break;
}
break;
case Attribute::Dereferenceable:
switch (MP) {
case AbstractAttribute::MP_RETURNED:
@ -1974,6 +1992,212 @@ ChangeStatus AADereferenceableCallSiteArgument::updateImpl(Attributor &A) {
: ChangeStatus::CHANGED;
}
// ------------------------ Align Argument Attribute ------------------------
struct AAAlignImpl : AAAlign, IntegerState {
// Max alignemnt value allowed in IR
static const unsigned MAX_ALIGN = 1U << 29;
AAAlignImpl(Value *AssociatedVal, Value &AnchoredValue,
InformationCache &InfoCache)
: AAAlign(AssociatedVal, AnchoredValue, InfoCache),
IntegerState(MAX_ALIGN) {}
AAAlignImpl(Value &V, InformationCache &InfoCache)
: AAAlignImpl(&V, 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() ? ("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<Attribute> &Attrs) const override {
LLVMContext &Ctx = AnchoredVal.getContext();
Attrs.emplace_back(Attribute::getWithAlignment(Ctx, getAssumedAlign()));
}
};
/// 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<AAReturnedValuesImpl>(*this, F);
if (!AARetValImpl) {
indicatePessimisticFixpoint();
return ChangeStatus::CHANGED;
}
// Currently, align<n> is deduced if alignments in return values are assumed
// 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<bool(Value &)> Pred = [&](Value &RV) -> bool {
auto *AlignAA = A.getAAFor<AAAlign>(*this, RV);
if (AlignAA)
takeAssumedMinimum(AlignAA->getAssumedAlign());
else
// Use IR information.
takeAssumedMinimum(RV.getPointerAlignment(
getAnchorScope().getParent()->getDataLayout()));
return isValidState();
};
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<Argument>(getAnchoredValue());
unsigned ArgNo = Arg.getArgNo();
const DataLayout &DL = F.getParent()->getDataLayout();
auto BeforeState = getAssumed();
// Callback function
std::function<bool(CallSite)> CallSiteCheck = [&](CallSite CS) {
assert(CS && "Sanity check: Call site was not initialized properly!");
auto *AlignAA = A.getAAFor<AAAlign>(*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<AAAlign>(*this, V);
if (AlignAA)
takeAssumedMinimum(AlignAA->getAssumedAlign());
else
indicatePessimisticFixpoint();
return BeforeState == getAssumed() ? ChangeStatus::UNCHANGED
: ChangeStatus::CHANGED;
}
/// ----------------------------------------------------------------------------
/// Attributor
/// ----------------------------------------------------------------------------
@ -2171,6 +2395,10 @@ void Attributor::identifyDefaultAbstractAttributes(
registerAA(*new AAReturnedValuesImpl(F, InfoCache));
if (ReturnType->isPointerTy()) {
// Every function with pointer return type might be marked align.
if (!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 +2424,10 @@ void Attributor::identifyDefaultAbstractAttributes(
// 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 +2486,10 @@ void Attributor::identifyDefaultAbstractAttributes(
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);
}
}
}

View File

@ -0,0 +1,168 @@
; 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 i32* @unknown()
declare align 8 i32* @align8()
; ATTRIBUTOR: define align 8 i32* @test5_1()
define i32* @test5_1() {
%ret = tail call align 8 i32* @unknown()
ret i32* %ret
}
; ATTRIBUTOR: define align 8 i32* @test5_2()
define i32* @test5_2() {
%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
}
; char a1 __attribute__((aligned(8)));
; char a2 __attribute__((aligned(16)));
;
; char* f1(char* a ){
; return a?a:f2(&a1);
; }
; char* f2(char* a){
; return a?f1(a):f3(&a2);
; }
;
; char* f3(char* a){
; return a?&a1: f1(&a2);
; }
@a1 = common global i8 0, align 8
@a2 = common global i8 0, align 16
; Function Attrs: nounwind readnone ssp uwtable
define internal i8* @f1(i8* readnone) local_unnamed_addr #0 {
; ATTRIBUTOR: define internal nonnull align 8 i8* @f1(i8* nonnull readnone align 8)
%2 = icmp eq i8* %0, null
br i1 %2, label %3, label %5
; <label>:3: ; preds = %1
; ATTRIBUTOR: %4 = tail call i8* @f2(i8* nonnull align 8 @a1)
%4 = tail call i8* @f2(i8* nonnull @a1)
br label %5
; <label>:5: ; preds = %1, %3
%6 = phi i8* [ %4, %3 ], [ %0, %1 ]
ret i8* %6
}
; Function Attrs: nounwind readnone ssp uwtable
define internal i8* @f2(i8* readnone) local_unnamed_addr #0 {
; ATTRIBUTOR: define internal nonnull align 8 i8* @f2(i8* nonnull readnone align 8)
%2 = icmp eq i8* %0, null
br i1 %2, label %5, label %3
; <label>:3: ; preds = %1
; ATTRIBUTOR: %4 = tail call i8* @f1(i8* nonnull align 8 %0)
%4 = tail call i8* @f1(i8* nonnull %0)
br label %7
; <label>:5: ; preds = %1
; ATTRIBUTOR: %6 = tail call i8* @f3(i8* nonnull align 16 @a2)
%6 = tail call i8* @f3(i8* nonnull @a2)
br label %7
; <label>:7: ; preds = %5, %3
%8 = phi i8* [ %4, %3 ], [ %6, %5 ]
ret i8* %8
}
; Function Attrs: nounwind readnone ssp uwtable
define internal i8* @f3(i8* readnone) local_unnamed_addr #0 {
; ATTRIBUTOR: define internal nonnull align 8 i8* @f3(i8* nonnull readnone align 16)
%2 = icmp eq i8* %0, null
br i1 %2, label %3, label %5
; <label>:3: ; preds = %1
; ATTRIBUTOR: %4 = tail call i8* @f1(i8* nonnull align 16 @a2)
%4 = tail call i8* @f1(i8* nonnull @a2)
br label %5
; <label>:5: ; preds = %1, %3
%6 = phi i8* [ %4, %3 ], [ @a1, %1 ]
ret i8* %6
}
; 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)
; ATTRIBUTOR: tail call void @test8(i32* align 4 %ptr1, i32* align 4 %ptr1, i32* %ptr0)
tail call void @test8(i32* %ptr2, i32* %ptr1, i32* %ptr1)
; ATTRIBUTOR: tail call void @test8(i32* align 8 %ptr2, i32* align 4 %ptr1, i32* align 4 %ptr1)
tail call void @test8(i32* %ptr2, i32* %ptr1, i32* %ptr1)
; ATTRIBUTOR: tail call void @test8(i32* align 8 %ptr2, i32* align 4 %ptr1, i32* align 4 %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 }

View File

@ -88,7 +88,7 @@ entry:
; 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)

View File

@ -79,13 +79,13 @@ declare i8* @baz(...) nounwind uwtable
; 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

View File

@ -40,14 +40,14 @@ define i8* @test3() {
; 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
}