Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ version = "0.1.17"
BitTwiddlingConvenienceFunctions = "62783981-4cbd-42fc-bca8-16325de8dc4b"
IfElse = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173"
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
Static = "aedffcd0-7271-4cad-89d0-dc628f76c6d3"

[compat]
Expand Down
54 changes: 49 additions & 5 deletions src/HostCPUFeatures.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,32 @@ end
using Libdl, Static
using Static: Zero, One, lt, gt
using IfElse: ifelse
using Preferences

using BitTwiddlingConvenienceFunctions: prevpow2, nextpow2, intlog2

export has_feature, fma_fast, pick_vector_width, pick_vector_width_shift, register_count,
register_size, simd_integer_register_size

_cpu_target = if @has_preference("cpu_target")
@load_preference("cpu_target")
else
Base.unsafe_string(Base.JLOptions().cpu_target)
end

const build_cpu_target = if occursin("native", _cpu_target)
"native" # 'native' takes priority if provided
else
split(_cpu_target, ";")[1]
end

# If true, this will opt-in to "freeze" an under-approximation of the CPU features at precompile-
# time based on the CPU target.
#
# This is only done by default if "native" was excluded from the CPU target (or via a preference).
const freeze_cpu_target =
@load_preference("freeze_cpu_target", false) || build_cpu_target != "native"

function get_cpu_name()::String
if isdefined(Sys, :CPU_NAME)
Sys.CPU_NAME
Expand All @@ -37,19 +57,43 @@ unwrap(::StaticSymbol{S}) where {S} = S

@noinline function redefine()
@debug "Defining CPU name."
define_cpu_name()
redefine_cpu_name()

reset_features!()
reset_extra_features!()
end
const BASELINE_CPU_NAME = get_cpu_name()
const allow_eval = @load_preference("allow_runtime_invalidation", false)
Copy link

Choose a reason for hiding this comment

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

Why not to check that --trim is enabled in JLOptions ?

everyone who will use it with trim will have to find out this culprit themself

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The main thinking is that this preference is usable by more than just JuliaC.jl, since some users may wish to be opt-in to error / warn on invalidation storms from this package. JuliaC.jl can set this preference automatically, so the end-user experience is the same.

Also technically checking JLOptions at pre-compilation time won't detect --trim properly, but something like JuliaLang/JuliaC.jl#31 would work


function make_generic(target)
target == "native" && return false
if Sys.ARCH === :x86_64 || Sys.ARCH === :i686
make_generic_x86(target)
return true
else
return false
end
end

make_generic(build_cpu_target)

function __init__()
ccall(:jl_generating_output, Cint, ()) == 1 && return
if Sys.ARCH === :x86_64 || Sys.ARCH === :i686
target = Base.unsafe_string(Base.JLOptions().cpu_target)
occursin("native", target) || return make_generic(target)
freeze_cpu_target && return # CPU info fixed at precompile-time

runtime_target = Base.unsafe_string(Base.JLOptions().cpu_target)
if !occursin("native", runtime_target)
# The CPU target included "native" at pre-compile time, but at runtime it did not!
#
# Fixing this discepancy will invalidate the whole world (so it should probably
# throw an error), but we do it anyway for backwards-compatibility.
if make_generic(runtime_target)
return nothing
end
end
if BASELINE_CPU_NAME != Sys.CPU_NAME::String
redefine()
end
BASELINE_CPU_NAME == Sys.CPU_NAME::String || redefine()
return nothing
end

Expand Down
27 changes: 20 additions & 7 deletions src/cpu_info.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,22 @@ function set_features!()
end
Libc.free(features_cstring)
end
set_features!()


if build_cpu_target == "native"
set_features!()
end

function reset_features!()
features, features_cstring = feature_string()
for ext ∈ features
feature, has = process_feature(ext)
if _has_feature(feature) ≠ has
@debug "Defining $(has ? "presence" : "absense") of feature $feature."
set_feature(feature, has)
if allow_eval
Copy link

Choose a reason for hiding this comment

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

This code doesn't execute if there's a preference right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Depends on which preference you mean

Are you saying you don't think this guard is necessary?

@debug "Defining $(has ? "presence" : "absense") of feature $feature."
set_feature(feature, has)
else
@warn "Runtime invalidation was disabled, but the CPU info is out-of-date.\nWill continue with incorrect CPU feature flag: $ext."
end
end
end
Libc.free(features_cstring)
Expand All @@ -58,8 +63,16 @@ end
register_size(::Type{T}) where {T} = register_size()
register_size(::Type{T}) where {T<:Union{Signed,Unsigned}} = simd_integer_register_size()

function define_cpu_name()
function redefine_cpu_name()
cpu = QuoteNode(Symbol(get_cpu_name()))
@eval cpu_name() = Val{$cpu}()
if allow_eval
@eval cpu_name() = Val{$cpu}()
else
@warn "Runtime invalidation was disabled, but the CPU info is out-of-date.\nWill continue with incorrect CPU name (from build time)."
end
end

let _cpu = QuoteNode(Symbol(build_cpu_target == "native" ?
get_cpu_name() : build_cpu_target))
@eval cpu_name() = Val{$_cpu}()
end
define_cpu_name()
16 changes: 13 additions & 3 deletions src/cpu_info_aarch64.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function _set_sve_vector_width!(bytes = _dynamic_register_size())
end


if _has_aarch64_sve()# && !(Bool(has_feature(Val(:aarch64_sve))))
if build_cpu_target == "native" && _has_aarch64_sve()# && !(Bool(has_feature(Val(:aarch64_sve))))
has_feature(::Val{:aarch64_sve_cpuid}) = True()
_set_sve_vector_width!()
else
Expand All @@ -39,10 +39,20 @@ end

function reset_extra_features!()
drs = _dynamic_register_size()
register_size() ≠ drs && _set_sve_vector_width!(drs)
if register_size() ≠ drs
if allow_eval
_set_sve_vector_width!(drs)
else
@warn "Runtime invalidation was disabled, but the CPU info is out-of-date.\nWill continue with incorrect CPU register size."
end
end
hassve = _has_aarch64_sve()
if hassve ≠ has_feature(Val(:aarch64_sve_cpuid))
@eval has_feature(::Val{:aarch64_sve_cpuid}) = $(Expr(:call, hassve ? :True : :False))
if allow_eval
@eval has_feature(::Val{:aarch64_sve_cpuid}) = $(Expr(:call, hassve ? :True : :False))
else
@warn "Runtime invalidation was disabled, but the CPU info is out-of-date.\nWill continue with incorrect CPU feature flag: :aarch64_sve_cpuid."
end
end
end

Expand Down
18 changes: 13 additions & 5 deletions src/cpu_info_x86.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,26 @@ fast_int64_to_double() = has_feature(Val(:x86_64_avx512dq))

fast_half() = False()

@noinline function setfeaturefalse(s)
@inline function setfeaturefalse(s)
Copy link
Member

Choose a reason for hiding this comment

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

why the inlining switch?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was to improve inferrability - has_feature(Val(s)) is only inferrable if the literal value of s is available in the function, which is true in all of the callers (the argument is always a literal Symbol)

if has_feature(Val(s)) === True()
@eval has_feature(::Val{$(QuoteNode(s))}) = False()
if allow_eval
@eval has_feature(::Val{$(QuoteNode(s))}) = False()
else
@warn "Runtime invalidation was disabled, but the CPU info is out-of-date.\nWill continue with incorrect CPU feature flag: $s."
end
end
end
@noinline function setfeaturetrue(s)
@inline function setfeaturetrue(s)
if has_feature(Val(s)) === False()
@eval has_feature(::Val{$(QuoteNode(s))}) = True()
if allow_eval
@eval has_feature(::Val{$(QuoteNode(s))}) = True()
else
@warn "Runtime invalidation was disabled, but the CPU info is out-of-date.\nWill continue with incorrect CPU feature flag: $s."
end
end
end

function make_generic(target)
function make_generic_x86(target)
if occursin("tigerlake", target) || occursin("znver4", target) || occursin("sapphirerapids", target)
# most feature-complete architectures we use
setfeaturetrue(:x86_64_avx512ifma)
Expand Down
Loading