@ 261107.7
current
// Copyright 2026 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/cpu/cpu.h"

#if defined(V8_OS_STARBOARD)
#include "starboard/cpu_features.h"
#endif

#if V8_LIBC_MSVCRT
#include <intrin.h>  // __cpuid()
#endif
#if V8_OS_LINUX
#include <linux/auxvec.h>  // AT_HWCAP
#include <sys/auxv.h>      // getauxval()
#endif
#if V8_OS_QNX
#include <sys/syspage.h>  // cpuinfo
#endif
#if V8_OS_DARWIN
#include <sys/sysctl.h>  // sysctlbyname
#endif
#if V8_OS_POSIX
#include <unistd.h>  // sysconf()
#endif

#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <algorithm>

#include "src/base/cpu/cpu-helper.h"
#include "src/base/logging.h"
#include "src/base/platform/wrappers.h"
#if V8_OS_WIN
#include <windows.h>
#endif

namespace v8::base {

#if V8_OS_LINUX
#if V8_HOST_ARCH_ARM

// See <uapi/asm/hwcap.h> kernel header.
/*
 * HWCAP flags - for elf_hwcap (in kernel) and AT_HWCAP
 */
#define HWCAP_SWP (1 << 0)
#define HWCAP_HALF (1 << 1)
#define HWCAP_THUMB (1 << 2)
#define HWCAP_26BIT (1 << 3) /* Play it safe */
#define HWCAP_FAST_MULT (1 << 4)
#define HWCAP_FPA (1 << 5)
#define HWCAP_VFP (1 << 6)
#define HWCAP_EDSP (1 << 7)
#define HWCAP_JAVA (1 << 8)
#define HWCAP_IWMMXT (1 << 9)
#define HWCAP_CRUNCH (1 << 10)
#define HWCAP_THUMBEE (1 << 11)
#define HWCAP_NEON (1 << 12)
#define HWCAP_VFPv3 (1 << 13)
#define HWCAP_VFPv3D16 (1 << 14) /* also set for VFPv4-D16 */
#define HWCAP_TLS (1 << 15)
#define HWCAP_VFPv4 (1 << 16)
#define HWCAP_IDIVA (1 << 17)
#define HWCAP_IDIVT (1 << 18)
#define HWCAP_VFPD32 (1 << 19) /* set if VFP has 32 regs (not 16) */
#define HWCAP_IDIV (HWCAP_IDIVA | HWCAP_IDIVT)
#define HWCAP_LPAE (1 << 20)

#endif  // V8_HOST_ARCH_ARM

#if V8_HOST_ARCH_ARM64

// See <uapi/asm/hwcap.h> kernel header.
/*
 * HWCAP flags - for elf_hwcap (in kernel) and AT_HWCAP
 */
#define HWCAP_FP (1 << 0)
#define HWCAP_ASIMD (1 << 1)
#define HWCAP_EVTSTRM (1 << 2)
#define HWCAP_AES (1 << 3)
#define HWCAP_PMULL (1 << 4)
#define HWCAP_SHA1 (1 << 5)
#define HWCAP_SHA2 (1 << 6)
#define HWCAP_CRC32 (1 << 7)
#define HWCAP_ATOMICS (1 << 8)
#define HWCAP_FPHP (1 << 9)
#define HWCAP_ASIMDHP (1 << 10)
#define HWCAP_CPUID (1 << 11)
#define HWCAP_ASIMDRDM (1 << 12)
#define HWCAP_JSCVT (1 << 13)
#define HWCAP_FCMA (1 << 14)
#define HWCAP_LRCPC (1 << 15)
#define HWCAP_DCPOP (1 << 16)
#define HWCAP_SHA3 (1 << 17)
#define HWCAP_SM3 (1 << 18)
#define HWCAP_SM4 (1 << 19)
#define HWCAP_ASIMDDP (1 << 20)
#define HWCAP_SHA512 (1 << 21)
#define HWCAP_SVE (1 << 22)
#define HWCAP_ASIMDFHM (1 << 23)
#define HWCAP_DIT (1 << 24)
#define HWCAP_USCAT (1 << 25)
#define HWCAP_ILRCPC (1 << 26)
#define HWCAP_FLAGM (1 << 27)
#define HWCAP_SSBS (1 << 28)
#define HWCAP_SB (1 << 29)
#define HWCAP_PACA (1 << 30)
#define HWCAP_PACG (1UL << 31)
// See <uapi/asm/hwcap.h> kernel header.
/*
 * HWCAP2 flags - for elf_hwcap2 (in kernel) and AT_HWCAP2
 */
#define HWCAP2_SVEBITPERM (1 << 4)
#define HWCAP2_MTE (1 << 18)
#define HWCAP2_CSSC (1UL << 34)
#define HWCAP2_MOPS (1UL << 43)
#define HWCAP2_HBC (1UL << 44)
#endif  // V8_HOST_ARCH_ARM64

#if V8_HOST_ARCH_ARM || V8_HOST_ARCH_ARM64

static std::tuple<uint64_t, uint64_t> ReadELFHWCaps() {
  uint64_t hwcap = 0;
  uint64_t hwcap2 = 0;
#if V8_OS_LINUX && defined(AT_HWCAP)
  hwcap = static_cast<uint64_t>(getauxval(AT_HWCAP));
#if defined(AT_HWCAP2)
  hwcap2 = static_cast<uint64_t>(getauxval(AT_HWCAP2));
#endif  // AT_HWCAP2
#else
  // Read the ELF HWCAP flags by parsing /proc/self/auxv.
  // If getauxval is not available, the kernel/libc is also not new enough to
  // expose hwcap2.
  FILE* fp = base::Fopen("/proc/self/auxv", "r");
  if (fp != nullptr) {
    struct {
      uint32_t tag;
      uint32_t value;
    } entry;
    for (;;) {
      size_t n = fread(&entry, sizeof(entry), 1, fp);
      if (n == 0 || (entry.tag == 0 && entry.value == 0)) {
        break;
      }
      if (entry.tag == AT_HWCAP) {
        hwcap = entry.value;
        break;
      }
    }
    base::Fclose(fp);
  }
#endif
  return std::make_tuple(hwcap, hwcap2);
}
#endif  // V8_HOST_ARCH_ARM || V8_HOST_ARCH_ARM64
#endif  // V8_OS_LINUX

void CPU::DetectFeatures() {
#if V8_HOST_ARCH_ARM

#if V8_OS_LINUX

  CPUInfo cpu_info;

  // Extract implementor from the "CPU implementer" field.
  char* implementer = cpu_info.ExtractField("CPU implementer");
  if (implementer != nullptr) {
    char* end;
    implementer_ = strtol(implementer, &end, 0);
    if (end == implementer) {
      implementer_ = 0;
    }
    delete[] implementer;
  }

  char* variant = cpu_info.ExtractField("CPU variant");
  if (variant != nullptr) {
    char* end;
    variant_ = strtol(variant, &end, 0);
    if (end == variant) {
      variant_ = -1;
    }
    delete[] variant;
  }

  // Extract part number from the "CPU part" field.
  char* part = cpu_info.ExtractField("CPU part");
  if (part != nullptr) {
    char* end;
    part_ = strtol(part, &end, 0);
    if (end == part) {
      part_ = 0;
    }
    delete[] part;
  }

  // Extract architecture from the "CPU Architecture" field.
  // The list is well-known, unlike the output of
  // the 'Processor' field which can vary greatly.
  // See the definition of the 'proc_arch' array in
  // $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
  // same file.
  char* architecture = cpu_info.ExtractField("CPU architecture");
  if (architecture != nullptr) {
    char* end;
    architecture_ = strtol(architecture, &end, 10);
    if (end == architecture) {
      // Kernels older than 3.18 report "CPU architecture: AArch64" on ARMv8.
      if (strcmp(architecture, "AArch64") == 0) {
        architecture_ = 8;
      } else {
        architecture_ = 0;
      }
    }
    delete[] architecture;

    // Unfortunately, it seems that certain ARMv6-based CPUs
    // report an incorrect architecture number of 7!
    //
    // See http://code.google.com/p/android/issues/detail?id=10812
    //
    // We try to correct this by looking at the 'elf_platform'
    // field reported by the 'Processor' field, which is of the
    // form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
    // an ARMv6-one. For example, the Raspberry Pi is one popular
    // ARMv6 device that reports architecture 7.
    if (architecture_ == 7) {
      char* processor = cpu_info.ExtractField("Processor");
      if (HasListItem(processor, "(v6l)")) {
        architecture_ = 6;
      }
      delete[] processor;
    }

    // elf_platform moved to the model name field in Linux v3.8.
    if (architecture_ == 7) {
      char* processor = cpu_info.ExtractField("model name");
      if (HasListItem(processor, "(v6l)")) {
        architecture_ = 6;
      }
      delete[] processor;
    }
  }

  // Try to extract the list of CPU features from ELF hwcaps.
  uint64_t hwcaps = 0;
  uint64_t hwcaps2 = 0;
  std::tie(hwcaps, hwcaps2) = ReadELFHWCaps();
  if (hwcaps != 0) {
    has_idiva_ = (hwcaps & HWCAP_IDIVA) != 0;
    has_neon_ = (hwcaps & HWCAP_NEON) != 0;
    has_vfp_ = (hwcaps & HWCAP_VFP) != 0;
    has_vfp3_ = (hwcaps & (HWCAP_VFPv3 | HWCAP_VFPv3D16 | HWCAP_VFPv4)) != 0;
    has_vfp3_d32_ = (has_vfp3_ && ((hwcaps & HWCAP_VFPv3D16) == 0 ||
                                   (hwcaps & HWCAP_VFPD32) != 0));
  } else {
    // Try to fallback to "Features" CPUInfo field.
    char* features = cpu_info.ExtractField("Features");
    has_idiva_ = HasListItem(features, "idiva");
    has_neon_ = HasListItem(features, "neon");
    has_thumb2_ = HasListItem(features, "thumb2");
    has_vfp_ = HasListItem(features, "vfp");
    if (HasListItem(features, "vfpv3d16")) {
      has_vfp3_ = true;
    } else if (HasListItem(features, "vfpv3")) {
      has_vfp3_ = true;
      has_vfp3_d32_ = true;
    }
    delete[] features;
  }

  // Some old kernels will report vfp not vfpv3. Here we make an attempt
  // to detect vfpv3 by checking for vfp *and* neon, since neon is only
  // available on architectures with vfpv3. Checking neon on its own is
  // not enough as it is possible to have neon without vfp.
  if (has_vfp_ && has_neon_) {
    has_vfp3_ = true;
  }

  // VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
  if (architecture_ < 7 && has_vfp3_) {
    architecture_ = 7;
  }

  // ARMv7 implies Thumb2.
  if (architecture_ >= 7) {
    has_thumb2_ = true;
  }

  // The earliest architecture with Thumb2 is ARMv6T2.
  if (has_thumb2_ && architecture_ < 6) {
    architecture_ = 6;
  }

  // We don't support any FPUs other than VFP.
  has_fpu_ = has_vfp_;

#elif V8_OS_QNX

  uint32_t cpu_flags = SYSPAGE_ENTRY(cpuinfo)->flags;
  if (cpu_flags & ARM_CPU_FLAG_V7) {
    architecture_ = 7;
    has_thumb2_ = true;
  } else if (cpu_flags & ARM_CPU_FLAG_V6) {
    architecture_ = 6;
    // QNX doesn't say if Thumb2 is available.
    // Assume false for the architectures older than ARMv7.
  }
  DCHECK_GE(architecture_, 6);
  has_fpu_ = (cpu_flags & CPU_FLAG_FPU) != 0;
  has_vfp_ = has_fpu_;
  if (cpu_flags & ARM_CPU_FLAG_NEON) {
    has_neon_ = true;
    has_vfp3_ = has_vfp_;
#ifdef ARM_CPU_FLAG_VFP_D32
    has_vfp3_d32_ = (cpu_flags & ARM_CPU_FLAG_VFP_D32) != 0;
#endif
  }
  has_idiva_ = (cpu_flags & ARM_CPU_FLAG_IDIV) != 0;

#endif  // V8_OS_LINUX
#elif V8_HOST_ARCH_ARM64
#ifdef V8_OS_WIN
  // Windows makes high-resolution thread timing information available in
  // user-space.
  has_non_stop_time_stamp_counter_ = true;

  // Defined in winnt.h, but only in 10.0.20348.0 version of the Windows SDK.
  // Copy the value here to support older versions as well.
#if !defined(PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE)
  constexpr int PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE = 44;
#endif
#if !defined(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE)
  constexpr int PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE = 43;
#endif
#if !defined(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE)
  constexpr int PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE = 34;
#endif
#if !defined(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE)
  constexpr int PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE = 30;
#endif
#if !defined(PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE)
  constexpr int PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE = 64;
#endif
#if !defined(PF_ARM_SVE_BITPERM_INSTRUCTIONS_AVAILABLE)
  constexpr int PF_ARM_SVE_BITPERM_INSTRUCTIONS_AVAILABLE = 51;
#endif
#if !defined(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE)
  constexpr int PF_ARM_SVE_INSTRUCTIONS_AVAILABLE = 46;
#endif
#if !defined(PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE)
  constexpr int PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE = 67;
#endif

  has_jscvt_ =
      IsProcessorFeaturePresent(PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE);
  has_dot_prod_ =
      IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE);
  has_lse_ =
      IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE);
  has_pmull1q_ =
      IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
  has_fp16_ = IsProcessorFeaturePresent(PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE);
  has_sha3_ = IsProcessorFeaturePresent(PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE);
  has_sve_ = IsProcessorFeaturePresent(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE);
  has_svebitperm_ =
      IsProcessorFeaturePresent(PF_ARM_SVE_BITPERM_INSTRUCTIONS_AVAILABLE);

#elif V8_OS_LINUX
  // Try to extract the list of CPU features from ELF hwcaps.
  uint64_t hwcaps, hwcaps2;
  std::tie(hwcaps, hwcaps2) = ReadELFHWCaps();
  has_cssc_ = (hwcaps2 & HWCAP2_CSSC) != 0;
  has_mte_ = (hwcaps2 & HWCAP2_MTE) != 0;
  has_hbc_ = (hwcaps2 & HWCAP2_HBC) != 0;
  has_mops_ = (hwcaps2 & HWCAP2_MOPS) != 0;
  has_svebitperm_ = (hwcaps2 & HWCAP2_SVEBITPERM) != 0;
  if (hwcaps != 0) {
    has_jscvt_ = (hwcaps & HWCAP_JSCVT) != 0;
    has_dot_prod_ = (hwcaps & HWCAP_ASIMDDP) != 0;
    has_lse_ = (hwcaps & HWCAP_ATOMICS) != 0;
    has_pmull1q_ = (hwcaps & HWCAP_PMULL) != 0;
    has_fp16_ = (hwcaps & HWCAP_FPHP) != 0;
    has_sha3_ = (hwcaps & HWCAP_SHA3) != 0;
    has_sve_ = (hwcaps & HWCAP_SVE) != 0;
  } else {
    // Try to fallback to "Features" CPUInfo field
    CPUInfo cpu_info;
    char* features = cpu_info.ExtractField("Features");
    has_jscvt_ = HasListItem(features, "jscvt");
    has_dot_prod_ = HasListItem(features, "asimddp");
    has_lse_ = HasListItem(features, "atomics");
    has_pmull1q_ = HasListItem(features, "pmull");
    has_fp16_ = HasListItem(features, "half");
    has_sha3_ = HasListItem(features, "sha3");
    has_sve_ = HasListItem(features, "sve");
    delete[] features;
  }
#elif V8_OS_DARWIN
#if V8_OS_IOS
  int64_t feat_jscvt = 0;
  size_t feat_jscvt_size = sizeof(feat_jscvt);
  if (sysctlbyname("hw.optional.arm.FEAT_JSCVT", &feat_jscvt, &feat_jscvt_size,
                   nullptr, 0) == -1) {
    has_jscvt_ = false;
  } else {
    has_jscvt_ = feat_jscvt;
  }
  int64_t feat_dot_prod = 0;
  size_t feat_dot_prod_size = sizeof(feat_dot_prod);
  if (sysctlbyname("hw.optional.arm.FEAT_DotProd", &feat_dot_prod,
                   &feat_dot_prod_size, nullptr, 0) == -1) {
    has_dot_prod_ = false;
  } else {
    has_dot_prod_ = feat_dot_prod;
  }
  int64_t feat_lse = 0;
  size_t feat_lse_size = sizeof(feat_lse);
  if (sysctlbyname("hw.optional.arm.FEAT_LSE", &feat_lse, &feat_lse_size,
                   nullptr, 0) == -1) {
    has_lse_ = false;
  } else {
    has_lse_ = feat_lse;
  }
  int64_t feat_pmull = 0;
  size_t feat_pmull_size = sizeof(feat_pmull);
  if (sysctlbyname("hw.optional.arm.FEAT_PMULL", &feat_pmull, &feat_pmull_size,
                   nullptr, 0) == -1) {
    has_pmull1q_ = false;
  } else {
    has_pmull1q_ = feat_pmull;
  }
  int64_t fp16 = 0;
  size_t fp16_size = sizeof(fp16);
  if (sysctlbyname("hw.optional.arm.FEAT_FP16", &fp16, &fp16_size, nullptr,
                   0) == -1) {
    has_fp16_ = false;
  } else {
    has_fp16_ = fp16;
  }
  int64_t feat_sha3 = 0;
  size_t feat_sha3_size = sizeof(feat_sha3);
  if (sysctlbyname("hw.optional.arm.FEAT_SHA3", &feat_sha3, &feat_sha3_size,
                   nullptr, 0) == -1) {
    has_sha3_ = false;
  } else {
    has_sha3_ = feat_sha3;
  }
#else
  // ARM64 Macs always have the following features.
  has_jscvt_ = true;
  has_dot_prod_ = true;
  has_lse_ = true;
  has_pmull1q_ = true;
  has_fp16_ = true;
  has_sha3_ = true;
#endif  // V8_OS_IOS
  int64_t feat_cssc = 0;
  size_t feat_cssc_size = sizeof(feat_cssc);
  if (sysctlbyname("hw.optional.arm.FEAT_CSSC", &feat_cssc, &feat_cssc_size,
                   nullptr, 0) == -1) {
    has_cssc_ = false;
  } else {
    has_cssc_ = feat_cssc;
  }
#endif  // V8_OS_WIN
#endif  // V8_HOST_ARCH_ARM64
}
}  // namespace v8::base