|
1 | 1 | #!/usr/bin/env python3 |
2 | 2 |
|
3 | | -"""Auto-generate implications between -mfpu options for multilib.yaml.in. |
| 3 | +"""Auto-generate implications between command-line options options for |
| 4 | +multilib.yaml.in. |
4 | 5 |
|
5 | 6 | Each FPU name that clang knows about is mapped to all the FPU names |
6 | 7 | that clang considers to be a subset of it (determined by extracting |
|
21 | 22 | d-registers, because the extra 16 d-registers are caller-saved, so |
22 | 23 | setjmp and exceptions need not preserve them. Interrupt handlers would |
23 | 24 | have to preserve them, but our libraries don't define any. |
| 25 | +
|
| 26 | +For architecture extension modifiers on the -march option, we expand these into |
| 27 | +options of the form -march=armvX+[no]feature, for each feature which is listed |
| 28 | +as enabled or disabled in the input options. Each of these new options has |
| 29 | +exactly one feature, so the multilib file can mark a library as depending on |
| 30 | +any set of by matching multiple options. The "armvX" architecture version isn't |
| 31 | +a valid option, but that doesn't matter to multilib, and means that we don't |
| 32 | +need to repeat the matching for every minor version. |
| 33 | +
|
| 34 | +For architecture versions, we expand -march=armvX.Y-a+features to include every |
| 35 | +lower or equal architecture version, so that if, for example, a library |
| 36 | +requires armv8.3-a, then a link command targeting any later version will be |
| 37 | +able to select it. These generated options don't include the feature modifiers, |
| 38 | +which can be matched separately if a library requires them. |
24 | 39 | """ |
25 | 40 |
|
26 | 41 | import argparse |
27 | 42 | import json |
28 | 43 | import os |
29 | 44 | import shlex |
30 | 45 | import subprocess |
| 46 | +from dataclasses import dataclass |
31 | 47 |
|
32 | 48 |
|
33 | 49 | def get_fpu_list(args): |
@@ -164,21 +180,7 @@ def get_target_features(args, fpu): |
164 | 180 | return features |
165 | 181 |
|
166 | 182 |
|
167 | | -def main(): |
168 | | - parser = argparse.ArgumentParser( |
169 | | - description=__doc__, |
170 | | - formatter_class=argparse.RawDescriptionHelpFormatter, |
171 | | - ) |
172 | | - parser.add_argument( |
173 | | - "--clang", required=True, help="Path to clang executable." |
174 | | - ) |
175 | | - parser.add_argument( |
176 | | - "--llvm-source", |
177 | | - required=True, |
178 | | - help="Path to root of llvm-project source tree.", |
179 | | - ) |
180 | | - args = parser.parse_args() |
181 | | - |
| 183 | +def generate_fpus(args): |
182 | 184 | # Collect all the data: make the list of FPU names, and the set of |
183 | 185 | # features that LLVM maps each one to. |
184 | 186 | fpu_features = { |
@@ -208,7 +210,109 @@ def main(): |
208 | 210 | print(" Flags:") |
209 | 211 | for sub_fpu in subsets: |
210 | 212 | print(" - -mfpu=" + sub_fpu) |
| 213 | + print() |
| 214 | + |
| 215 | + |
| 216 | +def get_extension_list(clang, triple): |
| 217 | + """Extract the list of architecture extension flags from clang, by running |
| 218 | + it with the --print-supported-extensions option.""" |
| 219 | + |
| 220 | + command = [ |
| 221 | + clang, |
| 222 | + "--target=" + triple, |
| 223 | + "--print-supported-extensions", |
| 224 | + ] |
| 225 | + |
| 226 | + output = subprocess.check_output( |
| 227 | + command, stderr=subprocess.STDOUT |
| 228 | + ).decode() |
| 229 | + |
| 230 | + for line in output.split("\n"): |
| 231 | + parts = line.split(maxsplit=1) |
| 232 | + # The feature lines will look like this, ignore everything else: |
| 233 | + # aes FEAT_AES, FEAT_PMULL Enable AES support |
| 234 | + if len(parts) == 2 and parts[1].startswith("FEAT_"): |
| 235 | + yield parts[0] |
| 236 | + |
| 237 | + |
| 238 | +def generate_extensions(args): |
| 239 | + aarch64_features = get_extension_list(args.clang, "aarch64-none-eabi") |
| 240 | + aarch32_features = get_extension_list(args.clang, "arm-none-eabi") |
| 241 | + all_features = set(aarch64_features) | set(aarch32_features) |
| 242 | + |
| 243 | + print("# Expand -march=...+[no]feature... into individual options we can match") |
| 244 | + print("# on. We use 'armvX' to represent a feature applied to any architecture, so") |
| 245 | + print("# that these don't need to be repeated for every version. Libraries which") |
| 246 | + print("# require a particular architecture version or profile should also match on the") |
| 247 | + print("# original option to check that.") |
| 248 | + |
| 249 | + for feature in all_features: |
| 250 | + print(f"- Match: -march=armv.*\\+{feature}($|\+.*)") |
| 251 | + print(f" Flags:") |
| 252 | + print(f" - -march=armvX+{feature}") |
| 253 | + print(f"- Match: -march=armv.*\\+no{feature}($|\+.*)") |
| 254 | + print(f" Flags:") |
| 255 | + print(f" - -march=armvX+no{feature}") |
| 256 | + print() |
| 257 | + |
| 258 | + |
| 259 | +@dataclass |
| 260 | +class Version: |
| 261 | + major: int |
| 262 | + minor: int |
| 263 | + profile: int |
| 264 | + |
| 265 | + def __str__(self): |
| 266 | + if self.minor == 0: |
| 267 | + return f"armv{self.major}-{self.profile}" |
| 268 | + else: |
| 269 | + return f"armv{self.major}.{self.minor}-{self.profile}" |
| 270 | + |
| 271 | + @property |
| 272 | + def all_compatible(self): |
| 273 | + yield self |
| 274 | + for compat_minor in range(self.minor): |
| 275 | + yield Version(self.major, compat_minor, self.profile) |
| 276 | + if self.major == 9: |
| 277 | + for compat_minor in range(self.minor + 5 + 1): |
| 278 | + yield Version(self.major - 1, compat_minor, self.profile) |
| 279 | + |
| 280 | +def generate_versions(args): |
| 281 | + """Generate match blocks which allow selecting a library build for a |
| 282 | + lower-version architecture, for the v8.x-A and v9.x-A minor versions.""" |
| 283 | + versions = ( |
| 284 | + [Version(8, minor, "a") for minor in range(10)] + |
| 285 | + [Version(9, minor, "a") for minor in range(6)] + |
| 286 | + [Version(8, minor, "r") for minor in range(1)] |
| 287 | + ) |
| 288 | + |
| 289 | + for match_ver in versions: |
| 290 | + print(f"- Match: -march={match_ver}.*") |
| 291 | + print(f" Flags:") |
| 292 | + for compat_ver in match_ver.all_compatible: |
| 293 | + print(f" - -march={compat_ver}") |
| 294 | + print() |
| 295 | + |
| 296 | + |
| 297 | + |
| 298 | +def main(): |
| 299 | + parser = argparse.ArgumentParser( |
| 300 | + description=__doc__, |
| 301 | + formatter_class=argparse.RawDescriptionHelpFormatter, |
| 302 | + ) |
| 303 | + parser.add_argument( |
| 304 | + "--clang", required=True, help="Path to clang executable." |
| 305 | + ) |
| 306 | + parser.add_argument( |
| 307 | + "--llvm-source", |
| 308 | + required=True, |
| 309 | + help="Path to root of llvm-project source tree.", |
| 310 | + ) |
| 311 | + args = parser.parse_args() |
211 | 312 |
|
| 313 | + generate_fpus(args) |
| 314 | + generate_extensions(args) |
| 315 | + generate_versions(args) |
212 | 316 |
|
213 | 317 | if __name__ == "__main__": |
214 | 318 | main() |
0 commit comments