diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -289,6 +289,19 @@ } ///} + /// Return true if the position refers to a function interface, that is the + /// function scope, the function return, or an argumnt. + bool isFnInterfaceKind() const { + switch (getPositionKind()) { + case IRPosition::IRP_FUNCTION: + case IRPosition::IRP_RETURNED: + case IRPosition::IRP_ARGUMENT: + return true; + default: + return false; + } + } + /// Return the Function surrounding the anchor value. /// ///{ @@ -1035,6 +1048,27 @@ IRAttribute(const IRPosition &IRP) : IRPosition(IRP) {} ~IRAttribute() {} + /// See AbstractAttribute::initialize(...). + virtual void initialize(Attributor &A) override { + if (hasAttr(getAttrKind())) { + this->getState().indicateOptimisticFixpoint(); + return; + } + + const IRPosition &IRP = this->getIRPosition(); + bool IsFnInterface = IRP.isFnInterfaceKind(); + const Function *FnScope = IRP.getAnchorScope(); + // TODO: Not all attributes require an exact definition. Find a way to + // enable deduction for some but not all attributes in case the + // definition might be changed at runtime, see also + // http://lists.llvm.org/pipermail/llvm-dev/2018-February/121275.html. + // TODO: We could always determine abstract attributes and if sufficient + // information was found we could duplicate the functions that do not + // have an exact definition. + if (IsFnInterface && (!FnScope || !FnScope->hasExactDefinition())) + this->getState().indicatePessimisticFixpoint(); + } + /// See AbstractAttribute::manifest(...). ChangeStatus manifest(Attributor &A) override { SmallVector DeducedAttrs; 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 @@ -641,12 +641,6 @@ struct AANoUnwindImpl : AANoUnwind { AANoUnwindImpl(const IRPosition &IRP) : AANoUnwind(IRP) {} - /// See AbstractAttribute::initialize(...). - void initialize(Attributor &A) override { - if (hasAttr({Attribute::NoUnwind})) - indicateOptimisticFixpoint(); - } - const std::string getAsStr() const override { return getAssumed() ? "nounwind" : "may-unwind"; } @@ -692,7 +686,7 @@ void initialize(Attributor &A) override { AANoUnwindImpl::initialize(A); Function *F = getAssociatedFunction(); - if (!F || !F->hasExactDefinition()) + if (!F) indicatePessimisticFixpoint(); } @@ -752,7 +746,7 @@ ReturnedValues.clear(); Function *F = getAssociatedFunction(); - if (!F || !F->hasExactDefinition()) { + if (!F) { indicatePessimisticFixpoint(); return; } @@ -771,6 +765,9 @@ return; } } + + if (!F->hasExactDefinition()) + indicatePessimisticFixpoint(); } /// See AbstractAttribute::manifest(...). @@ -1137,12 +1134,6 @@ struct AANoSyncImpl : AANoSync { AANoSyncImpl(const IRPosition &IRP) : AANoSync(IRP) {} - /// See AbstractAttribute::initialize(...). - void initialize(Attributor &A) override { - if (hasAttr({Attribute::NoSync})) - indicateOptimisticFixpoint(); - } - const std::string getAsStr() const override { return getAssumed() ? "nosync" : "may-sync"; } @@ -1310,7 +1301,7 @@ void initialize(Attributor &A) override { AANoSyncImpl::initialize(A); Function *F = getAssociatedFunction(); - if (!F || !F->hasExactDefinition()) + if (!F) indicatePessimisticFixpoint(); } @@ -1336,12 +1327,6 @@ struct AANoFreeImpl : public AANoFree { AANoFreeImpl(const IRPosition &IRP) : AANoFree(IRP) {} - /// See AbstractAttribute::initialize(...). - void initialize(Attributor &A) override { - if (hasAttr({Attribute::NoFree})) - indicateOptimisticFixpoint(); - } - /// See AbstractAttribute::updateImpl(...). ChangeStatus updateImpl(Attributor &A) override { auto CheckForNoFree = [&](Instruction &I) { @@ -1380,7 +1365,7 @@ void initialize(Attributor &A) override { AANoFreeImpl::initialize(A); Function *F = getAssociatedFunction(); - if (!F || !F->hasExactDefinition()) + if (!F) indicatePessimisticFixpoint(); } @@ -1409,6 +1394,8 @@ void initialize(Attributor &A) override { if (hasAttr({Attribute::NonNull, Attribute::Dereferenceable})) indicateOptimisticFixpoint(); + else + AANonNull::initialize(A); } /// See AbstractAttribute::getAsStr(). @@ -1514,14 +1501,6 @@ struct AANoRecurseImpl : public AANoRecurse { AANoRecurseImpl(const IRPosition &IRP) : AANoRecurse(IRP) {} - /// See AbstractAttribute::initialize(...). - void initialize(Attributor &A) override { - if (hasAttr({getAttrKind()})) { - indicateOptimisticFixpoint(); - return; - } - } - /// See AbstractAttribute::getAsStr() const std::string getAsStr() const override { return getAssumed() ? "norecurse" : "may-recurse"; @@ -1548,7 +1527,7 @@ void initialize(Attributor &A) override { AANoRecurseImpl::initialize(A); Function *F = getAssociatedFunction(); - if (!F || !F->hasExactDefinition()) + if (!F) indicatePessimisticFixpoint(); } @@ -1601,10 +1580,7 @@ /// See AbstractAttribute::initialize(...). void initialize(Attributor &A) override { - if (hasAttr({Attribute::WillReturn})) { - indicateOptimisticFixpoint(); - return; - } + AAWillReturn::initialize(A); Function *F = getAssociatedFunction(); if (containsPossiblyEndlessLoop(F)) @@ -1651,7 +1627,7 @@ void initialize(Attributor &A) override { AAWillReturnImpl::initialize(A); Function *F = getAssociatedFunction(); - if (!F || !F->hasExactDefinition()) + if (!F) indicatePessimisticFixpoint(); } @@ -1678,12 +1654,6 @@ struct AANoAliasImpl : AANoAlias { AANoAliasImpl(const IRPosition &IRP) : AANoAlias(IRP) {} - /// See AbstractAttribute::initialize(...). - void initialize(Attributor &A) override { - if (hasAttr({Attribute::NoAlias})) - indicateOptimisticFixpoint(); - } - const std::string getAsStr() const override { return getAssumed() ? "noalias" : "may-alias"; } @@ -1785,7 +1755,7 @@ void initialize(Attributor &A) override { AANoAliasImpl::initialize(A); Function *F = getAssociatedFunction(); - if (!F || !F->hasExactDefinition()) + if (!F) indicatePessimisticFixpoint(); } @@ -2179,6 +2149,12 @@ takeKnownDerefBytesMaximum(Attr.getValueAsInt()); NonNullAA = &A.getAAFor(*this, getIRPosition()); + + const IRPosition &IRP = this->getIRPosition(); + bool IsFnInterface = IRP.isFnInterfaceKind(); + const Function *FnScope = IRP.getAnchorScope(); + if (IsFnInterface && (!FnScope || !FnScope->hasExactDefinition())) + indicatePessimisticFixpoint(); } /// See AbstractAttribute::getState() @@ -2333,7 +2309,7 @@ void initialize(Attributor &A) override { AADereferenceableImpl::initialize(A); Function *F = getAssociatedFunction(); - if (!F || !F->hasExactDefinition()) + if (!F) indicatePessimisticFixpoint(); } @@ -2501,7 +2477,7 @@ void initialize(Attributor &A) override { AAAlignImpl::initialize(A); Function *F = getAssociatedFunction(); - if (!F || !F->hasExactDefinition()) + if (!F) indicatePessimisticFixpoint(); } @@ -2531,12 +2507,6 @@ return getAssumed() ? "noreturn" : "may-return"; } - /// See AbstractAttribute::initialize(...). - void initialize(Attributor &A) override { - if (hasAttr({getAttrKind()})) - indicateOptimisticFixpoint(); - } - /// See AbstractAttribute::updateImpl(Attributor &A). virtual ChangeStatus updateImpl(Attributor &A) override { auto CheckForNoReturn = [](Instruction &) { return false; }; @@ -2562,7 +2532,7 @@ void initialize(Attributor &A) override { AANoReturnImpl::initialize(A); Function *F = getAssociatedFunction(); - if (!F || !F->hasExactDefinition()) + if (!F) indicatePessimisticFixpoint(); } @@ -2592,10 +2562,7 @@ /// See AbstractAttribute::initialize(...). void initialize(Attributor &A) override { - if (hasAttr(Attribute::NoCapture)) { - indicateOptimisticFixpoint(); - return; - } + AANoCapture::initialize(A); const IRPosition &IRP = getIRPosition(); const Function *F = @@ -2604,8 +2571,7 @@ // Check what state the associated function can actually capture. if (F) determineFunctionCaptureCapabilities(*F, *this); - - if (!F || !F->hasExactDefinition()) + else indicatePessimisticFixpoint(); } @@ -2992,7 +2958,7 @@ } CallSite CS(U.getUser()); - if (!CS || !CS.isCallee(&U) || !CS.getCaller()->hasExactDefinition()) { + if (!CS || !CS.isCallee(&U)) { if (!RequireAllCallSites) continue; @@ -3022,7 +2988,7 @@ // Since we need to provide return instructions we have to have an exact // definition. const Function *AssociatedFunction = IRP.getAssociatedFunction(); - if (!AssociatedFunction || !AssociatedFunction->hasExactDefinition()) + if (!AssociatedFunction) return false; // If this is a call site query we use the call site specific return values @@ -3042,7 +3008,7 @@ const IRPosition &IRP = QueryingAA.getIRPosition(); const Function *AssociatedFunction = IRP.getAssociatedFunction(); - if (!AssociatedFunction || !AssociatedFunction->hasExactDefinition()) + if (!AssociatedFunction) return false; // TODO: use the function scope once we have call site AAReturnedValues. @@ -3064,7 +3030,7 @@ const IRPosition &IRP = QueryingAA.getIRPosition(); // Since we need to provide instructions we have to have an exact definition. const Function *AssociatedFunction = IRP.getAssociatedFunction(); - if (!AssociatedFunction || !AssociatedFunction->hasExactDefinition()) + if (!AssociatedFunction) return false; // TODO: use the function scope once we have call site AAReturnedValues. @@ -3569,25 +3535,16 @@ Attributor A(InfoCache, DepRecInterval); for (Function &F : M) { - // TODO: Not all attributes require an exact definition. Find a way to - // enable deduction for some but not all attributes in case the - // definition might be changed at runtime, see also - // http://lists.llvm.org/pipermail/llvm-dev/2018-February/121275.html. - // TODO: We could always determine abstract attributes and if sufficient - // information was found we could duplicate the functions that do not - // have an exact definition. - if (!F.hasExactDefinition()) { + if (F.hasExactDefinition()) + NumFnWithExactDefinition++; + else NumFnWithoutExactDefinition++; - continue; - } // For now we ignore naked and optnone functions. if (F.hasFnAttribute(Attribute::Naked) || F.hasFnAttribute(Attribute::OptimizeNone)) continue; - NumFnWithExactDefinition++; - // Populate the Attributor with abstract attribute opportunities in the // function and the information cache with IR information. A.identifyDefaultAbstractAttributes(F); diff --git a/llvm/test/Transforms/FunctionAttrs/arg_returned.ll b/llvm/test/Transforms/FunctionAttrs/arg_returned.ll --- a/llvm/test/Transforms/FunctionAttrs/arg_returned.ll +++ b/llvm/test/Transforms/FunctionAttrs/arg_returned.ll @@ -796,6 +796,33 @@ ret i32 %PHI2 } +define weak_odr i32 @non_exact_0() { + ret i32 0 +} +define weak_odr i32 @non_exact_1(i32 %a) { + ret i32 %a +} +define weak_odr i32 @non_exact_2(i32 returned %a) { + ret i32 %a +} +define weak_odr i32* @non_exact_3(i32* align 32 returned %a) { + ret i32* %a +} +define i32 @exact(i32* %a) { + %c0 = call i32 @non_exact_0() + %c1 = call i32 @non_exact_1(i32 1) + %c2 = call i32 @non_exact_2(i32 2) + %c3 = call i32* @non_exact_3(i32* %a) + %c4 = load i32, i32* %c3 +; FIXME: %c2 should be replaced but not %c0 or %c1! +; CHECK: %add1 = add i32 %c0, %c1 +; CHECK: %add2 = add i32 %add1, %c2 +; CHECK: %add3 = add i32 %add2, %c3 + %add1 = add i32 %c0, %c1 + %add2 = add i32 %add1, %c2 + %add3 = add i32 %add2, %c4 + ret i32 %add3 +} attributes #0 = { noinline nounwind uwtable } 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 @@ -458,4 +458,14 @@ ret i32* %c } +; ATTRIBUTOR: define internal void @called_by_weak(i32* nocapture nonnull %a) +define internal void @called_by_weak(i32* %a) { + ret void +} + +define weak_odr void @weak_caller(i32* nonnull %a) { + call void @called_by_weak(i32* %a) + ret void +} + attributes #0 = { "null-pointer-is-valid"="true" } diff --git a/llvm/test/Transforms/FunctionAttrs/noreturn_async.ll b/llvm/test/Transforms/FunctionAttrs/noreturn_async.ll --- a/llvm/test/Transforms/FunctionAttrs/noreturn_async.ll +++ b/llvm/test/Transforms/FunctionAttrs/noreturn_async.ll @@ -1,4 +1,4 @@ -; RUN: opt -functionattrs -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 -S < %s | FileCheck %s +; RUN: opt -functionattrs -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=4 -S < %s | FileCheck %s ; ; This file is the same as noreturn_sync.ll but with a personality which ; indicates that the exception handler *can* catch asynchronous exceptions. As diff --git a/llvm/test/Transforms/FunctionAttrs/nounwind.ll b/llvm/test/Transforms/FunctionAttrs/nounwind.ll --- a/llvm/test/Transforms/FunctionAttrs/nounwind.ll +++ b/llvm/test/Transforms/FunctionAttrs/nounwind.ll @@ -1,5 +1,5 @@ ; RUN: opt < %s -functionattrs -S | FileCheck %s -; RUN: opt < %s -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=4 -S | FileCheck %s --check-prefix=ATTRIBUTOR +; RUN: opt < %s -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=6 -S | FileCheck %s --check-prefix=ATTRIBUTOR ; TEST 1 ; CHECK: Function Attrs: norecurse nounwind readnone