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