| //===-- 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/MCAsmLexer.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(MCRegister &RegNo, SMLoc Loc, StringRef RegisterName); | |
| OperandMatchResultTy parseRegister(MCRegister &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(MCRegister &RegNo, SMLoc &StartLoc, | |
| SMLoc &EndLoc) override; | |
| OperandMatchResultTy tryParseRegister(MCRegister &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; | |
| MCRegister OuterReg; | |
| MCRegister 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(MCRegister &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(MCRegister &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(MCRegister &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(MCRegister &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); | |
| MCRegister 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; | |
| } | |
| MCRegister 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; | |
| } | |
| } |