@@ -10,8 +10,8 @@ export cpu_isa
1010A structure which represents the Instruction Set Architecture (ISA) of a
1111computer. It holds the `Set` of features of the CPU.
1212
13- The numerical values of the features are automatically generated from the C
14- source code of Julia and stored in the `features_h.jl` Julia file .
13+ Feature bit indices come from the cpufeatures library's generated tables
14+ (extracted from LLVM's TableGen data at build time) .
1515"""
1616struct ISA
1717 features:: Set{UInt32}
@@ -23,55 +23,167 @@ Base.isless(a::ISA, b::ISA) = a < b
2323
2424include (string (Base. BUILDROOT, " features_h.jl" )) # include($BUILDROOT/base/features_h.jl)
2525
26- # Keep in sync with `arch_march_isa_mapping`.
26+ """
27+ _featurebytes_to_isa(buf::Vector{UInt8}) -> ISA
28+
29+ Convert a raw feature byte buffer (from cpufeatures) into an ISA.
30+ """
31+ function _featurebytes_to_isa (buf:: Vector{UInt8} )
32+ features = Set {UInt32} ()
33+ for byte_idx in 0 : length (buf)- 1
34+ b = buf[byte_idx + 1 ]
35+ b == 0 && continue
36+ for bit in 0 : 7
37+ if (b >> bit) & 1 != 0
38+ push! (features, UInt32 (byte_idx * 8 + bit))
39+ end
40+ end
41+ end
42+ return ISA (features)
43+ end
44+
45+ """
46+ _cross_lookup_cpu(arch::String, name::String) -> ISA
47+
48+ Look up hardware features for a CPU on any architecture using the
49+ cross-arch tables. Works regardless of host architecture.
50+ Returns an empty ISA if the CPU or architecture is not found.
51+ """
52+ function _cross_lookup_cpu (arch:: String , name:: String )
53+ nbytes = ccall (:jl_cpufeatures_cross_nbytes , Csize_t, (Cstring,), arch)
54+ nbytes == 0 && return ISA (Set {UInt32} ())
55+ buf = Vector {UInt8} (undef, nbytes)
56+ written = ccall (:jl_cpufeatures_cross_lookup , Csize_t,
57+ (Cstring, Cstring, Ptr{UInt8}, Csize_t),
58+ arch, name, buf, nbytes)
59+ written == 0 && return ISA (Set {UInt32} ())
60+ return _featurebytes_to_isa (buf)
61+ end
62+
63+ """
64+ _build_bit_to_name(arch::String) -> Dict{UInt32, String}
65+
66+ Build a mapping from feature bit index to feature name for an architecture.
67+ """
68+ function _build_bit_to_name (arch:: String )
69+ nfeats = ccall (:jl_cpufeatures_cross_num_features , UInt32, (Cstring,), arch)
70+ result = Dict {UInt32, String} ()
71+ for i in 0 : nfeats- 1
72+ name_ptr = ccall (:jl_cpufeatures_cross_feature_name , Cstring, (Cstring, UInt32), arch, i)
73+ name_ptr == C_NULL && continue
74+ bit = ccall (:jl_cpufeatures_cross_feature_bit , Cint, (Cstring, UInt32), arch, i)
75+ bit < 0 && continue
76+ result[UInt32 (bit)] = unsafe_string (name_ptr)
77+ end
78+ return result
79+ end
80+
81+ """
82+ feature_names(arch::String, cpu::String) -> Vector{String}
83+ feature_names(arch::String, isa::ISA) -> Vector{String}
84+ feature_names(isa::ISA) -> Vector{String}
85+ feature_names() -> Vector{String}
86+
87+ Return sorted hardware feature names. Can query by CPU name (on any
88+ architecture) or by ISA. Defaults to the host architecture and CPU.
89+
90+ # Examples
91+ ```julia
92+ feature_names() # host CPU features
93+ feature_names("x86_64", "haswell") # haswell's features
94+ feature_names("aarch64", "cortex-x925") # cross-arch query
95+ ```
96+ """
97+ feature_names () = feature_names (string (Sys. ARCH), _host_isa ())
98+ feature_names (isa:: ISA ) = feature_names (string (Sys. ARCH), isa)
99+ function feature_names (arch:: String , cpu:: String )
100+ isa = _cross_lookup_cpu (arch, cpu)
101+ return feature_names (arch, isa)
102+ end
103+ function feature_names (arch:: String , isa:: ISA )
104+ mapping = _build_bit_to_name (arch)
105+ return sort ([get (mapping, bit, " unknown_$bit " ) for bit in isa. features])
106+ end
107+
108+ """
109+ _lookup_cpu(name::String) -> ISA
110+
111+ Look up hardware features for the named CPU on the host architecture.
112+ Returns an empty ISA if the CPU name is not found.
113+ """
114+ function _lookup_cpu (name:: String )
115+ nbytes = ccall (:jl_cpufeatures_nbytes , Csize_t, ())
116+ buf = Vector {UInt8} (undef, nbytes)
117+ ret = ccall (:jl_cpufeatures_lookup , Cint, (Cstring, Ptr{UInt8}, Csize_t), name, buf, nbytes)
118+ ret != 0 && return ISA (Set {UInt32} ())
119+ return _featurebytes_to_isa (buf)
120+ end
121+
122+ """
123+ _host_isa() -> ISA
124+
125+ Get the hardware features of the host CPU from the cpufeatures library.
126+ """
127+ function _host_isa ()
128+ nbytes = ccall (:jl_cpufeatures_nbytes , Csize_t, ())
129+ buf = Vector {UInt8} (undef, nbytes)
130+ ccall (:jl_cpufeatures_host , Cvoid, (Ptr{UInt8}, Csize_t), buf, nbytes)
131+ return _featurebytes_to_isa (buf)
132+ end
133+
134+ # Build an ISA list for a given architecture family.
135+ # Uses cross-arch lookup so it works on any host.
136+ # Entries with empty cpuname get an empty ISA (generic baseline).
137+ function _make_isa_list (arch:: String , entries:: Vector{Pair{String,String}} )
138+ result = Pair{String,ISA}[]
139+ for (label, cpuname) in entries
140+ if isempty (cpuname)
141+ push! (result, label => ISA (Set {UInt32} ()))
142+ else
143+ push! (result, label => _cross_lookup_cpu (arch, cpuname))
144+ end
145+ end
146+ return result
147+ end
148+
149+ # ISA definitions per architecture family.
150+ # CPU names are LLVM names in the cpufeatures database.
151+ # Keep in sync with `arch_march_isa_mapping` in binaryplatforms.jl.
27152const ISAs_by_family = Dict (
28- " i686" => [
29- # Source: https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html.
30- # Implicit in all sets, because always required by Julia: mmx, sse, sse2
31- " pentium4" => ISA (Set {UInt32} ()),
32- " prescott" => ISA (Set ((JL_X86_sse3,))),
33- ],
34- " x86_64" => [
35- # Source: https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html.
36- # Implicit in all sets, because always required by x86-64 architecture: mmx, sse, sse2
37- " x86_64" => ISA (Set {UInt32} ()),
38- " core2" => ISA (Set ((JL_X86_sse3, JL_X86_ssse3))),
39- " nehalem" => ISA (Set ((JL_X86_sse3, JL_X86_ssse3, JL_X86_sse41, JL_X86_sse42, JL_X86_popcnt))),
40- " sandybridge" => ISA (Set ((JL_X86_sse3, JL_X86_ssse3, JL_X86_sse41, JL_X86_sse42, JL_X86_popcnt, JL_X86_avx, JL_X86_aes, JL_X86_pclmul))),
41- " haswell" => ISA (Set ((JL_X86_movbe, JL_X86_sse3, JL_X86_ssse3, JL_X86_sse41, JL_X86_sse42, JL_X86_popcnt, JL_X86_avx, JL_X86_avx2, JL_X86_aes, JL_X86_pclmul, JL_X86_fsgsbase, JL_X86_rdrnd, JL_X86_fma, JL_X86_bmi, JL_X86_bmi2, JL_X86_f16c))),
42- " skylake" => ISA (Set ((JL_X86_movbe, JL_X86_sse3, JL_X86_ssse3, JL_X86_sse41, JL_X86_sse42, JL_X86_popcnt, JL_X86_avx, JL_X86_avx2, JL_X86_aes, JL_X86_pclmul, JL_X86_fsgsbase, JL_X86_rdrnd, JL_X86_fma, JL_X86_bmi, JL_X86_bmi2, JL_X86_f16c, JL_X86_rdseed, JL_X86_adx, JL_X86_prfchw, JL_X86_clflushopt, JL_X86_xsavec, JL_X86_xsaves))),
43- " skylake_avx512" => ISA (Set ((JL_X86_movbe, JL_X86_sse3, JL_X86_ssse3, JL_X86_sse41, JL_X86_sse42, JL_X86_popcnt, JL_X86_pku, JL_X86_avx, JL_X86_avx2, JL_X86_aes, JL_X86_pclmul, JL_X86_fsgsbase, JL_X86_rdrnd, JL_X86_fma, JL_X86_bmi, JL_X86_bmi2, JL_X86_f16c, JL_X86_rdseed, JL_X86_adx, JL_X86_prfchw, JL_X86_clflushopt, JL_X86_xsavec, JL_X86_xsaves, JL_X86_avx512f, JL_X86_clwb, JL_X86_avx512vl, JL_X86_avx512bw, JL_X86_avx512dq, JL_X86_avx512cd))),
44- ],
45- " armv6l" => [
46- # The only armv6l processor we know of that runs Julia on armv6l
47- # We don't have a good way to tell the different armv6l variants apart through features,
48- # and honestly we don't care much since it's basically this one chip that people want to use with Julia.
49- " arm1176jzfs" => ISA (Set {UInt32} ()),
50- ],
51- " armv7l" => [
52- " armv7l" => ISA (Set {UInt32} ()),
53- " armv7l+neon" => ISA (Set ((JL_AArch32_neon,))),
54- " armv7l+neon+vfpv4" => ISA (Set ((JL_AArch32_neon, JL_AArch32_vfp4))),
55- ],
56- " aarch64" => [
57- # Implicit in all sets, because always required: fp, asimd
58- " armv8.0-a" => ISA (Set {UInt32} ()),
59- " armv8.1-a" => ISA (Set ((JL_AArch64_v8_1a, JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm))),
60- " armv8.2-a+crypto" => ISA (Set ((JL_AArch64_v8_2a, JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm, JL_AArch64_aes, JL_AArch64_sha2))),
61- " a64fx" => ISA (Set ((JL_AArch64_v8_2a, JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm, JL_AArch64_sha2, JL_AArch64_ccpp, JL_AArch64_complxnum, JL_AArch64_fullfp16, JL_AArch64_sve))),
62- " apple_m1" => ISA (Set ((JL_AArch64_v8_5a, JL_AArch64_lse, JL_AArch64_crc, JL_AArch64_rdm, JL_AArch64_aes, JL_AArch64_sha2, JL_AArch64_sha3, JL_AArch64_ccpp, JL_AArch64_complxnum, JL_AArch64_fp16fml, JL_AArch64_fullfp16, JL_AArch64_dotprod, JL_AArch64_rcpc, JL_AArch64_altnzcv))),
63- ],
64- " riscv64" => [
65- " riscv64" => ISA (Set {UInt32} ()),
66- ],
67- " powerpc64le" => [
68- # We have no way to test powerpc64le features yet, so we're only going to declare the lowest ISA:
69- " power8" => ISA (Set {UInt32} ()),
70- ],
71- " riscv64" => [
72- # We have no way to test riscv64 features yet, so we're only going to declare the lowest ISA:
73- " riscv64" => ISA (Set {UInt32} ()),
74- ],
153+ " i686" => _make_isa_list (" x86_64" , [
154+ " pentium4" => " " ,
155+ " prescott" => " prescott" ,
156+ ]),
157+ " x86_64" => _make_isa_list (" x86_64" , [
158+ " x86_64" => " " ,
159+ " core2" => " core2" ,
160+ " nehalem" => " nehalem" ,
161+ " sandybridge" => " sandybridge" ,
162+ " haswell" => " haswell" ,
163+ " skylake" => " skylake" ,
164+ " skylake_avx512" => " skylake-avx512" ,
165+ ]),
166+ " aarch64" => _make_isa_list (" aarch64" , [
167+ " armv8.0-a" => " " ,
168+ " armv8.1-a" => " cortex-a76" ,
169+ " armv8.2-a+crypto" => " cortex-a78" ,
170+ " a64fx" => " a64fx" ,
171+ " apple_m1" => " apple-a14" ,
172+ ]),
173+ " armv6l" => _make_isa_list (" aarch64" , [
174+ " arm1176jzfs" => " " ,
175+ ]),
176+ " armv7l" => _make_isa_list (" aarch64" , [
177+ " armv7l" => " " ,
178+ " armv7l+neon" => " " ,
179+ " armv7l+neon+vfpv4" => " " ,
180+ ]),
181+ " riscv64" => _make_isa_list (" riscv64" , [
182+ " riscv64" => " " ,
183+ ]),
184+ " powerpc64le" => _make_isa_list (" powerpc64le" , [
185+ " power8" => " " ,
186+ ]),
75187)
76188
77189# Test a CPU feature exists on the currently-running host
@@ -96,27 +208,13 @@ function normalize_arch(arch::String)
96208 return arch
97209end
98210
99- let
100- # Collect all relevant features for the current architecture, if any.
101- FEATURES = UInt32[]
102- arch = normalize_arch (String (Sys. ARCH))
103- if arch in keys (ISAs_by_family)
104- for isa in ISAs_by_family[arch]
105- unique! (append! (FEATURES, last (isa). features))
106- end
107- end
108-
109- # Use `@eval` to inline the list of features.
110- @eval function cpu_isa ()
111- return ISA (Set {UInt32} (feat for feat in $ (FEATURES) if test_cpu_feature (feat)))
112- end
113- end
114-
115211"""
116212 cpu_isa()
117213
118214Return the [`ISA`](@ref) (instruction set architecture) of the current CPU.
119215"""
120- cpu_isa
216+ function cpu_isa ()
217+ return _host_isa ()
218+ end
121219
122220end # module CPUID
0 commit comments