Skip to content

Commit 85fdf50

Browse files
authored
[Multilib] Custom flags YAML parsing (#122903)
This patch is the first step to extend the current multilib system to support the selection of library variants which do not correspond to existing command-line options. Proposal can be found in https://discourse.llvm.org/t/rfc-multilib-custom-flags/81058 The multilib mechanism supports libraries that target code generation or language options such as `--target`, `-mcpu`, `-mfpu`, `-mbranch-protection`. However, some library variants are particular to features that do not correspond to any command-line options. Examples include variants for multithreading and semihosting. This work introduces a way to instruct the multilib system to consider these features in library selection. This particular patch comprises a new section in `multilib.yaml` to declare flags for which no option exists. Henceforth this sort of flag will be called `custom flag` for clarity. The `multilib.yaml` file will have a new section called Flags which contains the declarations of the target’s custom flags: ```yaml Flags: - Name: multithreaded 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=1] Default: io-none ``` - Name: the name to categorize a flag. - Values: a list of possible values. - Default: it specifies which value this flag should take if not specified in the command-line invocation. It must be one value from the Values field. Each flag Value follows this description: - Name (required): the name of the custom flag value (string). This is the string to be used in `-fmultilib-flag=<string>`. - MacroDefines (optional): a list of strings to be used as macro definitions. Each string is fed into the driver as ``-D<string>``. A Default value is useful to save users from specifying custom flags that have a most commonly used value. The namespace of flag values is common across all flags. This means that flag values must be unique.
1 parent bd76824 commit 85fdf50

File tree

3 files changed

+264
-11
lines changed

3 files changed

+264
-11
lines changed

clang/include/clang/Driver/Multilib.h

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,30 @@ class Multilib {
101101

102102
raw_ostream &operator<<(raw_ostream &OS, const Multilib &M);
103103

104+
namespace custom_flag {
105+
struct Declaration;
106+
107+
struct ValueDetail {
108+
std::string Name;
109+
std::optional<SmallVector<std::string>> MacroDefines;
110+
Declaration *Decl;
111+
};
112+
113+
struct Declaration {
114+
std::string Name;
115+
SmallVector<ValueDetail> ValueList;
116+
std::optional<size_t> DefaultValueIdx;
117+
118+
Declaration() = default;
119+
Declaration(const Declaration &);
120+
Declaration(Declaration &&);
121+
Declaration &operator=(const Declaration &);
122+
Declaration &operator=(Declaration &&);
123+
};
124+
125+
static constexpr StringRef Prefix = "-fmultilib-flag=";
126+
} // namespace custom_flag
127+
104128
/// See also MultilibSetBuilder for combining multilibs into a set.
105129
class MultilibSet {
106130
public:
@@ -120,15 +144,18 @@ class MultilibSet {
120144

121145
private:
122146
multilib_list Multilibs;
123-
std::vector<FlagMatcher> FlagMatchers;
147+
SmallVector<FlagMatcher> FlagMatchers;
148+
SmallVector<custom_flag::Declaration> CustomFlagDecls;
124149
IncludeDirsFunc IncludeCallback;
125150
IncludeDirsFunc FilePathsCallback;
126151

127152
public:
128153
MultilibSet() = default;
129154
MultilibSet(multilib_list &&Multilibs,
130-
std::vector<FlagMatcher> &&FlagMatchers = {})
131-
: Multilibs(Multilibs), FlagMatchers(FlagMatchers) {}
155+
SmallVector<FlagMatcher> &&FlagMatchers = {},
156+
SmallVector<custom_flag::Declaration> &&CustomFlagDecls = {})
157+
: Multilibs(std::move(Multilibs)), FlagMatchers(std::move(FlagMatchers)),
158+
CustomFlagDecls(std::move(CustomFlagDecls)) {}
132159

133160
const multilib_list &getMultilibs() { return Multilibs; }
134161

clang/lib/Driver/Multilib.cpp

Lines changed: 101 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "clang/Basic/LLVM.h"
1111
#include "clang/Driver/Driver.h"
1212
#include "llvm/ADT/DenseSet.h"
13+
#include "llvm/ADT/SmallSet.h"
1314
#include "llvm/ADT/StringRef.h"
1415
#include "llvm/Support/Compiler.h"
1516
#include "llvm/Support/ErrorHandling.h"
@@ -201,13 +202,20 @@ struct MultilibGroupSerialization {
201202

202203
struct MultilibSetSerialization {
203204
llvm::VersionTuple MultilibVersion;
204-
std::vector<MultilibGroupSerialization> Groups;
205-
std::vector<MultilibSerialization> Multilibs;
206-
std::vector<MultilibSet::FlagMatcher> FlagMatchers;
205+
SmallVector<MultilibGroupSerialization> Groups;
206+
SmallVector<MultilibSerialization> Multilibs;
207+
SmallVector<MultilibSet::FlagMatcher> FlagMatchers;
208+
SmallVector<custom_flag::Declaration> CustomFlagDeclarations;
207209
};
208210

209211
} // end anonymous namespace
210212

213+
LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization)
214+
LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibGroupSerialization)
215+
LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher)
216+
LLVM_YAML_IS_SEQUENCE_VECTOR(custom_flag::ValueDetail)
217+
LLVM_YAML_IS_SEQUENCE_VECTOR(custom_flag::Declaration)
218+
211219
template <> struct llvm::yaml::MappingTraits<MultilibSerialization> {
212220
static void mapping(llvm::yaml::IO &io, MultilibSerialization &V) {
213221
io.mapOptional("Dir", V.Dir);
@@ -255,11 +263,61 @@ template <> struct llvm::yaml::MappingTraits<MultilibSet::FlagMatcher> {
255263
}
256264
};
257265

266+
template <>
267+
struct llvm::yaml::MappingContextTraits<custom_flag::ValueDetail,
268+
llvm::SmallSet<std::string, 32>> {
269+
static void mapping(llvm::yaml::IO &io, custom_flag::ValueDetail &V,
270+
llvm::SmallSet<std::string, 32> &) {
271+
io.mapRequired("Name", V.Name);
272+
io.mapOptional("MacroDefines", V.MacroDefines);
273+
}
274+
static std::string validate(IO &io, custom_flag::ValueDetail &V,
275+
llvm::SmallSet<std::string, 32> &NameSet) {
276+
if (V.Name.empty())
277+
return "custom flag value requires a name";
278+
if (!NameSet.insert(V.Name).second)
279+
return "duplicate custom flag value name: \"" + V.Name + "\"";
280+
return {};
281+
}
282+
};
283+
284+
template <>
285+
struct llvm::yaml::MappingContextTraits<custom_flag::Declaration,
286+
llvm::SmallSet<std::string, 32>> {
287+
static void mapping(llvm::yaml::IO &io, custom_flag::Declaration &V,
288+
llvm::SmallSet<std::string, 32> &NameSet) {
289+
io.mapRequired("Name", V.Name);
290+
io.mapRequired("Values", V.ValueList, NameSet);
291+
std::string DefaultValueName;
292+
io.mapRequired("Default", DefaultValueName);
293+
294+
for (auto [Idx, Value] : llvm::enumerate(V.ValueList)) {
295+
Value.Decl = &V;
296+
if (Value.Name == DefaultValueName) {
297+
assert(!V.DefaultValueIdx);
298+
V.DefaultValueIdx = Idx;
299+
}
300+
}
301+
}
302+
static std::string validate(IO &io, custom_flag::Declaration &V,
303+
llvm::SmallSet<std::string, 32> &) {
304+
if (V.Name.empty())
305+
return "custom flag requires a name";
306+
if (V.ValueList.empty())
307+
return "custom flag must have at least one value";
308+
if (!V.DefaultValueIdx)
309+
return "custom flag must have a default value";
310+
return {};
311+
}
312+
};
313+
258314
template <> struct llvm::yaml::MappingTraits<MultilibSetSerialization> {
259315
static void mapping(llvm::yaml::IO &io, MultilibSetSerialization &M) {
260316
io.mapRequired("MultilibVersion", M.MultilibVersion);
261317
io.mapRequired("Variants", M.Multilibs);
262318
io.mapOptional("Groups", M.Groups);
319+
llvm::SmallSet<std::string, 32> NameSet;
320+
io.mapOptionalWithContext("Flags", M.CustomFlagDeclarations, NameSet);
263321
io.mapOptional("Mappings", M.FlagMatchers);
264322
}
265323
static std::string validate(IO &io, MultilibSetSerialization &M) {
@@ -288,10 +346,6 @@ template <> struct llvm::yaml::MappingTraits<MultilibSetSerialization> {
288346
}
289347
};
290348

291-
LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization)
292-
LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibGroupSerialization)
293-
LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher)
294-
295349
llvm::ErrorOr<MultilibSet>
296350
MultilibSet::parseYaml(llvm::MemoryBufferRef Input,
297351
llvm::SourceMgr::DiagHandlerTy DiagHandler,
@@ -319,7 +373,8 @@ MultilibSet::parseYaml(llvm::MemoryBufferRef Input,
319373
}
320374
}
321375

322-
return MultilibSet(std::move(Multilibs), std::move(MS.FlagMatchers));
376+
return MultilibSet(std::move(Multilibs), std::move(MS.FlagMatchers),
377+
std::move(MS.CustomFlagDeclarations));
323378
}
324379

325380
LLVM_DUMP_METHOD void MultilibSet::dump() const {
@@ -335,3 +390,41 @@ raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) {
335390
MS.print(OS);
336391
return OS;
337392
}
393+
394+
namespace clang::driver::custom_flag {
395+
Declaration::Declaration(const Declaration &Other)
396+
: Name(Other.Name), ValueList(Other.ValueList),
397+
DefaultValueIdx(Other.DefaultValueIdx) {
398+
for (ValueDetail &Detail : ValueList)
399+
Detail.Decl = this;
400+
}
401+
402+
Declaration::Declaration(Declaration &&Other)
403+
: Name(std::move(Other.Name)), ValueList(std::move(Other.ValueList)),
404+
DefaultValueIdx(std::move(Other.DefaultValueIdx)) {
405+
for (ValueDetail &Detail : ValueList)
406+
Detail.Decl = this;
407+
}
408+
409+
Declaration &Declaration::operator=(const Declaration &Other) {
410+
if (this == &Other)
411+
return *this;
412+
Name = Other.Name;
413+
ValueList = Other.ValueList;
414+
DefaultValueIdx = Other.DefaultValueIdx;
415+
for (ValueDetail &Detail : ValueList)
416+
Detail.Decl = this;
417+
return *this;
418+
}
419+
420+
Declaration &Declaration::operator=(Declaration &&Other) {
421+
if (this == &Other)
422+
return *this;
423+
Name = std::move(Other.Name);
424+
ValueList = std::move(Other.ValueList);
425+
DefaultValueIdx = std::move(Other.DefaultValueIdx);
426+
for (ValueDetail &Detail : ValueList)
427+
Detail.Decl = this;
428+
return *this;
429+
}
430+
} // namespace clang::driver::custom_flag
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# RUN: split-file %s %t
2+
3+
# RUN: %clang --target=arm-none-eabi --multi-lib-config=%t/multilib-without-macro-defines.yaml %s -### -o /dev/null 2>&1 \
4+
# RUN: | FileCheck %s
5+
# RUN: %clang --target=arm-none-eabi --multi-lib-config=%t/multilib-with-macro-defines.yaml %s -### -o /dev/null 2>&1 \
6+
# RUN: | FileCheck %s
7+
# CHECK-NOT: error:
8+
9+
# RUN: %clang --target=arm-none-eabi --multi-lib-config=%t/missing-flag-name.yaml %s -### -o /dev/null 2>&1 \
10+
# RUN: | FileCheck %s --check-prefix=CHECK-MISSING-FLAG-NAME
11+
# CHECK-MISSING-FLAG-NAME: error: custom flag requires a name
12+
13+
# RUN: %clang --target=arm-none-eabi --multi-lib-config=%t/missing-flag-values.yaml %s -### -o /dev/null 2>&1 \
14+
# RUN: | FileCheck %s --check-prefix=CHECK-MISSING-FLAG-VALUES
15+
# CHECK-MISSING-FLAG-VALUES: error: custom flag must have at least one value
16+
17+
# RUN: %clang --target=arm-none-eabi --multi-lib-config=%t/missing-flag-value-default.yaml %s -### -o /dev/null 2>&1 \
18+
# RUN: | FileCheck %s --check-prefix=CHECK-MISSING-FLAG-VALUE-DEFAULT
19+
# CHECK-MISSING-FLAG-VALUE-DEFAULT: error: custom flag must have a default value
20+
21+
# RUN: %clang --target=arm-none-eabi --multi-lib-config=%t/missing-flag-value-name.yaml %s -### -o /dev/null 2>&1 \
22+
# RUN: | FileCheck %s --check-prefix=CHECK-MISSING-FLAG-VALUE-NAME
23+
# CHECK-MISSING-FLAG-VALUE-NAME: error: custom flag value requires a name
24+
25+
# RUN: %clang --target=arm-none-eabi --multi-lib-config=%t/duplicate-flag-value-name.yaml %s -### -o /dev/null 2>&1 \
26+
# RUN: | FileCheck %s --check-prefix=CHECK-DUPLICATE-FLAG-VALUE-NAME
27+
# CHECK-DUPLICATE-FLAG-VALUE-NAME: error: duplicate custom flag value name: "value-name"
28+
# CHECK-DUPLICATE-FLAG-VALUE-NAME-NEXT: - Name: value-name
29+
30+
#--- multilib-without-macro-defines.yaml
31+
---
32+
MultilibVersion: 1.0
33+
34+
Variants:
35+
- Dir: libc
36+
Flags: [-fmultilib-flag=a]
37+
38+
Flags:
39+
- Name: flag
40+
Values:
41+
- Name: a
42+
- Name: b
43+
Default: a
44+
45+
#--- multilib-with-macro-defines.yaml
46+
---
47+
MultilibVersion: 1.0
48+
49+
Variants:
50+
- Dir: libc
51+
Flags: [-fmultilib-flag=a]
52+
53+
Flags:
54+
- Name: flag
55+
Values:
56+
- Name: a
57+
MacroDefines: [FEATURE_A]
58+
- Name: b
59+
MacroDefines: [FEATURE_B]
60+
Default: a
61+
62+
#--- missing-flag-name.yaml
63+
---
64+
MultilibVersion: 1.0
65+
66+
Variants:
67+
- Dir: libc
68+
Flags: [-fmultilib-flag=a]
69+
70+
Flags:
71+
- Values:
72+
- Name: a
73+
Default: a
74+
75+
#--- missing-flag-values.yaml
76+
---
77+
MultilibVersion: 1.0
78+
79+
Variants:
80+
- Dir: libc
81+
Flags: [-fmultilib-flag=a]
82+
83+
Flags:
84+
- Name: flag
85+
Values:
86+
Default: a
87+
88+
#--- missing-flag-value-default.yaml
89+
---
90+
MultilibVersion: 1.0
91+
92+
Variants:
93+
- Dir: libc
94+
Flags: [-fmultilib-flag=a]
95+
96+
Flags:
97+
- Name: flag
98+
Values:
99+
- Name: a
100+
Default:
101+
102+
#--- missing-flag-value-name.yaml
103+
---
104+
MultilibVersion: 1.0
105+
106+
Variants:
107+
- Dir: libc
108+
Flags: [-fmultilib-flag=a]
109+
110+
Flags:
111+
- Name: flag
112+
Values:
113+
- Name:
114+
Default: a
115+
116+
#--- duplicate-flag-value-name.yaml
117+
---
118+
MultilibVersion: 1.0
119+
120+
Variants:
121+
- Dir: libc
122+
Flags: [-fmultilib-flag=value-name]
123+
124+
Flags:
125+
- Name: a
126+
Values:
127+
- Name: value-name
128+
- Name: value-a
129+
Default: value-name
130+
- Name: b
131+
Values:
132+
- Name: value-name
133+
Default: value-name

0 commit comments

Comments
 (0)