Skip to content

Commit 958da95

Browse files
vchuravypchintalapudivtjnash
authored
print feature flags used for matching pkgimage (#50172)
``` julia> @CCall jl_dump_host_cpu()::Cvoid CPU: znver2 Features: sse3, pclmul, ssse3, fma, cx16, sse4.1, sse4.2, movbe, popcnt, aes, xsave, avx, f16c, rdrnd, fsgsbase, bmi, avx2, bmi2, rdseed, adx, clflushopt, clwb, sha, rdpid, sahf, lzcnt, sse4a, prfchw, mwaitx, xsaveopt, xsavec, xsaves, clzero, wbnoinvd julia> target = only(Base.current_image_targets()) znver2; flags=0; features_en=(sse3, pclmul, ssse3, fma, cx16, sse4.1, sse4.2, movbe, popcnt, aes, xsave, avx, f16c, fsgsbase, bmi, avx2, bmi2, adx, clflushopt, clwb, sha, rdpid, sahf, lzcnt, sse4a, prfchw, mwaitx, xsavec, xsaves, clzero, wbnoinvd) ``` Co-authored-by: Prem Chintalapudi <prem.chintalapudi@gmail.com> Co-authored-by: Jameson Nash <vtjnash@gmail.com>
1 parent 8b5e3e9 commit 958da95

File tree

6 files changed

+206
-41
lines changed

6 files changed

+206
-41
lines changed

base/loading.jl

Lines changed: 91 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2841,11 +2841,9 @@ get_compiletime_preferences(m::Module) = get_compiletime_preferences(PkgId(m).uu
28412841
get_compiletime_preferences(::Nothing) = String[]
28422842

28432843
function check_clone_targets(clone_targets)
2844-
try
2845-
ccall(:jl_check_pkgimage_clones, Cvoid, (Ptr{Cchar},), clone_targets)
2846-
return true
2847-
catch
2848-
return false
2844+
rejection_reason = ccall(:jl_check_pkgimage_clones, Any, (Ptr{Cchar},), clone_targets)
2845+
if rejection_reason !== nothing
2846+
return rejection_reason
28492847
end
28502848
end
28512849

@@ -2877,6 +2875,88 @@ function show(io::IO, cf::CacheFlags)
28772875
print(io, ", opt_level = ", cf.opt_level)
28782876
end
28792877

2878+
struct ImageTarget
2879+
name::String
2880+
flags::Int32
2881+
ext_features::String
2882+
features_en::Vector{UInt8}
2883+
features_dis::Vector{UInt8}
2884+
end
2885+
2886+
function parse_image_target(io::IO)
2887+
flags = read(io, Int32)
2888+
nfeature = read(io, Int32)
2889+
feature_en = read(io, 4*nfeature)
2890+
feature_dis = read(io, 4*nfeature)
2891+
name_len = read(io, Int32)
2892+
name = String(read(io, name_len))
2893+
ext_features_len = read(io, Int32)
2894+
ext_features = String(read(io, ext_features_len))
2895+
ImageTarget(name, flags, ext_features, feature_en, feature_dis)
2896+
end
2897+
2898+
function parse_image_targets(targets::Vector{UInt8})
2899+
io = IOBuffer(targets)
2900+
ntargets = read(io, Int32)
2901+
targets = Vector{ImageTarget}(undef, ntargets)
2902+
for i in 1:ntargets
2903+
targets[i] = parse_image_target(io)
2904+
end
2905+
return targets
2906+
end
2907+
2908+
function current_image_targets()
2909+
targets = @ccall jl_reflect_clone_targets()::Vector{UInt8}
2910+
return parse_image_targets(targets)
2911+
end
2912+
2913+
struct FeatureName
2914+
name::Cstring
2915+
bit::UInt32 # bit index into a `uint32_t` array;
2916+
llvmver::UInt32 # 0 if it is available on the oldest LLVM version we support
2917+
end
2918+
2919+
function feature_names()
2920+
fnames = Ref{Ptr{FeatureName}}()
2921+
nf = Ref{Csize_t}()
2922+
@ccall jl_reflect_feature_names(fnames::Ptr{Ptr{FeatureName}}, nf::Ptr{Csize_t})::Cvoid
2923+
if fnames[] == C_NULL
2924+
@assert nf[] == 0
2925+
return Vector{FeatureName}(undef, 0)
2926+
end
2927+
Base.unsafe_wrap(Array, fnames[], nf[], own=false)
2928+
end
2929+
2930+
function test_feature(features::Vector{UInt8}, feat::FeatureName)
2931+
bitidx = feat.bit
2932+
u8idx = div(bitidx, 8) + 1
2933+
bit = bitidx % 8
2934+
return (features[u8idx] & (1 << bit)) != 0
2935+
end
2936+
2937+
function show(io::IO, it::ImageTarget)
2938+
print(io, it.name)
2939+
if !isempty(it.ext_features)
2940+
print(io, ",", it.ext_features)
2941+
end
2942+
print(io, "; flags=", it.flags)
2943+
print(io, "; features_en=(")
2944+
first = true
2945+
for feat in feature_names()
2946+
if test_feature(it.features_en, feat)
2947+
name = Base.unsafe_string(feat.name)
2948+
if first
2949+
first = false
2950+
print(io, name)
2951+
else
2952+
print(io, ", ", name)
2953+
end
2954+
end
2955+
end
2956+
print(io, ")")
2957+
# Is feature_dis useful?
2958+
end
2959+
28802960
# Set by FileWatching.__init__()
28812961
global mkpidlock_hook
28822962
global trymkpidlock_hook
@@ -2914,7 +2994,6 @@ function maybe_cachefile_lock(f, pkg::PkgId, srcpath::String; stale_age=300)
29142994
f()
29152995
end
29162996
end
2917-
29182997
# returns true if it "cachefile.ji" is stale relative to "modpath.jl" and build_id for modkey
29192998
# otherwise returns the list of dependencies to also check
29202999
@constprop :none function stale_cachefile(modpath::String, cachefile::String; ignore_loaded::Bool = false)
@@ -2948,8 +3027,12 @@ end
29483027
@debug "Rejecting cache file $cachefile for $modkey since it would require usage of pkgimage"
29493028
return true
29503029
end
2951-
if !check_clone_targets(clone_targets)
2952-
@debug "Rejecting cache file $cachefile for $modkey since pkgimage can't be loaded on this target"
3030+
rejection_reasons = check_clone_targets(clone_targets)
3031+
if !isnothing(rejection_reasons)
3032+
@debug("Rejecting cache file $cachefile for $modkey:",
3033+
Reasons=rejection_reasons,
3034+
var"Image Targets"=parse_image_targets(clone_targets),
3035+
var"Current Targets"=current_image_targets())
29533036
return true
29543037
end
29553038
if !isfile(ocachefile)

src/processor.cpp

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,13 @@ static inline bool test_nbit(const T1 &bits, T2 _bitidx)
107107
}
108108

109109
template<typename T>
110-
static inline void unset_bits(T &bits)
110+
static inline void unset_bits(T &bits) JL_NOTSAFEPOINT
111111
{
112112
(void)bits;
113113
}
114114

115115
template<typename T, typename T1, typename... Rest>
116-
static inline void unset_bits(T &bits, T1 _bitidx, Rest... rest)
116+
static inline void unset_bits(T &bits, T1 _bitidx, Rest... rest) JL_NOTSAFEPOINT
117117
{
118118
auto bitidx = static_cast<uint32_t>(_bitidx);
119119
auto u32idx = bitidx / 32;
@@ -142,7 +142,7 @@ static inline void set_bit(T &bits, T1 _bitidx, bool val)
142142
template<size_t n>
143143
struct FeatureList {
144144
uint32_t eles[n];
145-
uint32_t &operator[](size_t pos)
145+
uint32_t &operator[](size_t pos) JL_NOTSAFEPOINT
146146
{
147147
return eles[pos];
148148
}
@@ -297,12 +297,6 @@ static inline void append_ext_features(std::vector<std::string> &features,
297297
* Target specific type/constant definitions, always enable.
298298
*/
299299

300-
struct FeatureName {
301-
const char *name;
302-
uint32_t bit; // bit index into a `uint32_t` array;
303-
uint32_t llvmver; // 0 if it is available on the oldest LLVM version we support
304-
};
305-
306300
template<typename CPU, size_t n>
307301
struct CPUSpec {
308302
const char *name;
@@ -636,7 +630,13 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback)
636630
jl_dlsym(hdl, "jl_image_pointers", (void**)&pointers, 1);
637631

638632
const void *ids = pointers->target_data;
639-
uint32_t target_idx = callback(ids);
633+
jl_value_t* rejection_reason = nullptr;
634+
JL_GC_PUSH1(&rejection_reason);
635+
uint32_t target_idx = callback(ids, &rejection_reason);
636+
if (target_idx == (uint32_t)-1) {
637+
jl_throw(jl_new_struct(jl_errorexception_type, rejection_reason));
638+
}
639+
JL_GC_POP();
640640

641641
if (pointers->header->version != 1) {
642642
jl_error("Image file is not compatible with this version of Julia");
@@ -855,17 +855,20 @@ struct SysimgMatch {
855855
// Find the best match in the sysimg.
856856
// Select the best one based on the largest vector register and largest compatible feature set.
857857
template<typename S, typename T, typename F>
858-
static inline SysimgMatch match_sysimg_targets(S &&sysimg, T &&target, F &&max_vector_size)
858+
static inline SysimgMatch match_sysimg_targets(S &&sysimg, T &&target, F &&max_vector_size, jl_value_t **rejection_reason)
859859
{
860860
SysimgMatch match;
861861
bool match_name = false;
862862
int feature_size = 0;
863+
std::vector<const char *> rejection_reasons;
864+
rejection_reasons.reserve(sysimg.size());
863865
for (uint32_t i = 0; i < sysimg.size(); i++) {
864866
auto &imgt = sysimg[i];
865867
if (!(imgt.en.features & target.dis.features).empty()) {
866868
// Check sysimg enabled features against runtime disabled features
867869
// This is valid (and all what we can do)
868870
// even if one or both of the targets are unknown.
871+
rejection_reasons.push_back("Rejecting this target due to use of runtime-disabled features\n");
869872
continue;
870873
}
871874
if (imgt.name == target.name) {
@@ -876,25 +879,44 @@ static inline SysimgMatch match_sysimg_targets(S &&sysimg, T &&target, F &&max_v
876879
}
877880
}
878881
else if (match_name) {
882+
rejection_reasons.push_back("Rejecting this target since another target has a cpu name match\n");
879883
continue;
880884
}
881885
int new_vsz = max_vector_size(imgt.en.features);
882-
if (match.vreg_size > new_vsz)
886+
if (match.vreg_size > new_vsz) {
887+
rejection_reasons.push_back("Rejecting this target since another target has a larger vector register size\n");
883888
continue;
889+
}
884890
int new_feature_size = imgt.en.features.nbits();
885891
if (match.vreg_size < new_vsz) {
886892
match.best_idx = i;
887893
match.vreg_size = new_vsz;
888894
feature_size = new_feature_size;
895+
rejection_reasons.push_back("Updating best match to this target due to larger vector register size\n");
889896
continue;
890897
}
891-
if (new_feature_size < feature_size)
898+
if (new_feature_size < feature_size) {
899+
rejection_reasons.push_back("Rejecting this target since another target has a larger feature set\n");
892900
continue;
901+
}
893902
match.best_idx = i;
894903
feature_size = new_feature_size;
904+
rejection_reasons.push_back("Updating best match to this target\n");
905+
}
906+
if (match.best_idx == (uint32_t)-1) {
907+
// Construct a nice error message for debugging purposes
908+
std::string error_msg = "Unable to find compatible target in cached code image.\n";
909+
for (size_t i = 0; i < rejection_reasons.size(); i++) {
910+
error_msg += "Target ";
911+
error_msg += std::to_string(i);
912+
error_msg += " (";
913+
error_msg += sysimg[i].name;
914+
error_msg += "): ";
915+
error_msg += rejection_reasons[i];
916+
}
917+
if (rejection_reason)
918+
*rejection_reason = jl_pchar_to_string(error_msg.data(), error_msg.size());
895919
}
896-
if (match.best_idx == (uint32_t)-1)
897-
jl_error("Unable to find compatible target in system image.");
898920
return match;
899921
}
900922

@@ -946,3 +968,30 @@ static inline void dump_cpu_spec(uint32_t cpu, const FeatureList<n> &features,
946968
#include "processor_fallback.cpp"
947969

948970
#endif
971+
972+
extern "C" JL_DLLEXPORT jl_value_t* jl_reflect_clone_targets() {
973+
auto specs = jl_get_llvm_clone_targets();
974+
const uint32_t base_flags = 0;
975+
std::vector<uint8_t> data;
976+
auto push_i32 = [&] (uint32_t v) {
977+
uint8_t buff[4];
978+
memcpy(buff, &v, 4);
979+
data.insert(data.end(), buff, buff + 4);
980+
};
981+
push_i32(specs.size());
982+
for (uint32_t i = 0; i < specs.size(); i++) {
983+
push_i32(base_flags | (specs[i].flags & JL_TARGET_UNKNOWN_NAME));
984+
auto &specdata = specs[i].data;
985+
data.insert(data.end(), specdata.begin(), specdata.end());
986+
}
987+
988+
jl_value_t *arr = (jl_value_t*)jl_alloc_array_1d(jl_array_uint8_type, data.size());
989+
uint8_t *out = (uint8_t*)jl_array_data(arr);
990+
memcpy(out, data.data(), data.size());
991+
return arr;
992+
}
993+
994+
extern "C" JL_DLLEXPORT void jl_reflect_feature_names(const FeatureName **fnames, size_t *nf) {
995+
*fnames = feature_names;
996+
*nf = nfeature_names;
997+
}

src/processor.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ JL_DLLEXPORT jl_value_t *jl_get_cpu_features(void);
226226
// Dump the name and feature set of the host CPU
227227
// For debugging only
228228
JL_DLLEXPORT void jl_dump_host_cpu(void);
229-
JL_DLLEXPORT void jl_check_pkgimage_clones(char* data);
229+
JL_DLLEXPORT jl_value_t* jl_check_pkgimage_clones(char* data);
230230

231231
JL_DLLEXPORT int32_t jl_set_zero_subnormals(int8_t isZero);
232232
JL_DLLEXPORT int32_t jl_get_zero_subnormals(void);
@@ -274,6 +274,15 @@ struct jl_target_spec_t {
274274
extern "C" JL_DLLEXPORT std::vector<jl_target_spec_t> jl_get_llvm_clone_targets(void) JL_NOTSAFEPOINT;
275275
std::string jl_get_cpu_name_llvm(void) JL_NOTSAFEPOINT;
276276
std::string jl_get_cpu_features_llvm(void) JL_NOTSAFEPOINT;
277+
278+
struct FeatureName {
279+
const char *name;
280+
uint32_t bit; // bit index into a `uint32_t` array;
281+
uint32_t llvmver; // 0 if it is available on the oldest LLVM version we support
282+
};
283+
284+
extern "C" JL_DLLEXPORT jl_value_t* jl_reflect_clone_targets();
285+
extern "C" JL_DLLEXPORT void jl_reflect_feature_names(const FeatureName **feature_names, size_t *nfeatures);
277286
#endif
278287

279288
#endif

src/processor_arm.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,7 +1561,7 @@ static int max_vector_size(const FeatureList<feature_sz> &features)
15611561
#endif
15621562
}
15631563

1564-
static uint32_t sysimg_init_cb(const void *id)
1564+
static uint32_t sysimg_init_cb(const void *id, jl_value_t **rejection_reason)
15651565
{
15661566
// First see what target is requested for the JIT.
15671567
auto &cmdline = get_cmdline_targets();
@@ -1573,7 +1573,9 @@ static uint32_t sysimg_init_cb(const void *id)
15731573
t.name = nname;
15741574
}
15751575
}
1576-
auto match = match_sysimg_targets(sysimg, target, max_vector_size);
1576+
auto match = match_sysimg_targets(sysimg, target, max_vector_size, rejection_reason);
1577+
if (match.best_idx == -1)
1578+
return match.best_idx;
15771579
// Now we've decided on which sysimg version to use.
15781580
// Make sure the JIT target is compatible with it and save the JIT target.
15791581
if (match.vreg_size != max_vector_size(target.en.features) &&
@@ -1586,7 +1588,7 @@ static uint32_t sysimg_init_cb(const void *id)
15861588
return match.best_idx;
15871589
}
15881590

1589-
static uint32_t pkgimg_init_cb(const void *id)
1591+
static uint32_t pkgimg_init_cb(const void *id, jl_value_t **rejection_reason JL_REQUIRE_ROOTED_SLOT)
15901592
{
15911593
TargetData<feature_sz> target = jit_targets.front();
15921594
auto pkgimg = deserialize_target_data<feature_sz>((const uint8_t*)id);
@@ -1595,8 +1597,7 @@ static uint32_t pkgimg_init_cb(const void *id)
15951597
t.name = nname;
15961598
}
15971599
}
1598-
auto match = match_sysimg_targets(pkgimg, target, max_vector_size);
1599-
1600+
auto match = match_sysimg_targets(pkgimg, target, max_vector_size, rejection_reason);
16001601
return match.best_idx;
16011602
}
16021603

@@ -1823,9 +1824,15 @@ jl_image_t jl_init_processor_pkgimg(void *hdl)
18231824
return parse_sysimg(hdl, pkgimg_init_cb);
18241825
}
18251826

1826-
JL_DLLEXPORT void jl_check_pkgimage_clones(char *data)
1827+
JL_DLLEXPORT jl_value_t* jl_check_pkgimage_clones(char *data)
18271828
{
1828-
pkgimg_init_cb(data);
1829+
jl_value_t *rejection_reason = NULL;
1830+
JL_GC_PUSH1(&rejection_reason);
1831+
uint32_t match_idx = pkgimg_init_cb(data, &rejection_reason);
1832+
JL_GC_POP();
1833+
if (match_idx == (uint32_t)-1)
1834+
return rejection_reason;
1835+
return jl_nothing;
18291836
}
18301837

18311838
std::pair<std::string,std::vector<std::string>> jl_get_llvm_target(bool imaging, uint32_t &flags)

0 commit comments

Comments
 (0)