Setting up the Nova target
Before we can start adding instructions, we need to set up some required configuration code. This contains the Nova subtarget, register and instruction information, frame and selection DAG lowering classes and a few others that set up the code generation pipeline.
The code we add here will allow us to build LLVM for the Nova target but not to compile any LLVM IR to assembly code yet.
Adding the NovaSubtarget
TableGen generates the NovaGenSubtarget
class. Add the tablegen invocation to
CMakeLists.txt:
tablegen(LLVM NovaGenRegisterInfo.inc -gen-register-info)
tablegen(LLVM NovaGenSubtargetInfo.inc -gen-subtarget)
Whip up a new header file for the subtarget class in lib/Target/Nova
. Here we
include the generated subtarget file from TableGen.
The Subtarget is the central place from where we access the target information per function. We will include some header files that we will create later on.
//===-- NovaSubtarget.h - Define Subtarget for the Nova --------*- C++ -*-===//
#ifndef LLVM_LIB_TARGET_NOVA_NOVASUBTARGET_H#define LLVM_LIB_TARGET_NOVA_NOVASUBTARGET_H
#include "NovaFrameLowering.h"#include "NovaISelLowering.h"#include "NovaInstrInfo.h"#include "NovaRegisterInfo.h"#include "llvm/CodeGen/SelectionDAGTargetInfo.h"#include "llvm/CodeGen/TargetSubtargetInfo.h"#include "llvm/IR/DataLayout.h"
#define GET_SUBTARGETINFO_HEADER#include "NovaGenSubtargetInfo.inc"
Add the fields for all info classes. These will be created ahead.
#include "NovaGenSubtargetInfo.inc"
namespace llvm {class NovaSubtarget : public NovaGenSubtargetInfo {protected: SelectionDAGTargetInfo TSInfo; NovaInstrInfo InstrInfo; NovaFrameLowering FrameLowering; NovaTargetLowering TLInfo; NovaRegisterInfo RegInfo;
Add the corresponding getters and the constructor, ending the file.
↳ class NovaSubtarget : public NovaGenSubtargetInfo {
NovaTargetLowering TLInfo; NovaRegisterInfo RegInfo;public: NovaSubtarget(const Triple &TT, StringRef CPU, StringRef FS, const TargetMachine &TM) : NovaGenSubtargetInfo(TT, CPU, CPU, FS), InstrInfo(*this), FrameLowering(*this, Align(8)), TLInfo(TM, *this) {}
const NovaRegisterInfo *getRegisterInfo() const override { return &RegInfo; }
const NovaInstrInfo *getInstrInfo() const override { return &InstrInfo; }
const NovaFrameLowering *getFrameLowering() const override { return &FrameLowering; }
const NovaTargetLowering *getTargetLowering() const override { return &TLInfo; }
const SelectionDAGTargetInfo *getSelectionDAGInfo() const override { return &TSInfo; } /// ParseSubtargetFeatures - Parses features string setting specified /// subtarget options. Definition of function is auto generated by tblgen. void ParseSubtargetFeatures(StringRef CPU, StringRef TuneCPU, StringRef FS);};
} // end namespace llvm#endif
Global functions
Targets have a few global functions to define or create codegen passes which
are consolidated in one header file. Create the Nova.h
file for our target.
This is empty for now.
/// This is for global functions in the Nova target.#ifndef LLVM_LIB_TARGET_NOVA_NOVA_H#define LLVM_LIB_TARGET_NOVA_NOVA_H#include "MCTargetDesc/NovaMCTargetDesc.h"
#endif
The definitions need to be added to the implementation file.
#include "Nova.h"#include "NovaSubtarget.h"#include "NovaRegisterInfo.h"#include "NovaTargetMachine.h"
using namespace llvm;
#define DEBUG_TYPE "nova-subtarget"
#define GET_SUBTARGETINFO_CTOR#define GET_SUBTARGETINFO_TARGET_DESC#include "NovaGenSubtargetInfo.inc"
Registering the subtarget
Include the generated file in NovaMCTargetDesc
files.
#include "NovaGenRegisterInfo.inc"
#define GET_SUBTARGETINFO_MC_DESC#include "NovaGenSubtargetInfo.inc"
static MCRegisterInfo* createNovaMCRegisterInfo(const Triple &TT) {
#include "NovaGenRegisterInfo.inc"
#define GET_SUBTARGETINFO_ENUM#include "NovaGenSubtargetInfo.inc"
This needs the definition of the MCSubtargetInfo
class which is the base.
#include "NovaMCTargetDesc.h"#include "NovaTargetInfo.h"#include "llvm/MC/MCSubtargetInfo.h"
We need to register the subtarget in NovaTargetMachine.cpp
as well.
}
static MCSubtargetInfo* createNovaSubtargetInfo(const Triple &TT, StringRef CPU, StringRef FS) { if (CPU.empty()) CPU = "generic"; return createNovaMCSubtargetInfoImpl(TT, CPU, CPU, FS);}
Target *T = &getTheNovaTarget(); TargetRegistry::RegisterMCRegInfo(*T, createNovaMCRegisterInfo); TargetRegistry::RegisterMCSubtargetInfo(*T, createNovaSubtargetInfo);
MCTargetDesc
The common place for the target descriptions is the NovaMCTargetDesc.h
file.
This has the instruction set, register info and subtarget information.
We already created this file in the registers section.
TargetObjectFile
Another class that is required but unused is NovaTargetObjectFile
.
#ifndef LLVM_LIB_TARGET_NOVA_NOVATARGETOBJECTFILE_H#define LLVM_LIB_TARGET_NOVA_NOVATARGETOBJECTFILE_H
#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
namespace llvm {class TargetMachine;class MCContext;class NovaTargetObjectFile final : public TargetLoweringObjectFileELF {public: void Initialize(MCContext &Ctx, const TargetMachine &TM) override;};} // namespace llvm
#endif
Write the Initialize
definition.
#include "NovaTargetObjectFile.h"#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"#include "llvm/MC/MCContext.h"#include "llvm/Target/TargetMachine.h"
using namespace llvm;
void NovaTargetObjectFile::Initialize(MCContext &Ctx, const TargetMachine &TM) { TargetLoweringObjectFileELF::Initialize(Ctx, TM);}
TargetMachine
The TargetMachine
class is the main entry point for the target. It contains
the codegeneration pipeline and other target information.
#ifndef LLVM_LIB_TARGET_NOVA_NOVATARGETMACHINE_H#define LLVM_LIB_TARGET_NOVA_NOVATARGETMACHINE_H
#include "NovaSubtarget.h"#include "NovaTargetObjectFile.h"#include "llvm/CodeGen/CodeGenTargetMachineImpl.h"#include "llvm/Support/CodeGen.h"#include "llvm/Target/TargetLoweringObjectFile.h"#include "llvm/Target/TargetMachine.h"
using namespace llvm;
static const char *NovaDataLayoutString = "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64";
static Reloc::Model getEffectiveRelocModel(std::optional<Reloc::Model> RM) { if (!RM) return Reloc::Static; return *RM;}
Add the class declaration.
}
namespace llvm {
class NovaTargetMachine final : public CodeGenTargetMachineImpl { NovaSubtarget Subtarget; std::unique_ptr<TargetLoweringObjectFile> TLOF;
public: NovaTargetMachine(const Target &T, const Triple &TT, StringRef CPU, StringRef FS, const TargetOptions &Options, std::optional<Reloc::Model> RM, std::optional<CodeModel::Model> CM, CodeGenOptLevel OL, bool JIT) : CodeGenTargetMachineImpl(T, NovaDataLayoutString, TT, CPU, FS, Options, getEffectiveRelocModel(RM), getEffectiveCodeModel(CM, CodeModel::Medium), OL), Subtarget(TT, CPU, FS, *this), TLOF(new NovaTargetObjectFile()) { initAsmInfo(); }
// ~NovaTargetMachine() override = default;
TargetLoweringObjectFile *getObjFileLowering() const override { return TLOF.get(); }
const NovaSubtarget *getSubtargetImpl(const Function &F) const override { return &Subtarget; }
TargetPassConfig *createPassConfig(PassManagerBase &PM) override;};} // namespace llvm
#endif
createPassConfig
will be added in the next section.
ASMInfo
The MCAsmInfo
class holds the information about the assembly language for the target.
#ifndef LLVM_LIB_TARGET_NOVA_MCTARGETDESC_NOVAMCASMINFO_H#define LLVM_LIB_TARGET_NOVA_MCTARGETDESC_NOVAMCASMINFO_H
#include "llvm/MC/MCAsmInfoELF.h"
namespace llvm { class Triple;class NovaMCAsmInfo final : public MCAsmInfoELF { void anchor() override;public: explicit NovaMCAsmInfo(const Triple &TheTriple);};} // end namespace llvm
#endif
I am adding the minimum possible stuff to get us running, but you can explore all the fields in the class and customize it here.
#include "NovaMCAsmInfo.h"#include "llvm/TargetParser/Triple.h"
using namespace llvm;
void NovaMCAsmInfo::anchor() {}
NovaMCAsmInfo::NovaMCAsmInfo(const Triple& TT) { IsLittleEndian = false; AlignmentIsInBytes = true;}
Like with other target info classes, we need to register the MCAsmInfo
class
in the NovaMCTargetDesc
file. This is done in the Initialize
function.
Include the headers.
#include "llvm/MC/MCSubtargetInfo.h"#include "llvm/MC/MCInstrInfo.h"#include "MCTargetDesc/NovaMCAsmInfo.h"#include "llvm/MC/MCDwarf.h"
Ready the MCAsmInfo instance.
}
static MCAsmInfo* createNovaMCAsmInfo(const MCRegisterInfo &MRI, const Triple &TT, const MCTargetOptions &Options) { MCAsmInfo *X = new NovaMCAsmInfo(TT); unsigned SP = MRI.getDwarfRegNum(Nova::SP, true); MCCFIInstruction Inst = MCCFIInstruction::createDefCfaRegister(nullptr, SP); X->addInitialFrameState(Inst); return X;}
Place it into the Target.
TargetRegistry::RegisterMCSubtargetInfo(*T, createNovaSubtargetInfo); TargetRegistry::RegisterMCInstrInfo(*T, createNovaMCInstrInfo); TargetRegistry::RegisterMCAsmInfo(*T, createNovaMCAsmInfo);
Lastly, don’t forget to add the source files to CMakeLists.txt.
NovaRegisterInfo.cpp MCTargetDesc/NovaMCTargetDesc.cpp NovaTargetObjectFile.cpp NovaSubtarget.cpp MCTargetDesc/NovaMCAsmInfo.cpp
With this, we are set to implement instructions.