diff --git a/llvm/lib/Target/M68k/AsmParser/CMakeLists.txt b/llvm/lib/Target/M68k/AsmParser/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/M68k/AsmParser/CMakeLists.txt @@ -0,0 +1,12 @@ +add_llvm_component_library(LLVMM68kAsmParser + M68kAsmParser.cpp + + LINK_COMPONENTS + MC + MCParser + Support + M68kCodeGen + + ADD_TO_COMPONENT + M68k +) diff --git a/llvm/lib/Target/M68k/AsmParser/M68kAsmParser.cpp b/llvm/lib/Target/M68k/AsmParser/M68kAsmParser.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/M68k/AsmParser/M68kAsmParser.cpp @@ -0,0 +1,865 @@ +//===---- 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/Support/TargetRegistry.h" + +#include + +#define DEBUG_TYPE "m68k-asm-parser" + +using namespace llvm; + +static cl::opt 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() const; + OperandMatchResultTy parseImm(OperandVector &Operands); + OperandMatchResultTy parseMemOp(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, + Reg, + RegIndirect, + RegPostIncrement, + RegPreDecrement, + RegIndirectDisplacement, + RegIndirectDisplacementIndex, + }; + + // These variables are used for the following forms: + // Addr: (OuterDisp) + // 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; + + 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 Kind { + Invalid, + Token, + Imm, + MemOp, + }; + + Kind Kind; + SMLoc Start, End; + union { + StringRef Token; + int64_t Imm; + const MCExpr *Expr; + M68kMemOp MemOp; + }; + +public: + M68kOperand(enum Kind 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 == Kind::MemOp; } + + static void addExpr(MCInst &Inst, const MCExpr *Expr); + + // Reg + bool isReg() const override; + unsigned getReg() const override; + void addRegOperands(MCInst &Inst, unsigned N) const; + + static std::unique_ptr createMemOp(M68kMemOp MemOp, SMLoc Start, + SMLoc End); + + // Token + bool isToken() const override; + StringRef getToken() const; + static std::unique_ptr createToken(StringRef Token, SMLoc Start, + SMLoc End); + + // Imm + bool isImm() const override; + void addImmOperands(MCInst &Inst, unsigned N) const; + + static std::unique_ptr createImm(const MCExpr *Expr, SMLoc Start, + SMLoc End); + + // Addr + bool isAddr() const; + 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 X(getTheM68kTarget()); +} + +#define GET_MATCHER_IMPLEMENTATION +#include "M68kGenAsmMatcher.inc" + +void M68kMemOp::print(raw_ostream &OS) const { + switch (Op) { + case Kind::Addr: + OS << OuterDisp; + 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; + default: + llvm_unreachable("unknown MemOp kind"); + } +} + +void M68kOperand::addExpr(MCInst &Inst, const MCExpr *Expr) { + if (auto Const = dyn_cast(Expr)) { + Inst.addOperand(MCOperand::createImm(Const->getValue())); + return; + } + + Inst.addOperand(MCOperand::createExpr(Expr)); +} + +// Reg +bool M68kOperand::isReg() const { + return Kind == Kind::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::createMemOp(M68kMemOp MemOp, + SMLoc Start, SMLoc End) { + auto Op = std::make_unique(Kind::MemOp, Start, End); + Op->MemOp = MemOp; + return Op; +} + +// Token +bool M68kOperand::isToken() const { return Kind == Kind::Token; } +StringRef M68kOperand::getToken() const { + assert(isToken()); + return Token; +} + +std::unique_ptr M68kOperand::createToken(StringRef Token, + SMLoc Start, SMLoc End) { + auto Op = std::make_unique(Kind::Token, Start, End); + Op->Token = Token; + return Op; +} + +// Imm +bool M68kOperand::isImm() const { return Kind == Kind::Imm; } +void M68kOperand::addImmOperands(MCInst &Inst, unsigned N) const { + assert(isImm() && "wrong oeprand kind"); + assert((N == 1) && "can only handle one register operand"); + + M68kOperand::addExpr(Inst, Expr); +} + +std::unique_ptr M68kOperand::createImm(const MCExpr *Expr, + SMLoc Start, SMLoc End) { + auto Op = std::make_unique(Kind::Imm, Start, End); + Op->Expr = Expr; + return Op; +} + +// Addr +bool M68kOperand::isAddr() const { + return isMemOp() && MemOp.Op == M68kMemOp::Kind::Addr; +} +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; + } +} + +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(); + + // Parse simple general-purpose registers. + if (RegisterNameLower.size() == 2) { + 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, + }; + + 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 = RegistersByIndex[IndexOffset + RegIndex]; + return true; + } + } + break; + } + + case 'c': + if (RegisterNameLower[1] == 'c' && RegisterNameLower[2] == 'r') { + RegNo = M68k::CCR; + 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() const { + switch (Parser.getTok().getKind()) { + case AsmToken::Identifier: + case AsmToken::Integer: + return true; + + 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. + auto Result = parseRegister(MemOp.OuterReg); + if (Result == MatchOperand_Success) { + MemOp.Op = M68kMemOp::Kind::Reg; + Operands.push_back( + M68kOperand::createMemOp(MemOp, Start, getLexer().getLoc())); + return MatchOperand_Success; + } + + if (Result == MatchOperand_ParseFail) { + 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; +} + +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 Kind::Invalid: + OS << "invalid"; + break; + + case Kind::Token: + OS << "token '" << Token << "'"; + break; + + case Kind::Imm: + OS << "immediate " << Imm; + break; + + case Kind::MemOp: + MemOp.print(OS); + break; + + default: + llvm_unreachable("unhandled operand kind"); + } +} diff --git a/llvm/lib/Target/M68k/CMakeLists.txt b/llvm/lib/Target/M68k/CMakeLists.txt --- a/llvm/lib/Target/M68k/CMakeLists.txt +++ b/llvm/lib/Target/M68k/CMakeLists.txt @@ -10,6 +10,7 @@ tablegen(LLVM M68kGenDAGISel.inc -gen-dag-isel) tablegen(LLVM M68kGenCallingConv.inc -gen-callingconv) tablegen(LLVM M68kGenAsmWriter.inc -gen-asm-writer) +tablegen(LLVM M68kGenAsmMatcher.inc -gen-asm-matcher) add_public_tablegen_target(M68kCommonTableGen) @@ -46,3 +47,4 @@ add_subdirectory(TargetInfo) add_subdirectory(MCTargetDesc) +add_subdirectory(AsmParser) diff --git a/llvm/lib/Target/M68k/M68k.td b/llvm/lib/Target/M68k/M68k.td --- a/llvm/lib/Target/M68k/M68k.td +++ b/llvm/lib/Target/M68k/M68k.td @@ -84,10 +84,34 @@ include "M68kCallingConv.td" +//===---------------------------------------------------------------------===// +// Assembly Printers +//===---------------------------------------------------------------------===// + +def M68kAsmWriter : AsmWriter { + string AsmWriterClassName = "InstPrinter"; + bit isMCAsmWriter = 1; +} + +//===---------------------------------------------------------------------===// +// Assembly Parsers +//===---------------------------------------------------------------------===// + +def M68kAsmParser : AsmParser { + let ShouldEmitMatchRegisterName = 0; + let ShouldEmitMatchRegisterAltName = 0; +} + +def M68kAsmParserVariant : AsmParserVariant { + int Variant = 0; +} + //===----------------------------------------------------------------------===// // Target //===----------------------------------------------------------------------===// def M68k : Target { let InstructionSet = M68kInstrInfo; + let AssemblyParsers = [M68kAsmParser]; + let AssemblyWriters = [M68kAsmWriter]; } diff --git a/llvm/lib/Target/M68k/M68kInstrInfo.td b/llvm/lib/Target/M68k/M68kInstrInfo.td --- a/llvm/lib/Target/M68k/M68kInstrInfo.td +++ b/llvm/lib/Target/M68k/M68kInstrInfo.td @@ -165,6 +165,13 @@ def MxSize16 : MxSize<16, "w", "word">; def MxSize32 : MxSize<32, "l", "long">; +class MxOpClass : AsmOperandClass { + let Name = name; + let ParserMethod = "parseMemOp"; +} + +def MxRegClass : MxOpClass<"Reg">; + class MxOperand { ValueType VT = vt; string Letter = letter; @@ -179,7 +186,9 @@ string letter, string pm = "printOperand"> : RegisterOperand, - MxOperand; + MxOperand { + let ParserMatchClass = MxRegClass; +} // REGISTER DIRECT. The operand is in the data register specified by // the effective address register field. @@ -206,11 +215,6 @@ def MxARD16_TC : MxRegOp; def MxARD32_TC : MxRegOp; -class MxOpClass : AsmOperandClass { - let Name = name; - let ParserMethod = "parse"#name; -} - class MxMemOp @@ -338,7 +342,12 @@ def MxPCI32 : MxMemOp<(ops i8imm, XR32), MxSize32, "k", "printPCI32Mem", MxPCI>; } // OPERAND_PCREL -def MxImm : MxOpClass<"MxImm">; +def MxImm : AsmOperandClass { + let Name = "MxImm"; + let PredicateMethod = "isImm"; + let RenderMethod = "addImmOperands"; + let ParserMethod = "parseImm"; +} class MxOp : Operand, @@ -362,7 +371,7 @@ } // OPERAND_IMMEDIATE let OperandType = "OPERAND_PCREL", - ParserMatchClass = MxImm, + ParserMatchClass = MxAddr, PrintMethod = "printPCRelImm" in { // Branch targets have OtherVT type and print as pc-relative values. @@ -378,7 +387,6 @@ let PrintMethod = "printMoveMask"; } - //===----------------------------------------------------------------------===// // Predicates //===----------------------------------------------------------------------===// diff --git a/llvm/test/MC/M68k/instructions.s b/llvm/test/MC/M68k/instructions.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/M68k/instructions.s @@ -0,0 +1,48 @@ +; RUN: llvm-mc -triple m68k -show-encoding -motorola-integers %s | FileCheck %s + +; At the moment, all encoding tests for M68k live in llvm/test/CodeGen/M68k/. +; This is the first test included as part of the AsmMatcher and lacks encoding checks. +; The current migration plan is to consolidate all of the encoding tests in this +; directory along with AsmMatcher/ Disassembler tests like the other platforms. +; For more information and status updates see bug #49865. + +.global ext_fn + +; CHECK: move.l %a1, %a0 +move.l %a1, %a0 +; CHECK: add.l %a0, %a1 +add.l %a0, %a1 +; CHECK: addx.l %d1, %d2 +addx.l %d1, %d2 +; CHECK: sub.w #4, %d1 +sub.w #4, %d1 +; CHECK: cmp.w %a0, %d0 +cmp.w %a0, %d0 +; CHECK: neg.w %d0 +neg.w %d0 +; CHECK: btst #8, %d3 +btst #$8, %d3 +; CHECK: bra ext_fn +bra ext_fn +; CHECK: jsr ext_fn +jsr ext_fn +; CHECK: seq %d0 +seq %d0 +; CHECK: sgt %d0 +sgt %d0 +; CHECK: lea (80,%a0), %a1 +lea $50(%a0), %a1 +; CHECK: lsl.l #8, %a1 +lsl.l #8, %a1 +; CHECK: lsr.l #8, %a1 +lsr.l #8, %a1 +; CHECK: asr.l #8, %a1 +asr.l #8, %a1 +; CHECK: rol.l #8, %a1 +rol.l #8, %a1 +; CHECK: ror.l #8, %a1 +ror.l #8, %a1 +; CHECK: nop +nop +; CHECK: rts +rts diff --git a/llvm/test/MC/M68k/lit.local.cfg b/llvm/test/MC/M68k/lit.local.cfg new file mode 100644 --- /dev/null +++ b/llvm/test/MC/M68k/lit.local.cfg @@ -0,0 +1,2 @@ +if not 'M68k' in config.root.targets: + config.unsupported = True