1 // SPDX-License-Identifier: Apache-2.0
2 // ----------------------------------------------------------------------------
3 // Copyright 2020-2023 Arm Limited
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 // use this file except in compliance with the License. You may obtain a copy
7 // of the License at:
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 // License for the specific language governing permissions and limitations
15 // under the License.
16 // ----------------------------------------------------------------------------
17
18 /**
19 * @brief Platform-specific function implementations.
20 *
21 * This module contains the CLI entry point which also performs the role of
22 * validating the host extended ISA support meets the needs of the tools.
23 */
24
25 #include <cstdio>
26
27 /**
28 * @brief The main entry point.
29 *
30 * @param argc The number of arguments.
31 * @param argv The vector of arguments.
32 *
33 * @return 0 on success, non-zero otherwise.
34 */
35 int astcenc_main(
36 int argc,
37 char **argv);
38
39 #if (ASTCENC_SSE > 20) || (ASTCENC_AVX > 0) || \
40 (ASTCENC_POPCNT > 0) || (ASTCENC_F16C > 0)
41
42 static bool g_init { false };
43
44 /** Does this CPU support SSE 4.1? Set to -1 if not yet initialized. */
45 static bool g_cpu_has_sse41 { false };
46
47 /** Does this CPU support AVX2? Set to -1 if not yet initialized. */
48 static bool g_cpu_has_avx2 { false };
49
50 /** Does this CPU support POPCNT? Set to -1 if not yet initialized. */
51 static bool g_cpu_has_popcnt { false };
52
53 /** Does this CPU support F16C? Set to -1 if not yet initialized. */
54 static bool g_cpu_has_f16c { false };
55
56 /* ============================================================================
57 Platform code for Visual Studio
58 ============================================================================ */
59 #if !defined(__clang__) && defined(_MSC_VER)
60 #define WIN32_LEAN_AND_MEAN
61 #include <windows.h>
62 #include <intrin.h>
63
64 /**
65 * @brief Detect platform CPU ISA support and update global trackers.
66 */
detect_cpu_isa()67 static void detect_cpu_isa()
68 {
69 int data[4];
70
71 __cpuid(data, 0);
72 int num_id = data[0];
73
74 if (num_id >= 1)
75 {
76 __cpuidex(data, 1, 0);
77 // SSE41 = Bank 1, ECX, bit 19
78 g_cpu_has_sse41 = data[2] & (1 << 19) ? true : false;
79 // POPCNT = Bank 1, ECX, bit 23
80 g_cpu_has_popcnt = data[2] & (1 << 23) ? true : false;
81 // F16C = Bank 1, ECX, bit 29
82 g_cpu_has_f16c = data[2] & (1 << 29) ? true : false;
83 }
84
85 if (num_id >= 7)
86 {
87 __cpuidex(data, 7, 0);
88 // AVX2 = Bank 7, EBX, bit 5
89 g_cpu_has_avx2 = data[1] & (1 << 5) ? true : false;
90 }
91
92 // Ensure state bits are updated before init flag is updated
93 MemoryBarrier();
94 g_init = true;
95 }
96
97 /* ============================================================================
98 Platform code for GCC and Clang
99 ============================================================================ */
100 #else
101 #include <cpuid.h>
102
103 /**
104 * @brief Detect platform CPU ISA support and update global trackers.
105 */
detect_cpu_isa()106 static void detect_cpu_isa()
107 {
108 unsigned int data[4];
109
110 if (__get_cpuid_count(1, 0, &data[0], &data[1], &data[2], &data[3]))
111 {
112 // SSE41 = Bank 1, ECX, bit 19
113 g_cpu_has_sse41 = data[2] & (1 << 19) ? true : false;
114 // POPCNT = Bank 1, ECX, bit 23
115 g_cpu_has_popcnt = data[2] & (1 << 23) ? true : false;
116 // F16C = Bank 1, ECX, bit 29
117 g_cpu_has_f16c = data[2] & (1 << 29) ? true : false;
118 }
119
120 g_cpu_has_avx2 = 0;
121 if (__get_cpuid_count(7, 0, &data[0], &data[1], &data[2], &data[3]))
122 {
123 // AVX2 = Bank 7, EBX, bit 5
124 g_cpu_has_avx2 = data[1] & (1 << 5) ? true : false;
125 }
126
127 // Ensure state bits are updated before init flag is updated
128 __sync_synchronize();
129 g_init = true;
130 }
131 #endif
132
133 #if ASTCENC_POPCNT > 0
134 /**
135 * @brief Run-time detection if the host CPU supports the POPCNT extension.
136 *
137 * @return @c true if supported, @c false if not.
138 */
cpu_supports_popcnt()139 static bool cpu_supports_popcnt()
140 {
141 if (!g_init)
142 {
143 detect_cpu_isa();
144 }
145
146 return g_cpu_has_popcnt;
147 }
148 #endif
149
150 #if ASTCENC_F16C > 0
151 /**
152 * @brief Run-time detection if the host CPU supports F16C extension.
153 *
154 * @return @c true if supported, @c false if not.
155 */
cpu_supports_f16c()156 static bool cpu_supports_f16c()
157 {
158 if (!g_init)
159 {
160 detect_cpu_isa();
161 }
162
163 return g_cpu_has_f16c;
164 }
165 #endif
166
167 #if ASTCENC_SSE >= 41
168 /**
169 * @brief Run-time detection if the host CPU supports SSE 4.1 extension.
170 *
171 * @return @c true if supported, @c false if not.
172 */
cpu_supports_sse41()173 static bool cpu_supports_sse41()
174 {
175 if (!g_init)
176 {
177 detect_cpu_isa();
178 }
179
180 return g_cpu_has_sse41;
181 }
182 #endif
183
184 #if ASTCENC_AVX >= 2
185 /**
186 * @brief Run-time detection if the host CPU supports AVX 2 extension.
187 *
188 * @return @c true if supported, @c false if not.
189 */
cpu_supports_avx2()190 static bool cpu_supports_avx2()
191 {
192 if (!g_init)
193 {
194 detect_cpu_isa();
195 }
196
197 return g_cpu_has_avx2;
198 }
199 #endif
200
201 /**
202 * @brief Print a string to stderr.
203 */
print_error(const char * format)204 static inline void print_error(
205 const char* format
206 ) {
207 fprintf(stderr, "%s", format);
208 }
209
210 /**
211 * @brief Validate CPU ISA support meets the requirements of this build of the library.
212 *
213 * Each library build is statically compiled for a particular set of CPU ISA features, such as the
214 * SIMD support or other ISA extensions such as POPCNT. This function checks that the host CPU
215 * actually supports everything this build needs.
216 *
217 * @return Return @c true if validated, @c false otherwise.
218 */
validate_cpu_isa()219 static bool validate_cpu_isa()
220 {
221 #if ASTCENC_AVX >= 2
222 if (!cpu_supports_avx2())
223 {
224 print_error("ERROR: Host does not support AVX2 ISA extension\n");
225 return false;
226 }
227 #endif
228
229 #if ASTCENC_F16C >= 1
230 if (!cpu_supports_f16c())
231 {
232 print_error("ERROR: Host does not support F16C ISA extension\n");
233 return false;
234 }
235 #endif
236
237 #if ASTCENC_SSE >= 41
238 if (!cpu_supports_sse41())
239 {
240 print_error("ERROR: Host does not support SSE4.1 ISA extension\n");
241 return false;
242 }
243 #endif
244
245 #if ASTCENC_POPCNT >= 1
246 if (!cpu_supports_popcnt())
247 {
248 print_error("ERROR: Host does not support POPCNT ISA extension\n");
249 return false;
250 }
251 #endif
252
253 return true;
254 }
255
256 #else
257
258 // Fallback for cases with no dynamic ISA availability
validate_cpu_isa()259 static bool validate_cpu_isa()
260 {
261 return true;
262 }
263
264 #endif
265
main(int argc,char ** argv)266 int main(
267 int argc,
268 char **argv
269 ) {
270 if (!validate_cpu_isa())
271 {
272 return 1;
273 }
274
275 return astcenc_main(argc, argv);
276 }
277