/src/solidity/libyul/backends/evm/ssa/util/UseCountSet.h
Line | Count | Source |
1 | | /* |
2 | | This file is part of solidity. |
3 | | |
4 | | solidity is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation, either version 3 of the License, or |
7 | | (at your option) any later version. |
8 | | |
9 | | solidity is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with solidity. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | // SPDX-License-Identifier: GPL-3.0 |
18 | | |
19 | | #pragma once |
20 | | |
21 | | #include <range/v3/range/concepts.hpp> |
22 | | #include <range/v3/range/traits.hpp> |
23 | | |
24 | | #include <algorithm> |
25 | | #include <concepts> |
26 | | #include <cstdint> |
27 | | #include <iterator> |
28 | | #include <utility> |
29 | | #include <vector> |
30 | | |
31 | | namespace solidity::yul::ssa::util |
32 | | { |
33 | | |
34 | | /// Vector-backed reference-counted set keyed on `Key`. Each entry carries a non-zero use count; absent keys have |
35 | | /// effective count 0. Designed for small working sets such that linear scan beats the constant overhead |
36 | | /// of a hashed or tree-based associative container. |
37 | | template <std::equality_comparable Key> |
38 | | class UseCountSet |
39 | | { |
40 | | public: |
41 | | using Value = Key; |
42 | | using Count = std::uint32_t; |
43 | | using Entries = std::vector<std::pair<Key, Count>>; |
44 | | |
45 | 0 | UseCountSet() = default; |
46 | | template<std::input_iterator Iter, std::sentinel_for<Iter> Sentinel> |
47 | | UseCountSet(Iter _begin, Sentinel _end): m_entries(_begin, _end) {} |
48 | 0 | explicit UseCountSet(Entries _entries): m_entries(std::move(_entries)) {} |
49 | | |
50 | | bool contains(Key const& _key) const |
51 | 0 | { |
52 | 0 | return findEntry(_key) != m_entries.end(); |
53 | 0 | } Unexecuted instantiation: solidity::yul::ssa::util::UseCountSet<solidity::yul::ssa::InstId>::contains(solidity::yul::ssa::InstId const&) const Unexecuted instantiation: solidity::yul::ssa::util::UseCountSet<solidity::yul::ssa::StackSlot>::contains(solidity::yul::ssa::StackSlot const&) const |
54 | | Count count(Key const& _key) const |
55 | | { |
56 | | auto const it = findEntry(_key); |
57 | | return it == m_entries.end() ? Count{0} : it->second; |
58 | | } |
59 | 0 | typename Entries::const_iterator begin() const { return m_entries.begin(); } |
60 | 0 | typename Entries::const_iterator end() const { return m_entries.end(); } |
61 | 0 | typename Entries::size_type size() const { return m_entries.size(); } |
62 | | bool empty() const { return m_entries.empty(); } |
63 | | |
64 | | /// Add `_count` to existing entry or insert a new one. No-op if `_count == 0`. |
65 | | void insert(Key const& _key, Count _count = 1) |
66 | 0 | { |
67 | 0 | if (_count == 0) |
68 | 0 | return; |
69 | 0 | auto it = findEntry(_key); |
70 | 0 | if (it != m_entries.end()) |
71 | 0 | it->second += _count; |
72 | 0 | else |
73 | 0 | m_entries.emplace_back(_key, _count); |
74 | 0 | } |
75 | | |
76 | | /// Remove the entry for `_key` if present. |
77 | | void erase(Key const& _key) |
78 | 0 | { |
79 | 0 | if (auto const it = findEntry(_key); it != m_entries.end()) |
80 | 0 | m_entries.erase(it); |
81 | 0 | } |
82 | | |
83 | | /// Subtract `_count` from the entry for `_key`, removing it if the count |
84 | | /// drops to zero. No-op if `_key` is absent or `_count == 0`. |
85 | | void remove(Key const& _key, Count _count = 1) |
86 | 0 | { |
87 | 0 | if (_count == 0) |
88 | 0 | return; |
89 | 0 | auto it = findEntry(_key); |
90 | 0 | if (it == m_entries.end()) |
91 | 0 | return; |
92 | 0 | if (it->second <= _count) |
93 | 0 | m_entries.erase(it); |
94 | 0 | else |
95 | 0 | it->second -= _count; |
96 | 0 | } |
97 | | |
98 | | /// Sum-merge counts from `_other` into this. |
99 | | UseCountSet& operator+=(UseCountSet const& _other) |
100 | 0 | { |
101 | 0 | for (auto const& [key, count]: _other.m_entries) |
102 | 0 | insert(key, count); |
103 | 0 | return *this; |
104 | 0 | } |
105 | | |
106 | | /// Union with `_other`, taking max count per key. |
107 | | UseCountSet& maxUnion(UseCountSet const& _other) |
108 | 0 | { |
109 | 0 | for (auto const& [key, count]: _other.m_entries) |
110 | 0 | { |
111 | 0 | auto it = findEntry(key); |
112 | 0 | if (it != m_entries.end()) |
113 | 0 | it->second = std::max(it->second, count); |
114 | 0 | else |
115 | 0 | m_entries.emplace_back(key, count); |
116 | 0 | } |
117 | 0 | return *this; |
118 | 0 | } |
119 | | |
120 | | void insertAll(ranges::input_range auto&& _values) requires std::convertible_to<ranges::range_reference_t<decltype(_values)>, Key> |
121 | 0 | { |
122 | 0 | for (auto const& value: _values) |
123 | 0 | insert(value); |
124 | 0 | } |
125 | | |
126 | | void eraseAll(ranges::input_range auto&& _values) requires std::convertible_to<ranges::range_reference_t<decltype(_values)>, Key> |
127 | 0 | { |
128 | 0 | for (auto const& value: _values) |
129 | 0 | erase(value); |
130 | 0 | } |
131 | | |
132 | | template<typename Predicate> |
133 | | void eraseIf(Predicate&& _predicate) |
134 | 0 | { |
135 | 0 | std::erase_if(m_entries, std::forward<Predicate>(_predicate)); |
136 | 0 | } |
137 | | |
138 | | private: |
139 | | typename Entries::iterator findEntry(Key const& _key) |
140 | 0 | { |
141 | 0 | return std::find_if(m_entries.begin(), m_entries.end(), [&](auto const& _entry) { return _entry.first == _key; }); |
142 | 0 | } |
143 | | typename Entries::const_iterator findEntry(Key const& _key) const |
144 | 0 | { |
145 | 0 | return std::find_if(m_entries.begin(), m_entries.end(), [&](auto const& _entry) { return _entry.first == _key; });Unexecuted instantiation: auto solidity::yul::ssa::util::UseCountSet<solidity::yul::ssa::InstId>::findEntry(solidity::yul::ssa::InstId const&) const::{lambda(auto:1 const&)#1}::operator()<std::__1::pair<solidity::yul::ssa::InstId, unsigned int> >(std::__1::pair<solidity::yul::ssa::InstId, unsigned int> const&) constUnexecuted instantiation: auto solidity::yul::ssa::util::UseCountSet<solidity::yul::ssa::StackSlot>::findEntry(solidity::yul::ssa::StackSlot const&) const::{lambda(auto:1 const&)#1}::operator()<std::__1::pair<solidity::yul::ssa::StackSlot, unsigned int> >(std::__1::pair<solidity::yul::ssa::StackSlot, unsigned int> const&) const |
146 | 0 | } Unexecuted instantiation: solidity::yul::ssa::util::UseCountSet<solidity::yul::ssa::InstId>::findEntry(solidity::yul::ssa::InstId const&) const Unexecuted instantiation: solidity::yul::ssa::util::UseCountSet<solidity::yul::ssa::StackSlot>::findEntry(solidity::yul::ssa::StackSlot const&) const |
147 | | |
148 | | Entries m_entries; |
149 | | }; |
150 | | |
151 | | } |