Skip to content

Commit a34cf69

Browse files
committed
[analysis] Add a generic powerset lattice
Add `Powerset2<Set>` that implements the lattice of sets concretely represented as `Set` ordered by subset as well as the full lattice `FinitePowerset2<Set>` that is additionally configurted with the universe of set elements so it can produce a top element, which is the set of everything in the universe. The "2" in the name serves to temporarily differentiate these new powerset lattices from the existing powerset lattices, which are less flexible and will be replaced with the new powerset lattices in a future PR. Also add a new `BitSet` utility that implements a set interface matching e.g. `std::set<size_t>` or `std::unordered_set<size_t>` in terms of a `std::vector<bool>` bit vector. Use `BitSet` as the concrete set implementation used to fuzz `Powerset2` and `FinitePowerset2`.
1 parent c5a6478 commit a34cf69

File tree

4 files changed

+538
-17
lines changed

4 files changed

+538
-17
lines changed

src/analysis/lattices/powerset2.h

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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+
#include <unordered_set>
18+
19+
#include "../lattice.h"
20+
#include "support/bitset.h"
21+
22+
#ifndef wasm_analysis_lattices_powerset2_h
23+
#define wasm_analysis_lattices_powerset2_h
24+
25+
namespace wasm::analysis {
26+
27+
// A powerset lattice whose elements are sets (represented concretely with type
28+
// `Set`) ordered by subset.
29+
template<typename Set> struct Powerset2 {
30+
using Element = Set;
31+
32+
Element getBottom() const noexcept { return Set{}; }
33+
34+
LatticeComparison compare(const Set& a, const Set& b) const noexcept {
35+
auto sizeA = a.size();
36+
auto sizeB = b.size();
37+
if (sizeA <= sizeB) {
38+
for (const auto& val : a) {
39+
if (!b.count(val)) {
40+
// At least one member differs between A and B.
41+
return NO_RELATION;
42+
}
43+
}
44+
// All elements in A were also in B.
45+
return sizeA == sizeB ? EQUAL : LESS;
46+
}
47+
for (const auto& val : b) {
48+
if (!a.count(val)) {
49+
// At least one member differs between A and B.
50+
return NO_RELATION;
51+
}
52+
}
53+
// A was larger and contained all the elements of B.
54+
return GREATER;
55+
}
56+
57+
bool join(Set& joinee, const Set& joiner) const noexcept {
58+
bool result = false;
59+
for (const auto& val : joiner) {
60+
result |= joinee.insert(val).second;
61+
}
62+
return result;
63+
}
64+
};
65+
66+
// A powerset lattice initialized with a list of all elements in the universe,
67+
// making it possible to produce a top elements that contains all of them.
68+
template<typename Set> struct FinitePowerset2 : Powerset2<Set> {
69+
private:
70+
const Set top;
71+
72+
public:
73+
using Element = Set;
74+
75+
FinitePowerset2(std::initializer_list<typename Set::value_type>&& vals)
76+
: top(std::move(vals)) {}
77+
78+
template<typename Vals>
79+
FinitePowerset2(const Vals& vals) : top(vals.begin(), vals.end()) {}
80+
81+
Element getTop() const noexcept { return top; }
82+
83+
bool meet(Set& meetee, const Set& meeter) const noexcept {
84+
bool result = false;
85+
for (auto it = meetee.begin(); it != meetee.end();) {
86+
if (!meeter.count(*it)) {
87+
it = meetee.erase(it);
88+
result = true;
89+
} else {
90+
++it;
91+
}
92+
}
93+
return result;
94+
}
95+
};
96+
97+
#if __cplusplus >= 202002L
98+
static_assert(Lattice<Powerset2<BitSet>>);
99+
static_assert(Lattice<Powerset2<std::unordered_set<int>>>);
100+
static_assert(FullLattice<FinitePowerset2<BitSet>>);
101+
static_assert(FullLattice<FinitePowerset2<std::unordered_set<int>>>);
102+
#endif
103+
104+
} // namespace wasm::analysis
105+
106+
#endif // wasm_analysis_lattices_powerset2_h

src/support/bitset.h

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
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_support_bitset_h
18+
#define wasm_support_bitset_h
19+
20+
#include <cassert>
21+
#include <cstddef>
22+
#include <iterator>
23+
#include <vector>
24+
25+
namespace wasm {
26+
27+
// Represent a set of integers in [0, N) with a bitvector of size N, where a 1
28+
// bit signifies set membership. This interface is intended to match that of
29+
// std::set or std::unordered_set as closely as possible.
30+
class BitSet : std::vector<bool> {
31+
std::vector<bool>& vec() noexcept { return *this; }
32+
const std::vector<bool>& vec() const noexcept { return *this; }
33+
34+
void ensure(size_t value) noexcept {
35+
if (vec().size() <= value) {
36+
vec().resize(value + 1);
37+
}
38+
}
39+
40+
public:
41+
using key_type = size_t;
42+
using value_type = size_t;
43+
44+
class iterator {
45+
const std::vector<bool>* parent = nullptr;
46+
size_t index = 0;
47+
48+
iterator(const std::vector<bool>* parent) : parent(parent) {
49+
const auto size = parent->size();
50+
while (index < size && !(*parent)[index]) {
51+
++index;
52+
}
53+
}
54+
55+
iterator(const std::vector<bool>* parent, size_t index)
56+
: parent(parent), index(index) {}
57+
58+
friend BitSet;
59+
60+
public:
61+
using difference_type = ptrdiff_t;
62+
using value_type = size_t;
63+
using pointer = size_t*;
64+
using reference = size_t&;
65+
using iterator_category = std::bidirectional_iterator_tag;
66+
67+
iterator() = default;
68+
iterator(const iterator&) = default;
69+
iterator(iterator&&) = default;
70+
iterator& operator=(const iterator&) = default;
71+
iterator& operator=(iterator&&) = default;
72+
73+
size_t operator*() const noexcept { return index; }
74+
75+
iterator& operator++() noexcept {
76+
const auto size = parent->size();
77+
do {
78+
++index;
79+
} while (index < size && !(*parent)[index]);
80+
return *this;
81+
}
82+
83+
iterator operator++(int) noexcept {
84+
iterator it = *this;
85+
++(*this);
86+
return it;
87+
}
88+
89+
iterator& operator--() noexcept {
90+
do {
91+
--index;
92+
assert(index != -1ull);
93+
} while (!(*parent)[index]);
94+
return *this;
95+
}
96+
97+
iterator operator--(int) noexcept {
98+
iterator it = *this;
99+
--(*this);
100+
return it;
101+
}
102+
103+
bool operator==(const iterator& other) const noexcept {
104+
assert(parent == other.parent);
105+
return index == other.index;
106+
}
107+
bool operator!=(const iterator& other) const noexcept {
108+
return !(*this == other);
109+
assert(parent == other.parent);
110+
return index == other.index;
111+
}
112+
};
113+
114+
BitSet() = default;
115+
BitSet(const BitSet&) = default;
116+
BitSet(BitSet&&) = default;
117+
118+
template<typename It> BitSet(It begin, It end) {
119+
for (auto it = begin; it != end; ++it) {
120+
insert(*it);
121+
}
122+
}
123+
BitSet(const std::initializer_list<size_t>& vals)
124+
: BitSet(vals.begin(), vals.end()) {}
125+
126+
BitSet& operator=(const BitSet&) = default;
127+
BitSet& operator=(BitSet&&) = default;
128+
129+
iterator begin() const noexcept { return iterator({this}); }
130+
iterator end() const noexcept { return iterator({this, vec().size()}); }
131+
132+
bool empty() const noexcept { return begin() == end(); }
133+
134+
size_t size() const noexcept {
135+
size_t result = 0;
136+
for (auto it = begin(); it != end(); ++it, ++result) {
137+
}
138+
return result;
139+
}
140+
141+
void clear() noexcept { vec().clear(); }
142+
143+
std::pair<iterator, bool> insert(size_t value) noexcept {
144+
ensure(value);
145+
bool didInsert = false;
146+
if (!(*this)[value]) {
147+
didInsert = true;
148+
(*this)[value] = true;
149+
}
150+
return {iterator({this, value}), didInsert};
151+
}
152+
153+
size_t erase(size_t key) noexcept {
154+
if (key < vec().size()) {
155+
return false;
156+
}
157+
bool didErase = false;
158+
if ((*this)[key]) {
159+
didErase = true;
160+
(*this)[key] = false;
161+
}
162+
return didErase;
163+
}
164+
165+
iterator erase(iterator it) noexcept {
166+
auto next = it;
167+
++next;
168+
(*this)[it.index] = false;
169+
return next;
170+
}
171+
172+
size_t count(size_t key) const noexcept {
173+
if (key >= vec().size()) {
174+
return 0;
175+
}
176+
return (*this)[key];
177+
}
178+
179+
bool operator==(const BitSet& other) const {
180+
auto thisSize = vec().size();
181+
auto otherSize = other.vec().size();
182+
const std::vector<bool>* shorter;
183+
const std::vector<bool>* longer;
184+
if (thisSize <= otherSize) {
185+
shorter = this;
186+
longer = &other;
187+
} else {
188+
shorter = &other;
189+
longer = this;
190+
}
191+
size_t i = 0;
192+
size_t shortSize = shorter->size();
193+
size_t longSize = longer->size();
194+
// All elements should match.
195+
for (; i < shortSize; ++i) {
196+
if ((*shorter)[i] != (*longer)[i]) {
197+
return false;
198+
}
199+
}
200+
// The rest of the longer vector should be zeroes.
201+
for (; i < longSize; ++i) {
202+
if ((*longer)[i]) {
203+
return false;
204+
}
205+
}
206+
return true;
207+
}
208+
};
209+
210+
#if __cplusplus >= 202002L
211+
static_assert(std::bidirectional_iterator<typename BitSet::iterator>);
212+
#endif
213+
214+
} // namespace wasm
215+
216+
#endif // wasm_support_bitset_h

0 commit comments

Comments
 (0)