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

Posted snsn1984

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入研究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

以上是关于深入研究Clang(十五) Clang的RISCV支持1的主要内容,如果未能解决你的问题,请参考以下文章

深入研究Clang(十九) Clang的RISCV支持2

深入研究Clang(十九) Clang的RISCV支持2

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

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

深入研究Clang(十八) Clang Driver库的job

深入研究Clang(十八) Clang Driver库的job