• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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