blob: aab3ac7e49852b817c96ef81b616204520af2a28 [file]
/*
* Copyright (c) 2015-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
* Nathanael Premillieu
* Rekai Gonzalez
*/
/* Vector Registers layout specification
*
* This register type is to be used to model the SIMD registers.
* It takes into account the possibility that different architectural names
* may overlap (like for ARMv8 AArch32 for example).
*/
#ifndef __ARCH_GENERIC_VEC_REG_HH__
#define __ARCH_GENERIC_VEC_REG_HH__
#include <assert.h>
#include <array>
#include <iostream>
#include <string>
#include <type_traits>
#include <vector>
#include "base/cprintf.hh"
template <size_t Sz>
class VecRegContainer;
/** Vector Register Abstraction
* This generic class is a view in a particularization of MVC, to vector
* registers. There is a VecRegContainer that implements the model, and
* contains the data. To that model we can interpose different instantiations
* of VecRegT to view the container as a vector of NumElems elems of type
* VecElem.
* @tparam VecElem Type of each element of the vector.
* @tparam NumElems Amount of components of the vector.
* @tparam Const Indicate if the underlying container can be modified through
* the view.
*/
template <typename VecElem, size_t NumElems, bool Const>
class VecRegT
{
/** Size of the register in bytes. */
static constexpr size_t SIZE = sizeof(VecElem) * NumElems;
public:
/** Container type alias. */
using Container = typename std::conditional<Const,
const VecRegContainer<SIZE>,
VecRegContainer<SIZE>>::type;
private:
/** My type alias. */
using MyClass = VecRegT<VecElem, NumElems, Const>;
/** Reference to container. */
Container& container;
public:
/** Constructor. */
VecRegT(Container& cnt) : container(cnt) {};
/** Zero the container. */
template<bool Condition = !Const>
typename std::enable_if<Condition, void>::type
zero() { container.zero(); }
template<bool Condition = !Const>
typename std::enable_if<Condition, MyClass&>::type
operator=(const MyClass& that)
{
container = that.container;
return *this;
}
/** Index operator. */
const VecElem& operator[](size_t idx) const
{
return container.template raw_ptr<VecElem>()[idx];
}
/** Index operator. */
template<bool Condition = !Const>
typename std::enable_if<Condition, VecElem&>::type
operator[](size_t idx)
{
return container.template raw_ptr<VecElem>()[idx];
}
/** Equality operator.
* Required to compare thread contexts.
*/
template<typename VE2, size_t NE2, bool C2>
bool
operator==(const VecRegT<VE2, NE2, C2>& that) const
{
return container == that.container;
}
/** Inequality operator.
* Required to compare thread contexts.
*/
template<typename VE2, size_t NE2, bool C2>
bool
operator!=(const VecRegT<VE2, NE2, C2>& that) const
{
return !operator==(that);
}
/** Output stream operator. */
friend std::ostream&
operator<<(std::ostream& os, const MyClass& vr)
{
/* 0-sized is not allowed */
os << "[" << std::hex << (uint32_t)vr[0];
for (uint32_t e = 1; e < vr.SIZE; e++)
os << " " << std::hex << (uint32_t)vr[e];
os << ']';
return os;
}
const std::string print() const { return csprintf("%s", *this); }
/**
* Cast to VecRegContainer&
* It is useful to get the reference to the container for ISA tricks,
* because casting to reference prevents unnecessary copies.
*/
operator Container&() { return container; }
};
/* Forward declaration. */
template <typename VecElem, bool Const>
class VecLaneT;
/**
* Vector Register Abstraction
* This generic class is the model in a particularization of MVC, to vector
* registers. The model has functionality to create views of itself, or a
* portion through the method 'as
* @tparam Sz Size of the container in bytes.
*/
template <size_t Sz>
class VecRegContainer
{
static_assert(Sz > 0,
"Cannot create Vector Register Container of zero size");
public:
static constexpr size_t SIZE = Sz;
using Container = std::array<uint8_t,Sz>;
private:
Container container;
using MyClass = VecRegContainer<SIZE>;
public:
VecRegContainer() {}
/* This is required for de-serialisation. */
VecRegContainer(const std::vector<uint8_t>& that)
{
assert(that.size() >= SIZE);
std::memcpy(container.data(), &that[0], SIZE);
}
/** Zero the container. */
void zero() { memset(container.data(), 0, SIZE); }
/** Assignment operators. */
/** @{ */
/** From VecRegContainer */
MyClass& operator=(const MyClass& that)
{
if (&that == this)
return *this;
memcpy(container.data(), that.container.data(), SIZE);
return *this;
}
/** From appropriately sized uint8_t[]. */
MyClass& operator=(const Container& that)
{
std::memcpy(container.data(), that.data(), SIZE);
return *this;
}
/** From vector<uint8_t>.
* This is required for de-serialisation.
* */
MyClass& operator=(const std::vector<uint8_t>& that)
{
assert(that.size() >= SIZE);
std::memcpy(container.data(), that.data(), SIZE);
return *this;
}
/** @} */
/** Copy the contents into the input buffer. */
/** @{ */
/** To appropriately sized uint8_t[] */
void copyTo(Container& dst) const
{
std::memcpy(dst.data(), container.data(), SIZE);
}
/** To vector<uint8_t>
* This is required for serialisation.
* */
void copyTo(std::vector<uint8_t>& dst) const
{
dst.resize(SIZE);
std::memcpy(dst.data(), container.data(), SIZE);
}
/** @} */
/** Equality operator.
* Required to compare thread contexts.
*/
template<size_t S2>
inline bool
operator==(const VecRegContainer<S2>& that) const
{
return SIZE == S2 && !memcmp(container.data(), that.container.data(), SIZE);
}
/** Inequality operator.
* Required to compare thread contexts.
*/
template<size_t S2>
bool
operator!=(const VecRegContainer<S2>& that) const
{
return !operator==(that);
}
const std::string print() const { return csprintf("%s", *this); }
/** Get pointer to bytes. */
template <typename Ret>
const Ret* raw_ptr() const { return (const Ret*)container.data(); }
template <typename Ret>
Ret* raw_ptr() { return (Ret*)container.data(); }
/**
* View interposers.
* Create a view of this container as a vector of VecElems with an
* optional amount of elements. If the amount of elements is provided,
* the size of the container is checked, to test bounds. If it is not
* provided, the length is inferred from the container size and the
* element size.
* @tparam VecElem Type of each element of the vector for the view.
* @tparam NumElem Amount of elements in the view.
*/
/** @{ */
template <typename VecElem, size_t NumElems = SIZE/sizeof(VecElem)>
VecRegT<VecElem, NumElems, true> as() const
{
static_assert(SIZE % sizeof(VecElem) == 0,
"VecElem does not evenly divide the register size");
static_assert(sizeof(VecElem) * NumElems <= SIZE,
"Viewing VecReg as something bigger than it is");
return VecRegT<VecElem, NumElems, true>(*this);
}
template <typename VecElem, size_t NumElems = SIZE/sizeof(VecElem)>
VecRegT<VecElem, NumElems, false> as()
{
static_assert(SIZE % sizeof(VecElem) == 0,
"VecElem does not evenly divide the register size");
static_assert(sizeof(VecElem) * NumElems <= SIZE,
"Viewing VecReg as something bigger than it is");
return VecRegT<VecElem, NumElems, false>(*this);
}
template <typename VecElem, int LaneIdx>
VecLaneT<VecElem, false> laneView();
template <typename VecElem, int LaneIdx>
VecLaneT<VecElem, true> laneView() const;
template <typename VecElem>
VecLaneT<VecElem, false> laneView(int laneIdx);
template <typename VecElem>
VecLaneT<VecElem, true> laneView(int laneIdx) const;
/** @} */
/**
* Output operator.
* Used for serialization.
*/
friend std::ostream& operator<<(std::ostream& os, const MyClass& v)
{
os << "0x";
for (auto& b: v.container) {
os << csprintf("%02x", b);
}
return os;
}
};
enum class LaneSize
{
Empty = 0,
Byte,
TwoByte,
FourByte,
EightByte,
};
template <LaneSize LS>
class LaneData
{
public:
/** Alias to the native type of the appropriate size. */
using UnderlyingType =
typename std::conditional<LS == LaneSize::EightByte, uint64_t,
typename std::conditional<LS == LaneSize::FourByte, uint32_t,
typename std::conditional<LS == LaneSize::TwoByte, uint16_t,
typename std::conditional<LS == LaneSize::Byte, uint8_t,
void>::type
>::type
>::type
>::type;
private:
static constexpr auto ByteSz = sizeof(UnderlyingType);
UnderlyingType _val;
using MyClass = LaneData<LS>;
public:
template <typename T> explicit
LaneData(typename std::enable_if<sizeof(T) == ByteSz, const T&>::type t)
: _val(t) {}
template <typename T>
typename std::enable_if<sizeof(T) == ByteSz, MyClass&>::type
operator=(const T& that)
{
_val = that;
return *this;
}
template<typename T,
typename std::enable_if<sizeof(T) == ByteSz, int>::type I = 0>
operator T() const {
return *static_cast<const T*>(&_val);
}
};
/** Output operator overload for LaneData<Size>. */
template <LaneSize LS>
inline std::ostream&
operator<<(std::ostream& os, const LaneData<LS>& d)
{
return os << static_cast<typename LaneData<LS>::UnderlyingType>(d);
}
/** Vector Lane abstraction
* Another view of a container. This time only a partial part of it is exposed.
* @tparam VecElem Type of each element of the vector.
* @tparam Const Indicate if the underlying container can be modified through
* the view.
*/
/** @{ */
/* General */
template <typename VecElem, bool Const>
class VecLaneT
{
public:
/** VecRegContainer friendship to access private VecLaneT constructors.
* Only VecRegContainers can build VecLanes.
*/
/** @{ */
template <size_t Sz>
template <typename VE>
friend VecLaneT<VE, true> VecRegContainer<Sz>::template laneView(int) const;
template <size_t Sz>
template <typename VE>
friend VecLaneT<VE, false> VecRegContainer<Sz>::template laneView(int);
template <size_t Sz>
template <typename VE, int LaneIdx>
friend VecLaneT<VE, true> VecRegContainer<Sz>::template laneView() const;
template <size_t Sz>
template <typename VE, int LaneIdx>
friend VecLaneT<VE, false> VecRegContainer<Sz>::template laneView();
/** My type alias. */
using MyClass = VecLaneT<VecElem, Const>;
private:
using Cont = typename std::conditional<Const,
const VecElem,
VecElem>::type;
static_assert(!std::is_const<VecElem>::value || Const,
"Asked for non-const lane of const type!");
static_assert(std::is_integral<VecElem>::value, "VecElem type is not integral!");
/** Reference to data. */
Cont& container;
/** Constructor */
VecLaneT(Cont& cont) : container(cont) { }
public:
/** Assignment operators.
* Assignment operators are only enabled if the underlying container is
* non-constant.
*/
/** @{ */
template <bool Assignable = !Const>
typename std::enable_if<Assignable, MyClass&>::type
operator=(const VecElem& that) {
container = that;
return *this;
}
/**
* Generic.
* Generic bitwise assignment. Narrowing and widening assignemnts are
* not allowed, pre-treatment of the rhs is required to conform.
*/
template <bool Assignable = !Const, typename T>
typename std::enable_if<Assignable, MyClass&>::type
operator=(const T& that) {
static_assert(sizeof(T) >= sizeof(VecElem),
"Attempt to perform widening bitwise copy.");
static_assert(sizeof(T) <= sizeof(VecElem),
"Attempt to perform narrowing bitwise copy.");
container = static_cast<VecElem>(that);
return *this;
}
/** @} */
/** Cast to vecElem. */
operator VecElem() const { return container; }
/** Constification. */
template <bool Cond = !Const, typename std::enable_if<Cond, int>::type = 0>
operator VecLaneT<VecElem, true>()
{
return VecLaneT<VecElem, true>(container);
}
};
namespace std {
template<>
template<typename T, bool Const>
struct add_const<VecLaneT<T, Const>> { typedef VecLaneT<T, true> type; };
}
/** View as the Nth lane of type VecElem. */
template <size_t Sz>
template <typename VecElem, int LaneIdx>
VecLaneT<VecElem, false>
VecRegContainer<Sz>::laneView()
{
return VecLaneT<VecElem, false>(as<VecElem>()[LaneIdx]);
}
/** View as the const Nth lane of type VecElem. */
template <size_t Sz>
template <typename VecElem, int LaneIdx>
VecLaneT<VecElem, true>
VecRegContainer<Sz>::laneView() const
{
return VecLaneT<VecElem, true>(as<VecElem>()[LaneIdx]);
}
/** View as the Nth lane of type VecElem. */
template <size_t Sz>
template <typename VecElem>
VecLaneT<VecElem, false>
VecRegContainer<Sz>::laneView(int laneIdx)
{
return VecLaneT<VecElem, false>(as<VecElem>()[laneIdx]);
}
/** View as the const Nth lane of type VecElem. */
template <size_t Sz>
template <typename VecElem>
VecLaneT<VecElem, true>
VecRegContainer<Sz>::laneView(int laneIdx) const
{
return VecLaneT<VecElem, true>(as<VecElem>()[laneIdx]);
}
using VecLane8 = VecLaneT<uint8_t, false>;
using VecLane16 = VecLaneT<uint16_t, false>;
using VecLane32 = VecLaneT<uint32_t, false>;
using VecLane64 = VecLaneT<uint64_t, false>;
using ConstVecLane8 = VecLaneT<uint8_t, true>;
using ConstVecLane16 = VecLaneT<uint16_t, true>;
using ConstVecLane32 = VecLaneT<uint32_t, true>;
using ConstVecLane64 = VecLaneT<uint64_t, true>;
/**
* Calls required for serialization/deserialization
*/
/** @{ */
template <size_t Sz>
inline bool
to_number(const std::string& value, VecRegContainer<Sz>& v)
{
assert(value[0] == '0' && value[1] == 'x');
assert(value.size() <= 2 * (sizeof(v) + 1));
int i = 1; // skip the 0x prefix
while ((i<<1) < value.size()) {
std::string byte = value.substr(i<<1, 2);
v.template raw_ptr<uint8_t>()[i] = stoul(byte, 0, 16);
i++;
}
return true;
}
/** @} */
#endif /* __ARCH_GENERIC_VEC_REG_HH__ */