diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -19,7 +19,8 @@ #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" -#include "llvm/Analysis/AliasAnalysis.h" +// #include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/Analysis/CaptureTracking.h" #include "llvm/Analysis/GlobalsModRef.h" #include "llvm/IR/Argument.h" @@ -98,7 +99,7 @@ switch (Attr.getKindAsEnum()) { case Attribute::Returned: - NumFnArgumentsReturned++; + NumFnArgumentReturned++; return; case Attribute::NoAlias: NumFnArgumentNoAlias++; @@ -657,6 +658,7 @@ /// ------------------------ NoAlias Argument Attribute ------------------------ struct AANoAliasImpl : AANoAlias, BooleanState { + AANoAliasImpl(Value &V, InformationCache &InfoCache) : AANoAlias(V, InfoCache) {} @@ -696,96 +698,52 @@ return MP_RETURNED; } + /// See AbstractAttriubute::initialize(...). + void initialize(Attributor &A) override { + Function &F = getAnchorScope(); + + // Already noalias. + if (F.returnDoesNotAlias()) { + indicatePessimisticFixpoint(); + return; + } + } + /// See AbstractAttribute::updateImpl(...). virtual ChangeStatus updateImpl(Attributor &A) override; - - /// Tests whether a function is "malloc-like" - /// - /// A function is "malloc-like" if it returns either null or a pointer that - /// doesn't alias any other pointer visible to the caller. - static bool isFunctionMallocLike(Function &F); }; -bool AANoAliasReturned::isFunctionMallocLike(Function &F) { - SmallSetVector FlowsToReturn; - for (BasicBlock &BB : F) - if (ReturnInst *Ret = dyn_cast(BB.getTerminator())) - FlowsToReturn.insert(Ret->getReturnValue()); - - for (unsigned i = 0; i != FlowsToReturn.size(); ++i) { - Value *RetVal = FlowsToReturn[i]; - - if (Constant *C = dyn_cast(RetVal)) { - if (!C->isNullValue() && !isa(C)) - return false; +ChangeStatus AANoAliasReturned::updateImpl(Attributor &A) { + Function &F = getAnchorScope(); - continue; - } + auto *AARetValImpl = A.getAAFor(*this, F); + if (!AARetValImpl) + return ChangeStatus::CHANGED; - if (isa(RetVal)) - return false; + for (auto It = AARetValImpl->begin(); It != AARetValImpl->end(); ++It) { + Value *RV = It->first; + const SmallPtrSet ReturnInsts = It->second; - if (Instruction *RVI = dyn_cast(RetVal)) - switch (RVI->getOpcode()) { - // Extend the analysis by looking upwards. - case Instruction::BitCast: - case Instruction::GetElementPtr: - case Instruction::AddrSpaceCast: - FlowsToReturn.insert(RVI->getOperand(0)); - continue; - case Instruction::Select: { - SelectInst *SI = cast(RVI); - FlowsToReturn.insert(SI->getTrueValue()); - FlowsToReturn.insert(SI->getFalseValue()); - continue; - } - case Instruction::PHI: { - PHINode *PN = cast(RVI); - for (Value *IncValue : PN->incoming_values()) - FlowsToReturn.insert(IncValue); + if (Constant *C = dyn_cast(RV)) + if (C->isNullValue() || isa(C)) continue; - } - - // Check whether the pointer came from an allocation. - case Instruction::Alloca: - break; - case Instruction::Call: - case Instruction::Invoke: { - CallSite CS(RVI); - if (CS.hasRetAttr(Attribute::NoAlias)) - break; - if (CS.getCalledFunction()) - break; - LLVM_FALLTHROUGH; - } - default: - return false; // Did not come from an allocation. - } - - if (PointerMayBeCaptured(RetVal, false, /*StoreCaptures=*/false)) - return false; - } - - return true; -} -ChangeStatus AANoAliasReturned::updateImpl(Attributor &A) { - Function &F = getAnchorScope(); + if (PointerMayBeCaptured(RV, false, false)) { + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } - // Alreade noalias. - if (F.returnDoesNotAlias()) - return ChangeStatus::UNCHANGED; + for (auto RI : ReturnInsts) { + ImmutableCallSite ICS(RI); + auto *NoAliasReturnedAA = A.getAAFor(*this, *RI); - // We can infer and propagate function attributes only when wh know that the - // definition we'll get at link time is *exactly* the definition we see now. - // For more details, see GlobalValue::mayBeDerefined. - // - // We annotate noalias return values, which are only applicable to pointer - // types - if (!F.hasExactDefinition() || !F.getReturnType()->isPointerTy() || - !isFunctionMallocLike(F)) { - indicatePessimisticFixpoint(); - return ChangeStatus::CHANGED; + if (ICS && + (!NoAliasReturnedAA || !NoAliasReturnedAA->isAssumedNoAlias()) && + !ICS.returnDoesNotAlias()) { + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } + } } return ChangeStatus::UNCHANGED; @@ -939,10 +897,11 @@ // though it is an argument attribute. if (!Whitelist || Whitelist->count(AAReturnedValues::ID)) registerAA(*new AAReturnedValuesImpl(F, InfoCache)); - } - // Every function return value might be marked noalias. - registerAA(*new AANoAliasReturned(F, InfoCache)); + // Every function with pointer return type might be marked noalias. + if (ReturnType->isPointerTy()) + registerAA(*new AANoAliasReturned(F, InfoCache)); + } // Walk all instructions to find more attribute opportunities and also // interesting instructions that might be queried by abstract attributes diff --git a/llvm/test/Transforms/FunctionAttrs/noalias.ll b/llvm/test/Transforms/FunctionAttrs/noalias.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/FunctionAttrs/noalias.ll @@ -0,0 +1,21 @@ +; RUN: opt -S -attributor < %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) diff --git a/llvm/test/Transforms/FunctionAttrs/nonnull.ll b/llvm/test/Transforms/FunctionAttrs/nonnull.ll --- a/llvm/test/Transforms/FunctionAttrs/nonnull.ll +++ b/llvm/test/Transforms/FunctionAttrs/nonnull.ll @@ -1,11 +1,13 @@ ; RUN: opt -S -functionattrs -enable-nonnull-arg-prop %s | FileCheck %s ; RUN: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s | FileCheck %s +; RUN: opt -S -attributor < %s | FileCheck %s --check-prefix=ATTRIBUTOR declare nonnull i8* @ret_nonnull() ; Return a pointer trivially nonnull (call return attribute) define i8* @test1() { ; CHECK: define nonnull i8* @test1 +; ATTRIBUTOR: define "noalias" i8* @test1 %ret = call i8* @ret_nonnull() ret i8* %ret } @@ -13,6 +15,7 @@ ; Return a pointer trivially nonnull (argument attribute) define i8* @test2(i8* nonnull %p) { ; CHECK: define nonnull i8* @test2 +; ATTRIBUTOR: define "noalias" i8* @test2 ret i8* %p } @@ -20,12 +23,14 @@ ; can we still mark the other one which is trivially nonnull define i8* @scc_binder() { ; CHECK: define i8* @scc_binder +; ATTRIBUTOR: define "noalias" i8* @scc_binder call i8* @test3() ret i8* null } define i8* @test3() { ; CHECK: define nonnull i8* @test3 +; ATTRIBUTOR: define "noalias" i8* @test3 call i8* @scc_binder() %ret = call i8* @ret_nonnull() ret i8* %ret @@ -36,12 +41,14 @@ ; just never return period.) define i8* @test4_helper() { ; CHECK: define noalias nonnull i8* @test4_helper +; ATTRIBUTOR: define "noalias" i8* @test4_helper %ret = call i8* @test4() ret i8* %ret } define i8* @test4() { ; CHECK: define noalias nonnull i8* @test4 +; ATTRIBUTOR: define "noalias" i8* @test4 %ret = call i8* @test4_helper() ret i8* %ret } @@ -50,12 +57,14 @@ ; make sure we haven't marked them as nonnull. define i8* @test5_helper() { ; CHECK: define noalias i8* @test5_helper +; ATTRIBUTOR: define "noalias" i8* @test5_helper %ret = call i8* @test5() ret i8* null } define i8* @test5() { ; CHECK: define noalias i8* @test5 +; ATTRIBUTOR: define "noalias" i8* @test5 %ret = call i8* @test5_helper() ret i8* %ret } @@ -64,6 +73,7 @@ define i8* @test6() { entry: ; CHECK: define nonnull i8* @test6 +; ATTRIBUTOR: define "noalias" i8* @test6 %ret = call i8* @ret_nonnull() br label %loop loop: @@ -219,6 +229,7 @@ } ; CHECK: define nonnull i32* @gep1( +; ATTRIBUTOR: define "noalias" i32* @gep1( define i32* @gep1(i32* %p) { %q = getelementptr inbounds i32, i32* %p, i32 1 ret i32* %q @@ -227,11 +238,13 @@ define i32* @gep1_no_null_opt(i32* %p) #0 { ; Should't be able to derive nonnull based on gep. ; CHECK: define i32* @gep1_no_null_opt( +; ATTRIBUTOR: define "noalias" i32* @gep1_no_null_opt( %q = getelementptr inbounds i32, i32* %p, i32 1 ret i32* %q } ; CHECK: define i32 addrspace(3)* @gep2( +; ATTRIBUTOR: define "noalias" i32 addrspace(3)* @gep2( define i32 addrspace(3)* @gep2(i32 addrspace(3)* %p) { %q = getelementptr inbounds i32, i32 addrspace(3)* %p, i32 1 ret i32 addrspace(3)* %q