Skip to content

Commit fd6e410

Browse files
committed
[analysis] Add a tuple lattice
This lattice combines any number of other lattices into a single lattice whose elements are tuples of elements of the other lattices. This will be one of the most important lattices in the analysis framework because it will be used to combine information about different parts of the program, e.g. locals and the value stack, into a single lattice.
1 parent a34cf69 commit fd6e410

File tree

3 files changed

+313
-6
lines changed

3 files changed

+313
-6
lines changed

src/analysis/lattices/tuple.h

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* Copyright 2023 WebAssembly Community Group participants
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef wasm_analysis_lattices_tuple_h
18+
#define wasm_analysis_lattices_tuple_h
19+
20+
#include <tuple>
21+
#include <utility>
22+
23+
#include "bool.h"
24+
#include "support/utilities.h"
25+
26+
namespace wasm::analysis {
27+
28+
template<Lattice... Ls> struct Tuple {
29+
using Element = std::tuple<typename Ls::Element...>;
30+
31+
std::tuple<Ls...> lattices;
32+
33+
Tuple(Ls&&... lattices) : lattices({std::move(lattices)...}) {}
34+
35+
private:
36+
template<size_t... I>
37+
Element getBottomImpl(std::index_sequence<I...>) const noexcept {
38+
return {std::get<I>(lattices).getBottom()...};
39+
}
40+
41+
template<size_t... I>
42+
Element getTopImpl(std::index_sequence<I...>) const noexcept {
43+
return {std::get<I>(lattices).getTop()...};
44+
}
45+
46+
LatticeComparison compareImpl(const Element& a,
47+
const Element& b,
48+
LatticeComparison result,
49+
std::index_sequence<>) const noexcept {
50+
// Base case: there is nothing left to compare.
51+
return result;
52+
}
53+
54+
template<size_t I, size_t... Is>
55+
LatticeComparison compareImpl(const Element& a,
56+
const Element& b,
57+
LatticeComparison result,
58+
std::index_sequence<I, Is...>) const noexcept {
59+
// Recursive case: compare the current elements, update `result`, and
60+
// recurse to the next elements if necessary.
61+
switch (std::get<I>(lattices).compare(std::get<I>(a), std::get<I>(b))) {
62+
case EQUAL:
63+
return compareImpl(a, b, result, std::index_sequence<Is...>{});
64+
case LESS:
65+
if (result == GREATER) {
66+
// Cannot be both less and greater.
67+
return NO_RELATION;
68+
}
69+
return compareImpl(a, b, LESS, std::index_sequence<Is...>{});
70+
case GREATER:
71+
if (result == LESS) {
72+
// Cannot be both greater and less.
73+
return NO_RELATION;
74+
}
75+
return compareImpl(a, b, GREATER, std::index_sequence<Is...>{});
76+
case NO_RELATION:
77+
return NO_RELATION;
78+
}
79+
WASM_UNREACHABLE("unexpected comparison");
80+
}
81+
82+
bool joinImpl(Element& joinee,
83+
const Element& joiner,
84+
std::index_sequence<>) const noexcept {
85+
// Base case: there is nothing left to join.
86+
return false;
87+
}
88+
89+
template<size_t I, size_t... Is>
90+
bool joinImpl(Element& joinee,
91+
const Element& joiner,
92+
std::index_sequence<I, Is...>) const noexcept {
93+
// Recursive case: join the current element and recurse to the next
94+
// elements.
95+
return std::get<I>(lattices).join(std::get<I>(joinee),
96+
std::get<I>(joiner)) |
97+
joinImpl(joinee, joiner, std::index_sequence<Is...>{});
98+
}
99+
100+
bool meetImpl(Element& meetee,
101+
const Element& meeter,
102+
std::index_sequence<>) const noexcept {
103+
// Base case: there is nothing left to mee.
104+
return false;
105+
}
106+
107+
template<size_t I, size_t... Is>
108+
bool meetImpl(Element& meetee,
109+
const Element& meeter,
110+
std::index_sequence<I, Is...>) const noexcept {
111+
// Recursive case: meet the current element and recurse to the next
112+
// elements.
113+
return std::get<I>(lattices).meet(std::get<I>(meetee),
114+
std::get<I>(meeter)) |
115+
meetImpl(meetee, meeter, std::index_sequence<Is...>{});
116+
}
117+
118+
public:
119+
Element getBottom() const noexcept {
120+
return getBottomImpl(std::index_sequence_for<Ls...>());
121+
}
122+
123+
Element getTop() const noexcept {
124+
return getTopImpl(std::index_sequence_for<Ls...>());
125+
}
126+
127+
LatticeComparison compare(const Element& a, const Element& b) const noexcept {
128+
return compareImpl(a, b, EQUAL, std::index_sequence_for<Ls...>());
129+
}
130+
131+
bool join(Element& joinee, const Element& joiner) const noexcept {
132+
return joinImpl(joinee, joiner, std::index_sequence_for<Ls...>());
133+
}
134+
135+
bool meet(Element& meetee, const Element& meeter) const noexcept {
136+
return meetImpl(meetee, meeter, std::index_sequence_for<Ls...>());
137+
}
138+
};
139+
140+
#if __cplusplus >= 202002L
141+
static_assert(FullLattice<Tuple<>>);
142+
static_assert(FullLattice<Tuple<Bool>>);
143+
#endif
144+
145+
} // namespace wasm::analysis
146+
147+
#endif // wasm_analysis_lattices_tuple_h

src/tools/wasm-fuzz-lattices.cpp

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "analysis/lattices/lift.h"
3030
#include "analysis/lattices/powerset2.h"
3131
#include "analysis/lattices/stack.h"
32+
#include "analysis/lattices/tuple.h"
3233
#include "analysis/lattices/vector.h"
3334
#include "analysis/liveness-transfer-function.h"
3435
#include "analysis/reaching-definitions-transfer-function.h"
@@ -154,37 +155,44 @@ static_assert(Lattice<RandomLattice>);
154155
using ArrayFullLattice = analysis::Array<RandomFullLattice, 2>;
155156
using ArrayLattice = analysis::Array<RandomLattice, 2>;
156157

158+
using TupleFullLattice = analysis::Tuple<RandomFullLattice, RandomFullLattice>;
159+
using TupleLattice = analysis::Tuple<RandomLattice, RandomLattice>;
160+
157161
struct RandomFullLattice::L : std::variant<Bool,
158162
UInt32,
159163
FinitePowerset2<BitSet>,
160164
Inverted<RandomFullLattice>,
161165
ArrayFullLattice,
162-
Vector<RandomFullLattice>> {};
166+
Vector<RandomFullLattice>,
167+
TupleFullLattice> {};
163168

164169
struct RandomFullLattice::ElementImpl
165170
: std::variant<typename Bool::Element,
166171
typename UInt32::Element,
167172
typename FinitePowerset2<BitSet>::Element,
168173
typename Inverted<RandomFullLattice>::Element,
169174
typename ArrayFullLattice::Element,
170-
typename Vector<RandomFullLattice>::Element> {};
175+
typename Vector<RandomFullLattice>::Element,
176+
typename TupleFullLattice::Element> {};
171177

172178
struct RandomLattice::L : std::variant<RandomFullLattice,
173179
Flat<uint32_t>,
174180
Powerset2<BitSet>,
175181
Lift<RandomLattice>,
176182
ArrayLattice,
177-
Vector<RandomLattice>> {};
183+
Vector<RandomLattice>,
184+
TupleLattice> {};
178185

179186
struct RandomLattice::ElementImpl
180187
: std::variant<typename RandomFullLattice::Element,
181188
typename Flat<uint32_t>::Element,
182189
typename Powerset2<BitSet>::Element,
183190
typename Lift<RandomLattice>::Element,
184191
typename ArrayLattice::Element,
185-
typename Vector<RandomLattice>::Element> {};
192+
typename Vector<RandomLattice>::Element,
193+
typename TupleLattice::Element> {};
186194

187-
constexpr int FullLatticePicks = 6;
195+
constexpr int FullLatticePicks = 7;
188196

189197
RandomFullLattice::RandomFullLattice(Random& rand,
190198
size_t depth,
@@ -214,13 +222,18 @@ RandomFullLattice::RandomFullLattice(Random& rand,
214222
lattice = std::make_unique<L>(
215223
L{Vector{RandomFullLattice{rand, depth + 1}, rand.upTo(4)}});
216224
return;
225+
case 6:
226+
lattice = std::make_unique<L>(
227+
L{TupleFullLattice{RandomFullLattice{rand, depth + 1},
228+
RandomFullLattice{rand, depth + 1}}});
229+
return;
217230
}
218231
WASM_UNREACHABLE("unexpected pick");
219232
}
220233

221234
RandomLattice::RandomLattice(Random& rand, size_t depth) : rand(rand) {
222235
// TODO: Limit the depth once we get lattices with more fan-out.
223-
uint32_t pick = rand.upTo(FullLatticePicks + 5);
236+
uint32_t pick = rand.upTo(FullLatticePicks + 6);
224237

225238
if (pick < FullLatticePicks) {
226239
lattice = std::make_unique<L>(L{RandomFullLattice{rand, depth, pick}});
@@ -245,6 +258,10 @@ RandomLattice::RandomLattice(Random& rand, size_t depth) : rand(rand) {
245258
lattice = std::make_unique<L>(
246259
L{Vector{RandomLattice{rand, depth + 1}, rand.upTo(4)}});
247260
return;
261+
case FullLatticePicks + 5:
262+
lattice = std::make_unique<L>(L{TupleLattice{
263+
RandomLattice{rand, depth + 1}, RandomLattice{rand, depth + 1}}});
264+
return;
248265
}
249266
WASM_UNREACHABLE("unexpected pick");
250267
}
@@ -280,6 +297,11 @@ RandomFullLattice::Element RandomFullLattice::makeElement() const noexcept {
280297
}
281298
return ElementImpl{std::move(elem)};
282299
}
300+
if (const auto* l = std::get_if<TupleFullLattice>(lattice.get())) {
301+
return ElementImpl{typename TupleFullLattice::Element{
302+
std::get<0>(l->lattices).makeElement(),
303+
std::get<1>(l->lattices).makeElement()}};
304+
}
283305
WASM_UNREACHABLE("unexpected lattice");
284306
}
285307

@@ -323,6 +345,11 @@ RandomLattice::Element RandomLattice::makeElement() const noexcept {
323345
}
324346
return ElementImpl{std::move(elem)};
325347
}
348+
if (const auto* l = std::get_if<TupleLattice>(lattice.get())) {
349+
return ElementImpl{
350+
typename TupleLattice::Element{std::get<0>(l->lattices).makeElement(),
351+
std::get<1>(l->lattices).makeElement()}};
352+
}
326353
WASM_UNREACHABLE("unexpected lattice");
327354
}
328355

@@ -376,6 +403,14 @@ void printFullElement(std::ostream& os,
376403
}
377404
indent(os, depth);
378405
os << "]\n";
406+
} else if (const auto* e =
407+
std::get_if<typename TupleFullLattice::Element>(&*elem)) {
408+
os << "Tuple(\n";
409+
const auto& [first, second] = *e;
410+
printFullElement(os, first, depth + 1);
411+
printFullElement(os, second, depth + 1);
412+
indent(os, depth);
413+
os << ")\n";
379414
} else {
380415
WASM_UNREACHABLE("unexpected element");
381416
}
@@ -437,6 +472,14 @@ void printElement(std::ostream& os,
437472
}
438473
indent(os, depth);
439474
os << "]\n";
475+
} else if (const auto* e =
476+
std::get_if<typename TupleLattice::Element>(&*elem)) {
477+
os << "Tuple(\n";
478+
const auto& [first, second] = *e;
479+
printElement(os, first, depth + 1);
480+
printElement(os, second, depth + 1);
481+
indent(os, depth);
482+
os << ")\n";
440483
} else {
441484
WASM_UNREACHABLE("unexpected element");
442485
}

0 commit comments

Comments
 (0)