Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 53 additions & 1 deletion src/Platforms.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Pkg.Artifacts, Pkg.BinaryPlatforms, Logging

export AnyPlatform, ExtendedPlatform, base_platform
export AnyPlatform, ExtendedPlatform, base_platform, extended_platform_key_abi

"""
AnyPlatform()
Expand Down Expand Up @@ -207,3 +207,55 @@ function Pkg.Artifacts.pack_platform!(meta::Dict, p::ExtendedPlatform)
# override the default ones
Artifacts.pack_platform!(meta, base_platform(p))
end

# Get the list of CPU features for the host system querying libLLVM
function get_cpu_features()
if VERSION < v"1.4"
error("Cannot automatically detect the features of the CPU with this version of Julia!")
end
Comment on lines +213 to +215
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we go by this route, all the microarchitecture stuff will work only with Julia v1.4+, as this requires libllvm v8+. Not too bad since the alternative would be to have some code in Base to do the same, but that would require Julia v1.6 or more.

features = filter(f -> startswith(f, "+"),
split(unsafe_string(ccall(:LLVMGetHostCPUFeatures, Cstring, ())),
","))
return sort!(replace.(features, r"^\+" => ""))
end

# Get the microarchitecture of a CPU from the list of its features.
# TODO: support other architectures, like armv7l and aarch64.
function march(cpu_features::Vector{String})
# List of CPU features from https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
if all(in(cpu_features), ("mmx", "sse", "sse2")) # Generic x86_64
if all(in(cpu_features), ("sse3", "ssse3", "sse4.1", "sse4.2", "popcnt", "avx", "aes", "pclmul")) # avx
if all(in(cpu_features), ("movbe", "avx2", "fsgsbase", "rdrnd", "fma", "bmi", "bmi2", "f16c")) # avx2
# Some names are different: ADCX -> ADX, PREFETCHW -> PRFCHW
if all(in(cpu_features), ("pku", "rdseed", "adx", "prfchw", "clflushopt", "xsavec", "xsaves", "avx512f", "clwb", "avx512vl", "avx512bw", "avx512dq", "avx512cd")) # avx512
return "avx512"
end
return "avx2"
end
return "avx"
end
return "x86_64"
else
@warn "Cannot determine the microarchitecture for the given set of features!"
return nothing
Comment on lines +239 to +240
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want a hard failure if we can't determine the microarchitecture from the given set of features, but it's better to issue a warning just in case we're we're getting strange results. Also, this function would need to be used only when we're asking the set of features for an architecture we know we support (currently only x86_64), as done below in extended_platform_key_abi

end
end

# NOTE: the keyword arguments are *not* part of the public API, they're only
# used for testing purposes and they may change in the future.
"""
extended_platform_key_abi()

Returns the `Platform` representing the current platform. It is an
[`ExtendedPlatform`](@ref) if it possible to detect additional features, like
the microarchitecture.
"""
function extended_platform_key_abi(; p::Platform = platform_key_abi(),
cpu_features::Vector{String} = get_cpu_features())

if arch(p) == :x86_64
return ExtendedPlatform(p; march=march(cpu_features))
else
return p
end
end
27 changes: 26 additions & 1 deletion test/platforms.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Test
using Pkg, Pkg.PlatformEngines, Pkg.BinaryPlatforms, Pkg.Artifacts
using BinaryBuilderBase
using BinaryBuilderBase: abi_agnostic, get_concrete_platform, march
using BinaryBuilderBase: abi_agnostic, get_concrete_platform, march, get_cpu_features

@testset "Supported Platforms" begin
all = supported_platforms()
Expand Down Expand Up @@ -205,6 +205,31 @@ end
# Extending different platforms
@test !platforms_match(ExtendedPlatform(Linux(:i686); cuda="10.1"), ExtendedPlatform(FreeBSD(:x86_64); cuda="11.1"))
end

@testset "extended_platform_key_abi" begin
if VERSION >= v"1.4"
@test get_cpu_features() isa Vector{String}
else
@test_throws ErrorException get_cpu_features()
end

@test @test_logs (:warn, r"^Cannot determine the microarchitecture") march(String[]) === nothing
for (cpu_features, μarch) in (
(["mmx", "sse", "sse2"], "x86_64"),
# The following sets of CPU features are obtained on real machines
(["64bit", "aes", "avx", "cmov", "cx16", "fma4", "lzcnt", "mmx", "pclmul", "popcnt", "prfchw", "sahf", "sse", "sse2", "sse3", "sse4.1", "sse4.2", "sse4a", "ssse3", "xop", "xsave"], "avx"),
(["64bit", "aes", "avx", "avx2", "bmi", "bmi2", "cmov", "cx16", "cx8", "f16c", "fma", "fsgsbase", "fxsr", "invpcid", "lzcnt", "mmx", "movbe", "pclmul", "popcnt", "rdrnd", "sahf", "sse", "sse2", "sse3", "sse4.1", "sse4.2", "ssse3", "xsave", "xsaveopt"], "avx2"),
(["64bit", "aes", "avx", "avx2", "bmi", "bmi2", "cmov", "cx16", "f16c", "fma", "fsgsbase", "invpcid", "lzcnt", "mmx", "movbe", "pclmul", "popcnt", "rdrnd", "sahf", "sse", "sse2", "sse3", "sse4.1", "sse4.2", "ssse3", "xsave", "xsaveopt"], "avx2"),
(["64bit", "adx", "aes", "avx", "avx2", "bmi", "bmi2", "clflushopt", "clwb", "clzero", "cmov", "cx16", "f16c", "fma", "fsgsbase", "lzcnt", "mmx", "movbe", "mwaitx", "pclmul", "popcnt", "prfchw", "rdpid", "rdrnd", "rdseed", "sahf", "sha", "sse", "sse2", "sse3", "sse4.1", "sse4.2", "sse4a", "ssse3", "wbnoinvd", "xsave", "xsavec", "xsaveopt", "xsaves"], "avx2"),
(["64bit", "adx", "aes", "avx", "avx2", "avx512bw", "avx512cd", "avx512dq", "avx512f", "avx512vl", "avx512vnni", "bmi", "bmi2", "clflushopt", "clwb", "cmov", "cx16", "cx8", "f16c", "fma", "fsgsbase", "fxsr", "invpcid", "lzcnt", "mmx", "movbe", "mpx", "pclmul", "pku", "popcnt", "prfchw", "rdrnd", "rdseed", "rtm", "sahf", "sse", "sse2", "sse3", "sse4.1", "sse4.2", "ssse3", "xsave", "xsavec", "xsaveopt", "xsaves"], "avx512")
)
@test march(cpu_features) == μarch
p = Linux(:x86_64)
@test extended_platform_key_abi(; p=p, cpu_features=cpu_features) == ExtendedPlatform(p; march=μarch)
end
p = Linux(:i686)
@test extended_platform_key_abi(; p=p, cpu_features=String[]) == p
end
end

@testset "AnyPlatform" begin
Expand Down