mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 20:51:52 +01:00
[CFLAA] Add interprocedural function summaries.
This patch adds function summaries, so that we don't need to recompute various properties about function parameters/return values at each callsite of a function. It also adds many interprocedural tests for CFLAA. Patch by Jia Chen. Differential Revision: http://reviews.llvm.org/D21475#inline-182390 llvm-svn: 273219
This commit is contained in:
parent
894b8d816e
commit
5e569e7990
@ -30,8 +30,7 @@ class TargetLibraryInfo;
|
||||
|
||||
class CFLAAResult : public AAResultBase<CFLAAResult> {
|
||||
friend AAResultBase<CFLAAResult>;
|
||||
|
||||
struct FunctionInfo;
|
||||
class FunctionInfo;
|
||||
|
||||
public:
|
||||
explicit CFLAAResult(const TargetLibraryInfo &);
|
||||
|
@ -64,14 +64,30 @@ CFLAAResult::CFLAAResult(CFLAAResult &&Arg)
|
||||
: AAResultBase(std::move(Arg)), TLI(Arg.TLI) {}
|
||||
CFLAAResult::~CFLAAResult() {}
|
||||
|
||||
/// Information we have about a function and would like to keep around.
|
||||
struct CFLAAResult::FunctionInfo {
|
||||
StratifiedSets<Value *> Sets;
|
||||
// Lots of functions have < 4 returns. Adjust as necessary.
|
||||
SmallVector<Value *, 4> ReturnedValues;
|
||||
/// We use ExternalRelation to describe an externally visible interaction
|
||||
/// between parameters/return value of a function.
|
||||
/// Both From and To are integer indices that represent a single parameter or
|
||||
/// return value. When the index is 0, they represent the return value. Non-zero
|
||||
/// index i represents the i-th parameter.
|
||||
struct ExternalRelation {
|
||||
unsigned From, To;
|
||||
};
|
||||
|
||||
FunctionInfo(StratifiedSets<Value *> &&S, SmallVector<Value *, 4> &&RV)
|
||||
: Sets(std::move(S)), ReturnedValues(std::move(RV)) {}
|
||||
/// Information we have about a function and would like to keep around.
|
||||
class CFLAAResult::FunctionInfo {
|
||||
StratifiedSets<Value *> Sets;
|
||||
|
||||
// RetParamRelations is a collection of ExternalRelations.
|
||||
SmallVector<ExternalRelation, 8> RetParamRelations;
|
||||
|
||||
public:
|
||||
FunctionInfo(Function &Fn, const SmallVectorImpl<Value *> &RetVals,
|
||||
StratifiedSets<Value *> S);
|
||||
|
||||
const StratifiedSets<Value *> &getStratifiedSets() const { return Sets; }
|
||||
const SmallVectorImpl<ExternalRelation> &getRetParamRelations() const {
|
||||
return RetParamRelations;
|
||||
}
|
||||
};
|
||||
|
||||
/// Try to go from a Value* to a Function*. Never returns nullptr.
|
||||
@ -101,6 +117,9 @@ LLVM_CONSTEXPR StratifiedAttr AttrEscaped = 1 << AttrEscapedIndex;
|
||||
LLVM_CONSTEXPR StratifiedAttr AttrUnknown = 1 << AttrUnknownIndex;
|
||||
LLVM_CONSTEXPR StratifiedAttr AttrGlobal = 1 << AttrGlobalIndex;
|
||||
|
||||
/// The maximum number of arguments we can put into a summary.
|
||||
LLVM_CONSTEXPR unsigned MaxSupportedArgsInSummary = 50;
|
||||
|
||||
/// StratifiedSets call for knowledge of "direction", so this is how we
|
||||
/// represent that locally.
|
||||
enum class Level { Same, Above, Below };
|
||||
@ -361,145 +380,43 @@ public:
|
||||
return Fn->isDeclaration() || !Fn->hasLocalLinkage();
|
||||
}
|
||||
|
||||
/// Gets whether the sets at Index1 above, below, or equal to the sets at
|
||||
/// Index2. Returns None if they are not in the same set chain.
|
||||
static Optional<Level> getIndexRelation(const StratifiedSets<Value *> &Sets,
|
||||
StratifiedIndex Index1,
|
||||
StratifiedIndex Index2) {
|
||||
if (Index1 == Index2)
|
||||
return Level::Same;
|
||||
|
||||
const auto *Current = &Sets.getLink(Index1);
|
||||
while (Current->hasBelow()) {
|
||||
if (Current->Below == Index2)
|
||||
return Level::Below;
|
||||
Current = &Sets.getLink(Current->Below);
|
||||
}
|
||||
|
||||
Current = &Sets.getLink(Index1);
|
||||
while (Current->hasAbove()) {
|
||||
if (Current->Above == Index2)
|
||||
return Level::Above;
|
||||
Current = &Sets.getLink(Current->Above);
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
// Encodes the notion of a "use"
|
||||
struct Edge {
|
||||
// Which value the edge is coming from
|
||||
Value *From;
|
||||
|
||||
// Which value the edge is pointing to
|
||||
Value *To;
|
||||
|
||||
// Edge weight
|
||||
EdgeType Weight;
|
||||
|
||||
// Whether we aliased any external values along the way that may be
|
||||
// invisible to the analysis
|
||||
StratifiedAttrs FromAttrs, ToAttrs;
|
||||
};
|
||||
|
||||
bool
|
||||
tryInterproceduralAnalysis(const SmallVectorImpl<Function *> &Fns,
|
||||
Value *FuncValue,
|
||||
const iterator_range<User::op_iterator> &Args) {
|
||||
const unsigned ExpectedMaxArgs = 8;
|
||||
const unsigned MaxSupportedArgs = 50;
|
||||
bool tryInterproceduralAnalysis(CallSite CS,
|
||||
const SmallVectorImpl<Function *> &Fns) {
|
||||
assert(Fns.size() > 0);
|
||||
|
||||
// This algorithm is n^2, so an arbitrary upper-bound of 50 args was
|
||||
// selected, so it doesn't take too long in insane cases.
|
||||
if (std::distance(Args.begin(), Args.end()) > (int)MaxSupportedArgs)
|
||||
if (CS.arg_size() > MaxSupportedArgsInSummary)
|
||||
return false;
|
||||
|
||||
// Exit early if we'll fail anyway
|
||||
for (auto *Fn : Fns) {
|
||||
if (isFunctionExternal(Fn) || Fn->isVarArg())
|
||||
return false;
|
||||
// Fail if the caller does not provide enough arguments
|
||||
assert(Fn->arg_size() <= CS.arg_size());
|
||||
auto &MaybeInfo = AA.ensureCached(Fn);
|
||||
if (!MaybeInfo.hasValue())
|
||||
return false;
|
||||
}
|
||||
|
||||
SmallVector<Edge, 8> Output;
|
||||
SmallVector<Value *, ExpectedMaxArgs> Arguments(Args.begin(), Args.end());
|
||||
SmallVector<StratifiedInfo, ExpectedMaxArgs> Parameters;
|
||||
for (auto *Fn : Fns) {
|
||||
auto &Info = *AA.ensureCached(Fn);
|
||||
auto &Sets = Info.Sets;
|
||||
auto &RetVals = Info.ReturnedValues;
|
||||
auto &FnInfo = AA.ensureCached(Fn);
|
||||
assert(FnInfo.hasValue());
|
||||
|
||||
Parameters.clear();
|
||||
for (auto &Param : Fn->args()) {
|
||||
auto MaybeInfo = Sets.find(&Param);
|
||||
// Did a new parameter somehow get added to the function/slip by?
|
||||
if (!MaybeInfo.hasValue())
|
||||
return false;
|
||||
Parameters.push_back(*MaybeInfo);
|
||||
auto &RetParamRelations = FnInfo->getRetParamRelations();
|
||||
for (auto &Relation : RetParamRelations) {
|
||||
auto FromIndex = Relation.From;
|
||||
auto ToIndex = Relation.To;
|
||||
auto FromVal = (FromIndex == 0) ? CS.getInstruction()
|
||||
: CS.getArgument(FromIndex - 1);
|
||||
auto ToVal =
|
||||
(ToIndex == 0) ? CS.getInstruction() : CS.getArgument(ToIndex - 1);
|
||||
if (FromVal->getType()->isPointerTy() &&
|
||||
ToVal->getType()->isPointerTy())
|
||||
// Actual arguments must be defined before they are used at callsite.
|
||||
// Therefore by the time we reach here, FromVal and ToVal should
|
||||
// already exist in the graph. We can go ahead and add them directly.
|
||||
Graph.addEdge(FromVal, ToVal, EdgeType::Assign);
|
||||
}
|
||||
|
||||
// Adding an edge from argument -> return value for each parameter that
|
||||
// may alias the return value
|
||||
for (unsigned I = 0, E = Parameters.size(); I != E; ++I) {
|
||||
auto &ParamInfo = Parameters[I];
|
||||
auto &ArgVal = Arguments[I];
|
||||
bool AddEdge = false;
|
||||
StratifiedAttrs RetAttrs, ParamAttrs;
|
||||
for (unsigned X = 0, XE = RetVals.size(); X != XE; ++X) {
|
||||
auto MaybeInfo = Sets.find(RetVals[X]);
|
||||
if (!MaybeInfo.hasValue())
|
||||
return false;
|
||||
|
||||
auto &RetInfo = *MaybeInfo;
|
||||
RetAttrs |= Sets.getLink(RetInfo.Index).Attrs;
|
||||
ParamAttrs |= Sets.getLink(ParamInfo.Index).Attrs;
|
||||
auto MaybeRelation =
|
||||
getIndexRelation(Sets, ParamInfo.Index, RetInfo.Index);
|
||||
if (MaybeRelation.hasValue())
|
||||
AddEdge = true;
|
||||
}
|
||||
if (AddEdge)
|
||||
Output.push_back(
|
||||
Edge{FuncValue, ArgVal, EdgeType::Assign, RetAttrs, ParamAttrs});
|
||||
}
|
||||
|
||||
if (Parameters.size() != Arguments.size())
|
||||
return false;
|
||||
|
||||
/// Adding edges between arguments for arguments that may end up aliasing
|
||||
/// each other. This is necessary for functions such as
|
||||
/// void foo(int** a, int** b) { *a = *b; }
|
||||
/// (Technically, the proper sets for this would be those below
|
||||
/// Arguments[I] and Arguments[X], but our algorithm will produce
|
||||
/// extremely similar, and equally correct, results either way)
|
||||
for (unsigned I = 0, E = Arguments.size(); I != E; ++I) {
|
||||
auto &MainVal = Arguments[I];
|
||||
auto &MainInfo = Parameters[I];
|
||||
auto &MainAttrs = Sets.getLink(MainInfo.Index).Attrs;
|
||||
for (unsigned X = I + 1; X != E; ++X) {
|
||||
auto &SubInfo = Parameters[X];
|
||||
auto &SubVal = Arguments[X];
|
||||
auto &SubAttrs = Sets.getLink(SubInfo.Index).Attrs;
|
||||
auto MaybeRelation =
|
||||
getIndexRelation(Sets, MainInfo.Index, SubInfo.Index);
|
||||
|
||||
if (!MaybeRelation.hasValue())
|
||||
continue;
|
||||
|
||||
Output.push_back(
|
||||
Edge{MainVal, SubVal, EdgeType::Assign, MainAttrs, SubAttrs});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Commit all edges in Output to CFLGraph
|
||||
for (const auto &Edge : Output) {
|
||||
addEdge(Edge.From, Edge.To, Edge.Weight);
|
||||
Graph.addAttr(Edge.From, Edge.FromAttrs);
|
||||
Graph.addAttr(Edge.To, Edge.ToAttrs);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -511,7 +428,7 @@ public:
|
||||
// Make sure all arguments and return value are added to the graph first
|
||||
for (Value *V : CS.args())
|
||||
addNode(V);
|
||||
if (!Inst->getType()->isVoidTy())
|
||||
if (Inst->getType()->isPointerTy())
|
||||
addNode(Inst);
|
||||
|
||||
// Check if Inst is a call to a library function that allocates/deallocates
|
||||
@ -526,7 +443,7 @@ public:
|
||||
// that we can tack on.
|
||||
SmallVector<Function *, 4> Targets;
|
||||
if (getPossibleTargets(CS, Targets))
|
||||
if (tryInterproceduralAnalysis(Targets, Inst, CS.args()))
|
||||
if (tryInterproceduralAnalysis(CS, Targets))
|
||||
return;
|
||||
|
||||
// Because the function is opaque, we need to note that anything
|
||||
@ -539,7 +456,7 @@ public:
|
||||
Escapes.insert(V);
|
||||
}
|
||||
|
||||
if (!Inst->getType()->isVoidTy()) {
|
||||
if (Inst->getType()->isPointerTy()) {
|
||||
auto *Fn = CS.getCalledFunction();
|
||||
if (Fn == nullptr || !Fn->doesNotAlias(0))
|
||||
Graph.addAttr(Inst, AttrUnknown);
|
||||
@ -643,8 +560,10 @@ class CFLGraphBuilder {
|
||||
void addInstructionToGraph(Instruction &Inst) {
|
||||
// We don't want the edges of most "return" instructions, but we *do* want
|
||||
// to know what can be returned.
|
||||
if (isa<ReturnInst>(&Inst))
|
||||
ReturnedValues.push_back(&Inst);
|
||||
if (auto RetInst = dyn_cast<ReturnInst>(&Inst))
|
||||
if (auto RetVal = RetInst->getReturnValue())
|
||||
if (RetVal->getType()->isPointerTy())
|
||||
ReturnedValues.push_back(RetVal);
|
||||
|
||||
if (!hasUsefulEdges(&Inst))
|
||||
return;
|
||||
@ -671,12 +590,16 @@ public:
|
||||
buildGraphFrom(Fn);
|
||||
}
|
||||
|
||||
const CFLGraph &getCFLGraph() { return Graph; }
|
||||
SmallVector<Value *, 4> takeReturnValues() {
|
||||
return std::move(ReturnedValues);
|
||||
const CFLGraph &getCFLGraph() const { return Graph; }
|
||||
const SmallVector<Value *, 4> &getReturnValues() const {
|
||||
return ReturnedValues;
|
||||
}
|
||||
const SmallPtrSet<Value *, 8> &getExternalValues() const {
|
||||
return ExternalValues;
|
||||
}
|
||||
const SmallPtrSet<Value *, 8> &getEscapedValues() const {
|
||||
return EscapedValues;
|
||||
}
|
||||
const SmallPtrSet<Value *, 8> &getExternalValues() { return ExternalValues; }
|
||||
const SmallPtrSet<Value *, 8> &getEscapedValues() { return EscapedValues; }
|
||||
};
|
||||
}
|
||||
|
||||
@ -788,6 +711,96 @@ static bool canSkipAddingToSets(Value *Val) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Gets whether the sets at Index1 above, below, or equal to the sets at
|
||||
/// Index2. Returns None if they are not in the same set chain.
|
||||
static Optional<Level> getIndexRelation(const StratifiedSets<Value *> &Sets,
|
||||
StratifiedIndex Index1,
|
||||
StratifiedIndex Index2) {
|
||||
if (Index1 == Index2)
|
||||
return Level::Same;
|
||||
|
||||
const auto *Current = &Sets.getLink(Index1);
|
||||
while (Current->hasBelow()) {
|
||||
if (Current->Below == Index2)
|
||||
return Level::Below;
|
||||
Current = &Sets.getLink(Current->Below);
|
||||
}
|
||||
|
||||
Current = &Sets.getLink(Index1);
|
||||
while (Current->hasAbove()) {
|
||||
if (Current->Above == Index2)
|
||||
return Level::Above;
|
||||
Current = &Sets.getLink(Current->Above);
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
CFLAAResult::FunctionInfo::FunctionInfo(Function &Fn,
|
||||
const SmallVectorImpl<Value *> &RetVals,
|
||||
StratifiedSets<Value *> S)
|
||||
: Sets(std::move(S)) {
|
||||
LLVM_CONSTEXPR unsigned ExpectedMaxArgs = 8;
|
||||
|
||||
// Collect StratifiedInfo for each parameter
|
||||
SmallVector<Optional<StratifiedInfo>, ExpectedMaxArgs> ParamInfos;
|
||||
for (auto &Param : Fn.args()) {
|
||||
if (Param.getType()->isPointerTy())
|
||||
ParamInfos.push_back(Sets.find(&Param));
|
||||
else
|
||||
ParamInfos.push_back(None);
|
||||
}
|
||||
// Collect StratifiedInfo for each return value
|
||||
SmallVector<Optional<StratifiedInfo>, 4> RetInfos;
|
||||
RetInfos.reserve(RetVals.size());
|
||||
for (unsigned I = 0, E = RetVals.size(); I != E; ++I)
|
||||
RetInfos.push_back(Sets.find(RetVals[I]));
|
||||
|
||||
// This summary generation algorithm is n^2. An arbitrary upper-bound of 50
|
||||
// args was selected, so it doesn't take too long in insane cases.
|
||||
if (Fn.arg_size() <= MaxSupportedArgsInSummary) {
|
||||
for (unsigned I = 0, E = ParamInfos.size(); I != E; ++I) {
|
||||
auto &MainInfo = ParamInfos[I];
|
||||
if (!MainInfo)
|
||||
continue;
|
||||
|
||||
// Adding edges between arguments for arguments that may end up aliasing
|
||||
// each other. This is necessary for functions such as
|
||||
// void foo(int** a, int** b) { *a = *b; }
|
||||
// (Technically, the proper sets for this would be those below
|
||||
// Arguments[I] and Arguments[X], but our algorithm will produce
|
||||
// extremely similar, and equally correct, results either way)
|
||||
for (unsigned X = I + 1; X != E; ++X) {
|
||||
auto &SubInfo = ParamInfos[X];
|
||||
if (!SubInfo)
|
||||
continue;
|
||||
|
||||
auto MaybeRelation =
|
||||
getIndexRelation(Sets, MainInfo->Index, SubInfo->Index);
|
||||
if (!MaybeRelation.hasValue())
|
||||
continue;
|
||||
|
||||
RetParamRelations.push_back(ExternalRelation{1 + I, 1 + X});
|
||||
}
|
||||
|
||||
// Adding an edge from argument -> return value for each parameter that
|
||||
// may alias the return value
|
||||
for (unsigned X = 0, XE = RetInfos.size(); X != XE; ++X) {
|
||||
auto &RetInfo = RetInfos[X];
|
||||
if (!RetInfo)
|
||||
continue;
|
||||
|
||||
auto MaybeRelation =
|
||||
getIndexRelation(Sets, MainInfo->Index, RetInfo->Index);
|
||||
if (!MaybeRelation.hasValue())
|
||||
continue;
|
||||
|
||||
RetParamRelations.push_back(ExternalRelation{1 + I, 0});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Builds the graph + StratifiedSets for a function.
|
||||
CFLAAResult::FunctionInfo CFLAAResult::buildSetsFrom(Function *Fn) {
|
||||
CFLGraphBuilder GraphBuilder(*this, TLI, *Fn);
|
||||
@ -848,7 +861,7 @@ CFLAAResult::FunctionInfo CFLAAResult::buildSetsFrom(Function *Fn) {
|
||||
SetBuilder.addAttributesBelow(Escape, AttrUnknown);
|
||||
}
|
||||
|
||||
return FunctionInfo(SetBuilder.build(), GraphBuilder.takeReturnValues());
|
||||
return FunctionInfo(*Fn, GraphBuilder.getReturnValues(), SetBuilder.build());
|
||||
}
|
||||
|
||||
void CFLAAResult::scan(Function *Fn) {
|
||||
@ -912,7 +925,7 @@ AliasResult CFLAAResult::query(const MemoryLocation &LocA,
|
||||
auto &MaybeInfo = ensureCached(Fn);
|
||||
assert(MaybeInfo.hasValue());
|
||||
|
||||
auto &Sets = MaybeInfo->Sets;
|
||||
auto &Sets = MaybeInfo->getStratifiedSets();
|
||||
auto MaybeA = Sets.find(ValA);
|
||||
if (!MaybeA.hasValue())
|
||||
return MayAlias;
|
||||
|
@ -1,22 +1,22 @@
|
||||
; This testcase ensures that CFL AA gives conservative answers on variables
|
||||
; that involve arguments.
|
||||
; This testcase ensures that CFL AA won't be too conservative when trying to do
|
||||
; interprocedural analysis on simple callee
|
||||
|
||||
; RUN: opt < %s -disable-basicaa -cfl-aa -aa-eval -print-may-aliases -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-aa -passes=aa-eval -print-may-aliases -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -disable-basicaa -cfl-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
; CHECK: Function: test2
|
||||
; CHECK-LABEL: Function: noop_callee
|
||||
; CHECK: MayAlias: i32* %arg1, i32* %arg2
|
||||
define void @test2(i32* %arg1, i32* %arg2) {
|
||||
define void @noop_callee(i32* %arg1, i32* %arg2) {
|
||||
store i32 0, i32* %arg1
|
||||
store i32 0, i32* %arg2
|
||||
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @test() {
|
||||
; CHECK-LABEL: Function: test_noop
|
||||
; CHECK: NoAlias: i32* %a, i32* %b
|
||||
define void @test_noop() {
|
||||
%a = alloca i32, align 4
|
||||
%b = alloca i32, align 4
|
||||
call void @test2(i32* %a, i32* %b)
|
||||
call void @noop_callee(i32* %a, i32* %b)
|
||||
|
||||
ret void
|
||||
}
|
||||
|
25
test/Analysis/CFLAliasAnalysis/interproc-ret-arg.ll
Normal file
25
test/Analysis/CFLAliasAnalysis/interproc-ret-arg.ll
Normal file
@ -0,0 +1,25 @@
|
||||
; This testcase ensures that CFL AA answers queries soundly when callee tries
|
||||
; to return one of its parameters
|
||||
|
||||
; RUN: opt < %s -disable-basicaa -cfl-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
; We have to xfail this since @return_arg_callee is treated as an opaque
|
||||
; function, and the anlysis couldn't prove that %b and %c are not aliases
|
||||
; XFAIL: *
|
||||
|
||||
define i32* @return_arg_callee(i32* %arg1, i32* %arg2) {
|
||||
ret i32* %arg1
|
||||
}
|
||||
; CHECK-LABEL: Function: test_return_arg
|
||||
; CHECK: NoAlias: i32* %a, i32* %b
|
||||
; CHECK: MayAlias: i32* %a, i32* %c
|
||||
; CHECK: NoAlias: i32* %b, i32* %c
|
||||
define void @test_return_arg() {
|
||||
%a = alloca i32, align 4
|
||||
%b = alloca i32, align 4
|
||||
|
||||
%c = call i32* @return_arg_callee(i32* %a, i32* %b)
|
||||
|
||||
ret void
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
; This testcase ensures that CFL AA answers queries soundly when callee tries
|
||||
; to return the multi-level dereference of one of its parameters
|
||||
|
||||
; RUN: opt < %s -disable-basicaa -cfl-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
; xfail for now due to buggy interproc analysis
|
||||
; XFAIL: *
|
||||
|
||||
define i32* @return_deref_arg_multilevel_callee(i32*** %arg1) {
|
||||
%deref = load i32**, i32*** %arg1
|
||||
%deref2 = load i32*, i32** %deref
|
||||
ret i32* %deref2
|
||||
}
|
||||
; CHECK-LABEL: Function: test_return_deref_arg_multilevel
|
||||
; CHECK: NoAlias: i32* %a, i32* %b
|
||||
; CHECK: MayAlias: i32* %a, i32* %c
|
||||
; CHECK: NoAlias: i32* %b, i32* %c
|
||||
; CHECK: NoAlias: i32* %c, i32** %p
|
||||
; CHECK: NoAlias: i32* %c, i32*** %pp
|
||||
; CHECK: MayAlias: i32** %lpp, i32** %p
|
||||
; CHECK: NoAlias: i32** %lpp, i32*** %pp
|
||||
; CHECK: NoAlias: i32* %c, i32** %lpp
|
||||
; CHECK: MayAlias: i32* %a, i32* %lpp_deref
|
||||
; CHECK: NoAlias: i32* %b, i32* %lpp_deref
|
||||
; CHECK: MayAlias: i32* %lpp_deref, i32** %p
|
||||
; CHECK: NoAlias: i32* %lpp_deref, i32*** %pp
|
||||
; CHECK: MayAlias: i32* %a, i32* %lp
|
||||
; CHECK: NoAlias: i32* %b, i32* %lp
|
||||
; CHECK: NoAlias: i32* %lp, i32** %p
|
||||
; CHECK: NoAlias: i32* %lp, i32*** %pp
|
||||
; CHECK: MayAlias: i32* %c, i32* %lp
|
||||
; CHECK: NoAlias: i32* %lp, i32** %lpp
|
||||
; CHECK: MayAlias: i32* %lp, i32* %lpp_deref
|
||||
define void @test_return_deref_arg_multilevel() {
|
||||
%a = alloca i32, align 4
|
||||
%b = alloca i32, align 4
|
||||
%p = alloca i32*, align 8
|
||||
%pp = alloca i32**, align 8
|
||||
|
||||
store i32* %a, i32** %p
|
||||
store i32** %p, i32*** %pp
|
||||
%c = call i32* @return_deref_arg_multilevel_callee(i32*** %pp)
|
||||
|
||||
%lpp = load i32**, i32*** %pp
|
||||
%lpp_deref = load i32*, i32** %lpp
|
||||
%lp = load i32*, i32** %p
|
||||
|
||||
ret void
|
||||
}
|
33
test/Analysis/CFLAliasAnalysis/interproc-ret-deref-arg.ll
Normal file
33
test/Analysis/CFLAliasAnalysis/interproc-ret-deref-arg.ll
Normal file
@ -0,0 +1,33 @@
|
||||
; This testcase ensures that CFL AA answers queries soundly when callee tries
|
||||
; to return the dereference of one of its parameters
|
||||
|
||||
; RUN: opt < %s -disable-basicaa -cfl-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
; xfail for now due to buggy interproc analysis
|
||||
; XFAIL: *
|
||||
|
||||
define i32* @return_deref_arg_callee(i32** %arg1) {
|
||||
%deref = load i32*, i32** %arg1
|
||||
ret i32* %deref
|
||||
}
|
||||
; CHECK-LABEL: Function: test_return_deref_arg
|
||||
; CHECK: NoAlias: i32* %a, i32* %b
|
||||
; CHECK: MayAlias: i32* %a, i32* %c
|
||||
; CHECK: NoAlias: i32* %b, i32* %c
|
||||
; CHECK: MayAlias: i32* %a, i32* %lp
|
||||
; CHECK: NoAlias: i32* %b, i32* %lp
|
||||
; CHECK: NoAlias: i32* %lp, i32** %p
|
||||
; CHECK: MayAlias: i32* %c, i32* %lp
|
||||
define void @test_return_deref_arg() {
|
||||
%a = alloca i32, align 4
|
||||
%b = alloca i32, align 4
|
||||
%p = alloca i32*, align 8
|
||||
|
||||
store i32* %a, i32** %p
|
||||
%c = call i32* @return_deref_arg_callee(i32** %p)
|
||||
|
||||
%lp = load i32*, i32** %p
|
||||
|
||||
ret void
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
; This testcase ensures that CFL AA answers queries soundly when callee tries
|
||||
; to return the multi-level reference of one of its parameters
|
||||
|
||||
; RUN: opt < %s -disable-basicaa -cfl-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
; xfail for now due to buggy interproc analysis
|
||||
; XFAIL: *
|
||||
|
||||
declare noalias i8* @malloc(i64)
|
||||
|
||||
define i32*** @return_ref_arg_multilevel_callee(i32* %arg1) {
|
||||
%ptr = call noalias i8* @malloc(i64 8)
|
||||
%ptr_cast = bitcast i8* %ptr to i32***
|
||||
%ptr2 = call noalias i8* @malloc(i64 8)
|
||||
%ptr_cast2 = bitcast i8* %ptr2 to i32**
|
||||
store i32* %arg1, i32** %ptr_cast2
|
||||
store i32** %ptr_cast2, i32*** %ptr_cast
|
||||
ret i32*** %ptr_cast
|
||||
}
|
||||
; CHECK-LABEL: Function: test_return_ref_arg_multilevel
|
||||
; CHECK: NoAlias: i32* %a, i32*** %b
|
||||
; CHECK: NoAlias: i32** %p, i32*** %b
|
||||
; CHECK: NoAlias: i32*** %b, i32*** %pp
|
||||
; CHECK: NoAlias: i32* %a, i32** %lb
|
||||
; CHECK: NoAlias: i32** %lb, i32** %p
|
||||
; CHECK: NoAlias: i32** %lb, i32*** %pp
|
||||
; CHECK: NoAlias: i32** %lb, i32*** %b
|
||||
; CHECK: MayAlias: i32* %a, i32* %lb_deref
|
||||
; CHECK: NoAlias: i32* %lb_deref, i32** %lpp
|
||||
; CHECK: MayAlias: i32* %lb_deref, i32* %lpp_deref
|
||||
; CHECK: NoAlias: i32* %lpp_deref, i32** %lpp
|
||||
; CHECK: MayAlias: i32* %lb_deref, i32* %lp
|
||||
; CHECK: NoAlias: i32* %lp, i32** %lpp
|
||||
; CHECK: MayAlias: i32* %lp, i32* %lpp_deref
|
||||
define void @test_return_ref_arg_multilevel() {
|
||||
%a = alloca i32, align 4
|
||||
%p = alloca i32*, align 8
|
||||
%pp = alloca i32**, align 8
|
||||
|
||||
store i32* %a, i32** %p
|
||||
store i32** %p, i32*** %pp
|
||||
%b = call i32*** @return_ref_arg_multilevel_callee(i32* %a)
|
||||
|
||||
%lb = load i32**, i32*** %b
|
||||
%lb_deref = load i32*, i32** %lb
|
||||
%lpp = load i32**, i32*** %pp
|
||||
%lpp_deref = load i32*, i32** %lpp
|
||||
%lp = load i32*, i32** %p
|
||||
|
||||
ret void
|
||||
}
|
37
test/Analysis/CFLAliasAnalysis/interproc-ret-ref-arg.ll
Normal file
37
test/Analysis/CFLAliasAnalysis/interproc-ret-ref-arg.ll
Normal file
@ -0,0 +1,37 @@
|
||||
; This testcase ensures that CFL AA answers queries soundly when callee tries
|
||||
; to return the reference of one of its parameters
|
||||
|
||||
; RUN: opt < %s -disable-basicaa -cfl-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
; xfail for now due to buggy interproc analysis
|
||||
; XFAIL: *
|
||||
|
||||
declare noalias i8* @malloc(i64)
|
||||
|
||||
define i32** @return_ref_arg_callee(i32* %arg1) {
|
||||
%ptr = call noalias i8* @malloc(i64 8)
|
||||
%ptr_cast = bitcast i8* %ptr to i32**
|
||||
store i32* %arg1, i32** %ptr_cast
|
||||
ret i32** %ptr_cast
|
||||
}
|
||||
; CHECK-LABEL: Function: test_return_ref_arg
|
||||
; CHECK: NoAlias: i32** %b, i32** %p
|
||||
; CHECK: MayAlias: i32* %a, i32* %lb
|
||||
; CHECK: NoAlias: i32* %lb, i32** %p
|
||||
; CHECK: NoAlias: i32* %lb, i32** %b
|
||||
; CHECK: NoAlias: i32* %lp, i32** %p
|
||||
; CHECK: NoAlias: i32* %lp, i32** %b
|
||||
; CHECK: MayAlias: i32* %lb, i32* %lp
|
||||
define void @test_return_ref_arg() {
|
||||
%a = alloca i32, align 4
|
||||
%p = alloca i32*, align 8
|
||||
|
||||
store i32* %a, i32** %p
|
||||
%b = call i32** @return_ref_arg_callee(i32* %a)
|
||||
|
||||
%lb = load i32*, i32** %b
|
||||
%lp = load i32*, i32** %p
|
||||
|
||||
ret void
|
||||
}
|
27
test/Analysis/CFLAliasAnalysis/interproc-ret-unknown.ll
Normal file
27
test/Analysis/CFLAliasAnalysis/interproc-ret-unknown.ll
Normal file
@ -0,0 +1,27 @@
|
||||
; This testcase ensures that CFL AA answers queries soundly when callee tries
|
||||
; to return an unknown pointer
|
||||
|
||||
; RUN: opt < %s -disable-basicaa -cfl-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
; xfail for now due to buggy interproc analysis
|
||||
; XFAIL: *
|
||||
|
||||
@g = external global i32
|
||||
|
||||
define i32* @return_unknown_callee(i32* %arg1, i32* %arg2) {
|
||||
ret i32* @g
|
||||
}
|
||||
; CHECK-LABEL: Function: test_return_unknown
|
||||
; CHECK: NoAlias: i32* %a, i32* %b
|
||||
; CHECK: MayAlias: i32* %c, i32* %x
|
||||
; CHECK: NoAlias: i32* %a, i32* %c
|
||||
; CHECK: NoAlias: i32* %b, i32* %c
|
||||
define void @test_return_unknown(i32* %x) {
|
||||
%a = alloca i32, align 4
|
||||
%b = alloca i32, align 4
|
||||
|
||||
%c = call i32* @return_unknown_callee(i32* %a, i32* %b)
|
||||
|
||||
ret void
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
; This testcase ensures that CFL AA answers queries soundly when callee tries
|
||||
; to mutate the memory pointed to by its parameters
|
||||
|
||||
; RUN: opt < %s -disable-basicaa -cfl-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
; xfail for now due to buggy interproc analysis
|
||||
; XFAIL: *
|
||||
|
||||
declare noalias i8* @malloc(i64)
|
||||
|
||||
define void @store_arg_multilevel_callee(i32*** %arg1, i32* %arg2) {
|
||||
%ptr = call noalias i8* @malloc(i64 8)
|
||||
%ptr_cast = bitcast i8* %ptr to i32**
|
||||
store i32* %arg2, i32** %ptr_cast
|
||||
store i32** %ptr_cast, i32*** %arg1
|
||||
ret void
|
||||
}
|
||||
; CHECK-LABEL: Function: test_store_arg_multilevel
|
||||
; CHECK: NoAlias: i32* %a, i32* %b
|
||||
; CHECK: NoAlias: i32* %a, i32** %lpp
|
||||
; CHECK: NoAlias: i32* %b, i32** %lpp
|
||||
; CHECK: MayAlias: i32** %lpp, i32** %p
|
||||
; CHECK: MayAlias: i32* %a, i32* %lpp_deref
|
||||
; CHECK: MayAlias: i32* %b, i32* %lpp_deref
|
||||
; CHECK: NoAlias: i32* %lpp_deref, i32** %p
|
||||
; CHECK: NoAlias: i32* %lpp_deref, i32*** %pp
|
||||
; CHECK: NoAlias: i32* %lpp_deref, i32** %lpp
|
||||
; CHECK: MayAlias: i32* %a, i32* %lp
|
||||
; CHECK: NoAlias: i32* %b, i32* %lp
|
||||
; CHECK: NoAlias: i32* %lp, i32*** %pp
|
||||
; CHECK: NoAlias: i32* %lp, i32** %lpp
|
||||
; CHECK: MayAlias: i32* %lp, i32* %lpp_deref
|
||||
define void @test_store_arg_multilevel() {
|
||||
%a = alloca i32, align 4
|
||||
%b = alloca i32, align 4
|
||||
%p = alloca i32*, align 8
|
||||
%pp = alloca i32**, align 8
|
||||
|
||||
store i32* %a, i32** %p
|
||||
store i32** %p, i32*** %pp
|
||||
call void @store_arg_multilevel_callee(i32*** %pp, i32* %b)
|
||||
|
||||
%lpp = load i32**, i32*** %pp
|
||||
%lpp_deref = load i32*, i32** %lpp
|
||||
%lp = load i32*, i32** %p
|
||||
|
||||
ret void
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
; This testcase ensures that CFL AA answers queries soundly when callee tries
|
||||
; to mutate the memory pointed to by its parameters
|
||||
|
||||
; RUN: opt < %s -disable-basicaa -cfl-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
; xfail for now due to buggy interproc analysis
|
||||
; XFAIL: *
|
||||
|
||||
@g = external global i32
|
||||
|
||||
define void @store_arg_unknown_callee(i32** %arg1) {
|
||||
store i32* @g, i32** %arg1
|
||||
ret void
|
||||
}
|
||||
; CHECK-LABEL: Function: test_store_arg_unknown
|
||||
; CHECK: NoAlias: i32* %x, i32** %p
|
||||
; CHECK: NoAlias: i32* %a, i32** %p
|
||||
; CHECK: NoAlias: i32* %b, i32** %p
|
||||
; CHECK: MayAlias: i32* %lp, i32* %x
|
||||
; CHECK: MayAlias: i32* %a, i32* %lp
|
||||
; CHECK: NoAlias: i32* %b, i32* %lp
|
||||
; CHECK: MayAlias: 32* %lp, i32** %p
|
||||
define void @test_store_arg_unknown(i32* %x) {
|
||||
%a = alloca i32, align 4
|
||||
%b = alloca i32, align 4
|
||||
%p = alloca i32*, align 8
|
||||
|
||||
store i32* %a, i32** %p
|
||||
call void @store_arg_unknown_callee(i32** %p)
|
||||
|
||||
%lp = load i32*, i32** %p
|
||||
|
||||
ret void
|
||||
}
|
37
test/Analysis/CFLAliasAnalysis/interproc-store-arg.ll
Normal file
37
test/Analysis/CFLAliasAnalysis/interproc-store-arg.ll
Normal file
@ -0,0 +1,37 @@
|
||||
; This testcase ensures that CFL AA answers queries soundly when callee tries
|
||||
; to mutate the memory pointed to by its parameters
|
||||
|
||||
; RUN: opt < %s -disable-basicaa -cfl-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
; xfail for now due to buggy interproc analysis
|
||||
; XFAIL: *
|
||||
|
||||
define void @store_arg_callee(i32** %arg1, i32* %arg2) {
|
||||
store i32* %arg2, i32** %arg1
|
||||
ret void
|
||||
}
|
||||
; CHECK-LABEL: Function: test_store_arg
|
||||
; CHECK: NoAlias: i32* %a, i32* %b
|
||||
; CHECK: NoAlias: i32* %a, i32** %p
|
||||
; CHECK: NoAlias: i32* %b, i32** %p
|
||||
; CHECK: MayAlias: i32* %a, i32* %lp
|
||||
; CHECK: MayAlias: i32* %b, i32* %lp
|
||||
; CHECK: NoAlias: i32* %a, i32* %lq
|
||||
; CHECK: MayAlias: i32* %b, i32* %lq
|
||||
; CHECK: NoAlias: i32* %lp, i32* %lq
|
||||
define void @test_store_arg() {
|
||||
%a = alloca i32, align 4
|
||||
%b = alloca i32, align 4
|
||||
%p = alloca i32*, align 8
|
||||
%q = alloca i32*, align 8
|
||||
|
||||
store i32* %a, i32** %p
|
||||
store i32* %b, i32** %q
|
||||
call void @store_arg_callee(i32** %p, i32* %b)
|
||||
|
||||
%lp = load i32*, i32** %p
|
||||
%lq = load i32*, i32** %q
|
||||
|
||||
ret void
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user