Printing an assembly instruction is an important step in generating target code. Various classes are defined that work as a gateway to the streamers.
First, we initialize the class for instruction, assigning the operands, the assembly string, pattern, the output variable, and so on in the TOYInstrFormats.td
file:
class InstTOY<dag outs, dag ins, string asmstr, list<dag> pattern> : Instruction { field bits<32> Inst; let Namespace = "TOY"; dag OutOperandList = outs; dag InOperandList = ins; let AsmString = asmstr; let Pattern = pattern; let Size = 4; }
Then, we define functions to print operands in TOYInstPrinter.cpp
.
void TOYInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O) { const MCOperand &Op = MI->getOperand(OpNo); if (Op.isReg()) { printRegName(O, Op.getReg()); return; } if (Op.isImm()) { O << "#" << Op.getImm(); return; } assert(Op.isExpr() && "unknown operand kind in printOperand"); printExpr(Op.getExpr(), O); }
This function simply prints operands, registers, or immediate values, as the case may be.
We also define a function to print the register names in the same file:
void TOYInstPrinter::printRegName(raw_ostream &OS, unsigned RegNo) const { OS << StringRef(getRegisterName(RegNo)).lower(); }
Next, we define a function to print the instruction:
void TOYInstPrinter::printInst(const MCInst *MI, raw_ostream &O, StringRef Annot) { printInstruction(MI, O); printAnnotation(O, Annot); }
Next, we declare and define assembly info as follows:
We create a TOYMCAsmInfo.h
and declare an ASMInfo
class:
#ifndef TOYTARGETASMINFO_H #define TOYTARGETASMINFO_H #include "llvm/MC/MCAsmInfoELF.h" namespace llvm { class StringRef; class Target; class TOYMCAsmInfo : public MCAsmInfoELF { virtual void anchor(); public: explicit TOYMCAsmInfo(StringRef TT); }; } // namespace llvm #endif
The constructor can be defined in TOYMCAsmInfo.cpp
as follows:
#include "TOYMCAsmInfo.h" #include "llvm/ADT/StringRef.h" using namespace llvm; void TOYMCAsmInfo::anchor() {} TOYMCAsmInfo::TOYMCAsmInfo(StringRef TT) { SupportsDebugInformation = true; Data16bitsDirective = " .short "; Data32bitsDirective = " .long "; Data64bitsDirective = 0; ZeroDirective = " .space "; CommentString = "#"; AscizDirective = ".asciiz"; HiddenVisibilityAttr = MCSA_Invalid; HiddenDeclarationVisibilityAttr = MCSA_Invalid; ProtectedVisibilityAttr = MCSA_Invalid; }
For compilation, we define LLVMBuild.txt
as follows:
[component_0] type = Library name = TOYAsmPrinter parent = TOY required_libraries = MC Support add_to_library_groups = TOY
Furthermore, we define the CMakeLists.txt
file as follows:
add_llvm_library(LLVMTOYAsmPrinter TOYInstPrinter.cpp )
When the final compilation takes place, the llc
tool—a static compiler—will generate the assembly of the TOY
architecture (after registering the TOY architecture with the llc
tool).
To register our TOY
target with static compiler llc
, follow the steps mentioned below:
TOY
backend to llvm_root_dir/CMakeLists.txt
:set(LLVM_ALL_TARGETS AArch64 ARM … … TOY )
toy
entry to llvm_root_dir/include/llvm/ADT/Triple.h
:class Triple { public: enum ArchType { UnknownArch, arm, // ARM (little endian): arm, armv.*, xscale armeb, // ARM (big endian): armeb aarch64, // AArch64 (little endian): aarch64 … … toy // TOY: toy };
toy
entry to llvm_root_dir/include/llvm/ MC/MCExpr.h
:class MCSymbolRefExpr : public MCExpr { public: enum VariantKind { ... VK_TOY_LO, VK_TOY_HI, };
toy
entry to llvm_root_dir/include/llvm/ Support/ELF.h
:enum { EM_NONE = 0, // No machine EM_M32 = 1, // AT&T WE 32100 … … EM_TOY = 220 // whatever is the next number };
toy
entry to lib/MC/MCExpr.cpp
:StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) { switch (Kind) { … … case VK_TOY_LO: return "TOY_LO"; case VK_TOY_HI: return "TOY_HI"; } … }
toy
entry to lib/Support/Triple.cpp
:const char *Triple::getArchTypeName(ArchType Kind) { switch (Kind) { … … case toy: return "toy"; } const char *Triple::getArchTypePrefix(ArchType Kind) { switch (Kind) { … … case toy: return "toy"; } } Triple::ArchType Triple::getArchTypeForLLVMName(StringRef Name) { … … .Case("toy", toy) … } static Triple::ArchType parseArch(StringRef ArchName) { … … .Case("toy", Triple::toy) … } static unsigned getArchPointerBitWidth(llvm::Triple::ArchType Arch) { … … case llvm::Triple::toy: return 32; … … } Triple Triple::get32BitArchVariant() const { … … case Triple::toy: // Already 32-bit. break; … } Triple Triple::get64BitArchVariant() const { … … case Triple::toy: T.setArch(UnknownArch); break; … … }
toy
directory entry to lib/Target/LLVMBuild.txt
:[common] subdirectories = ARM AArch64 CppBackend Hexagon MSP430 … … TOY
TOY.h
in the lib/Target/TOY
folder:#ifndef TARGET_TOY_H #define TARGET_TOY_H #include "MCTargetDesc/TOYMCTargetDesc.h" #include "llvm/Target/TargetMachine.h" namespace llvm { class TargetMachine; class TOYTargetMachine; FunctionPass *createTOYISelDag(TOYTargetMachine &TM, CodeGenOpt::Level OptLevel); } // end namespace llvm; #endif
TargetInfo
in the lib/Target/TOY
folder. Inside that folder, create a new file called TOYTargetInfo.cpp
, as follows:#include "TOY.h" #include "llvm/IR/Module.h" #include "llvm/Support/TargetRegistry.h" using namespace llvm; Target llvm::TheTOYTarget; extern "C" void LLVMInitializeTOYTargetInfo() { RegisterTarget<Triple::toy> X(TheTOYTarget, "toy", "TOY"); }
CMakeLists.txt
file:add_llvm_library(LLVMTOYInfo TOYTargetInfo.cpp)
LLVMBuild.txt
file:[component_0] type = Library name = TOYInfo parent = TOY required_libraries = Support add_to_library_groups = TOY
lib/Target/TOY
folder, create a file called TOYTargetMachine.cpp
:#include "TOYTargetMachine.h" #include "TOY.h" #include "TOYFrameLowering.h" #include "TOYInstrInfo.h" #include "TOYISelLowering.h " #include "TOYSelectionDAGInfo.h" #include "llvm/CodeGen/Passes.h" #include "llvm/IR/Module.h" #include "llvm/PassManager.h" #include "llvm/Support/TargetRegistry.h" using namespace llvm; TOYTargetMachine::TOYTargetMachine(const Target &T, StringRef TT, StringRef CPU, StringRef FS, const TargetOptions &Options, Reloc::Model RM, CodeModel::Model CM, CodeGenOpt::Level OL) : LLVMTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL), Subtarget(TT, CPU, FS, *this) { initAsmInfo(); } namespace { class TOYPassConfig : public TargetPassConfig { public: TOYPassConfig(TOYTargetMachine *TM, PassManagerBase &PM) : TargetPassConfig(TM, PM) {} TOYTargetMachine &getTOYTargetMachine() const { return getTM<TOYTargetMachine>(); } virtual bool addPreISel(); virtual bool addInstSelector(); virtual bool addPreEmitPass(); }; } // namespace TargetPassConfig *TOYTargetMachine::createPassConfig (PassManagerBase &PM) { return new TOYPassConfig(this, PM); } bool TOYPassConfig::addPreISel() { return false; } bool TOYPassConfig::addInstSelector() { addPass(createTOYISelDag(getTOYTargetMachine(), getOptLevel())); return false; } bool TOYPassConfig::addPreEmitPass() { return false; } // Force static initialization. extern "C" void LLVMInitializeTOYTarget() { RegisterTargetMachine<TOYTargetMachine> X(TheTOYTarget); } void TOYTargetMachine::addAnalysisPasses(PassManagerBase &PM) {}
MCTargetDesc
and a new file called TOYMCTargetDesc.h
:#ifndef TOYMCTARGETDESC_H #define TOYMCTARGETDESC_H #include "llvm/Support/DataTypes.h" namespace llvm { class Target; class MCInstrInfo; class MCRegisterInfo; class MCSubtargetInfo; class MCContext; class MCCodeEmitter; class MCAsmInfo; class MCCodeGenInfo; class MCInstPrinter; class MCObjectWriter; class MCAsmBackend; class StringRef; class raw_ostream; extern Target TheTOYTarget; MCCodeEmitter *createTOYMCCodeEmitter(const MCInstrInfo &MCII, const MCRegisterInfo &MRI, const MCSubtargetInfo &STI, MCContext &Ctx); MCAsmBackend *createTOYAsmBackend(const Target &T, const MCRegisterInfo &MRI, StringRef TT, StringRef CPU); MCObjectWriter *createTOYELFObjectWriter(raw_ostream &OS, uint8_t OSABI); } // End llvm namespace #define GET_REGINFO_ENUM #include "TOYGenRegisterInfo.inc" #define GET_INSTRINFO_ENUM #include "TOYGenInstrInfo.inc" #define GET_SUBTARGETINFO_ENUM #include "TOYGenSubtargetInfo.inc" #endif
TOYMCTargetDesc.cpp
, in the same folder:#include "TOYMCTargetDesc.h" #include "InstPrinter/TOYInstPrinter.h" #include "TOYMCAsmInfo.h" #include "llvm/MC/MCCodeGenInfo.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/TargetRegistry.h" #define GET_INSTRINFO_MC_DESC #include "TOYGenInstrInfo.inc" #define GET_SUBTARGETINFO_MC_DESC #include "TOYGenSubtargetInfo.inc" #define GET_REGINFO_MC_DESC #include "TOYGenRegisterInfo.inc" using namespace llvm; static MCInstrInfo *createTOYMCInstrInfo() { MCInstrInfo *X = new MCInstrInfo(); InitTOYMCInstrInfo(X); return X; } static MCRegisterInfo *createTOYMCRegisterInfo(StringRef TT) { MCRegisterInfo *X = new MCRegisterInfo(); InitTOYMCRegisterInfo(X, TOY::LR); return X; } static MCSubtargetInfo *createTOYMCSubtargetInfo(StringRef TT, StringRef CPU, StringRef FS) { MCSubtargetInfo *X = new MCSubtargetInfo(); InitTOYMCSubtargetInfo(X, TT, CPU, FS); return X; } static MCAsmInfo *createTOYMCAsmInfo(const MCRegisterInfo &MRI, StringRef TT) { MCAsmInfo *MAI = new TOYMCAsmInfo(TT); return MAI; } static MCCodeGenInfo *createTOYMCCodeGenInfo(StringRef TT, Reloc::Model RM, CodeModel::Model CM, CodeGenOpt::Level OL) { MCCodeGenInfo *X = new MCCodeGenInfo(); if (RM == Reloc::Default) { RM = Reloc::Static; } if (CM == CodeModel::Default) { CM = CodeModel::Small; } if (CM != CodeModel::Small && CM != CodeModel::Large) { report_fatal_error("Target only supports CodeModel Small or Large"); } X->InitMCCodeGenInfo(RM, CM, OL); return X; } static MCInstPrinter * createTOYMCInstPrinter(const Target &T, unsigned SyntaxVariant, const MCAsmInfo &MAI, const MCInstrInfo & MII, const MCRegisterInfo &MRI, const MCSubtargetInfo &STI) { return new TOYInstPrinter(MAI, MII, MRI); } static MCStreamer * createMCAsmStreamer(MCContext &Ctx, formatted_raw_ostream &OS, bool isVerboseAsm, bool useDwarfDirectory, MCInstPrinter *InstPrint, MCCodeEmitter *CE, MCAsmBackend *TAB, bool ShowInst) { return createAsmStreamer(Ctx, OS, isVerboseAsm, useD - warfDirectory, InstPrint, CE, TAB, ShowInst); } static MCStreamer *createMCStreamer(const Target &T, StringRef TT, MCContext &Ctx, MCAsmBackend &MAB, raw_ostream &OS, MCCodeEmitter *Emitter, const MCSubtargetInfo &STI, bool RelaxAll, bool NoExecStack) { return createELFStreamer(Ctx, MAB, OS, Emitter, false, NoExecStack); } // Force static initialization. extern "C" void LLVMInitializeTOYTargetMC() { // Register the MC asm info. RegisterMCAsmInfoFn X(TheTOYTarget, createTOYMCAsmInfo); // Register the MC codegen info. TargetRegistry::RegisterMCCodeGenInfo(TheTOYTarget, createTOYMCCodeGenInfo); // Register the MC instruction info. TargetRegistry::RegisterMCInstrInfo(TheTOYTarget, createTOYMCInstrInfo); // Register the MC register info. TargetRegistry::RegisterMCRegInfo(TheTOYTarget, createTOYMCRegisterInfo); // Register the MC subtarget info. TargetRegistry::RegisterMCSubtargetInfo(TheTOYTarget, createTOYMCSub targetInfo); // Register the MCInstPrinter TargetRegistry::RegisterMCInstPrinter(TheTOYTarget, createTOYMCInstPrinter); // Register the ASM Backend. TargetRegistry::RegisterMCAsmBackend(TheTOYTarget, createTOYAsmBackend); // Register the assembly streamer. TargetRegistry::RegisterAsmStreamer(TheTOYTarget, createMCAsmStreamer); // Register the object streamer. TargetRegistry::RegisterMCObjectStreamer(TheTOYTarget, createMCStreamer); // Register the MCCodeEmitter TargetRegistry::RegisterMCCodeEmitter(TheTOYTarget, createTOYMCCodeEmitter); }
LLVMBuild.txt
file:[component_0] type = Library name = TOYDesc parent = TOY required_libraries = MC Support TOYAsmPrinter TOYInfo add_to_library_groups = TOY
add_llvm_library(LLVMTOYDesc TOYMCTargetDesc.cpp)
Build the enitre LLVM project, as follows:
$ cmake llvm_src_dir –DCMAKE_BUILD_TYPE=Release – DLLVM_TARGETS_TO_BUILD="TOY" $ make Here, we have specified that we are building the LLVM compiler for the toy target. After the build completes, check whether the TOY target appears with the llc command: $ llc –version … … Registered Targets : toy – TOY
The following IR, when given to the llc
tool, will generate an assembly as shown:
target datalayout = "e-m:e-p:32:32-i1:8:32-i8:8:32- i16:16:32-i64:32-f64:32-a:0:32-n32" target triple = "toy" define i32 @foo(i32 %a, i32 %b){ %c = add nsw i32 %a, %b ret i32 %c } $ llc foo.ll .text .file "foo.ll" .globl foo .type foo,@function foo: # @foo # BB#0: # %entry add r0, r0, r1 b lr .Ltmp0: .size foo, .Ltmp0-foo
To see the details of how to register a target with llc
, you can visit http://llvm.org/docs/WritingAnLLVMBackend.html#target-registration and http://jonathan2251.github.io/lbd/llvmstructure.html#target-registration by Chen Chung-Shu and Anoushe Jamshidi.
3.136.233.153