chromium/v8/src/base/cpu.cc

// Copyright 2013 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.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
#endif
#if V8_GLIBC_PREREQ(2, 16) || V8_OS_ANDROID
#include <sys/auxv.h>  // getauxval()
#endif
#if V8_OS_QNX
#include <sys/syspage.h>  // cpuinfo
#endif
#if V8_OS_LINUX && V8_HOST_ARCH_PPC64
#include <elf.h>
#endif
#if V8_OS_AIX
#include <sys/systemcfg.h>  // _system_configuration
#ifndef POWER_8
#define POWER_8
#endif
#ifndef POWER_9
#define POWER_9
#endif
#ifndef POWER_10
#define POWER_10
#endif
#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/logging.h"
#include "src/base/platform/wrappers.h"
#if V8_OS_WIN
#include <windows.h>
#endif

namespace v8 {
namespace base {

#if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64

// Define __cpuid() for non-MSVC libraries.
#if !V8_LIBC_MSVCRT

static V8_INLINE void __cpuid(int cpu_info[4], int info_type) {}

static V8_INLINE void __cpuidex(int cpu_info[4], int info_type,
                                int sub_info_type) {}

#endif  // !V8_LIBC_MSVCRT

#elif V8_HOST_ARCH_ARM || V8_HOST_ARCH_ARM64 || V8_HOST_ARCH_MIPS64 || \
    V8_HOST_ARCH_RISCV64

#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
#define HWCAP_HALF
#define HWCAP_THUMB
#define HWCAP_26BIT
#define HWCAP_FAST_MULT
#define HWCAP_FPA
#define HWCAP_VFP
#define HWCAP_EDSP
#define HWCAP_JAVA
#define HWCAP_IWMMXT
#define HWCAP_CRUNCH
#define HWCAP_THUMBEE
#define HWCAP_NEON
#define HWCAP_VFPv3
#define HWCAP_VFPv3D16
#define HWCAP_TLS
#define HWCAP_VFPv4
#define HWCAP_IDIVA
#define HWCAP_IDIVT
#define HWCAP_VFPD32
#define HWCAP_IDIV
#define HWCAP_LPAE

#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
#define HWCAP_ASIMD
#define HWCAP_EVTSTRM
#define HWCAP_AES
#define HWCAP_PMULL
#define HWCAP_SHA1
#define HWCAP_SHA2
#define HWCAP_CRC32
#define HWCAP_ATOMICS
#define HWCAP_FPHP
#define HWCAP_ASIMDHP
#define HWCAP_CPUID
#define HWCAP_ASIMDRDM
#define HWCAP_JSCVT
#define HWCAP_FCMA
#define HWCAP_LRCPC
#define HWCAP_DCPOP
#define HWCAP_SHA3
#define HWCAP_SM3
#define HWCAP_SM4
#define HWCAP_ASIMDDP
#define HWCAP_SHA512
#define HWCAP_SVE
#define HWCAP_ASIMDFHM
#define HWCAP_DIT
#define HWCAP_USCAT
#define HWCAP_ILRCPC
#define HWCAP_FLAGM
#define HWCAP_SSBS
#define HWCAP_SB
#define HWCAP_PACA
#define HWCAP_PACG
// See <uapi/asm/hwcap.h> kernel header.
/*
 * HWCAP2 flags - for elf_hwcap2 (in kernel) and AT_HWCAP2
 */
#define HWCAP2_MTE
#endif  // V8_HOST_ARCH_ARM64

#if V8_HOST_ARCH_ARM || V8_HOST_ARCH_ARM64

static std::tuple<uint32_t, uint32_t> ReadELFHWCaps() {
  uint32_t hwcap = 0;
  uint32_t hwcap2 = 0;
#if (V8_GLIBC_PREREQ(2, 16) || V8_OS_ANDROID) && defined(AT_HWCAP)
  hwcap = static_cast<uint32_t>(getauxval(AT_HWCAP));
#if defined(AT_HWCAP2)
  hwcap2 = static_cast<uint32_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

// Extract the information exposed by the kernel via /proc/cpuinfo.
class CPUInfo final {
 public:
  CPUInfo() : datalen_(0) {
    // Get the size of the cpuinfo file by reading it until the end. This is
    // required because files under /proc do not always return a valid size
    // when using fseek(0, SEEK_END) + ftell(). Nor can the be mmap()-ed.
    static const char PATHNAME[] = "/proc/cpuinfo";
    FILE* fp = base::Fopen(PATHNAME, "r");
    if (fp != nullptr) {
      for (;;) {
        char buffer[256];
        size_t n = fread(buffer, 1, sizeof(buffer), fp);
        if (n == 0) {
          break;
        }
        datalen_ += n;
      }
      base::Fclose(fp);
    }

    // Read the contents of the cpuinfo file.
    data_ = new char[datalen_ + 1];
    fp = base::Fopen(PATHNAME, "r");
    if (fp != nullptr) {
      for (size_t offset = 0; offset < datalen_; ) {
        size_t n = fread(data_ + offset, 1, datalen_ - offset, fp);
        if (n == 0) {
          break;
        }
        offset += n;
      }
      base::Fclose(fp);
    }

    // Zero-terminate the data.
    data_[datalen_] = '\0';
  }

  ~CPUInfo() {
    delete[] data_;
  }

  // Extract the content of a the first occurrence of a given field in
  // the content of the cpuinfo file and return it as a heap-allocated
  // string that must be freed by the caller using delete[].
  // Return nullptr if not found.
  char* ExtractField(const char* field) const {
    DCHECK_NOT_NULL(field);

    // Look for first field occurrence, and ensure it starts the line.
    size_t fieldlen = strlen(field);
    char* p = data_;
    for (;;) {
      p = strstr(p, field);
      if (p == nullptr) {
        return nullptr;
      }
      if (p == data_ || p[-1] == '\n') {
        break;
      }
      p += fieldlen;
    }

    // Skip to the first colon followed by a space.
    p = strchr(p + fieldlen, ':');
    if (p == nullptr || !isspace(p[1])) {
      return nullptr;
    }
    p += 2;

    // Find the end of the line.
    char* q = strchr(p, '\n');
    if (q == nullptr) {
      q = data_ + datalen_;
    }

    // Copy the line into a heap-allocated buffer.
    size_t len = q - p;
    char* result = new char[len + 1];
    if (result != nullptr) {
      memcpy(result, p, len);
      result[len] = '\0';
    }
    return result;
  }

 private:
  char* data_;
  size_t datalen_;
};

// Checks that a space-separated list of items contains one given 'item'.
static bool HasListItem(const char* list, const char* item) {
  ssize_t item_len = strlen(item);
  const char* p = list;
  if (p != nullptr) {
    while (*p != '\0') {
      // Skip whitespace.
      while (isspace(*p)) ++p;

      // Find end of current list item.
      const char* q = p;
      while (*q != '\0' && !isspace(*q)) ++q;

      if (item_len == q - p && memcmp(p, item, item_len) == 0) {
        return true;
      }

      // Skip to next item.
      p = q;
    }
  }
  return false;
}

#endif  // V8_OS_LINUX

#endif  // V8_HOST_ARCH_ARM || V8_HOST_ARCH_ARM64 ||
        // V8_HOST_ARCH_MIPS64 || V8_HOST_ARCH_RISCV64

#if defined(V8_OS_STARBOARD)

bool CPU::StarboardDetectCPU() {
  SbCPUFeatures features;
  if (!SbCPUFeaturesGet(&features)) {
    return false;
  }
  architecture_ = features.arm.architecture_generation;
  switch (features.architecture) {
    case kSbCPUFeaturesArchitectureArm:
    case kSbCPUFeaturesArchitectureArm64:
      has_neon_ = features.arm.has_neon;
      has_thumb2_ = features.arm.has_thumb2;
      has_vfp_ = features.arm.has_vfp;
      has_vfp3_ = features.arm.has_vfp3;
      has_vfp3_d32_ = features.arm.has_vfp3_d32;
      has_idiva_ = features.arm.has_idiva;
      break;
    case kSbCPUFeaturesArchitectureX86:
    case kSbCPUFeaturesArchitectureX86_64:
      // Following flags are mandatory for V8
      has_cmov_ = features.x86.has_cmov;
      has_sse2_ = features.x86.has_sse2;
      // These flags are optional
      has_sse3_ = features.x86.has_sse3;
      has_ssse3_ = features.x86.has_ssse3;
      has_sse41_ = features.x86.has_sse41;
      has_sahf_ = features.x86.has_sahf;
      has_avx_ = features.x86.has_avx;
      has_avx2_ = features.x86.has_avx2;
      // TODO: Support AVX-VNNI on Starboard
      has_fma3_ = features.x86.has_fma3;
      has_bmi1_ = features.x86.has_bmi1;
      has_bmi2_ = features.x86.has_bmi2;
      has_lzcnt_ = features.x86.has_lzcnt;
      has_popcnt_ = features.x86.has_popcnt;
      has_f16c_ = features.x86.has_f16c;
      break;
    default:
      return false;
  }

  return true;
}

#endif

CPU::CPU()
    :{}

}  // namespace base
}  // namespace v8