Coverage Report

Created: 2026-11-12 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}