1//===- Multilib.cpp - Multilib Implementation -----------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "clang/Driver/Multilib.h"
10#include "clang/Basic/LLVM.h"
11#include "clang/Driver/Driver.h"
12#include "llvm/ADT/DenseSet.h"
13#include "llvm/ADT/SmallSet.h"
14#include "llvm/ADT/StringRef.h"
15#include "llvm/Support/Compiler.h"
16#include "llvm/Support/ErrorHandling.h"
17#include "llvm/Support/Regex.h"
18#include "llvm/Support/VersionTuple.h"
19#include "llvm/Support/YAMLParser.h"
20#include "llvm/Support/YAMLTraits.h"
21#include "llvm/Support/raw_ostream.h"
22#include <algorithm>
23#include <cassert>
24#include <string>
25
26using namespace clang;
27using namespace driver;
28using namespace llvm::sys;
29
30Multilib::Multilib(StringRef GCCSuffix, StringRef OSSuffix,
31 StringRef IncludeSuffix, const flags_list &Flags,
32 StringRef ExclusiveGroup, std::optional<StringRef> Error)
33 : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix),
34 Flags(Flags), ExclusiveGroup(ExclusiveGroup), Error(Error) {
35 assert(GCCSuffix.empty() ||
36 (StringRef(GCCSuffix).front() == '/' && GCCSuffix.size() > 1));
37 assert(OSSuffix.empty() ||
38 (StringRef(OSSuffix).front() == '/' && OSSuffix.size() > 1));
39 assert(IncludeSuffix.empty() ||
40 (StringRef(IncludeSuffix).front() == '/' && IncludeSuffix.size() > 1));
41}
42
43LLVM_DUMP_METHOD void Multilib::dump() const {
44 print(OS&: llvm::errs());
45}
46
47void Multilib::print(raw_ostream &OS) const {
48 if (GCCSuffix.empty())
49 OS << ".";
50 else {
51 OS << StringRef(GCCSuffix).drop_front();
52 }
53 OS << ";";
54 for (StringRef Flag : Flags) {
55 if (Flag.front() == '-')
56 OS << "@" << Flag.substr(Start: 1);
57 }
58}
59
60bool Multilib::operator==(const Multilib &Other) const {
61 // Check whether the flags sets match
62 // allowing for the match to be order invariant
63 llvm::StringSet<> MyFlags(llvm::from_range, Flags);
64
65 for (const auto &Flag : Other.Flags)
66 if (!MyFlags.contains(key: Flag))
67 return false;
68
69 if (osSuffix() != Other.osSuffix())
70 return false;
71
72 if (gccSuffix() != Other.gccSuffix())
73 return false;
74
75 if (includeSuffix() != Other.includeSuffix())
76 return false;
77
78 return true;
79}
80
81raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) {
82 M.print(OS);
83 return OS;
84}
85
86MultilibSet &MultilibSet::FilterOut(FilterCallback F) {
87 llvm::erase_if(C&: Multilibs, P: F);
88 return *this;
89}
90
91void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(x: M); }
92
93static void DiagnoseUnclaimedMultilibCustomFlags(
94 const Driver &D, const SmallVector<StringRef> &UnclaimedCustomFlagValues,
95 const SmallVector<custom_flag::Declaration> &CustomFlagDecls) {
96 struct EditDistanceInfo {
97 StringRef FlagValue;
98 unsigned EditDistance;
99 };
100 const unsigned MaxEditDistance = 5;
101
102 for (StringRef Unclaimed : UnclaimedCustomFlagValues) {
103 std::optional<EditDistanceInfo> BestCandidate;
104 for (const auto &Decl : CustomFlagDecls) {
105 for (const auto &Value : Decl.ValueList) {
106 const std::string &FlagValueName = Value.Name;
107 unsigned EditDistance =
108 Unclaimed.edit_distance(Other: FlagValueName, /*AllowReplacements=*/true,
109 /*MaxEditDistance=*/MaxEditDistance);
110 if (!BestCandidate || (EditDistance <= MaxEditDistance &&
111 EditDistance < BestCandidate->EditDistance)) {
112 BestCandidate = {.FlagValue: FlagValueName, .EditDistance: EditDistance};
113 }
114 }
115 }
116 if (!BestCandidate)
117 D.Diag(clang::diag::DiagID: err_drv_unsupported_opt)
118 << (custom_flag::Prefix + Unclaimed).str();
119 else
120 D.Diag(clang::diag::DiagID: err_drv_unsupported_opt_with_suggestion)
121 << (custom_flag::Prefix + Unclaimed).str()
122 << (custom_flag::Prefix + BestCandidate->FlagValue).str();
123 }
124}
125
126namespace clang::driver::custom_flag {
127// Map implemented using linear searches as the expected size is too small for
128// the overhead of a search tree or a hash table.
129class ValueNameToDetailMap {
130 SmallVector<std::pair<StringRef, const ValueDetail *>> Mapping;
131
132public:
133 template <typename It>
134 ValueNameToDetailMap(It FlagDeclsBegin, It FlagDeclsEnd) {
135 for (auto DeclIt = FlagDeclsBegin; DeclIt != FlagDeclsEnd; ++DeclIt) {
136 const Declaration &Decl = *DeclIt;
137 for (const auto &Value : Decl.ValueList)
138 Mapping.emplace_back(Args: Value.Name, Args: &Value);
139 }
140 }
141
142 const ValueDetail *get(StringRef Key) const {
143 auto Iter = llvm::find_if(
144 Range: Mapping, P: [&](const auto &Pair) { return Pair.first == Key; });
145 return Iter != Mapping.end() ? Iter->second : nullptr;
146 }
147};
148} // namespace clang::driver::custom_flag
149
150std::pair<Multilib::flags_list, SmallVector<StringRef>>
151MultilibSet::processCustomFlags(const Driver &D,
152 const Multilib::flags_list &Flags) const {
153 Multilib::flags_list Result;
154 SmallVector<StringRef> MacroDefines;
155
156 // Custom flag values detected in the flags list
157 SmallVector<const custom_flag::ValueDetail *> ClaimedCustomFlagValues;
158
159 // Arguments to -fmultilib-flag=<arg> that don't correspond to any valid
160 // custom flag value. An error will be printed out for each of these.
161 SmallVector<StringRef> UnclaimedCustomFlagValueStrs;
162
163 const auto ValueNameToValueDetail = custom_flag::ValueNameToDetailMap(
164 CustomFlagDecls.begin(), CustomFlagDecls.end());
165
166 for (StringRef Flag : Flags) {
167 if (!Flag.starts_with(Prefix: custom_flag::Prefix)) {
168 Result.push_back(x: Flag.str());
169 continue;
170 }
171
172 StringRef CustomFlagValueStr = Flag.substr(Start: custom_flag::Prefix.size());
173 const custom_flag::ValueDetail *Detail =
174 ValueNameToValueDetail.get(Key: CustomFlagValueStr);
175 if (Detail)
176 ClaimedCustomFlagValues.push_back(Elt: Detail);
177 else
178 UnclaimedCustomFlagValueStrs.push_back(Elt: CustomFlagValueStr);
179 }
180
181 // Set of custom flag declarations for which a value was passed in the flags
182 // list. This is used to, firstly, detect multiple values for the same flag
183 // declaration (in this case, the last one wins), and secondly, to detect
184 // which declarations had no value passed in (in this case, the default value
185 // is selected).
186 llvm::SmallPtrSet<custom_flag::Declaration *, 32> TriggeredCustomFlagDecls;
187
188 // Detect multiple values for the same flag declaration. Last one wins.
189 for (auto *CustomFlagValue : llvm::reverse(C&: ClaimedCustomFlagValues)) {
190 if (!TriggeredCustomFlagDecls.insert(Ptr: CustomFlagValue->Decl).second)
191 continue;
192 Result.push_back(x: std::string(custom_flag::Prefix) + CustomFlagValue->Name);
193 if (CustomFlagValue->MacroDefines)
194 MacroDefines.append(in_start: CustomFlagValue->MacroDefines->begin(),
195 in_end: CustomFlagValue->MacroDefines->end());
196 }
197
198 // Detect flag declarations with no value passed in. Select default value.
199 for (const auto &Decl : CustomFlagDecls) {
200 if (TriggeredCustomFlagDecls.contains(Ptr: &Decl))
201 continue;
202 const custom_flag::ValueDetail &CustomFlagValue =
203 Decl.ValueList[*Decl.DefaultValueIdx];
204 Result.push_back(x: std::string(custom_flag::Prefix) + CustomFlagValue.Name);
205 if (CustomFlagValue.MacroDefines)
206 MacroDefines.append(in_start: CustomFlagValue.MacroDefines->begin(),
207 in_end: CustomFlagValue.MacroDefines->end());
208 }
209
210 DiagnoseUnclaimedMultilibCustomFlags(D, UnclaimedCustomFlagValues: UnclaimedCustomFlagValueStrs,
211 CustomFlagDecls);
212
213 return {Result, MacroDefines};
214}
215
216bool MultilibSet::select(
217 const Driver &D, const Multilib::flags_list &Flags,
218 llvm::SmallVectorImpl<Multilib> &Selected,
219 llvm::SmallVector<StringRef> *CustomFlagMacroDefines) const {
220 auto [FlagsWithCustom, CFMacroDefines] = processCustomFlags(D, Flags);
221 llvm::StringSet<> FlagSet(expandFlags(FlagsWithCustom));
222 Selected.clear();
223 bool AnyErrors = false;
224
225 // Determining the list of macro defines depends only on the custom flags
226 // passed in. The library variants actually selected are not relevant in
227 // this. Therefore this assignment can take place before the selection
228 // happens.
229 if (CustomFlagMacroDefines)
230 *CustomFlagMacroDefines = std::move(CFMacroDefines);
231
232 // Decide which multilibs we're going to select at all.
233 llvm::DenseSet<StringRef> ExclusiveGroupsSelected;
234 for (const Multilib &M : llvm::reverse(C: Multilibs)) {
235 // If this multilib doesn't match all our flags, don't select it.
236 if (!llvm::all_of(Range: M.flags(), P: [&FlagSet](const std::string &F) {
237 return FlagSet.contains(key: F);
238 }))
239 continue;
240
241 const std::string &group = M.exclusiveGroup();
242 if (!group.empty()) {
243 // If this multilib has the same ExclusiveGroup as one we've already
244 // selected, skip it. We're iterating in reverse order, so the group
245 // member we've selected already is preferred.
246 //
247 // Otherwise, add the group name to the set of groups we've already
248 // selected a member of.
249 auto [It, Inserted] = ExclusiveGroupsSelected.insert(V: group);
250 if (!Inserted)
251 continue;
252 }
253
254 // If this multilib is actually a placeholder containing an error message
255 // written by the multilib.yaml author, then set a flag that will cause a
256 // failure return. Our caller will display the error message.
257 if (M.isError())
258 AnyErrors = true;
259
260 // Select this multilib.
261 Selected.push_back(Elt: M);
262 }
263
264 // We iterated in reverse order, so now put Selected back the right way
265 // round.
266 std::reverse(first: Selected.begin(), last: Selected.end());
267
268 return !AnyErrors && !Selected.empty();
269}
270
271llvm::StringSet<>
272MultilibSet::expandFlags(const Multilib::flags_list &InFlags) const {
273 llvm::StringSet<> Result(llvm::from_range, InFlags);
274 for (const FlagMatcher &M : FlagMatchers) {
275 std::string RegexString(M.Match);
276
277 // Make the regular expression match the whole string.
278 if (!StringRef(M.Match).starts_with(Prefix: "^"))
279 RegexString.insert(p: RegexString.begin(), c: '^');
280 if (!StringRef(M.Match).ends_with(Suffix: "$"))
281 RegexString.push_back(c: '$');
282
283 const llvm::Regex Regex(RegexString);
284 assert(Regex.isValid());
285 if (llvm::any_of(Range: InFlags,
286 P: [&Regex](StringRef F) { return Regex.match(String: F); })) {
287 Result.insert_range(R: M.Flags);
288 }
289 }
290 return Result;
291}
292
293namespace {
294
295// When updating this also update MULTILIB_VERSION in MultilibTest.cpp
296static const VersionTuple MultilibVersionCurrent(1, 0);
297
298struct MultilibSerialization {
299 std::string Dir; // if this record successfully selects a library dir
300 std::string Error; // if this record reports a fatal error message
301 std::vector<std::string> Flags;
302 std::string Group;
303};
304
305enum class MultilibGroupType {
306 /*
307 * The only group type currently supported is 'Exclusive', which indicates a
308 * group of multilibs of which at most one may be selected.
309 */
310 Exclusive,
311
312 /*
313 * Future possibility: a second group type indicating a set of library
314 * directories that are mutually _dependent_ rather than mutually exclusive:
315 * if you include one you must include them all.
316 *
317 * It might also be useful to allow groups to be members of other groups, so
318 * that a mutually exclusive group could contain a mutually dependent set of
319 * library directories, or vice versa.
320 *
321 * These additional features would need changes in the implementation, but
322 * the YAML schema is set up so they can be added without requiring changes
323 * in existing users' multilib.yaml files.
324 */
325};
326
327struct MultilibGroupSerialization {
328 std::string Name;
329 MultilibGroupType Type;
330};
331
332struct MultilibSetSerialization {
333 llvm::VersionTuple MultilibVersion;
334 SmallVector<MultilibGroupSerialization> Groups;
335 SmallVector<MultilibSerialization> Multilibs;
336 SmallVector<MultilibSet::FlagMatcher> FlagMatchers;
337 SmallVector<custom_flag::Declaration> CustomFlagDeclarations;
338};
339
340} // end anonymous namespace
341
342LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization)
343LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibGroupSerialization)
344LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher)
345LLVM_YAML_IS_SEQUENCE_VECTOR(custom_flag::ValueDetail)
346LLVM_YAML_IS_SEQUENCE_VECTOR(custom_flag::Declaration)
347
348template <> struct llvm::yaml::MappingTraits<MultilibSerialization> {
349 static void mapping(llvm::yaml::IO &io, MultilibSerialization &V) {
350 io.mapOptional(Key: "Dir", Val&: V.Dir);
351 io.mapOptional(Key: "Error", Val&: V.Error);
352 io.mapRequired(Key: "Flags", Val&: V.Flags);
353 io.mapOptional(Key: "Group", Val&: V.Group);
354 }
355 static std::string validate(IO &io, MultilibSerialization &V) {
356 if (V.Dir.empty() && V.Error.empty())
357 return "one of the 'Dir' and 'Error' keys must be specified";
358 if (!V.Dir.empty() && !V.Error.empty())
359 return "the 'Dir' and 'Error' keys may not both be specified";
360 if (StringRef(V.Dir).starts_with(Prefix: "/"))
361 return "paths must be relative but \"" + V.Dir + "\" starts with \"/\"";
362 return std::string{};
363 }
364};
365
366template <> struct llvm::yaml::ScalarEnumerationTraits<MultilibGroupType> {
367 static void enumeration(IO &io, MultilibGroupType &Val) {
368 io.enumCase(Val, Str: "Exclusive", ConstVal: MultilibGroupType::Exclusive);
369 }
370};
371
372template <> struct llvm::yaml::MappingTraits<MultilibGroupSerialization> {
373 static void mapping(llvm::yaml::IO &io, MultilibGroupSerialization &V) {
374 io.mapRequired(Key: "Name", Val&: V.Name);
375 io.mapRequired(Key: "Type", Val&: V.Type);
376 }
377};
378
379template <> struct llvm::yaml::MappingTraits<MultilibSet::FlagMatcher> {
380 static void mapping(llvm::yaml::IO &io, MultilibSet::FlagMatcher &M) {
381 io.mapRequired(Key: "Match", Val&: M.Match);
382 io.mapRequired(Key: "Flags", Val&: M.Flags);
383 }
384 static std::string validate(IO &io, MultilibSet::FlagMatcher &M) {
385 llvm::Regex Regex(M.Match);
386 std::string RegexError;
387 if (!Regex.isValid(Error&: RegexError))
388 return RegexError;
389 if (M.Flags.empty())
390 return "value required for 'Flags'";
391 return std::string{};
392 }
393};
394
395template <>
396struct llvm::yaml::MappingContextTraits<custom_flag::ValueDetail,
397 llvm::SmallSet<std::string, 32>> {
398 static void mapping(llvm::yaml::IO &io, custom_flag::ValueDetail &V,
399 llvm::SmallSet<std::string, 32> &) {
400 io.mapRequired(Key: "Name", Val&: V.Name);
401 io.mapOptional(Key: "MacroDefines", Val&: V.MacroDefines);
402 }
403 static std::string validate(IO &io, custom_flag::ValueDetail &V,
404 llvm::SmallSet<std::string, 32> &NameSet) {
405 if (V.Name.empty())
406 return "custom flag value requires a name";
407 if (!NameSet.insert(V: V.Name).second)
408 return "duplicate custom flag value name: \"" + V.Name + "\"";
409 return {};
410 }
411};
412
413template <>
414struct llvm::yaml::MappingContextTraits<custom_flag::Declaration,
415 llvm::SmallSet<std::string, 32>> {
416 static void mapping(llvm::yaml::IO &io, custom_flag::Declaration &V,
417 llvm::SmallSet<std::string, 32> &NameSet) {
418 io.mapRequired(Key: "Name", Val&: V.Name);
419 io.mapRequired(Key: "Values", Val&: V.ValueList, Ctx&: NameSet);
420 std::string DefaultValueName;
421 io.mapRequired(Key: "Default", Val&: DefaultValueName);
422
423 for (auto [Idx, Value] : llvm::enumerate(First&: V.ValueList)) {
424 Value.Decl = &V;
425 if (Value.Name == DefaultValueName) {
426 assert(!V.DefaultValueIdx);
427 V.DefaultValueIdx = Idx;
428 }
429 }
430 }
431 static std::string validate(IO &io, custom_flag::Declaration &V,
432 llvm::SmallSet<std::string, 32> &) {
433 if (V.Name.empty())
434 return "custom flag requires a name";
435 if (V.ValueList.empty())
436 return "custom flag must have at least one value";
437 if (!V.DefaultValueIdx)
438 return "custom flag must have a default value";
439 return {};
440 }
441};
442
443template <> struct llvm::yaml::MappingTraits<MultilibSetSerialization> {
444 static void mapping(llvm::yaml::IO &io, MultilibSetSerialization &M) {
445 io.mapRequired(Key: "MultilibVersion", Val&: M.MultilibVersion);
446 io.mapRequired(Key: "Variants", Val&: M.Multilibs);
447 io.mapOptional(Key: "Groups", Val&: M.Groups);
448 llvm::SmallSet<std::string, 32> NameSet;
449 io.mapOptionalWithContext(Key: "Flags", Val&: M.CustomFlagDeclarations, Ctx&: NameSet);
450 io.mapOptional(Key: "Mappings", Val&: M.FlagMatchers);
451 }
452 static std::string validate(IO &io, MultilibSetSerialization &M) {
453 if (M.MultilibVersion.empty())
454 return "missing required key 'MultilibVersion'";
455 if (M.MultilibVersion.getMajor() != MultilibVersionCurrent.getMajor())
456 return "multilib version " + M.MultilibVersion.getAsString() +
457 " is unsupported";
458 if (M.MultilibVersion.getMinor() > MultilibVersionCurrent.getMinor())
459 return "multilib version " + M.MultilibVersion.getAsString() +
460 " is unsupported";
461 for (const MultilibSerialization &Lib : M.Multilibs) {
462 if (!Lib.Group.empty()) {
463 bool Found = false;
464 for (const MultilibGroupSerialization &Group : M.Groups)
465 if (Group.Name == Lib.Group) {
466 Found = true;
467 break;
468 }
469 if (!Found)
470 return "multilib \"" + Lib.Dir +
471 "\" specifies undefined group name \"" + Lib.Group + "\"";
472 }
473 }
474 return std::string{};
475 }
476};
477
478llvm::ErrorOr<MultilibSet>
479MultilibSet::parseYaml(llvm::MemoryBufferRef Input,
480 llvm::SourceMgr::DiagHandlerTy DiagHandler,
481 void *DiagHandlerCtxt) {
482 MultilibSetSerialization MS;
483 llvm::yaml::Input YamlInput(Input, nullptr, DiagHandler, DiagHandlerCtxt);
484 YamlInput >> MS;
485 if (YamlInput.error())
486 return YamlInput.error();
487
488 multilib_list Multilibs;
489 Multilibs.reserve(n: MS.Multilibs.size());
490 for (const auto &M : MS.Multilibs) {
491 if (!M.Error.empty()) {
492 Multilibs.emplace_back(args: "", args: "", args: "", args: M.Flags, args: M.Group, args: M.Error);
493 } else {
494 std::string Dir;
495 if (M.Dir != ".")
496 Dir = "/" + M.Dir;
497 // We transfer M.Group straight into the ExclusiveGroup parameter for the
498 // Multilib constructor. If we later support more than one type of group,
499 // we'll have to look up the group name in MS.Groups, check its type, and
500 // decide what to do here.
501 Multilibs.emplace_back(args&: Dir, args&: Dir, args&: Dir, args: M.Flags, args: M.Group);
502 }
503 }
504
505 return MultilibSet(std::move(Multilibs), std::move(MS.FlagMatchers),
506 std::move(MS.CustomFlagDeclarations));
507}
508
509LLVM_DUMP_METHOD void MultilibSet::dump() const {
510 print(OS&: llvm::errs());
511}
512
513void MultilibSet::print(raw_ostream &OS) const {
514 for (const auto &M : *this)
515 OS << M << "\n";
516}
517
518raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) {
519 MS.print(OS);
520 return OS;
521}
522
523namespace clang::driver::custom_flag {
524Declaration::Declaration(const Declaration &Other)
525 : Name(Other.Name), ValueList(Other.ValueList),
526 DefaultValueIdx(Other.DefaultValueIdx) {
527 for (ValueDetail &Detail : ValueList)
528 Detail.Decl = this;
529}
530
531Declaration::Declaration(Declaration &&Other)
532 : Name(std::move(Other.Name)), ValueList(std::move(Other.ValueList)),
533 DefaultValueIdx(std::move(Other.DefaultValueIdx)) {
534 for (ValueDetail &Detail : ValueList)
535 Detail.Decl = this;
536}
537
538Declaration &Declaration::operator=(const Declaration &Other) {
539 if (this == &Other)
540 return *this;
541 Name = Other.Name;
542 ValueList = Other.ValueList;
543 DefaultValueIdx = Other.DefaultValueIdx;
544 for (ValueDetail &Detail : ValueList)
545 Detail.Decl = this;
546 return *this;
547}
548
549Declaration &Declaration::operator=(Declaration &&Other) {
550 if (this == &Other)
551 return *this;
552 Name = std::move(Other.Name);
553 ValueList = std::move(Other.ValueList);
554 DefaultValueIdx = std::move(Other.DefaultValueIdx);
555 for (ValueDetail &Detail : ValueList)
556 Detail.Decl = this;
557 return *this;
558}
559} // namespace clang::driver::custom_flag
560

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of clang/lib/Driver/Multilib.cpp