diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h index 80bce574a3b64..3f29bdf2ff23f 100644 --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -466,7 +466,7 @@ class Driver { /// ArgList. llvm::opt::InputArgList ParseArgStrings(ArrayRef Args, bool UseDriverMode, - bool &ContainsError); + bool &ContainsError) const; /// BuildInputs - Construct the list of inputs and their types from /// the given arguments. diff --git a/clang/include/clang/Driver/Multilib.h b/clang/include/clang/Driver/Multilib.h index 0a533ed2804e2..fc071ef48ca0f 100644 --- a/clang/include/clang/Driver/Multilib.h +++ b/clang/include/clang/Driver/Multilib.h @@ -168,9 +168,18 @@ class MultilibSet { const_iterator begin() const { return Multilibs.begin(); } const_iterator end() const { return Multilibs.end(); } + /// Process custom flags from \p Flags and returns an expanded flags list and + /// a list of macro defines. + /// Returns a pair where: + /// - first: the new flags list including custom flags after processing. + /// - second: the extra macro defines to be fed to the driver. + std::pair> + processCustomFlags(const Driver &D, const Multilib::flags_list &Flags) const; + /// Select compatible variants, \returns false if none are compatible bool select(const Driver &D, const Multilib::flags_list &Flags, - llvm::SmallVectorImpl &) const; + llvm::SmallVectorImpl &, + llvm::SmallVector * = nullptr) const; unsigned size() const { return Multilibs.size(); } diff --git a/clang/include/clang/Driver/ToolChain.h b/clang/include/clang/Driver/ToolChain.h index 701a1d25ca4c8..7d1d8feebf35e 100644 --- a/clang/include/clang/Driver/ToolChain.h +++ b/clang/include/clang/Driver/ToolChain.h @@ -686,6 +686,13 @@ class ToolChain { /// Add warning options that need to be passed to cc1 for this target. virtual void addClangWarningOptions(llvm::opt::ArgStringList &CC1Args) const; + // Get the list of extra macro defines requested by the multilib + // configuration. + virtual SmallVector + getMultilibMacroDefinesStr(llvm::opt::ArgList &Args) const { + return {}; + }; + // GetRuntimeLibType - Determine the runtime library type to use with the // given compilation arguments. virtual RuntimeLibType diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index eefbdca805739..3aaaf6e58a7c1 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -264,7 +264,8 @@ void Driver::setDriverMode(StringRef Value) { } InputArgList Driver::ParseArgStrings(ArrayRef ArgStrings, - bool UseDriverMode, bool &ContainsError) { + bool UseDriverMode, + bool &ContainsError) const { llvm::PrettyStackTraceString CrashInfo("Command line argument parsing"); ContainsError = false; @@ -1627,13 +1628,31 @@ Compilation *Driver::BuildCompilation(ArrayRef ArgList) { std::unique_ptr UArgs = std::make_unique(std::move(Args)); + // Owned by the host. + const ToolChain &TC = + getToolChain(*UArgs, computeTargetTriple(*this, TargetTriple, *UArgs)); + + { + SmallVector MultilibMacroDefinesStr = + TC.getMultilibMacroDefinesStr(*UArgs); + SmallVector MLMacroDefinesChar( + llvm::map_range(MultilibMacroDefinesStr, [&UArgs](const auto &S) { + return UArgs->MakeArgString(Twine("-D") + Twine(S)); + })); + bool MLContainsError; + auto MultilibMacroDefineList = + std::make_unique(ParseArgStrings( + MLMacroDefinesChar, /*UseDriverMode=*/false, MLContainsError)); + if (!MLContainsError) { + for (auto *Opt : *MultilibMacroDefineList) { + appendOneArg(*UArgs, Opt); + } + } + } + // Perform the default argument translations. DerivedArgList *TranslatedArgs = TranslateInputArgs(*UArgs); - // Owned by the host. - const ToolChain &TC = getToolChain( - *UArgs, computeTargetTriple(*this, TargetTriple, *UArgs)); - // Check if the environment version is valid except wasm case. llvm::Triple Triple = TC.getTriple(); if (!Triple.isWasm()) { diff --git a/clang/lib/Driver/Multilib.cpp b/clang/lib/Driver/Multilib.cpp index ccf747e90cb2c..efb99d3ffc752 100644 --- a/clang/lib/Driver/Multilib.cpp +++ b/clang/lib/Driver/Multilib.cpp @@ -92,12 +92,145 @@ MultilibSet &MultilibSet::FilterOut(FilterCallback F) { void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); } -bool MultilibSet::select(const Driver &D, const Multilib::flags_list &Flags, - llvm::SmallVectorImpl &Selected) const { - llvm::StringSet<> FlagSet(expandFlags(Flags)); +static void DiagnoseUnclaimedMultilibCustomFlags( + const Driver &D, const SmallVector &UnclaimedCustomFlagValues, + const SmallVector &CustomFlagDecls) { + struct EditDistanceInfo { + StringRef FlagValue; + unsigned EditDistance; + }; + const unsigned MaxEditDistance = 5; + + for (StringRef Unclaimed : UnclaimedCustomFlagValues) { + std::optional BestCandidate; + for (const auto &Decl : CustomFlagDecls) { + for (const auto &Value : Decl.ValueList) { + const std::string &FlagValueName = Value.Name; + unsigned EditDistance = + Unclaimed.edit_distance(FlagValueName, /*AllowReplacements=*/true, + /*MaxEditDistance=*/MaxEditDistance); + if (!BestCandidate || (EditDistance <= MaxEditDistance && + EditDistance < BestCandidate->EditDistance)) { + BestCandidate = {FlagValueName, EditDistance}; + } + } + } + if (!BestCandidate) + D.Diag(clang::diag::err_drv_unsupported_opt) + << (custom_flag::Prefix + Unclaimed).str(); + else + D.Diag(clang::diag::err_drv_unsupported_opt_with_suggestion) + << (custom_flag::Prefix + Unclaimed).str() + << (custom_flag::Prefix + BestCandidate->FlagValue).str(); + } +} + +namespace clang::driver::custom_flag { +// Map implemented using linear searches as the expected size is too small for +// the overhead of a search tree or a hash table. +class ValueNameToDetailMap { + SmallVector> Mapping; + +public: + template + ValueNameToDetailMap(It FlagDeclsBegin, It FlagDeclsEnd) { + for (auto DeclIt = FlagDeclsBegin; DeclIt != FlagDeclsEnd; ++DeclIt) { + const Declaration &Decl = *DeclIt; + for (const auto &Value : Decl.ValueList) + Mapping.emplace_back(Value.Name, &Value); + } + } + + const ValueDetail *get(StringRef Key) const { + auto Iter = llvm::find_if( + Mapping, [&](const auto &Pair) { return Pair.first == Key; }); + return Iter != Mapping.end() ? Iter->second : nullptr; + } +}; +} // namespace clang::driver::custom_flag + +std::pair> +MultilibSet::processCustomFlags(const Driver &D, + const Multilib::flags_list &Flags) const { + Multilib::flags_list Result; + SmallVector MacroDefines; + + // Custom flag values detected in the flags list + SmallVector ClaimedCustomFlagValues; + + // Arguments to -fmultilib-flag= that don't correspond to any valid + // custom flag value. An error will be printed out for each of these. + SmallVector UnclaimedCustomFlagValueStrs; + + const auto ValueNameToValueDetail = custom_flag::ValueNameToDetailMap( + CustomFlagDecls.begin(), CustomFlagDecls.end()); + + for (StringRef Flag : Flags) { + if (!Flag.starts_with(custom_flag::Prefix)) { + Result.push_back(Flag.str()); + continue; + } + + StringRef CustomFlagValueStr = Flag.substr(custom_flag::Prefix.size()); + const custom_flag::ValueDetail *Detail = + ValueNameToValueDetail.get(CustomFlagValueStr); + if (Detail) + ClaimedCustomFlagValues.push_back(Detail); + else + UnclaimedCustomFlagValueStrs.push_back(CustomFlagValueStr); + } + + // Set of custom flag declarations for which a value was passed in the flags + // list. This is used to, firstly, detect multiple values for the same flag + // declaration (in this case, the last one wins), and secondly, to detect + // which declarations had no value passed in (in this case, the default value + // is selected). + llvm::SmallPtrSet TriggeredCustomFlagDecls; + + // Detect multiple values for the same flag declaration. Last one wins. + for (auto *CustomFlagValue : llvm::reverse(ClaimedCustomFlagValues)) { + if (!TriggeredCustomFlagDecls.insert(CustomFlagValue->Decl).second) + continue; + Result.push_back(std::string(custom_flag::Prefix) + CustomFlagValue->Name); + if (CustomFlagValue->MacroDefines) + MacroDefines.append(CustomFlagValue->MacroDefines->begin(), + CustomFlagValue->MacroDefines->end()); + } + + // Detect flag declarations with no value passed in. Select default value. + for (const auto &Decl : CustomFlagDecls) { + if (TriggeredCustomFlagDecls.contains(&Decl)) + continue; + const custom_flag::ValueDetail &CustomFlagValue = + Decl.ValueList[*Decl.DefaultValueIdx]; + Result.push_back(std::string(custom_flag::Prefix) + CustomFlagValue.Name); + if (CustomFlagValue.MacroDefines) + MacroDefines.append(CustomFlagValue.MacroDefines->begin(), + CustomFlagValue.MacroDefines->end()); + } + + DiagnoseUnclaimedMultilibCustomFlags(D, UnclaimedCustomFlagValueStrs, + CustomFlagDecls); + + return {Result, MacroDefines}; +} + +bool MultilibSet::select( + const Driver &D, const Multilib::flags_list &Flags, + llvm::SmallVectorImpl &Selected, + llvm::SmallVector *CustomFlagMacroDefines) const { + auto [FlagsWithCustom, CFMacroDefines] = processCustomFlags(D, Flags); + llvm::StringSet<> FlagSet(expandFlags(FlagsWithCustom)); Selected.clear(); bool AnyErrors = false; + // Determining the list of macro defines depends only on the custom flags + // passed in. The library variants actually selected are not relevant in + // this. Therefore this assignment can take place before the selection + // happens. + if (CustomFlagMacroDefines) + *CustomFlagMacroDefines = std::move(CFMacroDefines); + // Decide which multilibs we're going to select at all. llvm::DenseSet ExclusiveGroupsSelected; for (const Multilib &M : llvm::reverse(Multilibs)) { diff --git a/clang/lib/Driver/ToolChains/BareMetal.cpp b/clang/lib/Driver/ToolChains/BareMetal.cpp index eecaaa9a42930..ffb1c6e34d603 100644 --- a/clang/lib/Driver/ToolChains/BareMetal.cpp +++ b/clang/lib/Driver/ToolChains/BareMetal.cpp @@ -162,9 +162,11 @@ static bool isPPCBareMetal(const llvm::Triple &Triple) { Triple.getEnvironment() == llvm::Triple::EABI; } -static void findMultilibsFromYAML(const ToolChain &TC, const Driver &D, - StringRef MultilibPath, const ArgList &Args, - DetectedMultilibs &Result) { +static void +findMultilibsFromYAML(const ToolChain &TC, const Driver &D, + StringRef MultilibPath, const ArgList &Args, + DetectedMultilibs &Result, + SmallVector &CustomFlagsMacroDefines) { llvm::ErrorOr> MB = D.getVFS().getBufferForFile(MultilibPath); if (!MB) @@ -175,7 +177,8 @@ static void findMultilibsFromYAML(const ToolChain &TC, const Driver &D, if (ErrorOrMultilibSet.getError()) return; Result.Multilibs = ErrorOrMultilibSet.get(); - if (Result.Multilibs.select(D, Flags, Result.SelectedMultilibs)) + if (Result.Multilibs.select(D, Flags, Result.SelectedMultilibs, + &CustomFlagsMacroDefines)) return; D.Diag(clang::diag::warn_drv_missing_multilib) << llvm::join(Flags, " "); std::stringstream ss; @@ -234,9 +237,13 @@ void BareMetal::findMultilibs(const Driver &D, const llvm::Triple &Triple, // If multilib.yaml is found, update sysroot so it doesn't use a target // specific suffix SysRoot = computeBaseSysRoot(D, /*IncludeTriple=*/false); - findMultilibsFromYAML(*this, D, *MultilibPath, Args, Result); + SmallVector CustomFlagMacroDefines; + findMultilibsFromYAML(*this, D, *MultilibPath, Args, Result, + CustomFlagMacroDefines); SelectedMultilibs = Result.SelectedMultilibs; Multilibs = Result.Multilibs; + MultilibMacroDefines.append(CustomFlagMacroDefines.begin(), + CustomFlagMacroDefines.end()); } else if (isRISCVBareMetal(Triple)) { if (findRISCVMultilibs(D, Triple, Args, Result)) { SelectedMultilibs = Result.SelectedMultilibs; @@ -551,3 +558,8 @@ SanitizerMask BareMetal::getSupportedSanitizers() const { } return Res; } + +SmallVector +BareMetal::getMultilibMacroDefinesStr(llvm::opt::ArgList &Args) const { + return MultilibMacroDefines; +} diff --git a/clang/lib/Driver/ToolChains/BareMetal.h b/clang/lib/Driver/ToolChains/BareMetal.h index 483b5efab5e6e..f6295bda0a6a2 100644 --- a/clang/lib/Driver/ToolChains/BareMetal.h +++ b/clang/lib/Driver/ToolChains/BareMetal.h @@ -70,12 +70,17 @@ class LLVM_LIBRARY_VISIBILITY BareMetal : public ToolChain { std::string computeSysRoot() const override; SanitizerMask getSupportedSanitizers() const override; + SmallVector + getMultilibMacroDefinesStr(llvm::opt::ArgList &Args) const override; + private: using OrderedMultilibs = llvm::iterator_range::const_reverse_iterator>; OrderedMultilibs getOrderedMultilibs() const; std::string SysRoot; + + SmallVector MultilibMacroDefines; }; } // namespace toolchains diff --git a/clang/test/Driver/baremetal-multilib-custom-flags.yaml b/clang/test/Driver/baremetal-multilib-custom-flags.yaml new file mode 100644 index 0000000000000..9c0320ea16117 --- /dev/null +++ b/clang/test/Driver/baremetal-multilib-custom-flags.yaml @@ -0,0 +1,81 @@ +# UNSUPPORTED: system-windows + +# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \ +# RUN: --target=thumbv8m.main-none-eabi -mfpu=none --sysroot= \ +# RUN: | FileCheck --check-prefix=CHECK-DEFAULT %s + +# CHECK-DEFAULT: "-cc1" "-triple" "thumbv8m.main-unknown-none-eabi" +# CHECK-DEFAULT-SAME: "-internal-isystem" "[[SYSROOT:[^"]*]]/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/nofp/include" +# CHECK-DEFAULT-NEXT: "-L[[SYSROOT]]/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/nofp/lib" + +# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \ +# RUN: --target=thumbv8m.main-none-eabi -mfpu=none -fmultilib-flag=no-multithreaded --sysroot= \ +# RUN: | FileCheck --check-prefix=CHECK-NOMULTI %s + +# CHECK-NOMULTI: "-cc1" "-triple" "thumbv8m.main-unknown-none-eabi" +# CHECK-NOMULTI-SAME: "-internal-isystem" "[[SYSROOT:[^"]*]]/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/nofp/include" +# CHECK-NOMULTI-NEXT: "-L[[SYSROOT]]/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/nofp/lib" + +# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \ +# RUN: --target=thumbv8m.main-none-eabi -mfpu=none -fmultilib-flag=multithreaded --sysroot= \ +# RUN: | FileCheck --check-prefix=CHECK-MULTI %s + +# CHECK-MULTI: "-cc1" "-triple" "thumbv8m.main-unknown-none-eabi" +# CHECK-MULTI-SAME: "-internal-isystem" "[[SYSROOT:[^"]*]]/bin/../lib/clang-runtimes/arm-none-eabi/multithreaded/thumb/v8-m.main/nofp/include" +# CHECK-MULTI-NEXT: "-L[[SYSROOT]]/bin/../lib/clang-runtimes/arm-none-eabi/multithreaded/thumb/v8-m.main/nofp/lib" + +# RUN: not %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \ +# RUN: --target=thumbv8m.main-none-eabi -mfpu=none -fmultilib-flag=singlethreaded -fmultilib-flag=no-io --sysroot= \ +# RUN: | FileCheck --check-prefix=CHECK-ERROR %s +# CHECK-ERROR-DAG: error: unsupported option '-fmultilib-flag=singlethreaded' +# CHECK-ERROR-DAG: error: unsupported option '-fmultilib-flag=no-io'; did you mean '-fmultilib-flag=io-none'? + +# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \ +# RUN: --target=thumbv8m.main-none-eabi -mfpu=none -print-multi-lib --sysroot= \ +# RUN: | FileCheck --check-prefix=CHECK-PRINT-MULTI-LIB %s +# CHECK-PRINT-MULTI-LIB: arm-none-eabi/thumb/v8-m.main/nofp;@-target=thumbv8m.main-unknown-none-eabi@mfpu=none@fmultilib-flag=no-multithreaded +# CHECK-PRINT-MULTI-LIB: arm-none-eabi/multithreaded/thumb/v8-m.main/nofp;@-target=thumbv8m.main-unknown-none-eabi@mfpu=none@fmultilib-flag=multithreaded + +# RUN: %clang --target=arm-none-eabi --multi-lib-config=%s -x c %s -fmultilib-flag=no-multithreaded -### -o /dev/null 2>&1 \ +# RUN: | FileCheck --check-prefix=CHECK-MACRODEFINES-NOMULTI %s +# CHECK-MACRODEFINES-NOMULTI: "-D" "__SINGLE_THREAD__" + +# RUN: %clang --target=arm-none-eabi --multi-lib-config=%s -x c %s -fmultilib-flag=io-semihosting -### -o /dev/null 2>&1 \ +# RUN: | FileCheck --check-prefix=CHECK-MACRODEFINES-IO-SEMIHOSTING %s +# CHECK-MACRODEFINES-IO-SEMIHOSTING: "-D" "SEMIHOSTING" + +# RUN: %clang --target=arm-none-eabi --multi-lib-config=%s -x c %s -fmultilib-flag=io-linux-syscalls -### -o /dev/null 2>&1 \ +# RUN: | FileCheck --check-prefix=CHECK-MACRODEFINES-IO-LINUX %s +# CHECK-MACRODEFINES-IO-LINUX: "-D" "LINUX_SYSCALLS" +# CHECK-MACRODEFINES-IO-LINUX-SAME: "-D" "HOSTED" + +--- +MultilibVersion: 1.0 + +Groups: +- Name: stdlib + Type: Exclusive + +Variants: +- Dir: arm-none-eabi/thumb/v8-m.main/nofp + Flags: [--target=thumbv8m.main-unknown-none-eabi, -mfpu=none, -fmultilib-flag=no-multithreaded] + Group: stdlib +- Dir: arm-none-eabi/multithreaded/thumb/v8-m.main/nofp + Flags: [--target=thumbv8m.main-unknown-none-eabi, -mfpu=none, -fmultilib-flag=multithreaded] + Group: stdlib + +Flags: + - Name: multithreading + Values: + - Name: no-multithreaded + MacroDefines: [__SINGLE_THREAD__] + - Name: multithreaded + Default: no-multithreaded + - Name: io + Values: + - Name: io-none + - Name: io-semihosting + MacroDefines: [SEMIHOSTING] + - Name: io-linux-syscalls + MacroDefines: [LINUX_SYSCALLS, HOSTED] + Default: io-none \ No newline at end of file