forked from OSchip/llvm-project
[CFLAA] Add attributes handling for CFLAnders.
This patch adds proper handling of stratified attributes into our anders-style CFLAA implementation. It also comes bundled with more CFLAnders tests. :) Patch by Jia Chen. Differential Revision: https://reviews.llvm.org/D22325 llvm-svn: 275604
This commit is contained in:
parent
62fba48e0f
commit
22682e293b
|
@ -141,6 +141,38 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// We use AliasAttrMap to keep track of the AliasAttr of each node.
|
||||||
|
class AliasAttrMap {
|
||||||
|
typedef DenseMap<InstantiatedValue, AliasAttrs> MapType;
|
||||||
|
MapType AttrMap;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef MapType::const_iterator const_iterator;
|
||||||
|
|
||||||
|
bool add(InstantiatedValue V, AliasAttrs Attr) {
|
||||||
|
if (Attr.none())
|
||||||
|
return false;
|
||||||
|
auto &OldAttr = AttrMap[V];
|
||||||
|
auto NewAttr = OldAttr | Attr;
|
||||||
|
if (OldAttr == NewAttr)
|
||||||
|
return false;
|
||||||
|
OldAttr = NewAttr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
AliasAttrs getAttrs(InstantiatedValue V) const {
|
||||||
|
AliasAttrs Attr;
|
||||||
|
auto Itr = AttrMap.find(V);
|
||||||
|
if (Itr != AttrMap.end())
|
||||||
|
Attr = Itr->second;
|
||||||
|
return Attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator_range<const_iterator> mappings() const {
|
||||||
|
return make_range<const_iterator>(AttrMap.begin(), AttrMap.end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct WorkListItem {
|
struct WorkListItem {
|
||||||
InstantiatedValue From;
|
InstantiatedValue From;
|
||||||
InstantiatedValue To;
|
InstantiatedValue To;
|
||||||
|
@ -155,17 +187,33 @@ class CFLAndersAAResult::FunctionInfo {
|
||||||
/// AliasMap[a] but not vice versa.
|
/// AliasMap[a] but not vice versa.
|
||||||
DenseMap<const Value *, std::vector<const Value *>> AliasMap;
|
DenseMap<const Value *, std::vector<const Value *>> AliasMap;
|
||||||
|
|
||||||
|
/// Map a value to its corresponding AliasAttrs
|
||||||
|
DenseMap<const Value *, AliasAttrs> AttrMap;
|
||||||
|
|
||||||
/// Summary of externally visible effects.
|
/// Summary of externally visible effects.
|
||||||
AliasSummary Summary;
|
AliasSummary Summary;
|
||||||
|
|
||||||
|
AliasAttrs getAttrs(const Value *) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FunctionInfo(const ReachabilitySet &);
|
FunctionInfo(const ReachabilitySet &, AliasAttrMap);
|
||||||
|
|
||||||
bool mayAlias(const Value *LHS, const Value *RHS) const;
|
bool mayAlias(const Value *LHS, const Value *RHS) const;
|
||||||
const AliasSummary &getAliasSummary() const { return Summary; }
|
const AliasSummary &getAliasSummary() const { return Summary; }
|
||||||
};
|
};
|
||||||
|
|
||||||
CFLAndersAAResult::FunctionInfo::FunctionInfo(const ReachabilitySet &ReachSet) {
|
CFLAndersAAResult::FunctionInfo::FunctionInfo(const ReachabilitySet &ReachSet,
|
||||||
|
AliasAttrMap AMap) {
|
||||||
|
// Populate AttrMap
|
||||||
|
for (const auto &Mapping : AMap.mappings()) {
|
||||||
|
auto IVal = Mapping.first;
|
||||||
|
|
||||||
|
// AttrMap only cares about top-level values
|
||||||
|
if (IVal.DerefLevel == 0)
|
||||||
|
AttrMap[IVal.Val] = Mapping.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate AliasMap
|
||||||
for (const auto &OuterMapping : ReachSet.value_mappings()) {
|
for (const auto &OuterMapping : ReachSet.value_mappings()) {
|
||||||
// AliasMap only cares about top-level values
|
// AliasMap only cares about top-level values
|
||||||
if (OuterMapping.first.DerefLevel > 0)
|
if (OuterMapping.first.DerefLevel > 0)
|
||||||
|
@ -186,18 +234,39 @@ CFLAndersAAResult::FunctionInfo::FunctionInfo(const ReachabilitySet &ReachSet) {
|
||||||
// TODO: Populate function summary here
|
// TODO: Populate function summary here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AliasAttrs CFLAndersAAResult::FunctionInfo::getAttrs(const Value *V) const {
|
||||||
|
assert(V != nullptr);
|
||||||
|
|
||||||
|
AliasAttrs Attr;
|
||||||
|
auto Itr = AttrMap.find(V);
|
||||||
|
if (Itr != AttrMap.end())
|
||||||
|
Attr = Itr->second;
|
||||||
|
return Attr;
|
||||||
|
}
|
||||||
|
|
||||||
bool CFLAndersAAResult::FunctionInfo::mayAlias(const Value *LHS,
|
bool CFLAndersAAResult::FunctionInfo::mayAlias(const Value *LHS,
|
||||||
const Value *RHS) const {
|
const Value *RHS) const {
|
||||||
assert(LHS && RHS);
|
assert(LHS && RHS);
|
||||||
|
|
||||||
auto Itr = AliasMap.find(LHS);
|
auto Itr = AliasMap.find(LHS);
|
||||||
if (Itr == AliasMap.end())
|
if (Itr != AliasMap.end()) {
|
||||||
|
if (std::binary_search(Itr->second.begin(), Itr->second.end(), RHS,
|
||||||
|
std::less<const Value *>()))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Even if LHS and RHS are not reachable, they may still alias due to their
|
||||||
|
// AliasAttrs
|
||||||
|
auto AttrsA = getAttrs(LHS);
|
||||||
|
auto AttrsB = getAttrs(RHS);
|
||||||
|
|
||||||
|
if (AttrsA.none() || AttrsB.none())
|
||||||
return false;
|
return false;
|
||||||
|
if (hasUnknownOrCallerAttr(AttrsA) || hasUnknownOrCallerAttr(AttrsB))
|
||||||
// TODO: Check AliasAttrs before drawing any conclusions
|
return true;
|
||||||
|
if (isGlobalOrArgAttr(AttrsA) && isGlobalOrArgAttr(AttrsB))
|
||||||
return std::binary_search(Itr->second.begin(), Itr->second.end(), RHS,
|
return true;
|
||||||
std::less<const Value *>());
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void propagate(InstantiatedValue From, InstantiatedValue To,
|
static void propagate(InstantiatedValue From, InstantiatedValue To,
|
||||||
|
@ -247,7 +316,6 @@ static void processWorkListItem(const WorkListItem &Item, const CFLGraph &Graph,
|
||||||
auto NodeInfo = Graph.getNode(ToNode);
|
auto NodeInfo = Graph.getNode(ToNode);
|
||||||
assert(NodeInfo != nullptr);
|
assert(NodeInfo != nullptr);
|
||||||
|
|
||||||
// TODO: propagate AliasAttr as well
|
|
||||||
// TODO: propagate field offsets
|
// TODO: propagate field offsets
|
||||||
|
|
||||||
// FIXME: Here is a neat trick we can do: since both ReachSet and MemSet holds
|
// FIXME: Here is a neat trick we can do: since both ReachSet and MemSet holds
|
||||||
|
@ -325,6 +393,52 @@ static void processWorkListItem(const WorkListItem &Item, const CFLGraph &Graph,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AliasAttrMap buildAttrMap(const CFLGraph &Graph,
|
||||||
|
const ReachabilitySet &ReachSet) {
|
||||||
|
AliasAttrMap AttrMap;
|
||||||
|
std::vector<InstantiatedValue> WorkList, NextList;
|
||||||
|
|
||||||
|
// Initialize each node with its original AliasAttrs in CFLGraph
|
||||||
|
for (const auto &Mapping : Graph.value_mappings()) {
|
||||||
|
auto Val = Mapping.first;
|
||||||
|
auto &ValueInfo = Mapping.second;
|
||||||
|
for (unsigned I = 0, E = ValueInfo.getNumLevels(); I < E; ++I) {
|
||||||
|
auto Node = InstantiatedValue{Val, I};
|
||||||
|
AttrMap.add(Node, ValueInfo.getNodeInfoAtLevel(I).Attr);
|
||||||
|
WorkList.push_back(Node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!WorkList.empty()) {
|
||||||
|
for (const auto &Dst : WorkList) {
|
||||||
|
auto DstAttr = AttrMap.getAttrs(Dst);
|
||||||
|
if (DstAttr.none())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Propagate attr on the same level
|
||||||
|
for (const auto &Mapping : ReachSet.reachableValueAliases(Dst)) {
|
||||||
|
auto Src = Mapping.first;
|
||||||
|
if (AttrMap.add(Src, DstAttr))
|
||||||
|
NextList.push_back(Src);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Propagate attr to the levels below
|
||||||
|
auto DstBelow = getNodeBelow(Graph, Dst);
|
||||||
|
while (DstBelow) {
|
||||||
|
if (AttrMap.add(*DstBelow, DstAttr)) {
|
||||||
|
NextList.push_back(*DstBelow);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DstBelow = getNodeBelow(Graph, *DstBelow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WorkList.swap(NextList);
|
||||||
|
NextList.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return AttrMap;
|
||||||
|
}
|
||||||
|
|
||||||
CFLAndersAAResult::FunctionInfo
|
CFLAndersAAResult::FunctionInfo
|
||||||
CFLAndersAAResult::buildInfoFrom(const Function &Fn) {
|
CFLAndersAAResult::buildInfoFrom(const Function &Fn) {
|
||||||
CFLGraphBuilder<CFLAndersAAResult> GraphBuilder(
|
CFLGraphBuilder<CFLAndersAAResult> GraphBuilder(
|
||||||
|
@ -347,7 +461,11 @@ CFLAndersAAResult::buildInfoFrom(const Function &Fn) {
|
||||||
NextList.clear();
|
NextList.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
return FunctionInfo(ReachSet);
|
// Now that we have all the reachability info, propagate AliasAttrs according
|
||||||
|
// to it
|
||||||
|
auto IValueAttrMap = buildAttrMap(Graph, ReachSet);
|
||||||
|
|
||||||
|
return FunctionInfo(ReachSet, std::move(IValueAttrMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFLAndersAAResult::scan(const Function &Fn) {
|
void CFLAndersAAResult::scan(const Function &Fn) {
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
; This testcase ensures that AliasAttrs are propagated not only on the same
|
||||||
|
; level but also downward.
|
||||||
|
|
||||||
|
; 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: test_attr_below
|
||||||
|
; CHECK: MayAlias: i64* %q, i64*** %p
|
||||||
|
; CHECK: NoAlias: i64* %esc, i64*** %p
|
||||||
|
; CHECK: NoAlias: i64* %esc, i64* %q
|
||||||
|
|
||||||
|
; CHECK: MayAlias: i64* %unknown, i64*** %p
|
||||||
|
; CHECK: MayAlias: i64* %q, i64* %unknown
|
||||||
|
; CHECK: MayAlias: i64* %esc, i64* %unknown
|
||||||
|
; CHECK: MayAlias: i64* %q, i64** %pdrf
|
||||||
|
; CHECK: MayAlias: i64* %esc, i64** %pdrf
|
||||||
|
; CHECK: MayAlias: i64* %unknown, i64** %pdrf
|
||||||
|
; CHECK: MayAlias: i64* %pdrf2, i64* %q
|
||||||
|
; CHECK: MayAlias: i64* %esc, i64* %pdrf2
|
||||||
|
; CHECK: MayAlias: i64* %pdrf2, i64* %unknown
|
||||||
|
define void @test_attr_below(i64*** %p, i64* %q) {
|
||||||
|
%esc = alloca i64, align 8
|
||||||
|
%escint = ptrtoint i64* %esc to i64
|
||||||
|
%unknown = inttoptr i64 %escint to i64*
|
||||||
|
|
||||||
|
%pdrf = load i64**, i64*** %p
|
||||||
|
%pdrf2 = load i64*, i64** %pdrf
|
||||||
|
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: Function: test_attr_assign_below
|
||||||
|
; CHECK: MayAlias: i64** %sel, i64*** %p
|
||||||
|
; CHECK: MayAlias: i64* %q, i64** %sel
|
||||||
|
; CHECK: MayAlias: i64** %a, i64** %sel
|
||||||
|
; CHECK: MayAlias: i64** %pdrf, i64** %sel
|
||||||
|
|
||||||
|
; CHECK: MayAlias: i64** %c, i64*** %p
|
||||||
|
; CHECK: MayAlias: i64* %q, i64** %c
|
||||||
|
; CHECK: MayAlias: i64** %a, i64** %c
|
||||||
|
; CHECK: MayAlias: i64** %c, i64** %pdrf
|
||||||
|
; CHECK: MayAlias: i64** %c, i64** %sel
|
||||||
|
|
||||||
|
; CHECK: MayAlias: i64* %d, i64*** %p
|
||||||
|
; CHECK: MayAlias: i64* %d, i64* %q
|
||||||
|
; CHECK: MayAlias: i64* %d, i64** %pdrf
|
||||||
|
; CHECK: MayAlias: i64* %d, i64** %sel
|
||||||
|
define void @test_attr_assign_below(i64*** %p, i64* %q, i1 %cond) {
|
||||||
|
%a = alloca i64*, align 8
|
||||||
|
%pdrf = load i64**, i64*** %p
|
||||||
|
%sel = select i1 %cond, i64** %a, i64** %pdrf
|
||||||
|
|
||||||
|
%b = alloca i64**, align 8
|
||||||
|
store i64** %sel, i64*** %b
|
||||||
|
|
||||||
|
%c = load i64**, i64*** %b
|
||||||
|
%d = load i64*, i64** %c
|
||||||
|
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
; This testcase ensures that CFL AA handles escaped values no more conservative than it should
|
||||||
|
|
||||||
|
; 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: test_local
|
||||||
|
; CHECK: NoAlias: i32* %a, i32* %b
|
||||||
|
; CHECK: MayAlias: i32* %a, i32* %aAlias
|
||||||
|
; CHECK: NoAlias: i32* %aAlias, i32* %b
|
||||||
|
define void @test_local() {
|
||||||
|
%a = alloca i32, align 4
|
||||||
|
%b = alloca i32, align 4
|
||||||
|
%aint = ptrtoint i32* %a to i64
|
||||||
|
%aAlias = inttoptr i64 %aint to i32*
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: Function: test_global_param
|
||||||
|
; CHECK: NoAlias: i32* %a, i32** %x
|
||||||
|
; CHECK: MayAlias: i32* %a, i32* %xload
|
||||||
|
; CHECK: MayAlias: i32* %a, i32* %gload
|
||||||
|
; CHECK: MayAlias: i32* %gload, i32* %xload
|
||||||
|
; CHECK: MayAlias: i32** %x, i32** @ext_global
|
||||||
|
; CHECK: NoAlias: i32* %a, i32** @ext_global
|
||||||
|
@ext_global = external global i32*
|
||||||
|
define void @test_global_param(i32** %x) {
|
||||||
|
%a = alloca i32, align 4
|
||||||
|
%aint = ptrtoint i32* %a to i64
|
||||||
|
%xload = load i32*, i32** %x
|
||||||
|
%gload = load i32*, i32** @ext_global
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @external_func(i32**)
|
||||||
|
; CHECK-LABEL: Function: test_external_call
|
||||||
|
; CHECK: NoAlias: i32* %b, i32* %x
|
||||||
|
; CHECK: NoAlias: i32* %b, i32** %a
|
||||||
|
; CHECK: MayAlias: i32* %c, i32* %x
|
||||||
|
; CHECK: MayAlias: i32* %c, i32** %a
|
||||||
|
; CHECK: NoAlias: i32* %b, i32* %c
|
||||||
|
define void @test_external_call(i32* %x) {
|
||||||
|
%a = alloca i32*, align 8
|
||||||
|
%b = alloca i32, align 4
|
||||||
|
call void @external_func(i32** %a)
|
||||||
|
%c = load i32*, i32** %a
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @external_func_readonly(i32**) readonly
|
||||||
|
; CHECK-LABEL: Function: test_external_call_func_readonly
|
||||||
|
; CHECK: MayAlias: i32* %c, i32* %x
|
||||||
|
; CHECK: NoAlias: i32* %c, i32** %a
|
||||||
|
define void @test_external_call_func_readonly(i32* %x) {
|
||||||
|
%a = alloca i32*, align 8
|
||||||
|
%b = alloca i32, align 4
|
||||||
|
store i32* %x, i32** %a, align 4
|
||||||
|
call void @external_func_readonly(i32** %a)
|
||||||
|
%c = load i32*, i32** %a
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: Function: test_external_call_callsite_readonly
|
||||||
|
; CHECK: MayAlias: i32* %c, i32* %x
|
||||||
|
; CHECK: NoAlias: i32* %c, i32** %a
|
||||||
|
define void @test_external_call_callsite_readonly(i32* %x) {
|
||||||
|
%a = alloca i32*, align 8
|
||||||
|
%b = alloca i32, align 4
|
||||||
|
store i32* %x, i32** %a, align 4
|
||||||
|
call void @external_func(i32** %a) readonly
|
||||||
|
%c = load i32*, i32** %a
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i32* @external_func_normal_return(i32*)
|
||||||
|
; CHECK-LABEL: Function: test_external_call_normal_return
|
||||||
|
; CHECK: MayAlias: i32* %c, i32* %x
|
||||||
|
; CHECK: MayAlias: i32* %a, i32* %c
|
||||||
|
define void @test_external_call_normal_return(i32* %x) {
|
||||||
|
%a = alloca i32, align 8
|
||||||
|
%b = alloca i32, align 4
|
||||||
|
%c = call i32* @external_func_normal_return(i32* %a)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare noalias i32* @external_func_noalias_return(i32*)
|
||||||
|
; CHECK-LABEL: Function: test_external_call_noalias_return
|
||||||
|
; CHECK: NoAlias: i32* %c, i32* %x
|
||||||
|
; CHECK: NoAlias: i32* %a, i32* %c
|
||||||
|
define void @test_external_call_noalias_return(i32* %x) {
|
||||||
|
%a = alloca i32, align 8
|
||||||
|
%b = alloca i32, align 4
|
||||||
|
%c = call i32* @external_func_noalias_return(i32* %a)
|
||||||
|
ret void
|
||||||
|
}
|
Loading…
Reference in New Issue