1
0
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:
George Burgess IV 2016-06-20 23:10:56 +00:00
parent 894b8d816e
commit 5e569e7990
12 changed files with 511 additions and 154 deletions

View File

@ -30,8 +30,7 @@ class TargetLibraryInfo;
class CFLAAResult : public AAResultBase<CFLAAResult> {
friend AAResultBase<CFLAAResult>;
struct FunctionInfo;
class FunctionInfo;
public:
explicit CFLAAResult(const TargetLibraryInfo &);

View File

@ -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;

View File

@ -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
}

View 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
}

View File

@ -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
}

View 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
}

View File

@ -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
}

View 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
}

View 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
}

View File

@ -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
}

View File

@ -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
}

View 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
}