Skip to content

Commit d98ced1

Browse files
authored
[Multilib] Custom flags YAML parsing (#110657)
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 ad38e24 commit d98ced1

File tree

3 files changed

+223
-11
lines changed

3 files changed

+223
-11
lines changed

clang/include/clang/Driver/Multilib.h

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

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

104+
namespace custom_flag {
105+
struct Declaration;
106+
using DeclarationPtr = std::shared_ptr<Declaration>;
107+
108+
struct ValueDetail {
109+
std::string Name;
110+
std::optional<SmallVector<std::string>> MacroDefines;
111+
DeclarationPtr Decl;
112+
};
113+
114+
struct Declaration {
115+
std::string Name;
116+
SmallVector<ValueDetail> ValueList;
117+
std::optional<size_t> DefaultValueIdx;
118+
};
119+
120+
static constexpr StringRef Prefix = "-fmultilib-flag=";
121+
} // namespace custom_flag
122+
104123
/// See also MultilibSetBuilder for combining multilibs into a set.
105124
class MultilibSet {
106125
public:
@@ -120,15 +139,18 @@ class MultilibSet {
120139

121140
private:
122141
multilib_list Multilibs;
123-
std::vector<FlagMatcher> FlagMatchers;
142+
SmallVector<FlagMatcher> FlagMatchers;
143+
SmallVector<custom_flag::DeclarationPtr> CustomFlagDecls;
124144
IncludeDirsFunc IncludeCallback;
125145
IncludeDirsFunc FilePathsCallback;
126146

127147
public:
128148
MultilibSet() = default;
129149
MultilibSet(multilib_list &&Multilibs,
130-
std::vector<FlagMatcher> &&FlagMatchers = {})
131-
: Multilibs(Multilibs), FlagMatchers(FlagMatchers) {}
150+
SmallVector<FlagMatcher> &&FlagMatchers = {},
151+
SmallVector<custom_flag::DeclarationPtr> &&CustomFlagDecls = {})
152+
: Multilibs(std::move(Multilibs)), FlagMatchers(std::move(FlagMatchers)),
153+
CustomFlagDecls(std::move(CustomFlagDecls)) {}
132154

133155
const multilib_list &getMultilibs() { return Multilibs; }
134156

clang/lib/Driver/Multilib.cpp

Lines changed: 65 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::DeclarationPtr> 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::DeclarationPtr)
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,63 @@ 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::DeclarationPtr,
286+
llvm::SmallSet<std::string, 32>> {
287+
static void mapping(llvm::yaml::IO &io, custom_flag::DeclarationPtr &V,
288+
llvm::SmallSet<std::string, 32> &NameSet) {
289+
assert(!V);
290+
V = std::make_shared<custom_flag::Declaration>();
291+
io.mapRequired("Name", V->Name);
292+
io.mapRequired("Values", V->ValueList, NameSet);
293+
std::string DefaultValueName;
294+
io.mapRequired("Default", DefaultValueName);
295+
296+
for (auto [Idx, Value] : llvm::enumerate(V->ValueList)) {
297+
Value.Decl = V;
298+
if (Value.Name == DefaultValueName) {
299+
assert(!V->DefaultValueIdx);
300+
V->DefaultValueIdx = Idx;
301+
}
302+
}
303+
}
304+
static std::string validate(IO &io, custom_flag::DeclarationPtr &V,
305+
llvm::SmallSet<std::string, 32> &) {
306+
if (V->Name.empty())
307+
return "custom flag requires a name";
308+
if (V->ValueList.empty())
309+
return "custom flag must have at least one value";
310+
if (!V->DefaultValueIdx)
311+
return "custom flag must have a default value";
312+
return {};
313+
}
314+
};
315+
258316
template <> struct llvm::yaml::MappingTraits<MultilibSetSerialization> {
259317
static void mapping(llvm::yaml::IO &io, MultilibSetSerialization &M) {
260318
io.mapRequired("MultilibVersion", M.MultilibVersion);
261319
io.mapRequired("Variants", M.Multilibs);
262320
io.mapOptional("Groups", M.Groups);
321+
llvm::SmallSet<std::string, 32> NameSet;
322+
io.mapOptionalWithContext("Flags", M.CustomFlagDeclarations, NameSet);
263323
io.mapOptional("Mappings", M.FlagMatchers);
264324
}
265325
static std::string validate(IO &io, MultilibSetSerialization &M) {
@@ -288,10 +348,6 @@ template <> struct llvm::yaml::MappingTraits<MultilibSetSerialization> {
288348
}
289349
};
290350

291-
LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization)
292-
LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibGroupSerialization)
293-
LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher)
294-
295351
llvm::ErrorOr<MultilibSet>
296352
MultilibSet::parseYaml(llvm::MemoryBufferRef Input,
297353
llvm::SourceMgr::DiagHandlerTy DiagHandler,
@@ -319,7 +375,8 @@ MultilibSet::parseYaml(llvm::MemoryBufferRef Input,
319375
}
320376
}
321377

322-
return MultilibSet(std::move(Multilibs), std::move(MS.FlagMatchers));
378+
return MultilibSet(std::move(Multilibs), std::move(MS.FlagMatchers),
379+
std::move(MS.CustomFlagDeclarations));
323380
}
324381

325382
LLVM_DUMP_METHOD void MultilibSet::dump() const {
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)