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