types.h (10368B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #pragma once 5 #include <bit> 6 #include <cstdint> 7 #include <cstring> 8 #include <limits> 9 #include <type_traits> 10 11 // Force inline helper 12 #ifndef ALWAYS_INLINE 13 #if defined(_MSC_VER) 14 #define ALWAYS_INLINE __forceinline 15 #elif defined(__GNUC__) || defined(__clang__) 16 #define ALWAYS_INLINE __attribute__((always_inline)) inline 17 #else 18 #define ALWAYS_INLINE inline 19 #endif 20 #endif 21 22 // Force inline in non-debug helper 23 #ifdef _DEBUG 24 #define ALWAYS_INLINE_RELEASE inline 25 #else 26 #define ALWAYS_INLINE_RELEASE ALWAYS_INLINE 27 #endif 28 29 // unreferenced parameter macro 30 #ifndef UNREFERENCED_VARIABLE 31 #if defined(__GNUC__) || defined(__clang__) || defined(__EMSCRIPTEN__) 32 #define UNREFERENCED_VARIABLE(P) (void)(P) 33 #else 34 #define UNREFERENCED_VARIABLE(P) (P) 35 #endif 36 #endif 37 38 // countof macro 39 #ifndef countof 40 #ifdef _countof 41 #define countof _countof 42 #else 43 template<typename T, size_t N> 44 char (&__countof_ArraySizeHelper(T (&array)[N]))[N]; 45 #define countof(array) (sizeof(__countof_ArraySizeHelper(array))) 46 #endif 47 #endif 48 49 // offsetof macro. Need to use __builtin_offsetof(), otherwise it doesn't work in constant expressions. 50 #if defined(__clang__) || defined(__GNUC__) 51 #define OFFSETOF(st, m) __builtin_offsetof(st, m) 52 #else 53 #ifdef offsetof 54 #define OFFSETOF(st, m) offsetof(st, m) 55 #else 56 #define OFFSETOF(st, m) ((size_t)((char*)&((st*)(0))->m - (char*)0)) 57 #endif 58 #endif 59 60 #ifdef __GNUC__ 61 #define printflike(n, m) __attribute__((format(printf, n, m))) 62 #else 63 #define printflike(n, m) 64 #endif 65 66 // [[noreturn]] which can be used on function pointers. 67 #ifdef _MSC_VER 68 // __declspec(noreturn) produces error C3829. 69 #define NORETURN_FUNCTION_POINTER 70 #else 71 #define NORETURN_FUNCTION_POINTER __attribute__((noreturn)) 72 #endif 73 74 // __assume, potentially enables optimization. 75 #ifdef _MSC_VER 76 #define ASSUME(x) __assume(x) 77 #else 78 #define ASSUME(x) \ 79 do \ 80 { \ 81 if (!(x)) \ 82 __builtin_unreachable(); \ 83 } while (0) 84 #endif 85 86 // disable warnings that show up at warning level 4 87 // TODO: Move to build system instead 88 #ifdef _MSC_VER 89 #pragma warning(disable : 4201) // warning C4201: nonstandard extension used : nameless struct/union 90 #pragma warning(disable : 4100) // warning C4100: 'Platform' : unreferenced formal parameter 91 #pragma warning(disable : 4355) // warning C4355: 'this' : used in base member initializer list 92 #endif 93 94 using s8 = int8_t; 95 using u8 = uint8_t; 96 using s16 = int16_t; 97 using u16 = uint16_t; 98 using s32 = int32_t; 99 using u32 = uint32_t; 100 using s64 = int64_t; 101 using u64 = uint64_t; 102 103 // Enable use of static_assert in constexpr if 104 template<class T> 105 struct dependent_false : std::false_type 106 { 107 }; 108 template<int T> 109 struct dependent_int_false : std::false_type 110 { 111 }; 112 113 // Architecture detection. 114 #if defined(_MSC_VER) 115 116 #if defined(_M_X64) 117 #define CPU_ARCH_X64 1 118 #elif defined(_M_IX86) 119 #define CPU_ARCH_X86 1 120 #elif defined(_M_ARM64) 121 #define CPU_ARCH_ARM64 1 122 #elif defined(_M_ARM) 123 #define CPU_ARCH_ARM32 1 124 #else 125 #error Unknown architecture. 126 #endif 127 128 #elif defined(__GNUC__) || defined(__clang__) 129 130 #if defined(__x86_64__) 131 #define CPU_ARCH_X64 1 132 #elif defined(__i386__) 133 #define CPU_ARCH_X86 1 134 #elif defined(__aarch64__) 135 #define CPU_ARCH_ARM64 1 136 #elif defined(__arm__) 137 #define CPU_ARCH_ARM32 1 138 #elif defined(__riscv) && __riscv_xlen == 64 139 #define CPU_ARCH_RISCV64 1 140 #else 141 #error Unknown architecture. 142 #endif 143 144 #else 145 146 #error Unknown compiler. 147 148 #endif 149 150 #if defined(CPU_ARCH_X64) 151 #define CPU_ARCH_STR "x64" 152 #elif defined(CPU_ARCH_X86) 153 #define CPU_ARCH_STR "x86" 154 #elif defined(CPU_ARCH_ARM32) 155 #define CPU_ARCH_STR "arm32" 156 #elif defined(CPU_ARCH_ARM64) 157 #define CPU_ARCH_STR "arm64" 158 #elif defined(CPU_ARCH_RISCV64) 159 #define CPU_ARCH_STR "riscv64" 160 #else 161 #define CPU_ARCH_STR "Unknown" 162 #endif 163 164 // OS detection. 165 #if defined(_WIN32) 166 #define TARGET_OS_STR "Windows" 167 #elif defined(__ANDROID__) 168 #define TARGET_OS_STR "Android" 169 #elif defined(__linux__) 170 #define TARGET_OS_STR "Linux" 171 #elif defined(__FreeBSD__) 172 #define TARGET_OS_STR "FreeBSD" 173 #elif defined(__APPLE__) 174 #define TARGET_OS_STR "macOS" 175 #else 176 #define TARGET_OS_STR "Unknown" 177 #endif 178 179 // Host page sizes. 180 #if defined(OVERRIDE_HOST_PAGE_SIZE) 181 static constexpr u32 HOST_PAGE_SIZE = OVERRIDE_HOST_PAGE_SIZE; 182 static constexpr u32 HOST_PAGE_MASK = HOST_PAGE_SIZE - 1; 183 static constexpr u32 HOST_PAGE_SHIFT = std::bit_width(HOST_PAGE_MASK); 184 #elif defined(__APPLE__) && defined(__aarch64__) 185 static constexpr u32 HOST_PAGE_SIZE = 0x4000; 186 static constexpr u32 HOST_PAGE_MASK = HOST_PAGE_SIZE - 1; 187 static constexpr u32 HOST_PAGE_SHIFT = 14; 188 #else 189 static constexpr u32 HOST_PAGE_SIZE = 0x1000; 190 static constexpr u32 HOST_PAGE_MASK = HOST_PAGE_SIZE - 1; 191 static constexpr u32 HOST_PAGE_SHIFT = 12; 192 #endif 193 194 // Host cache line sizes. 195 #if defined(OVERRIDE_HOST_CACHE_LINE_SIZE) 196 static constexpr u32 HOST_CACHE_LINE_SIZE = OVERRIDE_HOST_CACHE_LINE_SIZE; 197 #elif defined(__APPLE__) && defined(__aarch64__) 198 static constexpr u32 HOST_CACHE_LINE_SIZE = 128; // Apple Silicon uses 128b cache lines. 199 #else 200 static constexpr u32 HOST_CACHE_LINE_SIZE = 64; // Everything else is 64b. 201 #endif 202 #define ALIGN_TO_CACHE_LINE alignas(HOST_CACHE_LINE_SIZE) 203 204 // Enum class bitwise operators 205 #define IMPLEMENT_ENUM_CLASS_BITWISE_OPERATORS(type_) \ 206 ALWAYS_INLINE constexpr type_ operator&(type_ lhs, type_ rhs) \ 207 { \ 208 return static_cast<type_>(static_cast<std::underlying_type<type_>::type>(lhs) & \ 209 static_cast<std::underlying_type<type_>::type>(rhs)); \ 210 } \ 211 ALWAYS_INLINE constexpr type_ operator|(type_ lhs, type_ rhs) \ 212 { \ 213 return static_cast<type_>(static_cast<std::underlying_type<type_>::type>(lhs) | \ 214 static_cast<std::underlying_type<type_>::type>(rhs)); \ 215 } \ 216 ALWAYS_INLINE constexpr type_ operator^(type_ lhs, type_ rhs) \ 217 { \ 218 return static_cast<type_>(static_cast<std::underlying_type<type_>::type>(lhs) ^ \ 219 static_cast<std::underlying_type<type_>::type>(rhs)); \ 220 } \ 221 ALWAYS_INLINE constexpr type_ operator~(type_ val) \ 222 { \ 223 return static_cast<type_>(~static_cast<std::underlying_type<type_>::type>(val)); \ 224 } \ 225 ALWAYS_INLINE constexpr type_& operator&=(type_& lhs, type_ rhs) \ 226 { \ 227 lhs = static_cast<type_>(static_cast<std::underlying_type<type_>::type>(lhs) & \ 228 static_cast<std::underlying_type<type_>::type>(rhs)); \ 229 return lhs; \ 230 } \ 231 ALWAYS_INLINE constexpr type_& operator|=(type_& lhs, type_ rhs) \ 232 { \ 233 lhs = static_cast<type_>(static_cast<std::underlying_type<type_>::type>(lhs) | \ 234 static_cast<std::underlying_type<type_>::type>(rhs)); \ 235 return lhs; \ 236 } \ 237 ALWAYS_INLINE constexpr type_& operator^=(type_& lhs, type_ rhs) \ 238 { \ 239 lhs = static_cast<type_>(static_cast<std::underlying_type<type_>::type>(lhs) ^ \ 240 static_cast<std::underlying_type<type_>::type>(rhs)); \ 241 return lhs; \ 242 } 243 244 // Compute the address of a base type given a field offset. 245 #define BASE_FROM_RECORD_FIELD(ptr, base_type, field) ((base_type*)(((char*)ptr) - offsetof(base_type, field)))