diff --git a/Project.toml b/Project.toml index 13090557..e533017a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "BinaryBuilderBase" uuid = "7f725544-6523-48cd-82d1-3fa08ff4056e" authors = ["Elliot Saba "] -version = "1.8.0" +version = "1.8.1" [deps] CodecZlib = "944b1d66-785c-5afd-91f1-9de20f533193" diff --git a/src/Products.jl b/src/Products.jl index 9cc3a5ec..169ec36f 100644 --- a/src/Products.jl +++ b/src/Products.jl @@ -222,7 +222,7 @@ function locate(lp::LibraryProduct, prefix::Prefix; platform::AbstractPlatform = end # If it does, try to `dlopen()` it if the current platform is good - if (!lp.dont_dlopen && !skip_dlopen) && platforms_match(platform, HostPlatform()) + if (!lp.dont_dlopen && !skip_dlopen) && platforms_match(platform, augment_microarchitecture!(HostPlatform())) if isolate # Isolated dlopen is a lot slower, but safer if success(`$(Base.julia_cmd()) --startup-file=no -e "import Libdl; Libdl.dlopen(\"$dl_path\")"`) diff --git a/src/utils.jl b/src/utils.jl index 0284499b..d27e2232 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,6 +1,9 @@ # The functions in this file may not be used anywhere in this package but may # needed by different modules of BinaryBuilder.jl +using Base.BinaryPlatforms: arch_march_isa_mapping, set_compare_strategy! +using Base.BinaryPlatforms.CPUID + with_logfile(f::Function, prefix::Prefix, name::String; subdir="") = with_logfile(f, joinpath(logdir(prefix; subdir), name)) function with_logfile(f::Function, logfile::String) mkpath(dirname(logfile)) @@ -60,3 +63,48 @@ end # We want the AnyPlatform to look like `default_host_platform`, get_concrete_platform(::AnyPlatform, shards::Vector{CompilerShard}) = get_concrete_platform(default_host_platform, shards) + +function march_comparison_strategy(a::String, b::String, a_requested::Bool, b_requested::Bool) + # If both b and a requested, then we fall back to equality: + if a_requested && b_requested + return a == b + end + + function get_arch_isa(isa_name::String) + for (arch, isas) in arch_march_isa_mapping + for (name, isa) in isas + name == isa_name && return arch, isa + end + end + return nothing, nothing + end + + a_arch, a_isa = get_arch_isa(a) + b_arch, b_isa = get_arch_isa(b) + if any(isnothing, (a_arch, b_arch)) || a_arch != b_arch + # Architectures are definitely not compatible, exit early + return false + end + + if a_requested + # ISA `b` is compatible with ISA `a` only if it's a subset of `a` + return b_isa ≤ a_isa + else + # ISA `a` is compatible with ISA `b` only if it's a subset of `b` + return a_isa ≤ b_isa + end +end + +function augment_microarchitecture!(platform::Platform) + if haskey(platform, "march") + set_compare_strategy!(platform, "march", march_comparison_strategy) + return platform + end + + host_arch = arch(HostPlatform()) + host_isas = arch_march_isa_mapping[host_arch] + idx = findlast(((name, isa),) -> isa <= CPUID.cpu_isa(), host_isas) + platform["march"] = first(host_isas[idx]) + set_compare_strategy!(platform, "march", march_comparison_strategy) + return platform +end diff --git a/test/compat.jl b/test/compat.jl index 97abaf7b..87f393ec 100644 --- a/test/compat.jl +++ b/test/compat.jl @@ -71,3 +71,44 @@ using BinaryBuilderBase: download_verify, list_tarball_files end end end + +using BinaryBuilderBase: march_comparison_strategy +using Base.BinaryPlatforms: Platform, platforms_match, set_compare_strategy! +@testset "Microarchitecture augmentation" begin + linux_x86_64 = Platform("x86_64", "linux") + linux_avx = Platform("x86_64", "linux"; march="avx") + linux_avx2 = Platform("x86_64", "linux"; march="avx2") + linux_avx512 = Platform("x86_64", "linux"; march="avx512") + # Platform with non-existing microarchitecture + linux_bad = Platform("x86_64", "linux"; march="bad") + + # Without any custom comparison strategy, the base platform without march matches + # everything, but the others are all incompatible + @test platforms_match(linux_x86_64, linux_avx) + @test platforms_match(linux_x86_64, linux_avx2) + @test platforms_match(linux_x86_64, linux_avx512) + @test platforms_match(linux_x86_64, linux_bad) + @test !platforms_match(linux_avx, linux_avx2) + @test !platforms_match(linux_avx, linux_avx512) + @test !platforms_match(linux_avx, linux_bad) + @test !platforms_match(linux_avx2, linux_bad) + @test !platforms_match(linux_avx2, linux_avx512) + @test !platforms_match(linux_avx512, linux_bad) + + # Teach AVX2 platform how to compare the others + set_compare_strategy!(linux_avx2, "march", march_comparison_strategy) + for compatible_p in (linux_x86_64, linux_avx) + @test platforms_match(compatible_p, linux_avx2) + @test platforms_match(linux_avx2, compatible_p) + end + for incompatible_p in (linux_avx512, linux_bad) + @test !platforms_match(incompatible_p, linux_avx2) + @test !platforms_match(linux_avx2, incompatible_p) + end + + # Teach also AVX platform how to compare + set_compare_strategy!(linux_avx, "march", march_comparison_strategy) + # Now when we compare AVX and AVX2, they must be equal + @test !platforms_match(linux_avx, linux_avx2) + @test !platforms_match(linux_avx2, linux_avx) +end