MYRISCVXTargetMachine
はターゲットマシンを定義するファイルである。
また、MYRISCVXTargetMachine
を継承したクラスとして複数のターゲットマシンを作ることができる。
MIPSなどのバイエンディアンのアーキテクチャでは、リトルエンディアンとビックエンディアンの両方が定義されるが、RISC-Vではリトルエンディアンしか存在しないので1種類しか不要だ。
ここでは32ビット版と64ビット版のターゲットマシンを作りたいので、MYRISCVX32TargetMachine
とMYRISCVX64TargetMachine
を定義する。
MYRISCVXTargetMachine.h
/// MYRISCVX32TargetMachine - MYRISCVX32 little endian target machine. /// class MYRISCVX32TargetMachine : public MYRISCVXTargetMachine { virtual void anchor(); public: MYRISCVX32TargetMachine(const Target &T, const Triple &TT, StringRef CPU, StringRef FS, const TargetOptions &Options, Optional<Reloc::Model> RM, Optional<CodeModel::Model> CM, CodeGenOpt::Level OL, bool JIT); }; /// MYRISCVX64TargetMachine - MYRISCVX64 little endian target machine. /// class MYRISCVX64TargetMachine : public MYRISCVXTargetMachine { virtual void anchor(); public: MYRISCVX64TargetMachine(const Target &T, const Triple &TT, StringRef CPU, StringRef FS, const TargetOptions &Options, Optional<Reloc::Model> RM, Optional<CodeModel::Model> CM, CodeGenOpt::Level OL, bool JIT); };
これらのターゲットマシンをLLVM側に認識させるために、LLVMInitializeMYRISCVXTarget()
という関数を作る。この関数の名前ルールは決まっており、llcで名前が決まっている。
lib/Target/MYRISCVX/MYRISCVXTargetMachine.cpp
extern "C" void LLVMInitializeMYRISCVXTarget() { // Register the target. //- Little endian Target Machine RegisterTargetMachine<MYRISCVX32TargetMachine> X(getTheMYRISCVX32Target()); RegisterTargetMachine<MYRISCVX64TargetMachine> Y(getTheMYRISCVX64Target()); }
LLVMInitialize(Target名)Target()
という初期化用のメソッドはllvm::InitializeAllTargets()
から呼ばれる。
include/llvm/Support/TargetSelect.h
/// InitializeAllTargets - The main program should call this function if it /// wants access to all available target machines that LLVM is configured to /// support, to make them available via the TargetRegistry. /// /// It is legal for a client to make multiple calls to this function. inline void InitializeAllTargets() { // FIXME: Remove this, clients should do it. InitializeAllTargetInfos(); #define LLVM_TARGET(TargetName) LLVMInitialize##TargetName##Target(); #include "llvm/Config/Targets.def" }
まずは、InitializeAllTargetInfos()
が呼ばれる。
namespace llvm { /// InitializeAllTargetInfos - The main program should call this function if /// it wants access to all available targets that LLVM is configured to /// support, to make them available via the TargetRegistry. /// /// It is legal for a client to make multiple calls to this function. inline void InitializeAllTargetInfos() { #define LLVM_TARGET(TargetName) LLVMInitialize##TargetName##TargetInfo(); #include "llvm/Config/Targets.def" }
llvm/Config/Targets.def
はCMakeにてビルド用のコンフィグレーションを作成したときに自動的に生成される。
LLVM_TARGETS_TO_BULID
およびDLLVM_EXPERIMENTAL_TARGETS_TO_BUILD
で指定したターゲットについて、初期化用の関数が呼ばれるという仕組みだ。
build-myriscvx/include/llvm/Config/Targets.def
#ifndef LLVM_TARGET # error Please define the macro LLVM_TARGET(TargetName) #endif LLVM_TARGET(X86) LLVM_TARGET(Mips) LLVM_TARGET(MYRISCVX) # これが展開されて`LLVMInitializeMYRISCVXTargetInfo()`となる。 LLVM_TARGET(RISCV) #undef LLVM_TARGET
これによりMYRISCVXとしてはLLVMInitializeMYRISCVXTargetInfo()
およびLLVMInitializeMYRISCVXTarget()
が呼ばれる。
LLVMInitializeMYRISCVXTarget()
を見てみるとRegisterTargetMachineにてターゲットをLLVMに登録する。
この際getTheMYRISCVX32Target()
およびgetTheMYRISCVX64Target()
という関数が呼ばれているが、
これはlib/Target/MYRISCVX/TargetInfo/MYRISCVXTargetInfo.cpp
で定義されている。getTheMYRISCX{32,64}Target()
を一度呼び出すとそのインスタンスが生成され、以降は同じインスタンスが呼び出させる、という仕組みだ。
lib/Target/MYRISCVX/TargetInfo/MYRISCVXTargetInfo.cpp
extern "C" void LLVMInitializeMYRISCVXTarget() { // Register the target. //- Little endian Target Machine RegisterTargetMachine<MYRISCVX32TargetMachine> X(getTheMYRISCVX32Target()); RegisterTargetMachine<MYRISCVX64TargetMachine> Y(getTheMYRISCVX64Target()); }
lib/Target/MYRISCVX/TargetInfo/MYRISCVXTargetInfo.cpp
namespace llvm { Target &getTheMYRISCVX32Target() { static Target TheMYRISCVX32Target; return TheMYRISCVX32Target; } Target &getTheMYRISCVX64Target() { static Target TheMYRISCVX64Target; return TheMYRISCVX64Target; } }
一方でLLVMInitializeMYRISCVXTargetInfo()
では以下を実行する。ターゲットアーキテクチャを登録する。
extern "C" void LLVMInitializeMYRISCVXTargetInfo() { RegisterTarget<Triple::myriscvx32> X(getTheMYRISCVX32Target(), "myriscvx32", "MYRISCVX (32-bit)", "MYRISCVX"); RegisterTarget<Triple::myriscvx64> Y(getTheMYRISCVX64Target(), "myriscvx64", "MYRISCVX (64-bit)", "MYRISCVX"); }