Skip to content

Commit 78820cb

Browse files
authored
[Clang] Enable multilib library support for Linux/Windows (llvm#188584)
Summary: This PR standardizes the logic used in the baremtal build to the common toolchain interface. We then use this to handle the support in Linux and Windows. The multilib functionality allows us to select variant libraries based off of a configuration file. For example, if the `multilib.yaml` file detects `-fsanitize=address` it will automatically use the libraries inside of `asan/` instead. These are layered so they do not necessarily need to be complete library builds. More documentation can be found at https://clang.llvm.org/docs/Multilib.html. The motivation for this is so platforms like ROCm can use a more standard way to ship debug / asan variants of libraries like OpenMP or similar for both GPU an CPU targets.
1 parent f2b5dc2 commit 78820cb

File tree

10 files changed

+339
-122
lines changed

10 files changed

+339
-122
lines changed

clang/include/clang/Driver/ToolChain.h

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,22 @@ class ToolChain {
206206
protected:
207207
MultilibSet Multilibs;
208208
llvm::SmallVector<Multilib> SelectedMultilibs;
209+
SmallVector<std::string> MultilibMacroDefines;
210+
211+
using OrderedMultilibs =
212+
llvm::iterator_range<llvm::SmallVector<Multilib>::const_reverse_iterator>;
213+
214+
/// Get selected multilibs in priority order with default fallback.
215+
OrderedMultilibs getOrderedMultilibs() const;
216+
217+
/// Discover and load a multilib.yaml configuration.
218+
bool loadMultilibsFromYAML(const llvm::opt::ArgList &Args, const Driver &D,
219+
StringRef Fallback = {});
220+
221+
/// Load multilib configuration from a YAML file at \p MultilibPath,
222+
std::optional<std::string> findMultilibsYAML(const llvm::opt::ArgList &Args,
223+
const Driver &D,
224+
StringRef FallbackDir = {});
209225

210226
ToolChain(const Driver &D, const llvm::Triple &T,
211227
const llvm::opt::ArgList &Args);
@@ -715,12 +731,12 @@ class ToolChain {
715731
/// Add warning options that need to be passed to cc1 for this target.
716732
virtual void addClangWarningOptions(llvm::opt::ArgStringList &CC1Args) const;
717733

718-
// Get the list of extra macro defines requested by the multilib
719-
// configuration.
720-
virtual SmallVector<std::string>
734+
/// Get the list of extra macro defines requested by the multilib
735+
/// configuration.
736+
SmallVector<std::string>
721737
getMultilibMacroDefinesStr(llvm::opt::ArgList &Args) const {
722-
return {};
723-
};
738+
return MultilibMacroDefines;
739+
}
724740

725741
// GetRuntimeLibType - Determine the runtime library type to use with the
726742
// given compilation arguments.

clang/lib/Driver/ToolChain.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "llvm/Support/ErrorHandling.h"
4040
#include "llvm/Support/FileSystem.h"
4141
#include "llvm/Support/FileUtilities.h"
42+
#include "llvm/Support/MemoryBuffer.h"
4243
#include "llvm/Support/Path.h"
4344
#include "llvm/Support/Process.h"
4445
#include "llvm/Support/VersionTuple.h"
@@ -105,6 +106,112 @@ ToolChain::ToolChain(const Driver &D, const llvm::Triple &T,
105106
addIfExists(getFilePaths(), Path);
106107
}
107108

109+
ToolChain::OrderedMultilibs ToolChain::getOrderedMultilibs() const {
110+
if (!SelectedMultilibs.empty())
111+
return llvm::reverse(SelectedMultilibs);
112+
113+
static const llvm::SmallVector<Multilib> Default = {Multilib()};
114+
return llvm::reverse(Default);
115+
}
116+
117+
bool ToolChain::loadMultilibsFromYAML(const llvm::opt::ArgList &Args,
118+
const Driver &D, StringRef Fallback) {
119+
std::optional<std::string> MultilibPath =
120+
findMultilibsYAML(Args, D, Fallback);
121+
if (!MultilibPath)
122+
return false;
123+
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB =
124+
D.getVFS().getBufferForFile(*MultilibPath);
125+
if (!MB)
126+
return false;
127+
128+
Multilib::flags_list Flags = getMultilibFlags(Args);
129+
llvm::ErrorOr<MultilibSet> ErrorOrMultilibSet =
130+
MultilibSet::parseYaml(*MB.get());
131+
if (ErrorOrMultilibSet.getError())
132+
return false;
133+
134+
Multilibs = std::move(ErrorOrMultilibSet.get());
135+
136+
SmallVector<StringRef> CustomFlagMacroDefines;
137+
bool Result =
138+
Multilibs.select(D, Flags, SelectedMultilibs, &CustomFlagMacroDefines);
139+
140+
// Custom flag macro defines are set by processCustomFlags regardless of
141+
// whether variant selection succeeds.
142+
MultilibMacroDefines.clear();
143+
for (StringRef Define : CustomFlagMacroDefines)
144+
MultilibMacroDefines.push_back(Define.str());
145+
146+
if (!Result) {
147+
D.Diag(clang::diag::warn_drv_missing_multilib) << llvm::join(Flags, " ");
148+
SmallString<0> Data;
149+
raw_svector_ostream OS(Data);
150+
for (const Multilib &M : Multilibs)
151+
if (!M.isError())
152+
OS << "\n" << llvm::join(M.flags(), " ");
153+
D.Diag(clang::diag::note_drv_available_multilibs) << OS.str();
154+
155+
for (const Multilib &M : SelectedMultilibs)
156+
if (M.isError())
157+
D.Diag(clang::diag::err_drv_multilib_custom_error)
158+
<< M.getErrorMessage();
159+
160+
SelectedMultilibs.clear();
161+
return false;
162+
}
163+
164+
// Prepend variant-specific library paths. The YAML's parent directory is
165+
// the base for file paths; getRuntimePath() is the base for runtime paths.
166+
StringRef YAMLBase = llvm::sys::path::parent_path(*MultilibPath);
167+
std::optional<std::string> RuntimeDir = getRuntimePath();
168+
size_t FileInsertPos = 0;
169+
size_t LibInsertPos = 0;
170+
for (const Multilib &M : getOrderedMultilibs()) {
171+
if (M.isDefault())
172+
continue;
173+
SmallString<128> FilePath(YAMLBase);
174+
llvm::sys::path::append(FilePath, M.gccSuffix());
175+
getFilePaths().insert(getFilePaths().begin() + FileInsertPos,
176+
std::string(FilePath));
177+
++FileInsertPos;
178+
if (RuntimeDir) {
179+
SmallString<128> LibPath(*RuntimeDir);
180+
llvm::sys::path::append(LibPath, M.gccSuffix());
181+
getLibraryPaths().insert(getLibraryPaths().begin() + LibInsertPos,
182+
std::string(LibPath));
183+
++LibInsertPos;
184+
}
185+
}
186+
187+
return true;
188+
}
189+
190+
std::optional<std::string>
191+
ToolChain::findMultilibsYAML(const llvm::opt::ArgList &Args, const Driver &D,
192+
StringRef FallbackDir) {
193+
if (Arg *A = Args.getLastArg(options::OPT_multi_lib_config)) {
194+
SmallString<128> MultilibPath(A->getValue());
195+
if (!D.getVFS().exists(MultilibPath)) {
196+
D.Diag(clang::diag::err_drv_no_such_file) << MultilibPath.str();
197+
return std::nullopt;
198+
}
199+
return std::string(MultilibPath);
200+
}
201+
202+
SmallString<128> MultilibPath;
203+
if (!FallbackDir.empty())
204+
MultilibPath = FallbackDir;
205+
else if (std::optional<std::string> StdlibDir = getStdlibPath())
206+
MultilibPath = *StdlibDir;
207+
else
208+
return std::nullopt;
209+
llvm::sys::path::append(MultilibPath, "multilib.yaml");
210+
if (!D.getVFS().exists(MultilibPath))
211+
return std::nullopt;
212+
return std::string(MultilibPath);
213+
}
214+
108215
void ToolChain::setTripleEnvironment(llvm::Triple::EnvironmentType Env) {
109216
Triple.setEnvironment(Env);
110217
if (EffectiveTriple != llvm::Triple())

clang/lib/Driver/ToolChains/BareMetal.cpp

Lines changed: 5 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
#include "llvm/Support/Path.h"
2525
#include "llvm/Support/VirtualFileSystem.h"
2626

27-
#include <sstream>
28-
2927
using namespace llvm::opt;
3028
using namespace clang;
3129
using namespace clang::driver;
@@ -264,89 +262,15 @@ BareMetal::BareMetal(const Driver &D, const llvm::Triple &Triple,
264262
}
265263
}
266264

267-
static void
268-
findMultilibsFromYAML(const ToolChain &TC, const Driver &D,
269-
StringRef MultilibPath, const ArgList &Args,
270-
DetectedMultilibs &Result,
271-
SmallVector<StringRef> &CustomFlagsMacroDefines) {
272-
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB =
273-
D.getVFS().getBufferForFile(MultilibPath);
274-
if (!MB)
275-
return;
276-
Multilib::flags_list Flags = TC.getMultilibFlags(Args);
277-
llvm::ErrorOr<MultilibSet> ErrorOrMultilibSet =
278-
MultilibSet::parseYaml(*MB.get());
279-
if (ErrorOrMultilibSet.getError())
280-
return;
281-
Result.Multilibs = ErrorOrMultilibSet.get();
282-
if (Result.Multilibs.select(D, Flags, Result.SelectedMultilibs,
283-
&CustomFlagsMacroDefines))
284-
return;
285-
D.Diag(clang::diag::warn_drv_missing_multilib) << llvm::join(Flags, " ");
286-
std::stringstream ss;
287-
288-
// If multilib selection didn't complete successfully, report a list
289-
// of all the configurations the user could have provided.
290-
for (const Multilib &Multilib : Result.Multilibs)
291-
if (!Multilib.isError())
292-
ss << "\n" << llvm::join(Multilib.flags(), " ");
293-
D.Diag(clang::diag::note_drv_available_multilibs) << ss.str();
294-
295-
// Now report any custom error messages requested by the YAML. We do
296-
// this after displaying the list of available multilibs, because
297-
// that list is probably large, and (in interactive use) risks
298-
// scrolling the useful error message off the top of the user's
299-
// terminal.
300-
for (const Multilib &Multilib : Result.SelectedMultilibs)
301-
if (Multilib.isError())
302-
D.Diag(clang::diag::err_drv_multilib_custom_error)
303-
<< Multilib.getErrorMessage();
304-
305-
// If there was an error, clear the SelectedMultilibs vector, in
306-
// case it contains partial data.
307-
Result.SelectedMultilibs.clear();
308-
}
309-
310-
static constexpr llvm::StringLiteral MultilibFilename = "multilib.yaml";
311-
312-
static std::optional<llvm::SmallString<128>>
313-
getMultilibConfigPath(const Driver &D, const llvm::Triple &Triple,
314-
const ArgList &Args) {
315-
llvm::SmallString<128> MultilibPath;
316-
if (Arg *ConfigFileArg = Args.getLastArg(options::OPT_multi_lib_config)) {
317-
MultilibPath = ConfigFileArg->getValue();
318-
if (!D.getVFS().exists(MultilibPath)) {
319-
D.Diag(clang::diag::err_drv_no_such_file) << MultilibPath.str();
320-
return {};
321-
}
322-
} else {
323-
MultilibPath = computeClangRuntimesSysRoot(D, /*IncludeTriple=*/false);
324-
llvm::sys::path::append(MultilibPath, MultilibFilename);
325-
}
326-
return MultilibPath;
327-
}
328-
329265
void BareMetal::findMultilibs(const Driver &D, const llvm::Triple &Triple,
330266
const ArgList &Args) {
331-
DetectedMultilibs Result;
332267
// Look for a multilib.yaml before trying target-specific hardwired logic.
333-
// If it exists, always do what it specifies.
334-
std::optional<llvm::SmallString<128>> MultilibPath =
335-
getMultilibConfigPath(D, Triple, Args);
336-
if (!MultilibPath)
337-
return;
338-
if (D.getVFS().exists(*MultilibPath)) {
339-
// If multilib.yaml is found, update sysroot so it doesn't use a target
340-
// specific suffix
341-
SysRoot = computeClangRuntimesSysRoot(D, /*IncludeTriple=*/false);
342-
SmallVector<StringRef> CustomFlagMacroDefines;
343-
findMultilibsFromYAML(*this, D, *MultilibPath, Args, Result,
344-
CustomFlagMacroDefines);
345-
SelectedMultilibs = Result.SelectedMultilibs;
346-
Multilibs = Result.Multilibs;
347-
MultilibMacroDefines.append(CustomFlagMacroDefines.begin(),
348-
CustomFlagMacroDefines.end());
268+
std::string FallbackDir =
269+
computeClangRuntimesSysRoot(D, /*IncludeTriple=*/false);
270+
if (loadMultilibsFromYAML(Args, D, FallbackDir)) {
271+
SysRoot = FallbackDir;
349272
} else if (isRISCVBareMetal(Triple) && !detectGCCToolchainAdjacent(D)) {
273+
DetectedMultilibs Result;
350274
if (findRISCVMultilibs(D, Triple, Args, Result)) {
351275
SelectedMultilibs = Result.SelectedMultilibs;
352276
Multilibs = Result.Multilibs;
@@ -368,16 +292,6 @@ Tool *BareMetal::buildStaticLibTool() const {
368292
return new tools::baremetal::StaticLibTool(*this);
369293
}
370294

371-
BareMetal::OrderedMultilibs BareMetal::getOrderedMultilibs() const {
372-
// Get multilibs in reverse order because they're ordered most-specific last.
373-
if (!SelectedMultilibs.empty())
374-
return llvm::reverse(SelectedMultilibs);
375-
376-
// No multilibs selected so return a single default multilib.
377-
static const llvm::SmallVector<Multilib> Default = {Multilib()};
378-
return llvm::reverse(Default);
379-
}
380-
381295
ToolChain::CXXStdlibType BareMetal::GetDefaultCXXStdlibType() const {
382296
if (getTriple().isRISCV() && IsGCCInstallationValid)
383297
return ToolChain::CST_Libstdcxx;
@@ -756,8 +670,3 @@ SanitizerMask BareMetal::getSupportedSanitizers() const {
756670
}
757671
return Res;
758672
}
759-
760-
SmallVector<std::string>
761-
BareMetal::getMultilibMacroDefinesStr(llvm::opt::ArgList &Args) const {
762-
return MultilibMacroDefines;
763-
}

clang/lib/Driver/ToolChains/BareMetal.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,19 +79,10 @@ class LLVM_LIBRARY_VISIBILITY BareMetal : public Generic_ELF {
7979
std::string getCompilerRTPath() const override;
8080
SanitizerMask getSupportedSanitizers() const override;
8181

82-
SmallVector<std::string>
83-
getMultilibMacroDefinesStr(llvm::opt::ArgList &Args) const override;
84-
8582
private:
86-
using OrderedMultilibs =
87-
llvm::iterator_range<llvm::SmallVector<Multilib>::const_reverse_iterator>;
88-
OrderedMultilibs getOrderedMultilibs() const;
89-
9083
std::string SysRoot;
9184

9285
bool IsGCCInstallationValid;
93-
94-
SmallVector<std::string> MultilibMacroDefines;
9586
};
9687

9788
} // namespace toolchains

clang/lib/Driver/ToolChains/CommonArgs.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,9 +1416,16 @@ void tools::addArchSpecificRPath(const ToolChain &TC, const ArgList &Args,
14161416
return;
14171417

14181418
SmallVector<std::string> CandidateRPaths(TC.getArchSpecificLibPaths());
1419-
if (const auto CandidateRPath = TC.getStdlibPath())
1420-
CandidateRPaths.emplace_back(*CandidateRPath);
1421-
1419+
if (const auto StdlibPath = TC.getStdlibPath()) {
1420+
for (const Multilib &M : llvm::reverse(TC.getSelectedMultilibs())) {
1421+
if (M.isDefault())
1422+
continue;
1423+
SmallString<128> P(*StdlibPath);
1424+
llvm::sys::path::append(P, M.gccSuffix());
1425+
CandidateRPaths.emplace_back(std::string(P));
1426+
}
1427+
CandidateRPaths.emplace_back(*StdlibPath);
1428+
}
14221429
for (const auto &CandidateRPath : CandidateRPaths) {
14231430
if (TC.getVFS().exists(CandidateRPath)) {
14241431
CmdArgs.push_back("-rpath");

clang/lib/Driver/ToolChains/Gnu.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3135,22 +3135,21 @@ void Generic_GCC::AddMultilibPaths(const Driver &D,
31353135
path_list &Paths) {
31363136
// Add the multilib suffixed paths where they are available.
31373137
if (GCCInstallation.isValid()) {
3138-
assert(!SelectedMultilibs.empty());
3138+
const Multilib &GCCMultilib = GCCInstallation.getMultilib();
31393139
const llvm::Triple &GCCTriple = GCCInstallation.getTriple();
31403140
const std::string &LibPath =
31413141
std::string(GCCInstallation.getParentLibPath());
31423142

31433143
// Sourcery CodeBench MIPS toolchain holds some libraries under
31443144
// a biarch-like suffix of the GCC installation.
3145-
if (const auto &PathsCallback = Multilibs.filePathsCallback())
3146-
for (const auto &Path : PathsCallback(SelectedMultilibs.back()))
3145+
if (const auto &PathsCallback =
3146+
GCCInstallation.getMultilibs().filePathsCallback())
3147+
for (const auto &Path : PathsCallback(GCCMultilib))
31473148
addPathIfExists(D, GCCInstallation.getInstallPath() + Path, Paths);
31483149

31493150
// Add lib/gcc/$triple/$version, with an optional /multilib suffix.
3150-
addPathIfExists(D,
3151-
GCCInstallation.getInstallPath() +
3152-
SelectedMultilibs.back().gccSuffix(),
3153-
Paths);
3151+
addPathIfExists(
3152+
D, GCCInstallation.getInstallPath() + GCCMultilib.gccSuffix(), Paths);
31543153

31553154
// Add lib/gcc/$triple/$libdir
31563155
// For GCC built with --enable-version-specific-runtime-libs.
@@ -3177,7 +3176,7 @@ void Generic_GCC::AddMultilibPaths(const Driver &D,
31773176
// Clang diverges from GCC's behavior.
31783177
addPathIfExists(D,
31793178
LibPath + "/../" + GCCTriple.str() + "/lib/../" + OSLibDir +
3180-
SelectedMultilibs.back().osSuffix(),
3179+
GCCMultilib.osSuffix(),
31813180
Paths);
31823181

31833182
// If the GCC installation we found is inside of the sysroot, we want to
@@ -3221,7 +3220,7 @@ void Generic_GCC::AddMultilibIncludeArgs(const ArgList &DriverArgs,
32213220
Twine(LibPath) + "/../" + GCCTriple.str() +
32223221
"/include");
32233222

3224-
const auto &Callback = Multilibs.includeDirsCallback();
3223+
const auto &Callback = GCCInstallation.getMultilibs().includeDirsCallback();
32253224
if (Callback) {
32263225
for (const auto &Path : Callback(GCCInstallation.getMultilib()))
32273226
addExternCSystemIncludeIfExists(DriverArgs, CC1Args,

0 commit comments

Comments
 (0)