99#include " clang/Driver/Multilib.h"
1010#include " clang/Basic/LLVM.h"
1111#include " clang/Basic/Version.h"
12+ #include " llvm/ADT/DenseSet.h"
1213#include " llvm/ADT/SmallString.h"
1314#include " llvm/ADT/StringRef.h"
1415#include " llvm/Support/Compiler.h"
@@ -29,9 +30,10 @@ using namespace driver;
2930using namespace llvm ::sys;
3031
3132Multilib::Multilib (StringRef GCCSuffix, StringRef OSSuffix,
32- StringRef IncludeSuffix, const flags_list &Flags)
33+ StringRef IncludeSuffix, const flags_list &Flags,
34+ StringRef ExclusiveGroup)
3335 : GCCSuffix(GCCSuffix), OSSuffix(OSSuffix), IncludeSuffix(IncludeSuffix),
34- Flags(Flags) {
36+ Flags(Flags), ExclusiveGroup(ExclusiveGroup) {
3537 assert (GCCSuffix.empty () ||
3638 (StringRef (GCCSuffix).front () == ' /' && GCCSuffix.size () > 1 ));
3739 assert (OSSuffix.empty () ||
@@ -96,13 +98,37 @@ bool MultilibSet::select(const Multilib::flags_list &Flags,
9698 llvm::SmallVector<Multilib> &Selected) const {
9799 llvm::StringSet<> FlagSet (expandFlags (Flags));
98100 Selected.clear ();
99- llvm::copy_if (Multilibs, std::back_inserter (Selected),
100- [&FlagSet](const Multilib &M) {
101- for (const std::string &F : M.flags ())
102- if (!FlagSet.contains (F))
103- return false ;
104- return true ;
105- });
101+
102+ // Decide which multilibs we're going to select at all.
103+ llvm::DenseSet<StringRef> ExclusiveGroupsSelected;
104+ for (const Multilib &M : llvm::reverse (Multilibs)) {
105+ // If this multilib doesn't match all our flags, don't select it.
106+ if (!llvm::all_of (M.flags (), [&FlagSet](const std::string &F) {
107+ return FlagSet.contains (F);
108+ }))
109+ continue ;
110+
111+ const std::string &group = M.exclusiveGroup ();
112+ if (!group.empty ()) {
113+ // If this multilib has the same ExclusiveGroup as one we've already
114+ // selected, skip it. We're iterating in reverse order, so the group
115+ // member we've selected already is preferred.
116+ //
117+ // Otherwise, add the group name to the set of groups we've already
118+ // selected a member of.
119+ auto [It, Inserted] = ExclusiveGroupsSelected.insert (group);
120+ if (!Inserted)
121+ continue ;
122+ }
123+
124+ // Select this multilib.
125+ Selected.push_back (M);
126+ }
127+
128+ // We iterated in reverse order, so now put Selected back the right way
129+ // round.
130+ std::reverse (Selected.begin (), Selected.end ());
131+
106132 return !Selected.empty ();
107133}
108134
@@ -138,10 +164,39 @@ static const VersionTuple MultilibVersionCurrent(1, 0);
138164struct MultilibSerialization {
139165 std::string Dir;
140166 std::vector<std::string> Flags;
167+ std::string Group;
168+ };
169+
170+ enum class MultilibGroupType {
171+ /*
172+ * The only group type currently supported is 'Exclusive', which indicates a
173+ * group of multilibs of which at most one may be selected.
174+ */
175+ Exclusive,
176+
177+ /*
178+ * Future possibility: a second group type indicating a set of library
179+ * directories that are mutually _dependent_ rather than mutually exclusive:
180+ * if you include one you must include them all.
181+ *
182+ * It might also be useful to allow groups to be members of other groups, so
183+ * that a mutually exclusive group could contain a mutually dependent set of
184+ * library directories, or vice versa.
185+ *
186+ * These additional features would need changes in the implementation, but
187+ * the YAML schema is set up so they can be added without requiring changes
188+ * in existing users' multilib.yaml files.
189+ */
190+ };
191+
192+ struct MultilibGroupSerialization {
193+ std::string Name;
194+ MultilibGroupType Type;
141195};
142196
143197struct MultilibSetSerialization {
144198 llvm::VersionTuple MultilibVersion;
199+ std::vector<MultilibGroupSerialization> Groups;
145200 std::vector<MultilibSerialization> Multilibs;
146201 std::vector<MultilibSet::FlagMatcher> FlagMatchers;
147202};
@@ -152,6 +207,7 @@ template <> struct llvm::yaml::MappingTraits<MultilibSerialization> {
152207 static void mapping (llvm::yaml::IO &io, MultilibSerialization &V) {
153208 io.mapRequired (" Dir" , V.Dir );
154209 io.mapRequired (" Flags" , V.Flags );
210+ io.mapOptional (" Group" , V.Group );
155211 }
156212 static std::string validate (IO &io, MultilibSerialization &V) {
157213 if (StringRef (V.Dir ).starts_with (" /" ))
@@ -160,6 +216,19 @@ template <> struct llvm::yaml::MappingTraits<MultilibSerialization> {
160216 }
161217};
162218
219+ template <> struct llvm ::yaml::ScalarEnumerationTraits<MultilibGroupType> {
220+ static void enumeration (IO &io, MultilibGroupType &Val) {
221+ io.enumCase (Val, " Exclusive" , MultilibGroupType::Exclusive);
222+ }
223+ };
224+
225+ template <> struct llvm ::yaml::MappingTraits<MultilibGroupSerialization> {
226+ static void mapping (llvm::yaml::IO &io, MultilibGroupSerialization &V) {
227+ io.mapRequired (" Name" , V.Name );
228+ io.mapRequired (" Type" , V.Type );
229+ }
230+ };
231+
163232template <> struct llvm ::yaml::MappingTraits<MultilibSet::FlagMatcher> {
164233 static void mapping (llvm::yaml::IO &io, MultilibSet::FlagMatcher &M) {
165234 io.mapRequired (" Match" , M.Match );
@@ -180,6 +249,7 @@ template <> struct llvm::yaml::MappingTraits<MultilibSetSerialization> {
180249 static void mapping (llvm::yaml::IO &io, MultilibSetSerialization &M) {
181250 io.mapRequired (" MultilibVersion" , M.MultilibVersion );
182251 io.mapRequired (" Variants" , M.Multilibs );
252+ io.mapOptional (" Groups" , M.Groups );
183253 io.mapOptional (" Mappings" , M.FlagMatchers );
184254 }
185255 static std::string validate (IO &io, MultilibSetSerialization &M) {
@@ -191,11 +261,25 @@ template <> struct llvm::yaml::MappingTraits<MultilibSetSerialization> {
191261 if (M.MultilibVersion .getMinor () > MultilibVersionCurrent.getMinor ())
192262 return " multilib version " + M.MultilibVersion .getAsString () +
193263 " is unsupported" ;
264+ for (const MultilibSerialization &Lib : M.Multilibs ) {
265+ if (!Lib.Group .empty ()) {
266+ bool Found = false ;
267+ for (const MultilibGroupSerialization &Group : M.Groups )
268+ if (Group.Name == Lib.Group ) {
269+ Found = true ;
270+ break ;
271+ }
272+ if (!Found)
273+ return " multilib \" " + Lib.Dir +
274+ " \" specifies undefined group name \" " + Lib.Group + " \" " ;
275+ }
276+ }
194277 return std::string{};
195278 }
196279};
197280
198281LLVM_YAML_IS_SEQUENCE_VECTOR (MultilibSerialization)
282+ LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibGroupSerialization)
199283LLVM_YAML_IS_SEQUENCE_VECTOR(MultilibSet::FlagMatcher)
200284
201285llvm::ErrorOr<MultilibSet>
@@ -214,7 +298,11 @@ MultilibSet::parseYaml(llvm::MemoryBufferRef Input,
214298 std::string Dir;
215299 if (M.Dir != " ." )
216300 Dir = " /" + M.Dir ;
217- Multilibs.emplace_back (Dir, Dir, Dir, M.Flags );
301+ // We transfer M.Group straight into the ExclusiveGroup parameter for the
302+ // Multilib constructor. If we later support more than one type of group,
303+ // we'll have to look up the group name in MS.Groups, check its type, and
304+ // decide what to do here.
305+ Multilibs.emplace_back (Dir, Dir, Dir, M.Flags , M.Group );
218306 }
219307
220308 return MultilibSet (std::move (Multilibs), std::move (MS.FlagMatchers ));
0 commit comments