/* __builtin_cpu_supports() wrapper that honors GLIBC_TUNABLES=glibc.cpu.hwcaps

   Copyright 2025-2026 Free Software Foundation, Inc.

   This file is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation, either version 3 of the
   License, or (at your option) any later version.

   This file is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#ifndef _CPU_SUPPORTS_H
# define _CPU_SUPPORTS_H

# include <string.h>
# include "attribute.h"

/* The main interface to this module is cpu_supports("feature"),
   which is like __builtin_cpu_supports("feature"), but we also check if
   the feature has been disabled by the GLIBC_TUNABLES env variable.

   Notes:

   You can call this with any "feature" accepted by __builtin_cpu_supports(),
   but we only check that the feature is disabled against the set listed
   in gcc_feature_to_glibc_hwcap() below.

   We don't currently support amalgamated feature checks like
   cpu_supports("feature1+feature2").

   We do not need glibc support for GLIBC_TUNABLES,
   rather mimic that functionality on all systems.  */

# define cpu_supports(feature) \
  (cpu_may_support (feature) && 0 < __builtin_cpu_supports (feature))


/* Check if the feature has been disabled by GLIBC_TUNABLES,
   but do NOT call __builtin_cpu_supports(), as some platforms
   use other interfaces like getauxval() instead.  */

# define cpu_may_support(feature) \
  (hwcap_allowed (gcc_feature_to_glibc_hwcap (feature)))



/* Return the glibc.cpu.hwcaps setting (prepended with "-"),
   corresponding to the passed gcc __builtin_cpu_supports(FEATURE).
   Supported hwcaps can be identified from the bit_cpu_* defines
   in GLIBC's sysdeps/x86/include/cpu-features.h for x86 and
   the HWCAP_* defines in sysdeps/unix/sysv/linux/aarch64/bits/hwcap.h
   for aarch64.
   Note this mapping should resolve at compile time.  */
ATTRIBUTE_PURE
static inline char const *
gcc_feature_to_glibc_hwcap (char const *feature)
{
  char const *hwcap = NULL;

  if (0) {}
  /* x86_64 */
  else if (streq (feature, "avx"))          hwcap = "-AVX";
  else if (streq (feature, "avx2"))         hwcap = "-AVX2";
  else if (streq (feature, "avx512bw"))     hwcap = "-AVX512BW";
  else if (streq (feature, "avx512f"))      hwcap = "-AVX512F";
  else if (streq (feature, "pclmul"))       hwcap = "-PCLMULQDQ";
  else if (streq (feature, "vpclmulqdq"))   hwcap = "-VPCLMULQDQ";
  /* aarch64 */
  else if (streq (feature, "asimd"))        hwcap = "-ASIMD";
  else if (streq (feature, "pmull"))        hwcap = "-PMULL";

  return hwcap;
}

/* Support GLIBC's interface to disable features using:
    export GLIBC_TUNABLES=glibc.cpu.hwcaps=-AVX512F,-AVX2,-AVX,-PMULL
   Return true if the HWCAP is allowed.  */
extern bool hwcap_allowed (char const *glibc_hwcap);

/* Set to true to enable reparsing of GLIBC_TUNABLES on each call.
   This can be useful for testing.  Off by default.  */
extern bool hwcap_allowed_nocache;

#endif /* _CPU_SUPPORTS_H */
