diff --git a/llvm/include/llvm/Analysis/MustExecute.h b/llvm/include/llvm/Analysis/MustExecute.h --- a/llvm/include/llvm/Analysis/MustExecute.h +++ b/llvm/include/llvm/Analysis/MustExecute.h @@ -27,6 +27,7 @@ #include "llvm/Analysis/EHPersonalities.h" #include "llvm/Analysis/InstructionPrecedenceTracking.h" #include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Instruction.h" @@ -366,6 +367,7 @@ /// the expected use case involves few iterators for "far apart" instructions. /// If that changes, we should consider caching more intermediate results. struct MustBeExecutedContextExplorer { + using IsGuaranteedToTransferFnTy = std::function; /// In the description of the parameters we use PP to denote a program point /// for which the must be executed context is explored, or put differently, @@ -375,7 +377,10 @@ /// other than the parent of PP should be /// explored. MustBeExecutedContextExplorer(bool ExploreInterBlock) - : ExploreInterBlock(ExploreInterBlock), EndIterator(*this, nullptr) {} + : ExploreInterBlock(ExploreInterBlock), EndIterator(*this, nullptr), + IsGuaranteedToTransferFn([](const Instruction *I) { + return isGuaranteedToTransferExecutionToSuccessor(I); + }) {} /// Clean up the dynamically allocated iterators. ~MustBeExecutedContextExplorer() { @@ -436,6 +441,13 @@ const bool ExploreInterBlock; ///} + /// Replace the function that determines if an instruction "terminates" with + /// \p IsGuaranteedToTransferFn. + void setIsGuaranteedToTransferFn( + IsGuaranteedToTransferFnTy IsGuaranteedToTransferFn) { + this->IsGuaranteedToTransferFn = IsGuaranteedToTransferFn; + } + private: /// Map from instructions to associated must be executed iterators. DenseMap @@ -443,6 +455,11 @@ /// A unique end iterator. MustBeExecutedIterator EndIterator; + + /// The callback to determines if a function *will* tranfer execution to its + /// successor. By default isGuaranteedToTransferExecutionToSuccessor(const + /// Instruction *) in ValueTracking.h. + IsGuaranteedToTransferFnTy IsGuaranteedToTransferFn; }; } // namespace llvm 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 @@ -1154,7 +1154,7 @@ /// This will usually make the optimistically assumed state the known to be /// true state. /// - /// \returns ChangeStatus::UNCHANGED as the assumed value should not change. + /// \returns ChangeStatus::CHANGED as the known value should have changed. virtual ChangeStatus indicateOptimisticFixpoint() = 0; /// Indicate that the abstract state should converge to the pessimistic state. @@ -1162,7 +1162,7 @@ /// This will usually revert the optimistically assumed state to the known to /// be true state. /// - /// \returns ChangeStatus::CHANGED as the assumed value may change. + /// \returns ChangeStatus::CHANGED as the assumed value should have change. virtual ChangeStatus indicatePessimisticFixpoint() = 0; }; @@ -1196,7 +1196,7 @@ /// See AbstractState::indicateOptimisticFixpoint(...) ChangeStatus indicateOptimisticFixpoint() override { Known = Assumed; - return ChangeStatus::UNCHANGED; + return ChangeStatus::CHANGED; } /// See AbstractState::indicatePessimisticFixpoint(...) @@ -1785,7 +1785,7 @@ ChangeStatus indicateOptimisticFixpoint() override { DerefBytesState.indicateOptimisticFixpoint(); GlobalState.indicateOptimisticFixpoint(); - return ChangeStatus::UNCHANGED; + return ChangeStatus::CHANGED; } /// See AbstractState::indicatePessimisticFixpoint(...) diff --git a/llvm/lib/Analysis/MustExecute.cpp b/llvm/lib/Analysis/MustExecute.cpp --- a/llvm/lib/Analysis/MustExecute.cpp +++ b/llvm/lib/Analysis/MustExecute.cpp @@ -12,7 +12,6 @@ #include "llvm/Analysis/InstructionSimplify.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/Passes.h" -#include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/AssemblyAnnotationWriter.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/InstIterator.h" @@ -459,7 +458,7 @@ // If we do not traverse the call graph we check if we can make progress in // the current function. First, check if the instruction is guaranteed to // transfer execution to the successor. - bool TransfersExecution = isGuaranteedToTransferExecutionToSuccessor(PP); + bool TransfersExecution = IsGuaranteedToTransferFn(PP); if (!TransfersExecution) return nullptr; 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 @@ -797,6 +797,25 @@ MustBeExecutedContextExplorer &Explorer = A.getInfoCache().getMustBeExecutedContextExplorer(); + Explorer.setIsGuaranteedToTransferFn([&](const Instruction *I) { + if (isGuaranteedToTransferExecutionToSuccessor(I)) + return true; + ImmutableCallSite ICS(I); + if (!ICS) + return false; + // We do look at known information not yet in the IR but we also establish + // a dependence which will trigger this update again if the information + // changed. + const IRPosition &CSPos = IRPosition::callsite_function(ICS); + bool WillReturn = ICS.hasFnAttr(Attribute::WillReturn); + if (!WillReturn) + WillReturn = A.getAAFor(*this, CSPos).isKnownWillReturn(); + bool NoUnwind = ICS.hasFnAttr(Attribute::NoUnwind); + if (!NoUnwind) + NoUnwind = A.getAAFor(*this, CSPos).isKnownNoUnwind(); + return WillReturn && NoUnwind; + }); + SetVector NextUses; for (const Use *U : Uses) { @@ -1020,7 +1039,7 @@ /// See AbstractState::indicateOptimisticFixpoint(...). ChangeStatus indicateOptimisticFixpoint() override { IsFixed = true; - return ChangeStatus::UNCHANGED; + return ChangeStatus::CHANGED; } ChangeStatus indicatePessimisticFixpoint() override { diff --git a/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll b/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll --- a/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll +++ b/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll @@ -304,7 +304,7 @@ ; } ; ; There should *not* be a no-capture attribute on %a -; CHECK: define void @negative_test_not_captured_but_returned_call_0b(i64* writeonly %a) +; CHECK: define void @negative_test_not_captured_but_returned_call_0b(i64* nonnull writeonly dereferenceable(8) %a) define void @negative_test_not_captured_but_returned_call_0b(i64* %a) #0 { entry: %call = call i64* @not_captured_but_returned_0(i64* %a) diff --git a/llvm/test/Transforms/FunctionAttrs/liveness.ll b/llvm/test/Transforms/FunctionAttrs/liveness.ll --- a/llvm/test/Transforms/FunctionAttrs/liveness.ll +++ b/llvm/test/Transforms/FunctionAttrs/liveness.ll @@ -1,4 +1,4 @@ -; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=5 -S < %s | FileCheck %s +; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=6 -S < %s | FileCheck %s declare void @no_return_call() nofree noreturn nounwind readnone 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 @@ -583,5 +583,23 @@ ret void } +declare void @unknown_willreturn() willreturn nounwind +define void @step_willreturn() { + call void @unknown_willreturn() + ret void +} +; ATTRIBUTOR: define internal i32 @deref_arg_delayed(i32* nocapture nonnull readonly dereferenceable(4) %D) +define internal i32 @deref_arg_delayed(i32* %D) { + call void @step_willreturn() + %v = load i32, i32* %D + ret i32 %v +} +; FIXME: We should do arg -> call site arg deduction *for known information* +; ATTRIBUTOR: define i32 @extern_deref_arg_use(i32* nocapture readonly %P) +define i32 @extern_deref_arg_use(i32* %P) { + %v = call i32 @deref_arg_delayed(i32* %P) + ret i32 %v +} + attributes #0 = { "null-pointer-is-valid"="true" } attributes #1 = { nounwind willreturn}