-
Notifications
You must be signed in to change notification settings - Fork 759
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[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`.
- Loading branch information
Showing
4 changed files
with
538 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/* | ||
* Copyright 2023 WebAssembly Community Group participants | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#include <unordered_set> | ||
|
||
#include "../lattice.h" | ||
#include "support/bitset.h" | ||
|
||
#ifndef wasm_analysis_lattices_powerset2_h | ||
#define wasm_analysis_lattices_powerset2_h | ||
|
||
namespace wasm::analysis { | ||
|
||
// A powerset lattice whose elements are sets (represented concretely with type | ||
// `Set`) ordered by subset. | ||
template<typename Set> struct Powerset2 { | ||
using Element = Set; | ||
|
||
Element getBottom() const noexcept { return Set{}; } | ||
|
||
LatticeComparison compare(const Set& a, const Set& b) const noexcept { | ||
auto sizeA = a.size(); | ||
auto sizeB = b.size(); | ||
if (sizeA <= sizeB) { | ||
for (const auto& val : a) { | ||
if (!b.count(val)) { | ||
// At least one member differs between A and B. | ||
return NO_RELATION; | ||
} | ||
} | ||
// All elements in A were also in B. | ||
return sizeA == sizeB ? EQUAL : LESS; | ||
} | ||
for (const auto& val : b) { | ||
if (!a.count(val)) { | ||
// At least one member differs between A and B. | ||
return NO_RELATION; | ||
} | ||
} | ||
// A was larger and contained all the elements of B. | ||
return GREATER; | ||
} | ||
|
||
bool join(Set& joinee, const Set& joiner) const noexcept { | ||
bool result = false; | ||
for (const auto& val : joiner) { | ||
result |= joinee.insert(val).second; | ||
} | ||
return result; | ||
} | ||
}; | ||
|
||
// A powerset lattice initialized with a list of all elements in the universe, | ||
// making it possible to produce a top elements that contains all of them. | ||
template<typename Set> struct FinitePowerset2 : Powerset2<Set> { | ||
private: | ||
const Set top; | ||
|
||
public: | ||
using Element = Set; | ||
|
||
FinitePowerset2(std::initializer_list<typename Set::value_type>&& vals) | ||
: top(std::move(vals)) {} | ||
|
||
template<typename Vals> | ||
FinitePowerset2(const Vals& vals) : top(vals.begin(), vals.end()) {} | ||
|
||
Element getTop() const noexcept { return top; } | ||
|
||
bool meet(Set& meetee, const Set& meeter) const noexcept { | ||
bool result = false; | ||
for (auto it = meetee.begin(); it != meetee.end();) { | ||
if (!meeter.count(*it)) { | ||
it = meetee.erase(it); | ||
result = true; | ||
} else { | ||
++it; | ||
} | ||
} | ||
return result; | ||
} | ||
}; | ||
|
||
#if __cplusplus >= 202002L | ||
static_assert(Lattice<Powerset2<BitSet>>); | ||
static_assert(Lattice<Powerset2<std::unordered_set<int>>>); | ||
static_assert(FullLattice<FinitePowerset2<BitSet>>); | ||
static_assert(FullLattice<FinitePowerset2<std::unordered_set<int>>>); | ||
#endif | ||
|
||
} // namespace wasm::analysis | ||
|
||
#endif // wasm_analysis_lattices_powerset2_h |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
/* | ||
* Copyright 2023 WebAssembly Community Group participants | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#ifndef wasm_support_bitset_h | ||
#define wasm_support_bitset_h | ||
|
||
#include <cassert> | ||
#include <cstddef> | ||
#include <iterator> | ||
#include <vector> | ||
|
||
namespace wasm { | ||
|
||
// Represent a set of integers in [0, N) with a bitvector of size N, where a 1 | ||
// bit signifies set membership. This interface is intended to match that of | ||
// std::set or std::unordered_set as closely as possible. | ||
class BitSet : std::vector<bool> { | ||
std::vector<bool>& vec() noexcept { return *this; } | ||
const std::vector<bool>& vec() const noexcept { return *this; } | ||
|
||
void ensure(size_t value) noexcept { | ||
if (vec().size() <= value) { | ||
vec().resize(value + 1); | ||
} | ||
} | ||
|
||
public: | ||
using key_type = size_t; | ||
using value_type = size_t; | ||
|
||
class iterator { | ||
const std::vector<bool>* parent = nullptr; | ||
size_t index = 0; | ||
|
||
iterator(const std::vector<bool>* parent) : parent(parent) { | ||
const auto size = parent->size(); | ||
while (index < size && !(*parent)[index]) { | ||
++index; | ||
} | ||
} | ||
|
||
iterator(const std::vector<bool>* parent, size_t index) | ||
: parent(parent), index(index) {} | ||
|
||
friend BitSet; | ||
|
||
public: | ||
using difference_type = ptrdiff_t; | ||
using value_type = size_t; | ||
using pointer = size_t*; | ||
using reference = size_t&; | ||
using iterator_category = std::bidirectional_iterator_tag; | ||
|
||
iterator() = default; | ||
iterator(const iterator&) = default; | ||
iterator(iterator&&) = default; | ||
iterator& operator=(const iterator&) = default; | ||
iterator& operator=(iterator&&) = default; | ||
|
||
size_t operator*() const noexcept { return index; } | ||
|
||
iterator& operator++() noexcept { | ||
const auto size = parent->size(); | ||
do { | ||
++index; | ||
} while (index < size && !(*parent)[index]); | ||
return *this; | ||
} | ||
|
||
iterator operator++(int) noexcept { | ||
iterator it = *this; | ||
++(*this); | ||
return it; | ||
} | ||
|
||
iterator& operator--() noexcept { | ||
do { | ||
--index; | ||
assert(index != -1ull); | ||
} while (!(*parent)[index]); | ||
return *this; | ||
} | ||
|
||
iterator operator--(int) noexcept { | ||
iterator it = *this; | ||
--(*this); | ||
return it; | ||
} | ||
|
||
bool operator==(const iterator& other) const noexcept { | ||
assert(parent == other.parent); | ||
return index == other.index; | ||
} | ||
bool operator!=(const iterator& other) const noexcept { | ||
return !(*this == other); | ||
assert(parent == other.parent); | ||
return index == other.index; | ||
} | ||
}; | ||
|
||
BitSet() = default; | ||
BitSet(const BitSet&) = default; | ||
BitSet(BitSet&&) = default; | ||
|
||
template<typename It> BitSet(It begin, It end) { | ||
for (auto it = begin; it != end; ++it) { | ||
insert(*it); | ||
} | ||
} | ||
BitSet(const std::initializer_list<size_t>& vals) | ||
: BitSet(vals.begin(), vals.end()) {} | ||
|
||
BitSet& operator=(const BitSet&) = default; | ||
BitSet& operator=(BitSet&&) = default; | ||
|
||
iterator begin() const noexcept { return iterator({this}); } | ||
iterator end() const noexcept { return iterator({this, vec().size()}); } | ||
|
||
bool empty() const noexcept { return begin() == end(); } | ||
|
||
size_t size() const noexcept { | ||
size_t result = 0; | ||
for (auto it = begin(); it != end(); ++it, ++result) { | ||
} | ||
return result; | ||
} | ||
|
||
void clear() noexcept { vec().clear(); } | ||
|
||
std::pair<iterator, bool> insert(size_t value) noexcept { | ||
ensure(value); | ||
bool didInsert = false; | ||
if (!(*this)[value]) { | ||
didInsert = true; | ||
(*this)[value] = true; | ||
} | ||
return {iterator({this, value}), didInsert}; | ||
} | ||
|
||
size_t erase(size_t key) noexcept { | ||
if (key < vec().size()) { | ||
return false; | ||
} | ||
bool didErase = false; | ||
if ((*this)[key]) { | ||
didErase = true; | ||
(*this)[key] = false; | ||
} | ||
return didErase; | ||
} | ||
|
||
iterator erase(iterator it) noexcept { | ||
auto next = it; | ||
++next; | ||
(*this)[it.index] = false; | ||
return next; | ||
} | ||
|
||
size_t count(size_t key) const noexcept { | ||
if (key >= vec().size()) { | ||
return 0; | ||
} | ||
return (*this)[key]; | ||
} | ||
|
||
bool operator==(const BitSet& other) const { | ||
auto thisSize = vec().size(); | ||
auto otherSize = other.vec().size(); | ||
const std::vector<bool>* shorter; | ||
const std::vector<bool>* longer; | ||
if (thisSize <= otherSize) { | ||
shorter = this; | ||
longer = &other; | ||
} else { | ||
shorter = &other; | ||
longer = this; | ||
} | ||
size_t i = 0; | ||
size_t shortSize = shorter->size(); | ||
size_t longSize = longer->size(); | ||
// All elements should match. | ||
for (; i < shortSize; ++i) { | ||
if ((*shorter)[i] != (*longer)[i]) { | ||
return false; | ||
} | ||
} | ||
// The rest of the longer vector should be zeroes. | ||
for (; i < longSize; ++i) { | ||
if ((*longer)[i]) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
}; | ||
|
||
#if __cplusplus >= 202002L | ||
static_assert(std::bidirectional_iterator<typename BitSet::iterator>); | ||
#endif | ||
|
||
} // namespace wasm | ||
|
||
#endif // wasm_support_bitset_h |
Oops, something went wrong.