深入研究Clang(十五) Clang的RISCV支持1

一、Clang/LLVM對RISCV的支持概況

目前已經有一系列的C類編譯器和庫開始支持RISCV,這其中包括了GCC和Clang/LLVM。從RISCV的官方網站,可以獲取目前的支持狀態。具體內容如下:

網址:https://riscv.org/software-status/#c-compilers-and-libraries

該列表中還包含了所支持的License和Maintainers。其中,Clang/LLVM的Maintainers是Alex Bradbury,這位大神是LLVMWEEKLY的創建者和維護者,這個週報目前已經做到了331期,地址:http://llvmweekly.org/

Alex Bradbury目前在劍橋一家叫lowRISC的非營利性機構裏,這家機構也是RISCV基金會的成員(Silver級別),lowRISC的官網地址:https://www.lowrisc.org/

二、Clang源碼中的RISCV支持概況

目前Clang源碼之中的RISCV相關代碼,主要位於以下幾個部分:

1、Driver部分

clang/lib/Driver/ToolChains/RISCVToolchain.h

clang/lib/Driver/ToolChains/RISCVToolchain.cpp

clang/lib/Driver/ToolChains/Arch/RISCV.h

clang/lib/Driver/ToolChains/Arch/RISCV.cpp

這部分代碼,主要是用來構建Clang的RISCV toolchain。toolchain是Clang中的一個概念,這個概念主要是用來訪問一個平臺的多個tools的(註釋原文:ToolChain - Access to tools for a single platform.)。有關ToolChain的內容,可以做專門的文章分析,此處不展開。

2、Basic部分

clang/lib/Basic/Targets/RISCV.h

clang/lib/Basic/Targets/RISCV.cpp

這部分代碼,主要用來聲明RISCV的目標平臺信息對象,簡單的可以認爲是保存目標平臺的相關信息。在RISCV上,可以分爲32位和64位的目標平臺,具體針對了頭文件RISCV.h中的RISCV32TargetInfo和RISCV64TargetInfo,這兩個類繼承於RISCVTargetInfo,而RISCVTargetInfo繼承於TargetInfo類。

構建和使用RISCV32TargetInfo和RISCV64TargetInfo的代碼位於/clang/lib/Basic/Targets.cpp之中的TargetInfo *AllocateTarget(const llvm::Triple &Triple, const TargetOptions &Opts)函數,其具體代碼爲:

//===----------------------------------------------------------------------===//
// Driver code
//===----------------------------------------------------------------------===//

TargetInfo *AllocateTarget(const llvm::Triple &Triple,
                           const TargetOptions &Opts) {
  llvm::Triple::OSType os = Triple.getOS();

  switch (Triple.getArch()) {
  default:
    return nullptr;

  case llvm::Triple::arc:
    return new ARCTargetInfo(Triple, Opts);

  case llvm::Triple::xcore:
    return new XCoreTargetInfo(Triple, Opts);

  ...
  case llvm::Triple::riscv32:
    // TODO: add cases for NetBSD, RTEMS once tested.
    switch (os) {
    case llvm::Triple::FreeBSD:
      return new FreeBSDTargetInfo<RISCV32TargetInfo>(Triple, Opts);
    case llvm::Triple::Linux:
      return new LinuxTargetInfo<RISCV32TargetInfo>(Triple, Opts);
    default:
      return new RISCV32TargetInfo(Triple, Opts);
    }

  case llvm::Triple::riscv64:
    // TODO: add cases for NetBSD, RTEMS once tested.
    switch (os) {
    case llvm::Triple::FreeBSD:
      return new FreeBSDTargetInfo<RISCV64TargetInfo>(Triple, Opts);
    case llvm::Triple::Fuchsia:
      return new FuchsiaTargetInfo<RISCV64TargetInfo>(Triple, Opts);
    case llvm::Triple::Linux:
      return new LinuxTargetInfo<RISCV64TargetInfo>(Triple, Opts);
    default:
      return new RISCV64TargetInfo(Triple, Opts);
    }

  ...
  case llvm::Triple::renderscript32:
    return new LinuxTargetInfo<RenderScript32TargetInfo>(Triple, Opts);
  case llvm::Triple::renderscript64:
    return new LinuxTargetInfo<RenderScript64TargetInfo>(Triple, Opts);
  }
}
} // namespace targets
} // namespace clang

3、CodeGen部分

clang/lib/CodeGen/TargetInfo.cpp

1)這部分代碼,有兩個RISCV相關的類:一個是RISCVABIInfo,繼承於DefaultABIInfo,DefaultABIInfo繼承於ABIInfo;一個是RISCVTargetCodeGenInfo,繼承於TargetCodeGenInfo。RISCVABIInfo在RISCVTargetCodeGenInfo構造函數中被使用:

RISCVTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT, unsigned XLen,
                       unsigned FLen)
    : TargetCodeGenInfo(std::make_unique<RISCVABIInfo>(CGT, XLen, FLen)) {}

RISCVTargetCodeGenInfo是被const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo()函數所調用,調用時需滿足條件:

switch (Triple.getArch()) {
......
case llvm::Triple::riscv32:
case llvm::Triple::riscv64:

也就是這裏的Triple.getArch()爲riscv32或者riscv64,調用該部分內容。

2)CodeGenModule::getTargetCodeGenInfo()實際調用RISCVTargetCodeGenInfo的時候,傳給RISCVTargetCodeGenInfo的是自己的Types:

  case llvm::Triple::riscv32:
  case llvm::Triple::riscv64: {
    StringRef ABIStr = getTarget().getABI();
    unsigned XLen = getTarget().getPointerWidth(0);
    unsigned ABIFLen = 0;
    if (ABIStr.endswith("f"))
      ABIFLen = 32;
    else if (ABIStr.endswith("d"))
      ABIFLen = 64;
    return SetCGInfo(new RISCVTargetCodeGenInfo(Types, XLen, ABIFLen));
  }

這個Types在clang/lib/CodeGen/CodeGenModule.h之中可以看到定義爲:

// This should not be moved earlier, since its initializati on depends on some
// of the previous reference members being already initialized and also checks
// if TheTargetCodeGenInfo is NULL  
CodeGenTypes Types;

這個Types是在/clang/lib/CodeGen/CodeGenModule.cpp中CodeGenModule的構造函數中做的初始化:

CodeGenModule::CodeGenModule(ASTContext &C, const HeaderSearchOptions &HSO,
                             const PreprocessorOptions &PPO,
                             const CodeGenOptions &CGO, llvm::Module &M,
                             DiagnosticsEngine &diags,
                             CoverageSourceInfo *CoverageInfo)
    : Context(C), LangOpts(C.getLangOpts()), HeaderSearchOpts(HSO),
      PreprocessorOpts(PPO), CodeGenOpts(CGO), TheModule(M), Diags(diags),
      Target(C.getTargetInfo()), ABI(createCXXABI(*this)),
      VMContext(M.getContext()), Types(*this), VTables(*this),
      SanitizerMD(new SanitizerMetadata(*this)) {

3)本部分代碼和Baisc部分代碼的關係

CodeGenModule::getTargetCodeGenInfo()函數中,關於RISCV的代碼有如下兩行:

StringRef ABIStr =getTarget().getABI();
unsigned XLen =getTarget().getPointerWidth(0);

這兩行代碼都使用了getTarget()這個成員函數。這個成員函數是CodeGenModule的成員函數,其定義位於clang/lib/CodeGen/CodeGenModule.h之中:

const TargetInfo &getTarget() const { return Target; }

其中的Target定義爲:

const TargetInfo &Target;

這裏用到的TargetInfo的定義位於clang/include/clang/Basic/TargetInfo.h,它正是RISCVTargetInfo的父類。至此,已經和Basic部分聯繫上了。/clang/lib/CodeGen/目錄之下雖然也有TargetInfo.h和TargetInfo.cpp文件,但是這兩個文件裏並沒有TargetInfo類。

 

——————

另外,之前在BILIBILI做了一個小視頻,內容和本文相似,簡單聊Clang對RISCV的支持,感興趣的也可以看看:https://www.bilibili.com/video/BV1b7411j7S6

 

 

編輯於 2020-05-18

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章