diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4159,6 +4159,8 @@ "library (\"libgcc.a\" or \"libclang_rt.builtins.*.a\")">; def print_multi_directory : Flag<["-", "--"], "print-multi-directory">; def print_multi_lib : Flag<["-", "--"], "print-multi-lib">; +def print_multi_selection_flags : Flag<["-", "--"], "print-multi-selection-flags-experimental">, + HelpText<"Print the flags used for selecting multilibs (experimental)">; def print_multi_os_directory : Flag<["-", "--"], "print-multi-os-directory">, Flags<[Unsupported]>; def print_target_triple : Flag<["-", "--"], "print-target-triple">, diff --git a/clang/include/clang/Driver/ToolChain.h b/clang/include/clang/Driver/ToolChain.h --- a/clang/include/clang/Driver/ToolChain.h +++ b/clang/include/clang/Driver/ToolChain.h @@ -285,6 +285,28 @@ const Multilib &getMultilib() const { return SelectedMultilib; } + /// Get flags suitable for multilib selection, based on the provided clang + /// command line arguments. The command line arguments aren't suitable to be + /// used directly for multilib selection because they are not normalized and + /// normalization is a complex process. The result of this function is similar + /// to clang command line arguments but different: + /// * It's incomplete. Only certain command line arguments are processed. If + /// more command line arguments are needed for multilib selection then this + /// function should be extended. + /// * The flags aren't prefixed with "-". + /// * Where a single command line argument would contain a list, the elements + /// are split out. For example, Arm extensions are appended to -march like + /// -march=armv8m.main+crc+crypto+sha2+mve. Since this function is + /// incomplete, a later clang version may infer the presence of more + /// extensions than an earlier version. If all extensions were in a single + /// flag then that would be unstable. Instead, each extension will be + /// returned separately. There may not be valid syntax to express this as a + /// clang argument so a pseudo argument syntax must be used instead. + /// To allow users to find out what flags are returned, clang accepts a + /// -print-multi-selection-flags-experimental argument. + std::vector + getMultiSelectionFlags(const llvm::opt::ArgList &) const; + SanitizerArgs getSanitizerArgs(const llvm::opt::ArgList &JobArgs) const; const XRayArgs& getXRayArgs() const; diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -2209,6 +2209,15 @@ return false; } + if (C.getArgs().hasArg(options::OPT_print_multi_selection_flags)) { + auto ArgFlags = TC.getMultiSelectionFlags(C.getArgs()); + auto AllFlags = TC.getMultilibs().expandFlags( + Multilib::flag_set(ArgFlags.begin(), ArgFlags.end())); + for (StringRef Flag : AllFlags) + llvm::outs() << Flag << '\n'; + return false; + } + if (C.getArgs().hasArg(options::OPT_print_multi_directory)) { const Multilib &Multilib = TC.getMultilib(); if (Multilib.gccSuffix().empty()) diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -7,8 +7,10 @@ //===----------------------------------------------------------------------===// #include "clang/Driver/ToolChain.h" +#include "ToolChains/Arch/AArch64.h" #include "ToolChains/Arch/ARM.h" #include "ToolChains/Clang.h" +#include "ToolChains/CommonArgs.h" #include "ToolChains/Flang.h" #include "ToolChains/InterfaceStubs.h" #include "clang/Basic/ObjCRuntime.h" @@ -41,6 +43,7 @@ #include "llvm/Support/TargetParser.h" #include "llvm/Support/VersionTuple.h" #include "llvm/Support/VirtualFileSystem.h" +#include "llvm/TargetParser/AArch64TargetParser.h" #include #include #include @@ -170,6 +173,136 @@ return PPC_LINUX_DEFAULT_IEEELONGDOUBLE && getTriple().isOSLinux(); } +static void getAArch64MultiSelectionFlags(const Driver &D, + const llvm::Triple &Triple, + const llvm::opt::ArgList &Args, + std::vector &Result) { + std::vector Features; + tools::aarch64::getAArch64TargetFeatures(D, Triple, Args, Features, false); + const auto UnifiedFeatures = tools::unifyTargetFeatures(Features); + llvm::DenseSet FeatureSet(UnifiedFeatures.begin(), + UnifiedFeatures.end()); + for (const auto &Ext : AArch64::Extensions) { + if (FeatureSet.find(Ext.Feature) != FeatureSet.end()) { + Result.push_back(("march=+" + Ext.Name).str()); + } + } +} + +static void getARMMultiSelectionFlags(const Driver &D, + const llvm::Triple &Triple, + const llvm::opt::ArgList &Args, + std::vector &Result) { + std::vector Features; + unsigned FPUKind = llvm::ARM::FK_INVALID; + tools::arm::getARMTargetFeatures(D, Triple, Args, Features, false, &FPUKind); + const auto UnifiedFeatures = tools::unifyTargetFeatures(Features); + llvm::DenseSet FeatureSet(UnifiedFeatures.begin(), + UnifiedFeatures.end()); + for (const auto &Ext : ARM::ARCHExtNames) { + if (FeatureSet.find(Ext.Feature) != FeatureSet.end()) { + Result.push_back(("march=+" + Ext.Name).str()); + } + } + + switch (FPUKind) { +#define ARM_FPU(NAME, KIND, VERSION, NEON_SUPPORT, RESTRICTION) \ + case llvm::ARM::KIND: \ + Result.push_back("mfpu=" NAME); \ + break; +#include "llvm/TargetParser/ARMTargetParser.def" + default: + llvm_unreachable("Invalid FPUKind"); + } + + switch (arm::getARMFloatABI(D, Triple, Args)) { + case arm::FloatABI::Soft: + Result.push_back("mfloat-abi=soft"); + break; + case arm::FloatABI::SoftFP: + Result.push_back("mfloat-abi=softfp"); + break; + case arm::FloatABI::Hard: + Result.push_back("mfloat-abi=hard"); + break; + case arm::FloatABI::Invalid: + llvm_unreachable("Invalid float ABI"); + } +} + +std::vector +ToolChain::getMultiSelectionFlags(const llvm::opt::ArgList &Args) const { + std::vector Result; + + using namespace clang::driver::options; + + const llvm::Triple Triple(ComputeEffectiveClangTriple(Args)); + + // Enumerate boolean flags we care about for the purposes of multilib here. + // There must be a smarter way to do it but this gets us started. + const struct HasFlag { + ID Pos, Neg; + bool Default; + } HasFlagList[] = { + {OPT_fexceptions, OPT_fno_exceptions, true}, + {OPT_frtti, OPT_fno_rtti, true}, + }; + + // Options we care about that have a single last-wins value. + static const ID GetLastArgValue[] = { + OPT_fcxx_abi_EQ, + }; + + for (const HasFlag &HF : HasFlagList) { + ID Option = Args.hasFlag(HF.Pos, HF.Neg, HF.Default) ? HF.Pos : HF.Neg; + Result.push_back( + clang::driver::getDriverOptTable().getOptionName(Option).str()); + } + + for (ID Option : GetLastArgValue) { + StringRef Value = Args.getLastArgValue(Option); + if (!Value.empty()) { + Result.push_back( + (clang::driver::getDriverOptTable().getOptionName(Option) + Value) + .str()); + } + } + + const SanitizerArgs SanArgs(getSanitizerArgs(Args)); + if (SanArgs.needsAsanRt()) { + Result.push_back("fsanitize=address"); + } + if (SanArgs.needsHwasanRt()) { + Result.push_back("fsanitize=hwaddress"); + } + + switch (Triple.getArch()) { + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_32: + case llvm::Triple::aarch64_be: + getAArch64MultiSelectionFlags(D, Triple, Args, Result); + break; + case llvm::Triple::arm: + case llvm::Triple::armeb: + case llvm::Triple::thumb: + case llvm::Triple::thumbeb: + getARMMultiSelectionFlags(D, Triple, Args, Result); + break; + default: + break; + } + + Result.push_back("target=" + Triple.str()); + + // Sort alphabetically + std::sort(Result.begin(), Result.end()); + + // Remove duplicate entries + Result.erase(std::unique(Result.begin(), Result.end()), Result.end()); + + return Result; +} + SanitizerArgs ToolChain::getSanitizerArgs(const llvm::opt::ArgList &JobArgs) const { SanitizerArgs SanArgs(*this, JobArgs, !SanitizerArgsChecked); diff --git a/clang/lib/Driver/ToolChains/Arch/ARM.h b/clang/lib/Driver/ToolChains/Arch/ARM.h --- a/clang/lib/Driver/ToolChains/Arch/ARM.h +++ b/clang/lib/Driver/ToolChains/Arch/ARM.h @@ -64,9 +64,11 @@ void getARMArchCPUFromArgs(const llvm::opt::ArgList &Args, llvm::StringRef &Arch, llvm::StringRef &CPU, bool FromAs = false); +// Optionally returns the FPUKind void getARMTargetFeatures(const Driver &D, const llvm::Triple &Triple, const llvm::opt::ArgList &Args, - std::vector &Features, bool ForAS); + std::vector &Features, bool ForAS, + unsigned *OutFPUKind = nullptr); int getARMSubArchVersionNumber(const llvm::Triple &Triple); bool isARMMProfile(const llvm::Triple &Triple); bool isARMAProfile(const llvm::Triple &Triple); diff --git a/clang/lib/Driver/ToolChains/Arch/ARM.cpp b/clang/lib/Driver/ToolChains/Arch/ARM.cpp --- a/clang/lib/Driver/ToolChains/Arch/ARM.cpp +++ b/clang/lib/Driver/ToolChains/Arch/ARM.cpp @@ -438,7 +438,8 @@ void arm::getARMTargetFeatures(const Driver &D, const llvm::Triple &Triple, const ArgList &Args, - std::vector &Features, bool ForAS) { + std::vector &Features, bool ForAS, + unsigned *OutFPUKind) { bool KernelOrKext = Args.hasArg(options::OPT_mkernel, options::OPT_fapple_kext); arm::FloatABI ABI = arm::getARMFloatABI(D, Triple, Args); @@ -632,6 +633,7 @@ // above call. Features.insert(Features.end(), {"-dotprod", "-fp16fml", "-bf16", "-mve", "-mve.fp", "-fpregs"}); + FPUID = llvm::ARM::FK_NONE; } else if (FPUID == llvm::ARM::FK_NONE || ArchArgFPUID == llvm::ARM::FK_NONE || CPUArgFPUID == llvm::ARM::FK_NONE) { @@ -643,6 +645,7 @@ {"-dotprod", "-fp16fml", "-bf16", "-mve.fp"}); if (!hasIntegerMVE(Features)) Features.emplace_back("-fpregs"); + FPUID = llvm::ARM::FK_NONE; } // En/disable crc code generation. @@ -899,6 +902,10 @@ if (Args.getLastArg(options::OPT_mno_bti_at_return_twice)) Features.push_back("+no-bti-at-return-twice"); + + if (OutFPUKind) { + *OutFPUKind = FPUID; + } } std::string arm::getARMArch(StringRef Arch, const llvm::Triple &Triple) { diff --git a/clang/test/Driver/print-multi-selection-flags.c b/clang/test/Driver/print-multi-selection-flags.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/print-multi-selection-flags.c @@ -0,0 +1,54 @@ +// RUN: %clang -print-multi-selection-flags-experimental --target=aarch64-linux -fc++-abi=itanium -fsanitize=address | FileCheck --check-prefix=CHECK-LINUX %s +// CHECK-LINUX: fc++-abi=itanium +// CHECK-LINUX: fexceptions +// CHECK-LINUX: frtti +// CHECK-LINUX: fsanitize=address +// CHECK-LINUX: target=aarch64-unknown-linux + +// RUN: %clang -print-multi-selection-flags-experimental --target=aarch64-fuchsia -fsanitize=hwaddress | FileCheck --check-prefix=CHECK-FUCHSIA %s +// CHECK-FUCHSIA: fsanitize=hwaddress +// CHECK-FUCHSIA: target=aarch64-unknown-fuchsia + +// RUN: %clang -print-multi-selection-flags-experimental --target=arm-none-eabi -mfloat-abi=soft -fno-exceptions -fno-rtti | FileCheck --check-prefix=CHECK-ARMV4T %s +// CHECK-ARMV4T: fno-exceptions +// CHECK-ARMV4T: fno-rtti +// CHECK-ARMV4T: mfloat-abi=soft +// CHECK-ARMV4T: mfpu=none +// CHECK-ARMV4T: target=armv4t-none-unknown-eabi + +// RUN: %clang -print-multi-selection-flags-experimental --target=armv7em-none-eabi -mfloat-abi=softfp | FileCheck --check-prefix=CHECK-SOFTFP %s +// CHECK-SOFTFP: mfloat-abi=softfp +// CHECK-SOFTFP: mfpu=fpv4-sp-d16 +// CHECK-SOFTFP: target=thumbv7em-none-unknown-eabi + +// RUN: %clang -print-multi-selection-flags-experimental --target=arm-none-eabihf -march=armv7em -mfpu=fpv5-d16 | FileCheck --check-prefix=CHECK-HARD %s +// CHECK-HARD: mfloat-abi=hard +// CHECK-HARD: mfpu=fpv5-d16 +// CHECK-HARD: target=thumbv7em-none-unknown-eabihf + +// RUN: %clang -print-multi-selection-flags-experimental --target=arm-none-eabi -mfloat-abi=soft -march=armv8-m.main+nofp | FileCheck --check-prefix=CHECK-V8MMAIN-NOFP %s +// CHECK-V8MMAIN-NOFP: mfloat-abi=soft +// CHECK-V8MMAIN-NOFP: mfpu=none +// CHECK-V8MMAIN-NOFP: target=thumbv8m.main-none-unknown-eabi + +// RUN: %clang -print-multi-selection-flags-experimental --target=arm-none-eabi -mfloat-abi=hard -march=armv8.1m.main+mve.fp | FileCheck --check-prefix=CHECK-MVE %s +// CHECK-MVE: march=+mve +// CHECK-MVE: march=+mve.fp +// CHECK-MVE: mfloat-abi=hard +// CHECK-MVE: mfpu=fp-armv8-fullfp16-sp-d16 +// CHECK-MVE: target=thumbv8.1m.main-none-unknown-eabihf + +// RUN: %clang -print-multi-selection-flags-experimental --target=arm-none-eabi -march=armv8.1m.main+mve+nofp | FileCheck --check-prefix=CHECK-MVENOFP %s +// CHECK-MVENOFP: march=+mve +// CHECK-MVENOFP-NOT: march=+mve.fp +// CHECK-MVENOFP: mfpu=none + +// RUN: %clang -print-multi-selection-flags-experimental --target=aarch64-none-elf -march=armv8-a+lse | FileCheck --check-prefix=CHECK-LSE %s +// CHECK-LSE: march=+lse + +// RUN: %clang -print-multi-selection-flags-experimental --target=aarch64-none-elf -march=armv8.5-a+sve+sve2 | FileCheck --check-prefix=CHECK-SVE2 %s +// RUN: %clang -print-multi-selection-flags-experimental --target=aarch64-none-elf -march=armv9-a | FileCheck --check-prefix=CHECK-SVE2 %s +// CHECK-SVE2: march=+simd +// CHECK-SVE2: march=+sve +// CHECK-SVE2: march=+sve2 +// CHECK-SVE2: target=aarch64-none-unknown-elf