オブジェクトファイルの生成
llcはアセンブリファイルを出力するだけでなく、オブジェクトファイルを出力する機能もある。オブジェクトファイルを出力するためには、llcのオプションで-filetype=asm
の代わりに-filetype=obj
を指定する。
$ ./bin/llc -debug -march=myriscvx32 -filetype=obj simple_main.bc -o - ... ./bin/llc: warning: target does not support generation of this file type!
オブジェクトファイルを出力するためには、第xxx章で実装したLLVMInitializeMYRISCVXTargetMC
にさらに機能を追加する。これらの機能は、MYRISCVXMCTargetDesc
でTargetMCとして登録する。登録したのは以下の4つの機能だ。
llvm-myriscvx80/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXMCTargetDesc.cpp
@@ -109,6 +133,17 @@ extern "C" void LLVMInitializeMYRISCVXTargetMC() { // Register the MCInstPrinter. TargetRegistry::RegisterMCInstPrinter(*T, createMYRISCVXMCInstPrinter); + // Register the elf streamer. + TargetRegistry::RegisterELFStreamer(*T, createMCStreamer); + + // Register the asm target streamer. + TargetRegistry::RegisterAsmTargetStreamer(*T, createMYRISCVXAsmTargetStreamer); + + // Register the MC Code Emitter + TargetRegistry::RegisterMCCodeEmitter(*T, createMYRISCVXMCCodeEmitter); + + // Register the asm backend. + TargetRegistry::RegisterMCAsmBackend(*T, createMYRISCVXAsmBackend); } }
RegisterELFStreamer
RegisterAsmTargetStreamer
RegisterMCCodeEmitter
RegisterMCAsmBackend
RegisterELFStreamer
commit:5b32e6b87bc Add MCStreamer / MYRISCVXAsmTargetStreamer
RegisterELFStreamer
はオブジェクトファイル出力のためのStreamerだ。
extern "C" void LLVMInitializeMYRISCVXTargetMC() { for (Target *T : {&getTheMYRISCVX32Target(), &getTheMYRISCVX64Target()}) { ... // Register the elf streamer. TargetRegistry::RegisterELFStreamer(*T, createMCStreamer); ... } } // 上記のcreateMCStreamerの実体は同じファイルに定義されている。 static MCStreamer *createMCStreamer(const Triple &T, MCContext &Context, std::unique_ptr<MCAsmBackend> &&MAB, std::unique_ptr<MCObjectWriter> &&OW, std::unique_ptr<MCCodeEmitter> &&Emitter, bool RelaxAll) { return createELFStreamer(Context, std::move(MAB), std::move(OW), std::move(Emitter), RelaxAll); }
llvm-myriscvx80/lib/MC/MCELFStreamer.cpp
MCStreamer *llvm::createELFStreamer(MCContext &Context, std::unique_ptr<MCAsmBackend> &&MAB, std::unique_ptr<MCObjectWriter> &&OW, std::unique_ptr<MCCodeEmitter> &&CE, bool RelaxAll) { MCELFStreamer *S = new MCELFStreamer(Context, std::move(MAB), std::move(OW), std::move(CE)); if (RelaxAll) S->getAssembler().setRelaxAll(true); return S; }
llvm-myriscvx80/lib/MC/MCELFStreamer.cpp
MCELFStreamer::MCELFStreamer(MCContext &Context, std::unique_ptr<MCAsmBackend> TAB, std::unique_ptr<MCObjectWriter> OW, std::unique_ptr<MCCodeEmitter> Emitter) : MCObjectStreamer(Context, std::move(TAB), std::move(OW), std::move(Emitter)) {}
最終的にはMCObjectStreamer
というクラスに到達する。
llvm-myriscvx80/include/llvm/MC/MCObjectStreamer.h
/// Streaming object file generation interface. /// /// This class provides an implementation of the MCStreamer interface which is /// suitable for use with the assembler backend. Specific object file formats /// are expected to subclass this interface to implement directives specific /// to that file format or custom semantics expected by the object writer /// implementation. class MCObjectStreamer : public MCStreamer { std::unique_ptr<MCAssembler> Assembler; MCSection::iterator CurInsertionPoint; bool EmitEHFrame; bool EmitDebugFrame; SmallVector<MCSymbol *, 2> PendingLabels; ...
TargetRegistry::RegisterELFStreamer()
を呼び出した時点では、createMCStreamer
は関数ポインタが登録されるだけで、引数は何もつけられていない。
この時点では、関数ポインタのみを登録して必要な時に引数を設定して呼び出すのだ。では、具体的にどのような引数が付けられるかというと
MCAsmBackend
: バックエンドに関する基本的な情報MCObjectWriter
: オブジェクトの出力を行うためのWriterMCCodeEmitter
: コードつまり命令をエンコーディングするための情報
を引数として受け取ります。MCObjectStreamer
が持つMCAssembler
がこれらの情報を利用して命令を出力する。
RegisterAsmTargetStreamer
RegisterAsmTargetStreamer
は、アセンブリ命令出力のためのStreamerだ。
llvm-myriscvx80/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXMCTargetDesc.cpp
extern "C" void LLVMInitializeMYRISCVXTargetMC() { for (Target *T : {&getTheMYRISCVX32Target(), &getTheMYRISCVX64Target()}) { ... // Register the asm target streamer. TargetRegistry::RegisterAsmTargetStreamer(*T, createMYRISCVXAsmTargetStreamer); ... } } // 上記のcreateMYRISCVXAsmTargetStreamerは同じファイル内の以下のコード。MYRISCVXTargetAsmStreamerを生成する。 static MCTargetStreamer *createMYRISCVXAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS, MCInstPrinter *InstPrint, bool isVerboseAsm) { return new MYRISCVXTargetAsmStreamer(S, OS); }
llvm-myriscvx80/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXTargetStreamer.cpp
// MYRISCVXTargetAsmStreamerは以下の2つのコンストラクタを経由してMCTargetStreamerが生成される。
MYRISCVXTargetStreamer::MYRISCVXTargetStreamer(MCStreamer &S)
: MCTargetStreamer(S) {
}
MYRISCVXTargetAsmStreamer::MYRISCVXTargetAsmStreamer(MCStreamer &S,
formatted_raw_ostream &OS)
: MYRISCVXTargetStreamer(S), OS(OS) {}
MCTargetStreamer
をベースとするわけだが、アセンブリファイルの出力に必要なプリント処理を行う。createMYRISCVXAsmTargetStreamer()
ではMCInstPrint
を引数に取り、命令の出力を行う。
RegisterMCCodeEmitter
commit:3815fb8ce30 Implement MCCodeEmitter
llvm-myriscvx80/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXMCCodeEmitter.cpp
MCCodeEmitter *createMYRISCVXMCCodeEmitter(const MCInstrInfo &MCII, const MCRegisterInfo &MRI, MCContext &Ctx) { return new MYRISCVXMCCodeEmitter(MCII, Ctx, true); }
llvm-myriscvx80/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXMCCodeEmitter.h
MYRISCVXMCCodeEmitter(const MCInstrInfo &mcii, MCContext &Ctx_, bool IsLittle) : MCII(mcii), Ctx(Ctx_), IsLittleEndian(IsLittle) {}
実体は、MCCodeEmitter
というクラスを継承している。MCCodeEmitter
は、命令をエンコーディングするためのクラスだ。
llvm-myriscvx80/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXMCCodeEmitter.h
class MYRISCVXMCCodeEmitter : public MCCodeEmitter { MYRISCVXMCCodeEmitter(const MYRISCVXMCCodeEmitter &) = delete; void operator=(const MYRISCVXMCCodeEmitter &) = delete; const MCInstrInfo &MCII; MCContext &Ctx; bool IsLittleEndian; ...
llvm-myriscvx80/include/llvm/MC/MCCodeEmitter.h
/// MCCodeEmitter - Generic instruction encoding interface. class MCCodeEmitter { protected: // Can only create subclasses. MCCodeEmitter(); ...
createMYRISCVXMCCodeEmitter()
ではMCInstrInfo
とMCRegisterInfo
クラスを引数に取っている。実際にはMCRegisterInfo
は使用しないのだが、MCInstrInfo
クラスの情報を用いて命令の生成を行う。
RegisterMCAsmBackend
commit:53b739059f9 Implement MYRISCVXAsmBackend
llvm-myriscvx80/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXMCTargetDesc.cpp
// Register the asm backend.
TargetRegistry::RegisterMCAsmBackend(*T, createMYRISCVXAsmBackend);
MYRISCVXMCAsmBackend
くらすは、MCAsmBackend
を継承しているクラスだ。
llvm-myriscvx80/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXAsmBackend.cpp
// MCAsmBackend MCAsmBackend *llvm::createMYRISCVXAsmBackend(const Target &T, const MCSubtargetInfo &STI, const MCRegisterInfo &MRI, const MCTargetOptions &Options) { return new MYRISCVXAsmBackend(T, MRI, STI.getTargetTriple(), STI.getCPU()); }
llvm-myriscvx80/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXAsmBackend.h
MYRISCVXAsmBackend(const Target &T, const MCRegisterInfo &MRI, const Triple &TT, StringRef CPU) : MCAsmBackend(TT.isLittleEndian() ? support::little : support::big), TheTriple(TT) {}
llvm-myriscvx80/include/llvm/MC/MCAsmBackend.h
/// Generic interface to target specific assembler backends. class MCAsmBackend { std::unique_ptr<MCCodePadder> CodePadder; ...
このMCAsmBackend
は抽象的なクラスで、いくつかのメソッドは実装されていないので継承したMYRISCVXAsmBackend
側で実装しなければならない。具体的には、以下のメソッドをMYRISCVXAsmBackend
で実装する必要がある。
createObjectTargetWriter() const = 0; /// Get the number of target specific fixup kinds. virtual unsigned getNumFixupKinds() const = 0; /// Apply the \p Value for given \p Fixup into the provided data fragment, at /// the offset specified by the fixup and following the fixup kind as /// appropriate. Errors (such as an out of range fixup value) should be /// reported via \p Ctx. /// The \p STI is present only for fragments of type MCRelaxableFragment and /// MCDataFragment with hasInstructions() == true. virtual void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef<char> Data, uint64_t Value, bool IsResolved, const MCSubtargetInfo *STI) const = 0; /// Check whether the given instruction may need relaxation. /// /// \param Inst - The instruction to test. /// \param STI - The MCSubtargetInfo in effect when the instruction was /// encoded. virtual bool mayNeedRelaxation(const MCInst &Inst, const MCSubtargetInfo &STI) const = 0; /// Simple predicate for targets where !Resolved implies requiring relaxation virtual bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, const MCRelaxableFragment *DF, const MCAsmLayout &Layout) const = 0; /// Relax the instruction in the given fragment to the next wider instruction. /// /// \param Inst The instruction to relax, which may be the same as the /// output. /// \param STI the subtarget information for the associated instruction. /// \param [out] Res On return, the relaxed instruction. virtual void relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, MCInst &Res) const = 0; /// Write an (optimal) nop sequence of Count bytes to the given output. If the /// target cannot generate such a sequence, it should return an error. /// /// \return - True on success. virtual bool writeNopData(raw_ostream &OS, uint64_t Count) const = 0;
それぞれ、MYRISCVXAsmBackend.h
, MYRISCVXAsmBackend.cpp
に実体を記述する。命令のRelaxなどに関する処理は、今回は実装していない。