1/**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef LIBPANDABASE_UTILS_REGMASK_H
17#define LIBPANDABASE_UTILS_REGMASK_H
18
19#include <array>
20#include "utils/bit_utils.h"
21#include "utils/type_helpers.h"
22
23namespace panda {
24
25template <typename T, size_t N>
26static constexpr size_t MakeMask(const std::array<T, N> &indexes)
27{
28    size_t res = 0;
29    for (size_t i : indexes) {
30        res |= (1UL << i);
31    }
32    return res;
33}
34
35template <typename... Indexes>
36static constexpr size_t MakeMask(Indexes... indexes)
37{
38    return ((1UL << helpers::ToUnsigned(indexes)) | ...);
39}
40
41template <typename... Indexes>
42static constexpr size_t MakeMaskByExcluding(size_t width, Indexes... indexes)
43{
44    size_t res = (1ULL << width) - 1;
45    size_t exclude = ((1ULL << helpers::ToUnsigned(indexes)) | ...);
46    return res & ~exclude;
47}
48
49/**
50 * Base struct for registers mask, template-parametrized by number of registers.
51 * Currently we don't support registers number greater than 32.
52 * Previously, Regmask class just inherited std::bitset, but std::bitset has poor constexpr support, that was the main
53 * reason to implement own RegMask class.
54 * Regmask has interface, similar to std::bitset.
55 */
56template <size_t N>
57class RegMaskImpl {
58public:
59    // We don't support architectures with CPU registers number, greater than 32.
60    static_assert(N <= sizeof(uint32_t) * BITS_PER_BYTE);
61
62    using ValueType = uint32_t;
63    using Self = RegMaskImpl<N>;
64
65    constexpr RegMaskImpl() = default;
66
67    // NOLINTNEXTLINE(google-explicit-constructor)
68    constexpr RegMaskImpl(ValueType v) : value_(v) {}
69
70    constexpr ValueType GetValue() const
71    {
72        return value_;
73    }
74
75    static constexpr size_t Size()
76    {
77        return N;
78    }
79
80    constexpr bool Any() const
81    {
82        return value_ != 0;
83    }
84
85    constexpr bool None() const
86    {
87        return value_ == 0;
88    }
89
90    constexpr bool Test(size_t bit) const
91    {
92        ASSERT(bit < Size());
93        return ((value_ >> static_cast<ValueType>(bit)) & 1U) != 0;
94    }
95
96    constexpr void Set()
97    {
98        value_ = ~static_cast<ValueType>(0U);
99    }
100    constexpr void Reset()
101    {
102        value_ = 0;
103    }
104
105    constexpr void Set(size_t bit)
106    {
107        ASSERT(bit < Size());
108        value_ |= (1U << bit);
109    }
110
111    constexpr void Set(size_t bit, bool value)
112    {
113        ASSERT(bit < Size());
114        if (value) {
115            Set(bit);
116        } else {
117            Reset(bit);
118        }
119    }
120
121    constexpr void Reset(size_t bit)
122    {
123        ASSERT(bit < Size());
124        value_ &= ~(1U << bit);
125    }
126
127    constexpr size_t Count() const
128    {
129        return Popcount(GetValue());
130    }
131
132    constexpr bool CountIsEven() const
133    {
134        return (Count() & 1U) == 0;
135    }
136
137    // Get number of registers from tail to the given register, counting only set bits.
138    // Given `reg` is not counted even if it is set.
139    constexpr size_t GetDistanceFromTail(size_t reg) const
140    {
141        ASSERT(reg < Size());
142        uint32_t val = GetValue() & ((1U << reg) - 1);
143        return Popcount(val);
144    }
145
146    // Get number of registers from head to the given register, counting only set bits.
147    // Given `reg` is not counted even if it is set.
148    constexpr size_t GetDistanceFromHead(size_t reg) const
149    {
150        if (reg < (Size() - 1)) {
151            uint32_t val = GetValue() & ~((1U << (reg + 1)) - 1);
152            return Popcount(val);
153        }
154        if (reg == Size() - 1) {
155            return 0;
156        }
157        // reg > (Size() - 1), something goes wrong...
158        UNREACHABLE();
159        return 0;
160    }
161
162    constexpr uint32_t GetMinRegister() const
163    {
164        ASSERT(Any());
165        return panda::Ctz(GetValue());
166    }
167
168    constexpr uint32_t GetMaxRegister() const
169    {
170        ASSERT(Any());
171        return (sizeof(decltype(GetValue())) * BITS_PER_BYTE) - 1 - panda::Clz(GetValue());
172    }
173
174    constexpr Self operator~() const
175    {
176        return Self(~GetValue());
177    }
178
179    constexpr Self operator&(Self other) const
180    {
181        return Self(GetValue() & other.GetValue());
182    }
183
184    constexpr Self operator|(Self other) const
185    {
186        return Self(GetValue() | other.GetValue());
187    }
188
189    constexpr Self operator^(Self other) const
190    {
191        return Self(GetValue() ^ other.GetValue());
192    }
193
194    constexpr Self operator&=(Self other)
195    {
196        value_ &= other.GetValue();
197        return *this;
198    }
199
200    constexpr Self operator|=(Self other)
201    {
202        value_ |= other.GetValue();
203        return *this;
204    }
205
206    constexpr Self operator^=(Self other)
207    {
208        value_ ^= other.GetValue();
209        return *this;
210    }
211    constexpr bool operator[](size_t bit) const
212    {
213        return Test(bit);
214    }
215    constexpr bool operator==(Self other) const
216    {
217        return value_ == other.value_;
218    }
219
220    constexpr bool operator!=(Self other) const
221    {
222        return !(*this == other);
223    }
224
225    constexpr ValueType to_ulong() const
226    {
227        return GetValue();
228    }
229
230    void Dump(std::ostream &out = std::cerr) const
231    {
232        out << "Regmask[" << N << "]: ";
233        for (size_t i = 0; i < N; i++) {
234            if (Test(i)) {
235                out << i << " ";
236            }
237        }
238    }
239
240    // The following methods are for compatibility with `std::bitset`, since we used `std::bitset` before.
241    // Don't use these method in a new code.
242    constexpr bool any() const
243    {
244        return Any();
245    }
246    constexpr bool none() const
247    {
248        return None();
249    }
250    constexpr bool test(size_t bit) const
251    {
252        return Test(bit);
253    }
254    constexpr void set(size_t bit)
255    {
256        Set(bit);
257    }
258    constexpr void set(size_t bit, bool value)
259    {
260        Set(bit, value);
261    }
262    constexpr Self set()
263    {
264        Set();
265        return *this;
266    }
267    constexpr Self reset()
268    {
269        Reset();
270        return *this;
271    }
272    constexpr void reset(size_t bit)
273    {
274        Reset(bit);
275    }
276    constexpr size_t count() const
277    {
278        return Count();
279    }
280    constexpr size_t size() const
281    {
282        return Size();
283    }
284
285private:
286    ValueType value_ {0};
287};
288
289static constexpr uint8_t REGISTERS_NUM = 32;
290static constexpr uint8_t VREGISTERS_NUM = 32;
291
292using RegMask = RegMaskImpl<REGISTERS_NUM>;
293using VRegMask = RegMaskImpl<VREGISTERS_NUM>;
294
295inline std::ostream &operator<<(std::ostream &stream, const RegMask &mask)
296{
297    mask.Dump(stream);
298    return stream;
299}
300
301}  // namespace panda
302
303#endif  // LIBPANDABASE_UTILS_REGMASK_H
304