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 @@ -4271,6 +4271,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_flags : Flag<["-", "--"], "print-multi-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,18 @@ 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 except that the list of arguments is + /// incomplete. Only certain command line arguments are processed. If more + /// command line arguments are needed for multilib selection then this + /// function should be extended. + /// To allow users to find out what flags are returned, clang accepts a + /// -print-multi-flags-experimental argument. + Multilib::flags_list getMultilibFlags(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 @@ -2217,6 +2217,17 @@ return false; } + if (C.getArgs().hasArg(options::OPT_print_multi_flags)) { + Multilib::flags_list ArgFlags = TC.getMultilibFlags(C.getArgs()); + llvm::StringSet<> ExpandedFlags = TC.getMultilibs().expandFlags(ArgFlags); + std::set SortedFlags; + for (const auto &FlagEntry : ExpandedFlags) + SortedFlags.insert(FlagEntry.getKey()); + for (auto Flag : SortedFlags) + 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" @@ -39,6 +41,7 @@ #include "llvm/Support/Path.h" #include "llvm/Support/VersionTuple.h" #include "llvm/Support/VirtualFileSystem.h" +#include "llvm/TargetParser/AArch64TargetParser.h" #include "llvm/TargetParser/TargetParser.h" #include "llvm/TargetParser/Triple.h" #include @@ -171,6 +174,101 @@ return PPC_LINUX_DEFAULT_IEEELONGDOUBLE && getTriple().isOSLinux(); } +static void getAArch64MultilibFlags(const Driver &D, + const llvm::Triple &Triple, + const llvm::opt::ArgList &Args, + Multilib::flags_list &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()); + std::vector MArch; + for (const auto &Ext : AArch64::Extensions) + if (FeatureSet.find(Ext.Feature) != FeatureSet.end()) + MArch.push_back(Ext.Name.str()); + for (const auto &Ext : AArch64::Extensions) + if (FeatureSet.find(Ext.NegFeature) != FeatureSet.end()) + MArch.push_back(("no" + Ext.Name).str()); + MArch.insert(MArch.begin(), ("-march=" + Triple.getArchName()).str()); + Result.push_back(llvm::join(MArch, "+")); +} + +static void getARMMultilibFlags(const Driver &D, + const llvm::Triple &Triple, + const llvm::opt::ArgList &Args, + Multilib::flags_list &Result) { + std::vector Features; + llvm::ARM::FPUKind FPUKind = + tools::arm::getARMTargetFeatures(D, Triple, Args, Features, false); + const auto UnifiedFeatures = tools::unifyTargetFeatures(Features); + llvm::DenseSet FeatureSet(UnifiedFeatures.begin(), + UnifiedFeatures.end()); + std::vector MArch; + for (const auto &Ext : ARM::ARCHExtNames) + if (FeatureSet.find(Ext.Feature) != FeatureSet.end()) + MArch.push_back(Ext.Name.str()); + for (const auto &Ext : ARM::ARCHExtNames) + if (FeatureSet.find(Ext.NegFeature) != FeatureSet.end()) + MArch.push_back(("no" + Ext.Name).str()); + MArch.insert(MArch.begin(), ("-march=" + Triple.getArchName()).str()); + Result.push_back(llvm::join(MArch, "+")); + + 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"); + } +} + +Multilib::flags_list +ToolChain::getMultilibFlags(const llvm::opt::ArgList &Args) const { + using namespace clang::driver::options; + + std::vector Result; + const llvm::Triple Triple(ComputeEffectiveClangTriple(Args)); + Result.push_back("--target=" + Triple.str()); + + switch (Triple.getArch()) { + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_32: + case llvm::Triple::aarch64_be: + getAArch64MultilibFlags(D, Triple, Args, Result); + break; + case llvm::Triple::arm: + case llvm::Triple::armeb: + case llvm::Triple::thumb: + case llvm::Triple::thumbeb: + getARMMultilibFlags(D, Triple, Args, Result); + break; + default: + break; + } + + // Sort and remove duplicates + std::sort(Result.begin(), Result.end()); + 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 @@ -63,9 +63,11 @@ void getARMArchCPUFromArgs(const llvm::opt::ArgList &Args, llvm::StringRef &Arch, llvm::StringRef &CPU, bool FromAs = false); -void getARMTargetFeatures(const Driver &D, const llvm::Triple &Triple, - const llvm::opt::ArgList &Args, - std::vector &Features, bool ForAS); +llvm::ARM::FPUKind getARMTargetFeatures(const Driver &D, + const llvm::Triple &Triple, + const llvm::opt::ArgList &Args, + std::vector &Features, + bool ForAS); 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 @@ -463,9 +463,11 @@ (NoMVE == F.rend() || std::distance(MVE, NoMVE) > 0); } -void arm::getARMTargetFeatures(const Driver &D, const llvm::Triple &Triple, - const ArgList &Args, - std::vector &Features, bool ForAS) { +llvm::ARM::FPUKind arm::getARMTargetFeatures(const Driver &D, + const llvm::Triple &Triple, + const ArgList &Args, + std::vector &Features, + bool ForAS) { bool KernelOrKext = Args.hasArg(options::OPT_mkernel, options::OPT_fapple_kext); arm::FloatABI ABI = arm::getARMFloatABI(D, Triple, Args); @@ -661,6 +663,7 @@ Features.insert(Features.end(), {"-dotprod", "-fp16fml", "-bf16", "-mve", "-mve.fp"}); HasFPRegs = false; + FPUKind = llvm::ARM::FK_NONE; } else if (FPUKind == llvm::ARM::FK_NONE || ArchArgFPUKind == llvm::ARM::FK_NONE || CPUArgFPUKind == llvm::ARM::FK_NONE) { @@ -671,6 +674,7 @@ Features.insert(Features.end(), {"-dotprod", "-fp16fml", "-bf16", "-mve.fp"}); HasFPRegs = hasIntegerMVE(Features); + FPUKind = llvm::ARM::FK_NONE; } if (!HasFPRegs) Features.emplace_back("-fpregs"); @@ -931,6 +935,8 @@ Features.push_back("+no-bti-at-return-twice"); checkARMFloatABI(D, Args, HasFPRegs); + + return FPUKind; } 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,44 @@ +// RUN: %clang -print-multi-flags-experimental --target=aarch64-linux -fc++-abi=itanium -fsanitize=address | FileCheck --check-prefix=CHECK-LINUX %s +// CHECK-LINUX: --target=aarch64-unknown-linux + +// RUN: %clang -print-multi-flags-experimental --target=aarch64-fuchsia -fsanitize=hwaddress | FileCheck --check-prefix=CHECK-FUCHSIA %s +// CHECK-FUCHSIA: --target=aarch64-unknown-fuchsia + +// RUN: %clang -print-multi-flags-experimental --target=arm-none-eabi -mfloat-abi=soft -fno-exceptions -fno-rtti | FileCheck --check-prefix=CHECK-ARMV4T %s +// CHECK-ARMV4T: --target=armv4t-none-unknown-eabi +// CHECK-ARMV4T: -mfloat-abi=soft +// CHECK-ARMV4T: -mfpu=none + +// RUN: %clang -print-multi-flags-experimental --target=armv7em-none-eabi -mfloat-abi=softfp | FileCheck --check-prefix=CHECK-SOFTFP %s +// CHECK-SOFTFP: --target=thumbv7em-none-unknown-eabi +// CHECK-SOFTFP: -mfloat-abi=softfp +// CHECK-SOFTFP: -mfpu=fpv4-sp-d16 + +// RUN: %clang -print-multi-flags-experimental --target=arm-none-eabihf -march=armv7em -mfpu=fpv5-d16 | FileCheck --check-prefix=CHECK-HARD %s +// CHECK-HARD: --target=thumbv7em-none-unknown-eabihf +// CHECK-HARD: -mfloat-abi=hard +// CHECK-HARD: -mfpu=fpv5-d16 + +// RUN: %clang -print-multi-flags-experimental --target=arm-none-eabi -mfloat-abi=soft -march=armv8-m.main+nofp | FileCheck --check-prefix=CHECK-V8MMAIN-NOFP %s +// CHECK-V8MMAIN-NOFP: --target=thumbv8m.main-none-unknown-eabi +// CHECK-V8MMAIN-NOFP: -mfloat-abi=soft +// CHECK-V8MMAIN-NOFP: -mfpu=none + +// RUN: %clang -print-multi-flags-experimental --target=arm-none-eabi -mfloat-abi=hard -march=armv8.1m.main+mve.fp | FileCheck --check-prefix=CHECK-MVE %s +// CHECK-MVE: --target=thumbv8.1m.main-none-unknown-eabihf +// CHECK-MVE: -march=thumbv8.1m.main{{.*}}+mve{{.*}}+mve.fp{{.*}} +// CHECK-MVE: -mfloat-abi=hard +// CHECK-MVE: -mfpu=fp-armv8-fullfp16-sp-d16 + +// RUN: %clang -print-multi-flags-experimental --target=arm-none-eabi -march=armv8.1m.main+mve+nofp | FileCheck --check-prefix=CHECK-MVENOFP %s +// CHECK-MVENOFP: -march=thumbv8.1m.main{{.*}}+mve{{.*}} +// CHECK-MVENOFP-NOT: -march=thumbv8.1m.main{{.*}}+mve.fp{{.*}} +// CHECK-MVENOFP: -mfpu=none + +// RUN: %clang -print-multi-flags-experimental --target=aarch64-none-elf -march=armv8-a+lse | FileCheck --check-prefix=CHECK-LSE %s +// CHECK-LSE: -march=aarch64{{.*}}+lse{{.*}} + +// RUN: %clang -print-multi-flags-experimental --target=aarch64-none-elf -march=armv8.5-a+sve+sve2 | FileCheck --check-prefix=CHECK-SVE2 %s +// RUN: %clang -print-multi-flags-experimental --target=aarch64-none-elf -march=armv9-a | FileCheck --check-prefix=CHECK-SVE2 %s +// CHECK-SVE2: --target=aarch64-none-unknown-elf +// CHECK-SVE2: -march=aarch64{{.*}}+simd{{.*}}+sve{{.*}}+sve2{{.*}}