Skip to content

Commit

Permalink
Add CPUID submodule
Browse files Browse the repository at this point in the history
This is a small internal module which defines the API to query the Instruction
Set Architecture (ISA) of the current CPU.  This can be used for example to
select an artifact in a JLL package which is compatible with the current CPU.
  • Loading branch information
giordano committed Sep 15, 2020
1 parent 8b412cc commit 4ce5867
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 2 deletions.
65 changes: 65 additions & 0 deletions base/binaryplatforms.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,62 @@ export AbstractPlatform, Platform, HostPlatform, platform_dlext, tags, arch, os,
select_platform, platforms_match, platform_name
import .Libc.Libdl

### Submodule with information about CPU features
module CPUID

export cpu_isa

"""
ISA(features::Set{UInt32})
A structure which represents the Instruction Set Architecture (ISA) of a
computer. It holds the `Set` of features of the CPU.
The numerical values of the features are automatically generated from the C
source code of Julia and stored in the `features_h.jl` Julia file.
"""
struct ISA
features::Set{UInt32}
end

Base.:<=(a::ISA, b::ISA) = a.features <= b.features
Base.:<(a::ISA, b::ISA) = a.features < b.features
Base.isless(a::ISA, b::ISA) = a < b

include("features_h.jl")

# Sets of features for the x86-64 microarchitectures come from
# https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html. Keep in sync with
# `arch_march_isa_mapping`.
const ISAs_by_family = Dict(
"x86_64" => (
"x86_64" => ISA(Set{UInt32}()),
"core2" => ISA(Set((JL_X86_sse3, JL_X86_ssse3))),
"nehalem" => ISA(Set((JL_X86_sse3, JL_X86_ssse3, JL_X86_sse41, JL_X86_sse42, JL_X86_popcnt))),
"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))),
"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))),
"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))),
"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))),
)
)

test_cpu_feature(feature::UInt32) = ccall(:jl_test_cpu_feature, Bool, (UInt32,), feature)
cpu_family() = String(Sys.ARCH)

"""
cpu_isa()
Return the [`ISA`](@ref) (instruction set architecture) of the current CPU.
"""
function cpu_isa()
all_features = last(last(get(ISAs_by_family, cpu_family(), "" => [ISA(Set{UInt32}())]))).features
return ISA(Set{UInt32}(feat for feat in all_features if test_cpu_feature(feat)))
end

end # module CPUID

using .CPUID

# This exists to ease compatibility with old-style Platform objects
abstract type AbstractPlatform; end

Expand Down Expand Up @@ -563,6 +619,15 @@ const arch_mapping = Dict(
"armv6l" => "armv6l",
"powerpc64le" => "p(ower)?pc64le",
)
# Keep this in sync with `CPUID.ISAs_by_family`
const arch_march_isa_mapping = Dict(
"x86_64" => Dict(
"x86_64" => CPUID.ISAs_by_family["x86_64"][1].second, # x86_64
"avx" => CPUID.ISAs_by_family["x86_64"][4], # sandybridge
"avx2" => CPUID.ISAs_by_family["x86_64"][5], # haswell
"avx512" => CPUID.ISAs_by_family["x86_64"][7], # skylake_avx512
)
)
const os_mapping = Dict(
"macos" => "-apple-darwin[\\d\\.]*",
"freebsd" => "-(.*-)?freebsd[\\d\\.]*",
Expand Down
16 changes: 14 additions & 2 deletions test/binaryplatforms.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
using Test, Base.BinaryPlatforms
using Test, Base.BinaryPlatforms, Base.BinaryPlatforms.CPUID

@testset "CPUID" begin
@test CPUID.cpu_isa() isa CPUID.ISA

get_x86_64(n) = (CPUID.ISAs_by_family["x86_64"][n].second)
@test get_x86_64(2) < get_x86_64(4)
@test get_x86_64(5) <= get_x86_64(5)
@test get_x86_64(3) >= get_x86_64(3)
@test get_x86_64(7) >= get_x86_64(1)
@test sort([get_x86_64(6), get_x86_64(4), get_x86_64(2), get_x86_64(4)]) ==
[get_x86_64(2), get_x86_64(4), get_x86_64(4), get_x86_64(6)]
end

# Helper constructor to create a Platform with `validate_strict` set to `true`.
P(args...; kwargs...) = Platform(args...; validate_strict=true, kwargs...)
Expand Down Expand Up @@ -354,4 +366,4 @@ end
@test !platforms_match(ac, bc)
@test platforms_match(ac, ac)
@test platforms_match(bc, bc)
end
end

0 comments on commit 4ce5867

Please sign in to comment.