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