/src/xnnpack/src/configs/hardware-config.c
Line | Count | Source |
1 | | // Copyright 2022 Google LLC |
2 | | // |
3 | | // This source code is licensed under the BSD-style license found in the |
4 | | // LICENSE file in the root directory of this source tree. |
5 | | |
6 | | #include <assert.h> |
7 | | #include <stddef.h> |
8 | | #include <stdint.h> |
9 | | |
10 | | #if XNN_ENABLE_CPUINFO |
11 | | #include <cpuinfo.h> |
12 | | #endif // XNN_ENABLE_CPUINFO |
13 | | |
14 | | #include "src/xnnpack/common.h" |
15 | | #include "src/xnnpack/init-once.h" |
16 | | |
17 | | #if _WIN32 |
18 | | #include <windows.h> |
19 | | |
20 | | #ifndef PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE |
21 | | #define PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE 43 |
22 | | #endif |
23 | | #endif |
24 | | #if XNN_ARCH_X86_64 && defined(__linux__) && !defined(CHROMIUM) |
25 | | #include <sys/syscall.h> |
26 | | #include <unistd.h> |
27 | | |
28 | 0 | #define XFEATURE_XTILEDATA 18 |
29 | 0 | #define ARCH_REQ_XCOMP_PERM 0x1023 |
30 | | #endif |
31 | | #if XNN_ENABLE_CPUINFO |
32 | | #include <cpuinfo.h> |
33 | | #endif // XNN_ENABLE_CPUINFO |
34 | | #if XNN_ARCH_HEXAGON |
35 | | // TODO - use CPUINFO |
36 | | #include <HAP_compute_res.h> |
37 | | #include <qurt.h> |
38 | | #include "HAP_farf.h" |
39 | | #include "HAP_power.h" // NOLINT - part of the hexagon SDK |
40 | | #endif |
41 | | |
42 | | #if XNN_ARCH_RISCV |
43 | | #include <inttypes.h> |
44 | | #include <sys/auxv.h> |
45 | | |
46 | | #define COMPAT_HWCAP_ISA_V (1 << ('V' - 'A')) |
47 | | #endif |
48 | | |
49 | | #if XNN_ARCH_PPC64 |
50 | | #include <sys/auxv.h> |
51 | | #if defined(__FreeBSD__) |
52 | | #include <machine/cpu.h> |
53 | | #endif |
54 | | #endif |
55 | | |
56 | | #if XNN_ARCH_WASM || XNN_ARCH_WASMSIMD || XNN_ARCH_WASMRELAXEDSIMD |
57 | | #include <math.h> |
58 | | #endif |
59 | | |
60 | | #if XNN_ARCH_WASMRELAXEDSIMD |
61 | | #include <wasm_simd128.h> |
62 | | #endif |
63 | | |
64 | | #include "src/xnnpack/hardware-config.h" |
65 | | #include "src/xnnpack/init-once.h" |
66 | | #include "src/xnnpack/log.h" |
67 | | |
68 | | #if XNN_ARCH_X86_64 && defined(__linux__) && !defined(CHROMIUM) |
69 | 0 | ssize_t xnn_syscall(size_t rax, size_t rdi, size_t rsi, size_t rdx) { |
70 | 0 | __asm ( |
71 | 0 | "syscall" |
72 | 0 | : "+a" (rax) |
73 | 0 | : "D"(rdi), "S"(rsi), "d"(rdx) |
74 | 0 | : "rcx", "r11", "memory" |
75 | 0 | ); |
76 | 0 | return rax; |
77 | 0 | } |
78 | | #endif |
79 | | |
80 | | #if XNN_ENABLE_CPUINFO |
81 | 1 | static enum xnn_uarch cpuinfo_to_xnn_uarch(enum cpuinfo_uarch uarch) { |
82 | 1 | switch (uarch) { |
83 | 0 | case cpuinfo_uarch_broadwell: return xnn_uarch_broadwell; |
84 | 0 | case cpuinfo_uarch_coyote_cove: return xnn_uarch_coyote_cove; |
85 | 0 | case cpuinfo_uarch_crestmont: return xnn_uarch_crestmont; |
86 | 0 | case cpuinfo_uarch_darkmont: return xnn_uarch_darkmont; |
87 | 0 | case cpuinfo_uarch_dhyana: return xnn_uarch_dhyana; |
88 | 0 | case cpuinfo_uarch_golden_cove: return xnn_uarch_golden_cove; |
89 | 0 | case cpuinfo_uarch_gracemont: return xnn_uarch_gracemont; |
90 | 0 | case cpuinfo_uarch_haswell: return xnn_uarch_haswell; |
91 | 0 | case cpuinfo_uarch_ivy_bridge: return xnn_uarch_ivy_bridge; |
92 | 0 | case cpuinfo_uarch_palm_cove: return xnn_uarch_palm_cove; |
93 | 0 | case cpuinfo_uarch_raptor_cove: return xnn_uarch_raptor_cove; |
94 | 0 | case cpuinfo_uarch_redwood_cove: return xnn_uarch_redwood_cove; |
95 | 0 | case cpuinfo_uarch_sky_lake: return xnn_uarch_sky_lake; |
96 | 0 | case cpuinfo_uarch_sunny_cove: return xnn_uarch_sunny_cove; |
97 | 0 | case cpuinfo_uarch_willow_cove: return xnn_uarch_willow_cove; |
98 | 0 | case cpuinfo_uarch_zen: return xnn_uarch_zen; |
99 | 1 | case cpuinfo_uarch_zen2: return xnn_uarch_zen2; |
100 | 0 | case cpuinfo_uarch_zen3: return xnn_uarch_zen3; |
101 | 0 | case cpuinfo_uarch_zen4: return xnn_uarch_zen4; |
102 | 0 | case cpuinfo_uarch_zen5: return xnn_uarch_zen5; |
103 | 0 | case cpuinfo_uarch_zen6: return xnn_uarch_zen6; |
104 | | |
105 | 0 | case cpuinfo_uarch_avalanche: return xnn_uarch_avalanche; |
106 | 0 | case cpuinfo_uarch_blizzard: return xnn_uarch_blizzard; |
107 | 0 | case cpuinfo_uarch_coll_everest: return xnn_uarch_coll_everest; |
108 | 0 | case cpuinfo_uarch_coll_sawtooth: return xnn_uarch_coll_sawtooth; |
109 | 0 | case cpuinfo_uarch_cortex_a5: return xnn_uarch_cortex_a5; |
110 | 0 | case cpuinfo_uarch_cortex_a7: return xnn_uarch_cortex_a7; |
111 | 0 | case cpuinfo_uarch_cortex_a32: return xnn_uarch_cortex_a32; |
112 | 0 | case cpuinfo_uarch_cortex_a35: return xnn_uarch_cortex_a35; |
113 | 0 | case cpuinfo_uarch_cortex_a53: return xnn_uarch_cortex_a53; |
114 | 0 | case cpuinfo_uarch_cortex_a55r0: return xnn_uarch_cortex_a55r0; |
115 | 0 | case cpuinfo_uarch_cortex_a55: return xnn_uarch_cortex_a55; |
116 | 0 | case cpuinfo_uarch_cortex_a57: return xnn_uarch_cortex_a57; |
117 | 0 | case cpuinfo_uarch_cortex_a72: return xnn_uarch_cortex_a72; |
118 | 0 | case cpuinfo_uarch_cortex_a73: return xnn_uarch_cortex_a73; |
119 | 0 | case cpuinfo_uarch_cortex_a75: return xnn_uarch_cortex_a75; |
120 | 0 | case cpuinfo_uarch_cortex_a76: return xnn_uarch_cortex_a76; |
121 | 0 | case cpuinfo_uarch_cortex_a77: return xnn_uarch_cortex_a77; |
122 | 0 | case cpuinfo_uarch_cortex_a78: return xnn_uarch_cortex_a78; |
123 | 0 | case cpuinfo_uarch_cortex_a320: return xnn_uarch_cortex_a320; |
124 | 0 | case cpuinfo_uarch_cortex_a510: return xnn_uarch_cortex_a510; |
125 | 0 | case cpuinfo_uarch_cortex_a520: return xnn_uarch_cortex_a520; |
126 | 0 | case cpuinfo_uarch_cortex_a710: return xnn_uarch_cortex_a710; |
127 | 0 | case cpuinfo_uarch_cortex_a715: return xnn_uarch_cortex_a715; |
128 | 0 | case cpuinfo_uarch_cortex_a720: return xnn_uarch_cortex_a720; |
129 | 0 | case cpuinfo_uarch_cortex_a725: return xnn_uarch_cortex_a725; |
130 | 0 | case cpuinfo_uarch_cortex_x1: return xnn_uarch_cortex_x1; |
131 | 0 | case cpuinfo_uarch_cortex_x2: return xnn_uarch_cortex_x2; |
132 | 0 | case cpuinfo_uarch_cortex_x3: return xnn_uarch_cortex_x3; |
133 | 0 | case cpuinfo_uarch_cortex_x4: return xnn_uarch_cortex_x4; |
134 | 0 | case cpuinfo_uarch_cortex_x925: return xnn_uarch_cortex_x925; |
135 | 0 | case cpuinfo_uarch_donan_everest: return xnn_uarch_donan_everest; |
136 | 0 | case cpuinfo_uarch_donan_sawtooth: return xnn_uarch_donan_sawtooth; |
137 | 0 | case cpuinfo_uarch_everest: return xnn_uarch_everest; |
138 | 0 | case cpuinfo_uarch_exynos_m1: return xnn_uarch_exynos_m1; |
139 | 0 | case cpuinfo_uarch_exynos_m2: return xnn_uarch_exynos_m2; |
140 | 0 | case cpuinfo_uarch_exynos_m3: return xnn_uarch_exynos_m3; |
141 | 0 | case cpuinfo_uarch_exynos_m4: return xnn_uarch_exynos_m4; |
142 | 0 | case cpuinfo_uarch_exynos_m5: return xnn_uarch_exynos_m5; |
143 | 0 | case cpuinfo_uarch_firestorm: return xnn_uarch_firestorm; |
144 | 0 | case cpuinfo_uarch_icestorm: return xnn_uarch_icestorm; |
145 | 0 | case cpuinfo_uarch_krait: return xnn_uarch_krait; |
146 | 0 | case cpuinfo_uarch_kryo: return xnn_uarch_kryo; |
147 | 0 | case cpuinfo_uarch_lumex_c1_nano: return xnn_uarch_lumex_c1_nano; |
148 | 0 | case cpuinfo_uarch_lumex_c1_premium: return xnn_uarch_lumex_c1_premium; |
149 | 0 | case cpuinfo_uarch_lumex_c1_pro: return xnn_uarch_lumex_c1_pro; |
150 | 0 | case cpuinfo_uarch_lumex_c1_ultra: return xnn_uarch_lumex_c1_ultra; |
151 | 0 | case cpuinfo_uarch_neoverse_n1: return xnn_uarch_neoverse_n1; |
152 | 0 | case cpuinfo_uarch_neoverse_n2: return xnn_uarch_neoverse_n2; |
153 | 0 | case cpuinfo_uarch_neoverse_v1: return xnn_uarch_neoverse_v1; |
154 | 0 | case cpuinfo_uarch_neoverse_v2: return xnn_uarch_neoverse_v2; |
155 | 0 | case cpuinfo_uarch_oryon: return xnn_uarch_oryon; |
156 | 0 | case cpuinfo_uarch_oryon_v3: return xnn_uarch_oryon_v3; |
157 | 0 | case cpuinfo_uarch_sawtooth: return xnn_uarch_sawtooth; |
158 | 0 | case cpuinfo_uarch_sotra_performance: return xnn_uarch_sotra_performance; |
159 | 0 | case cpuinfo_uarch_sotra_super: return xnn_uarch_sotra_super; |
160 | 0 | case cpuinfo_uarch_tahiti_everest: return xnn_uarch_tahiti_everest; |
161 | 0 | case cpuinfo_uarch_tahiti_sawtooth: return xnn_uarch_tahiti_sawtooth; |
162 | 0 | case cpuinfo_uarch_tilos_everest: return xnn_uarch_tilos_everest; |
163 | 0 | case cpuinfo_uarch_tilos_sawtooth: return xnn_uarch_tilos_sawtooth; |
164 | 0 | case cpuinfo_uarch_tupai_everest: return xnn_uarch_tupai_everest; |
165 | 0 | case cpuinfo_uarch_tupai_sawtooth: return xnn_uarch_tupai_sawtooth; |
166 | 0 | default: return xnn_uarch_unknown; |
167 | 1 | } |
168 | 1 | } |
169 | | #endif // XNN_ENABLE_CPUINFO |
170 | | |
171 | | #if XNN_ENABLE_CPUINFO && XNN_ARCH_ARM |
172 | | static bool cpuinfo_uarch_to_dot(enum cpuinfo_uarch uarch) { |
173 | | switch (uarch) { |
174 | | case cpuinfo_uarch_cortex_a76: return true; |
175 | | case cpuinfo_uarch_cortex_a77: return true; |
176 | | case cpuinfo_uarch_cortex_a78: return true; |
177 | | case cpuinfo_uarch_cortex_a320: return true; |
178 | | case cpuinfo_uarch_cortex_a510: return true; |
179 | | case cpuinfo_uarch_cortex_a520: return true; |
180 | | case cpuinfo_uarch_cortex_a710: return true; |
181 | | case cpuinfo_uarch_cortex_a715: return true; |
182 | | case cpuinfo_uarch_cortex_a720: return true; |
183 | | case cpuinfo_uarch_cortex_a725: return true; |
184 | | case cpuinfo_uarch_cortex_x1: return true; |
185 | | case cpuinfo_uarch_cortex_x2: return true; |
186 | | case cpuinfo_uarch_cortex_x3: return true; |
187 | | case cpuinfo_uarch_cortex_x4: return true; |
188 | | case cpuinfo_uarch_cortex_x925: return true; |
189 | | case cpuinfo_uarch_lumex_c1_nano: return true; |
190 | | case cpuinfo_uarch_lumex_c1_premium: return true; |
191 | | case cpuinfo_uarch_lumex_c1_pro: return true; |
192 | | case cpuinfo_uarch_lumex_c1_ultra: return true; |
193 | | case cpuinfo_uarch_neoverse_n2: return true; |
194 | | case cpuinfo_uarch_neoverse_v2: return true; |
195 | | case cpuinfo_uarch_oryon: return true; |
196 | | case cpuinfo_uarch_oryon_v3: return true; |
197 | | default: return false; |
198 | | } |
199 | | } |
200 | | #endif // XNN_ENABLE_CPUINFO && XNN_ARCH_ARM |
201 | | |
202 | | #if XNN_ARCH_RISCV |
203 | | |
204 | | #include <unistd.h> |
205 | | #include <sys/syscall.h> |
206 | | |
207 | | extern long syscall(long number, ...); |
208 | | |
209 | | /* Constants for kernels where headers might be missing */ |
210 | | #ifndef __NR_riscv_hwprobe |
211 | | #define __NR_riscv_hwprobe 258 |
212 | | #endif |
213 | | |
214 | | #ifndef RISCV_HWPROBE_KEY_MVENDORID |
215 | | #define RISCV_HWPROBE_KEY_MVENDORID 0 |
216 | | #endif |
217 | | |
218 | | #ifndef RISCV_HWPROBE_KEY_MARCHID |
219 | | #define RISCV_HWPROBE_KEY_MARCHID 1 |
220 | | #endif |
221 | | |
222 | | #ifndef RISCV_HWPROBE_KEY_MIMPID |
223 | | #define RISCV_HWPROBE_KEY_MIMPID 2 |
224 | | #endif |
225 | | |
226 | | #ifndef RISCV_HWPROBE_KEY_IMA_EXT_0 |
227 | | #define RISCV_HWPROBE_KEY_IMA_EXT_0 4 |
228 | | #endif |
229 | | |
230 | | #ifndef RISCV_HWPROBE_EXT_ZFH |
231 | | #define RISCV_HWPROBE_EXT_ZFH (1ULL << 27) |
232 | | #endif |
233 | | |
234 | | #ifndef RISCV_HWPROBE_EXT_ZVFH |
235 | | /* Bit 30 represents the Zvfh (Vector Float16) extension */ |
236 | | #define RISCV_HWPROBE_EXT_ZVFH (1ULL << 30) |
237 | | #endif |
238 | | |
239 | | struct riscv_hwprobe { |
240 | | int64_t key; |
241 | | uint64_t value; |
242 | | }; |
243 | | |
244 | | enum riscv_chipset_vendor { |
245 | | riscv_chipset_vendor_unknown = 0, |
246 | | riscv_chipset_vendor_sifive = 0x489, |
247 | | }; |
248 | | |
249 | | static enum xnn_uarch riscv_decode_vendor_uarch( |
250 | | uint32_t vendor_id, |
251 | | uint32_t arch_id, |
252 | | uint32_t imp_id) { |
253 | | switch (vendor_id) { |
254 | | case riscv_chipset_vendor_sifive: |
255 | | switch (arch_id & 0x7FFFFFFFU) { |
256 | | case 0x00000907: |
257 | | case 0x00FFFB07: |
258 | | return xnn_uarch_sifive_x280; |
259 | | case 0x00001007: |
260 | | case 0x00FFFC07: |
261 | | return xnn_uarch_sifive_x390; |
262 | | default: |
263 | | if (imp_id == 0x07230908) { |
264 | | return xnn_uarch_sifive_x280; |
265 | | } |
266 | | break; |
267 | | } |
268 | | break; |
269 | | default: |
270 | | break; |
271 | | } |
272 | | return xnn_uarch_unknown; |
273 | | } |
274 | | |
275 | | static bool riscv_uarch_to_fp16(enum xnn_uarch uarch) { |
276 | | switch (uarch) { |
277 | | case xnn_uarch_sifive_x280: |
278 | | case xnn_uarch_sifive_x390: |
279 | | return true; |
280 | | default: |
281 | | return false; |
282 | | } |
283 | | } |
284 | | |
285 | | static void riscv_decode_hwprobe( |
286 | | enum xnn_uarch* uarch, |
287 | | bool* supports_fp16) { |
288 | | struct riscv_hwprobe pairs[] = { |
289 | | {.key = RISCV_HWPROBE_KEY_MVENDORID}, |
290 | | {.key = RISCV_HWPROBE_KEY_MARCHID}, |
291 | | {.key = RISCV_HWPROBE_KEY_MIMPID}, |
292 | | {.key = RISCV_HWPROBE_KEY_IMA_EXT_0}, |
293 | | }; |
294 | | const size_t pairs_count = sizeof(pairs) / sizeof(struct riscv_hwprobe); |
295 | | |
296 | | *uarch = xnn_uarch_unknown; |
297 | | *supports_fp16 = false; |
298 | | |
299 | | if (syscall(__NR_riscv_hwprobe, pairs, pairs_count, 0, NULL, 0) != 0) { |
300 | | return; |
301 | | } |
302 | | |
303 | | uint32_t vendor_id = 0; |
304 | | uint32_t arch_id = 0; |
305 | | uint32_t imp_id = 0; |
306 | | uint64_t ima_ext_0 = 0; |
307 | | for (size_t pair = 0; pair < pairs_count; pair++) { |
308 | | switch (pairs[pair].key) { |
309 | | case RISCV_HWPROBE_KEY_MVENDORID: |
310 | | vendor_id = (uint32_t) pairs[pair].value; |
311 | | break; |
312 | | case RISCV_HWPROBE_KEY_MARCHID: |
313 | | arch_id = (uint32_t) pairs[pair].value; |
314 | | break; |
315 | | case RISCV_HWPROBE_KEY_MIMPID: |
316 | | imp_id = (uint32_t) pairs[pair].value; |
317 | | break; |
318 | | case RISCV_HWPROBE_KEY_IMA_EXT_0: |
319 | | ima_ext_0 = pairs[pair].value; |
320 | | break; |
321 | | default: |
322 | | break; |
323 | | } |
324 | | } |
325 | | xnn_log_debug( |
326 | | "riscv_hwprobe: vendor_id=0x%" PRIx32 ", arch_id=0x%" PRIx32 |
327 | | ", imp_id=0x%" PRIx32 ", ima_ext_0=0x%" PRIx64, |
328 | | vendor_id, arch_id, imp_id, ima_ext_0); |
329 | | |
330 | | *uarch = riscv_decode_vendor_uarch(vendor_id, arch_id, imp_id); |
331 | | *supports_fp16 = |
332 | | ((ima_ext_0 & RISCV_HWPROBE_EXT_ZVFH) != 0) || riscv_uarch_to_fp16(*uarch); |
333 | | } |
334 | | #endif // XNN_ARCH_RISCV |
335 | | |
336 | | static struct xnn_hardware_config hardware_config = {0}; |
337 | | |
338 | | static struct xnn_hardware_config original_hardware_config = {0}; |
339 | | static bool original_hardware_config_saved = false; |
340 | | |
341 | | XNN_INIT_ONCE_GUARD(hardware); |
342 | | |
343 | 23 | static void set_arch_flag(uint64_t flag, bool value) { |
344 | 23 | if (value) { |
345 | 8 | hardware_config.arch_flags |= flag; |
346 | 15 | } else { |
347 | 15 | hardware_config.arch_flags &= ~flag; |
348 | 15 | } |
349 | 23 | } |
350 | | |
351 | 1 | static void init_hardware_config(void) { |
352 | 1 | if (original_hardware_config_saved) { |
353 | 0 | return; |
354 | 0 | } |
355 | 1 | hardware_config.arch_flags = 0; |
356 | 2 | for (size_t i = 0; i < XNN_MAX_UARCH_TYPES; ++i) { |
357 | 1 | hardware_config.uarch[i] = xnn_uarch_unknown; |
358 | 1 | } |
359 | | #if (XNN_ARCH_ARM64 || XNN_ARCH_ARM) && XNN_ENABLE_CPUINFO |
360 | | #if XNN_PLATFORM_WINDOWS |
361 | | SYSTEM_INFO system_info; |
362 | | GetSystemInfo(&system_info); |
363 | | switch (system_info.wProcessorLevel) { |
364 | | case 0x803: // Kryo 385 Silver |
365 | | set_arch_flag(xnn_arch_arm_neon_fp16_arith, true); |
366 | | break; |
367 | | default: |
368 | | // Assume that Dot Product support implies FP16 support. |
369 | | // ARM manuals don't guarantee that, but it holds in practice. |
370 | | set_arch_flag(xnn_arch_arm_neon_fp16_arith, |
371 | | !!IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE)); |
372 | | break; |
373 | | } |
374 | | set_arch_flag(xnn_arch_arm_fp16_arith, hardware_config.arch_flags & xnn_arch_arm_neon_fp16_arith); |
375 | | |
376 | | set_arch_flag(xnn_arch_arm_neon_bf16, false); |
377 | | set_arch_flag(xnn_arch_arm_neon_dot, !!IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE)); |
378 | | #else |
379 | | set_arch_flag(xnn_arch_arm_fp16_arith, cpuinfo_has_arm_fp16_arith()); |
380 | | set_arch_flag(xnn_arch_arm_neon_fp16_arith, cpuinfo_has_arm_neon_fp16_arith()); |
381 | | set_arch_flag(xnn_arch_arm_neon_bf16, cpuinfo_has_arm_neon_bf16()); |
382 | | #endif |
383 | | set_arch_flag(xnn_arch_arm_vfpv3, cpuinfo_has_arm_vfpv3()); |
384 | | set_arch_flag(xnn_arch_arm_neon, cpuinfo_has_arm_neon()); |
385 | | set_arch_flag(xnn_arch_arm_neon_fp16, cpuinfo_has_arm_neon_fp16()); |
386 | | set_arch_flag(xnn_arch_arm_neon_fma, cpuinfo_has_arm_neon_fma()); |
387 | | set_arch_flag(xnn_arch_arm_neon_v8, cpuinfo_has_arm_neon_v8()); |
388 | | #endif |
389 | | |
390 | | #if XNN_ARCH_ARM && XNN_ENABLE_CPUINFO |
391 | | set_arch_flag(xnn_arch_arm_v6, cpuinfo_has_arm_v6()); |
392 | | set_arch_flag(xnn_arch_arm_vfpv2, cpuinfo_has_arm_vfpv2()); |
393 | | |
394 | | // TODO(b/435053808) Fix dot product detect on arm |
395 | | const struct cpuinfo_uarch_info* uarch = cpuinfo_get_uarch(0); |
396 | | bool uarch_dot = uarch ? cpuinfo_uarch_to_dot(uarch->uarch) : false; |
397 | | set_arch_flag(xnn_arch_arm_neon_dot, uarch_dot); |
398 | | #endif |
399 | | |
400 | | #if XNN_ARCH_ARM64 && XNN_ENABLE_CPUINFO |
401 | | set_arch_flag(xnn_arch_arm_neon_dot, cpuinfo_has_arm_neon_dot()); |
402 | | set_arch_flag(xnn_arch_arm_neon_i8mm, cpuinfo_has_arm_i8mm()); |
403 | | #if !XNN_COMPILER_HAS_FEATURE(memory_sanitizer) |
404 | | // msan (understandably) does not support SVE/SME (b/494230133). |
405 | | set_arch_flag(xnn_arch_arm_sve, cpuinfo_has_arm_sve()); |
406 | | set_arch_flag(xnn_arch_arm_sve2, cpuinfo_has_arm_sve2()); |
407 | | // TODO(b/581442477): Remove once |
408 | | // https://github.com/pytorch/cpuinfo/issues/442 is fixed and smelen is |
409 | | // implemented for Windows ARM. |
410 | | const bool use_arm_sme = |
411 | | cpuinfo_has_arm_sme() && cpuinfo_get_max_arm_sme_length() > 0; |
412 | | set_arch_flag(xnn_arch_arm_sme, use_arm_sme); |
413 | | set_arch_flag(xnn_arch_arm_sme2, use_arm_sme && cpuinfo_has_arm_sme2()); |
414 | | set_arch_flag(xnn_arch_arm_sme2p1, use_arm_sme && cpuinfo_has_arm_sme2p1()); |
415 | | #endif // !XNN_COMPILER_HAS_FEATURE(memory_sanitizer) |
416 | | #endif |
417 | | |
418 | 1 | #if (XNN_ARCH_X86 || XNN_ARCH_X86_64) && XNN_ENABLE_CPUINFO |
419 | 1 | const bool use_x86_avx512skx = XNN_ENABLE_AVX512SKX && cpuinfo_has_x86_avx512f() && cpuinfo_has_x86_avx512bw() && cpuinfo_has_x86_avx512dq() && cpuinfo_has_x86_avx512vl(); |
420 | 1 | const bool use_x86_avx512vnni = XNN_ENABLE_AVX512VNNI && use_x86_avx512skx && cpuinfo_has_x86_avx512vnni(); |
421 | 1 | const bool use_x86_avx512vnnigfni = XNN_ENABLE_AVX512VNNIGFNI && use_x86_avx512vnni && cpuinfo_has_x86_gfni(); |
422 | 1 | const bool use_x86_avx512amx = XNN_ENABLE_AVX512AMX && XNN_ARCH_X86_64 && use_x86_avx512vnnigfni && cpuinfo_has_x86_amx_int8(); |
423 | 1 | const bool use_x86_avx512amxfp16 = XNN_ENABLE_AVX512AMXFP16 && XNN_ARCH_X86_64 && use_x86_avx512vnnigfni && cpuinfo_has_x86_avx512fp16() && cpuinfo_has_x86_amx_fp16(); |
424 | | #if XNN_ENABLE_ACE && XNN_ARCH_X86_64 && \ |
425 | | (defined(__clang__) || defined(__GNUC__)) |
426 | | // ACE microkernels use GCC/Clang inline asm; other compilers get stubs. |
427 | | const bool use_x86_ace = use_x86_avx512vnnigfni && |
428 | | cpuinfo_has_x86_avx10_2() && |
429 | | cpuinfo_has_x86_amx_tile() && cpuinfo_has_x86_ace(); |
430 | | #else |
431 | 1 | const bool use_x86_ace = false; |
432 | 1 | #endif |
433 | 1 | const bool use_x86_avx256skx = XNN_ENABLE_AVX256SKX && cpuinfo_has_x86_avx512f() && cpuinfo_has_x86_avx512bw() && cpuinfo_has_x86_avx512dq() && cpuinfo_has_x86_avx512vl(); |
434 | 1 | const bool use_x86_avx256vnni = XNN_ENABLE_AVX256VNNI && use_x86_avx256skx && cpuinfo_has_x86_avx512vnni(); |
435 | | |
436 | 1 | set_arch_flag(xnn_arch_x86_sse, XNN_ENABLE_SSE && cpuinfo_has_x86_sse()); |
437 | 1 | set_arch_flag(xnn_arch_x86_sse2, XNN_ENABLE_SSE2 && cpuinfo_has_x86_sse2()); |
438 | 1 | set_arch_flag(xnn_arch_x86_ssse3, XNN_ENABLE_SSSE3 && cpuinfo_has_x86_ssse3()); |
439 | 1 | set_arch_flag(xnn_arch_x86_sse4_1, XNN_ENABLE_SSE41 && cpuinfo_has_x86_sse4_1()); |
440 | 1 | set_arch_flag(xnn_arch_x86_avx, XNN_ENABLE_AVX && cpuinfo_has_x86_avx()); |
441 | 1 | set_arch_flag(xnn_arch_x86_f16c, XNN_ENABLE_F16C && cpuinfo_has_x86_f16c()); |
442 | 1 | set_arch_flag(xnn_arch_x86_fma3, XNN_ENABLE_FMA3 && cpuinfo_has_x86_fma3()); |
443 | 1 | set_arch_flag(xnn_arch_x86_avx2, XNN_ENABLE_AVX2 && cpuinfo_has_x86_avx2()); |
444 | 1 | set_arch_flag(xnn_arch_x86_avx512f, XNN_ENABLE_AVX512F && cpuinfo_has_x86_avx512f()); |
445 | 1 | set_arch_flag(xnn_arch_x86_avx512skx, use_x86_avx512skx); |
446 | 1 | set_arch_flag(xnn_arch_x86_avx512vbmi, XNN_ENABLE_AVX512VBMI && use_x86_avx512skx && cpuinfo_has_x86_avx512vbmi()); |
447 | 1 | set_arch_flag(xnn_arch_x86_avx512vnni, use_x86_avx512vnni); |
448 | 1 | set_arch_flag(xnn_arch_x86_avx512vnnigfni, use_x86_avx512vnnigfni); |
449 | 1 | set_arch_flag(xnn_arch_x86_avx512fp16, XNN_ENABLE_AVX512FP16 && cpuinfo_has_x86_avx512fp16()); |
450 | 1 | set_arch_flag(xnn_arch_x86_avx512bf16, XNN_ENABLE_AVX512BF16 && cpuinfo_has_x86_avx512bf16()); |
451 | 1 | set_arch_flag(xnn_arch_x86_avx512amx, use_x86_avx512amx); |
452 | 1 | set_arch_flag(xnn_arch_x86_avx512amxfp16, use_x86_avx512amxfp16); |
453 | 1 | set_arch_flag(xnn_arch_x86_ace, use_x86_ace); |
454 | 1 | set_arch_flag(xnn_arch_x86_avxvnni, XNN_ENABLE_AVXVNNI && cpuinfo_has_x86_avx2() && cpuinfo_has_x86_avxvnni() && cpuinfo_has_x86_gfni()); |
455 | 1 | set_arch_flag(xnn_arch_x86_avxvnniint8, XNN_ENABLE_AVXVNNIINT8 && cpuinfo_has_x86_avx2() && cpuinfo_has_x86_avx_vnni_int8() && cpuinfo_has_x86_gfni()); |
456 | 1 | set_arch_flag(xnn_arch_x86_avx256skx, use_x86_avx256skx); |
457 | 1 | set_arch_flag(xnn_arch_x86_avx256vnni, use_x86_avx256vnni); |
458 | 1 | set_arch_flag(xnn_arch_x86_avx256vnnigfni, XNN_ENABLE_AVX256VNNIGFNI && use_x86_avx256vnni && cpuinfo_has_x86_gfni()); |
459 | 1 | #if XNN_ARCH_X86_64 && defined(__linux__) && !defined(CHROMIUM) |
460 | 1 | if (use_x86_avx512amx || use_x86_avx512amxfp16 || use_x86_ace) { |
461 | 0 | size_t status = xnn_syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_PERM, XFEATURE_XTILEDATA, 0); |
462 | 0 | if (status) { |
463 | 0 | xnn_log_info("XFEATURE_XTILEDATA setup is failed, TMUL usage is not allowed"); |
464 | 0 | set_arch_flag(xnn_arch_x86_avx512amx, 0); |
465 | 0 | set_arch_flag(xnn_arch_x86_avx512amxfp16, 0); |
466 | 0 | set_arch_flag(xnn_arch_x86_ace, 0); |
467 | 0 | } |
468 | 0 | } |
469 | 1 | #endif |
470 | 1 | #endif // !XNN_ARCH_X86 && !XNN_ARCH_X86_64 |
471 | | |
472 | | #if (XNN_ARCH_X86 || XNN_ARCH_X86_64) && !XNN_ENABLE_CPUINFO |
473 | | // Without cpuinfo respect the build flags |
474 | | set_arch_flag(xnn_arch_x86_sse, XNN_ENABLE_SSE); |
475 | | set_arch_flag(xnn_arch_x86_sse2, XNN_ENABLE_SSE2); |
476 | | set_arch_flag(xnn_arch_x86_ssse3, XNN_ENABLE_SSSE3); |
477 | | set_arch_flag(xnn_arch_x86_sse4_1, XNN_ENABLE_SSE41); |
478 | | set_arch_flag(xnn_arch_x86_avx, XNN_ENABLE_AVX); |
479 | | set_arch_flag(xnn_arch_x86_f16c, XNN_ENABLE_F16C); |
480 | | set_arch_flag(xnn_arch_x86_fma3, XNN_ENABLE_FMA3); |
481 | | set_arch_flag(xnn_arch_x86_avx2, XNN_ENABLE_AVX2); |
482 | | #endif |
483 | | |
484 | | #if XNN_ARCH_HEXAGON |
485 | | qurt_arch_version_t vers = {0}; |
486 | | unsigned int arch_version = 0; |
487 | | if (qurt_sysenv_get_arch_version(&vers) == QURT_EOK) { |
488 | | // Lower 8 bits represents the version number in hex form |
489 | | arch_version = vers.arch_version & 0xff; |
490 | | } |
491 | | |
492 | | // TODO: use xnn_log_info |
493 | | FARF(ALWAYS, "HEXAGON UARCH VERSION %x\n", arch_version); |
494 | | |
495 | | // v69 is in Samsung S22 |
496 | | if (arch_version >= 0x69) { |
497 | | set_arch_flag(xnn_arch_hvx, XNN_ENABLE_HVX); |
498 | | } |
499 | | |
500 | | #if XNN_ENABLE_HMX |
501 | | // HMX FP16 is supported on Hexagon V73+ (Snapdragon 8 Gen 2 and newer) when |
502 | | // the HMX hardware unit is present, can be powered on, and can be locked in |
503 | | // the current protection domain. |
504 | | if (arch_version >= 0x73) { |
505 | | int power_ctx = 0; |
506 | | HAP_power_request_t request = {0}; |
507 | | request.type = HAP_power_set_HMX; |
508 | | request.hmx.power_up = 1; |
509 | | if (HAP_power_set(&power_ctx, &request) == AEE_SUCCESS) { |
510 | | compute_res_attr_t compute_res; |
511 | | HAP_compute_res_attr_init(&compute_res); |
512 | | HAP_compute_res_attr_set_hmx_param(&compute_res, 1); |
513 | | unsigned int context_id = HAP_compute_res_acquire(&compute_res, 10000); |
514 | | if (context_id != 0) { |
515 | | if (HAP_compute_res_hmx_lock(context_id) == 0) { |
516 | | set_arch_flag(xnn_arch_hmx, true); |
517 | | HAP_compute_res_hmx_unlock(context_id); |
518 | | } |
519 | | HAP_compute_res_release(context_id); |
520 | | } |
521 | | request.hmx.power_up = 0; |
522 | | HAP_power_set(&power_ctx, &request); |
523 | | } |
524 | | HAP_power_destroy(&power_ctx); |
525 | | } |
526 | | #endif // XNN_ENABLE_HMX |
527 | | |
528 | | unsigned int max_hthreads = 1; |
529 | | qurt_sysenv_max_hthreads_t hardware_threads = {0}; |
530 | | if (qurt_sysenv_get_max_hw_threads(&hardware_threads) == QURT_EOK) { |
531 | | max_hthreads = hardware_threads.max_hthreads; |
532 | | } |
533 | | FARF(ALWAYS, "HEXAGON HTHREADS %d\n", max_hthreads); |
534 | | |
535 | | unsigned int clkFreqHz = 0; |
536 | | HAP_power_response_t response = {.type = HAP_power_get_clk_Freq}; |
537 | | if (HAP_power_get(NULL, &response) == AEE_SUCCESS) { |
538 | | clkFreqHz = response.clkFreqHz; |
539 | | } |
540 | | FARF(ALWAYS, "HEXAGON CLKFREQHZ %d\n", clkFreqHz); |
541 | | #endif // XNN_ARCH_HEXAGON |
542 | | |
543 | | #if XNN_ARCH_RISCV |
544 | | // TODO: Use cpuinfo when fp16 detect works |
545 | | const long hwcap = getauxval(AT_HWCAP); |
546 | | xnn_log_debug("getauxval(AT_HWCAP) = %08lX", hwcap); |
547 | | const bool use_riscv_vector = (hwcap & COMPAT_HWCAP_ISA_V) != 0; |
548 | | set_arch_flag(xnn_arch_riscv_vector, use_riscv_vector); |
549 | | |
550 | | enum xnn_uarch uarch = xnn_uarch_unknown; |
551 | | bool supports_fp16 = false; |
552 | | riscv_decode_hwprobe(&uarch, &supports_fp16); |
553 | | hardware_config.uarch[0] = uarch; |
554 | | set_arch_flag(xnn_arch_riscv_vector_fp16_arith, XNN_ENABLE_RISCV_FP16_VECTOR && use_riscv_vector && supports_fp16); |
555 | | |
556 | | if (use_riscv_vector) { |
557 | | register uint32_t vlenb __asm__ ("t0"); |
558 | | __asm__(".word 0xC22022F3" /* CSRR t0, vlenb */ : "=r" (vlenb)); |
559 | | hardware_config.vlenb = vlenb; |
560 | | xnn_log_info("RISC-V VLENB: %" PRIu32, vlenb); |
561 | | } |
562 | | #endif |
563 | | |
564 | | #if XNN_ARCH_PPC64 |
565 | | #if defined(__FreeBSD__) |
566 | | unsigned long HWCAPs = 0, HWCAPs_2 = 0; |
567 | | elf_aux_info(AT_HWCAP, &HWCAPs, sizeof(HWCAPs)); |
568 | | elf_aux_info(AT_HWCAP2, &HWCAPs_2, sizeof(HWCAPs_2)); |
569 | | #else |
570 | | const unsigned long HWCAPs = getauxval(AT_HWCAP); |
571 | | const unsigned long HWCAPs_2 = getauxval(AT_HWCAP2); |
572 | | #endif |
573 | | if (HWCAPs & PPC_FEATURE_HAS_VSX) { |
574 | | set_arch_flag(xnn_arch_vsx, 1); |
575 | | } |
576 | | #if defined PPC_FEATURE2_ARCH_3_1 |
577 | | if (HWCAPs_2 & PPC_FEATURE2_ARCH_3_1) { |
578 | | set_arch_flag(xnn_arch_vsx3, 1); |
579 | | } |
580 | | if (HWCAPs_2 & PPC_FEATURE2_MMA) { |
581 | | set_arch_flag(xnn_arch_mma, 1); |
582 | | } |
583 | | #endif |
584 | | #endif |
585 | | |
586 | | #if XNN_ARCH_WASM || XNN_ARCH_WASMSIMD || XNN_ARCH_WASMRELAXEDSIMD |
587 | | // Unlike most other architectures, on x86/x86-64 when floating-point instructions |
588 | | // have no NaN arguments, but produce NaN output, the output NaN has sign bit set. |
589 | | // We use it to distinguish x86/x86-64 from other architectures, by doing subtraction |
590 | | // of two infinities (must produce NaN per IEEE 754 standard). |
591 | | static const volatile float inf = INFINITY; |
592 | | hardware_config.is_x86 = signbit(inf - inf); |
593 | | #endif // XNN_ARCH_WASM || XNN_ARCH_WASMSIMD || XNN_ARCH_WASMRELAXEDSIMD |
594 | | |
595 | | #if XNN_ARCH_WASMRELAXEDSIMD |
596 | | { |
597 | | // Check if out-of-bounds behavior of Relaxed Swizzle is consistent with PSHUFB. |
598 | | const v128_t table = wasm_i8x16_const(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); |
599 | | const v128_t index_mask = wasm_i8x16_const_splat(INT8_C(0x8F)); |
600 | | const volatile v128_t index_increment = wasm_i8x16_const_splat(16); // volatile to confuse Clang which otherwise mis-compiles |
601 | | v128_t index = wasm_i8x16_const(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); |
602 | | v128_t diff = wasm_i8x16_const_splat(0); |
603 | | for (uint32_t i = 16; i != 0; i--) { |
604 | | const v128_t pshufb_result = wasm_i8x16_swizzle(table, wasm_v128_and(index, index_mask)); |
605 | | const v128_t relaxed_result = wasm_i8x16_relaxed_swizzle(table, index); |
606 | | diff = wasm_v128_or(diff, wasm_v128_xor(pshufb_result, relaxed_result)); |
607 | | index = wasm_i8x16_add(index, index_increment); |
608 | | } |
609 | | set_arch_flag(xnn_arch_wasm_pshufb, !wasm_v128_any_true(diff)); |
610 | | } |
611 | | |
612 | | { |
613 | | // Check out-of-bounds behaviour of Relaxed Integer Dot Product with Accumulation. |
614 | | const v128_t int8_input = wasm_i8x16_const(0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0); |
615 | | const volatile v128_t xint8_input = wasm_i8x16_const(0, 0, 0, -128, 0, 0, -128, 0, 0, -128, 0, 0, -128, 0, 0, 0); // volatile to confuse Clang which otherwise ICE's |
616 | | const v128_t xint8_output = wasm_i32x4_relaxed_dot_i8x16_i7x16_add(int8_input, xint8_input, wasm_i8x16_const_splat(0)); |
617 | | |
618 | | const volatile v128_t overflow_input = wasm_i8x16_const(-128, -128, -128, -128, -128, -128, -1, -1, -1, -1, -128, -128, -1, -1, -1, -1); // volatile to confuse Clang which otherwise ICE's |
619 | | const v128_t overflow_output = wasm_i32x4_relaxed_dot_i8x16_i7x16_add(wasm_i8x16_const_splat(-128), overflow_input, wasm_i8x16_const_splat(0)); |
620 | | set_arch_flag(xnn_arch_wasm_sdot, !wasm_v128_any_true(wasm_v128_or( |
621 | | wasm_v128_xor(xint8_output, wasm_i32x4_const_splat(-128)), |
622 | | wasm_v128_xor(overflow_output, wasm_i32x4_const(65536, 33024, 33024, 512))))); |
623 | | } |
624 | | { |
625 | | // Check out-of-bounds behaviour of Relaxed Integer Dot Product with Accumulation with signed and unsigned input (e.g. vpdpbusd). |
626 | | const v128_t int8_input = wasm_i8x16_const(0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0); |
627 | | const volatile v128_t xint8_input = wasm_i8x16_const(0, 0, 0, -128, 0, 0, -128, 0, 0, -128, 0, 0, -128, 0, 0, 0); // volatile to confuse Clang which otherwise ICE's |
628 | | const v128_t xint8_output = wasm_i32x4_relaxed_dot_i8x16_i7x16_add(int8_input, xint8_input, wasm_i8x16_const_splat(0)); |
629 | | |
630 | | const volatile v128_t overflow_input = wasm_i8x16_const(-128, -128, -128, -128, -128, -128, -1, -1, -1, -1, -128, -128, -1, -1, -1, -1); // volatile to confuse Clang which otherwise ICE's |
631 | | const v128_t overflow_output = wasm_i32x4_relaxed_dot_i8x16_i7x16_add(wasm_i8x16_const_splat(-128), overflow_input, wasm_i8x16_const_splat(0)); |
632 | | set_arch_flag(xnn_arch_wasm_usdot, !wasm_v128_any_true(wasm_v128_or( |
633 | | wasm_v128_xor(xint8_output, wasm_i32x4_const_splat(128)), |
634 | | wasm_v128_xor(overflow_output, wasm_i32x4_const(-65536, -98048, -98048, -130560))))); |
635 | | } |
636 | | { |
637 | | const v128_t input1 = wasm_i32x4_const(0xF0F0F0F0, 0xAAAAAAAA, 0xCCCCCCCC, 0x99999999); |
638 | | const v128_t input2 = wasm_i32x4_const(0x0F0F0F0F, 0x55555555, 0x33333333, 0x66666666); |
639 | | v128_t diff = wasm_i8x16_const_splat(0); |
640 | | for (uint32_t shift = 0; shift < 32; ++shift) { |
641 | | const uint32_t mask = UINT32_C(1) << shift; |
642 | | const volatile v128_t vmask = wasm_u32x4_splat(mask); |
643 | | const v128_t blendvps_result = wasm_v128_bitselect(input1, input2, wasm_i32x4_shr(vmask, 31)); |
644 | | const v128_t relaxed_result = wasm_i32x4_relaxed_laneselect(input1, input2, vmask); |
645 | | diff = wasm_v128_or(diff, wasm_v128_xor(blendvps_result, relaxed_result)); |
646 | | } |
647 | | set_arch_flag(xnn_arch_wasm_blendvps, !wasm_v128_any_true(diff)); |
648 | | } |
649 | | { |
650 | | const v128_t input1 = wasm_f32x4_const(16777218.f, 0.f, 0.f, 0.f); |
651 | | const v128_t input2 = wasm_f32x4_const(3.f, 0.f, 0.f, 0.f); |
652 | | const v128_t input3 = wasm_f32x4_const(3.f, 0.f, 0.f, 0.f); |
653 | | v128_t diff = wasm_i8x16_const_splat(0); |
654 | | const v128_t relaxed_result = wasm_f32x4_relaxed_madd(input1, input2, input3); |
655 | | const v128_t mul_result = wasm_f32x4_add(input3, wasm_f32x4_mul(input1, input2)); |
656 | | diff = wasm_v128_or(diff, wasm_v128_xor(mul_result, relaxed_result)); |
657 | | set_arch_flag(xnn_arch_wasm_fma, !wasm_v128_any_true(diff)); |
658 | | } |
659 | | #endif // XNN_ARCH_WASMRELAXEDSIMD |
660 | | |
661 | 1 | #if XNN_ENABLE_CPUINFO |
662 | | // Set the size of the L1 and L2 data caches. |
663 | 1 | if (!cpuinfo_initialize()) { |
664 | 0 | xnn_log_warning( |
665 | 0 | "Failed to initialize cpuinfo, unable to determine L1/L2 data cache " |
666 | 0 | "properties."); |
667 | 1 | } else { |
668 | 1 | const struct cpuinfo_processor* proc_info = cpuinfo_get_processor(0); |
669 | 1 | if (proc_info != NULL) { |
670 | | // Get the L1 cache information. |
671 | 1 | const struct cpuinfo_cache* l1_data_cache = proc_info->cache.l1d; |
672 | 1 | if (l1_data_cache != NULL) { |
673 | 1 | hardware_config.l1_data_cache_bytes = l1_data_cache->size; |
674 | 1 | hardware_config.l1_data_cache_line_size = l1_data_cache->line_size; |
675 | 1 | hardware_config.l1_data_cache_associativity = |
676 | 1 | l1_data_cache->associativity; |
677 | 1 | hardware_config.l1_data_cache_num_sets = l1_data_cache->sets; |
678 | 1 | xnn_log_info( |
679 | 1 | "l1_data_cache_bytes=%zu, l1_data_cache_line_size=%zu, " |
680 | 1 | "l1_data_cache_associativity=%zu, l1_data_cache_num_sets=%zu.", |
681 | 1 | hardware_config.l1_data_cache_bytes, |
682 | 1 | hardware_config.l1_data_cache_line_size, |
683 | 1 | hardware_config.l1_data_cache_associativity, |
684 | 1 | hardware_config.l1_data_cache_num_sets); |
685 | 1 | } else { |
686 | 0 | xnn_log_warning("Unable to determine L1 data cache properties."); |
687 | 0 | } |
688 | | |
689 | | // Get the L2 cache information. |
690 | 1 | const struct cpuinfo_cache* l2_data_cache = proc_info->cache.l2; |
691 | 1 | if (l2_data_cache != NULL) { |
692 | 1 | hardware_config.l2_data_cache_bytes = l2_data_cache->size; |
693 | 1 | hardware_config.l2_data_cache_line_size = l2_data_cache->line_size; |
694 | 1 | hardware_config.l2_data_cache_associativity = |
695 | 1 | l2_data_cache->associativity; |
696 | 1 | hardware_config.l2_data_cache_num_sets = l2_data_cache->sets; |
697 | 1 | xnn_log_info( |
698 | 1 | "l2_data_cache_bytes=%zu, l2_data_cache_line_size=%zu, " |
699 | 1 | "l2_data_cache_associativity=%zu, l2_data_cache_num_sets=%zu.", |
700 | 1 | hardware_config.l2_data_cache_bytes, |
701 | 1 | hardware_config.l2_data_cache_line_size, |
702 | 1 | hardware_config.l2_data_cache_associativity, |
703 | 1 | hardware_config.l2_data_cache_num_sets); |
704 | 1 | } else { |
705 | 0 | xnn_log_warning("Unable to determine L2 data cache properties."); |
706 | 0 | } |
707 | 1 | } else { |
708 | 0 | xnn_log_warning("Unable to determine L1/L2 data cache properties."); |
709 | 0 | } |
710 | 1 | } |
711 | | |
712 | | #if XNN_MAX_UARCH_TYPES > 1 |
713 | | // Print what we think we know about the microarchs. |
714 | | xnn_log_info("cpuinfo_get_uarchs_count: %u.", cpuinfo_get_uarchs_count()); |
715 | | for (int i = 0; i < cpuinfo_get_uarchs_count(); i++) { |
716 | | xnn_log_info("cpu_get_uarch(%i): 0x%x", i, cpuinfo_get_uarch(i)->uarch); |
717 | | } |
718 | | #endif // XNN_MAX_UARCH_TYPES > 1 |
719 | 1 | #if !XNN_ARCH_RISCV |
720 | 2 | for (size_t i = 0; i < XNN_MAX_UARCH_TYPES; ++i) { |
721 | 1 | const struct cpuinfo_uarch_info* uarch = cpuinfo_get_uarch(i); |
722 | 1 | hardware_config.uarch[i] = uarch ? cpuinfo_to_xnn_uarch(uarch->uarch) : xnn_uarch_unknown; |
723 | 1 | } |
724 | 1 | #endif // !XNN_ARCH_RISCV |
725 | | #else |
726 | | xnn_log_warning("Unable to determine L1/L2 data cache properties."); |
727 | | #endif // XNN_ENABLE_CPUINFO |
728 | 1 | } |
729 | | |
730 | 347 | const struct xnn_hardware_config* xnn_init_hardware_config() { |
731 | 347 | #if !XNN_PLATFORM_WEB && !XNN_ARCH_RISCV && !XNN_ARCH_PPC64 && XNN_ENABLE_CPUINFO |
732 | 347 | if (!cpuinfo_initialize()) { |
733 | 0 | xnn_log_error("failed to initialize cpuinfo"); |
734 | 0 | return NULL; |
735 | 0 | } |
736 | 347 | #endif // !XNN_PLATFORM_WEB && !XNN_ARCH_RISCV && !XNN_ARCH_PPC64 |
737 | | #if XNN_ARCH_ARM |
738 | | if (!cpuinfo_has_arm_v6()) { |
739 | | xnn_log_debug("unsupported hardware: ARMv6 not detected"); |
740 | | return NULL; |
741 | | } |
742 | | |
743 | | if (!cpuinfo_has_arm_vfpv2() && !cpuinfo_has_arm_vfpv3()) { |
744 | | xnn_log_debug("unsupported hardware: VFP FPU not detected"); |
745 | | return NULL; |
746 | | } |
747 | | #endif // XNN_ARCH_ARM |
748 | | #if XNN_ARCH_X86 |
749 | | if (!cpuinfo_has_x86_sse2()) { |
750 | | xnn_log_debug("unsupported hardware: SSE2 not detected"); |
751 | | return NULL; |
752 | | } |
753 | | #endif // XNN_ARCH_X86 |
754 | | |
755 | 347 | XNN_INIT_ONCE(hardware); |
756 | 347 | return &hardware_config; |
757 | 347 | } |
758 | | |
759 | 0 | void xnn_set_hardware_config(const struct xnn_hardware_config* config) { |
760 | 0 | const struct xnn_hardware_config* current = xnn_init_hardware_config(); |
761 | 0 | if (current != NULL && !original_hardware_config_saved) { |
762 | 0 | original_hardware_config = hardware_config; |
763 | 0 | original_hardware_config_saved = true; |
764 | 0 | } |
765 | 0 | if (config != NULL) { |
766 | 0 | hardware_config = *config; |
767 | 0 | } |
768 | 0 | xnn_reset_all_init_guards(); |
769 | 0 | } |
770 | | |
771 | 0 | void xnn_reset_hardware_config(void) { |
772 | 0 | if (original_hardware_config_saved) { |
773 | 0 | hardware_config = original_hardware_config; |
774 | 0 | original_hardware_config_saved = false; |
775 | 0 | } else { |
776 | 0 | xnn_init_hardware_config(); |
777 | 0 | } |
778 | 0 | xnn_reset_all_init_guards(); |
779 | 0 | } |