[M68k] Implement AsmParser
authorRicky Taylor <rickytaylor26@gmail.com>
Thu, 11 Mar 2021 20:37:57 +0000 (20:37 +0000)
committerRicky Taylor <rickytaylor26@gmail.com>
Tue, 13 Apr 2021 08:25:34 +0000 (09:25 +0100)
This is a work-in-progress implementation of an assembler for M68k.

Outstanding work:
- Updating existing tests assembly syntax
- Writing new tests for the assembler (and disassembler)

I've left those until there's consensus that this approach is okay (I hope that's okay!).

Questions I'm aware of:
- Should this use Motorola or gas syntax? (At the moment it uses Motorola syntax.)
- The disassembler produces a table at runtime for disassembly generated from the code beads. Is this okay? (This is less than ideal but as I mentioned in my llvm-dev post, it's quite complicated to write a table-gen parser for code beads.)

Depends on D98519

Depends on D98532

Depends on D98534

Depends on D98535

Depends on D98536

Differential Revision: https://reviews.llvm.org/D98537

llvm/lib/Target/M68k/AsmParser/CMakeLists.txt [new file with mode: 0644]
llvm/lib/Target/M68k/AsmParser/M68kAsmParser.cpp [new file with mode: 0644]
llvm/lib/Target/M68k/CMakeLists.txt
llvm/lib/Target/M68k/M68k.td
llvm/lib/Target/M68k/M68kInstrInfo.td
llvm/test/MC/M68k/instructions.s [new file with mode: 0644]
llvm/test/MC/M68k/lit.local.cfg [new file with mode: 0644]

diff --git a/llvm/lib/Target/M68k/AsmParser/CMakeLists.txt b/llvm/lib/Target/M68k/AsmParser/CMakeLists.txt
new file mode 100644 (file)
index 0000000..747a95e
--- /dev/null
@@ -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 (file)
index 0000000..3792da3
--- /dev/null
@@ -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 <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() 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<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);
+
+  // 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<M68kAsmParser> 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<MCConstantExpr>(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> M68kOperand::createMemOp(M68kMemOp MemOp,
+                                                      SMLoc Start, SMLoc End) {
+  auto Op = std::make_unique<M68kOperand>(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> M68kOperand::createToken(StringRef Token,
+                                                      SMLoc Start, SMLoc End) {
+  auto Op = std::make_unique<M68kOperand>(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> M68kOperand::createImm(const MCExpr *Expr,
+                                                    SMLoc Start, SMLoc End) {
+  auto Op = std::make_unique<M68kOperand>(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");
+  }
+}
index b894096..b6d7810 100644 (file)
@@ -10,6 +10,7 @@ tablegen(LLVM M68kGenMCPseudoLowering.inc -gen-pseudo-lowering)
 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_llvm_target(M68kCodeGen
 
 add_subdirectory(TargetInfo)
 add_subdirectory(MCTargetDesc)
+add_subdirectory(AsmParser)
index f308a6d..85e3a01 100644 (file)
@@ -84,10 +84,34 @@ def M68kInstrInfo : InstrInfo;
 
 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];
 }
index 973a617..e743213 100644 (file)
@@ -165,6 +165,13 @@ def MxSize8  : MxSize<8,  "b", "byte">;
 def MxSize16 : MxSize<16, "w", "word">;
 def MxSize32 : MxSize<32, "l", "long">;
 
+class MxOpClass<string name> : AsmOperandClass {
+  let Name = name;
+  let ParserMethod = "parseMemOp";
+}
+
+def MxRegClass : MxOpClass<"Reg">;
+
 class MxOperand<ValueType vt, MxSize size, string letter, RegisterClass rc, dag pat = (null_frag)> {
   ValueType VT = vt;
   string Letter = letter;
@@ -179,7 +186,9 @@ class MxRegOp<ValueType vt,
               string letter,
               string pm = "printOperand">
     : RegisterOperand<rc, pm>,
-      MxOperand<vt, size, letter, rc>;
+      MxOperand<vt, size, letter, rc> {
+  let ParserMatchClass = MxRegClass;
+}
 
 // REGISTER DIRECT. The operand is in the data register specified by
 // the effective address register field.
@@ -206,11 +215,6 @@ def MxARD32 : MxRegOp<i32, AR32, MxSize32, "a">;
 def MxARD16_TC : MxRegOp<i16, AR16_TC, MxSize16, "a">;
 def MxARD32_TC : MxRegOp<i32, AR32_TC, MxSize32, "a">;
 
-class MxOpClass<string name> : AsmOperandClass {
-  let Name = name;
-  let ParserMethod = "parse"#name;
-}
-
 class MxMemOp<dag ops, MxSize size, string letter,
               string printMethod = "printOperand",
               AsmOperandClass parserMatchClass = ImmAsmOperand>
@@ -338,7 +342,12 @@ def MxPCI16  : MxMemOp<(ops i8imm, XR32), MxSize16, "k", "printPCI16Mem", MxPCI>
 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<ValueType vt, MxSize size, string letter>
     : Operand<vt>,
@@ -362,7 +371,7 @@ def Mxi32imm : MxOp<i32, MxSize32, "i">;
 } // 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 @@ def MxMoveMask : MxOp<i16, MxSize16, "m"> {
   let PrintMethod = "printMoveMask";
 }
 
-
 //===----------------------------------------------------------------------===//
 // Predicates
 //===----------------------------------------------------------------------===//
diff --git a/llvm/test/MC/M68k/instructions.s b/llvm/test/MC/M68k/instructions.s
new file mode 100644 (file)
index 0000000..b6cb210
--- /dev/null
@@ -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 (file)
index 0000000..a0faa52
--- /dev/null
@@ -0,0 +1,2 @@
+if not 'M68k' in config.root.targets:
+    config.unsupported = True