1. The target registry expects a factory method for each of the classes here. Let’s begin with the instruction information. We allocate an instance of the MCInstrInfo class, and call the InitM88kMCInstrInfo() generated function to populate the object:

static MCInstrInfo *createM88kMCInstrInfo() {
MCInstrInfo *X = new MCInstrInfo();
InitM88kMCInstrInfo(X);
return X;
}

  1. Next, we allocate an object of the MCRegisterInfo class and call a generated function to populate it. The additional M88k::R1 parameter value tells LLVM that the r1 register holds the return address:

static MCRegisterInfo *
createM88kMCRegisterInfo(const Triple &TT) {
MCRegisterInfo *X = new MCRegisterInfo();
InitM88kMCRegisterInfo(X, M88k::R1);
return X;
}

  1. And last, we need a factory method for the sub-target information. This method takes a target triple, a CPU name, and a feature string as parameters, and forwards them to the generated method:

static MCSubtargetInfo *
createM88kMCSubtargetInfo(const Triple &TT,
StringRef CPU, StringRef FS) {
return createM88kMCSubtargetInfoImpl(TT, CPU,
/TuneCPU/ CPU,
FS);
}

  1. Having the factory methods defined, we can now register them. Similar to the target registration, LLVM expects a global function called LLVMInitializeTargetMC():

extern “C” LLVM_EXTERNAL_VISIBILITY void
LLVMInitializeM88kTargetMC() {
TargetRegistry::RegisterMCInstrInfo(
getTheM88kTarget(), createM88kMCInstrInfo);
TargetRegistry::RegisterMCRegInfo(
getTheM88kTarget(), createM88kMCRegisterInfo);
TargetRegistry::RegisterMCSubtargetInfo(
getTheM88kTarget(), createM88kMCSubtargetInfo);
}

  1. The M88kMCTargetDesc.h header file just makes some generated code available:

define GET_REGINFO_ENUM
include “M88kGenRegisterInfo.inc”
define GET_INSTRINFO_ENUM
include “M88kGenInstrInfo.inc”
define GET_SUBTARGETINFO_ENUM
include “M88kGenSubtargetInfo.inc”

The implementation is almost done. To prevent a linker error, we need to provide another function, which registers a factory method for an object of the TargetMachine class. This class is required for code generation, and we implement it in Chapter 12, Instruction Selection, up next. Here, we just define an empty function in the M88kTargetMachine.cpp file:

include “TargetInfo/M88kTargetInfo.h”
include “llvm/MC/TargetRegistry.h”
extern “C” LLVM_EXTERNAL_VISIBILITY void
LLVMInitializeM88kTarget() {
// TODO Register the target machine. See chapter 12.
}

This concludes our first implementation. However, LLVM does not yet know about our new backend. To integrate it, open the llvm/CMakeLists.txt file, locate the section defining all the experimental targets, and add the M88k target to the list:

set(LLVM_ALL_EXPERIMENTAL_TARGETS ARC …
M88k
…)

Assuming the LLVM source code with our new backend is in the directory, you can configure the build by typing the following:

$ mkdir build
$ cd build
$ cmake -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=M88k \
../llvm-m88k/llvm

— Targeting M88k

After building LLVM, you can verify that the tools already know about our new target:

$ bin/llc –version
LLVM (http://llvm.org/):
LLVM version 17.0.2
Registered Targets:
m88k – M88k

The journey to get to this point was difficult, so take a moment to celebrate!

Fixing a possible compile error

There is a small oversight in LLVM 17.0.2, which causes a compile error. In one place in the code, the TableGen emitter for the sub-target information uses the removed value llvm::None instead of std:: nullopt, causing an error while compiling M88kMCTargetDesc.cpp. The easiest way to fix this problem is to cherry-pick the fix from the LLVM 18 development branch: git cherry-pick -x a587f429.

In the next section, we implement the assembler parser, which will give us the first working LLVM tool.

Leave a Reply

Your email address will not be published. Required fields are marked *