1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-23 03:02:36 +01:00

[MustExecute] Forward iterate over conditional branches

Summary:
If a conditional branch is encountered we can try to find a join block
where the execution is known to continue. This means finding a suitable
block, e.g., the immediate post dominator of the conditional branch, and
proofing control will always reach that block.

This patch implements different techniques that work with and without
provided analysis.

Reviewers: uenoku, sstefan1, hfinkel

Subscribers: hiraditya, bollu, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D68933
This commit is contained in:
Johannes Doerfert 2019-10-12 20:46:49 -05:00
parent 4bf91275a6
commit ff3180c323
4 changed files with 556 additions and 16 deletions

View File

@ -33,8 +33,13 @@
namespace llvm {
namespace {
template <typename T> using GetterTy = std::function<T *(const Function &F)>;
}
class Instruction;
class DominatorTree;
class PostDominatorTree;
class Loop;
/// Captures loop safety information.
@ -374,8 +379,14 @@ struct MustBeExecutedContextExplorer {
/// \param ExploreInterBlock Flag to indicate if instructions in blocks
/// other than the parent of PP should be
/// explored.
MustBeExecutedContextExplorer(bool ExploreInterBlock)
: ExploreInterBlock(ExploreInterBlock), EndIterator(*this, nullptr) {}
MustBeExecutedContextExplorer(
bool ExploreInterBlock,
GetterTy<const LoopInfo> LIGetter =
[](const Function &) { return nullptr; },
GetterTy<const PostDominatorTree> PDTGetter =
[](const Function &) { return nullptr; })
: ExploreInterBlock(ExploreInterBlock), LIGetter(LIGetter),
PDTGetter(PDTGetter), EndIterator(*this, nullptr) {}
/// Clean up the dynamically allocated iterators.
~MustBeExecutedContextExplorer() {
@ -454,6 +465,9 @@ struct MustBeExecutedContextExplorer {
getMustBeExecutedNextInstruction(MustBeExecutedIterator &It,
const Instruction *PP);
/// Find the next join point from \p InitBB in forward direction.
const BasicBlock *findForwardJoinPoint(const BasicBlock *InitBB);
/// Parameter that limit the performed exploration. See the constructor for
/// their meaning.
///{
@ -461,6 +475,19 @@ struct MustBeExecutedContextExplorer {
///}
private:
/// Getters for common CFG analyses: LoopInfo, DominatorTree, and
/// PostDominatorTree.
///{
GetterTy<const LoopInfo> LIGetter;
GetterTy<const PostDominatorTree> PDTGetter;
///}
/// Map to cache isGuaranteedToTransferExecutionToSuccessor results.
DenseMap<const BasicBlock *, Optional<bool>> BlockTransferMap;
/// Map to cache containsIrreducibleCFG results.
DenseMap<const Function*, Optional<bool>> IrreducibleControlMap;
/// Map from instructions to associated must be executed iterators.
DenseMap<const Instruction *, MustBeExecutedIterator *>
InstructionIteratorMap;

View File

@ -13,6 +13,7 @@
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/Passes.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/Analysis/PostDominators.h"
#include "llvm/IR/AssemblyAnnotationWriter.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/InstIterator.h"
@ -353,7 +354,19 @@ ModulePass *llvm::createMustBeExecutedContextPrinter() {
}
bool MustBeExecutedContextPrinter::runOnModule(Module &M) {
MustBeExecutedContextExplorer Explorer(true);
// We provide non-PM analysis here because the old PM doesn't like to query
// function passes from a module pass. Given that this is a printer, we don't
// care much about memory leaks.
GetterTy<LoopInfo> LIGetter = [this](const Function &F) {
DominatorTree *DT = new DominatorTree(const_cast<Function &>(F));
LoopInfo *LI = new LoopInfo(*DT);
return LI;
};
GetterTy<PostDominatorTree> PDTGetter = [this](const Function &F) {
PostDominatorTree *PDT = new PostDominatorTree(const_cast<Function &>(F));
return PDT;
};
MustBeExecutedContextExplorer Explorer(true, LIGetter, PDTGetter);
for (Function &F : M) {
for (Instruction &I : instructions(F)) {
dbgs() << "-- Explore context of: " << I << "\n";
@ -443,6 +456,173 @@ bool MustExecutePrinter::runOnFunction(Function &F) {
return false;
}
/// Return true if \p L might be an endless loop.
static bool maybeEndlessLoop(const Loop &L) {
if (L.getHeader()->getParent()->hasFnAttribute(Attribute::WillReturn))
return false;
// TODO: Actually try to prove it is not.
// TODO: If maybeEndlessLoop is going to be expensive, cache it.
return true;
}
static bool mayContainIrreducibleControl(const Function &F, const LoopInfo *LI) {
if (!LI)
return false;
using RPOTraversal = ReversePostOrderTraversal<const Function *>;
RPOTraversal FuncRPOT(&F);
return !containsIrreducibleCFG<const BasicBlock *, const RPOTraversal,
const LoopInfo>(FuncRPOT, *LI);
}
/// Lookup \p Key in \p Map and return the result, potentially after
/// initializing the optional through \p Fn(\p args).
template <typename K, typename V, typename FnTy, typename... ArgsTy>
static V getOrCreateCachedOptional(K Key, DenseMap<K, Optional<V>> &Map,
FnTy &&Fn, ArgsTy&&... args) {
Optional<V> &OptVal = Map[Key];
if (!OptVal.hasValue())
OptVal = Fn(std::forward<ArgsTy>(args)...);
return OptVal.getValue();
}
const BasicBlock *
MustBeExecutedContextExplorer::findForwardJoinPoint(const BasicBlock *InitBB) {
const LoopInfo *LI = LIGetter(*InitBB->getParent());
const PostDominatorTree *PDT = PDTGetter(*InitBB->getParent());
LLVM_DEBUG(dbgs() << "\tFind forward join point for " << InitBB->getName()
<< (LI ? " [LI]" : "") << (PDT ? " [PDT]" : ""));
const Function &F = *InitBB->getParent();
const Loop *L = LI ? LI->getLoopFor(InitBB) : nullptr;
const BasicBlock *HeaderBB = L ? L->getHeader() : InitBB;
bool WillReturnAndNoThrow = (F.hasFnAttribute(Attribute::WillReturn) ||
(L && !maybeEndlessLoop(*L))) &&
F.doesNotThrow();
LLVM_DEBUG(dbgs() << (L ? " [in loop]" : "")
<< (WillReturnAndNoThrow ? " [WillReturn] [NoUnwind]" : "")
<< "\n");
// Determine the adjacent blocks in the given direction but exclude (self)
// loops under certain circumstances.
SmallVector<const BasicBlock *, 8> Worklist;
for (const BasicBlock *SuccBB : successors(InitBB)) {
bool IsLatch = SuccBB == HeaderBB;
// Loop latches are ignored in forward propagation if the loop cannot be
// endless and may not throw: control has to go somewhere.
if (!WillReturnAndNoThrow || !IsLatch)
Worklist.push_back(SuccBB);
}
LLVM_DEBUG(dbgs() << "\t\t#Worklist: " << Worklist.size() << "\n");
// If there are no other adjacent blocks, there is no join point.
if (Worklist.empty())
return nullptr;
// If there is one adjacent block, it is the join point.
if (Worklist.size() == 1)
return Worklist[0];
// Try to determine a join block through the help of the post-dominance
// tree. If no tree was provided, we perform simple pattern matching for one
// block conditionals and one block loops only.
const BasicBlock *JoinBB = nullptr;
if (PDT)
if (const auto *InitNode = PDT->getNode(InitBB))
if (const auto *IDomNode = InitNode->getIDom())
JoinBB = IDomNode->getBlock();
if (!JoinBB && Worklist.size() == 2) {
const BasicBlock *Succ0 = Worklist[0];
const BasicBlock *Succ1 = Worklist[1];
const BasicBlock *Succ0UniqueSucc = Succ0->getUniqueSuccessor();
const BasicBlock *Succ1UniqueSucc = Succ1->getUniqueSuccessor();
if (Succ0UniqueSucc == InitBB) {
// InitBB -> Succ0 -> InitBB
// InitBB -> Succ1 = JoinBB
JoinBB = Succ1;
} else if (Succ1UniqueSucc == InitBB) {
// InitBB -> Succ1 -> InitBB
// InitBB -> Succ0 = JoinBB
JoinBB = Succ0;
} else if (Succ0 == Succ1UniqueSucc) {
// InitBB -> Succ0 = JoinBB
// InitBB -> Succ1 -> Succ0 = JoinBB
JoinBB = Succ0;
} else if (Succ1 == Succ0UniqueSucc) {
// InitBB -> Succ0 -> Succ1 = JoinBB
// InitBB -> Succ1 = JoinBB
JoinBB = Succ1;
} else if (Succ0UniqueSucc == Succ1UniqueSucc) {
// InitBB -> Succ0 -> JoinBB
// InitBB -> Succ1 -> JoinBB
JoinBB = Succ0UniqueSucc;
}
}
if (!JoinBB && L)
JoinBB = L->getUniqueExitBlock();
if (!JoinBB)
return nullptr;
LLVM_DEBUG(dbgs() << "\t\tJoin block candidate: " << JoinBB->getName() << "\n");
// In forward direction we check if control will for sure reach JoinBB from
// InitBB, thus it can not be "stopped" along the way. Ways to "stop" control
// are: infinite loops and instructions that do not necessarily transfer
// execution to their successor. To check for them we traverse the CFG from
// the adjacent blocks to the JoinBB, looking at all intermediate blocks.
// If we know the function is "will-return" and "no-throw" there is no need
// for futher checks.
if (!F.hasFnAttribute(Attribute::WillReturn) || !F.doesNotThrow()) {
auto BlockTransfersExecutionToSuccessor = [](const BasicBlock *BB) {
return isGuaranteedToTransferExecutionToSuccessor(BB);
};
SmallPtrSet<const BasicBlock *, 16> Visited;
while (!Worklist.empty()) {
const BasicBlock *ToBB = Worklist.pop_back_val();
if (ToBB == JoinBB)
continue;
// Make sure all loops in-between are finite.
if (!Visited.insert(ToBB).second) {
if (!F.hasFnAttribute(Attribute::WillReturn)) {
if (!LI)
return nullptr;
bool MayContainIrreducibleControl = getOrCreateCachedOptional(
&F, IrreducibleControlMap, mayContainIrreducibleControl, F, LI);
if (MayContainIrreducibleControl)
return nullptr;
const Loop *L = LI->getLoopFor(ToBB);
if (L && maybeEndlessLoop(*L))
return nullptr;
}
continue;
}
// Make sure the block has no instructions that could stop control
// transfer.
bool TransfersExecution = getOrCreateCachedOptional(
ToBB, BlockTransferMap, BlockTransfersExecutionToSuccessor, ToBB);
if (!TransfersExecution)
return nullptr;
for (const BasicBlock *AdjacentBB : successors(ToBB))
Worklist.push_back(AdjacentBB);
}
}
LLVM_DEBUG(dbgs() << "\tJoin block: " << JoinBB->getName() << "\n");
return JoinBB;
}
const Instruction *
MustBeExecutedContextExplorer::getMustBeExecutedNextInstruction(
MustBeExecutedIterator &It, const Instruction *PP) {
@ -490,6 +670,12 @@ MustBeExecutedContextExplorer::getMustBeExecutedNextInstruction(
return &PP->getSuccessor(0)->front();
}
// Multiple successors mean we need to find the join point where control flow
// converges again. We use the findForwardJoinPoint helper function with
// information about the function and helper analyses, if available.
if (const BasicBlock *JoinBB = findForwardJoinPoint(PP->getParent()))
return &JoinBB->front();
LLVM_DEBUG(dbgs() << "\tNo join point found\n");
return nullptr;
}

View File

@ -1,5 +1,6 @@
; RUN: opt -print-mustexecute -analyze 2>&1 < %s | FileCheck %s --check-prefix=ME
; RUN: opt -print-must-be-executed-contexts -analyze 2>&1 < %s | FileCheck %s --check-prefix=MBEC
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -print-mustexecute -analyze 2>&1 | FileCheck %s --check-prefix=ME
; RUN: opt < %s -print-must-be-executed-contexts -analyze 2>&1 | FileCheck %s --check-prefix=MBEC
;
; void simple_conditional(int c) {
; A();
@ -36,6 +37,8 @@ bb:
; MBEC-NEXT: [F: simple_conditional] call void @B()
; MBEC-NEXT: [F: simple_conditional] %tmp = icmp eq i32 %arg, 0
; MBEC-NEXT: [F: simple_conditional] br i1 %tmp, label %bb2, label %bb1
; MBEC-NEXT: [F: simple_conditional] call void @E()
; MBEC-NEXT: [F: simple_conditional] call void @F()
; MBEC-NOT: call
call void @B()
@ -43,6 +46,8 @@ bb:
; MBEC-NEXT: [F: simple_conditional] call void @B()
; MBEC-NEXT: [F: simple_conditional] %tmp = icmp eq i32 %arg, 0
; MBEC-NEXT: [F: simple_conditional] br i1 %tmp, label %bb2, label %bb1
; MBEC-NEXT: [F: simple_conditional] call void @E()
; MBEC-NEXT: [F: simple_conditional] call void @F()
; MBEC-NOT: call
; MBEC: -- Explore context of: %tmp
@ -280,3 +285,115 @@ declare void @E() nounwind willreturn
declare void @F() nounwind
declare void @G() nounwind willreturn
declare i32 @g(i32*) nounwind willreturn
declare void @h(i32*) nounwind willreturn
define i32 @nonnull_exec_ctx_1(i32* %a, i32 %b) {
; MBEC: -- Explore context of: %tmp3 = icmp eq i32 %b, 0
; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp3 = icmp eq i32 %b, 0
; MBEC-NEXT: [F: nonnull_exec_ctx_1] br i1 %tmp3, label %ex, label %hd
; MBEC-NEXT: -- Explore context of: br i1 %tmp3, label %ex, label %hd
; MBEC-NEXT: [F: nonnull_exec_ctx_1] br i1 %tmp3, label %ex, label %hd
; MBEC-NEXT: -- Explore context of: %tmp5 = tail call i32 @g(i32* nonnull %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp5 = tail call i32 @g(i32* nonnull %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_1] ret i32 %tmp5
; MBEC-NEXT: -- Explore context of: ret i32 %tmp5
; MBEC-NEXT: [F: nonnull_exec_ctx_1] ret i32 %tmp5
; MBEC-NEXT: -- Explore context of: %tmp7 = phi i32 [ %tmp8, %hd ], [ 0, %en ]
; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp7 = phi i32 [ %tmp8, %hd ], [ 0, %en ]
; MBEC-NEXT: [F: nonnull_exec_ctx_1] tail call void @h(i32* %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp8 = add nuw i32 %tmp7, 1
; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp9 = icmp eq i32 %tmp8, %b
; MBEC-NEXT: [F: nonnull_exec_ctx_1] br i1 %tmp9, label %ex, label %hd
; MBEC-NEXT: -- Explore context of: tail call void @h(i32* %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_1] tail call void @h(i32* %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp8 = add nuw i32 %tmp7, 1
; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp9 = icmp eq i32 %tmp8, %b
; MBEC-NEXT: [F: nonnull_exec_ctx_1] br i1 %tmp9, label %ex, label %hd
; MBEC-NEXT: -- Explore context of: %tmp8 = add nuw i32 %tmp7, 1
; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp8 = add nuw i32 %tmp7, 1
; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp9 = icmp eq i32 %tmp8, %b
; MBEC-NEXT: [F: nonnull_exec_ctx_1] br i1 %tmp9, label %ex, label %hd
; MBEC-NEXT: -- Explore context of: %tmp9 = icmp eq i32 %tmp8, %b
; MBEC-NEXT: [F: nonnull_exec_ctx_1] %tmp9 = icmp eq i32 %tmp8, %b
; MBEC-NEXT: [F: nonnull_exec_ctx_1] br i1 %tmp9, label %ex, label %hd
; MBEC-NEXT: -- Explore context of: br i1 %tmp9, label %ex, label %hd
; MBEC-NEXT: [F: nonnull_exec_ctx_1] br i1 %tmp9, label %ex, label %hd
en:
%tmp3 = icmp eq i32 %b, 0
br i1 %tmp3, label %ex, label %hd
ex:
%tmp5 = tail call i32 @g(i32* nonnull %a)
ret i32 %tmp5
hd:
%tmp7 = phi i32 [ %tmp8, %hd ], [ 0, %en ]
tail call void @h(i32* %a)
%tmp8 = add nuw i32 %tmp7, 1
%tmp9 = icmp eq i32 %tmp8, %b
br i1 %tmp9, label %ex, label %hd
}
define i32 @nonnull_exec_ctx_2(i32* %a, i32 %b) nounwind willreturn {
; MBEC: -- Explore context of: %tmp3 = icmp eq i32 %b, 0
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp3 = icmp eq i32 %b, 0
; MBEC-NEXT: [F: nonnull_exec_ctx_2] br i1 %tmp3, label %ex, label %hd
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp5 = tail call i32 @g(i32* nonnull %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5
; MBEC-NEXT: -- Explore context of: br i1 %tmp3, label %ex, label %hd
; MBEC-NEXT: [F: nonnull_exec_ctx_2] br i1 %tmp3, label %ex, label %hd
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp5 = tail call i32 @g(i32* nonnull %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5
; MBEC-NEXT: -- Explore context of: %tmp5 = tail call i32 @g(i32* nonnull %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp5 = tail call i32 @g(i32* nonnull %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5
; MBEC-NEXT: -- Explore context of: ret i32 %tmp5
; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5
; MBEC-NEXT: -- Explore context of: %tmp7 = phi i32 [ %tmp8, %hd ], [ 0, %en ]
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp7 = phi i32 [ %tmp8, %hd ], [ 0, %en ]
; MBEC-NEXT: [F: nonnull_exec_ctx_2] tail call void @h(i32* %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp8 = add nuw i32 %tmp7, 1
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp9 = icmp eq i32 %tmp8, %b
; MBEC-NEXT: [F: nonnull_exec_ctx_2] br i1 %tmp9, label %ex, label %hd
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp5 = tail call i32 @g(i32* nonnull %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5
; MBEC-NEXT: -- Explore context of: tail call void @h(i32* %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_2] tail call void @h(i32* %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp8 = add nuw i32 %tmp7, 1
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp9 = icmp eq i32 %tmp8, %b
; MBEC-NEXT: [F: nonnull_exec_ctx_2] br i1 %tmp9, label %ex, label %hd
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp5 = tail call i32 @g(i32* nonnull %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5
; MBEC-NEXT: -- Explore context of: %tmp8 = add nuw i32 %tmp7, 1
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp8 = add nuw i32 %tmp7, 1
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp9 = icmp eq i32 %tmp8, %b
; MBEC-NEXT: [F: nonnull_exec_ctx_2] br i1 %tmp9, label %ex, label %hd
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp5 = tail call i32 @g(i32* nonnull %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5
; MBEC-NEXT: -- Explore context of: %tmp9 = icmp eq i32 %tmp8, %b
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp9 = icmp eq i32 %tmp8, %b
; MBEC-NEXT: [F: nonnull_exec_ctx_2] br i1 %tmp9, label %ex, label %hd
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp5 = tail call i32 @g(i32* nonnull %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5
; MBEC-NEXT: -- Explore context of: br i1 %tmp9, label %ex, label %hd
; MBEC-NEXT: [F: nonnull_exec_ctx_2] br i1 %tmp9, label %ex, label %hd
; MBEC-NEXT: [F: nonnull_exec_ctx_2] %tmp5 = tail call i32 @g(i32* nonnull %a)
; MBEC-NEXT: [F: nonnull_exec_ctx_2] ret i32 %tmp5
en:
%tmp3 = icmp eq i32 %b, 0
br i1 %tmp3, label %ex, label %hd
ex:
%tmp5 = tail call i32 @g(i32* nonnull %a)
ret i32 %tmp5
hd:
%tmp7 = phi i32 [ %tmp8, %hd ], [ 0, %en ]
tail call void @h(i32* %a)
%tmp8 = add nuw i32 %tmp7, 1
%tmp9 = icmp eq i32 %tmp8, %b
br i1 %tmp9, label %ex, label %hd
}

View File

@ -1,3 +1,4 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -functionattrs -enable-nonnull-arg-prop %s | FileCheck %s --check-prefixes=BOTH,FNATTR
; RUN: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s | FileCheck %s --check-prefixes=BOTH,FNATTR
; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=8 -S < %s | FileCheck %s --check-prefixes=BOTH,ATTRIBUTOR
@ -159,7 +160,7 @@ define void @test13_helper() {
ret void
}
define internal void @test13(i8* %a, i8* %b, i8* %c) {
; ATTRIBUTOR: define internal void @test13(i8* nocapture nonnull readnone %a, i8* nocapture readnone %b, i8* nocapture readnone %c)
; ATTRIBUTOR: define internal void @test13(i8* nocapture nonnull readnone %a, i8* nocapture readnone %b, i8* nocapture readnone %c)
ret void
}
@ -172,7 +173,7 @@ declare nonnull i8* @nonnull()
; * Argument
; 1. In f1:bb6, %arg can be marked with nonnull because of the comparison in bb1
; 2. Because f2 is internal function, f2(i32* %arg) -> @f2(i32* nonnull %arg)
; 3. In f1:bb4 %tmp5 is nonnull and f3 is internal function.
; 3. In f1:bb4 %tmp5 is nonnull and f3 is internal function.
; Then, f3(i32* %arg) -> @f3(i32* nonnull %arg)
; 4. We get nonnull in whole f1 call sites so f1(i32* %arg) -> @f1(i32* nonnull %arg)
@ -208,21 +209,21 @@ bb9: ; preds = %bb4, %bb
}
define internal i32* @f2(i32* %arg) {
; FIXME: missing nonnull. It should be nonnull @f2(i32* nonnull %arg)
; FIXME: missing nonnull. It should be nonnull @f2(i32* nonnull %arg)
; ATTRIBUTOR: define internal nonnull i32* @f2(i32* readonly %arg)
bb:
; FIXME: missing nonnull. It should be @f1(i32* nonnull readonly %arg)
; FIXME: missing nonnull. It should be @f1(i32* nonnull readonly %arg)
; ATTRIBUTOR: %tmp = tail call nonnull i32* @f1(i32* readonly %arg)
%tmp = tail call i32* @f1(i32* %arg)
ret i32* %tmp
}
define dso_local noalias i32* @f3(i32* %arg) {
; FIXME: missing nonnull. It should be nonnull @f3(i32* nonnull readonly %arg)
; FIXME: missing nonnull. It should be nonnull @f3(i32* nonnull readonly %arg)
; ATTRIBUTOR: define dso_local noalias i32* @f3(i32* nocapture readonly %arg)
bb:
; FIXME: missing nonnull. It should be @f1(i32* nonnull readonly %arg)
; FIXME: missing nonnull. It should be @f1(i32* nonnull readonly %arg)
; ATTRIBUTOR: %tmp = call i32* @f1(i32* readonly %arg)
%tmp = call i32* @f1(i32* %arg)
ret i32* null
@ -266,8 +267,7 @@ if.else:
; fun1(nonnull %a)
; We can say that %a is nonnull
define void @f17(i8* %a, i8 %c) {
; FIXME: missing nonnull on %a
; ATTRIBUTOR: define void @f17(i8* %a, i8 %c)
; ATTRIBUTOR: define void @f17(i8* nonnull %a, i8 %c)
%cmp = icmp eq i8 %c, 0
br i1 %cmp, label %if.then, label %if.else
if.then:
@ -292,8 +292,7 @@ cont:
; fun1(nonnull %a)
define void @f18(i8* %a, i8* %b, i8 %c) {
; FIXME: missing nonnull on %a
; ATTRIBUTOR: define void @f18(i8* %a, i8* %b, i8 %c)
; ATTRIBUTOR: define void @f18(i8* nonnull %a, i8* %b, i8 %c)
%cmp1 = icmp eq i8 %c, 0
br i1 %cmp1, label %if.then, label %if.else
if.then:
@ -477,7 +476,7 @@ define i8 @parent7(i8* %a) {
declare i32 @esfp(...)
define i1 @parent8(i8* %a, i8* %bogus1, i8* %b) personality i8* bitcast (i32 (...)* @esfp to i8*){
; BOTH-LABEL: @parent8(i8* nonnull %a, i8* nocapture readnone %bogus1, i8* nonnull %b)
; BOTH-LABEL: @parent8(i8* nonnull %a, i8* nocapture readnone %bogus1, i8* nonnull %b)
; BOTH-NEXT: entry:
; FNATTR-NEXT: invoke void @use2nonnull(i8* %a, i8* %b)
; ATTRIBUTOR-NEXT: invoke void @use2nonnull(i8* nonnull %a, i8* nonnull %b)
@ -579,5 +578,216 @@ define void @make_live(i32* nonnull dereferenceable(8) %a) {
ret void
}
;int f(int *u, int n){
; for(int i = 0;i<n;i++){
; h(u);
; }
; return g(nonnull u);
;}
declare void @h(i32*) willreturn nounwind
declare i32 @g(i32*) willreturn nounwind
define i32 @nonnull_exec_ctx_1(i32* %a, i32 %b) {
; FNATTR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_1
; FNATTR-SAME: (i32* [[A:%.*]], i32 [[B:%.*]]) #3
; FNATTR-NEXT: en:
; FNATTR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
; FNATTR-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
; FNATTR: ex:
; FNATTR-NEXT: [[TMP5:%.*]] = tail call i32 @g(i32* nonnull [[A:%.*]])
; FNATTR-NEXT: ret i32 [[TMP5]]
; FNATTR: hd:
; FNATTR-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ]
; FNATTR-NEXT: tail call void @h(i32* [[A]])
; FNATTR-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1
; FNATTR-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
; FNATTR-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]]
;
; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_1
; ATTRIBUTOR-SAME: (i32* [[A:%.*]], i32 [[B:%.*]]) #5
; ATTRIBUTOR-NEXT: en:
; ATTRIBUTOR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
; ATTRIBUTOR-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
; ATTRIBUTOR: ex:
; ATTRIBUTOR-NEXT: [[TMP5:%.*]] = tail call i32 @g(i32* nonnull [[A:%.*]])
; ATTRIBUTOR-NEXT: ret i32 [[TMP5]]
; ATTRIBUTOR: hd:
; ATTRIBUTOR-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ]
; ATTRIBUTOR-NEXT: tail call void @h(i32* [[A]])
; ATTRIBUTOR-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1
; ATTRIBUTOR-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
; ATTRIBUTOR-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]]
;
en:
%tmp3 = icmp eq i32 %b, 0
br i1 %tmp3, label %ex, label %hd
ex:
%tmp5 = tail call i32 @g(i32* nonnull %a)
ret i32 %tmp5
hd:
%tmp7 = phi i32 [ %tmp8, %hd ], [ 0, %en ]
tail call void @h(i32* %a)
%tmp8 = add nuw i32 %tmp7, 1
%tmp9 = icmp eq i32 %tmp8, %b
br i1 %tmp9, label %ex, label %hd
}
define i32 @nonnull_exec_ctx_1b(i32* %a, i32 %b) {
; FNATTR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_1b
; FNATTR-SAME: (i32* [[A:%.*]], i32 [[B:%.*]]) #3
; FNATTR-NEXT: en:
; FNATTR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
; FNATTR-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
; FNATTR: ex:
; FNATTR-NEXT: [[TMP5:%.*]] = tail call i32 @g(i32* nonnull [[A:%.*]])
; FNATTR-NEXT: ret i32 [[TMP5]]
; FNATTR: hd:
; FNATTR-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ]
; FNATTR-NEXT: tail call void @h(i32* [[A]])
; FNATTR-NEXT: br label [[HD2]]
; FNATTR: hd2:
; FNATTR-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1
; FNATTR-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
; FNATTR-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]]
;
; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_1b
; ATTRIBUTOR-SAME: (i32* [[A:%.*]], i32 [[B:%.*]]) #5
; ATTRIBUTOR-NEXT: en:
; ATTRIBUTOR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
; ATTRIBUTOR-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
; ATTRIBUTOR: ex:
; ATTRIBUTOR-NEXT: [[TMP5:%.*]] = tail call i32 @g(i32* nonnull [[A:%.*]])
; ATTRIBUTOR-NEXT: ret i32 [[TMP5]]
; ATTRIBUTOR: hd:
; ATTRIBUTOR-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ]
; ATTRIBUTOR-NEXT: tail call void @h(i32* [[A]])
; ATTRIBUTOR-NEXT: br label [[HD2]]
; ATTRIBUTOR: hd2:
; ATTRIBUTOR-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1
; ATTRIBUTOR-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
; ATTRIBUTOR-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]]
;
en:
%tmp3 = icmp eq i32 %b, 0
br i1 %tmp3, label %ex, label %hd
ex:
%tmp5 = tail call i32 @g(i32* nonnull %a)
ret i32 %tmp5
hd:
%tmp7 = phi i32 [ %tmp8, %hd2 ], [ 0, %en ]
tail call void @h(i32* %a)
br label %hd2
hd2:
%tmp8 = add nuw i32 %tmp7, 1
%tmp9 = icmp eq i32 %tmp8, %b
br i1 %tmp9, label %ex, label %hd
}
define i32 @nonnull_exec_ctx_2(i32* %a, i32 %b) willreturn nounwind {
; FNATTR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_2
; FNATTR-SAME: (i32* [[A:%.*]], i32 [[B:%.*]]) #2
; FNATTR-NEXT: en:
; FNATTR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
; FNATTR-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
; FNATTR: ex:
; FNATTR-NEXT: [[TMP5:%.*]] = tail call i32 @g(i32* nonnull [[A:%.*]])
; FNATTR-NEXT: ret i32 [[TMP5]]
; FNATTR: hd:
; FNATTR-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ]
; FNATTR-NEXT: tail call void @h(i32* [[A]])
; FNATTR-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1
; FNATTR-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
; FNATTR-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]]
;
; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_2
; ATTRIBUTOR-SAME: (i32* [[A:%.*]], i32 [[B:%.*]]) #3
; ATTRIBUTOR-NEXT: en:
; ATTRIBUTOR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
; ATTRIBUTOR-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
; ATTRIBUTOR: ex:
; ATTRIBUTOR-NEXT: [[TMP5:%.*]] = tail call i32 @g(i32* nonnull [[A:%.*]])
; ATTRIBUTOR-NEXT: ret i32 [[TMP5]]
; ATTRIBUTOR: hd:
; ATTRIBUTOR-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD]] ], [ 0, [[EN:%.*]] ]
; ATTRIBUTOR-NEXT: tail call void @h(i32* nonnull [[A]])
; ATTRIBUTOR-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1
; ATTRIBUTOR-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
; ATTRIBUTOR-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]]
;
en:
%tmp3 = icmp eq i32 %b, 0
br i1 %tmp3, label %ex, label %hd
ex:
%tmp5 = tail call i32 @g(i32* nonnull %a)
ret i32 %tmp5
hd:
%tmp7 = phi i32 [ %tmp8, %hd ], [ 0, %en ]
tail call void @h(i32* %a)
%tmp8 = add nuw i32 %tmp7, 1
%tmp9 = icmp eq i32 %tmp8, %b
br i1 %tmp9, label %ex, label %hd
}
define i32 @nonnull_exec_ctx_2b(i32* %a, i32 %b) willreturn nounwind {
; FNATTR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_2b
; FNATTR-SAME: (i32* [[A:%.*]], i32 [[B:%.*]]) #2
; FNATTR-NEXT: en:
; FNATTR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
; FNATTR-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
; FNATTR: ex:
; FNATTR-NEXT: [[TMP5:%.*]] = tail call i32 @g(i32* nonnull [[A:%.*]])
; FNATTR-NEXT: ret i32 [[TMP5]]
; FNATTR: hd:
; FNATTR-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ]
; FNATTR-NEXT: tail call void @h(i32* [[A]])
; FNATTR-NEXT: br label [[HD2]]
; FNATTR: hd2:
; FNATTR-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1
; FNATTR-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
; FNATTR-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]]
;
; ATTRIBUTOR-LABEL: define {{[^@]+}}@nonnull_exec_ctx_2b
; ATTRIBUTOR-SAME: (i32* [[A:%.*]], i32 [[B:%.*]]) #3
; ATTRIBUTOR-NEXT: en:
; ATTRIBUTOR-NEXT: [[TMP3:%.*]] = icmp eq i32 [[B:%.*]], 0
; ATTRIBUTOR-NEXT: br i1 [[TMP3]], label [[EX:%.*]], label [[HD:%.*]]
; ATTRIBUTOR: ex:
; ATTRIBUTOR-NEXT: [[TMP5:%.*]] = tail call i32 @g(i32* nonnull [[A:%.*]])
; ATTRIBUTOR-NEXT: ret i32 [[TMP5]]
; ATTRIBUTOR: hd:
; ATTRIBUTOR-NEXT: [[TMP7:%.*]] = phi i32 [ [[TMP8:%.*]], [[HD2:%.*]] ], [ 0, [[EN:%.*]] ]
; ATTRIBUTOR-NEXT: tail call void @h(i32* nonnull [[A]])
; ATTRIBUTOR-NEXT: br label [[HD2]]
; ATTRIBUTOR: hd2:
; ATTRIBUTOR-NEXT: [[TMP8]] = add nuw i32 [[TMP7]], 1
; ATTRIBUTOR-NEXT: [[TMP9:%.*]] = icmp eq i32 [[TMP8]], [[B]]
; ATTRIBUTOR-NEXT: br i1 [[TMP9]], label [[EX]], label [[HD]]
;
en:
%tmp3 = icmp eq i32 %b, 0
br i1 %tmp3, label %ex, label %hd
ex:
%tmp5 = tail call i32 @g(i32* nonnull %a)
ret i32 %tmp5
hd:
%tmp7 = phi i32 [ %tmp8, %hd2 ], [ 0, %en ]
tail call void @h(i32* %a)
br label %hd2
hd2:
%tmp8 = add nuw i32 %tmp7, 1
%tmp9 = icmp eq i32 %tmp8, %b
br i1 %tmp9, label %ex, label %hd
}
attributes #0 = { "null-pointer-is-valid"="true" }
attributes #1 = { nounwind willreturn}