This moves the registry higher in the LLVM library dependency stack. Every client of the target registry needs to link against MC anyway to actually use the target, so we might as well move this out of Support. This allows us to ensure that Support doesn't have includes from MC/*. Differential Revision: https://reviews.llvm.org/D111454
1030 lines
28 KiB
C++
1030 lines
28 KiB
C++
//===---- M68kAsmParser.cpp - Parse M68k assembly to MCInst instructions --===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "M68kInstrInfo.h"
|
|
#include "M68kRegisterInfo.h"
|
|
#include "TargetInfo/M68kTargetInfo.h"
|
|
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
|
|
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
|
|
#include "llvm/MC/MCStreamer.h"
|
|
#include "llvm/MC/TargetRegistry.h"
|
|
|
|
#include <sstream>
|
|
|
|
#define DEBUG_TYPE "m68k-asm-parser"
|
|
|
|
using namespace llvm;
|
|
|
|
static cl::opt<bool> RegisterPrefixOptional(
|
|
"m68k-register-prefix-optional", cl::Hidden,
|
|
cl::desc("Enable specifying registers without the % prefix"),
|
|
cl::init(false));
|
|
|
|
namespace {
|
|
/// Parses M68k assembly from a stream.
|
|
class M68kAsmParser : public MCTargetAsmParser {
|
|
const MCSubtargetInfo &STI;
|
|
MCAsmParser &Parser;
|
|
const MCRegisterInfo *MRI;
|
|
|
|
#define GET_ASSEMBLER_HEADER
|
|
#include "M68kGenAsmMatcher.inc"
|
|
|
|
// Helpers for Match&Emit.
|
|
bool invalidOperand(const SMLoc &Loc, const OperandVector &Operands,
|
|
const uint64_t &ErrorInfo);
|
|
bool missingFeature(const SMLoc &Loc, const uint64_t &ErrorInfo);
|
|
bool emit(MCInst &Inst, SMLoc const &Loc, MCStreamer &Out) const;
|
|
bool parseRegisterName(unsigned int &RegNo, SMLoc Loc,
|
|
StringRef RegisterName);
|
|
OperandMatchResultTy parseRegister(unsigned int &RegNo);
|
|
|
|
// Parser functions.
|
|
void eatComma();
|
|
|
|
bool isExpr();
|
|
OperandMatchResultTy parseImm(OperandVector &Operands);
|
|
OperandMatchResultTy parseMemOp(OperandVector &Operands);
|
|
OperandMatchResultTy parseRegOrMoveMask(OperandVector &Operands);
|
|
|
|
public:
|
|
M68kAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser,
|
|
const MCInstrInfo &MII, const MCTargetOptions &Options)
|
|
: MCTargetAsmParser(Options, STI, MII), STI(STI), Parser(Parser) {
|
|
MCAsmParserExtension::Initialize(Parser);
|
|
MRI = getContext().getRegisterInfo();
|
|
|
|
setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits()));
|
|
}
|
|
|
|
unsigned validateTargetOperandClass(MCParsedAsmOperand &Op,
|
|
unsigned Kind) override;
|
|
bool ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) override;
|
|
OperandMatchResultTy tryParseRegister(unsigned &RegNo, SMLoc &StartLoc,
|
|
SMLoc &EndLoc) override;
|
|
bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
|
|
SMLoc NameLoc, OperandVector &Operands) override;
|
|
bool ParseDirective(AsmToken DirectiveID) override;
|
|
bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
|
|
OperandVector &Operands, MCStreamer &Out,
|
|
uint64_t &ErrorInfo,
|
|
bool MatchingInlineAsm) override;
|
|
};
|
|
|
|
struct M68kMemOp {
|
|
enum class Kind {
|
|
Addr,
|
|
RegMask,
|
|
Reg,
|
|
RegIndirect,
|
|
RegPostIncrement,
|
|
RegPreDecrement,
|
|
RegIndirectDisplacement,
|
|
RegIndirectDisplacementIndex,
|
|
};
|
|
|
|
// These variables are used for the following forms:
|
|
// Addr: (OuterDisp)
|
|
// RegMask: RegMask (as register mask)
|
|
// Reg: %OuterReg
|
|
// RegIndirect: (%OuterReg)
|
|
// RegPostIncrement: (%OuterReg)+
|
|
// RegPreDecrement: -(%OuterReg)
|
|
// RegIndirectDisplacement: OuterDisp(%OuterReg)
|
|
// RegIndirectDisplacementIndex:
|
|
// OuterDisp(%OuterReg, %InnerReg.Size * Scale, InnerDisp)
|
|
|
|
Kind Op;
|
|
unsigned OuterReg;
|
|
unsigned InnerReg;
|
|
const MCExpr *OuterDisp;
|
|
const MCExpr *InnerDisp;
|
|
uint8_t Size : 4;
|
|
uint8_t Scale : 4;
|
|
const MCExpr *Expr;
|
|
uint16_t RegMask;
|
|
|
|
M68kMemOp() {}
|
|
M68kMemOp(Kind Op) : Op(Op) {}
|
|
|
|
void print(raw_ostream &OS) const;
|
|
};
|
|
|
|
/// An parsed M68k assembly operand.
|
|
class M68kOperand : public MCParsedAsmOperand {
|
|
typedef MCParsedAsmOperand Base;
|
|
|
|
enum class KindTy {
|
|
Invalid,
|
|
Token,
|
|
Imm,
|
|
MemOp,
|
|
};
|
|
|
|
KindTy Kind;
|
|
SMLoc Start, End;
|
|
union {
|
|
StringRef Token;
|
|
int64_t Imm;
|
|
const MCExpr *Expr;
|
|
M68kMemOp MemOp;
|
|
};
|
|
|
|
template <unsigned N> bool isAddrN() const;
|
|
|
|
public:
|
|
M68kOperand(KindTy Kind, SMLoc Start, SMLoc End)
|
|
: Base(), Kind(Kind), Start(Start), End(End) {}
|
|
|
|
SMLoc getStartLoc() const override { return Start; }
|
|
SMLoc getEndLoc() const override { return End; }
|
|
|
|
void print(raw_ostream &OS) const override;
|
|
|
|
bool isMem() const override { return false; }
|
|
bool isMemOp() const { return Kind == KindTy::MemOp; }
|
|
|
|
static void addExpr(MCInst &Inst, const MCExpr *Expr);
|
|
|
|
// Reg
|
|
bool isReg() const override;
|
|
bool isAReg() const;
|
|
bool isDReg() const;
|
|
unsigned getReg() const override;
|
|
void addRegOperands(MCInst &Inst, unsigned N) const;
|
|
|
|
static std::unique_ptr<M68kOperand> createMemOp(M68kMemOp MemOp, SMLoc Start,
|
|
SMLoc End);
|
|
|
|
// Token
|
|
bool isToken() const override;
|
|
StringRef getToken() const;
|
|
static std::unique_ptr<M68kOperand> createToken(StringRef Token, SMLoc Start,
|
|
SMLoc End);
|
|
|
|
// Imm
|
|
bool isImm() const override;
|
|
void addImmOperands(MCInst &Inst, unsigned N) const;
|
|
|
|
static std::unique_ptr<M68kOperand> createImm(const MCExpr *Expr, SMLoc Start,
|
|
SMLoc End);
|
|
|
|
// MoveMask
|
|
bool isMoveMask() const;
|
|
void addMoveMaskOperands(MCInst &Inst, unsigned N) const;
|
|
|
|
// Addr
|
|
bool isAddr() const;
|
|
bool isAddr8() const { return isAddrN<8>(); }
|
|
bool isAddr16() const { return isAddrN<16>(); }
|
|
bool isAddr32() const { return isAddrN<32>(); }
|
|
void addAddrOperands(MCInst &Inst, unsigned N) const;
|
|
|
|
// ARI
|
|
bool isARI() const;
|
|
void addARIOperands(MCInst &Inst, unsigned N) const;
|
|
|
|
// ARID
|
|
bool isARID() const;
|
|
void addARIDOperands(MCInst &Inst, unsigned N) const;
|
|
|
|
// ARII
|
|
bool isARII() const;
|
|
void addARIIOperands(MCInst &Inst, unsigned N) const;
|
|
|
|
// ARIPD
|
|
bool isARIPD() const;
|
|
void addARIPDOperands(MCInst &Inst, unsigned N) const;
|
|
|
|
// ARIPI
|
|
bool isARIPI() const;
|
|
void addARIPIOperands(MCInst &Inst, unsigned N) const;
|
|
|
|
// PCD
|
|
bool isPCD() const;
|
|
void addPCDOperands(MCInst &Inst, unsigned N) const;
|
|
|
|
// PCI
|
|
bool isPCI() const;
|
|
void addPCIOperands(MCInst &Inst, unsigned N) const;
|
|
};
|
|
|
|
} // end anonymous namespace.
|
|
|
|
extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeM68kAsmParser() {
|
|
RegisterMCAsmParser<M68kAsmParser> X(getTheM68kTarget());
|
|
}
|
|
|
|
#define GET_MATCHER_IMPLEMENTATION
|
|
#include "M68kGenAsmMatcher.inc"
|
|
|
|
static inline unsigned getRegisterByIndex(unsigned RegisterIndex) {
|
|
static unsigned RegistersByIndex[] = {
|
|
M68k::D0, M68k::D1, M68k::D2, M68k::D3, M68k::D4, M68k::D5,
|
|
M68k::D6, M68k::D7, M68k::A0, M68k::A1, M68k::A2, M68k::A3,
|
|
M68k::A4, M68k::A5, M68k::A6, M68k::SP,
|
|
};
|
|
assert(RegisterIndex <=
|
|
sizeof(RegistersByIndex) / sizeof(RegistersByIndex[0]));
|
|
return RegistersByIndex[RegisterIndex];
|
|
}
|
|
|
|
static inline unsigned getRegisterIndex(unsigned Register) {
|
|
if (Register >= M68k::D0 && Register <= M68k::D7)
|
|
return Register - M68k::D0;
|
|
if (Register >= M68k::A0 && Register <= M68k::A6)
|
|
return Register - M68k::A0 + 8;
|
|
|
|
switch (Register) {
|
|
case M68k::SP:
|
|
// SP is sadly not contiguous with the rest of the An registers
|
|
return 15;
|
|
|
|
case M68k::PC:
|
|
case M68k::CCR:
|
|
return 16;
|
|
|
|
default:
|
|
llvm_unreachable("unexpected register number");
|
|
}
|
|
}
|
|
|
|
void M68kMemOp::print(raw_ostream &OS) const {
|
|
switch (Op) {
|
|
case Kind::Addr:
|
|
OS << OuterDisp;
|
|
break;
|
|
case Kind::RegMask:
|
|
OS << "RegMask(" << format("%04x", RegMask) << ")";
|
|
break;
|
|
case Kind::Reg:
|
|
OS << '%' << OuterReg;
|
|
break;
|
|
case Kind::RegIndirect:
|
|
OS << "(%" << OuterReg << ')';
|
|
break;
|
|
case Kind::RegPostIncrement:
|
|
OS << "(%" << OuterReg << ")+";
|
|
break;
|
|
case Kind::RegPreDecrement:
|
|
OS << "-(%" << OuterReg << ")";
|
|
break;
|
|
case Kind::RegIndirectDisplacement:
|
|
OS << OuterDisp << "(%" << OuterReg << ")";
|
|
break;
|
|
case Kind::RegIndirectDisplacementIndex:
|
|
OS << OuterDisp << "(%" << OuterReg << ", " << InnerReg << "." << Size
|
|
<< ", " << InnerDisp << ")";
|
|
break;
|
|
}
|
|
}
|
|
|
|
void M68kOperand::addExpr(MCInst &Inst, const MCExpr *Expr) {
|
|
if (auto Const = dyn_cast<MCConstantExpr>(Expr)) {
|
|
Inst.addOperand(MCOperand::createImm(Const->getValue()));
|
|
return;
|
|
}
|
|
|
|
Inst.addOperand(MCOperand::createExpr(Expr));
|
|
}
|
|
|
|
// Reg
|
|
bool M68kOperand::isReg() const {
|
|
return Kind == KindTy::MemOp && MemOp.Op == M68kMemOp::Kind::Reg;
|
|
}
|
|
|
|
unsigned M68kOperand::getReg() const {
|
|
assert(isReg());
|
|
return MemOp.OuterReg;
|
|
}
|
|
|
|
void M68kOperand::addRegOperands(MCInst &Inst, unsigned N) const {
|
|
assert(isReg() && "wrong operand kind");
|
|
assert((N == 1) && "can only handle one register operand");
|
|
|
|
Inst.addOperand(MCOperand::createReg(getReg()));
|
|
}
|
|
|
|
std::unique_ptr<M68kOperand> M68kOperand::createMemOp(M68kMemOp MemOp,
|
|
SMLoc Start, SMLoc End) {
|
|
auto Op = std::make_unique<M68kOperand>(KindTy::MemOp, Start, End);
|
|
Op->MemOp = MemOp;
|
|
return Op;
|
|
}
|
|
|
|
// Token
|
|
bool M68kOperand::isToken() const { return Kind == KindTy::Token; }
|
|
StringRef M68kOperand::getToken() const {
|
|
assert(isToken());
|
|
return Token;
|
|
}
|
|
|
|
std::unique_ptr<M68kOperand> M68kOperand::createToken(StringRef Token,
|
|
SMLoc Start, SMLoc End) {
|
|
auto Op = std::make_unique<M68kOperand>(KindTy::Token, Start, End);
|
|
Op->Token = Token;
|
|
return Op;
|
|
}
|
|
|
|
// Imm
|
|
bool M68kOperand::isImm() const { return Kind == KindTy::Imm; }
|
|
void M68kOperand::addImmOperands(MCInst &Inst, unsigned N) const {
|
|
assert(isImm() && "wrong operand kind");
|
|
assert((N == 1) && "can only handle one register operand");
|
|
|
|
M68kOperand::addExpr(Inst, Expr);
|
|
}
|
|
|
|
std::unique_ptr<M68kOperand> M68kOperand::createImm(const MCExpr *Expr,
|
|
SMLoc Start, SMLoc End) {
|
|
auto Op = std::make_unique<M68kOperand>(KindTy::Imm, Start, End);
|
|
Op->Expr = Expr;
|
|
return Op;
|
|
}
|
|
|
|
// MoveMask
|
|
bool M68kOperand::isMoveMask() const {
|
|
if (!isMemOp())
|
|
return false;
|
|
|
|
if (MemOp.Op == M68kMemOp::Kind::RegMask)
|
|
return true;
|
|
|
|
if (MemOp.Op != M68kMemOp::Kind::Reg)
|
|
return false;
|
|
|
|
// Only regular address / data registers are allowed to be used
|
|
// in register masks.
|
|
return getRegisterIndex(MemOp.OuterReg) < 16;
|
|
}
|
|
|
|
void M68kOperand::addMoveMaskOperands(MCInst &Inst, unsigned N) const {
|
|
assert(isMoveMask() && "wrong operand kind");
|
|
assert((N == 1) && "can only handle one immediate operand");
|
|
|
|
uint16_t MoveMask = MemOp.RegMask;
|
|
if (MemOp.Op == M68kMemOp::Kind::Reg)
|
|
MoveMask = 1 << getRegisterIndex(MemOp.OuterReg);
|
|
|
|
Inst.addOperand(MCOperand::createImm(MoveMask));
|
|
}
|
|
|
|
// Addr
|
|
bool M68kOperand::isAddr() const {
|
|
return isMemOp() && MemOp.Op == M68kMemOp::Kind::Addr;
|
|
}
|
|
// TODO: Maybe we can also store the size of OuterDisp
|
|
// in Size?
|
|
template <unsigned N> bool M68kOperand::isAddrN() const {
|
|
if (isAddr()) {
|
|
int64_t Res;
|
|
if (MemOp.OuterDisp->evaluateAsAbsolute(Res))
|
|
return isInt<N>(Res);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
void M68kOperand::addAddrOperands(MCInst &Inst, unsigned N) const {
|
|
M68kOperand::addExpr(Inst, MemOp.OuterDisp);
|
|
}
|
|
|
|
// ARI
|
|
bool M68kOperand::isARI() const {
|
|
return isMemOp() && MemOp.Op == M68kMemOp::Kind::RegIndirect &&
|
|
M68k::AR32RegClass.contains(MemOp.OuterReg);
|
|
}
|
|
void M68kOperand::addARIOperands(MCInst &Inst, unsigned N) const {
|
|
Inst.addOperand(MCOperand::createReg(MemOp.OuterReg));
|
|
}
|
|
|
|
// ARID
|
|
bool M68kOperand::isARID() const {
|
|
return isMemOp() && MemOp.Op == M68kMemOp::Kind::RegIndirectDisplacement &&
|
|
M68k::AR32RegClass.contains(MemOp.OuterReg);
|
|
}
|
|
void M68kOperand::addARIDOperands(MCInst &Inst, unsigned N) const {
|
|
M68kOperand::addExpr(Inst, MemOp.OuterDisp);
|
|
Inst.addOperand(MCOperand::createReg(MemOp.OuterReg));
|
|
}
|
|
|
|
// ARII
|
|
bool M68kOperand::isARII() const {
|
|
return isMemOp() &&
|
|
MemOp.Op == M68kMemOp::Kind::RegIndirectDisplacementIndex &&
|
|
M68k::AR32RegClass.contains(MemOp.OuterReg);
|
|
}
|
|
void M68kOperand::addARIIOperands(MCInst &Inst, unsigned N) const {
|
|
M68kOperand::addExpr(Inst, MemOp.OuterDisp);
|
|
Inst.addOperand(MCOperand::createReg(MemOp.OuterReg));
|
|
Inst.addOperand(MCOperand::createReg(MemOp.InnerReg));
|
|
}
|
|
|
|
// ARIPD
|
|
bool M68kOperand::isARIPD() const {
|
|
return isMemOp() && MemOp.Op == M68kMemOp::Kind::RegPreDecrement &&
|
|
M68k::AR32RegClass.contains(MemOp.OuterReg);
|
|
}
|
|
void M68kOperand::addARIPDOperands(MCInst &Inst, unsigned N) const {
|
|
Inst.addOperand(MCOperand::createReg(MemOp.OuterReg));
|
|
}
|
|
|
|
// ARIPI
|
|
bool M68kOperand::isARIPI() const {
|
|
return isMemOp() && MemOp.Op == M68kMemOp::Kind::RegPostIncrement &&
|
|
M68k::AR32RegClass.contains(MemOp.OuterReg);
|
|
}
|
|
void M68kOperand::addARIPIOperands(MCInst &Inst, unsigned N) const {
|
|
Inst.addOperand(MCOperand::createReg(MemOp.OuterReg));
|
|
}
|
|
|
|
// PCD
|
|
bool M68kOperand::isPCD() const {
|
|
return isMemOp() && MemOp.Op == M68kMemOp::Kind::RegIndirectDisplacement &&
|
|
MemOp.OuterReg == M68k::PC;
|
|
}
|
|
void M68kOperand::addPCDOperands(MCInst &Inst, unsigned N) const {
|
|
M68kOperand::addExpr(Inst, MemOp.OuterDisp);
|
|
}
|
|
|
|
// PCI
|
|
bool M68kOperand::isPCI() const {
|
|
return isMemOp() &&
|
|
MemOp.Op == M68kMemOp::Kind::RegIndirectDisplacementIndex &&
|
|
MemOp.OuterReg == M68k::PC;
|
|
}
|
|
void M68kOperand::addPCIOperands(MCInst &Inst, unsigned N) const {
|
|
M68kOperand::addExpr(Inst, MemOp.OuterDisp);
|
|
Inst.addOperand(MCOperand::createReg(MemOp.InnerReg));
|
|
}
|
|
|
|
static inline bool checkRegisterClass(unsigned RegNo, bool Data, bool Address,
|
|
bool SP) {
|
|
switch (RegNo) {
|
|
case M68k::A0:
|
|
case M68k::A1:
|
|
case M68k::A2:
|
|
case M68k::A3:
|
|
case M68k::A4:
|
|
case M68k::A5:
|
|
case M68k::A6:
|
|
return Address;
|
|
|
|
case M68k::SP:
|
|
return SP;
|
|
|
|
case M68k::D0:
|
|
case M68k::D1:
|
|
case M68k::D2:
|
|
case M68k::D3:
|
|
case M68k::D4:
|
|
case M68k::D5:
|
|
case M68k::D6:
|
|
case M68k::D7:
|
|
return Data;
|
|
|
|
case M68k::SR:
|
|
case M68k::CCR:
|
|
return false;
|
|
|
|
default:
|
|
llvm_unreachable("unexpected register type");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool M68kOperand::isAReg() const {
|
|
return isReg() && checkRegisterClass(getReg(),
|
|
/*Data=*/false,
|
|
/*Address=*/true, /*SP=*/true);
|
|
}
|
|
|
|
bool M68kOperand::isDReg() const {
|
|
return isReg() && checkRegisterClass(getReg(),
|
|
/*Data=*/true,
|
|
/*Address=*/false, /*SP=*/false);
|
|
}
|
|
|
|
unsigned M68kAsmParser::validateTargetOperandClass(MCParsedAsmOperand &Op,
|
|
unsigned Kind) {
|
|
M68kOperand &Operand = (M68kOperand &)Op;
|
|
|
|
switch (Kind) {
|
|
case MCK_XR16:
|
|
case MCK_SPILL:
|
|
if (Operand.isReg() &&
|
|
checkRegisterClass(Operand.getReg(), true, true, true)) {
|
|
return Match_Success;
|
|
}
|
|
break;
|
|
|
|
case MCK_AR16:
|
|
case MCK_AR32:
|
|
if (Operand.isReg() &&
|
|
checkRegisterClass(Operand.getReg(), false, true, true)) {
|
|
return Match_Success;
|
|
}
|
|
break;
|
|
|
|
case MCK_AR32_NOSP:
|
|
if (Operand.isReg() &&
|
|
checkRegisterClass(Operand.getReg(), false, true, false)) {
|
|
return Match_Success;
|
|
}
|
|
break;
|
|
|
|
case MCK_DR8:
|
|
case MCK_DR16:
|
|
case MCK_DR32:
|
|
if (Operand.isReg() &&
|
|
checkRegisterClass(Operand.getReg(), true, false, false)) {
|
|
return Match_Success;
|
|
}
|
|
break;
|
|
|
|
case MCK_AR16_TC:
|
|
if (Operand.isReg() &&
|
|
((Operand.getReg() == M68k::A0) || (Operand.getReg() == M68k::A1))) {
|
|
return Match_Success;
|
|
}
|
|
break;
|
|
|
|
case MCK_DR16_TC:
|
|
if (Operand.isReg() &&
|
|
((Operand.getReg() == M68k::D0) || (Operand.getReg() == M68k::D1))) {
|
|
return Match_Success;
|
|
}
|
|
break;
|
|
|
|
case MCK_XR16_TC:
|
|
if (Operand.isReg() &&
|
|
((Operand.getReg() == M68k::D0) || (Operand.getReg() == M68k::D1) ||
|
|
(Operand.getReg() == M68k::A0) || (Operand.getReg() == M68k::A1))) {
|
|
return Match_Success;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return Match_InvalidOperand;
|
|
}
|
|
|
|
bool M68kAsmParser::parseRegisterName(unsigned &RegNo, SMLoc Loc,
|
|
StringRef RegisterName) {
|
|
auto RegisterNameLower = RegisterName.lower();
|
|
|
|
// CCR register
|
|
if (RegisterNameLower == "ccr") {
|
|
RegNo = M68k::CCR;
|
|
return true;
|
|
}
|
|
|
|
// Parse simple general-purpose registers.
|
|
if (RegisterNameLower.size() == 2) {
|
|
|
|
switch (RegisterNameLower[0]) {
|
|
case 'd':
|
|
case 'a': {
|
|
if (isdigit(RegisterNameLower[1])) {
|
|
unsigned IndexOffset = (RegisterNameLower[0] == 'a') ? 8 : 0;
|
|
unsigned RegIndex = (unsigned)(RegisterNameLower[1] - '0');
|
|
if (RegIndex < 8) {
|
|
RegNo = getRegisterByIndex(IndexOffset + RegIndex);
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 's':
|
|
if (RegisterNameLower[1] == 'p') {
|
|
RegNo = M68k::SP;
|
|
return true;
|
|
} else if (RegisterNameLower[1] == 'r') {
|
|
RegNo = M68k::SR;
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
case 'p':
|
|
if (RegisterNameLower[1] == 'c') {
|
|
RegNo = M68k::PC;
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
OperandMatchResultTy M68kAsmParser::parseRegister(unsigned &RegNo) {
|
|
bool HasPercent = false;
|
|
AsmToken PercentToken;
|
|
|
|
LLVM_DEBUG(dbgs() << "parseRegister "; getTok().dump(dbgs()); dbgs() << "\n");
|
|
|
|
if (getTok().is(AsmToken::Percent)) {
|
|
HasPercent = true;
|
|
PercentToken = Lex();
|
|
} else if (!RegisterPrefixOptional.getValue()) {
|
|
return MatchOperand_NoMatch;
|
|
}
|
|
|
|
if (!Parser.getTok().is(AsmToken::Identifier)) {
|
|
if (HasPercent) {
|
|
getLexer().UnLex(PercentToken);
|
|
}
|
|
return MatchOperand_NoMatch;
|
|
}
|
|
|
|
auto RegisterName = Parser.getTok().getString();
|
|
if (!parseRegisterName(RegNo, Parser.getLexer().getLoc(), RegisterName)) {
|
|
if (HasPercent) {
|
|
getLexer().UnLex(PercentToken);
|
|
}
|
|
return MatchOperand_NoMatch;
|
|
}
|
|
|
|
Parser.Lex();
|
|
return MatchOperand_Success;
|
|
}
|
|
|
|
bool M68kAsmParser::ParseRegister(unsigned &RegNo, SMLoc &StartLoc,
|
|
SMLoc &EndLoc) {
|
|
auto Result = tryParseRegister(RegNo, StartLoc, EndLoc);
|
|
if (Result != MatchOperand_Success) {
|
|
return Error(StartLoc, "expected register");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
OperandMatchResultTy M68kAsmParser::tryParseRegister(unsigned &RegNo,
|
|
SMLoc &StartLoc,
|
|
SMLoc &EndLoc) {
|
|
StartLoc = getLexer().getLoc();
|
|
auto Result = parseRegister(RegNo);
|
|
EndLoc = getLexer().getLoc();
|
|
return Result;
|
|
}
|
|
|
|
bool M68kAsmParser::isExpr() {
|
|
switch (Parser.getTok().getKind()) {
|
|
case AsmToken::Identifier:
|
|
case AsmToken::Integer:
|
|
return true;
|
|
case AsmToken::Minus:
|
|
return getLexer().peekTok().getKind() == AsmToken::Integer;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
OperandMatchResultTy M68kAsmParser::parseImm(OperandVector &Operands) {
|
|
if (getLexer().isNot(AsmToken::Hash)) {
|
|
return MatchOperand_NoMatch;
|
|
}
|
|
SMLoc Start = getLexer().getLoc();
|
|
Parser.Lex();
|
|
|
|
SMLoc End;
|
|
const MCExpr *Expr;
|
|
|
|
if (getParser().parseExpression(Expr, End)) {
|
|
return MatchOperand_ParseFail;
|
|
}
|
|
|
|
Operands.push_back(M68kOperand::createImm(Expr, Start, End));
|
|
return MatchOperand_Success;
|
|
}
|
|
|
|
OperandMatchResultTy M68kAsmParser::parseMemOp(OperandVector &Operands) {
|
|
SMLoc Start = getLexer().getLoc();
|
|
bool IsPD = false;
|
|
M68kMemOp MemOp;
|
|
|
|
// Check for a plain register or register mask.
|
|
auto Result = parseRegOrMoveMask(Operands);
|
|
if (Result != llvm::MatchOperand_NoMatch) {
|
|
return Result;
|
|
}
|
|
|
|
// Check for pre-decrement & outer displacement.
|
|
bool HasDisplacement = false;
|
|
if (getLexer().is(AsmToken::Minus)) {
|
|
IsPD = true;
|
|
Parser.Lex();
|
|
} else if (isExpr()) {
|
|
if (Parser.parseExpression(MemOp.OuterDisp)) {
|
|
return MatchOperand_ParseFail;
|
|
}
|
|
HasDisplacement = true;
|
|
}
|
|
|
|
if (getLexer().isNot(AsmToken::LParen)) {
|
|
if (HasDisplacement) {
|
|
MemOp.Op = M68kMemOp::Kind::Addr;
|
|
Operands.push_back(
|
|
M68kOperand::createMemOp(MemOp, Start, getLexer().getLoc()));
|
|
return MatchOperand_Success;
|
|
} else if (IsPD) {
|
|
Error(getLexer().getLoc(), "expected (");
|
|
return MatchOperand_ParseFail;
|
|
}
|
|
|
|
return MatchOperand_NoMatch;
|
|
}
|
|
Parser.Lex();
|
|
|
|
// Check for constant dereference & MIT-style displacement
|
|
if (!HasDisplacement && isExpr()) {
|
|
if (Parser.parseExpression(MemOp.OuterDisp)) {
|
|
return MatchOperand_ParseFail;
|
|
}
|
|
HasDisplacement = true;
|
|
|
|
// If we're not followed by a comma, we're a constant dereference.
|
|
if (getLexer().isNot(AsmToken::Comma)) {
|
|
MemOp.Op = M68kMemOp::Kind::Addr;
|
|
Operands.push_back(
|
|
M68kOperand::createMemOp(MemOp, Start, getLexer().getLoc()));
|
|
return MatchOperand_Success;
|
|
}
|
|
|
|
Parser.Lex();
|
|
}
|
|
|
|
Result = parseRegister(MemOp.OuterReg);
|
|
if (Result == MatchOperand_ParseFail) {
|
|
return MatchOperand_ParseFail;
|
|
}
|
|
|
|
if (Result != MatchOperand_Success) {
|
|
Error(getLexer().getLoc(), "expected register");
|
|
return MatchOperand_ParseFail;
|
|
}
|
|
|
|
// Check for Index.
|
|
bool HasIndex = false;
|
|
if (Parser.getTok().is(AsmToken::Comma)) {
|
|
Parser.Lex();
|
|
|
|
Result = parseRegister(MemOp.InnerReg);
|
|
if (Result == MatchOperand_ParseFail) {
|
|
return Result;
|
|
}
|
|
|
|
if (Result == MatchOperand_NoMatch) {
|
|
Error(getLexer().getLoc(), "expected register");
|
|
return MatchOperand_ParseFail;
|
|
}
|
|
|
|
// TODO: parse size, scale and inner displacement.
|
|
MemOp.Size = 4;
|
|
MemOp.Scale = 1;
|
|
MemOp.InnerDisp = MCConstantExpr::create(0, Parser.getContext(), true, 4);
|
|
HasIndex = true;
|
|
}
|
|
|
|
if (Parser.getTok().isNot(AsmToken::RParen)) {
|
|
Error(getLexer().getLoc(), "expected )");
|
|
return MatchOperand_ParseFail;
|
|
}
|
|
Parser.Lex();
|
|
|
|
bool IsPI = false;
|
|
if (!IsPD && Parser.getTok().is(AsmToken::Plus)) {
|
|
Parser.Lex();
|
|
IsPI = true;
|
|
}
|
|
|
|
SMLoc End = getLexer().getLoc();
|
|
|
|
unsigned OpCount = IsPD + IsPI + (HasIndex || HasDisplacement);
|
|
if (OpCount > 1) {
|
|
Error(Start, "only one of post-increment, pre-decrement or displacement "
|
|
"can be used");
|
|
return MatchOperand_ParseFail;
|
|
}
|
|
|
|
if (IsPD) {
|
|
MemOp.Op = M68kMemOp::Kind::RegPreDecrement;
|
|
} else if (IsPI) {
|
|
MemOp.Op = M68kMemOp::Kind::RegPostIncrement;
|
|
} else if (HasIndex) {
|
|
MemOp.Op = M68kMemOp::Kind::RegIndirectDisplacementIndex;
|
|
} else if (HasDisplacement) {
|
|
MemOp.Op = M68kMemOp::Kind::RegIndirectDisplacement;
|
|
} else {
|
|
MemOp.Op = M68kMemOp::Kind::RegIndirect;
|
|
}
|
|
|
|
Operands.push_back(M68kOperand::createMemOp(MemOp, Start, End));
|
|
return MatchOperand_Success;
|
|
}
|
|
|
|
OperandMatchResultTy
|
|
M68kAsmParser::parseRegOrMoveMask(OperandVector &Operands) {
|
|
SMLoc Start = getLexer().getLoc();
|
|
M68kMemOp MemOp(M68kMemOp::Kind::RegMask);
|
|
MemOp.RegMask = 0;
|
|
|
|
for (;;) {
|
|
bool IsFirstRegister =
|
|
(MemOp.Op == M68kMemOp::Kind::RegMask) && (MemOp.RegMask == 0);
|
|
|
|
unsigned FirstRegister;
|
|
auto Result = parseRegister(FirstRegister);
|
|
if (IsFirstRegister && (Result == llvm::MatchOperand_NoMatch)) {
|
|
return MatchOperand_NoMatch;
|
|
}
|
|
if (Result != llvm::MatchOperand_Success) {
|
|
Error(getLexer().getLoc(), "expected start register");
|
|
return MatchOperand_ParseFail;
|
|
}
|
|
|
|
unsigned LastRegister = FirstRegister;
|
|
if (getLexer().is(AsmToken::Minus)) {
|
|
getLexer().Lex();
|
|
Result = parseRegister(LastRegister);
|
|
if (Result != llvm::MatchOperand_Success) {
|
|
Error(getLexer().getLoc(), "expected end register");
|
|
return MatchOperand_ParseFail;
|
|
}
|
|
}
|
|
|
|
unsigned FirstRegisterIndex = getRegisterIndex(FirstRegister);
|
|
unsigned LastRegisterIndex = getRegisterIndex(LastRegister);
|
|
|
|
uint16_t NumNewBits = LastRegisterIndex - FirstRegisterIndex + 1;
|
|
uint16_t NewMaskBits = ((1 << NumNewBits) - 1) << FirstRegisterIndex;
|
|
|
|
if (IsFirstRegister && (FirstRegister == LastRegister)) {
|
|
// First register range is a single register, simplify to just Reg
|
|
// so that it matches more operands.
|
|
MemOp.Op = M68kMemOp::Kind::Reg;
|
|
MemOp.OuterReg = FirstRegister;
|
|
} else {
|
|
if (MemOp.Op == M68kMemOp::Kind::Reg) {
|
|
// This is the second register being specified - expand the Reg operand
|
|
// into a mask first.
|
|
MemOp.Op = M68kMemOp::Kind::RegMask;
|
|
MemOp.RegMask = 1 << getRegisterIndex(MemOp.OuterReg);
|
|
|
|
if (MemOp.RegMask == 0) {
|
|
Error(getLexer().getLoc(),
|
|
"special registers cannot be used in register masks");
|
|
return MatchOperand_ParseFail;
|
|
}
|
|
}
|
|
|
|
if ((FirstRegisterIndex >= 16) || (LastRegisterIndex >= 16)) {
|
|
Error(getLexer().getLoc(),
|
|
"special registers cannot be used in register masks");
|
|
return MatchOperand_ParseFail;
|
|
}
|
|
|
|
if (NewMaskBits & MemOp.RegMask) {
|
|
Error(getLexer().getLoc(), "conflicting masked registers");
|
|
return MatchOperand_ParseFail;
|
|
}
|
|
|
|
MemOp.RegMask |= NewMaskBits;
|
|
}
|
|
|
|
if (getLexer().isNot(AsmToken::Slash)) {
|
|
break;
|
|
}
|
|
|
|
getLexer().Lex();
|
|
}
|
|
|
|
Operands.push_back(
|
|
M68kOperand::createMemOp(MemOp, Start, getLexer().getLoc()));
|
|
return MatchOperand_Success;
|
|
}
|
|
|
|
void M68kAsmParser::eatComma() {
|
|
if (Parser.getTok().is(AsmToken::Comma)) {
|
|
Parser.Lex();
|
|
}
|
|
}
|
|
|
|
bool M68kAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
|
|
SMLoc NameLoc, OperandVector &Operands) {
|
|
SMLoc Start = getLexer().getLoc();
|
|
Operands.push_back(M68kOperand::createToken(Name, Start, Start));
|
|
|
|
bool First = true;
|
|
while (Parser.getTok().isNot(AsmToken::EndOfStatement)) {
|
|
if (!First) {
|
|
eatComma();
|
|
} else {
|
|
First = false;
|
|
}
|
|
|
|
auto MatchResult = MatchOperandParserImpl(Operands, Name);
|
|
if (MatchResult == MatchOperand_Success) {
|
|
continue;
|
|
}
|
|
|
|
// Add custom operand formats here...
|
|
SMLoc Loc = getLexer().getLoc();
|
|
Parser.eatToEndOfStatement();
|
|
return Error(Loc, "unexpected token parsing operands");
|
|
}
|
|
|
|
// Eat EndOfStatement.
|
|
Parser.Lex();
|
|
return false;
|
|
}
|
|
|
|
bool M68kAsmParser::ParseDirective(AsmToken DirectiveID) { return true; }
|
|
|
|
bool M68kAsmParser::invalidOperand(SMLoc const &Loc,
|
|
OperandVector const &Operands,
|
|
uint64_t const &ErrorInfo) {
|
|
SMLoc ErrorLoc = Loc;
|
|
char const *Diag = 0;
|
|
|
|
if (ErrorInfo != ~0U) {
|
|
if (ErrorInfo >= Operands.size()) {
|
|
Diag = "too few operands for instruction.";
|
|
} else {
|
|
auto const &Op = (M68kOperand const &)*Operands[ErrorInfo];
|
|
if (Op.getStartLoc() != SMLoc()) {
|
|
ErrorLoc = Op.getStartLoc();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Diag) {
|
|
Diag = "invalid operand for instruction";
|
|
}
|
|
|
|
return Error(ErrorLoc, Diag);
|
|
}
|
|
|
|
bool M68kAsmParser::missingFeature(llvm::SMLoc const &Loc,
|
|
uint64_t const &ErrorInfo) {
|
|
return Error(Loc, "instruction requires a CPU feature not currently enabled");
|
|
}
|
|
|
|
bool M68kAsmParser::emit(MCInst &Inst, SMLoc const &Loc,
|
|
MCStreamer &Out) const {
|
|
Inst.setLoc(Loc);
|
|
Out.emitInstruction(Inst, STI);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool M68kAsmParser::MatchAndEmitInstruction(SMLoc Loc, unsigned &Opcode,
|
|
OperandVector &Operands,
|
|
MCStreamer &Out,
|
|
uint64_t &ErrorInfo,
|
|
bool MatchingInlineAsm) {
|
|
MCInst Inst;
|
|
unsigned MatchResult =
|
|
MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm);
|
|
|
|
switch (MatchResult) {
|
|
case Match_Success:
|
|
return emit(Inst, Loc, Out);
|
|
case Match_MissingFeature:
|
|
return missingFeature(Loc, ErrorInfo);
|
|
case Match_InvalidOperand:
|
|
return invalidOperand(Loc, Operands, ErrorInfo);
|
|
case Match_MnemonicFail:
|
|
return Error(Loc, "invalid instruction");
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void M68kOperand::print(raw_ostream &OS) const {
|
|
switch (Kind) {
|
|
case KindTy::Invalid:
|
|
OS << "invalid";
|
|
break;
|
|
|
|
case KindTy::Token:
|
|
OS << "token '" << Token << "'";
|
|
break;
|
|
|
|
case KindTy::Imm:
|
|
OS << "immediate " << Imm;
|
|
break;
|
|
|
|
case KindTy::MemOp:
|
|
MemOp.print(OS);
|
|
break;
|
|
}
|
|
}
|