Skip to content

Commit 4dcc47a

Browse files
committed
[clang][dataflow] Add parameterized map lattice.
This patchs adds a `MapLattice` template for lifting a lattice to a keyed map. A typical use is for modeling variables in a scope with a partcular lattice. Differential Revision: https://reviews.llvm.org/D116369
1 parent fd6d3e6 commit 4dcc47a

File tree

3 files changed

+297
-0
lines changed

3 files changed

+297
-0
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//===------------------------ MapLattice.h ----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file defines a parameterized lattice that maps keys to individual
10+
// lattice elements (of the parameter lattice type). A typical usage is lifting
11+
// a particular lattice to all variables in a lexical scope.
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE__MAPLATTICE_H
16+
#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE__MAPLATTICE_H
17+
18+
#include <ostream>
19+
#include <string>
20+
#include <utility>
21+
22+
#include "DataflowAnalysis.h"
23+
#include "clang/AST/Decl.h"
24+
#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
25+
#include "llvm/ADT/DenseMap.h"
26+
#include "llvm/ADT/StringRef.h"
27+
28+
namespace clang {
29+
namespace dataflow {
30+
31+
/// A lattice that maps keys to individual lattice elements. When instantiated
32+
/// with an `ElementLattice` that is a bounded semi-lattice, `MapLattice` is
33+
/// itself a bounded semi-lattice, so long as the user limits themselves to a
34+
/// finite number of keys. In that case, `top` is (implicitly), the map
35+
/// containing all valid keys mapped to `top` of `ElementLattice`.
36+
///
37+
/// Requirements on `ElementLattice`:
38+
/// * Provides standard declarations of a bounded semi-lattice.
39+
template <typename Key, typename ElementLattice> class MapLattice {
40+
using Container = llvm::DenseMap<Key, ElementLattice>;
41+
Container C;
42+
43+
public:
44+
using key_type = Key;
45+
using mapped_type = ElementLattice;
46+
using value_type = typename Container::value_type;
47+
using iterator = typename Container::iterator;
48+
using const_iterator = typename Container::const_iterator;
49+
50+
MapLattice() = default;
51+
52+
explicit MapLattice(Container C) { C = std::move(C); }
53+
54+
// The `bottom` element is the empty map.
55+
static MapLattice bottom() { return MapLattice(); }
56+
57+
void insert(const std::pair<const key_type, mapped_type> &P) { C.insert(P); }
58+
59+
void insert(std::pair<const key_type, mapped_type> &&P) {
60+
C.insert(std::move(P));
61+
}
62+
63+
unsigned size() const { return C.size(); }
64+
bool empty() const { return C.empty(); }
65+
66+
iterator begin() { return C.begin(); }
67+
iterator end() { return C.end(); }
68+
const_iterator begin() const { return C.begin(); }
69+
const_iterator end() const { return C.end(); }
70+
71+
// Equality is direct equality of underlying map entries. One implication of
72+
// this definition is that a map with (only) keys that map to bottom is not
73+
// equal to the empty map.
74+
friend bool operator==(const MapLattice &LHS, const MapLattice &RHS) {
75+
return LHS.C == RHS.C;
76+
}
77+
78+
friend bool operator!=(const MapLattice &LHS, const MapLattice &RHS) {
79+
return !(LHS == RHS);
80+
}
81+
82+
bool contains(const key_type &K) const { return C.find(K) != C.end(); }
83+
84+
iterator find(const key_type &K) { return C.find(K); }
85+
const_iterator find(const key_type &K) const { return C.find(K); }
86+
87+
mapped_type &operator[](const key_type &K) { return C[K]; }
88+
89+
/// If an entry exists in one map but not the other, the missing entry is
90+
/// treated as implicitly mapping to `bottom`. So, the joined map contains the
91+
/// entry as it was in the source map.
92+
LatticeJoinEffect join(const MapLattice &Other) {
93+
LatticeJoinEffect Effect = LatticeJoinEffect::Unchanged;
94+
for (const auto &O : Other.C) {
95+
auto It = C.find(O.first);
96+
if (It == C.end()) {
97+
C.insert(O);
98+
Effect = LatticeJoinEffect::Changed;
99+
} else if (It->second.join(O.second) == LatticeJoinEffect::Changed)
100+
Effect = LatticeJoinEffect::Changed;
101+
}
102+
return Effect;
103+
}
104+
};
105+
106+
/// Convenience alias that captures the common use of map lattices to model
107+
/// in-scope variables.
108+
template <typename ElementLattice>
109+
using VarMapLattice = MapLattice<const clang::VarDecl *, ElementLattice>;
110+
111+
template <typename Key, typename ElementLattice>
112+
std::ostream &
113+
operator<<(std::ostream &Os,
114+
const clang::dataflow::MapLattice<Key, ElementLattice> &M) {
115+
std::string Separator = "";
116+
Os << "{";
117+
for (const auto &E : M) {
118+
Os << std::exchange(Separator, ", ") << E.first << " => " << E.second;
119+
}
120+
Os << "}";
121+
return Os;
122+
}
123+
124+
template <typename ElementLattice>
125+
std::ostream &
126+
operator<<(std::ostream &Os,
127+
const clang::dataflow::VarMapLattice<ElementLattice> &M) {
128+
std::string Separator = "";
129+
Os << "{";
130+
for (const auto &E : M) {
131+
Os << std::exchange(Separator, ", ") << E.first->getName().str() << " => "
132+
<< E.second;
133+
}
134+
Os << "}";
135+
return Os;
136+
}
137+
} // namespace dataflow
138+
} // namespace clang
139+
140+
#endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE__MAPLATTICE_H

clang/unittests/Analysis/FlowSensitive/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
44
)
55

66
add_clang_unittest(ClangAnalysisFlowSensitiveTests
7+
MapLatticeTest.cpp
78
SingleVarConstantPropagationTest.cpp
89
TestingSupport.cpp
910
TestingSupportTest.cpp
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#include "clang/Analysis/FlowSensitive/MapLattice.h"
2+
#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
3+
#include "llvm/Support/Error.h"
4+
#include "gmock/gmock.h"
5+
#include "gtest/gtest.h"
6+
#include <ostream>
7+
8+
using namespace clang;
9+
using namespace dataflow;
10+
11+
namespace {
12+
// A simple lattice for basic tests.
13+
class BooleanLattice {
14+
public:
15+
BooleanLattice() : Value(false) {}
16+
explicit BooleanLattice(bool B) : Value(B) {}
17+
18+
static BooleanLattice bottom() { return BooleanLattice(false); }
19+
20+
static BooleanLattice top() { return BooleanLattice(true); }
21+
22+
LatticeJoinEffect join(BooleanLattice Other) {
23+
auto Prev = Value;
24+
Value = Value || Other.Value;
25+
return Prev == Value ? LatticeJoinEffect::Unchanged
26+
: LatticeJoinEffect::Changed;
27+
}
28+
29+
friend bool operator==(BooleanLattice LHS, BooleanLattice RHS) {
30+
return LHS.Value == RHS.Value;
31+
}
32+
33+
friend bool operator!=(BooleanLattice LHS, BooleanLattice RHS) {
34+
return LHS.Value != RHS.Value;
35+
}
36+
37+
friend std::ostream &operator<<(std::ostream &Os, const BooleanLattice &B) {
38+
Os << B.Value;
39+
return Os;
40+
}
41+
42+
bool value() const { return Value; }
43+
44+
private:
45+
bool Value;
46+
};
47+
} // namespace
48+
49+
static constexpr int Key1 = 0;
50+
static constexpr int Key2 = 1;
51+
52+
namespace {
53+
using ::testing::Pair;
54+
using ::testing::UnorderedElementsAre;
55+
56+
TEST(MapLatticeTest, InsertWorks) {
57+
MapLattice<int, BooleanLattice> Lattice;
58+
Lattice.insert({Key1, BooleanLattice(false)});
59+
Lattice.insert({Key2, BooleanLattice(false)});
60+
61+
EXPECT_THAT(Lattice, UnorderedElementsAre(Pair(Key1, BooleanLattice(false)),
62+
Pair(Key2, BooleanLattice(false))));
63+
}
64+
65+
TEST(MapLatticeTest, ComparisonWorks) {
66+
MapLattice<int, BooleanLattice> Lattice1;
67+
Lattice1.insert({Key1, BooleanLattice(true)});
68+
Lattice1.insert({Key2, BooleanLattice(false)});
69+
MapLattice<int, BooleanLattice> Lattice2 = Lattice1;
70+
EXPECT_EQ(Lattice1, Lattice2);
71+
72+
Lattice2.find(Key2)->second = BooleanLattice(true);
73+
EXPECT_NE(Lattice1, Lattice2);
74+
}
75+
76+
TEST(MapLatticeTest, JoinChange) {
77+
MapLattice<int, BooleanLattice> Lattice1;
78+
Lattice1.insert({Key1, BooleanLattice(false)});
79+
Lattice1.insert({Key2, BooleanLattice(false)});
80+
81+
MapLattice<int, BooleanLattice> Lattice2;
82+
Lattice2.insert({Key1, BooleanLattice(true)});
83+
Lattice2.insert({Key2, BooleanLattice(true)});
84+
85+
ASSERT_THAT(Lattice1,
86+
UnorderedElementsAre(Pair(Key1, BooleanLattice(false)),
87+
Pair(Key2, BooleanLattice(false))));
88+
89+
ASSERT_EQ(Lattice1.join(Lattice2), LatticeJoinEffect::Changed);
90+
EXPECT_THAT(Lattice1, UnorderedElementsAre(Pair(Key1, BooleanLattice(true)),
91+
Pair(Key2, BooleanLattice(true))));
92+
}
93+
94+
TEST(MapLatticeTest, JoinEqNoChange) {
95+
MapLattice<int, BooleanLattice> Lattice;
96+
Lattice.insert({Key1, BooleanLattice(false)});
97+
Lattice.insert({Key2, BooleanLattice(false)});
98+
99+
ASSERT_EQ(Lattice.join(Lattice), LatticeJoinEffect::Unchanged);
100+
EXPECT_THAT(Lattice, UnorderedElementsAre(Pair(Key1, BooleanLattice(false)),
101+
Pair(Key2, BooleanLattice(false))));
102+
}
103+
104+
TEST(MapLatticeTest, JoinLtNoChange) {
105+
MapLattice<int, BooleanLattice> Lattice1;
106+
Lattice1.insert({Key1, BooleanLattice(false)});
107+
Lattice1.insert({Key2, BooleanLattice(false)});
108+
109+
MapLattice<int, BooleanLattice> Lattice2;
110+
Lattice2.insert({Key1, BooleanLattice(true)});
111+
Lattice2.insert({Key2, BooleanLattice(true)});
112+
113+
ASSERT_THAT(Lattice1,
114+
UnorderedElementsAre(Pair(Key1, BooleanLattice(false)),
115+
Pair(Key2, BooleanLattice(false))));
116+
117+
ASSERT_THAT(Lattice2, UnorderedElementsAre(Pair(Key1, BooleanLattice(true)),
118+
Pair(Key2, BooleanLattice(true))));
119+
120+
ASSERT_EQ(Lattice2.join(Lattice1), LatticeJoinEffect::Unchanged);
121+
EXPECT_THAT(Lattice2, UnorderedElementsAre(Pair(Key1, BooleanLattice(true)),
122+
Pair(Key2, BooleanLattice(true))));
123+
}
124+
125+
TEST(MapLatticeTest, JoinDifferentDomainsProducesUnion) {
126+
MapLattice<int, BooleanLattice> Lattice1;
127+
Lattice1.insert({Key1, BooleanLattice(true)});
128+
MapLattice<int, BooleanLattice> Lattice2;
129+
Lattice2.insert({Key2, BooleanLattice(true)});
130+
131+
ASSERT_EQ(Lattice1.join(Lattice2), LatticeJoinEffect::Changed);
132+
EXPECT_THAT(Lattice1, UnorderedElementsAre(Pair(Key1, BooleanLattice(true)),
133+
Pair(Key2, BooleanLattice(true))));
134+
}
135+
136+
TEST(MapLatticeTest, FindWorks) {
137+
MapLattice<int, BooleanLattice> Lattice;
138+
Lattice.insert({Key1, BooleanLattice(true)});
139+
Lattice.insert({Key2, BooleanLattice(false)});
140+
141+
auto It = Lattice.find(Key1);
142+
ASSERT_NE(It, Lattice.end());
143+
EXPECT_EQ(It->second, BooleanLattice(true));
144+
145+
It = Lattice.find(Key2);
146+
ASSERT_NE(It, Lattice.end());
147+
EXPECT_EQ(It->second, BooleanLattice(false));
148+
}
149+
150+
TEST(MapLatticeTest, ContainsWorks) {
151+
MapLattice<int, BooleanLattice> Lattice;
152+
Lattice.insert({Key1, BooleanLattice(true)});
153+
EXPECT_TRUE(Lattice.contains(Key1));
154+
EXPECT_FALSE(Lattice.contains(Key2));
155+
}
156+
} // namespace

0 commit comments

Comments
 (0)