LLVMについて理解するために、RISC-Vをネタにしていろいろ勉強してみたい。 Release 7.0では一応RISC-Vの32-bitと64-bit版がサポートされているようなので、ビルドに追加して様子を見てみる。
次はRISCV_msyksphinzのターゲットアーキテクチャについて定義していく。 Cpu0をベースにして、少しずつコードを追加していこう。
computeDataLayout(const Triple &TT, StringRef CPU, const TargetOptions &Options, bool isLittle)
アーキテクチャのデータレイアウトを定義する関数。データレイアウトについてはさっぱり知らなかったので、資料を参考にしながら読み解いていく。
LLVM Language Reference Manual — LLVM 8 documentation
static std::string computeDataLayout(const Triple &TT, StringRef CPU, const TargetOptions &Options, bool isLittle) {
データレイアウトがリトルエンディアンならば"e", ビッグエンディアンならば "E"とする。 RISC-Vにはリトルエンディアンしか存在しないので"e"で良いと思う。
if (isLittle) Ret += "e"; else Ret += "E";
C++などでも使用されるマングリング?を指定する。ここで"m"となっているので、MIPS形式のマングリングが適用されるのだと思う。
Ret += "-m:m";
ポインタと変数のアラインメントについて。Cpu0では、以下のように定義されていた。 ポインタについては32ビット長で、32bitアライメントを前提とする。その次の行は、8bit、16bitはそれぞれそのビット長でアライメントしてよいが、実際には32bitでアライメントしてほしいことを意味している?
// Pointers are 32 bit on some ABIs. Ret += "-p:32:32"; // 8 and 16 bit integers only need to have natural alignment, but try to // align them to 32 bits. 64 bit integers have natural alignment. Ret += "-i8:8:32-i16:16:32-i64:64"; // 32 bit registers are always available and the stack is at least 64 bit // aligned. Ret += "-n32-S64";
RISCVの実装を参考にすると、以下のようになっているので、
if (TT.isArch64Bit()) { return "e-m:e-p:64:64-i64:64-i128:128-n64-S128"; } else { assert(TT.isArch32Bit() && "only RV32 and RV64 are currently supported"); return "e-m:e-p:32:32-i64:64-n32-S128"; }
- RV64のときは64bit長でポインタをアライメント、変数は64bitでアライメントすべき。128bitの変数もアライメントを定義する。
- RV32のときは32bit長でポインタをアライメント、64bitと128bitの変数もアライメントを定義する。
RISCV_msyksphinzアーキテクチャのインスタンス化
そして、このComptueDataLayoutを使用してアーキテクチャを定義する関数は2つ用意されている。
RISCV_msyksphinzebTargetMachine::RISCV_msyksphinzebTargetMachine(const Target &T, const Triple &TT, StringRef CPU, StringRef FS, const TargetOptions &Options, Optional<Reloc::Model> RM, CodeModel::Model CM, CodeGenOpt::Level OL) : RISCV_msyksphinzTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, false) {} void RISCV_msyksphinzelTargetMachine::anchor() { } RISCV_msyksphinzelTargetMachine::RISCV_msyksphinzelTargetMachine(const Target &T, const Triple &TT, StringRef CPU, StringRef FS, const TargetOptions &Options, Optional<Reloc::Model> RM, CodeModel::Model CM, CodeGenOpt::Level OL) : RISCV_msyksphinzTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, true) {}
ここで、ビッグエンディアンは必要ないので削除してしまおう。RISCV_msyksphinzebTargetMachine
の方だ。ebとelが微妙に異なる。
そして、LLVMのターゲットを作成するのはLLVMInitializeRISCV_msyksphinzTarget()
である。これもビッグエンディアン用が存在したのだが削除した。
extern "C" void LLVMInitializeRISCV_msyksphinzTarget() { // Register the target. //- Little endian Target Machine RegisterTargetMachine<RISCV_msyksphinzelTargetMachine> Y(TheRISCV_msyksphinzelTarget); }