ToolChain是Clang的Driver庫裏的一個類,它是用來獲取具體某個平臺的工具集合,代碼註釋中的原文是:ToolChain - Access to tools for a single platform.(clang/include/clang/Driver/ToolChain.h)這裏涉及到的Tool也是Clang的Driver庫裏的一個類,它是具體編譯工具的信息,代碼註釋中的原文是:Tool - Information on a specific compilation tool.(clang/include/clang/Driver/Tool.h)
一、ToolChain的實現
1、ToolChain的定義和實現位於文件clang/include/clang/Driver/ToolChain.h和clang/lib/Driver/ToolChain.cpp中。
2、ToolChain本身有和Driver的綁定關係,其成員變量中有專門的D,具體如下:
const Driver &D;
3、ToolChain本身還關聯了一系列的Tool,其保存了這些Tool的信息,並且有相關的操作函數:
mutable std::unique_ptr<Tool> Clang;
mutable std::unique_ptr<Tool> Flang;
mutable std::unique_ptr<Tool> Assemble;
mutable std::unique_ptr<Tool> Link;
mutable std::unique_ptr<Tool> IfsMerge;
mutable std::unique_ptr<Tool> OffloadBundler;
mutable std::unique_ptr<Tool> OffloadWrapper;
Tool *getClang() const;
Tool *getFlang() const;
Tool *getAssemble() const;
Tool *getLink() const;
Tool *getIfsMerge() const;
Tool *getClangAs() const;
Tool *getOffloadBundler() const;
Tool *getOffloadWrapper() const;
Tool的相關內容,後續作專門的文章分析。
4、ToolChain根據不同的軟硬件平臺,有一系列的子類,這些子類的定義和實現都位於/clang/lib/Driver/ToolChains/目錄之下。同時,/clang/lib/Driver/ToolChains/目錄之下還有一個Arch目錄,裏面放置了一些獲取目標平臺相關信息的實現。
二、ToolChain的調用關係
1、clang/lib/Driver/Driver.cpp中的Compilation *Driver::BuildCompilation(ArrayRef<constchar *> ArgList) 函數中首先調用了getToolChain函數,返回了ToolChain類型的結果,具體代碼如下:
// Owned by the host.
const ToolChain &TC = getToolChain(
*UArgs, computeTargetTriple(*this, TargetTriple, *UArgs));
// The compilation takes ownership of Args.
Compilation *C = new Compilation(*this, TC, UArgs.release(), TranslatedArgs,
ContainsError);
並且是在構建Compilation之前調用的,爲構建Compilation準備了參數。從宏觀上也可以理解爲,爲Compilation(具體的編譯動作)準備了工具鏈相關信息。
2、同文件下的getToolChain函數的實現相對比較簡單,具體代碼如下:
const ToolChain &Driver::getToolChain(const ArgList &Args,
const llvm::Triple &Target) const {
auto &TC = ToolChains[Target.str()];
if (!TC) {
switch (Target.getOS()) {
case llvm::Triple::AIX:
TC = std::make_unique<toolchains::AIX>(*this, Target, Args);
break;
case llvm::Triple::Haiku:
TC = std::make_unique<toolchains::Haiku>(*this, Target, Args);
break;
case llvm::Triple::Ananas:
TC = std::make_unique<toolchains::Ananas>(*this, Target, Args);
break;
case llvm::Triple::CloudABI:
TC = std::make_unique<toolchains::CloudABI>(*this, Target, Args);
break;
case llvm::Triple::Darwin:
case llvm::Triple::MacOSX:
case llvm::Triple::IOS:
case llvm::Triple::TvOS:
case llvm::Triple::WatchOS:
TC = std::make_unique<toolchains::DarwinClang>(*this, Target, Args);
break;
case llvm::Triple::DragonFly:
TC = std::make_unique<toolchains::DragonFly>(*this, Target, Args);
break;
case llvm::Triple::OpenBSD:
TC = std::make_unique<toolchains::OpenBSD>(*this, Target, Args);
break;
case llvm::Triple::NetBSD:
TC = std::make_unique<toolchains::NetBSD>(*this, Target, Args);
break;
case llvm::Triple::FreeBSD:
TC = std::make_unique<toolchains::FreeBSD>(*this, Target, Args);
break;
case llvm::Triple::Minix:
TC = std::make_unique<toolchains::Minix>(*this, Target, Args);
break;
case llvm::Triple::Linux:
case llvm::Triple::ELFIAMCU:
if (Target.getArch() == llvm::Triple::hexagon)
TC = std::make_unique<toolchains::HexagonToolChain>(*this, Target,
Args);
else if ((Target.getVendor() == llvm::Triple::MipsTechnologies) &&
!Target.hasEnvironment())
TC = std::make_unique<toolchains::MipsLLVMToolChain>(*this, Target,
Args);
else if (Target.getArch() == llvm::Triple::ppc ||
Target.getArch() == llvm::Triple::ppc64 ||
Target.getArch() == llvm::Triple::ppc64le)
TC = std::make_unique<toolchains::PPCLinuxToolChain>(*this, Target,
Args);
else
TC = std::make_unique<toolchains::Linux>(*this, Target, Args);
break;
case llvm::Triple::NaCl:
TC = std::make_unique<toolchains::NaClToolChain>(*this, Target, Args);
break;
case llvm::Triple::Fuchsia:
TC = std::make_unique<toolchains::Fuchsia>(*this, Target, Args);
break;
case llvm::Triple::Solaris:
TC = std::make_unique<toolchains::Solaris>(*this, Target, Args);
break;
case llvm::Triple::AMDHSA:
TC = std::make_unique<toolchains::ROCMToolChain>(*this, Target, Args);
break;
case llvm::Triple::AMDPAL:
case llvm::Triple::Mesa3D:
TC = std::make_unique<toolchains::AMDGPUToolChain>(*this, Target, Args);
break;
case llvm::Triple::Win32:
switch (Target.getEnvironment()) {
default:
if (Target.isOSBinFormatELF())
TC = std::make_unique<toolchains::Generic_ELF>(*this, Target, Args);
else if (Target.isOSBinFormatMachO())
TC = std::make_unique<toolchains::MachO>(*this, Target, Args);
else
TC = std::make_unique<toolchains::Generic_GCC>(*this, Target, Args);
break;
case llvm::Triple::GNU:
TC = std::make_unique<toolchains::MinGW>(*this, Target, Args);
break;
case llvm::Triple::Itanium:
TC = std::make_unique<toolchains::CrossWindowsToolChain>(*this, Target,
Args);
break;
case llvm::Triple::MSVC:
case llvm::Triple::UnknownEnvironment:
if (Args.getLastArgValue(options::OPT_fuse_ld_EQ)
.startswith_lower("bfd"))
TC = std::make_unique<toolchains::CrossWindowsToolChain>(
*this, Target, Args);
else
TC =
std::make_unique<toolchains::MSVCToolChain>(*this, Target, Args);
break;
}
break;
case llvm::Triple::PS4:
TC = std::make_unique<toolchains::PS4CPU>(*this, Target, Args);
break;
case llvm::Triple::Contiki:
TC = std::make_unique<toolchains::Contiki>(*this, Target, Args);
break;
case llvm::Triple::Hurd:
TC = std::make_unique<toolchains::Hurd>(*this, Target, Args);
break;
default:
// Of these targets, Hexagon is the only one that might have
// an OS of Linux, in which case it got handled above already.
switch (Target.getArch()) {
case llvm::Triple::tce:
TC = std::make_unique<toolchains::TCEToolChain>(*this, Target, Args);
break;
case llvm::Triple::tcele:
TC = std::make_unique<toolchains::TCELEToolChain>(*this, Target, Args);
break;
case llvm::Triple::hexagon:
TC = std::make_unique<toolchains::HexagonToolChain>(*this, Target,
Args);
break;
case llvm::Triple::lanai:
TC = std::make_unique<toolchains::LanaiToolChain>(*this, Target, Args);
break;
case llvm::Triple::xcore:
TC = std::make_unique<toolchains::XCoreToolChain>(*this, Target, Args);
break;
case llvm::Triple::wasm32:
case llvm::Triple::wasm64:
TC = std::make_unique<toolchains::WebAssembly>(*this, Target, Args);
break;
case llvm::Triple::avr:
TC = std::make_unique<toolchains::AVRToolChain>(*this, Target, Args);
break;
case llvm::Triple::msp430:
TC =
std::make_unique<toolchains::MSP430ToolChain>(*this, Target, Args);
break;
case llvm::Triple::riscv32:
case llvm::Triple::riscv64:
TC = std::make_unique<toolchains::RISCVToolChain>(*this, Target, Args);
break;
default:
if (Target.getVendor() == llvm::Triple::Myriad)
TC = std::make_unique<toolchains::MyriadToolChain>(*this, Target,
Args);
else if (toolchains::BareMetal::handlesTarget(Target))
TC = std::make_unique<toolchains::BareMetal>(*this, Target, Args);
else if (Target.isOSBinFormatELF())
TC = std::make_unique<toolchains::Generic_ELF>(*this, Target, Args);
else if (Target.isOSBinFormatMachO())
TC = std::make_unique<toolchains::MachO>(*this, Target, Args);
else
TC = std::make_unique<toolchains::Generic_GCC>(*this, Target, Args);
}
}
}
// Intentionally omitted from the switch above: llvm::Triple::CUDA. CUDA
// compiles always need two toolchains, the CUDA toolchain and the host
// toolchain. So the only valid way to create a CUDA toolchain is via
// CreateOffloadingDeviceToolChains.
return *TC;
}
這裏通過了具體的操作系統和具體的目標平臺,來構建具體的ToolChain 。以RISCV爲例,其中通過如下代碼構建工具鏈:
case llvm::Triple::riscv32:
case llvm::Triple::riscv64:
TC = std::make_unique<toolchains::RISCVToolChain>(*this, Target, Args);
break;
關於RISCVToolChain,有專門的clang/lib/Driver/ToolChains/RISCVToolchain.h、
clang/lib/Driver/ToolChains/RISCVToolchain.cpp來定義和實現相關的內容。
3、Driver類中本身帶有一個成員變量ToolChains,用來緩存driver使用的所有的ToolChain,它的具體代碼如下(clang/include/clang/Driver/Driver.h):
/// Cache of all the ToolChains in use by the driver.
///
/// This maps from the string representation of a triple to a ToolChain
/// created targeting that triple. The driver owns all the ToolChain objects
/// stored in it, and will clean them up when torn down.
mutable llvm::StringMap<std::unique_ptr<ToolChain>> ToolChains;
三、總結
ToolChain上接Driver,和Driver有雙向的綁定關係;下接具體的目標平臺的ToolChain子類,爲具體目標平臺的ToolChain子類實現提供相關信息,這裏面就包含了一系列的Tool信息及操作。有關Tool的相關信息,在後續文章中專門介紹。
編輯於 2020-05-14