Skip to content

Commit f2525ef

Browse files
committed
[Multilib] Custom flags processing for library selection
Select library variants in the multilib system using the flags passed following the '-fmultilib-flag=' format. Multilib flags that were not passed in the command-line have their default value fed into the library selection mechanism. A warning is shown if the flag's value name is invalid. If the wrong name is close enough to any valid one, according to edit distance, the closest valid value name is suggested. Details about this change can be found in this thread: https://discourse.llvm.org/t/rfc-multilib-custom-flags/81058
1 parent ee2d268 commit f2525ef

File tree

5 files changed

+201
-6
lines changed

5 files changed

+201
-6
lines changed

clang/include/clang/Basic/DiagnosticDriverKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ def err_drv_no_such_file_with_suggestion : Error<
1414
def err_drv_unsupported_opt : Error<"unsupported option '%0'">;
1515
def err_drv_unsupported_opt_with_suggestion : Error<
1616
"unsupported option '%0'; did you mean '%1'?">;
17+
def warn_drv_unsupported_opt : Warning<
18+
"unsupported option '%0'">,
19+
InGroup<UnusedCommandLineArgument>;
20+
def warn_drv_unsupported_opt_with_suggestion : Warning<
21+
"unsupported option '%0'; did you mean '%1'?">,
22+
InGroup<UnusedCommandLineArgument>;
1723
def err_drv_unsupported_opt_for_target : Error<
1824
"unsupported option '%0' for target '%1'">;
1925
def err_drv_unsupported_opt_for_language_mode : Error<

clang/include/clang/Driver/Multilib.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ class MultilibSet {
163163
const_iterator begin() const { return Multilibs.begin(); }
164164
const_iterator end() const { return Multilibs.end(); }
165165

166+
Multilib::flags_list
167+
processCustomFlags(const Driver &D, const Multilib::flags_list &Flags) const;
168+
166169
/// Select compatible variants, \returns false if none are compatible
167170
bool select(const Driver &D, const Multilib::flags_list &Flags,
168171
llvm::SmallVectorImpl<Multilib> &) const;

clang/lib/Driver/Driver.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2324,9 +2324,7 @@ bool Driver::HandleImmediateArgs(Compilation &C) {
23242324
}
23252325

23262326
if (C.getArgs().hasArg(options::OPT_print_multi_lib)) {
2327-
for (const Multilib &Multilib : TC.getMultilibs())
2328-
if (!Multilib.isError())
2329-
llvm::outs() << Multilib << "\n";
2327+
llvm::outs() << TC.getMultilibs();
23302328
return false;
23312329
}
23322330

clang/lib/Driver/Multilib.cpp

Lines changed: 134 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "clang/Driver/Driver.h"
1313
#include "llvm/ADT/DenseSet.h"
1414
#include "llvm/ADT/SmallSet.h"
15+
#include "llvm/ADT/SmallString.h"
1516
#include "llvm/ADT/StringRef.h"
1617
#include "llvm/Support/Compiler.h"
1718
#include "llvm/Support/Error.h"
@@ -95,9 +96,113 @@ MultilibSet &MultilibSet::FilterOut(FilterCallback F) {
9596

9697
void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); }
9798

99+
static void WarnUnclaimedMultilibCustomFlags(
100+
const Driver &D, const SmallVector<StringRef> &UnclaimedCustomFlagValues,
101+
const SmallVector<custom_flag::CustomFlagDeclarationPtr> &CustomFlagDecls) {
102+
struct EditDistanceInfo {
103+
StringRef FlagValue;
104+
unsigned EditDistance;
105+
};
106+
const unsigned MaxEditDistance = 5;
107+
108+
for (StringRef Unclaimed : UnclaimedCustomFlagValues) {
109+
std::optional<EditDistanceInfo> BestCandidate;
110+
for (const auto &Decl : CustomFlagDecls) {
111+
for (const auto &Value : Decl->ValueList) {
112+
const std::string &FlagValueName = Value.Name;
113+
unsigned EditDistance =
114+
Unclaimed.edit_distance(FlagValueName, /*AllowReplacements=*/true,
115+
/*MaxEditDistance=*/MaxEditDistance);
116+
if (!BestCandidate || (EditDistance <= MaxEditDistance &&
117+
EditDistance < BestCandidate->EditDistance)) {
118+
BestCandidate = {FlagValueName, EditDistance};
119+
}
120+
}
121+
}
122+
if (!BestCandidate)
123+
D.Diag(clang::diag::warn_drv_unsupported_opt)
124+
<< (custom_flag::Prefix + Unclaimed).str();
125+
else
126+
D.Diag(clang::diag::warn_drv_unsupported_opt_with_suggestion)
127+
<< (custom_flag::Prefix + Unclaimed).str()
128+
<< (custom_flag::Prefix + BestCandidate->FlagValue).str();
129+
}
130+
}
131+
132+
namespace clang::driver::custom_flag {
133+
class ValueNameToDetailMap {
134+
SmallVector<std::pair<StringRef, const CustomFlagValueDetail *>> Mapping;
135+
136+
public:
137+
template <typename It>
138+
ValueNameToDetailMap(It FlagDeclsBegin, It FlagDeclsEnd) {
139+
for (auto DeclIt = FlagDeclsBegin; DeclIt != FlagDeclsEnd; ++DeclIt) {
140+
const CustomFlagDeclarationPtr &Decl = *DeclIt;
141+
for (const auto &Value : Decl->ValueList)
142+
Mapping.emplace_back(Value.Name, &Value);
143+
}
144+
}
145+
146+
const CustomFlagValueDetail *get(StringRef Key) const {
147+
auto Iter = llvm::find_if(
148+
Mapping, [&](const auto &Pair) { return Pair.first == Key; });
149+
return Iter != Mapping.end() ? Iter->second : nullptr;
150+
}
151+
};
152+
} // namespace clang::driver::custom_flag
153+
154+
Multilib::flags_list
155+
MultilibSet::processCustomFlags(const Driver &D,
156+
const Multilib::flags_list &Flags) const {
157+
Multilib::flags_list Result;
158+
SmallVector<const custom_flag::CustomFlagValueDetail *>
159+
ClaimedCustomFlagValues;
160+
SmallVector<StringRef> UnclaimedCustomFlagValueStrs;
161+
162+
const auto ValueNameToValueDetail = custom_flag::ValueNameToDetailMap(
163+
CustomFlagDecls.begin(), CustomFlagDecls.end());
164+
165+
for (StringRef Flag : Flags) {
166+
if (!Flag.starts_with(custom_flag::Prefix)) {
167+
Result.push_back(Flag.str());
168+
continue;
169+
}
170+
171+
StringRef CustomFlagValueStr = Flag.substr(custom_flag::Prefix.size());
172+
const custom_flag::CustomFlagValueDetail *Detail =
173+
ValueNameToValueDetail.get(CustomFlagValueStr);
174+
if (Detail)
175+
ClaimedCustomFlagValues.push_back(Detail);
176+
else
177+
UnclaimedCustomFlagValueStrs.push_back(CustomFlagValueStr);
178+
}
179+
180+
llvm::SmallSet<custom_flag::CustomFlagDeclarationPtr, 32>
181+
TriggeredCustomFlagDecls;
182+
183+
for (auto *CustomFlagValue : llvm::reverse(ClaimedCustomFlagValues)) {
184+
if (!TriggeredCustomFlagDecls.insert(CustomFlagValue->Decl).second)
185+
continue;
186+
Result.push_back(std::string(custom_flag::Prefix) + CustomFlagValue->Name);
187+
}
188+
189+
for (const auto &Decl : CustomFlagDecls) {
190+
if (TriggeredCustomFlagDecls.contains(Decl))
191+
continue;
192+
Result.push_back(std::string(custom_flag::Prefix) +
193+
Decl->ValueList[Decl->DefaultValueIdx].Name);
194+
}
195+
196+
WarnUnclaimedMultilibCustomFlags(D, UnclaimedCustomFlagValueStrs,
197+
CustomFlagDecls);
198+
199+
return Result;
200+
}
201+
98202
bool MultilibSet::select(const Driver &D, const Multilib::flags_list &Flags,
99203
llvm::SmallVectorImpl<Multilib> &Selected) const {
100-
llvm::StringSet<> FlagSet(expandFlags(Flags));
204+
Multilib::flags_list FlagsWithCustom = processCustomFlags(D, Flags);
205+
llvm::StringSet<> FlagSet(expandFlags(FlagsWithCustom));
101206
Selected.clear();
102207
bool AnyErrors = false;
103208

@@ -387,8 +492,34 @@ LLVM_DUMP_METHOD void MultilibSet::dump() const {
387492
}
388493

389494
void MultilibSet::print(raw_ostream &OS) const {
390-
for (const auto &M : *this)
391-
OS << M << "\n";
495+
const auto ValueNameToValueDetail = custom_flag::ValueNameToDetailMap(
496+
CustomFlagDecls.begin(), CustomFlagDecls.end());
497+
498+
for (const auto &M : *this) {
499+
if (M.isError())
500+
continue;
501+
llvm::SmallString<128> Buf;
502+
llvm::raw_svector_ostream StrOS(Buf);
503+
504+
M.print(StrOS);
505+
506+
for (StringRef Flag : M.flags()) {
507+
if (!Flag.starts_with(custom_flag::Prefix))
508+
continue;
509+
510+
StringRef CustomFlagValueStr = Flag.substr(custom_flag::Prefix.size());
511+
const custom_flag::CustomFlagValueDetail *Detail =
512+
ValueNameToValueDetail.get(CustomFlagValueStr);
513+
514+
if (!Detail || !Detail->ExtraBuildArgs)
515+
continue;
516+
517+
for (StringRef Arg : *Detail->ExtraBuildArgs)
518+
StrOS << '@' << (Arg[0] == '-' ? Arg.substr(1) : Arg);
519+
}
520+
521+
OS << StrOS.str() << '\n';
522+
}
392523
}
393524

394525
raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
2+
# RUN: --target=thumbv8m.main-none-eabi -mfpu=none --sysroot= \
3+
# RUN: | FileCheck --check-prefix=CHECK-DEFAULT %s
4+
5+
# CHECK-DEFAULT: "-cc1" "-triple" "thumbv8m.main-unknown-none-eabi"
6+
# CHECK-DEFAULT-SAME: "-internal-isystem" "[[SYSROOT:[^"]*]]/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/nofp/include"
7+
# CHECK-DEFAULT-NEXT: "-L[[SYSROOT]]/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/nofp/lib"
8+
9+
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
10+
# RUN: --target=thumbv8m.main-none-eabi -mfpu=none -fmultilib-flag=no-multithreaded --sysroot= \
11+
# RUN: | FileCheck --check-prefix=CHECK-NOMULTI %s
12+
13+
# CHECK-NOMULTI: "-cc1" "-triple" "thumbv8m.main-unknown-none-eabi"
14+
# CHECK-NOMULTI-SAME: "-internal-isystem" "[[SYSROOT:[^"]*]]/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/nofp/include"
15+
# CHECK-NOMULTI-NEXT: "-L[[SYSROOT]]/bin/../lib/clang-runtimes/arm-none-eabi/thumb/v8-m.main/nofp/lib"
16+
17+
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
18+
# RUN: --target=thumbv8m.main-none-eabi -mfpu=none -fmultilib-flag=multithreaded --sysroot= \
19+
# RUN: | FileCheck --check-prefix=CHECK-MULTI %s
20+
21+
# CHECK-MULTI: "-cc1" "-triple" "thumbv8m.main-unknown-none-eabi"
22+
# CHECK-MULTI-SAME: "-internal-isystem" "[[SYSROOT:[^"]*]]/bin/../lib/clang-runtimes/arm-none-eabi/multithreaded/thumb/v8-m.main/nofp/include"
23+
# CHECK-MULTI-NEXT: "-L[[SYSROOT]]/bin/../lib/clang-runtimes/arm-none-eabi/multithreaded/thumb/v8-m.main/nofp/lib"
24+
25+
# RUN: %clang --multi-lib-config=%s -no-canonical-prefixes -x c %s -### -o /dev/null 2>&1 \
26+
# RUN: --target=thumbv8m.main-none-eabi -mfpu=none -fmultilib-flag=singlethreaded -fmultilib-flag=no-io --sysroot= \
27+
# RUN: | FileCheck --check-prefix=CHECK-WARNING %s
28+
# CHECK-WARNING-DAG: warning: unsupported option '-fmultilib-flag=singlethreaded'
29+
# CHECK-WARNING-DAG: warning: unsupported option '-fmultilib-flag=no-io'; did you mean '-fmultilib-flag=io-none'?
30+
31+
---
32+
MultilibVersion: 1.0
33+
34+
Groups:
35+
- Name: stdlib
36+
Type: Exclusive
37+
38+
Variants:
39+
- Dir: arm-none-eabi/thumb/v8-m.main/nofp
40+
Flags: [--target=thumbv8m.main-unknown-none-eabi, -mfpu=none, -fmultilib-flag=no-multithreaded]
41+
Group: stdlib
42+
- Dir: arm-none-eabi/multithreaded/thumb/v8-m.main/nofp
43+
Flags: [--target=thumbv8m.main-unknown-none-eabi, -mfpu=none, -fmultilib-flag=multithreaded]
44+
Group: stdlib
45+
46+
Flags:
47+
- Name: multithreading
48+
Values:
49+
- Name: no-multithreaded
50+
- Name: multithreaded
51+
Default: no-multithreaded
52+
- Name: io
53+
Values:
54+
- Name: io-none
55+
- Name: io-semihosting
56+
- Name: io-linux-syscalls
57+
Default: io-none

0 commit comments

Comments
 (0)