Skip to content

Commit a8372db

Browse files
tlivelyradekdoulik
authored andcommitted
[analysis] Add a tuple lattice (WebAssembly#6062)
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 e6cafad commit a8372db

File tree

3 files changed

+325
-17
lines changed

3 files changed

+325
-17
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+
int 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+
int 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+
int 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+
int 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: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "analysis/lattices/inverted.h"
2929
#include "analysis/lattices/lift.h"
3030
#include "analysis/lattices/stack.h"
31+
#include "analysis/lattices/tuple.h"
3132
#include "analysis/lattices/vector.h"
3233
#include "analysis/liveness-transfer-function.h"
3334
#include "analysis/reaching-definitions-transfer-function.h"
@@ -152,38 +153,47 @@ static_assert(Lattice<RandomLattice>);
152153
using ArrayFullLattice = analysis::Array<RandomFullLattice, 2>;
153154
using ArrayLattice = analysis::Array<RandomLattice, 2>;
154155

156+
using TupleFullLattice = analysis::Tuple<RandomFullLattice, RandomFullLattice>;
157+
using TupleLattice = analysis::Tuple<RandomLattice, RandomLattice>;
158+
155159
struct RandomFullLattice::L : std::variant<Bool,
156160
UInt32,
157161
Inverted<RandomFullLattice>,
158162
ArrayFullLattice,
159-
Vector<RandomFullLattice>> {};
163+
Vector<RandomFullLattice>,
164+
TupleFullLattice> {};
160165

161166
struct RandomFullLattice::ElementImpl
162167
: std::variant<typename Bool::Element,
163168
typename UInt32::Element,
164169
typename Inverted<RandomFullLattice>::Element,
165170
typename ArrayFullLattice::Element,
166-
typename Vector<RandomFullLattice>::Element> {};
171+
typename Vector<RandomFullLattice>::Element,
172+
typename TupleFullLattice::Element> {};
167173

168174
struct RandomLattice::L : std::variant<RandomFullLattice,
169175
Flat<uint32_t>,
170176
Lift<RandomLattice>,
171177
ArrayLattice,
172-
Vector<RandomLattice>> {};
178+
Vector<RandomLattice>,
179+
TupleLattice> {};
173180

174181
struct RandomLattice::ElementImpl
175182
: std::variant<typename RandomFullLattice::Element,
176183
typename Flat<uint32_t>::Element,
177184
typename Lift<RandomLattice>::Element,
178185
typename ArrayLattice::Element,
179-
typename Vector<RandomLattice>::Element> {};
186+
typename Vector<RandomLattice>::Element,
187+
typename TupleLattice::Element> {};
188+
189+
constexpr int FullLatticePicks = 6;
180190

181191
RandomFullLattice::RandomFullLattice(Random& rand,
182192
size_t depth,
183193
std::optional<uint32_t> maybePick)
184194
: rand(rand) {
185195
// TODO: Limit the depth once we get lattices with more fan-out.
186-
uint32_t pick = maybePick ? *maybePick : rand.upTo(5);
196+
uint32_t pick = maybePick ? *maybePick : rand.upTo(FullLatticePicks);
187197
switch (pick) {
188198
case 0:
189199
lattice = std::make_unique<L>(L{Bool{}});
@@ -203,35 +213,43 @@ RandomFullLattice::RandomFullLattice(Random& rand,
203213
lattice = std::make_unique<L>(
204214
L{Vector{RandomFullLattice{rand, depth + 1}, rand.upTo(4)}});
205215
return;
216+
case 5:
217+
lattice = std::make_unique<L>(
218+
L{TupleFullLattice{RandomFullLattice{rand, depth + 1},
219+
RandomFullLattice{rand, depth + 1}}});
220+
return;
206221
}
207222
WASM_UNREACHABLE("unexpected pick");
208223
}
209224

210225
RandomLattice::RandomLattice(Random& rand, size_t depth) : rand(rand) {
211226
// TODO: Limit the depth once we get lattices with more fan-out.
212-
uint32_t pick = rand.upTo(9);
227+
uint32_t pick = rand.upTo(FullLatticePicks + 5);
228+
229+
if (pick < FullLatticePicks) {
230+
lattice = std::make_unique<L>(L{RandomFullLattice{rand, depth, pick}});
231+
return;
232+
}
233+
213234
switch (pick) {
214-
case 0:
215-
case 1:
216-
case 2:
217-
case 3:
218-
case 4:
219-
lattice = std::make_unique<L>(L{RandomFullLattice{rand, depth, pick}});
220-
return;
221-
case 5:
235+
case FullLatticePicks + 0:
222236
lattice = std::make_unique<L>(L{Flat<uint32_t>{}});
223237
return;
224-
case 6:
238+
case FullLatticePicks + 1:
225239
lattice = std::make_unique<L>(L{Lift{RandomLattice{rand, depth + 1}}});
226240
return;
227-
case 7:
241+
case FullLatticePicks + 2:
228242
lattice =
229243
std::make_unique<L>(L{ArrayLattice{RandomLattice{rand, depth + 1}}});
230244
return;
231-
case 8:
245+
case FullLatticePicks + 3:
232246
lattice = std::make_unique<L>(
233247
L{Vector{RandomLattice{rand, depth + 1}, rand.upTo(4)}});
234248
return;
249+
case FullLatticePicks + 4:
250+
lattice = std::make_unique<L>(L{TupleLattice{
251+
RandomLattice{rand, depth + 1}, RandomLattice{rand, depth + 1}}});
252+
return;
235253
}
236254
WASM_UNREACHABLE("unexpected pick");
237255
}
@@ -258,6 +276,11 @@ RandomFullLattice::Element RandomFullLattice::makeElement() const noexcept {
258276
}
259277
return ElementImpl{std::move(elem)};
260278
}
279+
if (const auto* l = std::get_if<TupleFullLattice>(lattice.get())) {
280+
return ElementImpl{typename TupleFullLattice::Element{
281+
std::get<0>(l->lattices).makeElement(),
282+
std::get<1>(l->lattices).makeElement()}};
283+
}
261284
WASM_UNREACHABLE("unexpected lattice");
262285
}
263286

@@ -292,6 +315,11 @@ RandomLattice::Element RandomLattice::makeElement() const noexcept {
292315
}
293316
return ElementImpl{std::move(elem)};
294317
}
318+
if (const auto* l = std::get_if<TupleLattice>(lattice.get())) {
319+
return ElementImpl{
320+
typename TupleLattice::Element{std::get<0>(l->lattices).makeElement(),
321+
std::get<1>(l->lattices).makeElement()}};
322+
}
295323
WASM_UNREACHABLE("unexpected lattice");
296324
}
297325

@@ -333,6 +361,14 @@ void printFullElement(std::ostream& os,
333361
}
334362
indent(os, depth);
335363
os << "]\n";
364+
} else if (const auto* e =
365+
std::get_if<typename TupleFullLattice::Element>(&*elem)) {
366+
os << "Tuple(\n";
367+
const auto& [first, second] = *e;
368+
printFullElement(os, first, depth + 1);
369+
printFullElement(os, second, depth + 1);
370+
indent(os, depth);
371+
os << ")\n";
336372
} else {
337373
WASM_UNREACHABLE("unexpected element");
338374
}
@@ -382,6 +418,14 @@ void printElement(std::ostream& os,
382418
}
383419
indent(os, depth);
384420
os << "]\n";
421+
} else if (const auto* e =
422+
std::get_if<typename TupleLattice::Element>(&*elem)) {
423+
os << "Tuple(\n";
424+
const auto& [first, second] = *e;
425+
printElement(os, first, depth + 1);
426+
printElement(os, second, depth + 1);
427+
indent(os, depth);
428+
os << ")\n";
385429
} else {
386430
WASM_UNREACHABLE("unexpected element");
387431
}

0 commit comments

Comments
 (0)