[llvm-branch-commits] [clang] [Multilib] Custom flags processing for library selection (PR #110659)

Victor Campos via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Jan 9 03:46:18 PST 2025


================
@@ -92,12 +93,141 @@ MultilibSet &MultilibSet::FilterOut(FilterCallback F) {
 
 void MultilibSet::push_back(const Multilib &M) { Multilibs.push_back(M); }
 
-bool MultilibSet::select(const Driver &D, const Multilib::flags_list &Flags,
-                         llvm::SmallVectorImpl<Multilib> &Selected) const {
-  llvm::StringSet<> FlagSet(expandFlags(Flags));
+static void DiagnoseUnclaimedMultilibCustomFlags(
+    const Driver &D, const SmallVector<StringRef> &UnclaimedCustomFlagValues,
+    const SmallVector<custom_flag::DeclarationPtr> &CustomFlagDecls) {
+  struct EditDistanceInfo {
+    StringRef FlagValue;
+    unsigned EditDistance;
+  };
+  const unsigned MaxEditDistance = 5;
+
+  for (StringRef Unclaimed : UnclaimedCustomFlagValues) {
+    std::optional<EditDistanceInfo> BestCandidate;
+    for (const auto &Decl : CustomFlagDecls) {
+      for (const auto &Value : Decl->ValueList) {
+        const std::string &FlagValueName = Value.Name;
+        unsigned EditDistance =
+            Unclaimed.edit_distance(FlagValueName, /*AllowReplacements=*/true,
+                                    /*MaxEditDistance=*/MaxEditDistance);
+        if (!BestCandidate || (EditDistance <= MaxEditDistance &&
+                               EditDistance < BestCandidate->EditDistance)) {
+          BestCandidate = {FlagValueName, EditDistance};
+        }
+      }
+    }
+    if (!BestCandidate)
+      D.Diag(clang::diag::err_drv_unsupported_opt)
+          << (custom_flag::Prefix + Unclaimed).str();
+    else
+      D.Diag(clang::diag::err_drv_unsupported_opt_with_suggestion)
+          << (custom_flag::Prefix + Unclaimed).str()
+          << (custom_flag::Prefix + BestCandidate->FlagValue).str();
+  }
+}
+
+namespace clang::driver::custom_flag {
+// Map implemented using linear searches as the expected size is too small for
+// the overhead of a search tree or a hash table.
+class ValueNameToDetailMap {
+  SmallVector<std::pair<StringRef, const ValueDetail *>> Mapping;
+
+public:
+  template <typename It>
+  ValueNameToDetailMap(It FlagDeclsBegin, It FlagDeclsEnd) {
+    for (auto DeclIt = FlagDeclsBegin; DeclIt != FlagDeclsEnd; ++DeclIt) {
+      const DeclarationPtr &Decl = *DeclIt;
+      for (const auto &Value : Decl->ValueList)
+        Mapping.emplace_back(Value.Name, &Value);
+    }
+  }
+
+  const ValueDetail *get(StringRef Key) const {
+    auto Iter = llvm::find_if(
+        Mapping, [&](const auto &Pair) { return Pair.first == Key; });
+    return Iter != Mapping.end() ? Iter->second : nullptr;
+  }
+};
+} // namespace clang::driver::custom_flag
+
+std::pair<Multilib::flags_list, SmallVector<StringRef>>
+MultilibSet::processCustomFlags(const Driver &D,
+                                const Multilib::flags_list &Flags) const {
+  Multilib::flags_list Result;
+  SmallVector<StringRef> MacroDefines;
+
+  // Custom flag values detected in the flags list
+  SmallVector<const custom_flag::ValueDetail *> ClaimedCustomFlagValues;
+
+  // Arguments to -fmultilib-flag=<arg> that don't correspond to any valid
+  // custom flag value. An error will be printed out for each of these.
+  SmallVector<StringRef> UnclaimedCustomFlagValueStrs;
+
+  const auto ValueNameToValueDetail = custom_flag::ValueNameToDetailMap(
+      CustomFlagDecls.begin(), CustomFlagDecls.end());
+
+  for (StringRef Flag : Flags) {
+    if (!Flag.starts_with(custom_flag::Prefix)) {
+      Result.push_back(Flag.str());
+      continue;
+    }
+
+    StringRef CustomFlagValueStr = Flag.substr(custom_flag::Prefix.size());
+    const custom_flag::ValueDetail *Detail =
+        ValueNameToValueDetail.get(CustomFlagValueStr);
+    if (Detail)
+      ClaimedCustomFlagValues.push_back(Detail);
+    else
+      UnclaimedCustomFlagValueStrs.push_back(CustomFlagValueStr);
+  }
+
+  // Set of custom flag declarations for which a value was passed in the flags
+  // list. This is used to, firstly, detect multiple values for the same flag
+  // declaration (in this case, the last one wins), and secondly, to detect
+  // which declarations had no value passed in (in this case, the default value
+  // is selected).
+  llvm::SmallSet<custom_flag::DeclarationPtr, 32> TriggeredCustomFlagDecls;
+
+  // Detect multiple values for the same flag declaration. Last one wins.
+  for (auto *CustomFlagValue : llvm::reverse(ClaimedCustomFlagValues)) {
+    if (!TriggeredCustomFlagDecls.insert(CustomFlagValue->Decl).second)
+      continue;
+    Result.push_back(std::string(custom_flag::Prefix) + CustomFlagValue->Name);
+    if (CustomFlagValue->MacroDefines)
+      MacroDefines.append(CustomFlagValue->MacroDefines->begin(),
+                          CustomFlagValue->MacroDefines->end());
+  }
+
+  // Detect flag declarations with no value passed in. Select default value.
+  for (const auto &Decl : CustomFlagDecls) {
+    if (TriggeredCustomFlagDecls.contains(Decl))
+      continue;
+    custom_flag::ValueDetail &CustomFlagValue =
+        Decl->ValueList[*Decl->DefaultValueIdx];
+    Result.push_back(std::string(custom_flag::Prefix) + CustomFlagValue.Name);
+    if (CustomFlagValue.MacroDefines)
+      MacroDefines.append(CustomFlagValue.MacroDefines->begin(),
+                          CustomFlagValue.MacroDefines->end());
+  }
+
+  DiagnoseUnclaimedMultilibCustomFlags(D, UnclaimedCustomFlagValueStrs,
+                                       CustomFlagDecls);
+
+  return {Result, MacroDefines};
+}
+
+bool MultilibSet::select(
+    const Driver &D, const Multilib::flags_list &Flags,
+    llvm::SmallVectorImpl<Multilib> &Selected,
+    llvm::SmallVector<StringRef> *CustomFlagMacroDefines) const {
+  auto [FlagsWithCustom, CFMacroDefines] = processCustomFlags(D, Flags);
+  llvm::StringSet<> FlagSet(expandFlags(FlagsWithCustom));
   Selected.clear();
   bool AnyErrors = false;
 
+  if (CustomFlagMacroDefines)
+    *CustomFlagMacroDefines = std::move(CFMacroDefines);
----------------
vhscampos wrote:

Added a clarifying comment. Thanks for the suggestion

https://github.com/llvm/llvm-project/pull/110659


More information about the llvm-branch-commits mailing list