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 Clang;
mutable std::unique_ptr Flang;
mutable std::unique_ptr Assemble;
mutable std::unique_ptr Link;
mutable std::unique_ptr IfsMerge;
mutable std::unique_ptr OffloadBundler;
mutable std::unique_ptr 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
// 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(*this, Target, Args);
break;
case llvm::Triple::Haiku:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::Ananas:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::CloudABI:
TC = std::make_unique(*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(*this, Target, Args);
break;
case llvm::Triple::DragonFly:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::OpenBSD:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::NetBSD:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::FreeBSD:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::Minix:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::Linux:
case llvm::Triple::ELFIAMCU:
if (Target.getArch() == llvm::Triple::hexagon)
TC = std::make_unique(*this, Target,
Args);
else if ((Target.getVendor() == llvm::Triple::MipsTechnologies) &&
!Target.hasEnvironment())
TC = std::make_unique(*this, Target,
Args);
else if (Target.getArch() == llvm::Triple::ppc ||
Target.getArch() == llvm::Triple::ppc64 ||
Target.getArch() == llvm::Triple::ppc64le)
TC = std::make_unique(*this, Target,
Args);
else
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::NaCl:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::Fuchsia:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::Solaris:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::AMDHSA:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::AMDPAL:
case llvm::Triple::Mesa3D:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::Win32:
switch (Target.getEnvironment()) {
default:
if (Target.isOSBinFormatELF())
TC = std::make_unique(*this, Target, Args);
else if (Target.isOSBinFormatMachO())
TC = std::make_unique(*this, Target, Args);
else
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::GNU:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::Itanium:
TC = std::make_unique(*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(
*this, Target, Args);
else
TC =
std::make_unique(*this, Target, Args);
break;
}
break;
case llvm::Triple::PS4:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::Contiki:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::Hurd:
TC = std::make_unique(*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(*this, Target, Args);
break;
case llvm::Triple::tcele:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::hexagon:
TC = std::make_unique(*this, Target,
Args);
break;
case llvm::Triple::lanai:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::xcore:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::wasm32:
case llvm::Triple::wasm64:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::avr:
TC = std::make_unique(*this, Target, Args);
break;
case llvm::Triple::msp430:
TC =
std::make_unique(*this, Target, Args);
break;
case llvm::Triple::riscv32:
case llvm::Triple::riscv64:
TC = std::make_unique(*this, Target, Args);
break;
default:
if (Target.getVendor() == llvm::Triple::Myriad)
TC = std::make_unique(*this, Target,
Args);
else if (toolchains::BareMetal::handlesTarget(Target))
TC = std::make_unique(*this, Target, Args);
else if (Target.isOSBinFormatELF())
TC = std::make_unique(*this, Target, Args);
else if (Target.isOSBinFormatMachO())
TC = std::make_unique(*this, Target, Args);
else
TC = std::make_unique(*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(*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> ToolChains;
三、总结
ToolChain上接Driver,和Driver有双向的绑定关系;下接具体的目标平台的ToolChain子类,为具体目标平台的ToolChain子类实现提供相关信息,这里面就包含了一系列的Tool信息及操作。有关Tool的相关信息,在后续文章中专门介绍。
编辑于 2020-05-14