
Since crash() stops the program, adding the `noreturn` attribute allows the compiler to optimize in certain cases. CLion and ReSharper also use this in its static analysis engine to warn about unreachable code. Additionally, since `assert()` uses `crash()` if the checked pointer is null, this lets the analysis engine correctly discern that the pointer will in fact not be null and stop erroneous null pointer warnings.
265 lines
6.4 KiB
C
265 lines
6.4 KiB
C
// #Portability rip ARM
|
|
typedef struct Cpu_Info_X86 {
|
|
u32 eax;
|
|
u32 ebx;
|
|
u32 ecx;
|
|
u32 edx;
|
|
} Cpu_Info_X86;
|
|
|
|
typedef struct Cpu_Capabilities {
|
|
bool sse1;
|
|
bool sse2;
|
|
bool sse3;
|
|
bool ssse3;
|
|
bool sse41;
|
|
bool sse42;
|
|
bool any_sse;
|
|
bool avx;
|
|
bool avx2;
|
|
bool avx512;
|
|
|
|
} Cpu_Capabilities;
|
|
|
|
// I think this is the standard? (sse1)
|
|
#define COMPILER_CAN_DO_SSE 1
|
|
|
|
///
|
|
// Compiler specific stuff
|
|
#if COMPILER_MVSC
|
|
#define inline __forceinline
|
|
#define alignat(x) __declspec(align(x))
|
|
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
|
|
inline void
|
|
crash() {
|
|
__debugbreak();
|
|
volatile int *a = 0;
|
|
*a = 5;
|
|
a = (volatile int*)0xDEADBEEF;
|
|
*a = 5;
|
|
}
|
|
#include <intrin.h>
|
|
#pragma intrinsic(__rdtsc)
|
|
inline u64
|
|
rdtsc() {
|
|
return __rdtsc();
|
|
}
|
|
inline Cpu_Info_X86 cpuid(u32 function_id) {
|
|
Cpu_Info_X86 i;
|
|
__cpuid((int*)&i, function_id);
|
|
return i;
|
|
}
|
|
|
|
#if _M_IX86_FP >= 2
|
|
#define COMPILER_CAN_DO_SSE2 1
|
|
#define COMPILER_CAN_DO_SSE41 1
|
|
#else
|
|
#define COMPILER_CAN_DO_SSE2 0
|
|
#define COMPILER_CAN_DO_SSE41 0
|
|
#endif
|
|
#ifdef __AVX__
|
|
#define COMPILER_CAN_DO_AVX 1
|
|
#else
|
|
#define COMPILER_CAN_DO_AVX 0
|
|
#endif
|
|
#ifdef __AVX2__
|
|
#define COMPILER_CAN_DO_AVX2 1
|
|
#else
|
|
#define COMPILER_CAN_DO_AVX2 0
|
|
#endif
|
|
#ifdef __AVX512F__
|
|
#define COMPILER_CAN_DO_AVX512 1
|
|
#else
|
|
#define COMPILER_CAN_DO_AVX512 0
|
|
#endif
|
|
|
|
#define DEPRECATED(proc, msg) __declspec(deprecated(msg)) func
|
|
|
|
#pragma intrinsic(_InterlockedCompareExchange8)
|
|
#pragma intrinsic(_InterlockedCompareExchange16)
|
|
#pragma intrinsic(_InterlockedCompareExchange)
|
|
#pragma intrinsic(_InterlockedCompareExchange64)
|
|
|
|
inline bool
|
|
compare_and_swap_8(uint8_t *a, uint8_t b, uint8_t old) {
|
|
return _InterlockedCompareExchange8((volatile char*)a, (char)b, (char)old) == old;
|
|
}
|
|
|
|
inline bool
|
|
compare_and_swap_16(uint16_t *a, uint16_t b, uint16_t old) {
|
|
return _InterlockedCompareExchange16((volatile short*)a, (short)b, (short)old) == old;
|
|
}
|
|
|
|
inline bool
|
|
compare_and_swap_32(uint32_t *a, uint32_t b, uint32_t old) {
|
|
return _InterlockedCompareExchange((volatile long*)a, (long)b, (long)old) == old;
|
|
}
|
|
|
|
inline bool
|
|
compare_and_swap_64(uint64_t *a, uint64_t b, uint64_t old) {
|
|
return _InterlockedCompareExchange64((volatile long long*)a, (long long)b, (long long)old) == old;
|
|
}
|
|
|
|
inline bool
|
|
compare_and_swap_bool(bool *a, bool b, bool old) {
|
|
return compare_and_swap_8((uint8_t*)a, (uint8_t)b, (uint8_t)old);
|
|
}
|
|
|
|
#define MEMORY_BARRIER _ReadWriteBarrier()
|
|
|
|
#elif COMPILER_GCC || COMPILER_CLANG
|
|
#define inline __attribute__((always_inline)) inline
|
|
#define alignat(x) __attribute__((aligned(x)))
|
|
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
|
|
|
|
inline void __attribute__((noreturn))
|
|
crash() {
|
|
__builtin_trap();
|
|
volatile int *a = 0;
|
|
*a = 5;
|
|
a = (int*)0xDEADBEEF;
|
|
*a = 5;
|
|
}
|
|
|
|
inline u64
|
|
rdtsc() {
|
|
unsigned int lo, hi;
|
|
__asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
|
|
return ((u64)hi << 32) | lo;
|
|
}
|
|
|
|
inline
|
|
Cpu_Info_X86 cpuid(u32 function_id) {
|
|
Cpu_Info_X86 info;
|
|
__asm__ __volatile__(
|
|
"cpuid"
|
|
: "=a"(info.eax), "=b"(info.ebx), "=c"(info.ecx), "=d"(info.edx)
|
|
: "a"(function_id), "c"(0));
|
|
return info;
|
|
}
|
|
|
|
#ifdef __SSE2__
|
|
#define COMPILER_CAN_DO_SSE2 1
|
|
#else
|
|
#define COMPILER_CAN_DO_SSE2 0
|
|
#endif
|
|
#ifdef __SSE4_1__
|
|
#define COMPILER_CAN_DO_SSE41 1
|
|
#else
|
|
#define COMPILER_CAN_DO_SSE41 0
|
|
#endif
|
|
#ifdef __AVX__
|
|
#define COMPILER_CAN_DO_AVX 1
|
|
#else
|
|
#define COMPILER_CAN_DO_AVX 0
|
|
#endif
|
|
#ifdef __AVX2__
|
|
#define COMPILER_CAN_DO_AVX2 1
|
|
#else
|
|
#define COMPILER_CAN_DO_AVX2 0
|
|
#endif
|
|
#ifdef __AVX512F__
|
|
#define COMPILER_CAN_DO_AVX512 1
|
|
#else
|
|
#define COMPILER_CAN_DO_AVX512 0
|
|
#endif
|
|
|
|
#define DEPRECATED(proc, msg) proc __attribute__((deprecated(msg)))
|
|
|
|
inline bool
|
|
compare_and_swap_8(uint8_t *a, uint8_t b, uint8_t old) {
|
|
unsigned char result;
|
|
__asm__ __volatile__(
|
|
"lock; cmpxchgb %2, %1"
|
|
: "=a" (result), "=m" (*a)
|
|
: "r" (b), "m" (*a), "a" (old)
|
|
: "memory"
|
|
);
|
|
return result == old;
|
|
}
|
|
|
|
inline bool
|
|
compare_and_swap_16(uint16_t *a, uint16_t b, uint16_t old) {
|
|
unsigned short result;
|
|
__asm__ __volatile__(
|
|
"lock; cmpxchgw %2, %1"
|
|
: "=a" (result), "=m" (*a)
|
|
: "r" (b), "m" (*a), "a" (old)
|
|
: "memory"
|
|
);
|
|
return result == old;
|
|
}
|
|
|
|
inline bool
|
|
compare_and_swap_32(uint32_t *a, uint32_t b, uint32_t old) {
|
|
unsigned int result;
|
|
__asm__ __volatile__(
|
|
"lock; cmpxchgl %2, %1"
|
|
: "=a" (result), "=m" (*a)
|
|
: "r" (b), "m" (*a), "a" (old)
|
|
: "memory"
|
|
);
|
|
return result == old;
|
|
}
|
|
|
|
inline bool
|
|
compare_and_swap_64(uint64_t *a, uint64_t b, uint64_t old) {
|
|
unsigned long long result;
|
|
__asm__ __volatile__(
|
|
"lock; cmpxchgq %2, %1"
|
|
: "=a" (result), "=m" (*a)
|
|
: "r" (b), "m" (*a), "a" (old)
|
|
: "memory"
|
|
);
|
|
return result == old;
|
|
}
|
|
|
|
inline bool
|
|
compare_and_swap_bool(bool *a, bool b, bool old) {
|
|
return compare_and_swap_8((uint8_t*)a, (uint8_t)b, (uint8_t)old);
|
|
}
|
|
|
|
#define MEMORY_BARRIER __asm__ __volatile__("" ::: "memory")
|
|
|
|
#else
|
|
#define inline inline
|
|
#define COMPILER_HAS_MEMCPY_INTRINSICS 0
|
|
|
|
inline u64
|
|
rdtsc() { return 0; }
|
|
inline Cpu_Info_X86 cpuid(u32 function_id) {return (Cpu_Info_X86){0};}
|
|
#define COMPILER_CAN_DO_SSE2 0
|
|
#define COMPILER_CAN_DO_AVX 0
|
|
#define COMPILER_CAN_DO_AVX2 0
|
|
#define COMPILER_CAN_DO_AVX512 0
|
|
|
|
#define deprecated(msg)
|
|
|
|
#define MEMORY_BARRIER
|
|
|
|
#warning "Compiler is not explicitly supported, some things will probably not work as expected"
|
|
#endif
|
|
|
|
Cpu_Capabilities
|
|
query_cpu_capabilities() {
|
|
Cpu_Capabilities result = {0};
|
|
|
|
Cpu_Info_X86 info = cpuid(1);
|
|
|
|
result.sse1 = (info.edx & (1 << 25)) != 0;
|
|
result.sse2 = (info.edx & (1 << 26)) != 0;
|
|
result.sse3 = (info.ecx & (1 << 0)) != 0;
|
|
result.ssse3 = (info.ecx & (1 << 9)) != 0;
|
|
result.sse41 = (info.ecx & (1 << 19)) != 0;
|
|
result.sse42 = (info.ecx & (1 << 20)) != 0;
|
|
result.any_sse = result.sse1 || result.sse2 || result.sse3 || result.ssse3 || result.sse41 || result.sse42;
|
|
|
|
result.avx = (info.ecx & (1 << 28)) != 0;
|
|
|
|
Cpu_Info_X86 ext_info = cpuid(7);
|
|
result.avx2 = (ext_info.ebx & (1 << 5)) != 0;
|
|
|
|
result.avx512 = (ext_info.ebx & (1 << 16)) != 0;
|
|
|
|
return result;
|
|
}
|
|
|