diff --git a/llvm/lib/Target/M68k/AsmParser/M68kAsmParser.cpp b/llvm/lib/Target/M68k/AsmParser/M68kAsmParser.cpp --- a/llvm/lib/Target/M68k/AsmParser/M68kAsmParser.cpp +++ b/llvm/lib/Target/M68k/AsmParser/M68kAsmParser.cpp @@ -52,6 +52,7 @@ bool isExpr(); OperandMatchResultTy parseImm(OperandVector &Operands); OperandMatchResultTy parseMemOp(OperandVector &Operands); + OperandMatchResultTy parseRegOrMoveMask(OperandVector &Operands); public: M68kAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser, @@ -80,6 +81,7 @@ struct M68kMemOp { enum class Kind { Addr, + RegMask, Reg, RegIndirect, RegPostIncrement, @@ -90,6 +92,7 @@ // These variables are used for the following forms: // Addr: (OuterDisp) + // RegMask: RegMask (as register mask) // Reg: %OuterReg // RegIndirect: (%OuterReg) // RegPostIncrement: (%OuterReg)+ @@ -106,6 +109,7 @@ uint8_t Size : 4; uint8_t Scale : 4; const MCExpr *Expr; + uint16_t RegMask; M68kMemOp() {} M68kMemOp(Kind Op) : Op(Op) {} @@ -172,6 +176,10 @@ static std::unique_ptr 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>(); } @@ -217,11 +225,45 @@ #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; @@ -294,7 +336,7 @@ // Imm bool M68kOperand::isImm() const { return Kind == Kind::Imm; } void M68kOperand::addImmOperands(MCInst &Inst, unsigned N) const { - assert(isImm() && "wrong oeprand kind"); + assert(isImm() && "wrong operand kind"); assert((N == 1) && "can only handle one register operand"); M68kOperand::addExpr(Inst, Expr); @@ -307,6 +349,33 @@ 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; @@ -517,11 +586,6 @@ // 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': @@ -530,7 +594,7 @@ unsigned IndexOffset = (RegisterNameLower[0] == 'a') ? 8 : 0; unsigned RegIndex = (unsigned)(RegisterNameLower[1] - '0'); if (RegIndex < 8) { - RegNo = RegistersByIndex[IndexOffset + RegIndex]; + RegNo = getRegisterByIndex(IndexOffset + RegIndex); return true; } } @@ -646,16 +710,9 @@ 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) { + // Check for a plain register or register mask. + auto Result = parseRegOrMoveMask(Operands); + if (Result != llvm::MatchOperand_NoMatch) { return Result; } @@ -773,6 +830,87 @@ 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(); 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 @@ -404,9 +404,11 @@ def MxBrTarget32 : MxBrTargetOperand<32>; // Used with MOVEM +def MxMoveMaskClass : MxOpClass<"MoveMask">; def MxMoveMask : MxOp { let OperandType = "OPERAND_IMMEDIATE"; let PrintMethod = "printMoveMask"; + let ParserMatchClass = MxMoveMaskClass; } //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/M68k/MCTargetDesc/M68kInstPrinter.cpp b/llvm/lib/Target/M68k/MCTargetDesc/M68kInstPrinter.cpp --- a/llvm/lib/Target/M68k/MCTargetDesc/M68kInstPrinter.cpp +++ b/llvm/lib/Target/M68k/MCTargetDesc/M68kInstPrinter.cpp @@ -109,7 +109,7 @@ // Print separation comma only if // both data & register parts have bit(s) set if (s != 0 && (Mask & 0xFF) && HalfMask) - O << ','; + O << '/'; for (int i = 0; HalfMask; ++i) { if ((HalfMask >> i) & 0b1) { @@ -130,7 +130,7 @@ i = j; if (HalfMask) - O << ','; + O << '/'; } } } diff --git a/llvm/test/CodeGen/M68k/CollapseMOVEM.mir b/llvm/test/CodeGen/M68k/CollapseMOVEM.mir --- a/llvm/test/CodeGen/M68k/CollapseMOVEM.mir +++ b/llvm/test/CodeGen/M68k/CollapseMOVEM.mir @@ -10,7 +10,7 @@ --- # CollapseMOVEM_RM # # CHECK-LABEL: CollapseMOVEM_RM -# CHECK: movem.l (0,%sp), %d0-%d2,%d7,%a1-%a3,%a5 +# CHECK: movem.l (0,%sp), %d0-%d2/%d7/%a1-%a3/%a5 name: CollapseMOVEM_RM body: | bb.0: @@ -26,7 +26,7 @@ ... # # CHECK-LABEL: CollapseMOVEM_RM_Reversed -# CHECK: movem.l (0,%sp), %d0-%d2,%d7,%a1-%a3,%a5 +# CHECK: movem.l (0,%sp), %d0-%d2/%d7/%a1-%a3/%a5 name: CollapseMOVEM_RM_Reversed body: | bb.0: @@ -66,7 +66,7 @@ --- # CollapseMOVEM_MR # # CHECK-LABEL: CollapseMOVEM_MR -# CHECK: movem.l %d0-%d2,%d7,%a1-%a3,%a5, (0,%sp) +# CHECK: movem.l %d0-%d2/%d7/%a1-%a3/%a5, (0,%sp) name: CollapseMOVEM_MR body: | bb.0: @@ -84,7 +84,7 @@ # # CHECK-LABEL: CollapseMOVEM_Mixed # CHECK: movem.l %d0-%d1, (0,%sp) -# CHECK: movem.l (8,%sp), %d2,%d7 +# CHECK: movem.l (8,%sp), %d2/%d7 # CHECK: movem.l %a1-%a2, (16,%sp) # CHECK: movem.l (24,%sp), %a3 # CHECK: movem.l %a5, (28,%sp) diff --git a/llvm/test/MC/M68k/instructions.s b/llvm/test/MC/M68k/instructions.s --- a/llvm/test/MC/M68k/instructions.s +++ b/llvm/test/MC/M68k/instructions.s @@ -46,3 +46,7 @@ nop ; CHECK: rts rts +; CHECK: movem.l %d0-%d6/%a0, (%sp) +movem.l %d0-%d6/%a0, (%sp) +; CHECK: movem.l (10,%sp), %d0-%d6/%a0 +movem.l (10,%sp), %d0-%d6/%a0