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 */
35int 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
42static bool g_init { false };
43
44/** Does this CPU support SSE 4.1? Set to -1 if not yet initialized. */
45static bool g_cpu_has_sse41 { false };
46
47/** Does this CPU support AVX2? Set to -1 if not yet initialized. */
48static bool g_cpu_has_avx2 { false };
49
50/** Does this CPU support POPCNT? Set to -1 if not yet initialized. */
51static bool g_cpu_has_popcnt { false };
52
53/** Does this CPU support F16C? Set to -1 if not yet initialized. */
54static 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 */
67static 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 */
106static 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 */
139static 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 */
156static 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 */
173static 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 */
190static 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 */
204static 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 */
219static 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
259static bool validate_cpu_isa()
260{
261	return true;
262}
263
264#endif
265
266int 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