xref: /netbsd-src/external/gpl3/gdb/dist/gdbsupport/packed.h (revision 5ba1f45f2a09259cc846f20c7c5501604d633c90)
1*5ba1f45fSchristos /* Copyright (C) 2022-2024 Free Software Foundation, Inc.
24b169a6bSchristos 
34b169a6bSchristos    This file is part of GDB.
44b169a6bSchristos 
54b169a6bSchristos    This program is free software; you can redistribute it and/or modify
64b169a6bSchristos    it under the terms of the GNU General Public License as published by
74b169a6bSchristos    the Free Software Foundation; either version 3 of the License, or
84b169a6bSchristos    (at your option) any later version.
94b169a6bSchristos 
104b169a6bSchristos    This program is distributed in the hope that it will be useful,
114b169a6bSchristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
124b169a6bSchristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
134b169a6bSchristos    GNU General Public License for more details.
144b169a6bSchristos 
154b169a6bSchristos    You should have received a copy of the GNU General Public License
164b169a6bSchristos    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
174b169a6bSchristos 
184b169a6bSchristos #ifndef PACKED_H
194b169a6bSchristos #define PACKED_H
204b169a6bSchristos 
214b169a6bSchristos #include "traits.h"
224b169a6bSchristos #include <atomic>
234b169a6bSchristos 
244b169a6bSchristos /* Each instantiation and full specialization of the packed template
254b169a6bSchristos    defines a type that behaves like a given scalar type, but that has
264b169a6bSchristos    byte alignment, and, may optionally have a smaller size than the
274b169a6bSchristos    given scalar type.  This is typically used as alternative to
284b169a6bSchristos    bit-fields (and ENUM_BITFIELD), when the fields must have separate
294b169a6bSchristos    memory locations to avoid data races.  */
304b169a6bSchristos 
314b169a6bSchristos /* There are two implementations here -- one standard compliant, using
324b169a6bSchristos    a byte array for internal representation, and another that relies
334b169a6bSchristos    on bitfields and attribute packed (and attribute gcc_struct on
344b169a6bSchristos    Windows).  The latter is preferable, as it is more convenient when
354b169a6bSchristos    debugging GDB -- printing a struct packed variable prints its field
364b169a6bSchristos    using its natural type, which is particularly useful if the type is
374b169a6bSchristos    an enum -- but may not work on all compilers.  */
384b169a6bSchristos 
394b169a6bSchristos /* Clang targeting Windows does not support attribute gcc_struct, so
40*5ba1f45fSchristos    we use the alternative byte array implementation there. */
414b169a6bSchristos #if defined _WIN32 && defined __clang__
424b169a6bSchristos # define PACKED_USE_ARRAY 1
434b169a6bSchristos #else
444b169a6bSchristos # define PACKED_USE_ARRAY 0
454b169a6bSchristos #endif
464b169a6bSchristos 
474b169a6bSchristos /* For the preferred implementation, we need gcc_struct on Windows, as
484b169a6bSchristos    otherwise the size of e.g., "packed<int, 1>" will be larger than
494b169a6bSchristos    what we want.  Clang targeting Windows does not support attribute
504b169a6bSchristos    gcc_struct.  */
514b169a6bSchristos #if !PACKED_USE_ARRAY && defined _WIN32 && !defined __clang__
524b169a6bSchristos # define ATTRIBUTE_GCC_STRUCT __attribute__((__gcc_struct__))
534b169a6bSchristos #else
544b169a6bSchristos # define ATTRIBUTE_GCC_STRUCT
554b169a6bSchristos #endif
564b169a6bSchristos 
574b169a6bSchristos template<typename T, size_t Bytes = sizeof (T)>
584b169a6bSchristos struct ATTRIBUTE_GCC_STRUCT packed
594b169a6bSchristos {
604b169a6bSchristos public:
614b169a6bSchristos   packed () noexcept = default;
624b169a6bSchristos 
634b169a6bSchristos   packed (T val)
644b169a6bSchristos   {
65*5ba1f45fSchristos     static_assert (sizeof (ULONGEST) >= sizeof (T));
664b169a6bSchristos 
674b169a6bSchristos #if PACKED_USE_ARRAY
684b169a6bSchristos     ULONGEST tmp = val;
694b169a6bSchristos     for (int i = (Bytes - 1); i >= 0; --i)
704b169a6bSchristos       {
714b169a6bSchristos 	m_bytes[i] = (gdb_byte) tmp;
724b169a6bSchristos 	tmp >>= HOST_CHAR_BIT;
734b169a6bSchristos       }
744b169a6bSchristos #else
754b169a6bSchristos     m_val = val;
764b169a6bSchristos #endif
774b169a6bSchristos 
784b169a6bSchristos     /* Ensure size and aligment are what we expect.  */
79*5ba1f45fSchristos     static_assert (sizeof (packed) == Bytes);
80*5ba1f45fSchristos     static_assert (alignof (packed) == 1);
814b169a6bSchristos 
824b169a6bSchristos     /* Make sure packed can be wrapped with std::atomic.  */
83*5ba1f45fSchristos     static_assert (std::is_trivially_copyable<packed>::value);
84*5ba1f45fSchristos     static_assert (std::is_copy_constructible<packed>::value);
85*5ba1f45fSchristos     static_assert (std::is_move_constructible<packed>::value);
86*5ba1f45fSchristos     static_assert (std::is_copy_assignable<packed>::value);
87*5ba1f45fSchristos     static_assert (std::is_move_assignable<packed>::value);
884b169a6bSchristos   }
894b169a6bSchristos 
904b169a6bSchristos   operator T () const noexcept
914b169a6bSchristos   {
924b169a6bSchristos #if PACKED_USE_ARRAY
934b169a6bSchristos     ULONGEST tmp = 0;
944b169a6bSchristos     for (int i = 0;;)
954b169a6bSchristos       {
964b169a6bSchristos 	tmp |= m_bytes[i];
974b169a6bSchristos 	if (++i == Bytes)
984b169a6bSchristos 	  break;
994b169a6bSchristos 	tmp <<= HOST_CHAR_BIT;
1004b169a6bSchristos       }
1014b169a6bSchristos     return (T) tmp;
1024b169a6bSchristos #else
1034b169a6bSchristos     return m_val;
1044b169a6bSchristos #endif
1054b169a6bSchristos   }
1064b169a6bSchristos 
1074b169a6bSchristos private:
1084b169a6bSchristos #if PACKED_USE_ARRAY
1094b169a6bSchristos   gdb_byte m_bytes[Bytes];
1104b169a6bSchristos #else
1114b169a6bSchristos   T m_val : (Bytes * HOST_CHAR_BIT) ATTRIBUTE_PACKED;
1124b169a6bSchristos #endif
1134b169a6bSchristos };
1144b169a6bSchristos 
1154b169a6bSchristos /* Add some comparisons between std::atomic<packed<T>> and packed<T>
1164b169a6bSchristos    and T.  We need this because even though std::atomic<T> doesn't
1174b169a6bSchristos    define these operators, the relational expressions still work via
1184b169a6bSchristos    implicit conversions.  Those wouldn't work when wrapped in packed
1194b169a6bSchristos    without these operators, because they'd require two implicit
1204b169a6bSchristos    conversions to go from T to packed<T> to std::atomic<packed<T>>
1214b169a6bSchristos    (and back), and C++ only does one.  */
1224b169a6bSchristos 
1234b169a6bSchristos #define PACKED_ATOMIC_OP(OP)						\
1244b169a6bSchristos   template<typename T, size_t Bytes>					\
1254b169a6bSchristos   bool operator OP (const std::atomic<packed<T, Bytes>> &lhs,		\
1264b169a6bSchristos 		    const std::atomic<packed<T, Bytes>> &rhs)		\
1274b169a6bSchristos   {									\
1284b169a6bSchristos     return lhs.load () OP rhs.load ();					\
1294b169a6bSchristos   }									\
1304b169a6bSchristos 									\
1314b169a6bSchristos   template<typename T, size_t Bytes>					\
1324b169a6bSchristos   bool operator OP (T lhs, const std::atomic<packed<T, Bytes>> &rhs)	\
1334b169a6bSchristos   {									\
1344b169a6bSchristos     return lhs OP rhs.load ();						\
1354b169a6bSchristos   }									\
1364b169a6bSchristos 									\
1374b169a6bSchristos   template<typename T, size_t Bytes>					\
1384b169a6bSchristos   bool operator OP (const std::atomic<packed<T, Bytes>> &lhs, T rhs)	\
1394b169a6bSchristos   {									\
1404b169a6bSchristos     return lhs.load () OP rhs;						\
1414b169a6bSchristos   }									\
1424b169a6bSchristos 									\
1434b169a6bSchristos   template<typename T, size_t Bytes>					\
1444b169a6bSchristos   bool operator OP (const std::atomic<packed<T, Bytes>> &lhs,		\
1454b169a6bSchristos 		    packed<T, Bytes> rhs)				\
1464b169a6bSchristos   {									\
1474b169a6bSchristos     return lhs.load () OP rhs;						\
1484b169a6bSchristos   }									\
1494b169a6bSchristos 									\
1504b169a6bSchristos   template<typename T, size_t Bytes>					\
1514b169a6bSchristos   bool operator OP (packed<T, Bytes> lhs,				\
1524b169a6bSchristos 		    const std::atomic<packed<T, Bytes>> &rhs)		\
1534b169a6bSchristos   {									\
1544b169a6bSchristos     return lhs OP rhs.load ();						\
1554b169a6bSchristos   }
1564b169a6bSchristos 
1574b169a6bSchristos PACKED_ATOMIC_OP (==)
1584b169a6bSchristos PACKED_ATOMIC_OP (!=)
1594b169a6bSchristos PACKED_ATOMIC_OP (>)
1604b169a6bSchristos PACKED_ATOMIC_OP (<)
1614b169a6bSchristos PACKED_ATOMIC_OP (>=)
1624b169a6bSchristos PACKED_ATOMIC_OP (<=)
1634b169a6bSchristos 
1644b169a6bSchristos #undef PACKED_ATOMIC_OP
1654b169a6bSchristos 
1664b169a6bSchristos #endif
167