1// Copyright (c) 2017 ARM Limited
2// All rights reserved
3//
4// The license below extends only to copyright in the software and shall
5// not be construed as granting a license to any other intellectual
6// property including but not limited to intellectual property relating
7// to a hardware implementation of the functionality of the software
8// licensed hereunder.  You may use the software subject to the license
9// terms below provided that you ensure that this notice is replicated
10// unmodified and in its entirety in all distributions of the software,
11// modified or unmodified, in source code or in binary form.
12//
13// Redistribution and use in source and binary forms, with or without
14// modification, are permitted provided that the following conditions are
15// met: redistributions of source code must retain the above copyright
16// notice, this list of conditions and the following disclaimer;
17// redistributions in binary form must reproduce the above copyright
18// notice, this list of conditions and the following disclaimer in the
19// documentation and/or other materials provided with the distribution;
20// neither the name of the copyright holders nor the names of its
21// contributors may be used to endorse or promote products derived from
22// this software without specific prior written permission.
23//
24// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35//
36// Authors: Giacomo Gabrielli
37//          Rekai Gonzalez
38//          Javier Setoain
39
40#ifndef __ARCH_GENERIC_VEC_PRED_REG_HH__
41#define __ARCH_GENERIC_VEC_PRED_REG_HH__
42
43#include <array>
44#include <cassert>
45#include <vector>
46
47#include "arch/generic/vec_reg.hh"
48#include "base/cprintf.hh"
49
50template <size_t NumBits, bool Packed>
51class VecPredRegContainer;
52
53/// Predicate register view.
54///
55/// This generic class implements the View in an MVC pattern, similarly to
56/// @see VecRegT. Since predicates are mainly used in conjunction with vectors
57/// to specify which lanes are active in a vector operation, the class is
58/// templated on the vector element type to simplify ISA definitions.
59/// @tparam VecElem Type of the vector elements.
60/// @tparam NumElems Number of vector elements making up the view.
61/// @tparam Packed True if the predicate register relies on a packed
62/// representation, i.e. adjacent bits refer to different vector elements
63/// irrespective of the vector element size (e.g. this is the case for
64/// AVX-512). If false, the predicate register relies on an unpacked
65/// representation, where each bit refers to the corresponding byte in a vector
66/// register (e.g. this is the case for ARM SVE).
67/// @tparam Const True if the underlying container can be modified through
68/// the view.
69template <typename VecElem, size_t NumElems, bool Packed, bool Const>
70class VecPredRegT
71{
72  protected:
73    /// Size of the register in bits.
74    static constexpr size_t NUM_BITS = Packed ? NumElems :
75                                                sizeof(VecElem) * NumElems;
76
77  public:
78    /// Container type alias.
79    using Container = typename std::conditional<
80        Const,
81        const VecPredRegContainer<NUM_BITS, Packed>,
82        VecPredRegContainer<NUM_BITS, Packed>>::type;
83
84  protected:
85    // Alias for this type
86    using MyClass = VecPredRegT<VecElem, NumElems, Packed, Const>;
87    /// Container corresponding to this view.
88    Container& container;
89
90  public:
91    VecPredRegT(Container& c) : container(c) {}
92
93    /// Reset the register to an all-false value.
94    template<bool Condition = !Const>
95    typename std::enable_if<Condition, void>::type
96    reset() { container.reset(); }
97
98    /// Reset the register to an all-true value.
99    template<bool Condition = !Const>
100    typename std::enable_if<Condition, void>::type
101    set() { container.set(); }
102
103    template<bool Condition = !Const>
104    typename std::enable_if<Condition, MyClass&>::type
105    operator=(const MyClass& that)
106    {
107        container = that.container;
108        return *this;
109    }
110
111    const bool&
112    operator[](size_t idx) const
113    {
114        return container[idx * (Packed ? 1 : sizeof(VecElem))];
115    }
116
117    template<bool Condition = !Const>
118    typename std::enable_if<Condition, bool&>::type
119    operator[](size_t idx)
120    {
121        return container[idx * (Packed ? 1 : sizeof(VecElem))];
122    }
123
124    /// Return an element of the predicate register as it appears
125    /// in the raw (untyped) internal representation
126    uint8_t
127    get_raw(size_t idx) const
128    {
129        return container.get_bits(idx * (Packed ? 1 : sizeof(VecElem)),
130                (Packed ? 1 : sizeof(VecElem)));
131    }
132
133    /// Write a raw value in an element of the predicate register
134    template<bool Condition = !Const>
135    typename std::enable_if<Condition, void>::type
136    set_raw(size_t idx, uint8_t val)
137    {
138        container.set_bits(idx * (Packed ? 1 : sizeof(VecElem)),
139                (Packed ? 1 : sizeof(VecElem)), val);
140    }
141
142    /// Equality operator, required to compare thread contexts.
143    template<typename VE2, size_t NE2, bool P2, bool C2>
144    bool
145    operator==(const VecPredRegT<VE2, NE2, P2, C2>& that) const
146    {
147        return container == that.container;
148    }
149
150    /// Inequality operator, required to compare thread contexts.
151    template<typename VE2, size_t NE2, bool P2, bool C2>
152    bool
153    operator!=(const VecPredRegT<VE2, NE2, P2, C2>& that) const
154    {
155        return !operator==(that);
156    }
157
158    friend std::ostream&
159    operator<<(std::ostream& os, const MyClass& p)
160    {
161        // 0-sized is not allowed
162        os << '[' << p.container[0];
163        for (int i = 0; i < p.NUM_BITS; ++i) {
164            os << " " << (p.container[i] ? 1 : 0);
165        }
166        os << ']';
167        return os;
168    }
169
170    /// Returns a string representation of the register content.
171    const std::string print() const { return csprintf("%s", *this); }
172
173    /// Returns true if the first active element of the register is true.
174    /// @param mask Input mask used to filter the predicates to be tested.
175    /// @param actual_num_elems Actual number of vector elements considered for
176    /// the test (corresponding to the current vector length).
177    template <bool MC>
178    bool
179    firstActive(const VecPredRegT<VecElem, NumElems, Packed, MC>& mask,
180                size_t actual_num_elems) const
181    {
182        assert(actual_num_elems <= NumElems);
183        for (int i = 0; i < actual_num_elems; ++i) {
184            if (mask[i]) {
185                return (*this)[i];
186            }
187        }
188        return false;
189    }
190
191    /// Returns true if there are no active elements in the register.
192    /// @param mask Input mask used to filter the predicates to be tested.
193    /// @param actual_num_elems Actual number of vector elements considered for
194    /// the test (corresponding to the current vector length).
195    template <bool MC>
196    bool
197    noneActive(const VecPredRegT<VecElem, NumElems, Packed, MC>& mask,
198               size_t actual_num_elems) const
199    {
200        assert(actual_num_elems <= NumElems);
201        for (int i = 0; i < actual_num_elems; ++i) {
202            if (mask[i] && operator[](i)) {
203                return false;
204            }
205        }
206        return true;
207    }
208
209    /// Returns true if the last active element of the register is true.
210    /// @param mask Input mask used to filter the predicates to be tested.
211    /// @param actual_num_elems Actual number of vector elements considered for
212    /// the test (corresponding to the current vector length).
213    template <bool MC>
214    bool
215    lastActive(const VecPredRegT<VecElem, NumElems, Packed, MC>& mask,
216               size_t actual_num_elems) const
217    {
218        assert(actual_num_elems <= NumElems);
219        for (int i = actual_num_elems - 1; i >= 0; --i) {
220            if (mask[i]) {
221                return operator[](i);
222            }
223        }
224        return false;
225    }
226};
227
228/// Generic predicate register container.
229///
230/// This generic class implements the Model in an MVC pattern, similarly to
231/// @see VecRegContainer.
232/// @tparam NumBits Size of the container in bits.
233/// @tparam Packed See @VecRegT.
234template <size_t NumBits, bool Packed>
235class VecPredRegContainer
236{
237    static_assert(NumBits > 0,
238                  "Size of a predicate register must be > 0");
239
240  public:
241    static constexpr size_t NUM_BITS = NumBits;
242    using Container = std::array<bool, NumBits>;
243
244  private:
245    Container container;
246    // Alias for this type
247    using MyClass = VecPredRegContainer<NumBits, Packed>;
248
249  public:
250    VecPredRegContainer() {}
251
252    MyClass&
253    operator=(const MyClass& that)
254    {
255        if (&that == this)
256            return *this;
257        container = that.container;
258        return *this;
259    }
260
261    /// Required for de-serialization.
262    MyClass&
263    operator=(const std::vector<uint8_t>& that)
264    {
265        assert(that.size() == NUM_BITS);
266        std::copy(that.begin(), that.end(), container.begin());
267        return *this;
268    }
269
270    /// Resets the predicate register to an all-false register.
271    void
272    reset()
273    {
274        container.fill(false);
275    }
276
277    /// Sets the predicate register to an all-true value.
278    void
279    set()
280    {
281        container.fill(true);
282    }
283
284    /// Equality operator, required to compare thread contexts.
285    template<size_t N2, bool P2>
286    inline bool
287    operator==(const VecPredRegContainer<N2, P2>& that) const
288    {
289        return NumBits == N2 && Packed == P2 && container == that.container;
290    }
291
292    /// Inequality operator, required to compare thread contexts.
293    template<size_t N2, bool P2>
294    bool
295    operator!=(const VecPredRegContainer<N2, P2>& that) const
296    {
297        return !operator==(that);
298    }
299
300    /// Returns a reference to a specific element of the internal container.
301    bool& operator[](size_t idx) { return container[idx]; }
302
303    /// Returns a const reference to a specific element of the internal
304    /// container.
305    const bool& operator[](size_t idx) const { return container[idx]; }
306
307    /// Returns a subset of bits starting from a specific element in the
308    /// container.
309    uint8_t
310    get_bits(size_t idx, uint8_t nbits) const
311    {
312        assert(nbits > 0 && nbits <= 8 && (idx + nbits - 1) < NumBits);
313        uint8_t v = 0;
314        idx = idx + nbits - 1;
315        for (int i = 0; i < nbits; ++i, --idx) {
316            v <<= 1;
317            v |= container[idx];
318        }
319        return v;
320    }
321
322    /// Set a subset of bits starting from a specific element in the
323    /// container.
324    void
325    set_bits(size_t idx, uint8_t nbits, uint8_t bval)
326    {
327        assert(nbits > 0 && nbits <= 8 && (idx + nbits - 1) < NumBits);
328        for (int i = 0; i < nbits; ++i, ++idx) {
329            container[idx] = bval & 1;
330            bval >>= 1;
331        }
332    }
333
334    /// Returns a string representation of the register content.
335    const std::string print() const { return csprintf("%s", *this); }
336
337    friend std::ostream&
338    operator<<(std::ostream& os, const MyClass& v)
339    {
340        for (auto b: v.container) {
341            os << csprintf("%d", b);
342        }
343        return os;
344    }
345
346    /// Create a view of this container.
347    ///
348    /// If NumElems is provided, the size of the container is bounds-checked,
349    /// otherwise the size is inferred from the container size.
350    /// @tparam VecElem Type of the vector elements.
351    /// @tparam NumElems Number of vector elements making up the view.
352    /// @{
353    template <typename VecElem,
354              size_t NumElems = (Packed ? NumBits : NumBits / sizeof(VecElem))>
355    VecPredRegT<VecElem, NumElems, Packed, true> as() const
356    {
357        static_assert((Packed && NumElems <= NumBits) ||
358                      (!Packed &&
359                       NumBits % sizeof(VecElem) == 0 &&
360                       sizeof(VecElem) * NumElems <= NumBits),
361                      "Container size incompatible with view size");
362        return VecPredRegT<VecElem, NumElems, Packed, true>(*this);
363    }
364
365    template <typename VecElem,
366              size_t NumElems = (Packed ? NumBits : NumBits / sizeof(VecElem))>
367    VecPredRegT<VecElem, NumElems, Packed, false> as()
368    {
369        static_assert((Packed && NumElems <= NumBits) ||
370                      (!Packed &&
371                       NumBits % sizeof(VecElem) == 0 &&
372                       sizeof(VecElem) * NumElems <= NumBits),
373                      "Container size incompatible with view size");
374        return VecPredRegT<VecElem, NumElems, Packed, false>(*this);
375    }
376    /// @}
377};
378
379/// Helper functions used for serialization/de-serialization
380template <size_t NumBits, bool Packed>
381inline bool
382to_number(const std::string& value, VecPredRegContainer<NumBits, Packed>& p)
383{
384    int i = 0;
385    for (const auto& c: value) {
386        p[i] = (c == '1');
387    }
388    return true;
389}
390
391/// Dummy type aliases and constants for architectures that do not implement
392/// vector predicate registers.
393/// @{
394constexpr bool DummyVecPredRegHasPackedRepr = false;
395using DummyVecPredReg = VecPredRegT<DummyVecElem, DummyNumVecElemPerVecReg,
396                                    DummyVecPredRegHasPackedRepr, false>;
397using DummyConstVecPredReg = VecPredRegT<DummyVecElem,
398                                         DummyNumVecElemPerVecReg,
399                                         DummyVecPredRegHasPackedRepr, true>;
400using DummyVecPredRegContainer = DummyVecPredReg::Container;
401constexpr size_t DummyVecPredRegSizeBits = 8;
402/// @}
403
404#endif  // __ARCH_GENERIC_VEC_PRED_REG_HH__
405