LCOV - code coverage report
Current view: top level - src - processor.cpp (source / functions) Hit Total Coverage
Test: [build process] commit ef510b1f346f4c9f9d86eaceace5ca54961a1dbc Lines: 213 385 55.3 %
Date: 2022-07-17 01:01:28 Functions: 50 58 86.2 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 73 226 32.3 %

           Branch data     Line data    Source code
       1                 :            : // This file is a part of Julia. License is MIT: https://julialang.org/license
       2                 :            : 
       3                 :            : // Processor feature detection
       4                 :            : 
       5                 :            : #include "llvm-version.h"
       6                 :            : #include <llvm/ADT/StringRef.h>
       7                 :            : #include <llvm/Support/MathExtras.h>
       8                 :            : #include <llvm/Support/raw_ostream.h>
       9                 :            : 
      10                 :            : #include "processor.h"
      11                 :            : 
      12                 :            : #include "julia.h"
      13                 :            : #include "julia_internal.h"
      14                 :            : 
      15                 :            : #include <map>
      16                 :            : #include <algorithm>
      17                 :            : 
      18                 :            : #include "julia_assert.h"
      19                 :            : 
      20                 :            : // CPU target string is a list of strings separated by `;` each string starts with a CPU
      21                 :            : // or architecture name and followed by an optional list of features separated by `,`.
      22                 :            : // A "generic" or empty CPU name means the basic required feature set of the target ISA
      23                 :            : // which is at least the architecture the C/C++ runtime is compiled with.
      24                 :            : 
      25                 :            : // CPU dispatch needs to determine the version to be used by the sysimg as well as
      26                 :            : // the target and feature used by the JIT. Currently the only limitation on JIT target
      27                 :            : // and feature is matching register size between the sysimg and JIT so that SIMD vectors
      28                 :            : // can be passed correctly. This means disabling AVX and AVX2 if AVX was not enabled
      29                 :            : // in sysimg and disabling AVX512 if it was not enabled in sysimg.
      30                 :            : // This also possibly means that SVE needs to be disabled on AArch64 if sysimg doesn't have it
      31                 :            : // enabled.
      32                 :            : 
      33                 :            : // CPU dispatch starts by first deciding the max feature set and CPU requested for JIT.
      34                 :            : // This is the host or the target specified on the command line with features unavailable
      35                 :            : // on the host disabled. All sysimg targets that require features not available in this set
      36                 :            : // will be ignored.
      37                 :            : 
      38                 :            : // The next step is matching CPU name.
      39                 :            : // If exact name match with compatible feature set exists, all versions without name match
      40                 :            : // are ignored.
      41                 :            : // This step will query LLVM first so it can accept CPU names that is recognized by LLVM but
      42                 :            : // not by us (yet) when LLVM is enabled.
      43                 :            : 
      44                 :            : // If there are still more than one candidates, a feature match is performed.
      45                 :            : // The ones with the largest register size will be used
      46                 :            : // (i.e. AVX512 > AVX2/AVX > SSE, SVE > ASIMD). If there's a tie, the one with the most features
      47                 :            : // enabled will be used. If there's still a tie the one that appears later in the list will be
      48                 :            : // used. (i.e. the order in the version list is significant in this case).
      49                 :            : 
      50                 :            : // Features that are not recognized will be passed to LLVM directly during codegen
      51                 :            : // but ignored otherwise.
      52                 :            : 
      53                 :            : // A few special features are supported:
      54                 :            : // 1. `clone_all`
      55                 :            : //
      56                 :            : //     This forces the target to have all functions in sysimg cloned.
      57                 :            : //     When used in negative form (i.e. `-clone_all`), this disables full clone that's
      58                 :            : //     enabled by default for certain targets.
      59                 :            : //
      60                 :            : // 2. `base([0-9]*)`
      61                 :            : //
      62                 :            : //     This specifies the (0-based) base target index. The base target is the target
      63                 :            : //     that the current target is based on, i.e. the functions that are not being cloned
      64                 :            : //     will use the version in the base target. This option causes the base target to be
      65                 :            : //     fully cloned (as if `clone_all` is specified for it) if it is not the default target (0).
      66                 :            : //     The index can only be smaller than the current index.
      67                 :            : //
      68                 :            : // 3. `opt_size`
      69                 :            : //
      70                 :            : //     Optimize for size with minimum performance impact. Clang/GCC's `-Os`.
      71                 :            : //
      72                 :            : // 4. `min_size`
      73                 :            : //
      74                 :            : //     Optimize only for size. Clang's `-Oz`.
      75                 :            : 
      76                 :            : JL_DLLEXPORT bool jl_processor_print_help = false;
      77                 :            : 
      78                 :            : namespace {
      79                 :            : 
      80                 :            : // Helper functions to test/set feature bits
      81                 :            : 
      82                 :            : template<typename T1, typename T2, typename T3>
      83                 :         75 : static inline bool test_bits(T1 v, T2 mask, T3 test)
      84                 :            : {
      85                 :         75 :     return T3(v & mask) == test;
      86                 :            : }
      87                 :            : 
      88                 :            : template<typename T1, typename T2>
      89                 :         75 : static inline bool test_all_bits(T1 v, T2 mask)
      90                 :            : {
      91                 :         75 :     return test_bits(v, mask, mask);
      92                 :            : }
      93                 :            : 
      94                 :            : template<typename T1, typename T2>
      95                 :       3541 : static inline bool test_nbit(const T1 &bits, T2 _bitidx)
      96                 :            : {
      97                 :       3541 :     auto bitidx = static_cast<uint32_t>(_bitidx);
      98                 :       3541 :     auto u32idx = bitidx / 32;
      99                 :       3541 :     auto bit = bitidx % 32;
     100                 :       3541 :     return (bits[u32idx] & (1 << bit)) != 0;
     101                 :            : }
     102                 :            : 
     103                 :            : template<typename T>
     104                 :         60 : static inline void unset_bits(T &bits)
     105                 :            : {
     106                 :            :     (void)bits;
     107                 :         60 : }
     108                 :            : 
     109                 :            : template<typename T, typename T1, typename... Rest>
     110                 :        345 : static inline void unset_bits(T &bits, T1 _bitidx, Rest... rest)
     111                 :            : {
     112                 :        345 :     auto bitidx = static_cast<uint32_t>(_bitidx);
     113                 :        345 :     auto u32idx = bitidx / 32;
     114                 :        345 :     auto bit = bitidx % 32;
     115                 :        345 :     bits[u32idx] = bits[u32idx] & ~uint32_t(1 << bit);
     116                 :        345 :     unset_bits(bits, rest...);
     117                 :        345 : }
     118                 :            : 
     119                 :            : template<typename T, typename T1>
     120                 :          0 : static inline void set_bit(T &bits, T1 _bitidx, bool val)
     121                 :            : {
     122                 :          0 :     auto bitidx = static_cast<uint32_t>(_bitidx);
     123                 :          0 :     auto u32idx = bitidx / 32;
     124                 :          0 :     auto bit = bitidx % 32;
     125         [ #  # ]:          0 :     if (val) {
     126                 :          0 :         bits[u32idx] = bits[u32idx] | uint32_t(1 << bit);
     127                 :            :     }
     128                 :            :     else {
     129                 :          0 :         bits[u32idx] = bits[u32idx] & ~uint32_t(1 << bit);
     130                 :            :     }
     131                 :          0 : }
     132                 :            : 
     133                 :            : // Helper functions to create feature masks
     134                 :            : 
     135                 :            : // This can be `std::array<uint32_t,n>` on C++14
     136                 :            : template<size_t n>
     137                 :            : struct FeatureList {
     138                 :            :     uint32_t eles[n];
     139                 :       1939 :     uint32_t &operator[](size_t pos)
     140                 :            :     {
     141                 :       1939 :         return eles[pos];
     142                 :            :     }
     143                 :       4249 :     constexpr const uint32_t &operator[](size_t pos) const
     144                 :            :     {
     145                 :       4249 :         return eles[pos];
     146                 :            :     }
     147                 :          2 :     inline int nbits() const
     148                 :            :     {
     149                 :          2 :         int cnt = 0;
     150         [ +  + ]:         24 :         for (size_t i = 0; i < n; i++)
     151                 :         22 :             cnt += llvm::countPopulation(eles[i]);
     152                 :          2 :         return cnt;
     153                 :            :     }
     154                 :          2 :     inline bool empty() const
     155                 :            :     {
     156         [ +  + ]:         24 :         for (size_t i = 0; i < n; i++) {
     157         [ -  + ]:         22 :             if (eles[i]) {
     158                 :          0 :                 return false;
     159                 :            :             }
     160                 :            :         }
     161                 :          2 :         return true;
     162                 :            :     }
     163                 :            : };
     164                 :            : 
     165                 :            : static inline constexpr uint32_t add_feature_mask_u32(uint32_t mask, uint32_t u32idx)
     166                 :            : {
     167                 :            :     return mask;
     168                 :            : }
     169                 :            : 
     170                 :            : template<typename T, typename... Rest>
     171                 :            : static inline constexpr uint32_t add_feature_mask_u32(uint32_t mask, uint32_t u32idx,
     172                 :            :                                                       T bit, Rest... args)
     173                 :            : {
     174                 :            :     return add_feature_mask_u32(mask | ((int(bit) >= 0 && int(bit) / 32 == (int)u32idx) ?
     175                 :            :                                         (1 << (int(bit) % 32)) : 0),
     176                 :            :                                 u32idx, args...);
     177                 :            : }
     178                 :            : 
     179                 :            : template<typename... Args>
     180                 :            : static inline constexpr uint32_t get_feature_mask_u32(uint32_t u32idx, Args... args)
     181                 :            : {
     182                 :            :     return add_feature_mask_u32(uint32_t(0), u32idx, args...);
     183                 :            : }
     184                 :            : 
     185                 :            : template<uint32_t... Is> struct seq{};
     186                 :            : template<uint32_t N, uint32_t... Is>
     187                 :            : struct gen_seq : gen_seq<N-1, N-1, Is...>{};
     188                 :            : template<uint32_t... Is>
     189                 :            : struct gen_seq<0, Is...> : seq<Is...>{};
     190                 :            : 
     191                 :            : template<size_t n, uint32_t... I, typename... Args>
     192                 :            : static inline constexpr FeatureList<n>
     193                 :            : _get_feature_mask(seq<I...>, Args... args)
     194                 :            : {
     195                 :            :     return FeatureList<n>{{get_feature_mask_u32(I, args...)...}};
     196                 :            : }
     197                 :            : 
     198                 :            : template<size_t n, typename... Args>
     199                 :            : static inline constexpr FeatureList<n> get_feature_masks(Args... args)
     200                 :            : {
     201                 :            :     return _get_feature_mask<n>(gen_seq<n>(), args...);
     202                 :            : }
     203                 :            : 
     204                 :            : template<size_t n, uint32_t... I>
     205                 :            : static inline constexpr FeatureList<n>
     206                 :            : _feature_mask_or(seq<I...>, const FeatureList<n> &a, const FeatureList<n> &b)
     207                 :            : {
     208                 :            :     return FeatureList<n>{{(a[I] | b[I])...}};
     209                 :            : }
     210                 :            : 
     211                 :            : template<size_t n>
     212                 :            : static inline constexpr FeatureList<n> operator|(const FeatureList<n> &a, const FeatureList<n> &b)
     213                 :            : {
     214                 :            :     return _feature_mask_or<n>(gen_seq<n>(), a, b);
     215                 :            : }
     216                 :            : 
     217                 :            : template<size_t n, uint32_t... I>
     218                 :            : static inline constexpr FeatureList<n>
     219                 :          2 : _feature_mask_and(seq<I...>, const FeatureList<n> &a, const FeatureList<n> &b)
     220                 :            : {
     221                 :          2 :     return FeatureList<n>{{(a[I] & b[I])...}};
     222                 :            : }
     223                 :            : 
     224                 :            : template<size_t n>
     225                 :          2 : static inline constexpr FeatureList<n> operator&(const FeatureList<n> &a, const FeatureList<n> &b)
     226                 :            : {
     227                 :          2 :     return _feature_mask_and<n>(gen_seq<n>(), a, b);
     228                 :            : }
     229                 :            : 
     230                 :            : template<size_t n, uint32_t... I>
     231                 :            : static inline constexpr FeatureList<n>
     232                 :            : _feature_mask_not(seq<I...>, const FeatureList<n> &a)
     233                 :            : {
     234                 :            :     return FeatureList<n>{{(~a[I])...}};
     235                 :            : }
     236                 :            : 
     237                 :            : template<size_t n>
     238                 :            : static inline constexpr FeatureList<n> operator~(const FeatureList<n> &a)
     239                 :            : {
     240                 :            :     return _feature_mask_not<n>(gen_seq<n>(), a);
     241                 :            : }
     242                 :            : 
     243                 :            : template<size_t n>
     244                 :         15 : static inline void mask_features(const FeatureList<n> masks, uint32_t *features)
     245                 :            : {
     246         [ +  + ]:        180 :     for (size_t i = 0; i < n; i++) {
     247                 :        165 :         features[i] = features[i] & masks[i];
     248                 :            :     }
     249                 :         15 : }
     250                 :            : 
     251                 :            : // Turn feature list to a string the LLVM accept
     252                 :          2 : static inline std::string join_feature_strs(const std::vector<std::string> &strs)
     253                 :            : {
     254                 :          2 :     size_t nstr = strs.size();
     255         [ -  + ]:          2 :     if (!nstr)
     256                 :          0 :         return std::string("");
     257                 :          4 :     std::string str = strs[0];
     258         [ +  + ]:        156 :     for (size_t i = 1; i < nstr; i++)
     259                 :        154 :         str += ',' + strs[i];
     260                 :          2 :     return str;
     261                 :            : }
     262                 :            : 
     263                 :          2 : static inline void append_ext_features(std::string &features, const std::string &ext_features)
     264                 :            : {
     265         [ +  - ]:          2 :     if (ext_features.empty())
     266                 :          2 :         return;
     267         [ #  # ]:          0 :     if (!features.empty())
     268                 :          0 :         features.push_back(',');
     269                 :          0 :     features.append(ext_features);
     270                 :            : }
     271                 :            : 
     272                 :         15 : static inline void append_ext_features(std::vector<std::string> &features,
     273                 :            :                                        const std::string &ext_features)
     274                 :            : {
     275         [ +  - ]:         15 :     if (ext_features.empty())
     276                 :         15 :         return;
     277                 :          0 :     const char *start = ext_features.c_str();
     278                 :          0 :     const char *p = start;
     279         [ #  # ]:          0 :     for (; *p; p++) {
     280         [ #  # ]:          0 :         if (*p == ',') {
     281                 :          0 :             features.emplace_back(start, p - start);
     282                 :          0 :             start = p + 1;
     283                 :            :         }
     284                 :            :     }
     285         [ #  # ]:          0 :     if (p > start) {
     286                 :          0 :         features.emplace_back(start, p - start);
     287                 :            :     }
     288                 :            : }
     289                 :            : 
     290                 :            : /**
     291                 :            :  * Target specific type/constant definitions, always enable.
     292                 :            :  */
     293                 :            : 
     294                 :            : struct FeatureName {
     295                 :            :     const char *name;
     296                 :            :     uint32_t bit; // bit index into a `uint32_t` array;
     297                 :            :     uint32_t llvmver; // 0 if it is available on the oldest LLVM version we support
     298                 :            : };
     299                 :            : 
     300                 :            : template<typename CPU, size_t n>
     301                 :            : struct CPUSpec {
     302                 :            :     const char *name;
     303                 :            :     CPU cpu;
     304                 :            :     CPU fallback;
     305                 :            :     uint32_t llvmver;
     306                 :            :     FeatureList<n> features;
     307                 :            : };
     308                 :            : 
     309                 :            : struct FeatureDep {
     310                 :            :     uint32_t feature;
     311                 :            :     uint32_t dep;
     312                 :            : };
     313                 :            : 
     314                 :            : // Recursively enable all features that the current feature set depends on.
     315                 :            : template<size_t n>
     316                 :         15 : static inline void enable_depends(FeatureList<n> &features, const FeatureDep *deps, size_t ndeps)
     317                 :            : {
     318                 :         15 :     bool changed = true;
     319         [ +  + ]:         30 :     while (changed) {
     320                 :         15 :         changed = false;
     321         [ +  + ]:        555 :         for (ssize_t i = ndeps - 1; i >= 0; i--) {
     322                 :        540 :             auto &dep = deps[i];
     323   [ +  +  +  -  :        540 :             if (!test_nbit(features, dep.feature) || test_nbit(features, dep.dep))
                   +  - ]
     324                 :        540 :                 continue;
     325                 :          0 :             set_bit(features, dep.dep, true);
     326                 :          0 :             changed = true;
     327                 :            :         }
     328                 :            :     }
     329                 :         15 : }
     330                 :            : 
     331                 :            : // Recursively disable all features that the current feature set does not provide.
     332                 :            : template<size_t n>
     333                 :         17 : static inline void disable_depends(FeatureList<n> &features, const FeatureDep *deps, size_t ndeps)
     334                 :            : {
     335                 :         17 :     bool changed = true;
     336         [ +  + ]:         34 :     while (changed) {
     337                 :         17 :         changed = false;
     338         [ +  + ]:        629 :         for (ssize_t i = ndeps - 1; i >= 0; i--) {
     339                 :        612 :             auto &dep = deps[i];
     340   [ +  +  +  -  :        612 :             if (!test_nbit(features, dep.feature) || test_nbit(features, dep.dep))
                   +  - ]
     341                 :        612 :                 continue;
     342                 :          0 :             unset_bits(features, dep.feature);
     343                 :          0 :             changed = true;
     344                 :            :         }
     345                 :            :     }
     346                 :         17 : }
     347                 :            : 
     348                 :            : template<typename CPU, size_t n>
     349                 :         15 : static const CPUSpec<CPU,n> *find_cpu(uint32_t cpu, const CPUSpec<CPU,n> *cpus, uint32_t ncpus)
     350                 :            : {
     351         [ +  - ]:        270 :     for (uint32_t i = 0; i < ncpus; i++) {
     352         [ +  + ]:        270 :         if (cpu == uint32_t(cpus[i].cpu)) {
     353                 :         15 :             return &cpus[i];
     354                 :            :         }
     355                 :            :     }
     356                 :          0 :     return nullptr;
     357                 :            : }
     358                 :            : 
     359                 :            : template<typename CPU, size_t n>
     360                 :         17 : static const CPUSpec<CPU,n> *find_cpu(llvm::StringRef name, const CPUSpec<CPU,n> *cpus,
     361                 :            :                                       uint32_t ncpus)
     362                 :            : {
     363         [ +  - ]:        306 :     for (uint32_t i = 0; i < ncpus; i++) {
     364         [ +  + ]:        306 :         if (name == cpus[i].name) {
     365                 :         17 :             return &cpus[i];
     366                 :            :         }
     367                 :            :     }
     368                 :          0 :     return nullptr;
     369                 :            : }
     370                 :            : 
     371                 :            : template<typename CPU, size_t n>
     372                 :         15 : static const char *find_cpu_name(uint32_t cpu, const CPUSpec<CPU,n> *cpus, uint32_t ncpus)
     373                 :            : {
     374         [ +  - ]:         15 :     if (auto *spec = find_cpu(cpu, cpus, ncpus))
     375                 :         15 :         return spec->name;
     376                 :          0 :     return "generic";
     377                 :            : }
     378                 :            : 
     379                 :          0 : JL_UNUSED static uint32_t find_feature_bit(const FeatureName *features, size_t nfeatures,
     380                 :            :                                            const char *str, size_t len)
     381                 :            : {
     382         [ #  # ]:          0 :     for (size_t i = 0; i < nfeatures; i++) {
     383                 :          0 :         auto &feature = features[i];
     384   [ #  #  #  # ]:          0 :         if (strncmp(feature.name, str, len) == 0 && feature.name[len] == 0) {
     385                 :          0 :             return feature.bit;
     386                 :            :         }
     387                 :            :     }
     388                 :          0 :     return (uint32_t)-1;
     389                 :            : }
     390                 :            : 
     391                 :            : // This is how we save the target identification.
     392                 :            : // CPU name is saved as string instead of binary data like features because
     393                 :            : // 1. CPU ID is less stable (they are not bound to hardware/OS API)
     394                 :            : // 2. We need to support CPU names that are not recognized by us and therefore doesn't have an ID
     395                 :            : // 3. CPU name is trivial to parse
     396                 :          2 : static inline std::vector<uint8_t> serialize_target_data(llvm::StringRef name,
     397                 :            :                                                          uint32_t nfeature,
     398                 :            :                                                          const uint32_t *features_en,
     399                 :            :                                                          const uint32_t *features_dis,
     400                 :            :                                                          llvm::StringRef ext_features)
     401                 :            : {
     402                 :          2 :     std::vector<uint8_t> res;
     403                 :         14 :     auto add_data = [&] (const void *data, size_t sz) {
     404         [ +  + ]:         14 :         if (sz == 0)
     405                 :          2 :             return;
     406                 :         12 :         size_t old_sz = res.size();
     407                 :         12 :         res.resize(old_sz + sz);
     408                 :         12 :         memcpy(&res[old_sz], data, sz);
     409                 :          2 :     };
     410                 :          2 :     add_data(&nfeature, 4);
     411                 :          2 :     add_data(features_en, 4 * nfeature);
     412                 :          2 :     add_data(features_dis, 4 * nfeature);
     413                 :          2 :     uint32_t namelen = name.size();
     414                 :          2 :     add_data(&namelen, 4);
     415                 :          2 :     add_data(name.data(), namelen);
     416                 :          2 :     uint32_t ext_features_len = ext_features.size();
     417                 :          2 :     add_data(&ext_features_len, 4);
     418                 :          2 :     add_data(ext_features.data(), ext_features_len);
     419                 :          2 :     return res;
     420                 :            : }
     421                 :            : 
     422                 :            : template<size_t n>
     423                 :          2 : static inline std::vector<uint8_t> serialize_target_data(llvm::StringRef name,
     424                 :            :                                                          const FeatureList<n> &features_en,
     425                 :            :                                                          const FeatureList<n> &features_dis,
     426                 :            :                                                          llvm::StringRef ext_features)
     427                 :            : {
     428                 :          2 :     return serialize_target_data(name, n, &features_en[0], &features_dis[0], ext_features);
     429                 :            : }
     430                 :            : 
     431                 :            : template<size_t n>
     432                 :            : struct TargetData {
     433                 :            :     std::string name;
     434                 :            :     std::string ext_features;
     435                 :            :     struct {
     436                 :            :         FeatureList<n> features;
     437                 :            :         uint32_t flags;
     438                 :            :     } en, dis;
     439                 :            :     int base;
     440                 :            : };
     441                 :            : 
     442                 :            : // In addition to the serialized data, the first `uint32_t` gives the number of targets saved
     443                 :            : // and each target has a `uint32_t` flag before the serialized target data.
     444                 :            : template<size_t n>
     445                 :          2 : static inline std::vector<TargetData<n>> deserialize_target_data(const uint8_t *data)
     446                 :            : {
     447                 :         16 :     auto load_data = [&] (void *dest, size_t sz) {
     448                 :         14 :         memcpy(dest, data, sz);
     449                 :         14 :         data += sz;
     450                 :            :     };
     451                 :         14 :     auto load_string = [&] () {
     452                 :            :         uint32_t len;
     453                 :          4 :         load_data(&len, 4);
     454                 :          8 :         std::string res((const char*)data, len);
     455                 :          4 :         data += len;
     456                 :          4 :         return res;
     457                 :            :     };
     458                 :            :     uint32_t ntarget;
     459                 :          2 :     load_data(&ntarget, 4);
     460                 :          2 :     std::vector<TargetData<n>> res(ntarget);
     461         [ +  + ]:          4 :     for (uint32_t i = 0; i < ntarget; i++) {
     462                 :          2 :         auto &target = res[i];
     463                 :          2 :         load_data(&target.en.flags, 4);
     464                 :          2 :         target.dis.flags = 0;
     465                 :            :         // Starting serialized target data
     466                 :            :         uint32_t nfeature;
     467                 :          2 :         load_data(&nfeature, 4);
     468         [ -  + ]:          2 :         assert(nfeature == n);
     469                 :          2 :         load_data(&target.en.features[0], 4 * n);
     470                 :          2 :         load_data(&target.dis.features[0], 4 * n);
     471                 :          2 :         target.name = load_string();
     472                 :          2 :         target.ext_features = load_string();
     473                 :          2 :         target.base = 0;
     474                 :            :     }
     475                 :          2 :     return res;
     476                 :            : }
     477                 :            : 
     478                 :            : // Try getting clone base argument. Return 1-based index. Return 0 if match failed.
     479                 :          0 : static inline int get_clone_base(const char *start, const char *end)
     480                 :            : {
     481                 :          0 :     const char *prefix = "base(";
     482                 :          0 :     const int prefix_len = strlen(prefix);
     483         [ #  # ]:          0 :     if (end - start <= prefix_len)
     484                 :          0 :         return 0;
     485         [ #  # ]:          0 :     if (memcmp(start, prefix, prefix_len) != 0)
     486                 :          0 :         return 0;
     487                 :          0 :     start += prefix_len;
     488   [ #  #  #  # ]:          0 :     if (*start > '9' || *start < '0')
     489                 :          0 :         return 0;
     490                 :            :     char *digit_end;
     491                 :          0 :     auto idx = strtol(start, &digit_end, 10);
     492         [ #  # ]:          0 :     if (idx < 0)
     493                 :          0 :         return 0;
     494   [ #  #  #  # ]:          0 :     if (*digit_end != ')' || digit_end + 1 != end)
     495                 :          0 :         return 0;
     496                 :          0 :     return (int)idx + 1;
     497                 :            : }
     498                 :            : 
     499                 :            : // Parse cmdline string. This handles `clone_all` and `base` special features.
     500                 :            : // Other feature names will be passed to `feature_cb` for target dependent parsing.
     501                 :            : template<size_t n, typename F>
     502                 :            : static inline std::vector<TargetData<n>>
     503                 :         15 : parse_cmdline(const char *option, F &&feature_cb)
     504                 :            : {
     505         [ -  + ]:         15 :     if (!option)
     506                 :          0 :         option = "native";
     507                 :         15 :     std::vector<TargetData<n>> res;
     508                 :         30 :     TargetData<n> arg{};
     509                 :        105 :     auto reset_arg = [&] {
     510                 :         15 :         res.push_back(arg);
     511                 :         15 :         arg.name.clear();
     512                 :         15 :         arg.ext_features.clear();
     513                 :         15 :         memset(&arg.en.features[0], 0, 4 * n);
     514                 :         15 :         memset(&arg.dis.features[0], 0, 4 * n);
     515                 :         15 :         arg.en.flags = 0;
     516                 :         15 :         arg.dis.flags = 0;
     517                 :            :     };
     518                 :         15 :     const char *start = option;
     519                 :        105 :     for (const char *p = option; ; p++) {
     520         [ +  + ]:        105 :         switch (*p) {
     521                 :         15 :         case ',':
     522                 :            :         case ';':
     523                 :            :         case '\0': {
     524                 :         15 :             bool done = *p == '\0';
     525   [ +  -  +  - ]:         15 :             bool next_target = *p == ';' || done;
     526         [ +  - ]:         15 :             if (arg.name.empty()) {
     527         [ -  + ]:         15 :                 if (p == start)
     528                 :          0 :                     jl_error("Invalid target option: empty CPU name");
     529                 :         15 :                 arg.name.append(start, p - start);
     530         [ -  + ]:         15 :                 if (arg.name == "help") {
     531                 :          0 :                     arg.name = "native";
     532                 :          0 :                     jl_processor_print_help = true;
     533                 :            :                 }
     534                 :         15 :                 start = p + 1;
     535         [ +  - ]:         15 :                 if (next_target)
     536                 :         15 :                     reset_arg();
     537         [ +  - ]:         15 :                 if (done)
     538                 :         15 :                     return res;
     539                 :          0 :                 continue;
     540                 :            :             }
     541                 :          0 :             bool disable = false;
     542                 :          0 :             const char *full = start;
     543                 :          0 :             const char *fname = full;
     544                 :          0 :             start = p + 1;
     545         [ #  # ]:          0 :             if (*full == '-') {
     546                 :          0 :                 disable = true;
     547                 :          0 :                 fname++;
     548                 :            :             }
     549         [ #  # ]:          0 :             else if (*full == '+') {
     550                 :          0 :                 fname++;
     551                 :            :             }
     552         [ #  # ]:          0 :             if (llvm::StringRef(fname, p - fname) == "clone_all") {
     553         [ #  # ]:          0 :                 if (!disable) {
     554                 :          0 :                     arg.en.flags |= JL_TARGET_CLONE_ALL;
     555                 :          0 :                     arg.dis.flags &= ~JL_TARGET_CLONE_ALL;
     556                 :            :                 }
     557                 :            :                 else {
     558                 :          0 :                     arg.dis.flags |= JL_TARGET_CLONE_ALL;
     559                 :          0 :                     arg.en.flags &= ~JL_TARGET_CLONE_ALL;
     560                 :            :                 }
     561                 :            :             }
     562         [ #  # ]:          0 :             else if (llvm::StringRef(fname, p - fname) == "opt_size") {
     563         [ #  # ]:          0 :                 if (disable)
     564                 :          0 :                     jl_error("Invalid target option: disabled opt_size.");
     565         [ #  # ]:          0 :                 if (arg.en.flags & JL_TARGET_MINSIZE)
     566                 :          0 :                     jl_error("Conflicting target option: both opt_size and min_size are specified.");
     567                 :          0 :                 arg.en.flags |= JL_TARGET_OPTSIZE;
     568                 :            :             }
     569         [ #  # ]:          0 :             else if (llvm::StringRef(fname, p - fname) == "min_size") {
     570         [ #  # ]:          0 :                 if (disable)
     571                 :          0 :                     jl_error("Invalid target option: disabled min_size.");
     572         [ #  # ]:          0 :                 if (arg.en.flags & JL_TARGET_OPTSIZE)
     573                 :          0 :                     jl_error("Conflicting target option: both opt_size and min_size are specified.");
     574                 :          0 :                 arg.en.flags |= JL_TARGET_MINSIZE;
     575                 :            :             }
     576         [ #  # ]:          0 :             else if (int base = get_clone_base(fname, p)) {
     577         [ #  # ]:          0 :                 if (disable)
     578                 :          0 :                     jl_error("Invalid target option: disabled base index.");
     579                 :          0 :                 base -= 1;
     580         [ #  # ]:          0 :                 if (base >= (int)res.size())
     581                 :          0 :                     jl_error("Invalid target option: base index must refer to a previous target.");
     582   [ #  #  #  # ]:          0 :                 if (res[base].dis.flags & JL_TARGET_CLONE_ALL ||
     583         [ #  # ]:          0 :                     !(res[base].en.flags & JL_TARGET_CLONE_ALL))
     584                 :          0 :                     jl_error("Invalid target option: base target must be clone_all.");
     585                 :          0 :                 arg.base = base;
     586                 :            :             }
     587         [ #  # ]:          0 :             else if (llvm::StringRef(fname, p - fname) == "help") {
     588                 :          0 :                 jl_processor_print_help = true;
     589                 :            :             }
     590                 :            :             else {
     591         [ #  # ]:          0 :                 FeatureList<n> &list = disable ? arg.dis.features : arg.en.features;
     592         [ #  # ]:          0 :                 if (!feature_cb(fname, p - fname, list)) {
     593         [ #  # ]:          0 :                     if (!arg.ext_features.empty())
     594                 :          0 :                         arg.ext_features += ',';
     595         [ #  # ]:          0 :                     arg.ext_features += disable ? '-' : '+';
     596                 :          0 :                     arg.ext_features.append(fname, p - fname);
     597                 :            :                 }
     598                 :            :             }
     599         [ #  # ]:          0 :             if (next_target)
     600                 :          0 :                 reset_arg();
     601         [ #  # ]:          0 :             if (done) {
     602                 :          0 :                 return res;
     603                 :            :             }
     604                 :            :         }
     605                 :            :             JL_FALLTHROUGH;
     606                 :            :         default:
     607                 :         90 :             continue;
     608                 :            :         }
     609                 :            :     }
     610                 :            : }
     611                 :            : 
     612                 :            : // Cached version of command line parsing
     613                 :            : template<size_t n, typename F>
     614                 :         17 : static inline std::vector<TargetData<n>> &get_cmdline_targets(F &&feature_cb)
     615                 :            : {
     616   [ +  +  +  - ]:         17 :     static std::vector<TargetData<n>> targets =
     617                 :            :         parse_cmdline<n>(jl_options.cpu_target, std::forward<F>(feature_cb));
     618                 :         17 :     return targets;
     619                 :            : }
     620                 :            : 
     621                 :            : // Load sysimg, use the `callback` for dispatch and perform all relocations
     622                 :            : // for the selected target.
     623                 :            : template<typename F>
     624                 :          2 : static inline jl_sysimg_fptrs_t parse_sysimg(void *hdl, F &&callback)
     625                 :            : {
     626                 :          2 :     jl_sysimg_fptrs_t res = {nullptr, 0, nullptr, 0, nullptr, nullptr};
     627                 :            : 
     628                 :            :     // .data base
     629                 :            :     char *data_base;
     630                 :          2 :     jl_dlsym(hdl, "jl_sysimg_gvars_base", (void**)&data_base, 1);
     631                 :            :     // .text base
     632                 :            :     char *text_base;
     633                 :          2 :     jl_dlsym(hdl, "jl_sysimg_fvars_base", (void**)&text_base, 1);
     634                 :          2 :     res.base = text_base;
     635                 :            : 
     636                 :            :     int32_t *offsets;
     637                 :          2 :     jl_dlsym(hdl, "jl_sysimg_fvars_offsets", (void**)&offsets, 1);
     638                 :          2 :     uint32_t nfunc = offsets[0];
     639                 :          2 :     res.offsets = offsets + 1;
     640                 :            : 
     641                 :            :     void *ids;
     642                 :          2 :     jl_dlsym(hdl, "jl_dispatch_target_ids", &ids, 1);
     643                 :          2 :     uint32_t target_idx = callback(ids);
     644                 :            : 
     645                 :            :     int32_t *reloc_slots;
     646                 :          2 :     jl_dlsym(hdl, "jl_dispatch_reloc_slots", (void **)&reloc_slots, 1);
     647                 :          2 :     const uint32_t nreloc = reloc_slots[0];
     648                 :          2 :     reloc_slots += 1;
     649                 :            :     uint32_t *clone_idxs;
     650                 :            :     int32_t *clone_offsets;
     651                 :          2 :     jl_dlsym(hdl, "jl_dispatch_fvars_idxs", (void**)&clone_idxs, 1);
     652                 :          2 :     jl_dlsym(hdl, "jl_dispatch_fvars_offsets", (void**)&clone_offsets, 1);
     653                 :          2 :     uint32_t tag_len = clone_idxs[0];
     654                 :          2 :     clone_idxs += 1;
     655                 :            : 
     656         [ -  + ]:          2 :     assert(tag_len & jl_sysimg_tag_mask);
     657                 :          4 :     std::vector<const int32_t*> base_offsets = {res.offsets};
     658                 :            :     // Find target
     659         [ -  + ]:          2 :     for (uint32_t i = 0;i < target_idx;i++) {
     660                 :          0 :         uint32_t len = jl_sysimg_val_mask & tag_len;
     661         [ #  # ]:          0 :         if (jl_sysimg_tag_mask & tag_len) {
     662         [ #  # ]:          0 :             if (i != 0)
     663                 :          0 :                 clone_offsets += nfunc;
     664                 :          0 :             clone_idxs += len + 1;
     665                 :            :         }
     666                 :            :         else {
     667                 :          0 :             clone_offsets += len;
     668                 :          0 :             clone_idxs += len + 2;
     669                 :            :         }
     670                 :          0 :         tag_len = clone_idxs[-1];
     671         [ #  # ]:          0 :         base_offsets.push_back(tag_len & jl_sysimg_tag_mask ? clone_offsets : nullptr);
     672                 :            :     }
     673                 :            : 
     674                 :          2 :     bool clone_all = (tag_len & jl_sysimg_tag_mask) != 0;
     675                 :            :     // Fill in return value
     676         [ +  - ]:          2 :     if (clone_all) {
     677                 :            :         // clone_all
     678         [ -  + ]:          2 :         if (target_idx != 0) {
     679                 :          0 :             res.offsets = clone_offsets;
     680                 :            :         }
     681                 :            :     }
     682                 :            :     else {
     683                 :          0 :         uint32_t base_idx = clone_idxs[0];
     684         [ #  # ]:          0 :         assert(base_idx < target_idx);
     685         [ #  # ]:          0 :         if (target_idx != 0) {
     686                 :          0 :             res.offsets = base_offsets[base_idx];
     687         [ #  # ]:          0 :             assert(res.offsets);
     688                 :            :         }
     689                 :          0 :         clone_idxs++;
     690                 :          0 :         res.nclones = tag_len;
     691                 :          0 :         res.clone_offsets = clone_offsets;
     692                 :          0 :         res.clone_idxs = clone_idxs;
     693                 :            :     }
     694                 :            :     // Do relocation
     695                 :          2 :     uint32_t reloc_i = 0;
     696                 :          2 :     uint32_t len = jl_sysimg_val_mask & tag_len;
     697         [ -  + ]:          2 :     for (uint32_t i = 0; i < len; i++) {
     698                 :          0 :         uint32_t idx = clone_idxs[i];
     699                 :            :         int32_t offset;
     700         [ #  # ]:          0 :         if (clone_all) {
     701                 :          0 :             offset = res.offsets[idx];
     702                 :            :         }
     703         [ #  # ]:          0 :         else if (idx & jl_sysimg_tag_mask) {
     704                 :          0 :             idx = idx & jl_sysimg_val_mask;
     705                 :          0 :             offset = clone_offsets[i];
     706                 :            :         }
     707                 :            :         else {
     708                 :          0 :             continue;
     709                 :            :         }
     710                 :          0 :         bool found = false;
     711         [ #  # ]:          0 :         for (; reloc_i < nreloc; reloc_i++) {
     712                 :          0 :             auto reloc_idx = ((const uint32_t*)reloc_slots)[reloc_i * 2];
     713         [ #  # ]:          0 :             if (reloc_idx == idx) {
     714                 :          0 :                 found = true;
     715                 :          0 :                 auto slot = (const void**)(data_base + reloc_slots[reloc_i * 2 + 1]);
     716                 :          0 :                 *slot = offset + res.base;
     717                 :            :             }
     718         [ #  # ]:          0 :             else if (reloc_idx > idx) {
     719                 :          0 :                 break;
     720                 :            :             }
     721                 :            :         }
     722         [ #  # ]:          0 :         assert(found && "Cannot find GOT entry for cloned function.");
     723                 :            :         (void)found;
     724                 :            :     }
     725                 :            : 
     726                 :          2 :     return res;
     727                 :            : }
     728                 :            : 
     729                 :            : template<typename T>
     730                 :         15 : static inline void check_cmdline(T &&cmdline, bool imaging)
     731                 :            : {
     732         [ -  + ]:         15 :     assert(cmdline.size() > 0);
     733                 :            :     // It's unclear what does specifying multiple target when not generating
     734                 :            :     // sysimg means. Make it an error for now.
     735         [ +  + ]:         15 :     if (!imaging) {
     736         [ -  + ]:         10 :         if (cmdline.size() > 1) {
     737                 :          0 :             jl_error("More than one command line CPU targets specified "
     738                 :            :                      "without a `--output-` flag specified");
     739                 :            :         }
     740         [ -  + ]:         10 :         if (cmdline[0].en.flags & JL_TARGET_CLONE_ALL) {
     741                 :          0 :             jl_error("\"clone_all\" feature specified "
     742                 :            :                      "without a `--output-` flag specified");
     743                 :            :         }
     744         [ -  + ]:         10 :         if (cmdline[0].en.flags & JL_TARGET_OPTSIZE) {
     745                 :          0 :             jl_error("\"opt_size\" feature specified "
     746                 :            :                      "without a `--output-` flag specified");
     747                 :            :         }
     748         [ -  + ]:         10 :         if (cmdline[0].en.flags & JL_TARGET_MINSIZE) {
     749                 :          0 :             jl_error("\"min_size\" feature specified "
     750                 :            :                      "without a `--output-` flag specified");
     751                 :            :         }
     752                 :            :     }
     753                 :         15 : }
     754                 :            : 
     755                 :            : struct SysimgMatch {
     756                 :            :     uint32_t best_idx{(uint32_t)-1};
     757                 :            :     int vreg_size{0};
     758                 :            : };
     759                 :            : 
     760                 :            : // Find the best match in the sysimg.
     761                 :            : // Select the best one based on the largest vector register and largest compatible feature set.
     762                 :            : template<typename S, typename T, typename F>
     763                 :          2 : static inline SysimgMatch match_sysimg_targets(S &&sysimg, T &&target, F &&max_vector_size)
     764                 :            : {
     765                 :          2 :     SysimgMatch match;
     766                 :          2 :     bool match_name = false;
     767                 :          2 :     int feature_size = 0;
     768         [ +  + ]:          4 :     for (uint32_t i = 0; i < sysimg.size(); i++) {
     769                 :          2 :         auto &imgt = sysimg[i];
     770         [ -  + ]:          2 :         if (!(imgt.en.features & target.dis.features).empty()) {
     771                 :            :             // Check sysimg enabled features against runtime disabled features
     772                 :            :             // This is valid (and all what we can do)
     773                 :            :             // even if one or both of the targets are unknown.
     774                 :          0 :             continue;
     775                 :            :         }
     776         [ +  - ]:          2 :         if (imgt.name == target.name) {
     777         [ +  - ]:          2 :             if (!match_name) {
     778                 :          2 :                 match_name = true;
     779                 :          2 :                 match.vreg_size = 0;
     780                 :          2 :                 feature_size = 0;
     781                 :            :             }
     782                 :            :         }
     783         [ #  # ]:          0 :         else if (match_name) {
     784                 :          0 :             continue;
     785                 :            :         }
     786                 :          2 :         int new_vsz = max_vector_size(imgt.en.features);
     787         [ -  + ]:          2 :         if (match.vreg_size > new_vsz)
     788                 :          0 :             continue;
     789                 :          2 :         int new_feature_size = imgt.en.features.nbits();
     790         [ +  - ]:          2 :         if (match.vreg_size < new_vsz) {
     791                 :          2 :             match.best_idx = i;
     792                 :          2 :             match.vreg_size = new_vsz;
     793                 :          2 :             feature_size = new_feature_size;
     794                 :          2 :             continue;
     795                 :            :         }
     796         [ #  # ]:          0 :         if (new_feature_size < feature_size)
     797                 :          0 :             continue;
     798                 :          0 :         match.best_idx = i;
     799                 :          0 :         feature_size = new_feature_size;
     800                 :            :     }
     801         [ -  + ]:          2 :     if (match.best_idx == (uint32_t)-1)
     802                 :          0 :         jl_error("Unable to find compatible target in system image.");
     803                 :          2 :     return match;
     804                 :            : }
     805                 :            : 
     806                 :            : // Debug helper
     807                 :            : 
     808                 :            : template<typename CPU, size_t n>
     809                 :          0 : static inline void dump_cpu_spec(uint32_t cpu, const FeatureList<n> &features,
     810                 :            :                                  const FeatureName *feature_names, uint32_t nfeature_names,
     811                 :            :                                  const CPUSpec<CPU,n> *cpus, uint32_t ncpus)
     812                 :            : {
     813                 :          0 :     bool cpu_found = false;
     814         [ #  # ]:          0 :     for (uint32_t i = 0;i < ncpus;i++) {
     815         [ #  # ]:          0 :         if (cpu == uint32_t(cpus[i].cpu)) {
     816                 :          0 :             cpu_found = true;
     817                 :          0 :             jl_safe_printf("CPU: %s\n", cpus[i].name);
     818                 :          0 :             break;
     819                 :            :         }
     820                 :            :     }
     821         [ #  # ]:          0 :     if (!cpu_found)
     822                 :          0 :         jl_safe_printf("CPU: generic\n");
     823                 :          0 :     jl_safe_printf("Features:");
     824                 :          0 :     bool first = true;
     825         [ #  # ]:          0 :     for (uint32_t i = 0;i < nfeature_names;i++) {
     826         [ #  # ]:          0 :         if (test_nbit(&features[0], feature_names[i].bit)) {
     827         [ #  # ]:          0 :             if (first) {
     828                 :          0 :                 jl_safe_printf(" %s", feature_names[i].name);
     829                 :          0 :                 first = false;
     830                 :            :             }
     831                 :            :             else {
     832                 :          0 :                 jl_safe_printf(", %s", feature_names[i].name);
     833                 :            :             }
     834                 :            :         }
     835                 :            :     }
     836                 :          0 :     jl_safe_printf("\n");
     837                 :          0 : }
     838                 :            : 
     839                 :            : }
     840                 :            : 
     841                 :            : #if defined(_CPU_X86_) || defined(_CPU_X86_64_)
     842                 :            : 
     843                 :            : #include "processor_x86.cpp"
     844                 :            : 
     845                 :            : #elif defined(_CPU_AARCH64_) || defined(_CPU_ARM_)
     846                 :            : 
     847                 :            : #include "processor_arm.cpp"
     848                 :            : 
     849                 :            : #else
     850                 :            : 
     851                 :            : #include "processor_fallback.cpp"
     852                 :            : 
     853                 :            : #endif

Generated by: LCOV version 1.14