Make some structs nested
[openal-soft.git] / core / cpu_caps.cpp
blob8211b331555aa4d6aa1370273d2c47cca1d3e9e7
2 #include "config.h"
4 #include "cpu_caps.h"
6 #if defined(_WIN32) && (defined(_M_ARM) || defined(_M_ARM64))
7 #define WIN32_LEAN_AND_MEAN
8 #include <windows.h>
9 #ifndef PF_ARM_NEON_INSTRUCTIONS_AVAILABLE
10 #define PF_ARM_NEON_INSTRUCTIONS_AVAILABLE 19
11 #endif
12 #endif
14 #ifdef HAVE_INTRIN_H
15 #include <intrin.h>
16 #endif
17 #ifdef HAVE_CPUID_H
18 #include <cpuid.h>
19 #endif
21 #include <array>
22 #include <cctype>
23 #include <string>
26 int CPUCapFlags{0};
28 namespace {
30 #if defined(HAVE_GCC_GET_CPUID) \
31 && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64))
32 using reg_type = unsigned int;
33 inline std::array<reg_type,4> get_cpuid(unsigned int f)
35 std::array<reg_type,4> ret{};
36 __get_cpuid(f, &ret[0], &ret[1], &ret[2], &ret[3]);
37 return ret;
39 #define CAN_GET_CPUID
40 #elif defined(HAVE_CPUID_INTRINSIC) \
41 && (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64))
42 using reg_type = int;
43 inline std::array<reg_type,4> get_cpuid(unsigned int f)
45 std::array<reg_type,4> ret{};
46 (__cpuid)(ret.data(), f);
47 return ret;
49 #define CAN_GET_CPUID
50 #endif
52 } // namespace
54 al::optional<CPUInfo> GetCPUInfo()
56 CPUInfo ret;
58 #ifdef CAN_GET_CPUID
59 auto cpuregs = get_cpuid(0);
60 if(cpuregs[0] == 0)
61 return al::nullopt;
63 const reg_type maxfunc{cpuregs[0]};
65 cpuregs = get_cpuid(0x80000000);
66 const reg_type maxextfunc{cpuregs[0]};
68 ret.mVendor.append(reinterpret_cast<char*>(&cpuregs[1]), 4);
69 ret.mVendor.append(reinterpret_cast<char*>(&cpuregs[3]), 4);
70 ret.mVendor.append(reinterpret_cast<char*>(&cpuregs[2]), 4);
71 auto iter_end = std::remove(ret.mVendor.begin(), ret.mVendor.end(), '\0');
72 iter_end = std::unique(ret.mVendor.begin(), iter_end,
73 [](auto&& c0, auto&& c1) { return std::isspace(c0) && std::isspace(c1); });
74 ret.mVendor.erase(iter_end, ret.mVendor.end());
75 if(!ret.mVendor.empty() && std::isspace(ret.mVendor.back()))
76 ret.mVendor.pop_back();
77 if(!ret.mVendor.empty() && std::isspace(ret.mVendor.front()))
78 ret.mVendor.erase(ret.mVendor.begin());
80 if(maxextfunc >= 0x80000004)
82 cpuregs = get_cpuid(0x80000002);
83 ret.mName.append(reinterpret_cast<char*>(cpuregs.data()), 16);
84 cpuregs = get_cpuid(0x80000003);
85 ret.mName.append(reinterpret_cast<char*>(cpuregs.data()), 16);
86 cpuregs = get_cpuid(0x80000004);
87 ret.mName.append(reinterpret_cast<char*>(cpuregs.data()), 16);
88 iter_end = std::remove(ret.mName.begin(), ret.mName.end(), '\0');
89 iter_end = std::unique(ret.mName.begin(), iter_end,
90 [](auto&& c0, auto&& c1) { return std::isspace(c0) && std::isspace(c1); });
91 ret.mName.erase(iter_end, ret.mName.end());
92 if(!ret.mName.empty() && std::isspace(ret.mName.back()))
93 ret.mName.pop_back();
94 if(!ret.mName.empty() && std::isspace(ret.mName.front()))
95 ret.mName.erase(ret.mName.begin());
98 if(maxfunc >= 1)
100 cpuregs = get_cpuid(1);
101 if((cpuregs[3]&(1<<25)))
102 ret.mCaps |= CPU_CAP_SSE;
103 if((ret.mCaps&CPU_CAP_SSE) && (cpuregs[3]&(1<<26)))
104 ret.mCaps |= CPU_CAP_SSE2;
105 if((ret.mCaps&CPU_CAP_SSE2) && (cpuregs[2]&(1<<0)))
106 ret.mCaps |= CPU_CAP_SSE3;
107 if((ret.mCaps&CPU_CAP_SSE3) && (cpuregs[2]&(1<<19)))
108 ret.mCaps |= CPU_CAP_SSE4_1;
111 #else
113 /* Assume support for whatever's supported if we can't check for it */
114 #if defined(HAVE_SSE4_1)
115 #warning "Assuming SSE 4.1 run-time support!"
116 ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1;
117 #elif defined(HAVE_SSE3)
118 #warning "Assuming SSE 3 run-time support!"
119 ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3;
120 #elif defined(HAVE_SSE2)
121 #warning "Assuming SSE 2 run-time support!"
122 ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2;
123 #elif defined(HAVE_SSE)
124 #warning "Assuming SSE run-time support!"
125 ret.mCaps |= CPU_CAP_SSE;
126 #endif
127 #endif /* CAN_GET_CPUID */
129 #ifdef HAVE_NEON
130 #ifdef __ARM_NEON
131 ret.mCaps |= CPU_CAP_NEON;
132 #elif defined(_WIN32) && (defined(_M_ARM) || defined(_M_ARM64))
133 if(IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE))
134 ret.mCaps |= CPU_CAP_NEON;
135 #else
136 #warning "Assuming NEON run-time support!"
137 ret.mCaps |= CPU_CAP_NEON;
138 #endif
139 #endif
141 return al::make_optional(ret);