深入研究Clang(十六) Clang Driver库的ToolChain

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 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(*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

你可能感兴趣的:(深入研究Clang)