// Copyright (c) 2017 ARM Limited // All rights reserved // // The license below extends only to copyright in the software and shall // not be construed as granting a license to any other intellectual // property including but not limited to intellectual property relating // to a hardware implementation of the functionality of the software // licensed hereunder. You may use the software subject to the license // terms below provided that you ensure that this notice is replicated // unmodified and in its entirety in all distributions of the software, // modified or unmodified, in source code or in binary form. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer; // redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution; // neither the name of the copyright holders nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Authors: Giacomo Gabrielli // Rekai Gonzalez // Javier Setoain #ifndef __ARCH_GENERIC_VEC_PRED_REG_HH__ #define __ARCH_GENERIC_VEC_PRED_REG_HH__ #include #include #include #include "arch/generic/vec_reg.hh" #include "base/cprintf.hh" template class VecPredRegContainer; /// Predicate register view. /// /// This generic class implements the View in an MVC pattern, similarly to /// @see VecRegT. Since predicates are mainly used in conjunction with vectors /// to specify which lanes are active in a vector operation, the class is /// templated on the vector element type to simplify ISA definitions. /// @tparam VecElem Type of the vector elements. /// @tparam NumElems Number of vector elements making up the view. /// @tparam Packed True if the predicate register relies on a packed /// representation, i.e. adjacent bits refer to different vector elements /// irrespective of the vector element size (e.g. this is the case for /// AVX-512). If false, the predicate register relies on an unpacked /// representation, where each bit refers to the corresponding byte in a vector /// register (e.g. this is the case for ARM SVE). /// @tparam Const True if the underlying container can be modified through /// the view. template class VecPredRegT { protected: /// Size of the register in bits. static constexpr size_t NUM_BITS = Packed ? NumElems : sizeof(VecElem) * NumElems; public: /// Container type alias. using Container = typename std::conditional< Const, const VecPredRegContainer, VecPredRegContainer>::type; protected: // Alias for this type using MyClass = VecPredRegT; /// Container corresponding to this view. Container& container; public: VecPredRegT(Container& c) : container(c) {} /// Reset the register to an all-false value. template typename std::enable_if::type reset() { container.reset(); } /// Reset the register to an all-true value. template typename std::enable_if::type set() { container.set(); } template typename std::enable_if::type operator=(const MyClass& that) { container = that.container; return *this; } const bool& operator[](size_t idx) const { return container[idx * (Packed ? 1 : sizeof(VecElem))]; } template typename std::enable_if::type operator[](size_t idx) { return container[idx * (Packed ? 1 : sizeof(VecElem))]; } /// Return an element of the predicate register as it appears /// in the raw (untyped) internal representation uint8_t get_raw(size_t idx) const { return container.get_bits(idx * (Packed ? 1 : sizeof(VecElem)), (Packed ? 1 : sizeof(VecElem))); } /// Write a raw value in an element of the predicate register template typename std::enable_if::type set_raw(size_t idx, uint8_t val) { container.set_bits(idx * (Packed ? 1 : sizeof(VecElem)), (Packed ? 1 : sizeof(VecElem)), val); } /// Equality operator, required to compare thread contexts. template bool operator==(const VecPredRegT& that) const { return container == that.container; } /// Inequality operator, required to compare thread contexts. template bool operator!=(const VecPredRegT& that) const { return !operator==(that); } friend std::ostream& operator<<(std::ostream& os, const MyClass& p) { // 0-sized is not allowed os << '[' << p.container[0]; for (int i = 0; i < p.NUM_BITS; ++i) { os << " " << (p.container[i] ? 1 : 0); } os << ']'; return os; } /// Returns a string representation of the register content. const std::string print() const { return csprintf("%s", *this); } /// Returns true if the first active element of the register is true. /// @param mask Input mask used to filter the predicates to be tested. /// @param actual_num_elems Actual number of vector elements considered for /// the test (corresponding to the current vector length). template bool firstActive(const VecPredRegT& mask, size_t actual_num_elems) const { assert(actual_num_elems <= NumElems); for (int i = 0; i < actual_num_elems; ++i) { if (mask[i]) { return (*this)[i]; } } return false; } /// Returns true if there are no active elements in the register. /// @param mask Input mask used to filter the predicates to be tested. /// @param actual_num_elems Actual number of vector elements considered for /// the test (corresponding to the current vector length). template bool noneActive(const VecPredRegT& mask, size_t actual_num_elems) const { assert(actual_num_elems <= NumElems); for (int i = 0; i < actual_num_elems; ++i) { if (mask[i] && operator[](i)) { return false; } } return true; } /// Returns true if the last active element of the register is true. /// @param mask Input mask used to filter the predicates to be tested. /// @param actual_num_elems Actual number of vector elements considered for /// the test (corresponding to the current vector length). template bool lastActive(const VecPredRegT& mask, size_t actual_num_elems) const { assert(actual_num_elems <= NumElems); for (int i = actual_num_elems - 1; i >= 0; --i) { if (mask[i]) { return operator[](i); } } return false; } }; /// Generic predicate register container. /// /// This generic class implements the Model in an MVC pattern, similarly to /// @see VecRegContainer. /// @tparam NumBits Size of the container in bits. /// @tparam Packed See @VecRegT. template class VecPredRegContainer { static_assert(NumBits > 0, "Size of a predicate register must be > 0"); public: static constexpr size_t NUM_BITS = NumBits; using Container = std::array; private: Container container; // Alias for this type using MyClass = VecPredRegContainer; public: VecPredRegContainer() {} VecPredRegContainer(const VecPredRegContainer &) = default; MyClass& operator=(const MyClass& that) { if (&that == this) return *this; container = that.container; return *this; } /// Required for de-serialization. MyClass& operator=(const std::vector& that) { assert(that.size() == NUM_BITS); std::copy(that.begin(), that.end(), container.begin()); return *this; } /// Resets the predicate register to an all-false register. void reset() { container.fill(false); } /// Sets the predicate register to an all-true value. void set() { container.fill(true); } /// Equality operator, required to compare thread contexts. template inline bool operator==(const VecPredRegContainer& that) const { return NumBits == N2 && Packed == P2 && container == that.container; } /// Inequality operator, required to compare thread contexts. template bool operator!=(const VecPredRegContainer& that) const { return !operator==(that); } /// Returns a reference to a specific element of the internal container. bool& operator[](size_t idx) { return container[idx]; } /// Returns a const reference to a specific element of the internal /// container. const bool& operator[](size_t idx) const { return container[idx]; } /// Returns a subset of bits starting from a specific element in the /// container. uint8_t get_bits(size_t idx, uint8_t nbits) const { assert(nbits > 0 && nbits <= 8 && (idx + nbits - 1) < NumBits); uint8_t v = 0; idx = idx + nbits - 1; for (int i = 0; i < nbits; ++i, --idx) { v <<= 1; v |= container[idx]; } return v; } /// Set a subset of bits starting from a specific element in the /// container. void set_bits(size_t idx, uint8_t nbits, uint8_t bval) { assert(nbits > 0 && nbits <= 8 && (idx + nbits - 1) < NumBits); for (int i = 0; i < nbits; ++i, ++idx) { container[idx] = bval & 1; bval >>= 1; } } /// Returns a string representation of the register content. const std::string print() const { return csprintf("%s", *this); } friend std::ostream& operator<<(std::ostream& os, const MyClass& v) { for (auto b: v.container) { os << csprintf("%d", b); } return os; } /// Create a view of this container. /// /// If NumElems is provided, the size of the container is bounds-checked, /// otherwise the size is inferred from the container size. /// @tparam VecElem Type of the vector elements. /// @tparam NumElems Number of vector elements making up the view. /// @{ template VecPredRegT as() const { static_assert((Packed && NumElems <= NumBits) || (!Packed && NumBits % sizeof(VecElem) == 0 && sizeof(VecElem) * NumElems <= NumBits), "Container size incompatible with view size"); return VecPredRegT(*this); } template VecPredRegT as() { static_assert((Packed && NumElems <= NumBits) || (!Packed && NumBits % sizeof(VecElem) == 0 && sizeof(VecElem) * NumElems <= NumBits), "Container size incompatible with view size"); return VecPredRegT(*this); } /// @} }; /// Helper functions used for serialization/de-serialization template inline bool to_number(const std::string& value, VecPredRegContainer& p) { int i = 0; for (const auto& c: value) { p[i] = (c == '1'); } return true; } /// Dummy type aliases and constants for architectures that do not implement /// vector predicate registers. /// @{ constexpr bool DummyVecPredRegHasPackedRepr = false; using DummyVecPredReg = VecPredRegT; using DummyConstVecPredReg = VecPredRegT; using DummyVecPredRegContainer = DummyVecPredReg::Container; constexpr size_t DummyVecPredRegSizeBits = 8; /// @} #endif // __ARCH_GENERIC_VEC_PRED_REG_HH__