mirror of
https://github.com/RPCSX/llvm.git
synced 2024-11-27 21:50:40 +00:00
[CFLAA] Add some interproc. analysis to CFLAnders.
This patch adds function summary support to CFLAnders. It also comes with a lot of tests! Woohoo! Patch by Jia Chen. Differential Revision: https://reviews.llvm.org/D22450 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@276026 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
fa9076153b
commit
1caa063db8
@ -120,6 +120,19 @@ inline bool operator==(InterfaceValue LHS, InterfaceValue RHS) {
|
||||
inline bool operator!=(InterfaceValue LHS, InterfaceValue RHS) {
|
||||
return !(LHS == RHS);
|
||||
}
|
||||
inline bool operator<(InterfaceValue LHS, InterfaceValue RHS) {
|
||||
return LHS.Index < RHS.Index ||
|
||||
(LHS.Index == RHS.Index && LHS.DerefLevel < RHS.DerefLevel);
|
||||
}
|
||||
inline bool operator>(InterfaceValue LHS, InterfaceValue RHS) {
|
||||
return RHS < LHS;
|
||||
}
|
||||
inline bool operator<=(InterfaceValue LHS, InterfaceValue RHS) {
|
||||
return !(RHS < LHS);
|
||||
}
|
||||
inline bool operator>=(InterfaceValue LHS, InterfaceValue RHS) {
|
||||
return !(LHS < RHS);
|
||||
}
|
||||
|
||||
/// We use ExternalRelation to describe an externally visible aliasing relations
|
||||
/// between parameters/return value of a function.
|
||||
@ -127,6 +140,25 @@ struct ExternalRelation {
|
||||
InterfaceValue From, To;
|
||||
};
|
||||
|
||||
inline bool operator==(ExternalRelation LHS, ExternalRelation RHS) {
|
||||
return LHS.From == RHS.From && LHS.To == RHS.To;
|
||||
}
|
||||
inline bool operator!=(ExternalRelation LHS, ExternalRelation RHS) {
|
||||
return !(LHS == RHS);
|
||||
}
|
||||
inline bool operator<(ExternalRelation LHS, ExternalRelation RHS) {
|
||||
return LHS.From < RHS.From || (LHS.From == RHS.From && LHS.To < RHS.To);
|
||||
}
|
||||
inline bool operator>(ExternalRelation LHS, ExternalRelation RHS) {
|
||||
return RHS < LHS;
|
||||
}
|
||||
inline bool operator<=(ExternalRelation LHS, ExternalRelation RHS) {
|
||||
return !(RHS < LHS);
|
||||
}
|
||||
inline bool operator>=(ExternalRelation LHS, ExternalRelation RHS) {
|
||||
return !(LHS < RHS);
|
||||
}
|
||||
|
||||
/// We use ExternalAttribute to describe an externally visible AliasAttrs
|
||||
/// for parameters/return value.
|
||||
struct ExternalAttribute {
|
||||
|
@ -106,10 +106,17 @@ enum class MatchState : uint8_t {
|
||||
FlowToMemAliasReadWrite,
|
||||
};
|
||||
|
||||
typedef std::bitset<7> StateSet;
|
||||
LLVM_CONSTEXPR StateSet ReadOnlyStateMask =
|
||||
(1 << static_cast<uint8_t>(MatchState::FlowFromReadOnly)) |
|
||||
(1 << static_cast<uint8_t>(MatchState::FlowFromMemAliasReadOnly));
|
||||
LLVM_CONSTEXPR StateSet WriteOnlyStateMask =
|
||||
(1 << static_cast<uint8_t>(MatchState::FlowToWriteOnly)) |
|
||||
(1 << static_cast<uint8_t>(MatchState::FlowToMemAliasWriteOnly));
|
||||
|
||||
// We use ReachabilitySet to keep track of value aliases (The nonterminal "V" in
|
||||
// the paper) during the analysis.
|
||||
class ReachabilitySet {
|
||||
typedef std::bitset<7> StateSet;
|
||||
typedef DenseMap<InstantiatedValue, StateSet> ValueStateMap;
|
||||
typedef DenseMap<InstantiatedValue, ValueStateMap> ValueReachMap;
|
||||
ValueReachMap ReachMap;
|
||||
@ -120,6 +127,7 @@ public:
|
||||
|
||||
// Insert edge 'From->To' at state 'State'
|
||||
bool insert(InstantiatedValue From, InstantiatedValue To, MatchState State) {
|
||||
assert(From != To);
|
||||
auto &States = ReachMap[To][From];
|
||||
auto Idx = static_cast<size_t>(State);
|
||||
if (!States.test(Idx)) {
|
||||
@ -207,6 +215,14 @@ struct WorkListItem {
|
||||
InstantiatedValue To;
|
||||
MatchState State;
|
||||
};
|
||||
|
||||
struct ValueSummary {
|
||||
struct Record {
|
||||
InterfaceValue IValue;
|
||||
unsigned DerefLevel;
|
||||
};
|
||||
SmallVector<Record, 4> FromRecords, ToRecords;
|
||||
};
|
||||
}
|
||||
|
||||
class CFLAndersAAResult::FunctionInfo {
|
||||
@ -225,15 +241,39 @@ class CFLAndersAAResult::FunctionInfo {
|
||||
AliasAttrs getAttrs(const Value *) const;
|
||||
|
||||
public:
|
||||
FunctionInfo(const ReachabilitySet &, AliasAttrMap);
|
||||
FunctionInfo(const Function &, const SmallVectorImpl<Value *> &,
|
||||
const ReachabilitySet &, AliasAttrMap);
|
||||
|
||||
bool mayAlias(const Value *LHS, const Value *RHS) const;
|
||||
const AliasSummary &getAliasSummary() const { return Summary; }
|
||||
};
|
||||
|
||||
CFLAndersAAResult::FunctionInfo::FunctionInfo(const ReachabilitySet &ReachSet,
|
||||
AliasAttrMap AMap) {
|
||||
// Populate AttrMap
|
||||
static bool hasReadOnlyState(StateSet Set) {
|
||||
return (Set & ReadOnlyStateMask).any();
|
||||
}
|
||||
|
||||
static bool hasWriteOnlyState(StateSet Set) {
|
||||
return (Set & WriteOnlyStateMask).any();
|
||||
}
|
||||
|
||||
static Optional<InterfaceValue>
|
||||
getInterfaceValue(InstantiatedValue IValue,
|
||||
const SmallVectorImpl<Value *> &RetVals) {
|
||||
auto Val = IValue.Val;
|
||||
|
||||
Optional<unsigned> Index;
|
||||
if (auto Arg = dyn_cast<Argument>(Val))
|
||||
Index = Arg->getArgNo() + 1;
|
||||
else if (is_contained(RetVals, Val))
|
||||
Index = 0;
|
||||
|
||||
if (Index)
|
||||
return InterfaceValue{*Index, IValue.DerefLevel};
|
||||
return None;
|
||||
}
|
||||
|
||||
static void populateAttrMap(DenseMap<const Value *, AliasAttrs> &AttrMap,
|
||||
const AliasAttrMap &AMap) {
|
||||
for (const auto &Mapping : AMap.mappings()) {
|
||||
auto IVal = Mapping.first;
|
||||
|
||||
@ -241,8 +281,11 @@ CFLAndersAAResult::FunctionInfo::FunctionInfo(const ReachabilitySet &ReachSet,
|
||||
if (IVal.DerefLevel == 0)
|
||||
AttrMap[IVal.Val] = Mapping.second;
|
||||
}
|
||||
}
|
||||
|
||||
// Populate AliasMap
|
||||
static void
|
||||
populateAliasMap(DenseMap<const Value *, std::vector<const Value *>> &AliasMap,
|
||||
const ReachabilitySet &ReachSet) {
|
||||
for (const auto &OuterMapping : ReachSet.value_mappings()) {
|
||||
// AliasMap only cares about top-level values
|
||||
if (OuterMapping.first.DerefLevel > 0)
|
||||
@ -259,8 +302,112 @@ CFLAndersAAResult::FunctionInfo::FunctionInfo(const ReachabilitySet &ReachSet,
|
||||
// Sort AliasList for faster lookup
|
||||
std::sort(AliasList.begin(), AliasList.end(), std::less<const Value *>());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Populate function summary here
|
||||
static void populateExternalRelations(
|
||||
SmallVectorImpl<ExternalRelation> &ExtRelations, const Function &Fn,
|
||||
const SmallVectorImpl<Value *> &RetVals, const ReachabilitySet &ReachSet) {
|
||||
// If a function only returns one of its argument X, then X will be both an
|
||||
// argument and a return value at the same time. This is an edge case that
|
||||
// needs special handling here.
|
||||
for (const auto &Arg : Fn.args()) {
|
||||
if (is_contained(RetVals, &Arg)) {
|
||||
auto ArgVal = InterfaceValue{Arg.getArgNo() + 1, 0};
|
||||
auto RetVal = InterfaceValue{0, 0};
|
||||
ExtRelations.push_back(ExternalRelation{ArgVal, RetVal});
|
||||
}
|
||||
}
|
||||
|
||||
// Below is the core summary construction logic.
|
||||
// A naive solution of adding only the value aliases that are parameters or
|
||||
// return values in ReachSet to the summary won't work: It is possible that a
|
||||
// parameter P is written into an intermediate value I, and the function
|
||||
// subsequently returns *I. In that case, *I is does not value alias anything
|
||||
// in ReachSet, and the naive solution will miss a summary edge from (P, 1) to
|
||||
// (I, 1).
|
||||
// To account for the aforementioned case, we need to check each non-parameter
|
||||
// and non-return value for the possibility of acting as an intermediate.
|
||||
// 'ValueMap' here records, for each value, which InterfaceValues read from or
|
||||
// write into it. If both the read list and the write list of a given value
|
||||
// are non-empty, we know that a particular value is an intermidate and we
|
||||
// need to add summary edges from the writes to the reads.
|
||||
DenseMap<Value *, ValueSummary> ValueMap;
|
||||
for (const auto &OuterMapping : ReachSet.value_mappings()) {
|
||||
if (auto Dst = getInterfaceValue(OuterMapping.first, RetVals)) {
|
||||
for (const auto &InnerMapping : OuterMapping.second) {
|
||||
// If Src is a param/return value, we get a same-level assignment.
|
||||
if (auto Src = getInterfaceValue(InnerMapping.first, RetVals)) {
|
||||
// This may happen if both Dst and Src are return values
|
||||
if (*Dst == *Src)
|
||||
continue;
|
||||
|
||||
if (hasReadOnlyState(InnerMapping.second))
|
||||
ExtRelations.push_back(ExternalRelation{*Dst, *Src});
|
||||
// No need to check for WriteOnly state, since ReachSet is symmetric
|
||||
} else {
|
||||
// If Src is not a param/return, add it to ValueMap
|
||||
auto SrcIVal = InnerMapping.first;
|
||||
if (hasReadOnlyState(InnerMapping.second))
|
||||
ValueMap[SrcIVal.Val].FromRecords.push_back(
|
||||
ValueSummary::Record{*Dst, SrcIVal.DerefLevel});
|
||||
if (hasWriteOnlyState(InnerMapping.second))
|
||||
ValueMap[SrcIVal.Val].ToRecords.push_back(
|
||||
ValueSummary::Record{*Dst, SrcIVal.DerefLevel});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &Mapping : ValueMap) {
|
||||
for (const auto &FromRecord : Mapping.second.FromRecords) {
|
||||
for (const auto &ToRecord : Mapping.second.ToRecords) {
|
||||
auto ToLevel = ToRecord.DerefLevel;
|
||||
auto FromLevel = FromRecord.DerefLevel;
|
||||
// Same-level assignments should have already been processed by now
|
||||
if (ToLevel == FromLevel)
|
||||
continue;
|
||||
|
||||
auto SrcIndex = FromRecord.IValue.Index;
|
||||
auto SrcLevel = FromRecord.IValue.DerefLevel;
|
||||
auto DstIndex = ToRecord.IValue.Index;
|
||||
auto DstLevel = ToRecord.IValue.DerefLevel;
|
||||
if (ToLevel > FromLevel)
|
||||
SrcLevel += ToLevel - FromLevel;
|
||||
else
|
||||
DstLevel += FromLevel - ToLevel;
|
||||
|
||||
ExtRelations.push_back(
|
||||
ExternalRelation{InterfaceValue{SrcIndex, SrcLevel},
|
||||
InterfaceValue{DstIndex, DstLevel}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove duplicates in ExtRelations
|
||||
std::sort(ExtRelations.begin(), ExtRelations.end());
|
||||
ExtRelations.erase(std::unique(ExtRelations.begin(), ExtRelations.end()),
|
||||
ExtRelations.end());
|
||||
}
|
||||
|
||||
static void populateExternalAttributes(
|
||||
SmallVectorImpl<ExternalAttribute> &ExtAttributes, const Function &Fn,
|
||||
const SmallVectorImpl<Value *> &RetVals, const AliasAttrMap &AMap) {
|
||||
for (const auto &Mapping : AMap.mappings()) {
|
||||
if (auto IVal = getInterfaceValue(Mapping.first, RetVals)) {
|
||||
auto Attr = getExternallyVisibleAttrs(Mapping.second);
|
||||
if (Attr.any())
|
||||
ExtAttributes.push_back(ExternalAttribute{*IVal, Attr});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CFLAndersAAResult::FunctionInfo::FunctionInfo(
|
||||
const Function &Fn, const SmallVectorImpl<Value *> &RetVals,
|
||||
const ReachabilitySet &ReachSet, AliasAttrMap AMap) {
|
||||
populateAttrMap(AttrMap, AMap);
|
||||
populateExternalAttributes(Summary.RetParamAttributes, Fn, RetVals, AMap);
|
||||
populateAliasMap(AliasMap, ReachSet);
|
||||
populateExternalRelations(Summary.RetParamRelations, Fn, RetVals, ReachSet);
|
||||
}
|
||||
|
||||
AliasAttrs CFLAndersAAResult::FunctionInfo::getAttrs(const Value *V) const {
|
||||
@ -510,7 +657,8 @@ CFLAndersAAResult::buildInfoFrom(const Function &Fn) {
|
||||
// to it
|
||||
auto IValueAttrMap = buildAttrMap(Graph, ReachSet);
|
||||
|
||||
return FunctionInfo(ReachSet, std::move(IValueAttrMap));
|
||||
return FunctionInfo(Fn, GraphBuilder.getReturnValues(), ReachSet,
|
||||
std::move(IValueAttrMap));
|
||||
}
|
||||
|
||||
void CFLAndersAAResult::scan(const Function &Fn) {
|
||||
|
22
test/Analysis/CFLAliasAnalysis/Andersen/basic-interproc.ll
Normal file
22
test/Analysis/CFLAliasAnalysis/Andersen/basic-interproc.ll
Normal file
@ -0,0 +1,22 @@
|
||||
; 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-anders-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-anders-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
; CHECK-LABEL: Function: noop_callee
|
||||
; CHECK: MayAlias: i32* %arg1, i32* %arg2
|
||||
define void @noop_callee(i32* %arg1, i32* %arg2) {
|
||||
store i32 0, i32* %arg1
|
||||
store i32 0, i32* %arg2
|
||||
ret void
|
||||
}
|
||||
; 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 @noop_callee(i32* %a, i32* %b)
|
||||
|
||||
ret void
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
; This testcase ensures that CFL AA answers queries soundly when callee tries
|
||||
; to escape the memory pointed to by its parameters
|
||||
|
||||
; RUN: opt < %s -disable-basicaa -cfl-anders-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-anders-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
declare void @opaque(i32*)
|
||||
define void @escape_arg_deref(i32** %arg) {
|
||||
%arg_deref = load i32*, i32** %arg
|
||||
call void @opaque(i32* %arg_deref)
|
||||
ret void
|
||||
}
|
||||
; CHECK-LABEL: Function: test_arg_deref_escape
|
||||
; CHECK: NoAlias: i32* %a, i32** %x
|
||||
; CHECK: NoAlias: i32* %b, i32** %x
|
||||
; CHECK: NoAlias: i32* %a, i32* %b
|
||||
; CHECK: NoAlias: i32** %p, i32** %x
|
||||
; CHECK: NoAlias: i32* %a, i32** %p
|
||||
; CHECK: NoAlias: i32* %b, i32** %p
|
||||
; CHECK: MayAlias: i32* %a, i32* %c
|
||||
; CHECK: NoAlias: i32* %b, i32* %c
|
||||
; CHECK: NoAlias: i32* %c, i32** %p
|
||||
define void @test_arg_deref_escape(i32** %x) {
|
||||
%a = alloca i32, align 4
|
||||
%b = alloca i32, align 4
|
||||
%p = alloca i32*, align 4
|
||||
|
||||
store i32* %a, i32** %p
|
||||
call void @escape_arg_deref(i32** %p)
|
||||
%c = load i32*, i32** %x
|
||||
|
||||
ret void
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
; This testcase ensures that CFL AA answers queries soundly when callee tries
|
||||
; to escape its parameters
|
||||
|
||||
; RUN: opt < %s -disable-basicaa -cfl-anders-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-anders-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
declare void @opaque(i32*)
|
||||
define void @escape_arg(i32* %arg) {
|
||||
call void @opaque(i32* %arg)
|
||||
ret void
|
||||
}
|
||||
; CHECK-LABEL: Function: test_arg_escape
|
||||
; CHECK: NoAlias: i32* %a, i32** %x
|
||||
; CHECK: NoAlias: i32* %b, i32** %x
|
||||
; CHECK: NoAlias: i32* %a, i32* %b
|
||||
; CHECK: NoAlias: i32* %c, i32** %x
|
||||
; CHECK: NoAlias: i32* %a, i32* %c
|
||||
; CHECK: NoAlias: i32* %b, i32* %c
|
||||
; CHECK: MayAlias: i32* %a, i32* %d
|
||||
; CHECK: MayAlias: i32* %b, i32* %d
|
||||
; CHECK: NoAlias: i32* %c, i32* %d
|
||||
define void @test_arg_escape(i32** %x) {
|
||||
%a = alloca i32, align 4
|
||||
%b = alloca i32, align 4
|
||||
%c = alloca i32, align 4
|
||||
call void @escape_arg(i32* %a)
|
||||
call void @escape_arg(i32* %b)
|
||||
%d = load i32*, i32** %x
|
||||
|
||||
ret void
|
||||
}
|
21
test/Analysis/CFLAliasAnalysis/Andersen/interproc-ret-arg.ll
Normal file
21
test/Analysis/CFLAliasAnalysis/Andersen/interproc-ret-arg.ll
Normal file
@ -0,0 +1,21 @@
|
||||
; This testcase ensures that CFL AA answers queries soundly when callee tries
|
||||
; to return one of its parameters
|
||||
|
||||
; RUN: opt < %s -disable-basicaa -cfl-anders-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-anders-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
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,46 @@
|
||||
; 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-anders-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-anders-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
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: 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
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
; 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-anders-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-anders-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
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,33 @@
|
||||
; This testcase ensures that CFL AA answers queries soundly when callee tries
|
||||
; to return an escaped pointer
|
||||
|
||||
; RUN: opt < %s -disable-basicaa -cfl-anders-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-anders-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
declare noalias i8* @malloc(i64)
|
||||
declare void @opaque(i32*)
|
||||
|
||||
define i32* @return_escaped_callee() {
|
||||
%ptr = call noalias i8* @malloc(i64 8)
|
||||
%ptr_cast = bitcast i8* %ptr to i32*
|
||||
call void @opaque(i32* %ptr_cast)
|
||||
ret i32* %ptr_cast
|
||||
}
|
||||
; CHECK-LABEL: Function: test_return_escape
|
||||
; CHECK: NoAlias: i32* %a, i32** %x
|
||||
; CHECK: NoAlias: i32* %b, i32** %x
|
||||
; CHECK: NoAlias: i32* %a, i32* %b
|
||||
; CHECK: NoAlias: i32* %c, i32** %x
|
||||
; CHECK: NoAlias: i32* %a, i32* %c
|
||||
; CHECK: NoAlias: i32* %b, i32* %c
|
||||
; CHECK: NoAlias: i32* %a, i32* %d
|
||||
; CHECK: MayAlias: i32* %b, i32* %d
|
||||
; CHECK: MayAlias: i32* %c, i32* %d
|
||||
define void @test_return_escape(i32** %x) {
|
||||
%a = alloca i32, align 4
|
||||
%b = call i32* @return_escaped_callee()
|
||||
%c = call i32* @return_escaped_callee()
|
||||
%d = load i32*, i32** %x
|
||||
|
||||
ret void
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
; 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-anders-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-anders-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
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
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
; 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-anders-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-anders-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
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
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
; This testcase ensures that CFL AA answers queries soundly when callee tries
|
||||
; to return an unknown pointer
|
||||
|
||||
; RUN: opt < %s -disable-basicaa -cfl-anders-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-anders-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
@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
|
||||
}
|
||||
|
||||
@g2 = external global i32*
|
||||
define i32** @return_unknown_callee2() {
|
||||
ret i32** @g2
|
||||
}
|
||||
; CHECK-LABEL: Function: test_return_unknown2
|
||||
; CHECK: MayAlias: i32* %x, i32** %a
|
||||
; CHECK: MayAlias: i32* %b, i32* %x
|
||||
; CHECK: MayAlias: i32* %b, i32** %a
|
||||
define void @test_return_unknown2(i32* %x) {
|
||||
%a = call i32** @return_unknown_callee2()
|
||||
%b = load i32*, i32** %a
|
||||
|
||||
ret void
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
; 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-anders-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-anders-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
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* %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,32 @@
|
||||
; 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-anders-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-anders-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
@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: NoAlias: i32* %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
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
; 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-anders-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
; RUN: opt < %s -aa-pipeline=cfl-anders-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s
|
||||
|
||||
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: MayAlias: 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…
Reference in New Issue
Block a user