Index: llvm/trunk/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/trunk/include/llvm/Transforms/IPO/Attributor.h +++ llvm/trunk/include/llvm/Transforms/IPO/Attributor.h @@ -784,6 +784,27 @@ /// 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 Index: llvm/trunk/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/trunk/lib/Transforms/IPO/Attributor.cpp +++ llvm/trunk/lib/Transforms/IPO/Attributor.cpp @@ -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(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 @@ case Attribute::WillReturn: NumFnWillReturn++; break; + case Attribute::NoAlias: + NumFnArgumentNoAlias++; + return; default: return; } @@ -1311,6 +1316,101 @@ 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(*this, F); + if (!AARetValImpl) { + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } + + std::function Pred = [&](Value &RV) -> bool { + if (Constant *C = dyn_cast(&RV)) + if (C->isNullValue() || isa(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(*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 @@ 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. Index: llvm/trunk/test/Transforms/FunctionAttrs/noalias_returned.ll =================================================================== --- llvm/trunk/test/Transforms/FunctionAttrs/noalias_returned.ll +++ llvm/trunk/test/Transforms/FunctionAttrs/noalias_returned.ll @@ -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 +} Index: llvm/trunk/test/Transforms/FunctionAttrs/nonnull.ll =================================================================== --- llvm/trunk/test/Transforms/FunctionAttrs/nonnull.ll +++ llvm/trunk/test/Transforms/FunctionAttrs/nonnull.ll @@ -22,7 +22,8 @@ ; 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 @@ ; 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 @@ ; 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 }