1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-21 19:33:03 +04:00
Files
opencv/3rdparty/mlas/lib/platform.cpp
T
omrope79 37c9ab1815 Merge pull request #29226 from omrope79:fix_mlas_x86
Fix MLAS 32-bit x86 build by integrating missing upstream assembly files #29226

### Pull Request Readiness Checklist

This resolves the 32-bit x86 build failure for MLAS. Related to: https://github.com/opencv/opencv/pull/29218

* Added the required `x86` assembly files and headers from upstream.
* Added `__x86_64__` guards around the AMX `syscall` and the FMA3/AVX512F kernel assignments to prevent 32-bit compilation crashes.

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
2026-06-04 23:33:54 +03:00

1096 lines
40 KiB
C++

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
Module Name:
platform.cpp
Abstract:
This module implements logic to select the best configuration for the
this platform.
--*/
#include "mlasi.h"
#ifdef MLAS_USE_SVE
#include "sve/mlasi_sve.h"
#endif
#if defined(MLAS_NEON_INTRINSICS) && defined(MLAS_F16VEC_INTRINSICS_SUPPORTED) && !defined(MLAS_GEMM_ONLY)
#include "erf_neon_fp16.h"
#include "gelu_neon_fp16.h"
#endif
#if defined(USE_KLEIDIAI)
#include "kleidiai/mlasi_kleidiai.h"
#endif
#include <cctype>
#include <cstdlib>
#include <mutex>
#include <thread>
#if defined(MLAS_TARGET_POWER)
#if defined(__linux__)
#include <sys/auxv.h>
#elif defined(_AIX)
#define POWER_10 0x40000
#define POWER_10_ANDUP (POWER_10)
#include <sys/systemcfg.h>
#define __power_10_andup() (_system_configuration.implementation & POWER_10_ANDUP)
#elif defined(__FreeBSD__)
#include <machine/cpu.h>
#include <sys/auxv.h>
#endif
#endif
#if defined(MLAS_TARGET_S390X)
#include <sys/auxv.h>
#endif
#if defined(MLAS_TARGET_RISCV64) && defined(MLAS_USE_RVV) && defined(__linux__)
#include <sys/auxv.h>
#include <asm/hwcap.h>
#ifndef COMPAT_HWCAP_ISA_V
#define COMPAT_HWCAP_ISA_V (1UL << ('V' - 'A'))
#endif
#endif
#if defined(MLAS_TARGET_RISCV64) && defined(MLAS_USE_RVV)
namespace {
bool
MlasStringEqualsIgnoreCase(
const char* value,
const char* expected
)
{
while (*value != '\0' && *expected != '\0') {
const auto lhs = static_cast<unsigned char>(*value);
const auto rhs = static_cast<unsigned char>(*expected);
if (std::tolower(lhs) != std::tolower(rhs)) {
return false;
}
++value;
++expected;
}
return *value == '\0' && *expected == '\0';
}
bool
MlasShouldForceScalarRiscv(
const char* value
)
{
if (value == nullptr || value[0] == '\0') {
return false;
}
return MlasStringEqualsIgnoreCase(value, "1") ||
MlasStringEqualsIgnoreCase(value, "true") ||
MlasStringEqualsIgnoreCase(value, "on") ||
MlasStringEqualsIgnoreCase(value, "yes");
}
} // namespace
#endif
#if defined(MLAS_TARGET_ARM64)
#if defined(_WIN32)
// N.B. Support building with downlevel versions of the Windows SDK.
#ifndef PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE
#define PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE 43
#endif
#if defined(BUILD_MLAS_NO_ONNXRUNTIME)
MLASCPUIDInfo::MLASCPUIDInfo()
{
has_arm_neon_dot_ = (IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) != 0);
// raw hack! Need CPUIDInfo implementation for more precise detection
has_fp16_ = has_arm_neon_dot_;
}
#endif
#elif defined(__linux__)
#include <sys/auxv.h>
#include <asm/hwcap.h>
// N.B. Support building with older versions of asm/hwcap.h that do not define
// this capability bit.
#ifndef HWCAP_ASIMDDP
#define HWCAP_ASIMDDP (1 << 20)
#endif
#ifndef HWCAP2_I8MM
#define HWCAP2_I8MM (1 << 13)
#endif
#ifndef HWCAP2_SVEI8MM
#define HWCAP2_SVEI8MM (1 << 9)
#endif
#ifndef HWCAP2_BF16
#define HWCAP2_BF16 (1 << 14)
#endif
#if defined(BUILD_MLAS_NO_ONNXRUNTIME)
MLASCPUIDInfo::MLASCPUIDInfo()
{
has_arm_neon_dot_ = ((getauxval(AT_HWCAP) & HWCAP_ASIMDDP) != 0);
// raw hack! Need CPUIDInfo implementation for more precise detection
has_fp16_ = has_arm_neon_dot_;
has_arm_neon_i8mm_ = ((getauxval(AT_HWCAP2) & HWCAP2_I8MM) != 0);
has_arm_sve_i8mm_ = ((getauxval(AT_HWCAP2) & HWCAP2_SVEI8MM) != 0);
has_arm_neon_bf16_ = ((getauxval(AT_HWCAP2) & HWCAP2_BF16) != 0);
}
#endif
#else
#if defined(BUILD_MLAS_NO_ONNXRUNTIME)
MLASCPUIDInfo::MLASCPUIDInfo() {}
#endif
#endif // Windows vs Linux vs Unknown
#else // not MLAS_TARGET_ARM64
#if defined(BUILD_MLAS_NO_ONNXRUNTIME)
MLASCPUIDInfo::MLASCPUIDInfo() {}
#endif
#endif // MLAS_TARGET_ARM64
#ifdef MLAS_TARGET_AMD64_IX86
//
// Stores a vector to build a conditional load/store mask for vmaskmovps.
//
MLAS_INTERNAL_DATA MLAS_DECLSPEC_ALIGN(const uint32_t MlasMaskMoveAvx[8], 32) = { 0, 1, 2, 3, 4, 5, 6, 7 };
//
// Stores a table of AVX vmaskmovps/vmaskmovpd load/store masks.
//
MLAS_INTERNAL_DATA MLAS_DECLSPEC_ALIGN(const uint32_t MlasMaskMoveTableAvx[16], 32) = {
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
};
//
// Stores a table of AVX512 opmask register values.
//
MLAS_INTERNAL_DATA MLAS_DECLSPEC_ALIGN(const int16_t MlasOpmask16BitTableAvx512[16], 32) = {
0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F,
0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF,
};
//
// Reads the processor extended control register to determine platform
// capabilities.
//
#if !defined(_XCR_XFEATURE_ENABLED_MASK)
#define _XCR_XFEATURE_ENABLED_MASK 0
#endif
#if !defined(XFEATURE_MASK_XTILE)
#define XFEATURE_XTILECFG 17
#define XFEATURE_XTILEDATA 18
#define XFEATURE_MASK_XTILECFG (1 << XFEATURE_XTILECFG)
#define XFEATURE_MASK_XTILEDATA (1 << XFEATURE_XTILEDATA)
#define XFEATURE_MASK_XTILE (XFEATURE_MASK_XTILECFG | XFEATURE_MASK_XTILEDATA)
#endif
inline
uint64_t
MlasReadExtendedControlRegister(
unsigned int ext_ctrl_reg
)
{
#if defined(_WIN32)
return _xgetbv(ext_ctrl_reg);
#else
uint32_t eax, edx;
__asm__
(
"xgetbv"
: "=a" (eax), "=d" (edx)
: "c" (ext_ctrl_reg)
);
return ((uint64_t)edx << 32) | eax;
#endif
}
#if defined(__linux__)
#include <sys/syscall.h>
#endif
bool
MlasInitAMX()
{
#if defined(__linux__)
#define ARCH_GET_XCOMP_PERM 0x1022
#define ARCH_REQ_XCOMP_PERM 0x1023
unsigned long bitmask = 0;
long rc = syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_PERM, XFEATURE_XTILEDATA);
if (rc) {
return false;
}
rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_PERM, &bitmask);
if (rc) {
return false;
}
if (bitmask & XFEATURE_MASK_XTILE) {
return true;
}
return false;
#else
return true;
#endif
}
#endif // MLAS_TARGET_AMD64_IX86
#ifdef MLAS_TARGET_LARCH64
#if defined(__linux__)
#include <sys/auxv.h>
#include <asm/hwcap.h>
#endif
//
// Stores a vector to build a conditional load/store mask for vmaskmovps.
//
MLAS_INTERNAL_DATA MLAS_DECLSPEC_ALIGN(const uint32_t MlasMaskMoveLasx[8], 32) = { 0, 1, 2, 3, 4, 5, 6, 7 };
//
// Stores a table of AVX vmaskmovps/vmaskmovpd load/store masks.
//
MLAS_INTERNAL_DATA MLAS_DECLSPEC_ALIGN(const uint32_t MlasMaskMoveTableLasx[16], 32) = {
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
};
#endif
// =============================================================================
// SGEMM-only constructor (vendor-local patch).
//
// When MLAS_GEMM_ONLY is defined, replace the original platform-init ctor
// with a stripped-down version that only assigns the four (-ish) dispatch
// fields read by sgemm.cpp:
// - GemmFloatKernel
// - KernelM1Routine (x86_64 only)
// - KernelM1TransposeBRoutine (x86_64 only)
// - TransposePackB16x4Routine (x86_64 / loongarch only)
// Plus, on the SBGemm aarch64+linux path, the SBGemm batch overrides — but
// those are nullptr-default and we don't enable SBGemm here.
//
// Also initializes the two softmax kernel pointers consumed by
// flashattn.cpp (ReduceMaximumF32Kernel, ComputeSumExpF32Kernel) to the
// portable fallbacks provided by compute.cpp. No SIMD-asm softmax kernels
// are vendored — the flash-attention path uses the portable C++ rowmax /
// sum-exp implementations.
//
// Every other dispatch field stays at its in-class default (most are
// `= nullptr`). Calling any non-SGEMM / non-FlashAttention MLAS API in this
// build is undefined.
//
// The original full ORT ctor is preserved unchanged below the #else for
// future re-vendoring — drop MLAS_GEMM_ONLY to use it.
// =============================================================================
#ifdef MLAS_GEMM_ONLY
MLAS_PLATFORM::MLAS_PLATFORM(void)
{
// Portable softmax kernels (compute.cpp). flashattn.cpp dereferences these
// function pointers on the AMD64 / LARCH64 path; compute.cpp's
// MlasComputeSoftmax does the same on AMD64 / LARCH64 / SVE / RISCV64.
// Other paths call the symbols directly. Gates mirror the MLAS_PLATFORM
// member visibility in mlasi.h so we initialize the field wherever it
// exists — leaving it null would crash any future code that reads it via
// the struct on those targets.
#if defined(MLAS_TARGET_LARCH64) || defined(MLAS_USE_SVE) || \
defined(MLAS_TARGET_AMD64) || defined(MLAS_TARGET_RISCV64)
this->ReduceMaximumF32Kernel = MlasReduceMaximumF32Kernel;
#endif
#if defined(MLAS_USE_SVE) || defined(MLAS_TARGET_AMD64) || defined(MLAS_TARGET_RISCV64)
this->ComputeSumExpF32Kernel = MlasComputeSumExpF32Kernel;
#endif
// The PreferredBufferAlignment field only exists on AMD64 (see
// MLAS_PLATFORM in mlasi.h). On other targets MlasGetPreferredBufferAlignment()
// returns MLAS_DEFAULT_PREFERRED_BUFFER_ALIGNMENT directly without
// consulting the struct.
#if defined(MLAS_TARGET_AMD64)
this->PreferredBufferAlignment = MLAS_DEFAULT_PREFERRED_BUFFER_ALIGNMENT;
#endif
#if defined(MLAS_TARGET_AMD64_IX86)
// SSE2 baseline (every x86 since 2003).
this->GemmFloatKernel = MlasGemmFloatKernelSse;
#if defined(MLAS_TARGET_AMD64)
this->TransposePackB16x4Routine = MlasSgemmTransposePackB16x4Sse;
#endif
unsigned Cpuid1[4];
#if defined(_WIN32)
__cpuid((int*)Cpuid1, 1);
#else
__cpuid(1, Cpuid1[0], Cpuid1[1], Cpuid1[2], Cpuid1[3]);
#endif
// AVX + OSXSAVE bits (matches the original ctor's checks).
if ((Cpuid1[2] & 0x18000000) == 0x18000000) {
uint64_t xcr0 = MlasReadExtendedControlRegister(_XCR_XFEATURE_ENABLED_MASK);
if ((xcr0 & 0x6) == 0x6) {
this->GemmFloatKernel = MlasGemmFloatKernelAvx;
#if defined(MLAS_TARGET_AMD64)
this->KernelM1Routine = MlasSgemmKernelM1Avx;
this->KernelM1TransposeBRoutine = MlasSgemmKernelM1TransposeBAvx;
this->TransposePackB16x4Routine = MlasSgemmTransposePackB16x4Avx;
// FMA3/AVX512F kernels exist for x86_64 only; 32-bit x86 stops at AVX.
unsigned Cpuid7[4];
#if defined(_WIN32)
__cpuidex((int*)Cpuid7, 7, 0);
#else
__cpuid_count(7, 0, Cpuid7[0], Cpuid7[1], Cpuid7[2], Cpuid7[3]);
#endif
// AVX2 + FMA3.
if (((Cpuid1[2] & 0x1000) != 0) && ((Cpuid7[1] & 0x20) != 0)) {
this->GemmFloatKernel = MlasGemmFloatKernelFma3;
// AVX-512F + ZMM-state save.
if (((Cpuid7[1] & 0x10000) != 0) && ((xcr0 & 0xE0) == 0xE0)) {
this->GemmFloatKernel = MlasGemmFloatKernelAvx512F;
}
}
#endif // MLAS_TARGET_AMD64
}
}
#endif // MLAS_TARGET_AMD64_IX86
#if defined(MLAS_TARGET_POWER)
// Default to the base SgemmKernelPower; the POWER10 detection branch in
// the original ctor is omitted because the POWER10 SgemmKernel symbol
// (MlasSgemmKernelPOWER10) is only present when -mcpu=power10 was
// detectable at configure time. CMake conditionally compiles it; the
// base kernel is always available.
this->GemmFloatKernel = MlasSgemmKernel;
#endif
#if defined(MLAS_TARGET_S390X)
this->GemmFloatKernel = MlasSgemmKernel;
#endif
#if defined(MLAS_TARGET_RISCV64)
this->GemmFloatKernel = nullptr;
#if defined(MLAS_USE_RVV)
bool has_rvv = true;
#if defined(__linux__)
has_rvv = (getauxval(AT_HWCAP) & COMPAT_HWCAP_ISA_V) != 0;
#endif
if (has_rvv) {
this->GemmFloatKernel = MlasGemmFloatKernelRvv;
}
#endif // MLAS_USE_RVV
#endif // MLAS_TARGET_RISCV64
#if defined(MLAS_TARGET_LARCH64)
// No fine-grained LSX/LASX detection here — pick LASX (256-bit) since
// the LoongArch64 spec requires it; LSX (128-bit) is the fallback.
this->GemmFloatKernel = MlasGemmFloatKernelLasx;
this->TransposePackB16x4Routine = MlasSgemmTransposePackB16x4Lasx;
#endif
// ARM64 and WASM intentionally do nothing here — sgemm.cpp's #else branch
// calls MlasSgemmKernelZero / MlasSgemmKernelAdd directly without going
// through GetMlasPlatform().GemmFloatKernel.
}
#else // !MLAS_GEMM_ONLY
MLAS_PLATFORM::MLAS_PLATFORM(
void
)
/*++
Routine Description:
This routine initializes the platform support for this library.
Arguments:
None.
Return Value:
None.
--*/
{
this->ConvDepthwiseU8S8Kernel = MlasConvDepthwiseKernel<uint8_t, int8_t>;
this->ConvDepthwiseU8U8Kernel = MlasConvDepthwiseKernel<uint8_t, uint8_t>;
this->ConvDepthwiseS8S8Kernel = MlasConvDepthwiseKernel<int8_t, int8_t>;
this->ConvDepthwiseS8U8Kernel = MlasConvDepthwiseKernel<int8_t, uint8_t>;
this->CastF16ToF32Kernel = nullptr;
this->CastF32ToF16Kernel = nullptr;
#if defined(MLAS_TARGET_RISCV64)
this->GemmFloatKernel = nullptr;
this->ErfKernelRoutine = MlasErfKernel;
this->LogisticKernelRoutine = MlasLogisticKernel;
this->ReduceMaximumF32Kernel = MlasReduceMaximumF32Kernel;
this->ComputeSumExpF32Kernel = MlasComputeSumExpF32Kernel;
this->ComputeSoftmaxOutputF32Kernel = MlasComputeSoftmaxOutputF32Kernel;
this->ComputeLogSoftmaxOutputF32Kernel = MlasComputeLogSoftmaxOutputF32Kernel;
#if defined(MLAS_USE_RVV)
bool has_rvv = true;
#if defined(__linux__)
has_rvv = (getauxval(AT_HWCAP) & COMPAT_HWCAP_ISA_V) != 0;
#endif
if (MlasShouldForceScalarRiscv(std::getenv("ORT_MLAS_RISCV_FORCE_SCALAR"))) {
has_rvv = false;
}
if (has_rvv) {
this->GemmFloatKernel = MlasGemmFloatKernelRvv;
this->ReduceMaximumF32Kernel = MlasReduceMaximumF32KernelRvv;
this->ComputeSumExpF32Kernel = MlasComputeSumExpF32KernelRvv;
this->ComputeSoftmaxOutputF32Kernel = MlasComputeSoftmaxOutputF32KernelRvv;
this->ComputeLogSoftmaxOutputF32Kernel = MlasComputeLogSoftmaxOutputF32KernelRvv;
}
#endif
#endif
#if defined(MLAS_TARGET_AMD64_IX86)
//
// Default to the baseline SSE2 support.
//
this->GemmFloatKernel = MlasGemmFloatKernelSse;
this->GemmU8S8Dispatch = &MlasGemmU8X8DispatchSse;
this->GemmU8U8Dispatch = &MlasGemmU8X8DispatchSse;
#if defined(MLAS_TARGET_AMD64)
this->TransposePackB16x4Routine = MlasSgemmTransposePackB16x4Sse;
this->GemmDoubleKernel = MlasGemmDoubleKernelSse;
this->ConvNchwFloatKernel = MlasConvNchwFloatKernelSse;
this->ConvNchwcFloatKernel = MlasConvNchwcFloatKernelSse;
this->ConvDepthwiseFloatKernel = MlasConvDepthwiseFloatKernelSse;
this->ConvPointwiseFloatKernel = MlasConvPointwiseFloatKernelSse;
this->PoolFloatKernel[MlasMaximumPooling] = MlasPoolMaximumFloatKernelSse;
this->PoolFloatKernel[MlasAveragePoolingExcludePad] = MlasPoolAverageExcludePadFloatKernelSse;
this->PoolFloatKernel[MlasAveragePoolingIncludePad] = MlasPoolAverageIncludePadFloatKernelSse;
this->ComputeExpF32Kernel = MlasComputeExpF32Kernel;
this->GeluErfKernelRoutine = MlasGeluErfKernel;
this->LogisticKernelRoutine = MlasLogisticKernel;
this->SiluKernelRoutine = MlasSiluKernel;
this->TanhKernelRoutine = MlasTanhKernel;
this->ErfKernelRoutine = MlasErfKernel;
this->ComputeSumExpF32Kernel = MlasComputeSumExpF32Kernel;
this->ComputeSoftmaxOutputF32Kernel = MlasComputeSoftmaxOutputF32Kernel;
this->ComputeLogSoftmaxOutputF32Kernel = MlasComputeLogSoftmaxOutputF32Kernel;
this->ReduceMaximumF32Kernel = MlasReduceMaximumF32Kernel;
this->ReduceMinimumMaximumF32Kernel = MlasReduceMinimumMaximumF32Kernel;
this->QLinearAddS8Kernel = MlasQLinearAddS8Kernel;
this->QLinearAddU8Kernel = MlasQLinearAddU8Kernel;
this->QuantizeLinearS8Kernel = MlasQuantizeLinearS8Kernel;
this->QuantizeLinearU8Kernel = MlasQuantizeLinearU8Kernel;
this->QuantizeLinearS16Kernel = MlasQuantizeLinearS16Kernel;
this->QuantizeLinearU16Kernel = MlasQuantizeLinearU16Kernel;
this->QuantizeLinearS4Kernel = MlasQuantizeLinearS4Kernel;
this->QuantizeLinearU4Kernel = MlasQuantizeLinearU4Kernel;
this->DequantizeLinearS8Kernel = MlasDequantizeLinearS8Kernel;
this->DequantizeLinearU8Kernel = MlasDequantizeLinearU8Kernel;
#ifndef __APPLE__
#ifndef FORCE_GENERIC_ALGORITHMS
this->CastF16ToF32Kernel = &MlasCastF16ToF32KernelSse;
#else // FORCE_GENERIC_ALGORITHMS
this->CastF16ToF32Kernel = nullptr;
#endif // FORCE_GENERIC_ALGORITHMS
#endif // __APPLE__
this->NchwcBlockSize = 8;
this->PreferredBufferAlignment = MLAS_DEFAULT_PREFERRED_BUFFER_ALIGNMENT;
this->MaximumThreadCount = MLAS_MAXIMUM_THREAD_COUNT;
#endif
unsigned Cpuid1[4];
#if defined(_WIN32)
__cpuid((int*)Cpuid1, 1);
#else
__cpuid(1, Cpuid1[0], Cpuid1[1], Cpuid1[2], Cpuid1[3]);
#endif
#if defined(_MSC_VER)
//
// Check if the processor supports SSE 4.1 instructions.
//
#ifndef FORCE_GENERIC_ALGORITHMS
if ((Cpuid1[2] & 0x80000) != 0) {
#else // FORCE_GENERIC_ALGORITHMS
if (false) {
#endif // FORCE_GENERIC_ALGORITHMS
this->GemmU8S8Dispatch = &MlasGemmU8S8DispatchSse41;
}
#endif
//
// Check if the processor supports the AVX and OSXSAVE features.
//
#ifndef FORCE_GENERIC_ALGORITHMS
if ((Cpuid1[2] & 0x18000000) == 0x18000000) {
#else // FORCE_GENERIC_ALGORITHMS
if (false) {
#endif // FORCE_GENERIC_ALGORITHMS
//
// Check if the operating system supports saving SSE and AVX states.
//
uint64_t xcr0 = MlasReadExtendedControlRegister(_XCR_XFEATURE_ENABLED_MASK);
if ((xcr0 & 0x6) == 0x6) {
this->GemmFloatKernel = MlasGemmFloatKernelAvx;
#if defined(MLAS_TARGET_AMD64)
this->KernelM1Routine = MlasSgemmKernelM1Avx;
this->KernelM1TransposeBRoutine = MlasSgemmKernelM1TransposeBAvx;
this->TransposePackB16x4Routine = MlasSgemmTransposePackB16x4Avx;
this->GemmDoubleKernel = MlasGemmDoubleKernelAvx;
this->ConvNchwFloatKernel = MlasConvNchwFloatKernelAvx;
this->ConvNchwcFloatKernel = MlasConvNchwcFloatKernelAvx;
this->ConvDepthwiseFloatKernel = MlasConvDepthwiseFloatKernelAvx;
this->ConvPointwiseFloatKernel = MlasConvPointwiseFloatKernelAvx;
this->PoolFloatKernel[MlasMaximumPooling] = MlasPoolMaximumFloatKernelAvx;
this->PoolFloatKernel[MlasAveragePoolingExcludePad] = MlasPoolAverageExcludePadFloatKernelAvx;
this->PoolFloatKernel[MlasAveragePoolingIncludePad] = MlasPoolAverageIncludePadFloatKernelAvx;
this->ComputeSoftmaxOutputF32Kernel = MlasComputeSoftmaxOutputF32KernelAvx;
this->ComputeLogSoftmaxOutputF32Kernel = MlasComputeLogSoftmaxOutputF32KernelAvx;
this->ReduceMaximumF32Kernel = MlasReduceMaximumF32KernelAvx;
this->ReduceMinimumMaximumF32Kernel = MlasReduceMinimumMaximumF32KernelAvx;
this->GemmU8U8Kernel = nullptr;
//
// Check if the processor supports AVX2/FMA3 features.
//
unsigned Cpuid7[4];
#if defined(_WIN32)
__cpuidex((int*)Cpuid7, 7, 0);
#else
__cpuid_count(7, 0, Cpuid7[0], Cpuid7[1], Cpuid7[2], Cpuid7[3]);
#endif
if (((Cpuid1[2] & 0x1000) != 0) && ((Cpuid7[1] & 0x20) != 0)) {
this->Avx2Supported_ = true;
this->GemmU8S8Dispatch = &MlasGemmU8S8DispatchAvx2;
this->GemmU8S8Kernel = MlasGemmU8S8KernelAvx2;
this->GemvU8S8Kernel = MlasGemvU8S8KernelAvx2;
this->GemmU8U8Dispatch = &MlasGemmU8U8DispatchAvx2;
this->GemmU8U8Kernel = MlasGemmU8U8KernelAvx2;
this->ConvSymU8S8Dispatch = &MlasConvSymDispatchAvx2;
this->GemmFloatKernel = MlasGemmFloatKernelFma3;
this->GemmDoubleKernel = MlasGemmDoubleKernelFma3;
this->ConvNchwFloatKernel = MlasConvNchwFloatKernelFma3;
this->ConvNchwcFloatKernel = MlasConvNchwcFloatKernelFma3;
this->ConvDepthwiseFloatKernel = MlasConvDepthwiseFloatKernelFma3;
this->ConvPointwiseFloatKernel = MlasConvPointwiseFloatKernelFma3;
this->ComputeExpF32Kernel = MlasComputeExpF32KernelFma3;
this->LogisticKernelRoutine = MlasComputeLogisticF32KernelFma3;
this->TanhKernelRoutine = MlasComputeTanhF32KernelFma3;
this->ErfKernelRoutine = MlasErfKernelFma3;
this->QLinearAddS8Kernel = MlasQLinearAddS8KernelAvx2;
this->QLinearAddU8Kernel = MlasQLinearAddU8KernelAvx2;
this->ConvDepthwiseU8S8Kernel = MlasConvDepthwiseKernelAvx2<uint8_t, int8_t>;
this->ConvDepthwiseU8U8Kernel = MlasConvDepthwiseKernelAvx2<uint8_t, uint8_t>;
this->ConvDepthwiseS8S8Kernel = MlasConvDepthwiseKernelAvx2<int8_t, int8_t>;
this->ConvDepthwiseS8U8Kernel = MlasConvDepthwiseKernelAvx2<int8_t, uint8_t>;
this->ComputeSumExpF32Kernel = MlasComputeSumExpF32KernelFma3;
this->QNBitGemmDispatch = &MlasSQNBitGemmDispatchAvx2;
this->CastF16ToF32Kernel = &MlasCastF16ToF32KernelAvx2;
this->CastF32ToF16Kernel = &MlasCastF32ToF16KernelAvx2;
this->RopeDispatch = &MlasRopeDispatchAvx2;
// TODO(vraspar): check if this really goes here or if there are other platform reqs that we need to fulfill
this->LutGenKernel = &MlasLutGenKernelAvx2;
//
// Check if the processor supports Hybrid core architecture.
//
if ((Cpuid7[3] & 0x8000) != 0) {
this->MaximumThreadCount = MLAS_MAXIMUM_THREAD_COUNT * 4;
}
//
// Check if the processor supports AVXVNNI features.
//
unsigned Cpuid7_1[4];
#if defined(_WIN32)
__cpuidex((int*)Cpuid7_1, 7, 1);
#else
__cpuid_count(7, 1, Cpuid7_1[0], Cpuid7_1[1], Cpuid7_1[2], Cpuid7_1[3]);
#endif
if ((Cpuid7_1[0] & 0x10) != 0) {
this->GemmU8S8Kernel = MlasGemmU8S8KernelAvxVnni;
this->GemvU8S8Kernel = MlasGemvU8S8KernelAvxVnni;
this->ConvSymU8S8Dispatch = &MlasConvSymDispatchAvxVnni;
this->QNBitGemmDispatch = &MlasSQNBitGemmDispatchAvx2vnni;
}
#if !defined(ORT_MINIMAL_BUILD)
//
// Check if the processor supports AVX512F features and the
// operating system supports saving AVX512F state.
//
if (((Cpuid7[1] & 0x10000) != 0) && ((xcr0 & 0xE0) == 0xE0)) {
this->GeluErfKernelRoutine = MlasGeluErfKernelAvx512F;
this->SiluKernelRoutine = MlasSiluKernelAvx512F;
this->GemmFloatKernel = MlasGemmFloatKernelAvx512F;
this->GemmDoubleKernel = MlasGemmDoubleKernelAvx512F;
this->ConvNchwFloatKernel = MlasConvNchwFloatKernelAvx512F;
this->ConvNchwcFloatKernel = MlasConvNchwcFloatKernelAvx512F;
this->ConvDepthwiseFloatKernel = MlasConvDepthwiseFloatKernelAvx512F;
this->ConvPointwiseFloatKernel = MlasConvPointwiseFloatKernelAvx512F;
this->PoolFloatKernel[MlasMaximumPooling] = MlasPoolMaximumFloatKernelAvx512F;
this->PoolFloatKernel[MlasAveragePoolingExcludePad] = MlasPoolAverageExcludePadFloatKernelAvx512F;
this->PoolFloatKernel[MlasAveragePoolingIncludePad] = MlasPoolAverageIncludePadFloatKernelAvx512F;
this->ComputeExpF32Kernel = MlasComputeExpF32KernelAvx512F;
this->ComputeSumExpF32Kernel = MlasComputeSumExpF32KernelAvx512F;
this->ReduceMaximumF32Kernel = MlasReduceMaximumF32KernelAvx512F;
this->QuantizeLinearS8Kernel = MlasQuantizeLinearS8KernelAvx512F;
this->QuantizeLinearU8Kernel = MlasQuantizeLinearU8KernelAvx512F;
this->NchwcBlockSize = 16;
this->PreferredBufferAlignment = 64;
//
// Check if the processor supports AVX512 core features
// (AVX512BW/AVX512DQ/AVX512VL).
//
if ((Cpuid7[1] & 0xC0020000) == 0xC0020000) {
this->Avx512Supported_ = true;
this->GemmU8S8Kernel = MlasGemmU8S8KernelAvx512Core;
this->GemvU8S8Kernel = MlasGemvU8S8KernelAvx512Core;
this->GemmU8U8Kernel = MlasGemmU8U8KernelAvx512Core;
this->ConvSymU8S8Dispatch = &MlasConvSymDispatchAvx512Core;
this->FpQ4GemmDispatch = &MlasFpQ4GemmDispatchAvx512;
this->QNBitGemmDispatch = &MlasSQNBitGemmDispatchAvx512;
//
// Check if the processor supports AVX512VNNI.
//
if ((Cpuid7[2] & 0x800) != 0) {
this->GemmU8S8Kernel = MlasGemmU8S8KernelAvx512Vnni;
this->GemvU8S8Kernel = MlasGemvU8S8KernelAvx512Vnni;
this->ConvSymU8S8Dispatch = &MlasConvSymDispatchAvx512Vnni;
this->Q8Q4GemmDispatch = &MlasQ8Q4GemmDispatchAvx512vnni;
this->QNBitGemmDispatch = &MlasSQNBitGemmDispatchAvx512vnni;
}
}
}
//
// Check if the processor supports AVX-VNNI-INT8
//
if ((Cpuid7_1[3] & 0x10) != 0) {
this->GemmU8U8Dispatch = &MlasGemmU8U8DispatchAvx2Vnni;
this->GemmS8S8Dispatch = &MlasGemmS8S8DispatchAvx2Vnni;
this->GemmS8S8Kernel = MlasGemmS8S8KernelAvx2Vnni;
this->GemmS8U8Dispatch = &MlasGemmS8U8DispatchAvx2Vnni;
this->GemmS8U8Kernel = MlasGemmS8U8KernelAvx2Vnni;
}
#ifndef __APPLE__
#if (defined(_MSC_VER) && (_MSC_VER >= 1933)) || (defined(__GNUC__) && (__GNUC__ >= 13))
//
// Check if the processor supports AVX NE CONVERT.
//
if ((Cpuid7_1[3] & (0b1 << 5)) != 0) {
this->CastF16ToF32Kernel = &MlasCastF16ToF32KernelAvx;
}
#endif // (defined(_MSC_VER) && (_MSC_VER >= 1933)) || (defined(__GNUC__) && (__GNUC__ >= 13))
//
// Check if the processor supports AMX-TILE and AMX-INT8
// features.
//
if ((Cpuid7[3] & 0b1 << 24) != 0 &&
(Cpuid7[3] & 0b1 << 25) != 0 &&
(xcr0 & XFEATURE_MASK_XTILE) == XFEATURE_MASK_XTILE) {
if (MlasInitAMX()) {
this->GemmU8S8Dispatch = &MlasGemmU8S8DispatchAmx;
}
}
#endif // __APPLE__
#endif // ORT_MINIMAL_BUILD
}
#endif // MLAS_TARGET_AMD64
}
}
#endif // MLAS_TARGET_AMD64_IX86
#if defined(MLAS_TARGET_ARM64)
this->GemmU8U8Dispatch = &MlasGemmU8X8DispatchNeon;
this->GemmU8S8Dispatch = &MlasGemmX8S8DispatchNeon;
this->GemmS8S8Dispatch = &MlasGemmX8S8DispatchNeon;
this->SymmQgemmDispatch = &MlasSymmQgemmS8DispatchNeon;
this->ConvSymU8S8Dispatch = &MlasConvSymU8DispatchNeon;
this->ConvSymS8S8Dispatch = &MlasConvSymS8DispatchNeon;
this->RopeDispatch = &MlasRopeDispatchNeon;
this->HGemmDispatch = &MlasHGemmDispatchNeon;
this->SoftmaxDispatch = &MlasSoftmaxDispatchNeon;
this->EltwiseDispatch = &MlasEltwiseDispatchNeon;
#if defined(MLAS_USE_ARM_NEON_NCHWC)
// Use the AArch64 assembly implementation on non-Windows platforms.
#if !defined(_WIN32)
// Prefer the hand written micro-kernel for the NCHW convolution path. It
// offers a tighter schedule and a specialised two-output inner loop that
// reduces pressure on the memory system compared to the generic kernel.
this->ConvNchwFloatKernel = MlasConvNchwFloatKernelNeonAsm;
#else
this->ConvNchwFloatKernel = MlasConvNchwFloatKernelNeon;
#endif
this->ConvNchwcFloatKernel = MlasConvNchwcFloatKernelNeon;
this->ConvDepthwiseFloatKernel = MlasConvDepthwiseFloatKernelNeon;
this->ConvPointwiseFloatKernel = MlasConvPointwiseFloatKernelNeon;
#if defined(__linux__)
this->ConvNchwBf16Kernel = MlasConvNchwBf16KernelNeon;
this->ConvDepthwiseBf16Kernel = MlasConvDepthwiseBf16KernelNeon;
this->ConvPointwiseBf16Kernel = MlasConvPointwiseBf16KernelNeon;
#endif
this->PoolFloatKernel[MlasMaximumPooling] = MlasPoolMaximumFloatKernelNeon;
this->PoolFloatKernel[MlasAveragePoolingExcludePad] = MlasPoolAverageExcludePadFloatKernelNeon;
this->PoolFloatKernel[MlasAveragePoolingIncludePad] = MlasPoolAverageIncludePadFloatKernelNeon;
this->NchwcBlockSize = MLAS_NEON_NCHWC_BLOCK_SIZE;
#endif
//
// Check if the processor supports ASIMD dot product instructions.
//
// Note:
// Do NOT use ID_AA64ISAR0_EL1. It causes illegal instruction errors on Mac M1 and ARMv8-A chips
// as well as failing on other ARM chips as it is an EL1 level register that requires extra
// privileges to read.
//
// uint64_t isar0_el1;
// asm("mrs %[reg], ID_AA64ISAR0_EL1\n" : [reg] "=r"(isar0_el1) : :);
// const bool HasDotProductInstructions = ((isar0_el1 >> 44) & 0xfu) == 0x1u;
const bool HasDotProductInstructions = MLAS_CPUIDINFO::GetCPUIDInfo().HasArmNeonDot();
if (HasDotProductInstructions) {
this->GemmU8U8Dispatch = &MlasGemmU8X8DispatchUdot;
this->GemmU8S8Dispatch = &MlasGemmU8X8DispatchUdot;
this->GemmS8S8Dispatch = &MlasGemmS8S8DispatchSdot;
this->SymmQgemmDispatch = &MlasSymmQgemmS8DispatchSdot;
this->ConvSymU8S8Dispatch = &MlasConvSymU8DispatchDot;
this->ConvSymS8S8Dispatch = &MlasConvSymS8DispatchDot;
}
#if defined(USE_KLEIDIAI)
if(MLAS_CPUIDINFO::GetCPUIDInfo().HasArm_SME()){
this->MlasSGemmBatchOverride = ArmKleidiAI::MlasGemmBatch;
this->MlasSGemmPackBSizeOverride = ArmKleidiAI::MlasGemmPackBSize;
this->MlasSGemmPackBOverride = ArmKleidiAI::MlasGemmPackB;
this->MlasDynamicQGemmBatchOverride = ArmKleidiAI::MlasDynamicQGemmBatch;
this->MlasDynamicQGemmPackBSizeOverride = ArmKleidiAI::MlasDynamicQGemmPackBSize;
this->MlasDynamicQGemmPackBOverride = ArmKleidiAI::MlasDynamicQGemmPackB;
this->MlasConvPrepareOverride = ArmKleidiAI::MlasConvPrepare;
this->MlasConvOverride = ArmKleidiAI::MlasConv;
#if defined(__aarch64__) && defined(__linux__)
// Currently only an SME2 variant of SBGEMM exists
if (ArmKleidiAI::UseSME2){
this->MlasSBGemmBatchOverride = ArmKleidiAI::MlasSBGemmBatch;
this->MlasSBGemmPackBSizeOverride = ArmKleidiAI::MlasSBGemmPackBSize;
this->MlasSBGemmPackBOverride = ArmKleidiAI::MlasSBGemmPackB;
}
#endif
}
#endif
#if defined(MLAS_USE_SVE)
if (MLAS_CPUIDINFO::GetCPUIDInfo().HasArmSve()) {
this->ErfKernelRoutine = MlasSveErfKernel;
this->LogisticKernelRoutine = MlasSveLogisticKernel;
this->ReduceMaximumF32Kernel = MlasSveReduceMaximumF32Kernel;
this->ComputeSumExpF32Kernel = MlasSveComputeSumExpF32Kernel;
this->ComputeLogSoftmaxOutputF32Kernel = MlasSveComputeLogSoftmaxOutputF32Kernel;
this->ComputeSoftmaxOutputF32Kernel = MlasSveComputeSoftmaxOutputF32Kernel;
}
else{
this->ErfKernelRoutine = MlasErfKernel;
this->LogisticKernelRoutine = MlasLogisticKernel;
this->ReduceMaximumF32Kernel = MlasReduceMaximumF32Kernel;
this->ComputeSumExpF32Kernel = MlasComputeSumExpF32Kernel;
this->ComputeLogSoftmaxOutputF32Kernel = MlasComputeLogSoftmaxOutputF32Kernel;
this->ComputeSoftmaxOutputF32Kernel = MlasComputeSoftmaxOutputF32Kernel;
}
#endif
#if defined(MLAS_F16VEC_INTRINSICS_SUPPORTED) && !defined(_WIN32)
#if defined(MLAS_USE_SVE)
if (MLAS_CPUIDINFO::GetCPUIDInfo().HasArmSve()) {
this->ErfFP16KernelRoutine = MlasSveErfFP16Kernel;
this->GeluFP16KernelRoutine = MlasSveGeluFP16Kernel;
this->TanhFP16KernelRoutine = MlasSveTanhFP16Kernel;
}
else{
this->ErfFP16KernelRoutine = MlasNeonErfFP16Kernel;
this->GeluFP16KernelRoutine = MlasNeonGeluFP16Kernel;
}
#else
this->ErfFP16KernelRoutine = MlasNeonErfFP16Kernel;
this->GeluFP16KernelRoutine = MlasNeonGeluFP16Kernel;
#endif
#endif
//
// Check if the processor supports ASIMD I8MM instructions.
//
const bool HasI8MMInstructions = MLAS_CPUIDINFO::GetCPUIDInfo().HasArmNeon_I8MM();
if (HasI8MMInstructions) {
#if defined(__linux__)
this->GemmU8U8Dispatch = &MlasGemmU8X8DispatchUmmla;
this->GemmU8S8Dispatch = &MlasGemmU8X8DispatchUmmla;
this->GemmS8S8Dispatch = &MlasGemmS8S8DispatchSmmla;
#endif
}
this->ArmNeonIsQuantActivationsUnsigned = HasI8MMInstructions ? false : true;
this->QNBitGemmDispatch = &GetMlasQNBitGemmDispatchNeon(HasDotProductInstructions, HasI8MMInstructions);
#if defined(MLAS_F16VEC_INTRINSICS_SUPPORTED)
this->CastF16ToF32Kernel = &MlasCastF16ToF32KernelNeon;
this->CastF32ToF16Kernel = &MlasCastF32ToF16KernelNeon;
#endif
#endif // MLAS_TARGET_ARM64
#if defined(MLAS_TARGET_POWER)
this->GemmFloatKernel = MlasSgemmKernel;
this->GemmDoubleKernel = MlasDgemmKernel;
this->QuantizeLinearS8Kernel = MlasQuantizeLinearS8Kernel;
this->QuantizeLinearU8Kernel = MlasQuantizeLinearU8Kernel;
this->QuantizeLinearS16Kernel = MlasQuantizeLinearS16Kernel;
this->QuantizeLinearU16Kernel = MlasQuantizeLinearU16Kernel;
this->QuantizeLinearS4Kernel = MlasQuantizeLinearS4Kernel;
this->QuantizeLinearU4Kernel = MlasQuantizeLinearU4Kernel;
#if defined(__linux__)
unsigned long hwcap2 = getauxval(AT_HWCAP2);
bool HasP9Instructions = hwcap2 & PPC_FEATURE2_ARCH_3_00;
#elif defined(_AIX)
bool HasP9Instructions = __power_9_andup();
#elif defined(__FreeBSD__)
unsigned long hwcap2;
elf_aux_info(AT_HWCAP2, &hwcap2, sizeof(hwcap2));
bool HasP9Instructions = hwcap2 & PPC_FEATURE2_ARCH_3_00;
#endif // __linux__
if (HasP9Instructions) {
this->QuantizeLinearS8Kernel = MlasQuantizeLinearS8KernelVSX;
this->QuantizeLinearU8Kernel = MlasQuantizeLinearU8KernelVSX;
}
#if defined(POWER10)
#if (defined(__GNUC__) && ((__GNUC__ > 10) || (__GNUC__== 10 && __GNUC_MINOR__ >= 2))) || \
(defined(__clang__) && (__clang_major__ >= 12))
#if defined(__linux__) || defined(__FreeBSD__)
bool HasP10Instructions = ((hwcap2 & PPC_FEATURE2_MMA) && (hwcap2 & PPC_FEATURE2_ARCH_3_1));
#elif defined(_AIX)
bool HasP10Instructions = (__power_10_andup() && __power_mma_version() == MMA_V31);
#endif // __linux__
if (HasP10Instructions) {
this->GemmFloatKernel = MlasSgemmKernelPOWER10;
this->GemmDoubleKernel = MlasDgemmKernelPOWER10;
this->GemmU8X8Dispatch = &MlasGemm8X8DispatchPOWER10;
}
#endif
#endif
#endif // MLAS_TARGET_POWER
#if defined(MLAS_TARGET_S390X)
this->GemmFloatKernel = MlasSgemmKernel;
this->GemmDoubleKernel = MlasDgemmKernel;
this->QuantizeLinearS8Kernel = MlasQuantizeLinearS8Kernel;
this->QuantizeLinearU8Kernel = MlasQuantizeLinearU8Kernel;
this->QuantizeLinearS16Kernel = MlasQuantizeLinearS16Kernel;
this->QuantizeLinearU16Kernel = MlasQuantizeLinearU16Kernel;
this->QuantizeLinearS4Kernel = MlasQuantizeLinearS4Kernel;
this->QuantizeLinearU4Kernel = MlasQuantizeLinearU4Kernel;
bool HasVXEInstructions = getauxval(AT_HWCAP) & HWCAP_S390_VXE;
if (HasVXEInstructions) {
this->GemmFloatKernel = MlasSgemmKernelZVECTOR;
this->GemmU8X8Dispatch = &MlasGemm8X8DispatchZVECTOR;
this->QuantizeLinearS8Kernel = MlasQuantizeLinearS8KernelZVECTOR;
this->QuantizeLinearU8Kernel = MlasQuantizeLinearU8KernelZVECTOR;
}
#endif // MLAS_TARGET_S390X
#if defined(MLAS_TARGET_LARCH64)
//
// Default to the baseline LSX support.
//
int hwcap = getauxval(AT_HWCAP);
bool cap_lasx = hwcap & HWCAP_LOONGARCH_LASX;
bool cap_lsx = hwcap & HWCAP_LOONGARCH_LSX;
if( cap_lasx ){
this->GemmFloatKernel = MlasGemmFloatKernelLasx;
this->GemmDoubleKernel = MlasGemmDoubleKernelLasx;
this->ConvNchwFloatKernel = MlasConvNchwFloatKernelLasx;
this->ConvNchwcFloatKernel = MlasConvNchwcFloatKernelLasx;
this->ConvDepthwiseFloatKernel = MlasConvDepthwiseFloatKernelLasx;
this->ConvPointwiseFloatKernel = MlasConvPointwiseFloatKernelLasx;
this->PoolFloatKernel[MlasMaximumPooling] = MlasPoolMaximumFloatKernelLasx;
this->PoolFloatKernel[MlasAveragePoolingExcludePad] = MlasPoolAverageExcludePadFloatKernelLasx;
this->PoolFloatKernel[MlasAveragePoolingIncludePad] = MlasPoolAverageIncludePadFloatKernelLasx;
this->ReduceMaximumF32Kernel = MlasReduceMaximumF32KernelLasx;
this->ComputeSoftmaxOutputF32Kernel = MlasComputeSoftmaxOutputF32KernelLasx;
this->ComputeLogSoftmaxOutputF32Kernel = MlasComputeLogSoftmaxOutputF32KernelLasx;
this->TransposePackB16x4Routine = MlasSgemmTransposePackB16x4Lasx;
// add new sqn-lasx kernel
this->QNBitGemmDispatch = &MlasSQNBitGemmDispatchLasx;
this->GemmU8S8Dispatch = &MlasGemmU8X8DispatchLSX;
this->GemmU8U8Dispatch = &MlasGemmU8X8DispatchLSX;
this->GemmS8S8Dispatch = &MlasGemmS8S8DispatchLSX;
this->GemmS8U8Dispatch = &MlasGemmS8U8DispatchLSX;
}else if( cap_lsx ){
this->GemmFloatKernel = MlasGemmFloatKernelLSX;
this->GemmU8S8Dispatch = &MlasGemmU8X8DispatchLSX;
this->GemmU8U8Dispatch = &MlasGemmU8X8DispatchLSX;
this->GemmS8S8Dispatch = &MlasGemmS8S8DispatchLSX;
this->GemmS8U8Dispatch = &MlasGemmS8U8DispatchLSX;
this->TransposePackB16x4Routine = MlasSgemmTransposePackB16x4LSX;
this->GemmDoubleKernel = MlasGemmDoubleKernelLSX;
this->ConvNchwFloatKernel = MlasConvNchwFloatKernelLSX;
this->ConvNchwcFloatKernel = MlasConvNchwcFloatKernelLSX;
this->ConvDepthwiseFloatKernel = MlasConvDepthwiseFloatKernelLSX;
this->ConvPointwiseFloatKernel = MlasConvPointwiseFloatKernelLSX;
this->PoolFloatKernel[MlasMaximumPooling] = MlasPoolMaximumFloatKernelLSX;
this->PoolFloatKernel[MlasAveragePoolingExcludePad] = MlasPoolAverageExcludePadFloatKernelLSX;
this->PoolFloatKernel[MlasAveragePoolingIncludePad] = MlasPoolAverageIncludePadFloatKernelLSX;
this->ReduceMaximumF32Kernel = MlasReduceMaximumF32Kernel;
this->ComputeSoftmaxOutputF32Kernel = MlasComputeSoftmaxOutputF32Kernel;
this->ComputeLogSoftmaxOutputF32Kernel = MlasComputeLogSoftmaxOutputF32Kernel;
}else{
this->ReduceMaximumF32Kernel = MlasReduceMaximumF32Kernel;
this->ComputeSoftmaxOutputF32Kernel = MlasComputeSoftmaxOutputF32Kernel;
this->ComputeLogSoftmaxOutputF32Kernel = MlasComputeLogSoftmaxOutputF32Kernel;
}
this->NchwcBlockSize = 8;
// this->PreferredBufferAlignment = MLAS_DEFAULT_PREFERRED_BUFFER_ALIGNMENT;
// this->MaximumThreadCount = MLAS_MAXIMUM_THREAD_COUNT;
#endif // MLAS_TARGET_LARCH64
}
#endif // MLAS_GEMM_ONLY
size_t
MLASCALL
MlasGetPreferredBufferAlignment(
void
)
/*++
Routine Description:
This routine returns the preferred byte alignment for buffers that are used
with this library. Buffers that are not byte aligned to this value will
function, but will not achieve best performance.
Arguments:
None.
Return Value:
Returns the preferred byte alignment for buffers.
--*/
{
#if defined(MLAS_TARGET_AMD64)
return GetMlasPlatform().PreferredBufferAlignment;
#else
return MLAS_DEFAULT_PREFERRED_BUFFER_ALIGNMENT;
#endif
}
#ifdef MLAS_TARGET_AMD64_IX86
bool
MLASCALL
MlasPlatformU8S8Overflow(
void
)
{
const auto& p = GetMlasPlatform();
return p.GemmU8U8Dispatch != p.GemmU8S8Dispatch;
}
#endif
thread_local size_t ThreadedBufSize = 0;
#ifdef _MSC_VER
thread_local std::unique_ptr<uint8_t, decltype(&_aligned_free)> ThreadedBufHolder(nullptr, &_aligned_free);
#else
thread_local std::unique_ptr<uint8_t, decltype(&free)> ThreadedBufHolder(nullptr, &free);
#endif