forked from OSchip/llvm-project
[Attributor] NoAlias on return values.
Porting function return value attribute noalias to attributor. This will be followed with a patch for callsite and function argumets. Reviewers: jdoerfert Subscribers: lebedev.ri, hiraditya, llvm-commits Differential Revision: https://reviews.llvm.org/D63067 llvm-svn: 366728
This commit is contained in:
parent
942537d9fa
commit
69ebb02001
|
@ -784,6 +784,27 @@ struct AAWillReturn : public AbstractAttribute {
|
|||
/// The identifier used by the Attributor for this class of attributes.
|
||||
static constexpr Attribute::AttrKind ID = Attribute::WillReturn;
|
||||
};
|
||||
|
||||
/// An abstract interface for all noalias attributes.
|
||||
struct AANoAlias : public AbstractAttribute {
|
||||
|
||||
/// See AbstractAttribute::AbstractAttribute(...).
|
||||
AANoAlias(Value &V, InformationCache &InfoCache)
|
||||
: AbstractAttribute(V, InfoCache) {}
|
||||
|
||||
/// Return true if we assume that the underlying value is alias.
|
||||
virtual bool isAssumedNoAlias() const = 0;
|
||||
|
||||
/// Return true if we know that underlying value is noalias.
|
||||
virtual bool isKnownNoAlias() const = 0;
|
||||
|
||||
/// 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::NoAlias;
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_TRANSFORMS_IPO_FUNCTIONATTRS_H
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/Analysis/CaptureTracking.h"
|
||||
#include "llvm/Analysis/GlobalsModRef.h"
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
#include "llvm/IR/Argument.h"
|
||||
|
@ -59,6 +60,7 @@ STATISTIC(NumFnReturnedNonNull,
|
|||
STATISTIC(NumFnArgumentNonNull, "Number of function arguments marked nonnull");
|
||||
STATISTIC(NumCSArgumentNonNull, "Number of call site arguments marked nonnull");
|
||||
STATISTIC(NumFnWillReturn, "Number of functions marked willreturn");
|
||||
STATISTIC(NumFnArgumentNoAlias, "Number of function arguments marked noalias");
|
||||
|
||||
// TODO: Determine a good default value.
|
||||
//
|
||||
|
@ -134,6 +136,9 @@ static void bookkeeping(AbstractAttribute::ManifestPosition MP,
|
|||
case Attribute::WillReturn:
|
||||
NumFnWillReturn++;
|
||||
break;
|
||||
case Attribute::NoAlias:
|
||||
NumFnArgumentNoAlias++;
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
@ -1311,6 +1316,101 @@ ChangeStatus AAWillReturnFunction::updateImpl(Attributor &A) {
|
|||
return ChangeStatus::UNCHANGED;
|
||||
}
|
||||
|
||||
/// ------------------------ NoAlias Argument Attribute ------------------------
|
||||
|
||||
struct AANoAliasImpl : AANoAlias, BooleanState {
|
||||
|
||||
AANoAliasImpl(Value &V, InformationCache &InfoCache)
|
||||
: AANoAlias(V, InfoCache) {}
|
||||
|
||||
/// See AbstractAttribute::getState()
|
||||
/// {
|
||||
AbstractState &getState() override { return *this; }
|
||||
const AbstractState &getState() const override { return *this; }
|
||||
/// }
|
||||
|
||||
const std::string getAsStr() const override {
|
||||
return getAssumed() ? "noalias" : "may-alias";
|
||||
}
|
||||
|
||||
/// See AANoAlias::isAssumedNoAlias().
|
||||
bool isAssumedNoAlias() const override { return getAssumed(); }
|
||||
|
||||
/// See AANoAlias::isKnowndNoAlias().
|
||||
bool isKnownNoAlias() const override { return getKnown(); }
|
||||
};
|
||||
|
||||
/// NoAlias attribute for function return value.
|
||||
struct AANoAliasReturned : AANoAliasImpl {
|
||||
|
||||
AANoAliasReturned(Function &F, InformationCache &InfoCache)
|
||||
: AANoAliasImpl(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 noalias.
|
||||
if (F.returnDoesNotAlias()) {
|
||||
indicateOptimisticFixpoint();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::updateImpl(...).
|
||||
virtual ChangeStatus updateImpl(Attributor &A) override;
|
||||
};
|
||||
|
||||
ChangeStatus AANoAliasReturned::updateImpl(Attributor &A) {
|
||||
Function &F = getAnchorScope();
|
||||
|
||||
auto *AARetValImpl = A.getAAFor<AAReturnedValuesImpl>(*this, F);
|
||||
if (!AARetValImpl) {
|
||||
indicatePessimisticFixpoint();
|
||||
return ChangeStatus::CHANGED;
|
||||
}
|
||||
|
||||
std::function<bool(Value &)> Pred = [&](Value &RV) -> bool {
|
||||
if (Constant *C = dyn_cast<Constant>(&RV))
|
||||
if (C->isNullValue() || isa<UndefValue>(C))
|
||||
return true;
|
||||
|
||||
/// For now, we can only deduce noalias if we have call sites.
|
||||
/// FIXME: add more support.
|
||||
ImmutableCallSite ICS(&RV);
|
||||
if (!ICS)
|
||||
return false;
|
||||
|
||||
auto *NoAliasAA = A.getAAFor<AANoAlias>(*this, RV);
|
||||
|
||||
if (!ICS.returnDoesNotAlias() && (!NoAliasAA ||
|
||||
!NoAliasAA->isAssumedNoAlias()))
|
||||
return false;
|
||||
|
||||
/// FIXME: We can improve capture check in two ways:
|
||||
/// 1. Use the AANoCapture facilities.
|
||||
/// 2. Use the location of return insts for escape queries.
|
||||
if (PointerMayBeCaptured(&RV, /* ReturnCaptures */ false,
|
||||
/* StoreCaptures */ true))
|
||||
return false;
|
||||
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!AARetValImpl->checkForallReturnedValues(Pred)) {
|
||||
indicatePessimisticFixpoint();
|
||||
return ChangeStatus::CHANGED;
|
||||
}
|
||||
|
||||
return ChangeStatus::UNCHANGED;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------------------------------
|
||||
/// Attributor
|
||||
/// ----------------------------------------------------------------------------
|
||||
|
@ -1507,10 +1607,15 @@ void Attributor::identifyDefaultAbstractAttributes(
|
|||
if (!Whitelist || Whitelist->count(AAReturnedValues::ID))
|
||||
registerAA(*new AAReturnedValuesImpl(F, InfoCache));
|
||||
|
||||
// Every function with pointer return type might be marked nonnull.
|
||||
if (ReturnType->isPointerTy() &&
|
||||
(!Whitelist || Whitelist->count(AANonNullReturned::ID)))
|
||||
registerAA(*new AANonNullReturned(F, InfoCache));
|
||||
if (ReturnType->isPointerTy()) {
|
||||
// Every function with pointer return type might be marked nonnull.
|
||||
if (!Whitelist || Whitelist->count(AANonNullReturned::ID))
|
||||
registerAA(*new AANonNullReturned(F, InfoCache));
|
||||
|
||||
// Every function with pointer return type might be marked noalias.
|
||||
if (!Whitelist || Whitelist->count(AANoAliasReturned::ID))
|
||||
registerAA(*new AANoAliasReturned(F, InfoCache));
|
||||
}
|
||||
}
|
||||
|
||||
// Every argument with pointer type might be marked nonnull.
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
; RUN: opt -S -attributor -attributor-disable=false < %s | FileCheck %s
|
||||
|
||||
; TEST 1 - negative.
|
||||
|
||||
; void *G;
|
||||
; void *foo(){
|
||||
; void *V = malloc(4);
|
||||
; G = V;
|
||||
; return V;
|
||||
; }
|
||||
|
||||
@G = external global i8*
|
||||
|
||||
; CHECK: define i8* @foo()
|
||||
define i8* @foo() {
|
||||
%1 = tail call noalias i8* @malloc(i64 4)
|
||||
store i8* %1, i8** @G, align 8
|
||||
ret i8* %1
|
||||
}
|
||||
|
||||
declare noalias i8* @malloc(i64)
|
||||
|
||||
; TEST 2
|
||||
; call noalias function in return instruction.
|
||||
|
||||
; CHECK: define noalias i8* @return_noalias()
|
||||
define i8* @return_noalias(){
|
||||
%1 = tail call noalias i8* @malloc(i64 4)
|
||||
ret i8* %1
|
||||
}
|
||||
|
||||
declare i8* @alias()
|
||||
|
||||
; TEST 3
|
||||
; CHECK: define i8* @call_alias()
|
||||
; CHECK-NOT: noalias
|
||||
define i8* @call_alias(){
|
||||
%1 = tail call i8* @alias()
|
||||
ret i8* %1
|
||||
}
|
||||
|
||||
; TEST 4
|
||||
; void *baz();
|
||||
; void *foo(int a);
|
||||
;
|
||||
; void *bar() {
|
||||
; foo(0);
|
||||
; return baz();
|
||||
; }
|
||||
;
|
||||
; void *foo(int a) {
|
||||
; if (a)
|
||||
; bar();
|
||||
; return malloc(4);
|
||||
; }
|
||||
|
||||
; CHECK: define i8* @bar()
|
||||
define i8* @bar() nounwind uwtable {
|
||||
%1 = tail call i8* (...) @baz()
|
||||
ret i8* %1
|
||||
}
|
||||
|
||||
; CHECK: define noalias i8* @foo1(i32)
|
||||
define i8* @foo1(i32) nounwind uwtable {
|
||||
%2 = icmp eq i32 %0, 0
|
||||
br i1 %2, label %5, label %3
|
||||
|
||||
3: ; preds = %1
|
||||
%4 = tail call i8* (...) @baz()
|
||||
br label %5
|
||||
|
||||
5: ; preds = %1, %3
|
||||
%6 = tail call noalias i8* @malloc(i64 4)
|
||||
ret i8* %6
|
||||
}
|
||||
|
||||
declare i8* @baz(...) nounwind uwtable
|
||||
|
||||
; TEST 5
|
||||
|
||||
; Returning global pointer. Should not be noalias.
|
||||
; CHECK: define nonnull i8** @getter()
|
||||
define i8** @getter() {
|
||||
ret i8** @G
|
||||
}
|
||||
|
||||
; Returning global pointer. Should not be noalias.
|
||||
; CHECK: define nonnull i8** @calle1()
|
||||
define i8** @calle1(){
|
||||
%1 = call i8** @getter()
|
||||
ret i8** %1
|
||||
}
|
||||
|
||||
; TEST 6
|
||||
declare noalias i8* @strdup(i8* nocapture) nounwind
|
||||
|
||||
; CHECK: define noalias i8* @test6()
|
||||
define i8* @test6() nounwind uwtable ssp {
|
||||
%x = alloca [2 x i8], align 1
|
||||
%arrayidx = getelementptr inbounds [2 x i8], [2 x i8]* %x, i64 0, i64 0
|
||||
store i8 97, i8* %arrayidx, align 1
|
||||
%arrayidx1 = getelementptr inbounds [2 x i8], [2 x i8]* %x, i64 0, i64 1
|
||||
store i8 0, i8* %arrayidx1, align 1
|
||||
%call = call noalias i8* @strdup(i8* %arrayidx) nounwind
|
||||
ret i8* %call
|
||||
}
|
||||
|
||||
; TEST 7
|
||||
|
||||
; CHECK: define noalias i8* @test7()
|
||||
define i8* @test7() nounwind {
|
||||
entry:
|
||||
%A = call noalias i8* @malloc(i64 4) nounwind
|
||||
%tobool = icmp eq i8* %A, null
|
||||
br i1 %tobool, label %return, label %if.end
|
||||
|
||||
if.end:
|
||||
store i8 7, i8* %A
|
||||
br label %return
|
||||
|
||||
return:
|
||||
%retval.0 = phi i8* [ %A, %if.end ], [ null, %entry ]
|
||||
ret i8* %retval.0
|
||||
}
|
||||
|
||||
; TEST 8
|
||||
|
||||
; CHECK: define noalias i8* @test8(i32*)
|
||||
define i8* @test8(i32*) nounwind uwtable {
|
||||
%2 = tail call noalias i8* @malloc(i64 4)
|
||||
%3 = icmp ne i32* %0, null
|
||||
br i1 %3, label %4, label %5
|
||||
|
||||
4: ; preds = %1
|
||||
store i8 10, i8* %2
|
||||
br label %5
|
||||
|
||||
5: ; preds = %1, %4
|
||||
ret i8* %2
|
||||
}
|
|
@ -22,7 +22,8 @@ define i8* @test2(i8* nonnull %p) {
|
|||
; Given an SCC where one of the functions can not be marked nonnull,
|
||||
; can we still mark the other one which is trivially nonnull
|
||||
define i8* @scc_binder() {
|
||||
; BOTH: define i8* @scc_binder
|
||||
; FNATTR: define i8* @scc_binder
|
||||
; ATTRIBUTOR: define noalias i8* @scc_binder
|
||||
call i8* @test3()
|
||||
ret i8* null
|
||||
}
|
||||
|
@ -39,14 +40,14 @@ define i8* @test3() {
|
|||
; just never return period.)
|
||||
define i8* @test4_helper() {
|
||||
; FNATTR: define noalias nonnull i8* @test4_helper
|
||||
; ATTRIBUTOR: define nonnull i8* @test4_helper
|
||||
; ATTRIBUTOR: define noalias nonnull i8* @test4_helper
|
||||
%ret = call i8* @test4()
|
||||
ret i8* %ret
|
||||
}
|
||||
|
||||
define i8* @test4() {
|
||||
; FNATTR: define noalias nonnull i8* @test4
|
||||
; ATTRIBUTOR: define nonnull i8* @test4
|
||||
; ATTRIBUTOR: define noalias nonnull i8* @test4
|
||||
%ret = call i8* @test4_helper()
|
||||
ret i8* %ret
|
||||
}
|
||||
|
@ -55,14 +56,14 @@ define i8* @test4() {
|
|||
; make sure we haven't marked them as nonnull.
|
||||
define i8* @test5_helper() {
|
||||
; FNATTR: define noalias i8* @test5_helper
|
||||
; ATTRIBUTOR: define i8* @test5_helper
|
||||
; ATTRIBUTOR: define noalias i8* @test5_helper
|
||||
%ret = call i8* @test5()
|
||||
ret i8* null
|
||||
}
|
||||
|
||||
define i8* @test5() {
|
||||
; FNATTR: define noalias i8* @test5
|
||||
; ATTRIBUTOR: define i8* @test5
|
||||
; ATTRIBUTOR: define noalias i8* @test5
|
||||
%ret = call i8* @test5_helper()
|
||||
ret i8* %ret
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue