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(llvm::from_range, Flags); 64 65 for (const auto &Flag : Other.Flags) 66 if (!MyFlags.contains(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 81 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const Multilib &M) { 82 M.print(OS); 83 return OS; 84 } 85 86 MultilibSet &MultilibSet::FilterOut(FilterCallback F) { 87 llvm::erase_if(Multilibs, F); 88 return *this; 89 } 90 91 void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); } 92 93 static 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(FlagValueName, /*AllowReplacements=*/true, 109 /*MaxEditDistance=*/MaxEditDistance); 110 if (!BestCandidate || (EditDistance <= MaxEditDistance && 111 EditDistance < BestCandidate->EditDistance)) { 112 BestCandidate = {FlagValueName, EditDistance}; 113 } 114 } 115 } 116 if (!BestCandidate) 117 D.Diag(clang::diag::err_drv_unsupported_opt) 118 << (custom_flag::Prefix + Unclaimed).str(); 119 else 120 D.Diag(clang::diag::err_drv_unsupported_opt_with_suggestion) 121 << (custom_flag::Prefix + Unclaimed).str() 122 << (custom_flag::Prefix + BestCandidate->FlagValue).str(); 123 } 124 } 125 126 namespace 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. 129 class ValueNameToDetailMap { 130 SmallVector<std::pair<StringRef, const ValueDetail *>> Mapping; 131 132 public: 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(Value.Name, &Value); 139 } 140 } 141 142 const ValueDetail *get(StringRef Key) const { 143 auto Iter = llvm::find_if( 144 Mapping, [&](const auto &Pair) { return Pair.first == Key; }); 145 return Iter != Mapping.end() ? Iter->second : nullptr; 146 } 147 }; 148 } // namespace clang::driver::custom_flag 149 150 std::pair<Multilib::flags_list, SmallVector<StringRef>> 151 MultilibSet::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(custom_flag::Prefix)) { 168 Result.push_back(Flag.str()); 169 continue; 170 } 171 172 StringRef CustomFlagValueStr = Flag.substr(custom_flag::Prefix.size()); 173 const custom_flag::ValueDetail *Detail = 174 ValueNameToValueDetail.get(CustomFlagValueStr); 175 if (Detail) 176 ClaimedCustomFlagValues.push_back(Detail); 177 else 178 UnclaimedCustomFlagValueStrs.push_back(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(ClaimedCustomFlagValues)) { 190 if (!TriggeredCustomFlagDecls.insert(CustomFlagValue->Decl).second) 191 continue; 192 Result.push_back(std::string(custom_flag::Prefix) + CustomFlagValue->Name); 193 if (CustomFlagValue->MacroDefines) 194 MacroDefines.append(CustomFlagValue->MacroDefines->begin(), 195 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(&Decl)) 201 continue; 202 const custom_flag::ValueDetail &CustomFlagValue = 203 Decl.ValueList[*Decl.DefaultValueIdx]; 204 Result.push_back(std::string(custom_flag::Prefix) + CustomFlagValue.Name); 205 if (CustomFlagValue.MacroDefines) 206 MacroDefines.append(CustomFlagValue.MacroDefines->begin(), 207 CustomFlagValue.MacroDefines->end()); 208 } 209 210 DiagnoseUnclaimedMultilibCustomFlags(D, UnclaimedCustomFlagValueStrs, 211 CustomFlagDecls); 212 213 return {Result, MacroDefines}; 214 } 215 216 bool 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(Multilibs)) { 235 // If this multilib doesn't match all our flags, don't select it. 236 if (!llvm::all_of(M.flags(), [&FlagSet](const std::string &F) { 237 return FlagSet.contains(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(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(M); 262 } 263 264 // We iterated in reverse order, so now put Selected back the right way 265 // round. 266 std::reverse(Selected.begin(), Selected.end()); 267 268 return !AnyErrors && !Selected.empty(); 269 } 270 271 llvm::StringSet<> 272 MultilibSet::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("^")) 279 RegexString.insert(RegexString.begin(), '^'); 280 if (!StringRef(M.Match).ends_with("$")) 281 RegexString.push_back('$'); 282 283 const llvm::Regex Regex(RegexString); 284 assert(Regex.isValid()); 285 if (llvm::any_of(InFlags, 286 [&Regex](StringRef F) { return Regex.match(F); })) { 287 Result.insert_range(M.Flags); 288 } 289 } 290 return Result; 291 } 292 293 namespace { 294 295 // When updating this also update MULTILIB_VERSION in MultilibTest.cpp 296 static const VersionTuple MultilibVersionCurrent(1, 0); 297 298 struct 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 305 enum 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 327 struct MultilibGroupSerialization { 328 std::string Name; 329 MultilibGroupType Type; 330 }; 331 332 struct 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 342 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSerialization) 343 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibGroupSerialization) 344 LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher) 345 LLVM_YAML_IS_SEQUENCE_VECTOR(custom_flag::ValueDetail) 346 LLVM_YAML_IS_SEQUENCE_VECTOR(custom_flag::Declaration) 347 348 template <> struct llvm::yaml::MappingTraits<MultilibSerialization> { 349 static void mapping(llvm::yaml::IO &io, MultilibSerialization &V) { 350 io.mapOptional("Dir", V.Dir); 351 io.mapOptional("Error", V.Error); 352 io.mapRequired("Flags", V.Flags); 353 io.mapOptional("Group", 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("/")) 361 return "paths must be relative but \"" + V.Dir + "\" starts with \"/\""; 362 return std::string{}; 363 } 364 }; 365 366 template <> struct llvm::yaml::ScalarEnumerationTraits<MultilibGroupType> { 367 static void enumeration(IO &io, MultilibGroupType &Val) { 368 io.enumCase(Val, "Exclusive", MultilibGroupType::Exclusive); 369 } 370 }; 371 372 template <> struct llvm::yaml::MappingTraits<MultilibGroupSerialization> { 373 static void mapping(llvm::yaml::IO &io, MultilibGroupSerialization &V) { 374 io.mapRequired("Name", V.Name); 375 io.mapRequired("Type", V.Type); 376 } 377 }; 378 379 template <> struct llvm::yaml::MappingTraits<MultilibSet::FlagMatcher> { 380 static void mapping(llvm::yaml::IO &io, MultilibSet::FlagMatcher &M) { 381 io.mapRequired("Match", M.Match); 382 io.mapRequired("Flags", 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(RegexError)) 388 return RegexError; 389 if (M.Flags.empty()) 390 return "value required for 'Flags'"; 391 return std::string{}; 392 } 393 }; 394 395 template <> 396 struct 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("Name", V.Name); 401 io.mapOptional("MacroDefines", 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.Name).second) 408 return "duplicate custom flag value name: \"" + V.Name + "\""; 409 return {}; 410 } 411 }; 412 413 template <> 414 struct 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("Name", V.Name); 419 io.mapRequired("Values", V.ValueList, NameSet); 420 std::string DefaultValueName; 421 io.mapRequired("Default", DefaultValueName); 422 423 for (auto [Idx, Value] : llvm::enumerate(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 443 template <> struct llvm::yaml::MappingTraits<MultilibSetSerialization> { 444 static void mapping(llvm::yaml::IO &io, MultilibSetSerialization &M) { 445 io.mapRequired("MultilibVersion", M.MultilibVersion); 446 io.mapRequired("Variants", M.Multilibs); 447 io.mapOptional("Groups", M.Groups); 448 llvm::SmallSet<std::string, 32> NameSet; 449 io.mapOptionalWithContext("Flags", M.CustomFlagDeclarations, NameSet); 450 io.mapOptional("Mappings", 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 478 llvm::ErrorOr<MultilibSet> 479 MultilibSet::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(MS.Multilibs.size()); 490 for (const auto &M : MS.Multilibs) { 491 if (!M.Error.empty()) { 492 Multilibs.emplace_back("", "", "", M.Flags, M.Group, 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(Dir, Dir, Dir, M.Flags, M.Group); 502 } 503 } 504 505 return MultilibSet(std::move(Multilibs), std::move(MS.FlagMatchers), 506 std::move(MS.CustomFlagDeclarations)); 507 } 508 509 LLVM_DUMP_METHOD void MultilibSet::dump() const { 510 print(llvm::errs()); 511 } 512 513 void MultilibSet::print(raw_ostream &OS) const { 514 for (const auto &M : *this) 515 OS << M << "\n"; 516 } 517 518 raw_ostream &clang::driver::operator<<(raw_ostream &OS, const MultilibSet &MS) { 519 MS.print(OS); 520 return OS; 521 } 522 523 namespace clang::driver::custom_flag { 524 Declaration::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 531 Declaration::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 538 Declaration &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 549 Declaration &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