native 0.0.1
Vectors, masks and wide register packs for C++26
Loading...
Searching...
No Matches
native.arm.features.ccm
1// SPDX-FileCopyrightText: 2026 Edward Kmett <ekmett@gmail.com>
2// SPDX-License-Identifier: BSD-2-Clause OR Apache-2.0
3module;
4#include <cstdint>
5#include <cstddef>
6#if defined(__APPLE__)
7#include <cerrno>
8#include <sys/sysctl.h>
9#elif defined(__linux__)
10#include <cerrno>
11#include <sys/auxv.h>
12#include <asm/hwcap.h>
13#elif defined(_WIN32)
14#ifndef NOMINMAX
15#define NOMINMAX
16#endif
17// Import only the SDK contract used here. The full umbrella also declares UI
18// APIs with anonymous parameter types that cannot be merged after a BMI import.
19// Standalone SDK headers require the architecture marker normally set by windows.h.
20#if defined(_M_ARM64) && !defined(_ARM64_)
21#define _ARM64_
22#endif
23#include <processthreadsapi.h>
24#endif
25
26export module native.arm.features;
27export import native.isa;
28
29export namespace native {
34
48 bool baseline_observed = false;
50 bool fp = false;
52 bool asimd = false;
54 bool fp16_observed = false;
56 bool scalar_fp16 = false;
58 bool vector_fp16 = false;
60 bool bf16_observed = false;
62 bool bf16 = false;
64 bool ebf16_observed = false;
67 bool ebf16 = false;
70 isa<arm> extra_observed{}, extra_features{};
71 };
72
74 };
75
82 arm_capabilities observe_arm_capabilities() noexcept;
83}
84
85namespace native {
87 arm_capabilities result;
88#if defined(__APPLE__)
89 auto query=[](char const * key, bool & value) noexcept {
90 int data=0; std::size_t size=sizeof(data);
91 if(sysctlbyname(key,&data,&size,nullptr,0)!=0) return errno==ENOENT ? 0 : -1;
92 if(size!=sizeof(data)) return -1;
93 value=data!=0; return 1;
94 };
95 bool baseline=false, half=false;
96 result.raw.baseline_observed=query("hw.optional.neon",baseline)==1;
97 result.raw.fp=result.raw.asimd=baseline;
98 auto status=query("hw.optional.arm.FEAT_FP16",half);
99 if(status==0) status=query("hw.optional.neon_fp16",half);
100 result.raw.fp16_observed=status==1;
101 result.raw.scalar_fp16=result.raw.vector_fp16=half;
102 result.raw.bf16_observed=query("hw.optional.arm.FEAT_BF16",result.raw.bf16)==1;
103 result.raw.ebf16_observed=query("hw.optional.arm.FEAT_EBF16",result.raw.ebf16)==1;
104 auto extra=[&](arm_feature f,char const * first,char const * second=nullptr) {
105 bool a=false,b=true;
106 auto observed=query(first,a)==1;
107 if(second) observed=(query(second,b)==1)&&observed;
108 if(observed) result.raw.extra_observed.set(f,true);
109 if(observed && a && b) result.raw.extra_features.set(f,true);
110 };
111 extra(arm_feature::aes,"hw.optional.arm.FEAT_AES");
112 extra(arm_feature::pmull,"hw.optional.arm.FEAT_PMULL");
113 extra(arm_feature::sha1,"hw.optional.arm.FEAT_SHA1");
114 extra(arm_feature::sha2,"hw.optional.arm.FEAT_SHA256");
115 extra(arm_feature::sha3,"hw.optional.arm.FEAT_SHA3");
116 extra(arm_feature::sha512,"hw.optional.arm.FEAT_SHA512");
117 extra(arm_feature::sm3,"hw.optional.arm.FEAT_SM3");
118 extra(arm_feature::sm4,"hw.optional.arm.FEAT_SM4");
119 extra(arm_feature::crc,"hw.optional.armv8_crc32");
120 extra(arm_feature::lse,"hw.optional.arm.FEAT_LSE");
121 extra(arm_feature::rdm,"hw.optional.arm.FEAT_RDM");
122 extra(arm_feature::fp16fml,"hw.optional.arm.FEAT_FHM");
123 extra(arm_feature::dotprod,"hw.optional.arm.FEAT_DotProd");
124 extra(arm_feature::complxnum,"hw.optional.arm.FEAT_FCMA");
125 extra(arm_feature::jsconv,"hw.optional.arm.FEAT_JSCVT");
126 extra(arm_feature::rcpc,"hw.optional.arm.FEAT_LRCPC");
127 extra(arm_feature::pauth,"hw.optional.arm.FEAT_PAuth");
128 extra(arm_feature::i8mm,"hw.optional.arm.FEAT_I8MM");
129#elif defined(__linux__)
130 errno=0;
131 auto hwcap=getauxval(AT_HWCAP);
132 if(hwcap==0 && errno!=0) return result;
133 result.raw.baseline_observed=result.raw.fp16_observed=true;
134 result.raw.fp=(hwcap&HWCAP_FP)!=0;
135 result.raw.asimd=(hwcap&HWCAP_ASIMD)!=0;
136 result.raw.scalar_fp16=(hwcap&HWCAP_FPHP)!=0;
137 result.raw.vector_fp16=(hwcap&HWCAP_ASIMDHP)!=0;
138 auto extra=[&](arm_feature f,unsigned long mask) {
139 result.raw.extra_observed.set(f,true);
140 if((hwcap&mask)==mask) result.raw.extra_features.set(f,true);
141 };
142 extra(arm_feature::aes,HWCAP_AES);
143 extra(arm_feature::pmull,HWCAP_PMULL);
144 extra(arm_feature::sha1,HWCAP_SHA1);
145 extra(arm_feature::sha2,HWCAP_SHA2);
146 extra(arm_feature::crc,HWCAP_CRC32);
147 extra(arm_feature::lse,HWCAP_ATOMICS);
148#ifdef HWCAP_ASIMDRDM
149 extra(arm_feature::rdm,HWCAP_ASIMDRDM);
150#endif
151#ifdef HWCAP_ASIMDFHM
152 extra(arm_feature::fp16fml,HWCAP_ASIMDFHM);
153#endif
154#ifdef HWCAP_ASIMDDP
155 extra(arm_feature::dotprod,HWCAP_ASIMDDP);
156#endif
157#ifdef HWCAP_FCMA
158 extra(arm_feature::complxnum,HWCAP_FCMA);
159#endif
160#ifdef HWCAP_JSCVT
161 extra(arm_feature::jsconv,HWCAP_JSCVT);
162#endif
163#ifdef HWCAP_LRCPC
164 extra(arm_feature::rcpc,HWCAP_LRCPC);
165#endif
166#ifdef HWCAP_SHA3
167 extra(arm_feature::sha3,HWCAP_SHA3);
168#endif
169#ifdef HWCAP_SHA512
170 extra(arm_feature::sha512,HWCAP_SHA512);
171#endif
172#ifdef HWCAP_SM3
173 extra(arm_feature::sm3,HWCAP_SM3);
174#endif
175#ifdef HWCAP_SM4
176 extra(arm_feature::sm4,HWCAP_SM4);
177#endif
178#if defined(HWCAP_PACA) && defined(HWCAP_PACG)
179 extra(arm_feature::pauth,HWCAP_PACA|HWCAP_PACG);
180#endif
181 errno=0;
182 auto hwcap2=getauxval(AT_HWCAP2);
183 if(hwcap2!=0 || errno==0) {
184#ifdef HWCAP2_I8MM
185 result.raw.extra_observed.set(arm_feature::i8mm,true);
186 result.raw.extra_features.set(arm_feature::i8mm,(hwcap2&HWCAP2_I8MM)!=0);
187#endif
188#ifdef HWCAP2_BF16
189 result.raw.bf16_observed=true;
190 result.raw.bf16=(hwcap2&HWCAP2_BF16)!=0;
191#endif
192#ifdef HWCAP2_EBF16
193 result.raw.ebf16_observed=true;
194 result.raw.ebf16=(hwcap2&HWCAP2_EBF16)!=0;
195#endif
196 }
197#elif defined(_WIN32)
198 result.raw.baseline_observed=result.raw.fp16_observed=true;
199 result.raw.fp=IsProcessorFeaturePresent(PF_ARM_V8_INSTRUCTIONS_AVAILABLE)!=0;
200 result.raw.asimd=IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE)!=0;
201 // Documented SDK 26100 selector; older HALs return false for unknown features.
202 constexpr DWORD fp16_feature=67; // PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE
203 result.raw.scalar_fp16=result.raw.vector_fp16=IsProcessorFeaturePresent(fp16_feature)!=0;
204 constexpr DWORD bf16_feature=68; // PF_ARM_V86_BF16_INSTRUCTIONS_AVAILABLE
205 constexpr DWORD ebf16_feature=69; // PF_ARM_V86_EBF16_INSTRUCTIONS_AVAILABLE
206 result.raw.bf16_observed=result.raw.ebf16_observed=true;
207 result.raw.bf16=IsProcessorFeaturePresent(bf16_feature)!=0;
208 result.raw.ebf16=IsProcessorFeaturePresent(ebf16_feature)!=0;
209 auto extra=[&](arm_feature f,DWORD selector) {
210 result.raw.extra_observed.set(f,true);
211 if(IsProcessorFeaturePresent(selector)) result.raw.extra_features.set(f,true);
212 };
213 // A positive bundle establishes every component. A negative bundle cannot
214 // distinguish absent components from independently available ones.
215 constexpr arm_feature crypto[]{arm_feature::aes,arm_feature::pmull,arm_feature::sha1,arm_feature::sha2};
216 if(IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE))
217 for(auto feature:crypto) {
218 result.raw.extra_observed.set(feature,true);
219 result.raw.extra_features.set(feature,true);
220 }
221 extra(arm_feature::crc,PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE);
222 extra(arm_feature::lse,PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE);
223 extra(arm_feature::dotprod,43); // PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE
224 extra(arm_feature::jsconv,44); // PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE
225 extra(arm_feature::rcpc,45); // PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE
226 extra(arm_feature::sha3,64); // PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE
227 extra(arm_feature::sha512,65); // PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE
228 extra(arm_feature::i8mm,66); // PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE
229 // Windows exposes no selectors for the remaining catalog entries here;
230 // leave them unobserved, even if a CPU model is usually known to have them.
231#endif
232 auto features=detail::decode_arm_features(result.raw);
233 result.present=features.present;
234 result.observed=features.observed;
235 return result;
236 }
237}
arm_capabilities observe_arm_capabilities() noexcept
typename mask_traits< std::remove_cvref_t< T > >::type mask
Definition mask_traits.h:22
Architecture-tagged vectors, register packs and supporting value types. Native arithmetic follows its...
arm_feature
Independently observable ARM instruction features, using local bit indices.
Definition isa.h:24
Raw OS query results retained for diagnostics and enhanced BF16 behavior.
bool bf16
OS advertises non-streaming Advanced SIMD BF16 instructions (FEAT_BF16).
bool fp
OS permits baseline floating-point instructions and their register state.
bool vector_fp16
OS advertises Advanced SIMD half-precision arithmetic (ASIMDHP/FEAT_FP16).
bool fp16_observed
True only after an OS half-arithmetic feature query succeeds.
bool bf16_observed
True only after an OS BF16 feature query succeeds.
bool asimd
OS permits baseline Advanced SIMD instructions and their register state.
bool baseline_observed
True only after the OS baseline FP/Advanced SIMD query succeeds.
bool ebf16_observed
True only after an OS enhanced BF16 feature query succeeds.
bool scalar_fp16
OS advertises scalar half-precision arithmetic (FPHP/FEAT_FP16).
isa< arm > present
Features whose queries succeeded and reported support; no compiler closure.
isa< arm > observed
Features whose OS queries succeeded, whether present or absent.
raw_observations raw
Diagnostic snapshot; editing it does not update present/observed.
constexpr void set(feature_type f, bool value) noexcept
Definition isa.h:225
A structural set of one family's features, without prerequisite closure.
Definition isa.h:58