From 2ac756f81f79a8b6d5b00394b748c3821c2e48b6 Mon Sep 17 00:00:00 2001 From: Ugochukwu Mmaduekwe Date: Sun, 12 Apr 2026 19:55:53 +0100 Subject: [PATCH] add arm cpu detection logic --- .../Delphi/PerformanceBenchmarkConsole.dpr | 2 + HashLib.Tests/Delphi.Tests/HashLib.Tests.dpr | 2 + HashLib/src/Checksum/HlpAdler32Dispatch.pas | 2 +- HashLib/src/Checksum/HlpCRCDispatch.pas | 2 +- HashLib/src/Crypto/HlpBlake2BDispatch.pas | 2 +- HashLib/src/Crypto/HlpBlake2SDispatch.pas | 2 +- HashLib/src/Crypto/HlpBlake3Dispatch.pas | 2 +- HashLib/src/Crypto/HlpSHA1Dispatch.pas | 2 +- HashLib/src/Crypto/HlpSHA2_256Dispatch.pas | 2 +- HashLib/src/Crypto/HlpSHA2_512Dispatch.pas | 2 +- HashLib/src/Crypto/HlpSHA3Dispatch.pas | 2 +- HashLib/src/Hash64/HlpXXHash3Dispatch.pas | 2 +- HashLib/src/Include/HashLib.inc | 90 +++--- HashLib/src/Include/HashLibFPC.inc | 32 +- HashLib/src/KDF/HlpArgon2Dispatch.pas | 2 +- HashLib/src/KDF/HlpScryptDispatch.pas | 2 +- .../Packages/Delphi/HashLib4PascalPackage.dpk | 2 + .../Packages/FPC/HashLib4PascalPackage.lpk | 10 +- .../Packages/FPC/HashLib4PascalPackage.pas | 3 +- HashLib/src/Utils/HlpArmHwCapProvider.pas | 264 +++++++++++++++ HashLib/src/Utils/HlpArmSimdFeatures.pas | 303 ++++++++++++++++-- HashLib/src/Utils/HlpCpuFeatures.pas | 27 +- HashLib/src/Utils/HlpDarwinSysCtl.pas | 129 ++++++++ HashLib/src/Utils/HlpX86SimdFeatures.pas | 32 +- 24 files changed, 805 insertions(+), 115 deletions(-) create mode 100644 HashLib/src/Utils/HlpArmHwCapProvider.pas create mode 100644 HashLib/src/Utils/HlpDarwinSysCtl.pas diff --git a/HashLib.Benchmark/Delphi/PerformanceBenchmarkConsole.dpr b/HashLib.Benchmark/Delphi/PerformanceBenchmarkConsole.dpr index 041f60d..5451c4d 100644 --- a/HashLib.Benchmark/Delphi/PerformanceBenchmarkConsole.dpr +++ b/HashLib.Benchmark/Delphi/PerformanceBenchmarkConsole.dpr @@ -128,6 +128,8 @@ uses HlpX86SimdFeatures in '..\..\HashLib\src\Utils\HlpX86SimdFeatures.pas', HlpArmSimdFeatures in '..\..\HashLib\src\Utils\HlpArmSimdFeatures.pas', HlpSimdLevels in '..\..\HashLib\src\Utils\HlpSimdLevels.pas', + HlpArmHwCapProvider in '..\..\HashLib\src\Utils\HlpArmHwCapProvider.pas', + HlpDarwinSysCtl in '..\..\HashLib\src\Utils\HlpDarwinSysCtl.pas', HlpHashLibTypes in '..\..\HashLib\src\Utils\HlpHashLibTypes.pas', HlpArrayUtils in '..\..\HashLib\src\Utils\HlpArrayUtils.pas'; diff --git a/HashLib.Tests/Delphi.Tests/HashLib.Tests.dpr b/HashLib.Tests/Delphi.Tests/HashLib.Tests.dpr index c62dc5a..c104b6f 100644 --- a/HashLib.Tests/Delphi.Tests/HashLib.Tests.dpr +++ b/HashLib.Tests/Delphi.Tests/HashLib.Tests.dpr @@ -146,6 +146,8 @@ uses HlpX86SimdFeatures in '..\..\HashLib\src\Utils\HlpX86SimdFeatures.pas', HlpArmSimdFeatures in '..\..\HashLib\src\Utils\HlpArmSimdFeatures.pas', HlpSimdLevels in '..\..\HashLib\src\Utils\HlpSimdLevels.pas', + HlpArmHwCapProvider in '..\..\HashLib\src\Utils\HlpArmHwCapProvider.pas', + HlpDarwinSysCtl in '..\..\HashLib\src\Utils\HlpDarwinSysCtl.pas', HlpHashLibTypes in '..\..\HashLib\src\Utils\HlpHashLibTypes.pas', HlpArrayUtils in '..\..\HashLib\src\Utils\HlpArrayUtils.pas', HashLibTestBase in '..\src\HashLibTestBase.pas', diff --git a/HashLib/src/Checksum/HlpAdler32Dispatch.pas b/HashLib/src/Checksum/HlpAdler32Dispatch.pas index 0ac6d7d..5674249 100644 --- a/HashLib/src/Checksum/HlpAdler32Dispatch.pas +++ b/HashLib/src/Checksum/HlpAdler32Dispatch.pas @@ -201,7 +201,7 @@ procedure InitDispatch(); end; {$ENDIF} {$IFDEF HASHLIB_X86_64_ASM} - case TCpuFeatures.X86.GetSimdLevel() of + case TCpuFeatures.X86.GetActiveSimdLevel() of TX86SimdLevel.AVX2: begin Adler32_Update := @Adler32_Update_Avx2; diff --git a/HashLib/src/Checksum/HlpCRCDispatch.pas b/HashLib/src/Checksum/HlpCRCDispatch.pas index dad6cdd..22e080f 100644 --- a/HashLib/src/Checksum/HlpCRCDispatch.pas +++ b/HashLib/src/Checksum/HlpCRCDispatch.pas @@ -521,7 +521,7 @@ procedure InitDispatch(); end; {$ENDIF HASHLIB_I386_ASM} {$IFDEF HASHLIB_X86_64_ASM} - case TCpuFeatures.X86.GetSimdLevel() of + case TCpuFeatures.X86.GetActiveSimdLevel() of TX86SimdLevel.AVX2, TX86SimdLevel.SSSE3, TX86SimdLevel.SSE2: BindSse2CrcFold; end; diff --git a/HashLib/src/Crypto/HlpBlake2BDispatch.pas b/HashLib/src/Crypto/HlpBlake2BDispatch.pas index a79f8e8..50ef386 100644 --- a/HashLib/src/Crypto/HlpBlake2BDispatch.pas +++ b/HashLib/src/Crypto/HlpBlake2BDispatch.pas @@ -140,7 +140,7 @@ procedure InitDispatch(); end; {$ENDIF} {$IFDEF HASHLIB_X86_64_ASM} - case TCpuFeatures.X86.GetSimdLevel() of + case TCpuFeatures.X86.GetActiveSimdLevel() of TX86SimdLevel.AVX2: begin Blake2B_Compress := @Blake2B_Compress_Avx2; diff --git a/HashLib/src/Crypto/HlpBlake2SDispatch.pas b/HashLib/src/Crypto/HlpBlake2SDispatch.pas index 04023d1..5fbf560 100644 --- a/HashLib/src/Crypto/HlpBlake2SDispatch.pas +++ b/HashLib/src/Crypto/HlpBlake2SDispatch.pas @@ -138,7 +138,7 @@ procedure InitDispatch(); end; {$ENDIF} {$IFDEF HASHLIB_X86_64_ASM} - case TCpuFeatures.X86.GetSimdLevel() of + case TCpuFeatures.X86.GetActiveSimdLevel() of TX86SimdLevel.AVX2: begin Blake2S_Compress := @Blake2S_Compress_Avx2; diff --git a/HashLib/src/Crypto/HlpBlake3Dispatch.pas b/HashLib/src/Crypto/HlpBlake3Dispatch.pas index c577bbc..aaf264d 100644 --- a/HashLib/src/Crypto/HlpBlake3Dispatch.pas +++ b/HashLib/src/Crypto/HlpBlake3Dispatch.pas @@ -723,7 +723,7 @@ procedure InitDispatch(); end; {$ENDIF} {$IFDEF HASHLIB_X86_64_ASM} - case TCpuFeatures.X86.GetSimdLevel() of + case TCpuFeatures.X86.GetActiveSimdLevel() of TX86SimdLevel.AVX2: begin Blake3_Compress := @Blake3_Compress_Avx2; diff --git a/HashLib/src/Crypto/HlpSHA1Dispatch.pas b/HashLib/src/Crypto/HlpSHA1Dispatch.pas index 57598f6..a7455fc 100644 --- a/HashLib/src/Crypto/HlpSHA1Dispatch.pas +++ b/HashLib/src/Crypto/HlpSHA1Dispatch.pas @@ -193,7 +193,7 @@ procedure InitDispatch(); SHA1_Compress := @SHA1_Compress_ShaNi_Wrap; Exit; end; - case TCpuFeatures.X86.GetSimdLevel() of + case TCpuFeatures.X86.GetActiveSimdLevel() of TX86SimdLevel.AVX2: begin SHA1_Compress := @SHA1_Compress_Avx2_Wrap; diff --git a/HashLib/src/Crypto/HlpSHA2_256Dispatch.pas b/HashLib/src/Crypto/HlpSHA2_256Dispatch.pas index 2ba4832..c79cf39 100644 --- a/HashLib/src/Crypto/HlpSHA2_256Dispatch.pas +++ b/HashLib/src/Crypto/HlpSHA2_256Dispatch.pas @@ -203,7 +203,7 @@ procedure InitDispatch(); SHA256_Compress := @SHA256_Compress_ShaNi_Wrap; Exit; end; - case TCpuFeatures.X86.GetSimdLevel() of + case TCpuFeatures.X86.GetActiveSimdLevel() of TX86SimdLevel.AVX2: begin SHA256_Compress := @SHA256_Compress_Avx2_Wrap; diff --git a/HashLib/src/Crypto/HlpSHA2_512Dispatch.pas b/HashLib/src/Crypto/HlpSHA2_512Dispatch.pas index e0c5b13..36129c9 100644 --- a/HashLib/src/Crypto/HlpSHA2_512Dispatch.pas +++ b/HashLib/src/Crypto/HlpSHA2_512Dispatch.pas @@ -207,7 +207,7 @@ procedure InitDispatch(); end; {$ENDIF} {$IFDEF HASHLIB_X86_64_ASM} - case TCpuFeatures.X86.GetSimdLevel() of + case TCpuFeatures.X86.GetActiveSimdLevel() of TX86SimdLevel.AVX2: begin SHA512_Compress := @SHA512_Compress_Avx2_Wrap; diff --git a/HashLib/src/Crypto/HlpSHA3Dispatch.pas b/HashLib/src/Crypto/HlpSHA3Dispatch.pas index 8ab3eb9..4ba7657 100644 --- a/HashLib/src/Crypto/HlpSHA3Dispatch.pas +++ b/HashLib/src/Crypto/HlpSHA3Dispatch.pas @@ -497,7 +497,7 @@ procedure InitDispatch(); KeccakF1600_Permute := @KeccakF1600_Scalar; KeccakF1600_Absorb := @KeccakF1600_Absorb_Scalar; {$IFDEF HASHLIB_X86_64_ASM} - case TCpuFeatures.X86.GetSimdLevel() of + case TCpuFeatures.X86.GetActiveSimdLevel() of TX86SimdLevel.AVX2: begin KeccakF1600_Permute := @KeccakF1600_Avx2_Wrap; diff --git a/HashLib/src/Hash64/HlpXXHash3Dispatch.pas b/HashLib/src/Hash64/HlpXXHash3Dispatch.pas index 0fedd69..5feaf1d 100644 --- a/HashLib/src/Hash64/HlpXXHash3Dispatch.pas +++ b/HashLib/src/Hash64/HlpXXHash3Dispatch.pas @@ -225,7 +225,7 @@ procedure InitDispatch(); end; {$ENDIF} {$IFDEF HASHLIB_X86_64_ASM} - case TCpuFeatures.X86.GetSimdLevel() of + case TCpuFeatures.X86.GetActiveSimdLevel() of TX86SimdLevel.AVX2: begin XXH3_Accumulate512 := @XXH3_Accumulate512_Avx2; diff --git a/HashLib/src/Include/HashLib.inc b/HashLib/src/Include/HashLib.inc index fd9bced..b35b5db 100644 --- a/HashLib/src/Include/HashLib.inc +++ b/HashLib/src/Include/HashLib.inc @@ -16,7 +16,7 @@ {$IFDEF FPC} {$I HashLibFPC.inc} {$ELSE} - // Delphi 2010 and Above + // Delphi 2010 and Above {$IF CompilerVersion < 21.0} {$MESSAGE ERROR 'This Library requires Delphi 2010 or higher.'} {$IFEND} @@ -43,54 +43,54 @@ {$DEFINE HASHLIB_USE_PPL} // Use Parallel Programming Library {$IFEND} -{============================== CPU Architecture ==============================} + {============================== CPU Architecture ==============================} -{$IF DEFINED(CPU386)} - {$DEFINE HASHLIB_I386} - {$IFDEF MSWINDOWS} - {$DEFINE HASHLIB_I386_ASM} - {$ENDIF} -{$IFEND} - -{$IF DEFINED(CPUX64)} - {$DEFINE HASHLIB_X86_64} - {$IFDEF MSWINDOWS} - {$DEFINE HASHLIB_X86_64_ASM} - {$ENDIF} -{$IFEND} + {$IF DEFINED(CPU386)} + {$DEFINE HASHLIB_I386} + {$IFDEF MSWINDOWS} + {$DEFINE HASHLIB_I386_ASM} + {$ENDIF} + {$IFEND} -{$IFDEF CPUARM32} - {$DEFINE HASHLIB_ARM} -{$ENDIF} + {$IF DEFINED(CPUX64)} + {$DEFINE HASHLIB_X86_64} + {$IFDEF MSWINDOWS} + {$DEFINE HASHLIB_X86_64_ASM} + {$ENDIF} + {$IFEND} -{$IFDEF CPUARM64} - {$DEFINE HASHLIB_AARCH64} -{$ENDIF} + {$IFDEF CPUARM32} + {$DEFINE HASHLIB_ARM32} + {$ENDIF} -{$ENDIF} + {$IFDEF CPUARM64} + {$DEFINE HASHLIB_AARCH64} + {$ENDIF} -{================================= Target OS ==================================} + {================================= Target OS ==================================} -{$IFDEF MSWINDOWS} - {$DEFINE HASHLIB_MSWINDOWS} -{$ENDIF} + {$IFDEF MSWINDOWS} + {$DEFINE HASHLIB_MSWINDOWS} + {$ENDIF} -{$IFDEF IOS} - {$DEFINE HASHLIB_IOS} -{$ENDIF} + {$IFDEF IOS} + {$DEFINE HASHLIB_IOS} + {$ENDIF} -{$IFDEF MACOS} - {$IFNDEF IOS} + {$IFDEF MACOS} + {$IFNDEF IOS} {$DEFINE HASHLIB_MACOS} - {$ENDIF} -{$ENDIF} + {$ENDIF} + {$ENDIF} -{$IFDEF ANDROID} - {$DEFINE HASHLIB_ANDROID} -{$ENDIF} + {$IFDEF ANDROID} + {$DEFINE HASHLIB_ANDROID} + {$ENDIF} + + {$IFDEF LINUX} + {$DEFINE HASHLIB_LINUX} + {$ENDIF} -{$IFDEF LINUX} - {$DEFINE HASHLIB_LINUX} {$ENDIF} {========================== Common Compiler Settings ==========================} @@ -102,7 +102,17 @@ {$SCOPEDENUMS ON} {$POINTERMATH ON} -{============================== SIMD Settings =================================} +{========================== Common Defines Settings ===========================} + +{$IF DEFINED(HASHLIB_I386) OR DEFINED(HASHLIB_X86_64)} + {$DEFINE HASHLIB_X86} +{$IFEND} + +{$IF DEFINED(HASHLIB_ARM32) OR DEFINED(HASHLIB_AARCH64)} + {$DEFINE HASHLIB_ARM} +{$IFEND} + +{============================ Common SIMD Settings ============================} // Uncomment to force scalar dispatch (available on all platforms): // {$DEFINE HASHLIB_FORCE_SCALAR} @@ -111,7 +121,7 @@ {$DEFINE HASHLIB_X86_SIMD} {$IFEND} -{$IF DEFINED(HASHLIB_ARM_ASM) OR DEFINED(HASHLIB_AARCH64_ASM)} +{$IF DEFINED(HASHLIB_ARM32_ASM) OR DEFINED(HASHLIB_AARCH64_ASM)} {$DEFINE HASHLIB_ARM_SIMD} {$IFEND} diff --git a/HashLib/src/Include/HashLibFPC.inc b/HashLib/src/Include/HashLibFPC.inc index dbdd893..e9bc5bf 100644 --- a/HashLib/src/Include/HashLibFPC.inc +++ b/HashLib/src/Include/HashLibFPC.inc @@ -13,25 +13,25 @@ // FPC 3.2.2 and Above {$IF FPC_FULLVERSION < 30202} - {$MESSAGE ERROR 'This Library requires FreePascal 3.2.2 or higher.'} + {$MESSAGE ERROR 'This Library requires FreePascal 3.2.2 or higher.'} {$IFEND} {$IFDEF ENDIAN_BIG} - {$MESSAGE FATAL 'This Library does not support "Big Endian" processors yet.'} + {$MESSAGE FATAL 'This Library does not support "Big Endian" processors yet.'} {$ENDIF} {$IFDEF FPC_LITTLE_ENDIAN} - {$DEFINE HASHLIB_LITTLE_ENDIAN} + {$DEFINE HASHLIB_LITTLE_ENDIAN} {$ENDIF} {$IFDEF FPC_REQUIRES_PROPER_ALIGNMENT} - {$DEFINE HASHLIB_REQUIRES_PROPER_ALIGNMENT} + {$DEFINE HASHLIB_REQUIRES_PROPER_ALIGNMENT} {$ENDIF} {========================= FPC Version Features ===============================} {$IF FPC_FULLVERSION >= 30301} - {.$DEFINE HASHLIB_USE_PPL} // Use Parallel Programming Library + {.$DEFINE HASHLIB_USE_PPL} // Use Parallel Programming Library {$IFEND} {$DEFINE USE_UNROLLED_VARIANT} @@ -49,43 +49,43 @@ {$IFEND} {$IFDEF CPUARM} - {$DEFINE HASHLIB_ARM} - {$DEFINE HASHLIB_ARM_ASM} + {$DEFINE HASHLIB_ARM32} + {$DEFINE HASHLIB_ARM32_ASM} {$ENDIF} {$IFDEF CPUAARCH64} - {$DEFINE HASHLIB_AARCH64} - {$DEFINE HASHLIB_AARCH64_ASM} + {$DEFINE HASHLIB_AARCH64} + {$DEFINE HASHLIB_AARCH64_ASM} {$ENDIF} {================================= Target OS ==================================} {$IFDEF MSWINDOWS} - {$DEFINE HASHLIB_MSWINDOWS} + {$DEFINE HASHLIB_MSWINDOWS} {$ENDIF} {$IFDEF ANDROID} - {$DEFINE HASHLIB_ANDROID} + {$DEFINE HASHLIB_ANDROID} {$ENDIF} {$IFDEF IOS} - {$DEFINE HASHLIB_IOS} + {$DEFINE HASHLIB_IOS} {$ENDIF} {$IF DEFINED(DARWIN) AND NOT DEFINED(HASHLIB_IOS)} - {$DEFINE HASHLIB_MACOS} + {$DEFINE HASHLIB_MACOS} {$IFEND} {$IF DEFINED(FREEBSD) OR DEFINED(NETBSD) OR DEFINED(OPENBSD) OR DEFINED(DRAGONFLY)} - {$DEFINE HASHLIB_BSD} + {$DEFINE HASHLIB_BSD} {$IFEND} {$IFDEF LINUX} - {$DEFINE HASHLIB_LINUX} + {$DEFINE HASHLIB_LINUX} {$ENDIF} {$IFDEF SOLARIS} - {$DEFINE HASHLIB_SOLARIS} + {$DEFINE HASHLIB_SOLARIS} {$ENDIF} {========================= Compiler Mode & Optimizations ======================} diff --git a/HashLib/src/KDF/HlpArgon2Dispatch.pas b/HashLib/src/KDF/HlpArgon2Dispatch.pas index 207169f..a786708 100644 --- a/HashLib/src/KDF/HlpArgon2Dispatch.pas +++ b/HashLib/src/KDF/HlpArgon2Dispatch.pas @@ -145,7 +145,7 @@ procedure InitDispatch(); end; {$ENDIF} {$IFDEF HASHLIB_X86_64_ASM} - case TCpuFeatures.X86.GetSimdLevel() of + case TCpuFeatures.X86.GetActiveSimdLevel() of TX86SimdLevel.AVX2: begin Argon2_FillBlock := @Argon2_FillBlock_Avx2; diff --git a/HashLib/src/KDF/HlpScryptDispatch.pas b/HashLib/src/KDF/HlpScryptDispatch.pas index f9c399e..6ee18e4 100644 --- a/HashLib/src/KDF/HlpScryptDispatch.pas +++ b/HashLib/src/KDF/HlpScryptDispatch.pas @@ -210,7 +210,7 @@ procedure InitDispatch(); end; {$ENDIF} {$IFDEF HASHLIB_X86_64_ASM} - case TCpuFeatures.X86.GetSimdLevel() of + case TCpuFeatures.X86.GetActiveSimdLevel() of TX86SimdLevel.AVX2: begin Scrypt_SalsaXor := @Scrypt_SalsaXor_Avx2; diff --git a/HashLib/src/Packages/Delphi/HashLib4PascalPackage.dpk b/HashLib/src/Packages/Delphi/HashLib4PascalPackage.dpk index 99a06f1..eb638f8 100644 --- a/HashLib/src/Packages/Delphi/HashLib4PascalPackage.dpk +++ b/HashLib/src/Packages/Delphi/HashLib4PascalPackage.dpk @@ -154,6 +154,8 @@ contains HlpX86SimdFeatures in '..\..\Utils\HlpX86SimdFeatures.pas', HlpArmSimdFeatures in '..\..\Utils\HlpArmSimdFeatures.pas', HlpSimdLevels in '..\..\Utils\HlpSimdLevels.pas', + HlpArmHwCapProvider in '..\..\Utils\HlpArmHwCapProvider.pas', + HlpDarwinSysCtl in '..\..\Utils\HlpDarwinSysCtl.pas', HlpHashLibTypes in '..\..\Utils\HlpHashLibTypes.pas', HlpArrayUtils in '..\..\Utils\HlpArrayUtils.pas'; diff --git a/HashLib/src/Packages/FPC/HashLib4PascalPackage.lpk b/HashLib/src/Packages/FPC/HashLib4PascalPackage.lpk index 60ffecc..646488f 100644 --- a/HashLib/src/Packages/FPC/HashLib4PascalPackage.lpk +++ b/HashLib/src/Packages/FPC/HashLib4PascalPackage.lpk @@ -29,7 +29,7 @@ "/> - + @@ -522,6 +522,14 @@ + + + + + + + + diff --git a/HashLib/src/Packages/FPC/HashLib4PascalPackage.pas b/HashLib/src/Packages/FPC/HashLib4PascalPackage.pas index 95346dc..cbf67c1 100644 --- a/HashLib/src/Packages/FPC/HashLib4PascalPackage.pas +++ b/HashLib/src/Packages/FPC/HashLib4PascalPackage.pas @@ -33,7 +33,8 @@ interface HlpArgon2Dispatch, HlpScryptDispatch, HlpBlake3Dispatch, HlpSHA2_256Dispatch, HlpSHA2_512Dispatch, HlpSHA1Dispatch, HlpAdler32Dispatch, HlpGF2, HlpCRCDispatch, HlpSHA3Dispatch, - HlpArmSimdFeatures, HlpSimdLevels, HlpX86SimdFeatures; + HlpArmSimdFeatures, HlpSimdLevels, HlpX86SimdFeatures, HlpArmHwCapProvider, + HlpDarwinSysCtl; implementation diff --git a/HashLib/src/Utils/HlpArmHwCapProvider.pas b/HashLib/src/Utils/HlpArmHwCapProvider.pas new file mode 100644 index 0000000..fac493f --- /dev/null +++ b/HashLib/src/Utils/HlpArmHwCapProvider.pas @@ -0,0 +1,264 @@ +unit HlpArmHwCapProvider; + +{$I ..\Include\HashLib.inc} + +interface + +{$IF DEFINED(HASHLIB_ARM)} + +{$IF DEFINED(HASHLIB_LINUX) OR DEFINED(HASHLIB_ANDROID) OR DEFINED(HASHLIB_BSD)} +uses + {$IFDEF FPC} + dl; + {$ELSE} + Posix.Dlfcn; + {$ENDIF} +{$IFEND} + +{$IF DEFINED(HASHLIB_MSWINDOWS)} +uses + Windows; +{$IFEND} + +{ ===== AArch64 HWCAP bit definitions (from asm/hwcap.h) ===== } +{ These constants are shared by Linux, Android, and BSD on AArch64. } + +{$IF DEFINED(HASHLIB_LINUX) OR DEFINED(HASHLIB_ANDROID) OR DEFINED(HASHLIB_BSD)} + +{$IF DEFINED(HASHLIB_AARCH64)} +const + AT_HWCAP = 16; + AT_HWCAP2 = 26; + + HWCAP_ASIMD = UInt64(1) shl 1; + HWCAP_AES = UInt64(1) shl 3; + HWCAP_PMULL = UInt64(1) shl 4; + HWCAP_SHA1 = UInt64(1) shl 5; + HWCAP_SHA2 = UInt64(1) shl 6; + HWCAP_CRC32 = UInt64(1) shl 7; + HWCAP_SHA3 = UInt64(1) shl 17; + HWCAP_SHA512 = UInt64(1) shl 21; + HWCAP_SVE = UInt64(1) shl 22; + + HWCAP2_SVE2 = UInt64(1) shl 1; +{$IFEND} + +{ ===== ARM32 HWCAP bit definitions (from asm/hwcap.h) ===== } +{ These constants are shared by Linux, Android, and BSD on ARM32. } + +{$IF DEFINED(HASHLIB_ARM32)} +const + AT_HWCAP = 16; + AT_HWCAP2 = 26; + + HWCAP_NEON = UInt64(1) shl 12; + + HWCAP2_AES = UInt64(1) shl 0; + HWCAP2_PMULL = UInt64(1) shl 1; + HWCAP2_SHA1 = UInt64(1) shl 2; + HWCAP2_SHA2 = UInt64(1) shl 3; + HWCAP2_CRC32 = UInt64(1) shl 4; +{$IFEND} + +{$IFEND} // HASHLIB_LINUX OR HASHLIB_ANDROID OR HASHLIB_BSD + +{ ===== Windows ARM64 PF_ARM_* constants ===== } + +{$IF DEFINED(HASHLIB_MSWINDOWS)} +const + // Standard constants (always available) + PF_ARM_NEON_INSTRUCTIONS_AVAILABLE = 19; + PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE = 30; // Bundles AES, PMULL, SHA1, SHA256 + PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE = 31; + // Newer constants (SDK 22621+, defined here to avoid SDK version dependency) + PF_ARM_V82_SHA3_INSTRUCTIONS_AVAILABLE = 46; + PF_ARM_V82_SHA512_INSTRUCTIONS_AVAILABLE = 47; +{$IFEND} + +type + /// + /// Provides ARM hardware capability information across platforms. + /// Linux/Android: resolves getauxval via dlsym. + /// BSD: resolves elf_aux_info / _elf_aux_info via dlsym. + /// Windows: wraps IsProcessorFeaturePresent from the Windows unit. + /// + TArmHwCapProvider = class sealed + +{$IF DEFINED(HASHLIB_LINUX) OR DEFINED(HASHLIB_ANDROID)} + strict private + type + TGetAuxValFunc = function(AType: UInt64): UInt64; cdecl; + + strict private + class var + FGetAuxVal: TGetAuxValFunc; + FResolved: Boolean; + + strict private + class procedure ResolveOnce(); static; + + public + class function GetHwCap(): UInt64; static; + class function GetHwCap2(): UInt64; static; +{$IFEND} + +{$IF DEFINED(HASHLIB_BSD)} + strict private + type + TElfAuxInfoFunc = function(AAuxType: Int32; ABuf: Pointer; ABufLen: Int32): Int32; cdecl; + + strict private + class var + FElfAuxInfo: TElfAuxInfoFunc; + FResolved: Boolean; + + strict private + class procedure ResolveOnce(); static; + + public + class function GetHwCap(): UInt64; static; + class function GetHwCap2(): UInt64; static; +{$IFEND} + +{$IF DEFINED(HASHLIB_MSWINDOWS)} + public + class function HasProcessorFeature(AFeature: UInt32): Boolean; static; +{$IFEND} + + end; + +{$IFEND} // HASHLIB_ARM + +implementation + +{$IF DEFINED(HASHLIB_ARM)} + +{ TArmHwCapProvider } + +{ ===== Linux / Android: getauxval ===== } + +{$IF DEFINED(HASHLIB_LINUX) OR DEFINED(HASHLIB_ANDROID)} + +class procedure TArmHwCapProvider.ResolveOnce(); +var + LHandle: Pointer; +begin + if FResolved then + Exit; + + FGetAuxVal := nil; + FResolved := True; + + LHandle := dlopen(nil, RTLD_NOW); + if LHandle = nil then + Exit; + + try + // getauxval is available in glibc (Linux) and Bionic (Android API 18+) + FGetAuxVal := TGetAuxValFunc(dlsym(LHandle, 'getauxval')); + finally + dlclose(LHandle); + end; +end; + +class function TArmHwCapProvider.GetHwCap(): UInt64; +begin + ResolveOnce(); + if System.Assigned(FGetAuxVal) then + Result := FGetAuxVal(AT_HWCAP) + else + Result := 0; +end; + +class function TArmHwCapProvider.GetHwCap2(): UInt64; +begin + ResolveOnce(); + if System.Assigned(FGetAuxVal) then + Result := FGetAuxVal(AT_HWCAP2) + else + Result := 0; +end; + +{$IFEND} // HASHLIB_LINUX OR HASHLIB_ANDROID + +{ ===== BSD: elf_aux_info / _elf_aux_info ===== } + +{$IF DEFINED(HASHLIB_BSD)} + +class procedure TArmHwCapProvider.ResolveOnce(); +var + LHandle: Pointer; +begin + if FResolved then + Exit; + + FElfAuxInfo := nil; + FResolved := True; + + LHandle := dlopen(nil, RTLD_NOW); + if LHandle = nil then + Exit; + + try + // FreeBSD exposes elf_aux_info + FElfAuxInfo := TElfAuxInfoFunc(dlsym(LHandle, 'elf_aux_info')); + if not System.Assigned(FElfAuxInfo) then + begin + // NetBSD and DragonFlyBSD use the underscore-prefixed variant + FElfAuxInfo := TElfAuxInfoFunc(dlsym(LHandle, '_elf_aux_info')); + end; + finally + dlclose(LHandle); + end; +end; + +class function TArmHwCapProvider.GetHwCap(): UInt64; +var + LValue: UInt64; +begin + ResolveOnce(); + if System.Assigned(FElfAuxInfo) then + begin + LValue := 0; + if FElfAuxInfo(Int32(AT_HWCAP), @LValue, SizeOf(LValue)) = 0 then + Result := LValue + else + Result := 0; + end + else + Result := 0; +end; + +class function TArmHwCapProvider.GetHwCap2(): UInt64; +var + LValue: UInt64; +begin + ResolveOnce(); + if System.Assigned(FElfAuxInfo) then + begin + LValue := 0; + if FElfAuxInfo(Int32(AT_HWCAP2), @LValue, SizeOf(LValue)) = 0 then + Result := LValue + else + Result := 0; + end + else + Result := 0; +end; + +{$IFEND} // HASHLIB_BSD + +{ ===== Windows: IsProcessorFeaturePresent ===== } + +{$IF DEFINED(HASHLIB_MSWINDOWS)} + +class function TArmHwCapProvider.HasProcessorFeature(AFeature: UInt32): Boolean; +begin + Result := IsProcessorFeaturePresent(AFeature); +end; + +{$IFEND} // HASHLIB_MSWINDOWS + +{$IFEND} // HASHLIB_ARM + +end. diff --git a/HashLib/src/Utils/HlpArmSimdFeatures.pas b/HashLib/src/Utils/HlpArmSimdFeatures.pas index 4dd61e1..6f8f3d2 100644 --- a/HashLib/src/Utils/HlpArmSimdFeatures.pas +++ b/HashLib/src/Utils/HlpArmSimdFeatures.pas @@ -5,18 +5,26 @@ interface uses - HlpSimdLevels; + HlpSimdLevels +{$IF DEFINED(HASHLIB_ARM)} + , HlpArmHwCapProvider + {$IF DEFINED(HASHLIB_MACOS) OR DEFINED(HASHLIB_IOS)} + , HlpDarwinSysCtl + {$IFEND} +{$IFEND} + ; type TArmSimdFeatures = class sealed strict private class var - FSimdLevel: TArmSimdLevel; + FActiveSimdLevel: TArmSimdLevel; FHasAES: Boolean; FHasSHA1: Boolean; FHasSHA256: Boolean; FHasSHA512: Boolean; FHasSHA3: Boolean; + FHasCRC32: Boolean; FHasPMULL: Boolean; strict private @@ -28,6 +36,7 @@ TArmSimdFeatures = class sealed class function CPUHasSHA256(): Boolean; static; class function CPUHasSHA512(): Boolean; static; class function CPUHasSHA3(): Boolean; static; + class function CPUHasCRC32(): Boolean; static; class function CPUHasPMULL(): Boolean; static; private @@ -35,7 +44,7 @@ TArmSimdFeatures = class sealed class procedure ApplyBuildOverrides(); static; public - class function GetSimdLevel(): TArmSimdLevel; static; + class function GetActiveSimdLevel(): TArmSimdLevel; static; class function HasNEON(): Boolean; static; class function HasSVE(): Boolean; static; class function HasSVE2(): Boolean; static; @@ -44,6 +53,7 @@ TArmSimdFeatures = class sealed class function HasSHA256(): Boolean; static; class function HasSHA512(): Boolean; static; class function HasSHA3(): Boolean; static; + class function HasCRC32(): Boolean; static; class function HasPMULL(): Boolean; static; end; @@ -51,86 +61,319 @@ implementation { TArmSimdFeatures } +{ ========================= CPUHas* Detection Methods ======================== } + class function TArmSimdFeatures.CPUHasNEON(): Boolean; begin - // TODO: implement platform-specific NEON detection +{$IF DEFINED(HASHLIB_ARM)} + + {$IF DEFINED(HASHLIB_MSWINDOWS)} + // NEON is mandatory on Windows ARM64, but we verify for safety + Result := TArmHwCapProvider.HasProcessorFeature(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE); + + {$ELSEIF DEFINED(HASHLIB_LINUX) OR DEFINED(HASHLIB_ANDROID) OR DEFINED(HASHLIB_BSD)} + {$IF DEFINED(HASHLIB_AARCH64)} + // NEON (ASIMD) is mandatory on AArch64, but we verify for safety + Result := TArmHwCapProvider.GetHwCap() and HWCAP_ASIMD <> 0; + {$ELSE} + Result := TArmHwCapProvider.GetHwCap() and HWCAP_NEON <> 0; + {$IFEND} + + {$ELSEIF DEFINED(HASHLIB_MACOS) OR DEFINED(HASHLIB_IOS)} + // NEON is mandatory on AArch64 Apple Silicon, but we verify for safety + Result := TDarwinSysCtl.HasFeature('hw.optional.arm.FEAT_AdvSIMD', 'hw.optional.neon'); + + {$ELSE} + Result := False; + {$IFEND} + +{$ELSE} Result := False; +{$IFEND} end; class function TArmSimdFeatures.CPUHasSVE(): Boolean; begin - // TODO: implement platform-specific SVE detection +{$IF DEFINED(HASHLIB_ARM)} + {$IF DEFINED(HASHLIB_AARCH64)} + + {$IF DEFINED(HASHLIB_MSWINDOWS)} + // Windows ARM64 does not currently expose SVE detection + Result := False; + + {$ELSEIF DEFINED(HASHLIB_LINUX) OR DEFINED(HASHLIB_ANDROID) OR DEFINED(HASHLIB_BSD)} + Result := TArmHwCapProvider.GetHwCap() and HWCAP_SVE <> 0; + + {$ELSEIF DEFINED(HASHLIB_MACOS) OR DEFINED(HASHLIB_IOS)} + // Apple Silicon does not implement SVE, but we check anyway for future-proofing + Result := TDarwinSysCtl.HasFeature('hw.optional.arm.FEAT_SVE'); + + {$ELSE} + Result := False; + {$IFEND} + + {$ELSE} + // SVE is AArch64-only + Result := False; + {$IFEND} +{$ELSE} Result := False; +{$IFEND} end; class function TArmSimdFeatures.CPUHasSVE2(): Boolean; begin - // TODO: implement platform-specific SVE2 detection +{$IF DEFINED(HASHLIB_ARM)} + {$IF DEFINED(HASHLIB_AARCH64)} + + {$IF DEFINED(HASHLIB_MSWINDOWS)} + // Windows ARM64 does not currently expose SVE2 detection + Result := False; + + {$ELSEIF DEFINED(HASHLIB_LINUX) OR DEFINED(HASHLIB_ANDROID) OR DEFINED(HASHLIB_BSD)} + Result := TArmHwCapProvider.GetHwCap2() and HWCAP2_SVE2 <> 0; + + {$ELSEIF DEFINED(HASHLIB_MACOS) OR DEFINED(HASHLIB_IOS)} + // Apple Silicon does not implement SVE2, but we check anyway for future-proofing + Result := TDarwinSysCtl.HasFeature('hw.optional.arm.FEAT_SVE2'); + + {$ELSE} + Result := False; + {$IFEND} + + {$ELSE} + // SVE2 is AArch64-only + Result := False; + {$IFEND} +{$ELSE} Result := False; +{$IFEND} end; class function TArmSimdFeatures.CPUHasAES(): Boolean; begin - // TODO: implement platform-specific AES extension detection +{$IF DEFINED(HASHLIB_ARM)} + + {$IF DEFINED(HASHLIB_MSWINDOWS)} + // AES is bundled with crypto on Windows ARM64, but we verify for safety + Result := TArmHwCapProvider.HasProcessorFeature(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE); + + {$ELSEIF DEFINED(HASHLIB_LINUX) OR DEFINED(HASHLIB_ANDROID) OR DEFINED(HASHLIB_BSD)} + {$IF DEFINED(HASHLIB_AARCH64)} + Result := TArmHwCapProvider.GetHwCap() and HWCAP_AES <> 0; + {$ELSE} + Result := TArmHwCapProvider.GetHwCap2() and HWCAP2_AES <> 0; + {$IFEND} + + {$ELSEIF DEFINED(HASHLIB_MACOS) OR DEFINED(HASHLIB_IOS)} + // AES is present on all Apple Silicon, but we verify for safety + Result := TDarwinSysCtl.HasFeature('hw.optional.arm.FEAT_AES'); + + {$ELSE} + Result := False; + {$IFEND} + +{$ELSE} Result := False; +{$IFEND} end; class function TArmSimdFeatures.CPUHasSHA1(): Boolean; begin - // TODO: implement platform-specific SHA1 extension detection +{$IF DEFINED(HASHLIB_ARM)} + + {$IF DEFINED(HASHLIB_MSWINDOWS)} + // SHA1 is bundled with crypto on Windows ARM64, but we verify for safety + Result := TArmHwCapProvider.HasProcessorFeature(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE); + + {$ELSEIF DEFINED(HASHLIB_LINUX) OR DEFINED(HASHLIB_ANDROID) OR DEFINED(HASHLIB_BSD)} + {$IF DEFINED(HASHLIB_AARCH64)} + Result := TArmHwCapProvider.GetHwCap() and HWCAP_SHA1 <> 0; + {$ELSE} + Result := TArmHwCapProvider.GetHwCap2() and HWCAP2_SHA1 <> 0; + {$IFEND} + + {$ELSEIF DEFINED(HASHLIB_MACOS) OR DEFINED(HASHLIB_IOS)} + // SHA1 is present on all Apple Silicon, but we verify for safety + Result := TDarwinSysCtl.HasFeature('hw.optional.arm.FEAT_SHA1'); + + {$ELSE} + Result := False; + {$IFEND} + +{$ELSE} Result := False; +{$IFEND} end; class function TArmSimdFeatures.CPUHasSHA256(): Boolean; begin - // TODO: implement platform-specific SHA256 extension detection +{$IF DEFINED(HASHLIB_ARM)} + + {$IF DEFINED(HASHLIB_MSWINDOWS)} + // SHA256 is bundled with crypto on Windows ARM64, but we verify for safety + Result := TArmHwCapProvider.HasProcessorFeature(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE); + + {$ELSEIF DEFINED(HASHLIB_LINUX) OR DEFINED(HASHLIB_ANDROID) OR DEFINED(HASHLIB_BSD)} + {$IF DEFINED(HASHLIB_AARCH64)} + Result := TArmHwCapProvider.GetHwCap() and HWCAP_SHA2 <> 0; + {$ELSE} + Result := TArmHwCapProvider.GetHwCap2() and HWCAP2_SHA2 <> 0; + {$IFEND} + + {$ELSEIF DEFINED(HASHLIB_MACOS) OR DEFINED(HASHLIB_IOS)} + // SHA256 is present on all Apple Silicon, but we verify for safety + Result := TDarwinSysCtl.HasFeature('hw.optional.arm.FEAT_SHA256'); + + {$ELSE} + Result := False; + {$IFEND} + +{$ELSE} Result := False; +{$IFEND} end; class function TArmSimdFeatures.CPUHasSHA512(): Boolean; begin - // TODO: implement platform-specific SHA512 extension detection +{$IF DEFINED(HASHLIB_ARM)} + {$IF DEFINED(HASHLIB_AARCH64)} + + {$IF DEFINED(HASHLIB_MSWINDOWS)} + Result := TArmHwCapProvider.HasProcessorFeature(PF_ARM_V82_SHA512_INSTRUCTIONS_AVAILABLE); + + {$ELSEIF DEFINED(HASHLIB_LINUX) OR DEFINED(HASHLIB_ANDROID) OR DEFINED(HASHLIB_BSD)} + Result := TArmHwCapProvider.GetHwCap() and HWCAP_SHA512 <> 0; + + {$ELSEIF DEFINED(HASHLIB_MACOS) OR DEFINED(HASHLIB_IOS)} + Result := TDarwinSysCtl.HasFeature('hw.optional.arm.FEAT_SHA512', 'hw.optional.armv8_2_sha512'); + + {$ELSE} + Result := False; + {$IFEND} + + {$ELSE} + // SHA512 acceleration is AArch64-only (ARMv8.2) + Result := False; + {$IFEND} +{$ELSE} Result := False; +{$IFEND} end; class function TArmSimdFeatures.CPUHasSHA3(): Boolean; begin - // TODO: implement platform-specific SHA3 extension detection +{$IF DEFINED(HASHLIB_ARM)} + {$IF DEFINED(HASHLIB_AARCH64)} + + {$IF DEFINED(HASHLIB_MSWINDOWS)} + Result := TArmHwCapProvider.HasProcessorFeature(PF_ARM_V82_SHA3_INSTRUCTIONS_AVAILABLE); + + {$ELSEIF DEFINED(HASHLIB_LINUX) OR DEFINED(HASHLIB_ANDROID) OR DEFINED(HASHLIB_BSD)} + Result := TArmHwCapProvider.GetHwCap() and HWCAP_SHA3 <> 0; + + {$ELSEIF DEFINED(HASHLIB_MACOS) OR DEFINED(HASHLIB_IOS)} + Result := TDarwinSysCtl.HasFeature('hw.optional.arm.FEAT_SHA3', 'hw.optional.armv8_2_sha3'); + + {$ELSE} + Result := False; + {$IFEND} + + {$ELSE} + // SHA3 acceleration is AArch64-only (ARMv8.2) + Result := False; + {$IFEND} +{$ELSE} Result := False; +{$IFEND} +end; + +class function TArmSimdFeatures.CPUHasCRC32(): Boolean; +begin +{$IF DEFINED(HASHLIB_ARM)} + + {$IF DEFINED(HASHLIB_MSWINDOWS)} + // CRC32 is mandatory on Windows ARM64, but we verify for safety + Result := TArmHwCapProvider.HasProcessorFeature(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE); + + {$ELSEIF DEFINED(HASHLIB_LINUX) OR DEFINED(HASHLIB_ANDROID) OR DEFINED(HASHLIB_BSD)} + {$IF DEFINED(HASHLIB_AARCH64)} + Result := TArmHwCapProvider.GetHwCap() and HWCAP_CRC32 <> 0; + {$ELSE} + Result := TArmHwCapProvider.GetHwCap2() and HWCAP2_CRC32 <> 0; + {$IFEND} + + {$ELSEIF DEFINED(HASHLIB_MACOS) OR DEFINED(HASHLIB_IOS)} + // CRC32 is present on all Apple Silicon, but we verify for safety + Result := TDarwinSysCtl.HasFeature('hw.optional.armv8_crc32'); + + {$ELSE} + Result := False; + {$IFEND} + +{$ELSE} + Result := False; +{$IFEND} end; class function TArmSimdFeatures.CPUHasPMULL(): Boolean; begin - // TODO: implement platform-specific PMULL extension detection +{$IF DEFINED(HASHLIB_ARM)} + + {$IF DEFINED(HASHLIB_MSWINDOWS)} + // PMULL is bundled with crypto on Windows ARM64, but we verify for safety + Result := TArmHwCapProvider.HasProcessorFeature(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE); + + {$ELSEIF DEFINED(HASHLIB_LINUX) OR DEFINED(HASHLIB_ANDROID) OR DEFINED(HASHLIB_BSD)} + {$IF DEFINED(HASHLIB_AARCH64)} + Result := TArmHwCapProvider.GetHwCap() and HWCAP_PMULL <> 0; + {$ELSE} + Result := TArmHwCapProvider.GetHwCap2() and HWCAP2_PMULL <> 0; + {$IFEND} + + {$ELSEIF DEFINED(HASHLIB_MACOS) OR DEFINED(HASHLIB_IOS)} + // PMULL is present on all Apple Silicon, but we verify for safety + Result := TDarwinSysCtl.HasFeature('hw.optional.arm.FEAT_PMULL'); + + {$ELSE} + Result := False; + {$IFEND} + +{$ELSE} Result := False; +{$IFEND} end; +{ ========================= Probe & Override ================================= } + class procedure TArmSimdFeatures.ProbeHardwareAndCache(); begin - FSimdLevel := TArmSimdLevel.Scalar; + FActiveSimdLevel := TArmSimdLevel.Scalar; FHasAES := False; FHasSHA1 := False; FHasSHA256 := False; FHasSHA512 := False; FHasSHA3 := False; + FHasCRC32 := False; FHasPMULL := False; if CPUHasNEON() then begin - FSimdLevel := TArmSimdLevel.NEON; + FActiveSimdLevel := TArmSimdLevel.NEON; FHasAES := CPUHasAES(); FHasSHA1 := CPUHasSHA1(); FHasSHA256 := CPUHasSHA256(); FHasSHA512 := CPUHasSHA512(); FHasSHA3 := CPUHasSHA3(); + FHasCRC32 := CPUHasCRC32(); FHasPMULL := CPUHasPMULL(); if CPUHasSVE() then begin - FSimdLevel := TArmSimdLevel.SVE; + FActiveSimdLevel := TArmSimdLevel.SVE; if CPUHasSVE2() then - FSimdLevel := TArmSimdLevel.SVE2; + FActiveSimdLevel := TArmSimdLevel.SVE2; end; end; end; @@ -138,40 +381,43 @@ class procedure TArmSimdFeatures.ProbeHardwareAndCache(); class procedure TArmSimdFeatures.ApplyBuildOverrides(); begin {$IF DEFINED(HASHLIB_FORCE_SCALAR)} - FSimdLevel := TArmSimdLevel.Scalar; + FActiveSimdLevel := TArmSimdLevel.Scalar; FHasAES := False; FHasSHA1 := False; FHasSHA256 := False; FHasSHA512 := False; FHasSHA3 := False; + FHasCRC32 := False; FHasPMULL := False; {$ELSEIF DEFINED(HASHLIB_FORCE_NEON)} - if FSimdLevel > TArmSimdLevel.NEON then - FSimdLevel := TArmSimdLevel.NEON; + if FActiveSimdLevel > TArmSimdLevel.NEON then + FActiveSimdLevel := TArmSimdLevel.NEON; {$ELSEIF DEFINED(HASHLIB_FORCE_SVE)} - if FSimdLevel > TArmSimdLevel.SVE then - FSimdLevel := TArmSimdLevel.SVE; + if FActiveSimdLevel > TArmSimdLevel.SVE then + FActiveSimdLevel := TArmSimdLevel.SVE; {$IFEND} end; -class function TArmSimdFeatures.GetSimdLevel(): TArmSimdLevel; +{ ========================= Public Accessors ================================= } + +class function TArmSimdFeatures.GetActiveSimdLevel(): TArmSimdLevel; begin - Result := FSimdLevel; + Result := FActiveSimdLevel; end; class function TArmSimdFeatures.HasNEON(): Boolean; begin - Result := FSimdLevel >= TArmSimdLevel.NEON; + Result := FActiveSimdLevel >= TArmSimdLevel.NEON; end; class function TArmSimdFeatures.HasSVE(): Boolean; begin - Result := FSimdLevel >= TArmSimdLevel.SVE; + Result := FActiveSimdLevel >= TArmSimdLevel.SVE; end; class function TArmSimdFeatures.HasSVE2(): Boolean; begin - Result := FSimdLevel >= TArmSimdLevel.SVE2; + Result := FActiveSimdLevel >= TArmSimdLevel.SVE2; end; class function TArmSimdFeatures.HasAES(): Boolean; @@ -199,6 +445,11 @@ class function TArmSimdFeatures.HasSHA3(): Boolean; Result := FHasSHA3; end; +class function TArmSimdFeatures.HasCRC32(): Boolean; +begin + Result := FHasCRC32; +end; + class function TArmSimdFeatures.HasPMULL(): Boolean; begin Result := FHasPMULL; diff --git a/HashLib/src/Utils/HlpCpuFeatures.pas b/HashLib/src/Utils/HlpCpuFeatures.pas index 4965630..510d3ac 100644 --- a/HashLib/src/Utils/HlpCpuFeatures.pas +++ b/HashLib/src/Utils/HlpCpuFeatures.pas @@ -5,36 +5,57 @@ interface uses - HlpSimdLevels, - HlpX86SimdFeatures, - HlpArmSimdFeatures; + HlpSimdLevels +{$IF DEFINED(HASHLIB_X86)} + , HlpX86SimdFeatures +{$IFEND} +{$IF DEFINED(HASHLIB_ARM)} + , HlpArmSimdFeatures +{$IFEND} + ; type +{$IF DEFINED(HASHLIB_X86)} TCpuFeaturesX86 = class of TX86SimdFeatures; +{$IFEND} +{$IF DEFINED(HASHLIB_ARM)} TCpuFeaturesArm = class of TArmSimdFeatures; +{$IFEND} TCpuFeatures = class sealed strict private + {$IF DEFINED(HASHLIB_X86)} class function GetX86(): TCpuFeaturesX86; static; + {$IFEND} + {$IF DEFINED(HASHLIB_ARM)} class function GetArm(): TCpuFeaturesArm; static; + {$IFEND} public + {$IF DEFINED(HASHLIB_X86)} class property X86: TCpuFeaturesX86 read GetX86; + {$IFEND} + {$IF DEFINED(HASHLIB_ARM)} class property Arm: TCpuFeaturesArm read GetArm; + {$IFEND} end; implementation { TCpuFeatures } +{$IF DEFINED(HASHLIB_X86)} class function TCpuFeatures.GetX86(): TCpuFeaturesX86; begin Result := TX86SimdFeatures; end; +{$IFEND} +{$IF DEFINED(HASHLIB_ARM)} class function TCpuFeatures.GetArm(): TCpuFeaturesArm; begin Result := TArmSimdFeatures; end; +{$IFEND} end. diff --git a/HashLib/src/Utils/HlpDarwinSysCtl.pas b/HashLib/src/Utils/HlpDarwinSysCtl.pas new file mode 100644 index 0000000..e993588 --- /dev/null +++ b/HashLib/src/Utils/HlpDarwinSysCtl.pas @@ -0,0 +1,129 @@ +unit HlpDarwinSysCtl; + +{$I ..\Include\HashLib.inc} + +interface + +{$IF DEFINED(HASHLIB_ARM)} +{$IF DEFINED(HASHLIB_MACOS) OR DEFINED(HASHLIB_IOS)} + +uses +{$IFDEF FPC} + dl +{$ELSE} + Posix.Dlfcn +{$ENDIF} + ; + +type + /// + /// Resolves sysctlbyname from the already-loaded process image via + /// dlopen(nil) + dlsym, avoiding any static import of Posix.SysSysctl. + /// Provides a simple Boolean query for ARM feature detection on Darwin + /// (macOS and iOS). + /// + TDarwinSysCtl = class sealed + strict private + type + TSysCtlByNameFunc = function(AName: PAnsiChar; AOldP: Pointer; + AOldLenP: Pointer; ANewP: Pointer; ANewLen: NativeUInt): Int32; cdecl; + + strict private + class var + FSysCtlByName: TSysCtlByNameFunc; + FResolved: Boolean; + + strict private + class procedure ResolveOnce(); static; + + /// + /// Queries a single sysctl key. Returns True if the key exists and + /// its integer value is >= 1. + /// + class function QueryKey(const AName: PAnsiChar): Boolean; static; + + public + /// + /// Returns True if the named sysctl feature is available. + /// Tries AModernName first (macOS 12+ FEAT_* keys). If that key does + /// not exist or returns 0, falls back to ALegacyName (macOS 11 keys). + /// If ALegacyName is nil, no fallback is attempted. + /// + class function HasFeature(const AModernName: PAnsiChar; + const ALegacyName: PAnsiChar = nil): Boolean; static; + end; + +{$IFEND} // HASHLIB_MACOS OR HASHLIB_IOS +{$IFEND} // HASHLIB_ARM + +implementation + +{$IF DEFINED(HASHLIB_ARM)} +{$IF DEFINED(HASHLIB_MACOS) OR DEFINED(HASHLIB_IOS)} + +{ TDarwinSysCtl } + +class procedure TDarwinSysCtl.ResolveOnce(); +var + LHandle: Pointer; +begin + if FResolved then + Exit; + + FSysCtlByName := nil; + FResolved := True; + + LHandle := dlopen(nil, RTLD_NOW); + if LHandle = nil then + Exit; + + try + FSysCtlByName := TSysCtlByNameFunc(dlsym(LHandle, 'sysctlbyname')); + finally + dlclose(LHandle); + end; +end; + +class function TDarwinSysCtl.QueryKey(const AName: PAnsiChar): Boolean; +var + LValue: Int32; + LLen: NativeUInt; +begin + if (AName = nil) or (not System.Assigned(FSysCtlByName)) then + begin + Result := False; + Exit; + end; + + LValue := 0; + LLen := SizeOf(LValue); + + if FSysCtlByName(AName, @LValue, @LLen, nil, 0) = 0 then + Result := LValue >= 1 + else + Result := False; +end; + +class function TDarwinSysCtl.HasFeature(const AModernName: PAnsiChar; + const ALegacyName: PAnsiChar): Boolean; +begin + ResolveOnce(); + + if not System.Assigned(FSysCtlByName) then + begin + Result := False; + Exit; + end; + + // Try the modern FEAT_* key first (available on macOS 12+) + Result := QueryKey(AModernName); + + // If the modern key was not found or returned 0, try the legacy key + if (not Result) and (ALegacyName <> nil) then + Result := QueryKey(ALegacyName); +end; + +{$IFEND} // HASHLIB_MACOS OR HASHLIB_IOS +{$IFEND} // HASHLIB_ARM + +end. diff --git a/HashLib/src/Utils/HlpX86SimdFeatures.pas b/HashLib/src/Utils/HlpX86SimdFeatures.pas index 2eb7793..cbfcbce 100644 --- a/HashLib/src/Utils/HlpX86SimdFeatures.pas +++ b/HashLib/src/Utils/HlpX86SimdFeatures.pas @@ -17,7 +17,7 @@ TCpuIdResult = record strict private class var - FSimdLevel: TX86SimdLevel; + FActiveSimdLevel: TX86SimdLevel; FHasSHANI: Boolean; FHasPCLMULQDQ: Boolean; FHasVPCLMULQDQ: Boolean; @@ -37,7 +37,7 @@ TCpuIdResult = record class procedure ApplyBuildOverrides(); static; public - class function GetSimdLevel(): TX86SimdLevel; static; + class function GetActiveSimdLevel(): TX86SimdLevel; static; class function HasSSE2(): Boolean; static; class function HasSSSE3(): Boolean; static; class function HasAVX2(): Boolean; static; @@ -182,7 +182,7 @@ class function TX86SimdFeatures.CPUHasAESNI(): Boolean; class procedure TX86SimdFeatures.ProbeHardwareAndCache(); begin - FSimdLevel := TX86SimdLevel.Scalar; + FActiveSimdLevel := TX86SimdLevel.Scalar; FHasSHANI := False; FHasPCLMULQDQ := False; FHasVPCLMULQDQ := False; @@ -190,14 +190,14 @@ class procedure TX86SimdFeatures.ProbeHardwareAndCache(); if CPUHasSSE2() then begin - FSimdLevel := TX86SimdLevel.SSE2; + FActiveSimdLevel := TX86SimdLevel.SSE2; FHasPCLMULQDQ := CPUHasPCLMULQDQ(); if CPUHasSSSE3() then begin - FSimdLevel := TX86SimdLevel.SSSE3; + FActiveSimdLevel := TX86SimdLevel.SSSE3; if CPUHasAVX2() then begin - FSimdLevel := TX86SimdLevel.AVX2; + FActiveSimdLevel := TX86SimdLevel.AVX2; FHasVPCLMULQDQ := CPUHasVPCLMULQDQ(); end; end; @@ -210,21 +210,21 @@ class procedure TX86SimdFeatures.ProbeHardwareAndCache(); class procedure TX86SimdFeatures.ApplyBuildOverrides(); begin {$IF DEFINED(HASHLIB_FORCE_SCALAR)} - FSimdLevel := TX86SimdLevel.Scalar; + FActiveSimdLevel := TX86SimdLevel.Scalar; FHasSHANI := False; FHasPCLMULQDQ := False; FHasVPCLMULQDQ := False; FHasAESNI := False; {$ELSEIF DEFINED(HASHLIB_FORCE_SSE2)} - if FSimdLevel > TX86SimdLevel.SSE2 then - FSimdLevel := TX86SimdLevel.SSE2; + if FActiveSimdLevel > TX86SimdLevel.SSE2 then + FActiveSimdLevel := TX86SimdLevel.SSE2; FHasSHANI := False; FHasPCLMULQDQ := False; FHasVPCLMULQDQ := False; FHasAESNI := False; {$ELSEIF DEFINED(HASHLIB_FORCE_SSSE3)} - if FSimdLevel > TX86SimdLevel.SSSE3 then - FSimdLevel := TX86SimdLevel.SSSE3; + if FActiveSimdLevel > TX86SimdLevel.SSSE3 then + FActiveSimdLevel := TX86SimdLevel.SSSE3; FHasSHANI := False; FHasPCLMULQDQ := False; FHasVPCLMULQDQ := False; @@ -232,24 +232,24 @@ class procedure TX86SimdFeatures.ApplyBuildOverrides(); {$IFEND} end; -class function TX86SimdFeatures.GetSimdLevel(): TX86SimdLevel; +class function TX86SimdFeatures.GetActiveSimdLevel(): TX86SimdLevel; begin - Result := FSimdLevel; + Result := FActiveSimdLevel; end; class function TX86SimdFeatures.HasSSE2(): Boolean; begin - Result := FSimdLevel >= TX86SimdLevel.SSE2; + Result := FActiveSimdLevel >= TX86SimdLevel.SSE2; end; class function TX86SimdFeatures.HasSSSE3(): Boolean; begin - Result := FSimdLevel >= TX86SimdLevel.SSSE3; + Result := FActiveSimdLevel >= TX86SimdLevel.SSSE3; end; class function TX86SimdFeatures.HasAVX2(): Boolean; begin - Result := FSimdLevel >= TX86SimdLevel.AVX2; + Result := FActiveSimdLevel >= TX86SimdLevel.AVX2; end; class function TX86SimdFeatures.HasSHANI(): Boolean;