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 @@ -193,7 +193,8 @@ std::vector &Result) { std::vector Features; unsigned FPUKind = llvm::ARM::FK_INVALID; - tools::arm::getARMTargetFeatures(D, Triple, Args, Features, false, &FPUKind); + tools::arm::getARMTargetFeatures(D, Triple, Args, Features, false /*ForAs*/, + true /*ForMultilib*/, &FPUKind); const auto UnifiedFeatures = tools::unifyTargetFeatures(Features); llvm::DenseSet FeatureSet(UnifiedFeatures.begin(), UnifiedFeatures.end()); 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 @@ -67,6 +67,7 @@ void getARMTargetFeatures(const Driver &D, const llvm::Triple &Triple, const llvm::opt::ArgList &Args, std::vector &Features, bool ForAS, + bool ForMultilib = false, unsigned *OutFPUKind = nullptr); int getARMSubArchVersionNumber(const llvm::Triple &Triple); bool isARMMProfile(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,7 @@ void arm::getARMTargetFeatures(const Driver &D, const llvm::Triple &Triple, const ArgList &Args, std::vector &Features, bool ForAS, - unsigned *OutFPUKind) { + bool ForMultilib, unsigned *OutFPUKind) { bool KernelOrKext = Args.hasArg(options::OPT_mkernel, options::OPT_fapple_kext); arm::FloatABI ABI = arm::getARMFloatABI(D, Triple, Args); @@ -775,7 +775,9 @@ // Generate execute-only output (no data access to code sections). // This only makes sense for the compiler, not for the assembler. - if (!ForAS) { + // It's not needed for multilib selection and may hide an unused + // argument diagnostic if the code is always run. + if (!ForAS && !ForMultilib) { // Supported only on ARMv6T2 and ARMv7 and above. // Cannot be combined with -mno-movt. if (Arg *A = Args.getLastArg(options::OPT_mexecute_only, options::OPT_mno_execute_only)) { diff --git a/clang/lib/Driver/ToolChains/BareMetal.cpp b/clang/lib/Driver/ToolChains/BareMetal.cpp --- a/clang/lib/Driver/ToolChains/BareMetal.cpp +++ b/clang/lib/Driver/ToolChains/BareMetal.cpp @@ -155,6 +155,44 @@ return Triple.getEnvironmentName() == "elf"; } +static bool findMultilibsFromYAML(const ToolChain &TC, const Driver &D, + StringRef MultilibPath, const ArgList &Args, + DetectedMultilibs &Result) { + llvm::ErrorOr> MB = + D.getVFS().getBufferForFile(MultilibPath); + if (!MB) + return false; + std::vector ArgsTags = TC.getMultiSelectionTags(Args); + if (!Result.Multilibs.parseYaml(*MB.get())) + return false; + Multilib::tag_set Tags(ArgsTags.begin(), ArgsTags.end()); + return Result.Multilibs.select(Tags, Result.SelectedMultilib); +} + +#define MULTILIB_YAML_FILENAME "multilib.yaml" + +// Get the sysroot, before multilib takes effect. +static std::string computeBaseSysRoot(const Driver &D, + const llvm::Triple &Triple) { + if (!D.SysRoot.empty()) + return D.SysRoot; + + SmallString<128> SysRootDir(D.Dir); + llvm::sys::path::append(SysRootDir, "../lib/clang-runtimes"); + + SmallString<128> MultilibPath(SysRootDir); + llvm::sys::path::append(MultilibPath, MULTILIB_YAML_FILENAME); + + // New behaviour: if multilib.yaml is found then use clang-runtimes as the + // sysroot. + if (D.getVFS().exists(MultilibPath)) + return std::string(SysRootDir); + + // Otherwise fall back to the old behaviour of appending the target triple. + llvm::sys::path::append(SysRootDir, D.getTargetTriple()); + return std::string(SysRootDir); +} + void BareMetal::findMultilibs(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) { DetectedMultilibs Result; @@ -163,6 +201,12 @@ SelectedMultilib = Result.SelectedMultilib; Multilibs = Result.Multilibs; } + } else { + llvm::SmallString<128> MultilibPath(computeBaseSysRoot(D, Triple)); + llvm::sys::path::append(MultilibPath, MULTILIB_YAML_FILENAME); + findMultilibsFromYAML(*this, D, MultilibPath, Args, Result); + SelectedMultilib = Result.SelectedMultilib; + Multilibs = Result.Multilibs; } } @@ -176,15 +220,8 @@ } std::string BareMetal::computeSysRoot() const { - if (!getDriver().SysRoot.empty()) - return getDriver().SysRoot + SelectedMultilib.osSuffix(); - - SmallString<128> SysRootDir; - llvm::sys::path::append(SysRootDir, getDriver().Dir, "../lib/clang-runtimes", - getDriver().getTargetTriple()); - - SysRootDir += SelectedMultilib.osSuffix(); - return std::string(SysRootDir); + return computeBaseSysRoot(getDriver(), getTriple()) + + SelectedMultilib.osSuffix(); } void BareMetal::AddClangSystemIncludeArgs(const ArgList &DriverArgs, diff --git a/clang/test/Driver/baremetal-multilib.yaml b/clang/test/Driver/baremetal-multilib.yaml new file mode 100644 --- /dev/null +++ b/clang/test/Driver/baremetal-multilib.yaml @@ -0,0 +1,143 @@ +# REQUIRES: shell +# UNSUPPORTED: system-windows + +# RUN: rm -rf %T/baremetal_multilib +# RUN: mkdir -p %T/baremetal_multilib/bin +# RUN: mkdir -p %T/baremetal_multilib/lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/fp/lib +# RUN: touch %T/baremetal_multilib/lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/fp/lib/libclang_rt.builtins.a +# RUN: ln -s %clang %T/baremetal_multilib/bin/clang +# RUN: ln -s %s %T/baremetal_multilib/lib/clang-runtimes/multilib.yaml + +# RUN: %T/baremetal_multilib/bin/clang -no-canonical-prefixes -x c++ %s -### -o %t.out 2>&1 \ +# RUN: --target=thumbv8m.main-none-eabihf --sysroot= \ +# RUN: | FileCheck -DSYSROOT=%T/baremetal_multilib %s +# CHECK: "-cc1" "-triple" "thumbv8m.main-none-unknown-eabihf" +# CHECK-SAME: "-internal-isystem" "[[SYSROOT]]/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/fp/include/c++/v1" +# CHECK-SAME: "-internal-isystem" "[[SYSROOT]]/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/fp/include" +# CHECK-SAME: "-x" "c++" "{{.*}}baremetal-multilib.yaml" +# CHECK-NEXT: ld{{(.exe)?}}" "{{.*}}.o" "-Bstatic" +# CHECK-SAME: "-L[[SYSROOT]]/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/fp/lib" +# CHECK-SAME: "-lc" "-lm" "-lclang_rt.builtins" +# CHECK-SAME: "-o" "{{.*}}.tmp.out" + +# RUN: %T/baremetal_multilib/bin/clang -no-canonical-prefixes -print-multi-directory 2>&1 \ +# RUN: --target=thumbv8m.main-none-eabihf --sysroot= \ +# RUN: | FileCheck --check-prefix=CHECK-PRINT-MULTI-DIRECTORY %s +# CHECK-PRINT-MULTI-DIRECTORY: arm-none-eabi/thumb/v8-m.main/fp + +# RUN: %T/baremetal_multilib/bin/clang -no-canonical-prefixes -print-multi-lib 2>&1 \ +# RUN: --target=arm-none-eabi --sysroot= \ +# RUN: | FileCheck --check-prefix=CHECK-PRINT-MULTI-LIB %s +# CHECK-PRINT-MULTI-LIB: arm-none-eabi/thumb/v6-m/nofp;@-target=thumbv6m-none-eabi@mfloat-abi=soft +# CHECK-PRINT-MULTI-LIB: arm-none-eabi/thumb/v7-m/nofp;@-target=thumbv7m-none-eabi@mfloat-abi=soft +# CHECK-PRINT-MULTI-LIB: arm-none-eabi/thumb/v7e-m/nofp;@-target=thumbv7em-none-eabi@mfloat-abi=soft@mfpu=none +# CHECK-PRINT-MULTI-LIB: arm-none-eabi/thumb/v8-m.main/nofp;@-target=arm-none-eabi@mfloat-abi=soft@march=armv8m.main+nofp +# CHECK-PRINT-MULTI-LIB: arm-none-eabi/thumb/v8.1-m.main/nofp/nomve;@-target=arm-none-eabi@mfloat-abi=soft@march=armv8.1m.main+nofp+nomve +# CHECK-PRINT-MULTI-LIB: arm-none-eabi/thumb/v7e-m/fpv4_sp_d16;@-target=thumbv7em-none-eabihf@mfpu=fpv4-sp-d16 +# CHECK-PRINT-MULTI-LIB: arm-none-eabi/thumb/v7e-m/fpv5_d16;@-target=thumbv7em-none-eabihf@mfpu=fpv5-d16 +# CHECK-PRINT-MULTI-LIB: arm-none-eabi/thumb/v8-m.main/fp;@-target=thumbv8m.main-none-eabihf +# CHECK-PRINT-MULTI-LIB: arm-none-eabi/thumb/v8.1-m.main/fp;@-target=thumbv8.1m.main-none-eabihf +# CHECK-PRINT-MULTI-LIB: arm-none-eabi/thumb/v8.1-m.main/nofp/mve;@-target=arm-none-eabihf@march=armv8.1m.main+nofp+mve + +# RUN: %T/baremetal_multilib/bin/clang -no-canonical-prefixes -x assembler -mexecute-only \ +# RUN: --target=arm-none-eabi --sysroot= %s -c -### 2>&1 \ +# RUN: | FileCheck %s --check-prefix=CHECK-NO-EXECUTE-ONLY-ASM +# CHECK-NO-EXECUTE-ONLY-ASM: warning: argument unused during compilation: '-mexecute-only' + +--- +# This file is in two parts: +# 1. A list of library variants. +# 2. A mapping from tags generated from command line arguments to further +# tags. + +# How does clang use this file? +# 1. If the ToolChain class for the architecture supports this form of +# multilib it then it loads the file if present in sysroot. +# 2. Generate tags from the user provided arguments. +# (Use `clang -print-multi-selection-tags` to see which tags are +# generated). +# 3. Compare the arguments against each regular expression and store +# associated tags if there's a match. +# 4. Find the last library variant whose tags are a subset of the +# tags derived from the user provided arguments. +# 5. Use the directory for the library variant as the sysroot. + +# This defines the minimum version of Clang required to use this file. +# It is required to be present. +# Clang will emit an error if this number is greater than its version, but +# will accept lesser versions. +ClangMinimumVersion: 17.0.0 + +# The first section of the file is the list of library variants. +# A library is considered compatible if the are a subset of the tags derived +# from the arguments provided by the user. +# If multiple libraries are deemed compatible then the one that appears +# last in the list wins. A ToolChain may instead opt to use more than one +# multilib, layered on top of each other. +# Each library variant must have a "PrintOptions" attribute. This is solely to +# support `clang -print-multi-lib`. + +Variants: +- Dir: arm-none-eabi/thumb/v6-m/nofp + Tags: [target=thumbv6m-none-unknown-eabi] + PrintOptions: [--target=thumbv6m-none-eabi, -mfloat-abi=soft] + +- Dir: arm-none-eabi/thumb/v7-m/nofp + Tags: [target=thumbv7m-none-unknown-eabi] + PrintOptions: [--target=thumbv7m-none-eabi, -mfloat-abi=soft] + +- Dir: arm-none-eabi/thumb/v7e-m/nofp + Tags: [target=thumbv7em-none-unknown-eabi] + PrintOptions: [--target=thumbv7em-none-eabi, -mfloat-abi=soft, -mfpu=none] + +- Dir: arm-none-eabi/thumb/v8-m.main/nofp + Tags: [target=thumbv8m.main-none-unknown-eabi] + PrintOptions: [--target=arm-none-eabi, -mfloat-abi=soft, -march=armv8m.main+nofp] + +- Dir: arm-none-eabi/thumb/v8.1-m.main/nofp/nomve + Tags: [target=thumbv8.1m.main-none-unknown-eabi] + PrintOptions: [--target=arm-none-eabi, -mfloat-abi=soft, -march=armv8.1m.main+nofp+nomve] + +- Dir: arm-none-eabi/thumb/v7e-m/fpv4_sp_d16 + Tags: [target=thumbv7em-none-unknown-eabihf, mfpu=fpv4-sp-d16] + PrintOptions: [--target=thumbv7em-none-eabihf, -mfpu=fpv4-sp-d16] + +- Dir: arm-none-eabi/thumb/v7e-m/fpv5_d16 + Tags: [target=thumbv7em-none-unknown-eabihf, mfpu=fpv5-d16] + PrintOptions: [--target=thumbv7em-none-eabihf, -mfpu=fpv5-d16] + +- Dir: arm-none-eabi/thumb/v8-m.main/fp + Tags: [target=thumbv8m.main-none-unknown-eabihf, hasfpu] + PrintOptions: [--target=thumbv8m.main-none-eabihf] + +- Dir: arm-none-eabi/thumb/v8.1-m.main/fp + Tags: [target=thumbv8.1m.main-none-unknown-eabihf, hasfpu] + PrintOptions: [--target=thumbv8.1m.main-none-eabihf] + +- Dir: arm-none-eabi/thumb/v8.1-m.main/nofp/mve + Tags: [target=thumbv8.1m.main-none-unknown-eabihf, march=+mve] + PrintOptions: [--target=arm-none-eabihf, -march=armv8.1m.main+nofp+mve] + + +# The second section of the file is a map from auto-detected tags +# to custom tags. The auto-detected tags can be printed out +# by running clang with `-print-multi-selection-tags`. +# The regex must match a whole tag string. +# "MatchTags" tags will be added if an argument matches, while +# "NoMatchTags" tags will be added otherwise. +TagMap: +- Regex: mfpu=none + NoMatchTags: [hasfpu] +# For v8m.base (and potential later v8m baseline versions) use v6m +- Regex: target=thumbv8(\.[0-9]+)?m\.base-none-unknown-eabi + MatchTags: [target=thumbv6m-none-unknown-eabi] +# Match versions after v8.1m.main. We assume that v8.2m (if/when it exists) will +# be backwards compatible with v8.1m. +# The alternative is to not recognise later versions, and require that +# this multilib spec is updated before it can be used with newer +# architecture versions. +- Regex: thumbv8\.[1-9]m\.main-none-unknown-eabi + MatchTags: [target=thumbv8.1m.main-none-unknown-eabi] +- Regex: thumbv8\.[1-9]m\.main-none-unknown-eabihf + MatchTags: [target=thumbv8.1m.main-none-unknown-eabihf] +... diff --git a/clang/test/Driver/baremetal.cpp b/clang/test/Driver/baremetal.cpp --- a/clang/test/Driver/baremetal.cpp +++ b/clang/test/Driver/baremetal.cpp @@ -118,9 +118,9 @@ // Verify that the bare metal driver does not include any host system paths: // CHECK-AARCH64-NO-HOST-INC: InstalledDir: [[INSTALLEDDIR:.+]] // CHECK-AARCH64-NO-HOST-INC: "-resource-dir" "[[RESOURCE:[^"]+]]" -// CHECK-AARCH64-NO-HOST-INC-SAME: "-internal-isystem" "[[INSTALLEDDIR]]{{[/\\]+}}..{{[/\\]+}}lib{{[/\\]+}}clang-runtimes{{[/\\]+}}aarch64-none-elf{{[/\\]+}}include{{[/\\]+}}c++{{[/\\]+}}v1" +// CHECK-AARCH64-NO-HOST-INC-SAME: "-internal-isystem" "[[INSTALLEDDIR]]{{[/\\]+}}..{{[/\\]+}}lib{{[/\\]+}}clang-runtimes{{[/\\]+[^"]*}}include{{[/\\]+}}c++{{[/\\]+}}v1" // CHECK-AARCH64-NO-HOST-INC-SAME: "-internal-isystem" "[[RESOURCE]]{{[/\\]+}}include" -// CHECK-AARCH64-NO-HOST-INC-SAME: "-internal-isystem" "[[INSTALLEDDIR]]{{[/\\]+}}..{{[/\\]+}}lib{{[/\\]+}}clang-runtimes{{[/\\]+}}aarch64-none-elf{{[/\\]+}}include" +// CHECK-AARCH64-NO-HOST-INC-SAME: "-internal-isystem" "[[INSTALLEDDIR]]{{[/\\]+}}..{{[/\\]+}}lib{{[/\\]+}}clang-runtimes{{[/\\]+[^"]*}}include" // RUN: %clang %s -### --target=riscv64-unknown-elf -o %t.out -L some/directory/user/asked/for \ // RUN: --sysroot=%S/Inputs/basic_riscv64_tree/riscv64-unknown-elf 2>&1 \ diff --git a/clang/test/Driver/lit.local.cfg b/clang/test/Driver/lit.local.cfg --- a/clang/test/Driver/lit.local.cfg +++ b/clang/test/Driver/lit.local.cfg @@ -1,7 +1,7 @@ from lit.llvm import llvm_config config.suffixes = ['.c', '.cpp', '.cppm', '.h', '.m', '.mm', '.S', '.s', '.f90', '.F90', '.f95', - '.cu', '.rs', '.cl', '.clcpp', '.hip', '.hipi', '.hlsl'] + '.cu', '.rs', '.cl', '.clcpp', '.hip', '.hipi', '.hlsl', '.yaml'] config.substitutions = list(config.substitutions) config.substitutions.insert(0, ('%clang_cc1',