chromium/third_party/libyuv/source/cpu_id.cc

/*
 *  Copyright 2011 The LibYuv 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 in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS. All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "libyuv/cpu_id.h"

#if defined(_MSC_VER)
#include <intrin.h>  // For __cpuidex()
#endif
#if !defined(__pnacl__) && !defined(__CLR_VER) &&                           \
    !defined(__native_client__) && (defined(_M_IX86) || defined(_M_X64)) && \
    defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 160040219)
#include <immintrin.h>  // For _xgetbv()
#endif

// For ArmCpuCaps() but unittested on all platforms
#include <stdio.h>  // For fopen()
#include <string.h>

#if defined(__linux__) && defined(__aarch64__)
#include <sys/auxv.h>  // For getauxval()
#endif

#if defined(_WIN32) && defined(__aarch64__)
#undef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#undef WIN32_EXTRA_LEAN
#define WIN32_EXTRA_LEAN
#include <windows.h>  // For IsProcessorFeaturePresent()
#endif

#if defined(__APPLE__) && defined(__aarch64__)
#include <sys/sysctl.h>  // For sysctlbyname()
#endif

#ifdef __cplusplus
namespace libyuv {
extern "C" {
#endif

// For functions that use the stack and have runtime checks for overflow,
// use SAFEBUFFERS to avoid additional check.
#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 160040219) && \
    !defined(__clang__)
#define SAFEBUFFERS
#else
#define SAFEBUFFERS
#endif

// cpu_info_ variable for SIMD instruction sets detected.
LIBYUV_API int cpu_info_ =;

// Low level cpuid for X86.
#if (defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \
     defined(__x86_64__)) &&                                     \
    !defined(__pnacl__) && !defined(__CLR_VER)
LIBYUV_API
void CpuId(int info_eax, int info_ecx, int* cpu_info) {}
#else  // (defined(_M_IX86) || defined(_M_X64) ...
LIBYUV_API
void CpuId(int eax, int ecx, int* cpu_info) {
  (void)eax;
  (void)ecx;
  cpu_info[0] = cpu_info[1] = cpu_info[2] = cpu_info[3] = 0;
}
#endif

// For VS2010 and earlier emit can be used:
//   _asm _emit 0x0f _asm _emit 0x01 _asm _emit 0xd0  // For VS2010 and earlier.
//  __asm {
//    xor        ecx, ecx    // xcr 0
//    xgetbv
//    mov        xcr0, eax
//  }
// For VS2013 and earlier 32 bit, the _xgetbv(0) optimizer produces bad code.
// https://code.google.com/p/libyuv/issues/detail?id=529
#if defined(_M_IX86) && defined(_MSC_VER) && (_MSC_VER < 1900)
#pragma optimize("g", off)
#endif
#if (defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \
     defined(__x86_64__)) &&                                     \
    !defined(__pnacl__) && !defined(__CLR_VER) && !defined(__native_client__)
// X86 CPUs have xgetbv to detect OS saves high parts of ymm registers.
static int GetXCR0() {}
#else
// xgetbv unavailable to query for OSSave support.  Return 0.
#define GetXCR0
#endif  // defined(_M_IX86) || defined(_M_X64) ..
// Return optimization to previous setting.
#if defined(_M_IX86) && defined(_MSC_VER) && (_MSC_VER < 1900)
#pragma optimize("g", on)
#endif

static int cpuinfo_search(const char* cpuinfo_line,
                          const char* needle,
                          int needle_len) {}

// Based on libvpx arm_cpudetect.c
// For Arm, but public to allow testing on any CPU
LIBYUV_API SAFEBUFFERS int ArmCpuCaps(const char* cpuinfo_name) {}

#ifdef __aarch64__
#ifdef __linux__
// Define hwcap values ourselves: building with an old auxv header where these
// hwcap values are not defined should not prevent features from being enabled.
#define YUV_AARCH64_HWCAP_ASIMDDP
#define YUV_AARCH64_HWCAP_SVE
#define YUV_AARCH64_HWCAP2_SVE2
#define YUV_AARCH64_HWCAP2_I8MM
#define YUV_AARCH64_HWCAP2_SME

// For AArch64, but public to allow testing on any CPU.
LIBYUV_API SAFEBUFFERS int AArch64CpuCaps(unsigned long hwcap,
                                          unsigned long hwcap2) {
  // Neon is mandatory on AArch64, so enable regardless of hwcaps.
  int features = kCpuHasNEON;

  // Don't try to enable later extensions unless earlier extensions are also
  // reported available. Some of these constraints aren't strictly required by
  // the architecture, but are satisfied by all micro-architectures of
  // interest. This also avoids an issue on some emulators where true
  // architectural constraints are not satisfied, e.g. SVE2 may be reported as
  // available while SVE is not.
  if (hwcap & YUV_AARCH64_HWCAP_ASIMDDP) {
    features |= kCpuHasNeonDotProd;
    if (hwcap2 & YUV_AARCH64_HWCAP2_I8MM) {
      features |= kCpuHasNeonI8MM;
      if (hwcap & YUV_AARCH64_HWCAP_SVE) {
        features |= kCpuHasSVE;
        if (hwcap2 & YUV_AARCH64_HWCAP2_SVE2) {
          features |= kCpuHasSVE2;
          if (hwcap2 & YUV_AARCH64_HWCAP2_SME) {
            features |= kCpuHasSME;
          }
        }
      }
    }
  }
  return features;
}

#elif defined(_WIN32)
// For AArch64, but public to allow testing on any CPU.
LIBYUV_API SAFEBUFFERS int AArch64CpuCaps() {
  // Neon is mandatory on AArch64, so enable unconditionally.
  int features = kCpuHasNEON;

  // For more information on IsProcessorFeaturePresent(), see:
  // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-isprocessorfeaturepresent#parameters
#ifdef PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE
  if (IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE)) {
    features |= kCpuHasNeonDotProd;
  }
#endif
  // No Neon I8MM or SVE feature detection available here at time of writing.
  return features;
}

#elif defined(__APPLE__)
static bool have_feature(const char* feature) {
  // For more information on sysctlbyname(), see:
  // https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_instruction_set_characteristics
  int64_t feature_present = 0;
  size_t size = sizeof(feature_present);
  if (sysctlbyname(feature, &feature_present, &size, NULL, 0) != 0) {
    return false;
  }
  return feature_present;
}

// For AArch64, but public to allow testing on any CPU.
LIBYUV_API SAFEBUFFERS int AArch64CpuCaps() {
  // Neon is mandatory on AArch64, so enable unconditionally.
  int features = kCpuHasNEON;

  if (have_feature("hw.optional.arm.FEAT_DotProd")) {
    features |= kCpuHasNeonDotProd;
    if (have_feature("hw.optional.arm.FEAT_I8MM")) {
      features |= kCpuHasNeonI8MM;
      if (have_feature("hw.optional.arm.FEAT_SME2")) {
        features |= kCpuHasSME;
      }
    }
  }
  // No SVE feature detection available here at time of writing.
  return features;
}

#else  // !defined(__linux__) && !defined(_WIN32) && !defined(__APPLE__)
// For AArch64, but public to allow testing on any CPU.
LIBYUV_API SAFEBUFFERS int AArch64CpuCaps() {
  // Neon is mandatory on AArch64, so enable unconditionally.
  int features = kCpuHasNEON;

  // TODO(libyuv:980) support feature detection on other platforms.

  return features;
}
#endif
#endif  // defined(__aarch64__)

LIBYUV_API SAFEBUFFERS int RiscvCpuCaps(const char* cpuinfo_name) {}

LIBYUV_API SAFEBUFFERS int MipsCpuCaps(const char* cpuinfo_name) {}

#define LOONGARCH_CFG2
#define LOONGARCH_CFG2_LSX
#define LOONGARCH_CFG2_LASX

#if defined(__loongarch__)
LIBYUV_API SAFEBUFFERS int LoongarchCpuCaps(void) {
  int flag = 0;
  uint32_t cfg2 = 0;

  __asm__ volatile("cpucfg %0, %1 \n\t" : "+&r"(cfg2) : "r"(LOONGARCH_CFG2));

  if (cfg2 & LOONGARCH_CFG2_LSX)
    flag |= kCpuHasLSX;

  if (cfg2 & LOONGARCH_CFG2_LASX)
    flag |= kCpuHasLASX;
  return flag;
}
#endif

static SAFEBUFFERS int GetCpuFlags(void) {}

// Note that use of this function is not thread safe.
LIBYUV_API
int MaskCpuFlags(int enable_flags) {}

LIBYUV_API
int InitCpuFlags(void) {}

#ifdef __cplusplus
}  // extern "C"
}  // namespace libyuv
#endif