Creating the M88k assembler parser class – The Target Description-2
By Peggy Johnston / August 4, 2022 / No Comments / Adding the M88k backend to LLVM, Creating the disassembler, Emitting machine instructions, Global instruction selection, ITCertification Exams
- And last, the superclass defines an abstract print() virtual method that we need to implement. This is only used for debugging purposes: void print(raw_ostream &OS) const override {
switch (Kind) {
case OpKind_Imm:
OS << “Imm: ” << getImm() << “\n”; break;
case OpKind_Token:
OS << “Token: ” << getToken() << “\n”; break;
case OpKind_Reg:
OS << “Reg: “
<< M88kInstPrinter::getRegisterName(getReg())
<< „\n”; break;
}
}
};
Next, we declare the M88kAsmParser class. The anonymous name space will end after the declaration:
- At the beginning of the class we include the generated fragment:
class M88kAsmParser : public MCTargetAsmParser {
define GET_ASSEMBLER_HEADER
include “M88kGenAsmMatcher.inc”
- Next, we define the required fields. We need a reference to the actual parser, which is of the MCAsmParser class, and a reference to the sub-target information: MCAsmParser &Parser;
const MCSubtargetInfo &SubtargetInfo; - To implement the assembler, we override a couple of methods defined in the MCTargetAsmParser superclass. The MatchAndEmitInstruction() method tries to match an instruction and emits the instruction represented by an instance of the MCInst class. Parsing an instruction is done in the ParseInstruction() method, while the parseRegister() and tryParseRegister() methods are responsible for parsing the register. The other methods are required internally: bool
ParseInstruction(ParseInstructionInfo &Info,
StringRef Name, SMLoc NameLoc,
OperandVector &Operands) override;
bool parseRegister(MCRegister &RegNo, SMLoc &StartLoc,
SMLoc &EndLoc) override;
OperandMatchResultTy
tryParseRegister(MCRegister &RegNo, SMLoc &StartLoc,
SMLoc &EndLoc) override;
bool parseRegister(MCRegister &RegNo, SMLoc &StartLoc,
SMLoc &EndLoc,
bool RestoreOnFailure);
bool parseOperand(OperandVector &Operands,
StringRef Mnemonic);
bool MatchAndEmitInstruction(
SMLoc IdLoc, unsigned &Opcode,
OperandVector &Operands, MCStreamer &Out,
uint64_t &ErrorInfo,
bool MatchingInlineAsm) override; - The constructor is defined inline. It mostly initializes all fields. This finishes the class declaration, after which the anonymous namespace ends:
public:
M88kAsmParser(const MCSubtargetInfo &STI,
MCAsmParser &Parser,
const MCInstrInfo &MII,
const MCTargetOptions &Options)
: MCTargetAsmParser(Options, STI, MII),
Parser(Parser), SubtargetInfo(STI) {
setAvailableFeatures(ComputeAvailableFeatures(
SubtargetInfo.getFeatureBits()));
}
};
- Now we include the generated parts of the assembler:
define GET_REGISTER_MATCHER
define GET_MATCHER_IMPLEMENTATION
include “M88kGenAsmMatcher.inc”
- The ParseInstruction() method is called whenever an instruction is expected. It must be able to parse all syntactical forms of an instruction. Currently, we only have instructions that take three operands, which are separated by a comma, so the parsing is simple. Be aware that the return value is true in case of an error!
bool M88kAsmParser::ParseInstruction(
ParseInstructionInfo &Info, StringRef Name,
SMLoc NameLoc, OperandVector &Operands) {
Operands.push_back(
M88kOperand::createToken(Name, NameLoc));
if (getLexer().isNot(AsmToken::EndOfStatement)) {
if (parseOperand(Operands, Name)) {
return Error(getLexer().getLoc(),
“expected operand”);
}
while (getLexer().is(AsmToken::Comma)) {
Parser.Lex();
if (parseOperand(Operands, Name)) {
return Error(getLexer().getLoc(),
“expected operand”);
}
}
if (getLexer().isNot(AsmToken::EndOfStatement))
return Error(getLexer().getLoc(),
“unexpected token in argument list”);
}
Parser.Lex();
return false;
}
- An operand can be a register or an immediate. We generalize a bit and parse an expression instead of just an integer. This helps later when adding address modes. When successful, the parsed operand is added to the Operands list:
bool M88kAsmParser::parseOperand(
OperandVector &Operands, StringRef Mnemonic) {
if (Parser.getTok().is(AsmToken::Percent)) {
MCRegister RegNo;
SMLoc StartLoc, EndLoc;
if (parseRegister(RegNo, StartLoc, EndLoc,
/RestoreOnFailure=/false))
return true;
Operands.push_back(M88kOperand::createReg(
RegNo, StartLoc, EndLoc));
return false;
}
if (Parser.getTok().is(AsmToken::Integer)) {
SMLoc StartLoc = Parser.getTok().getLoc();
const MCExpr *Expr;
if (Parser.parseExpression(Expr))
return true;
SMLoc EndLoc = Parser.getTok().getLoc();
Operands.push_back(
M88kOperand::createImm(Expr, StartLoc, EndLoc));
return false;
}
return true;
}