Now that we're using c++17, the type_traits with a ::value member have a _v alias which reduces verbosity. Or on other words std::is_integral<T>::value can be replaced with std::is_integral_v<T> Make this substitution throughout the code base. In places where gem5 introduced it's own similar templates, add a V alias, spelled differently to match gem5's internal style. gem5: :IsVarArgs<T>::value => gem5::IsVarArgsV<T> Change-Id: I1d84ffc4a236ad699471569e7916ec17fe5f109a Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/48604 Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br> Maintainer: Bobby R. Bruce <bbruce@ucdavis.edu> Tested-by: kokoro <noreply+kokoro@google.com>
629 lines
18 KiB
C++
629 lines
18 KiB
C++
/*
|
|
* Copyright (c) 2007-2008 The Regents of The University of Michigan
|
|
* All rights reserved.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef __BASE_BITUNION_HH__
|
|
#define __BASE_BITUNION_HH__
|
|
|
|
#include <functional>
|
|
#include <iostream>
|
|
#include <type_traits>
|
|
#include <typeinfo>
|
|
|
|
#include "base/bitfield.hh"
|
|
#include "base/compiler.hh"
|
|
#include "sim/serialize_handlers.hh"
|
|
|
|
namespace gem5
|
|
{
|
|
|
|
// The following implements the BitUnion system of defining bitfields
|
|
//on top of an underlying class. This is done through the pervasive use of
|
|
//both named and unnamed unions which all contain the same actual storage.
|
|
//Since they're unioned with each other, all of these storage locations
|
|
//overlap. This allows all of the bitfields to manipulate the same data
|
|
//without having to have access to each other. More details are provided with
|
|
//the individual components.
|
|
|
|
//This class wraps around another which defines getter/setter functions which
|
|
//manipulate the underlying data. The type of the underlying data and the type
|
|
//of the bitfield itself are inferred from the argument types of the setter
|
|
//function.
|
|
template<class Base>
|
|
class BitfieldTypeImpl : public Base
|
|
{
|
|
static_assert(std::is_empty_v<Base>, "Bitfield base class must be empty.");
|
|
|
|
private:
|
|
|
|
struct TypeDeducer
|
|
{
|
|
template<typename>
|
|
struct T;
|
|
|
|
template<typename C, typename Type1, typename Type2>
|
|
struct T<void (C::*)(Type1 &, Type2)>
|
|
{
|
|
typedef Type1 Storage;
|
|
typedef Type2 Type;
|
|
};
|
|
|
|
struct Wrapper : public Base
|
|
{
|
|
using Base::setter;
|
|
};
|
|
|
|
typedef typename T<decltype(&Wrapper::setter)>::Storage Storage;
|
|
typedef typename T<decltype(&Wrapper::setter)>::Type Type;
|
|
};
|
|
|
|
protected:
|
|
typedef typename TypeDeducer::Storage Storage;
|
|
typedef typename TypeDeducer::Type Type;
|
|
|
|
Type getter(const Storage &storage) const = delete;
|
|
void setter(Storage &storage, Type val) = delete;
|
|
|
|
BitfieldTypeImpl() = default;
|
|
BitfieldTypeImpl(const BitfieldTypeImpl &) = default;
|
|
|
|
Storage __storage;
|
|
|
|
operator Type () const
|
|
{
|
|
return Base::getter(__storage);
|
|
}
|
|
|
|
Type
|
|
operator=(const Type val)
|
|
{
|
|
Base::setter(__storage, val);
|
|
return val;
|
|
}
|
|
|
|
Type
|
|
operator=(BitfieldTypeImpl<Base> const & other)
|
|
{
|
|
return *this = (Type)other;
|
|
}
|
|
};
|
|
|
|
//A wrapper for the above class which allows setting and getting.
|
|
template<class Base>
|
|
class BitfieldType : public BitfieldTypeImpl<Base>
|
|
{
|
|
protected:
|
|
using Impl = BitfieldTypeImpl<Base>;
|
|
using typename Impl::Type;
|
|
|
|
public:
|
|
BitfieldType() = default;
|
|
BitfieldType(const BitfieldType &) = default;
|
|
|
|
operator Type () const { return Impl::operator Type(); }
|
|
|
|
Type operator=(const Type val) { return Impl::operator=(val); }
|
|
Type
|
|
operator=(BitfieldType<Base> const & other)
|
|
{
|
|
return Impl::operator=(other);
|
|
}
|
|
};
|
|
|
|
//A wrapper which only supports getting.
|
|
template<class Base>
|
|
class BitfieldROType : public BitfieldTypeImpl<Base>
|
|
{
|
|
public:
|
|
using Impl = BitfieldTypeImpl<Base>;
|
|
using typename Impl::Type;
|
|
|
|
BitfieldROType() = default;
|
|
BitfieldROType(const BitfieldROType &) = default;
|
|
|
|
Type operator=(BitfieldROType<Base> const &other) = delete;
|
|
operator Type () const { return Impl::operator Type(); }
|
|
};
|
|
|
|
//A wrapper which only supports setting.
|
|
template <class Base>
|
|
class BitfieldWOType : public BitfieldTypeImpl<Base>
|
|
{
|
|
protected:
|
|
using Impl = BitfieldTypeImpl<Base>;
|
|
using typename Impl::Type;
|
|
|
|
public:
|
|
BitfieldWOType() = default;
|
|
BitfieldWOType(const BitfieldWOType &) = default;
|
|
|
|
Type operator=(const Type val) { return Impl::operator=(val); }
|
|
Type
|
|
operator=(BitfieldWOType<Base> const & other)
|
|
{
|
|
return Impl::operator=(other);
|
|
}
|
|
};
|
|
|
|
//This namespace is for classes which implement the backend of the BitUnion
|
|
//stuff. Don't use any of these directly.
|
|
namespace bitfield_backend
|
|
{
|
|
template<class Storage, int first, int last>
|
|
class Unsigned
|
|
{
|
|
static_assert(first >= last,
|
|
"Bitfield ranges must be specified as <msb, lsb>");
|
|
|
|
protected:
|
|
uint64_t
|
|
getter(const Storage &storage) const
|
|
{
|
|
return bits(storage, first, last);
|
|
}
|
|
|
|
void
|
|
setter(Storage &storage, uint64_t val)
|
|
{
|
|
replaceBits(storage, first, last, val);
|
|
}
|
|
};
|
|
|
|
template<class Storage, int first, int last>
|
|
class Signed
|
|
{
|
|
static_assert(first >= last,
|
|
"Bitfield ranges must be specified as <msb, lsb>");
|
|
|
|
protected:
|
|
int64_t
|
|
getter(const Storage &storage) const
|
|
{
|
|
return sext<first - last + 1>(bits(storage, first, last));
|
|
}
|
|
|
|
void
|
|
setter(Storage &storage, int64_t val)
|
|
{
|
|
replaceBits(storage, first, last, val);
|
|
}
|
|
};
|
|
|
|
//This class contains the basic bitfield types which are automatically
|
|
//available within a BitUnion. They inherit their Storage type from the
|
|
//containing BitUnion.
|
|
template<class Storage>
|
|
class BitfieldTypes
|
|
{
|
|
protected:
|
|
|
|
template<int first, int last=first>
|
|
using Bitfield = BitfieldType<Unsigned<Storage, first, last> >;
|
|
template<int first, int last=first>
|
|
using BitfieldRO =
|
|
BitfieldROType<Unsigned<Storage, first, last> >;
|
|
template<int first, int last=first>
|
|
using BitfieldWO =
|
|
BitfieldWOType<Unsigned<Storage, first, last> >;
|
|
|
|
template<int first, int last=first>
|
|
using SignedBitfield =
|
|
BitfieldType<Signed<Storage, first, last> >;
|
|
template<int first, int last=first>
|
|
using SignedBitfieldRO =
|
|
BitfieldROType<Signed<Storage, first, last> >;
|
|
template<int first, int last=first>
|
|
using SignedBitfieldWO =
|
|
BitfieldWOType<Signed<Storage, first, last> >;
|
|
};
|
|
|
|
//When a BitUnion is set up, an underlying class is created which holds
|
|
//the actual union. This class then inherits from it, and provids the
|
|
//implementations for various operators. Setting things up this way
|
|
//prevents having to redefine these functions in every different BitUnion
|
|
//type. More operators could be implemented in the future, as the need
|
|
//arises.
|
|
template <class Base>
|
|
class BitUnionOperators : public Base
|
|
{
|
|
static_assert(sizeof(Base) == sizeof(typename Base::__StorageType),
|
|
"BitUnion larger than its storage type.");
|
|
|
|
public:
|
|
BitUnionOperators(typename Base::__StorageType const &val)
|
|
{
|
|
Base::__storage = val;
|
|
}
|
|
|
|
BitUnionOperators(const BitUnionOperators &) = default;
|
|
|
|
BitUnionOperators() {}
|
|
|
|
//Conversion operators.
|
|
operator const typename Base::__StorageType () const
|
|
{
|
|
return Base::__storage;
|
|
}
|
|
|
|
//Basic assignment operators.
|
|
BitUnionOperators &
|
|
operator=(typename Base::__StorageType const &val)
|
|
{
|
|
Base::__storage = val;
|
|
return *this;
|
|
}
|
|
|
|
BitUnionOperators &
|
|
operator=(BitUnionOperators const &other)
|
|
{
|
|
return operator=(other.__storage);
|
|
}
|
|
|
|
//Increment and decrement operators.
|
|
BitUnionOperators &
|
|
operator++()
|
|
{
|
|
Base::__storage++;
|
|
return *this;
|
|
}
|
|
|
|
BitUnionOperators
|
|
operator++(int)
|
|
{
|
|
BitUnionOperators ret = *this;
|
|
operator++();
|
|
return ret;
|
|
}
|
|
|
|
BitUnionOperators &
|
|
operator--()
|
|
{
|
|
Base::__storage--;
|
|
return *this;
|
|
}
|
|
|
|
BitUnionOperators
|
|
operator--(int)
|
|
{
|
|
BitUnionOperators ret = *this;
|
|
operator--();
|
|
return ret;
|
|
}
|
|
|
|
//Operation and assignment operators
|
|
BitUnionOperators &
|
|
operator+=(typename Base::__StorageType const &val)
|
|
{
|
|
Base::__storage += val;
|
|
return *this;
|
|
}
|
|
|
|
BitUnionOperators &
|
|
operator-=(typename Base::__StorageType const &val)
|
|
{
|
|
Base::__storage -= val;
|
|
return *this;
|
|
}
|
|
|
|
BitUnionOperators &
|
|
operator*=(typename Base::__StorageType const &val)
|
|
{
|
|
Base::__storage *= val;
|
|
return *this;
|
|
}
|
|
|
|
BitUnionOperators &
|
|
operator/=(typename Base::__StorageType const &val)
|
|
{
|
|
Base::__storage /= val;
|
|
return *this;
|
|
}
|
|
|
|
BitUnionOperators &
|
|
operator%=(typename Base::__StorageType const &val)
|
|
{
|
|
Base::__storage %= val;
|
|
return *this;
|
|
}
|
|
|
|
BitUnionOperators &
|
|
operator&=(typename Base::__StorageType const &val)
|
|
{
|
|
Base::__storage &= val;
|
|
return *this;
|
|
}
|
|
|
|
BitUnionOperators &
|
|
operator|=(typename Base::__StorageType const &val)
|
|
{
|
|
Base::__storage |= val;
|
|
return *this;
|
|
}
|
|
|
|
BitUnionOperators &
|
|
operator^=(typename Base::__StorageType const &val)
|
|
{
|
|
Base::__storage ^= val;
|
|
return *this;
|
|
}
|
|
|
|
BitUnionOperators &
|
|
operator<<=(typename Base::__StorageType const &val)
|
|
{
|
|
Base::__storage <<= val;
|
|
return *this;
|
|
}
|
|
|
|
BitUnionOperators &
|
|
operator>>=(typename Base::__StorageType const &val)
|
|
{
|
|
Base::__storage >>= val;
|
|
return *this;
|
|
}
|
|
};
|
|
} // namespace bitfield_backend
|
|
|
|
//This macro is a backend for other macros that specialize it slightly.
|
|
//First, it creates/extends a namespace "BitfieldUnderlyingClasses" and
|
|
//sticks the class which has the actual union in it, which
|
|
//BitfieldOperators above inherits from. Putting these classes in a special
|
|
//namespace ensures that there will be no collisions with other names as long
|
|
//as the BitUnion names themselves are all distinct and nothing else uses
|
|
//the BitfieldUnderlyingClasses namespace, which is unlikely. The class itself
|
|
//creates a typedef of the "type" parameter called __StorageType. This allows
|
|
//the type to propagate outside of the macro itself in a controlled way.
|
|
//Finally, the base storage is defined which BitfieldOperators will refer to
|
|
//in the operators it defines. This macro is intended to be followed by
|
|
//bitfield definitions which will end up inside it's union. As explained
|
|
//above, these is overlayed the __storage member in its entirety by each of the
|
|
//bitfields which are defined in the union, creating shared storage with no
|
|
//overhead.
|
|
#define __BitUnion(type, name) \
|
|
class BitfieldUnderlyingClasses##name : \
|
|
public gem5::bitfield_backend::BitfieldTypes<type> \
|
|
{ \
|
|
protected: \
|
|
typedef type __StorageType; \
|
|
friend gem5::bitfield_backend::BitUnionBaseType< \
|
|
gem5::bitfield_backend::BitUnionOperators< \
|
|
BitfieldUnderlyingClasses##name> >; \
|
|
friend gem5::bitfield_backend::BitUnionBaseType< \
|
|
BitfieldUnderlyingClasses##name>; \
|
|
public: \
|
|
union { \
|
|
type __storage;
|
|
|
|
/**
|
|
* This closes off the class and union started by the above macro. It is
|
|
* followed by a typedef which makes "name" refer to a BitfieldOperator
|
|
* class inheriting from the class and union just defined, which completes
|
|
* building up the type for the user.
|
|
*
|
|
* @ingroup api_bitunion
|
|
*/
|
|
#define EndBitUnion(name) \
|
|
}; \
|
|
}; \
|
|
typedef gem5::bitfield_backend::BitUnionOperators< \
|
|
BitfieldUnderlyingClasses##name> name;
|
|
|
|
//This sets up a bitfield which has other bitfields nested inside of it. The
|
|
//__storage member functions like the "underlying storage" of the top level
|
|
//BitUnion. Like everything else, it overlays with the top level storage, so
|
|
//making it a regular bitfield type makes the entire thing function as a
|
|
//regular bitfield when referred to by itself.
|
|
#define __SubBitUnion(name, fieldType, ...) \
|
|
class \
|
|
{ \
|
|
public: \
|
|
union { \
|
|
fieldType<__VA_ARGS__> __storage;
|
|
|
|
/**
|
|
* This closes off the union created above and gives it a name. Unlike the top
|
|
* level BitUnion, we're interested in creating an object instead of a type.
|
|
* The operators are defined in the macro itself instead of a class for
|
|
* technical reasons. If someone determines a way to move them to one, please
|
|
* do so.
|
|
*
|
|
* @ingroup api_bitunion
|
|
*/
|
|
#define EndSubBitUnion(name) \
|
|
}; \
|
|
inline operator __StorageType () const \
|
|
{ return __storage; } \
|
|
\
|
|
inline __StorageType operator = (const __StorageType & _storage) \
|
|
{ return __storage = _storage;} \
|
|
} name;
|
|
|
|
/**
|
|
* Regular bitfields
|
|
* These define macros for read/write regular bitfield based subbitfields.
|
|
*
|
|
* @ingroup api_bitunion
|
|
*/
|
|
#define SubBitUnion(name, first, last) \
|
|
__SubBitUnion(name, Bitfield, first, last)
|
|
|
|
/**
|
|
* Regular bitfields
|
|
* These define macros for read/write regular bitfield based subbitfields.
|
|
*
|
|
* @ingroup api_bitunion
|
|
*/
|
|
#define SignedSubBitUnion(name, first, last) \
|
|
__SubBitUnion(name, SignedBitfield, first, last)
|
|
|
|
/**
|
|
* Use this to define an arbitrary type overlayed with bitfields.
|
|
*
|
|
* @ingroup api_bitunion
|
|
*/
|
|
#define BitUnion(type, name) __BitUnion(type, name)
|
|
|
|
/**
|
|
* Use this to define conveniently sized values overlayed with bitfields.
|
|
*
|
|
* @ingroup api_bitunion
|
|
*/
|
|
#define BitUnion64(name) __BitUnion(uint64_t, name)
|
|
#define BitUnion32(name) __BitUnion(uint32_t, name)
|
|
#define BitUnion16(name) __BitUnion(uint16_t, name)
|
|
#define BitUnion8(name) __BitUnion(uint8_t, name)
|
|
|
|
|
|
//These templates make it possible to define other templates related to
|
|
//BitUnions without having to refer to internal typedefs or the
|
|
// bitfield_backend namespace.
|
|
|
|
//To build a template specialization which works for all BitUnions, accept a
|
|
//template argument T, and then use BitUnionType<T> as an argument in the
|
|
//template. To refer to the basic type the BitUnion wraps, use
|
|
//BitUnionBaseType<T>.
|
|
|
|
//For example:
|
|
//template <typename T>
|
|
//void func(BitUnionType<T> u) { BitUnionBaseType<T> b = u; }
|
|
|
|
//Also, BitUnionBaseType can be used on a BitUnion type directly.
|
|
|
|
/**
|
|
* @ingroup api_bitunion
|
|
*/
|
|
template <typename T>
|
|
using BitUnionType = bitfield_backend::BitUnionOperators<T>;
|
|
|
|
namespace bitfield_backend
|
|
{
|
|
template<typename T>
|
|
struct BitUnionBaseType
|
|
{
|
|
typedef typename BitUnionType<T>::__StorageType Type;
|
|
};
|
|
|
|
template<typename T>
|
|
struct BitUnionBaseType<BitUnionType<T> >
|
|
{
|
|
typedef typename BitUnionType<T>::__StorageType Type;
|
|
};
|
|
} // namespace bitfield_backend
|
|
|
|
/**
|
|
* @ingroup api_bitunion
|
|
*/
|
|
template <typename T>
|
|
using BitUnionBaseType = typename bitfield_backend::BitUnionBaseType<T>::Type;
|
|
|
|
namespace bitfield_backend
|
|
{
|
|
template<typename T>
|
|
static inline std::ostream &
|
|
bitfieldBackendPrinter(std::ostream &os, const T &t)
|
|
{
|
|
os << t;
|
|
return os;
|
|
}
|
|
|
|
//Since BitUnions are generally numerical values and not character codes,
|
|
//these specializations attempt to ensure that they get cast to integers
|
|
//of the appropriate type before printing.
|
|
template <>
|
|
inline std::ostream &
|
|
bitfieldBackendPrinter(std::ostream &os, const char &t)
|
|
{
|
|
os << (int)t;
|
|
return os;
|
|
}
|
|
|
|
template <>
|
|
inline std::ostream &
|
|
bitfieldBackendPrinter(std::ostream &os, const unsigned char &t)
|
|
{
|
|
os << (unsigned int)t;
|
|
return os;
|
|
}
|
|
} // namespace bitfield_backend
|
|
|
|
/**
|
|
* A default << operator which casts a bitunion to its underlying type and
|
|
* passes it to bitfield_backend::bitfieldBackendPrinter.
|
|
*
|
|
* @ingroup api_bitunion
|
|
*/
|
|
template <typename T>
|
|
std::ostream &
|
|
operator << (std::ostream &os, const BitUnionType<T> &bu)
|
|
{
|
|
return bitfield_backend::bitfieldBackendPrinter(
|
|
os, (BitUnionBaseType<T>)bu);
|
|
}
|
|
|
|
// Specialization for BitUnion types.
|
|
template <class T>
|
|
struct ParseParam<BitUnionType<T>>
|
|
{
|
|
static bool
|
|
parse(const std::string &s, BitUnionType<T> &value)
|
|
{
|
|
// Zero initialize storage to avoid leaking an uninitialized value
|
|
BitUnionBaseType<T> storage = BitUnionBaseType<T>();
|
|
auto res = to_number(s, storage);
|
|
value = storage;
|
|
return res;
|
|
}
|
|
};
|
|
|
|
template <class T>
|
|
struct ShowParam<BitUnionType<T>>
|
|
{
|
|
static void
|
|
show(std::ostream &os, const BitUnionType<T> &value)
|
|
{
|
|
ShowParam<BitUnionBaseType<T>>::show(
|
|
os, static_cast<const BitUnionBaseType<T> &>(value));
|
|
}
|
|
};
|
|
|
|
} // namespace gem5
|
|
|
|
//An STL style hash structure for hashing BitUnions based on their base type.
|
|
namespace std
|
|
{
|
|
template <typename T>
|
|
struct hash<gem5::BitUnionType<T>> : public hash<gem5::BitUnionBaseType<T>>
|
|
{
|
|
size_t
|
|
operator() (const gem5::BitUnionType<T> &val) const
|
|
{
|
|
return hash<gem5::BitUnionBaseType<T> >::operator()(val);
|
|
}
|
|
};
|
|
} // namespace std
|
|
|
|
#endif // __BASE_BITUNION_HH__
|