一、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