1. After, we define the getSubtargetImpl() method. The sub-target instance to use depends on the target-cpu and target-features function attributes. For example, the target-cpu attribute could be set to MC88110, thus targeting the second-generation CPU. However, the attribute target feature could describe that we should not use the graphics instructions of that CPU. We have not defined the CPUs and their features in the target description yet, so we are doing a bit more than what’s necessary here. However, the implementation is simple enough: we query the function attributes and use either the returned strings or the default values. With this information, we can query the SubtargetMap member, and if it’s not found, we create the sub-target:

const M88kSubtarget *
M88kTargetMachine::getSubtargetImpl(
const Function &F) const {
Attribute CPUAttr = F.getFnAttribute(“target-cpu”);
Attribute FSAttr =
F.getFnAttribute(“target-features”);
std::string CPU =
!CPUAttr.hasAttribute(Attribute::None)
? CPUAttr.getValueAsString().str()
: TargetCPU;
std::string FS = !FSAttr.hasAttribute(Attribute::None)
? FSAttr.getValueAsString().str()
: TargetFS;
auto &I = SubtargetMap[CPU + FS];
if (!I) {
resetTargetOptions(F);
I = std::make_unique(TargetTriple,
CPU, FS, *this);
}
return I.get();
}

  1. Finally, we create the pass configuration. For this, we need our own class, M88kPassConfig, which derives from the TargetPassConfig class. We only override the addInstSelector method:

namespace {
class M88kPassConfig : public TargetPassConfig {
public:
M88kPassConfig(M88kTargetMachine &TM,
PassManagerBase &PM)
: TargetPassConfig(TM, PM) {}
bool addInstSelector() override;
};
} // namespace

  1. With this definition, we can implement the createPassConfig factory method:

TargetPassConfig M88kTargetMachine::createPassConfig( PassManagerBase &PM) { return new M88kPassConfig(this, PM);
}

  1. Lastly, we must add our instruction selection class to the pass pipeline in the addInstSelector() method. The return value, false, indicates that we have added a pass that converts LLVM IR into machine instructions:

bool M88kPassConfig::addInstSelector() {
addPass(createM88kISelDag(getTM(),
getOptLevel()));
return false;
}

That was a long journey to finish the implementation! Now that we’ve built the llc tool, we can run an example. Save the following simple IR in the and.ll file:

define i32 @f1(i32 %a, i32 %b) {
%res = and i32 %a, %b
ret i32 %res
}

Now, we can run llc and verify that the generated assembly looks reasonable:

$ llc -mtriple m88k-openbsd < and.ll .text .file “”
.globl f1 | — Begin function f1
.align 2
.type f1,@function
f1: | @f1
| %bb.0:
and %r2, %r2, %r3
jmp %r1
.Lfunc_end0:
.size f1, .Lfunc_end0-f1
| — End function
.section “.note.GNU-stack”,””,@progbits

To compile for the m88k target, we must specify the triple on the command line, as in this example, or in the IR file.
Enjoy your success for a bit before we look at global instruction selection.

Leave a Reply

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