diff --git a/llvm/lib/Target/MSP430/AsmParser/MSP430AsmParser.cpp b/llvm/lib/Target/MSP430/AsmParser/MSP430AsmParser.cpp --- a/llvm/lib/Target/MSP430/AsmParser/MSP430AsmParser.cpp +++ b/llvm/lib/Target/MSP430/AsmParser/MSP430AsmParser.cpp @@ -60,6 +60,18 @@ bool parseJccInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, OperandVector &Operands); + /// Parse an rpt directive and the associated 430X extended instruction. + /// + /// '{' is a separator string, so two formats are accepted: + /// rpt # { ... + /// or + /// rpt # + /// ... + /// + /// Returns true on error. + bool parseRptDirective(ParseInstructionInfo &Info, StringRef Name, + SMLoc NameLoc, OperandVector &Operands); + bool ParseOperand(OperandVector &Operands); bool ParseLiteralValues(unsigned Size, SMLoc L); @@ -163,11 +175,8 @@ bool isPostIndReg() const { return Kind == k_PostIndReg; } bool isCGImm() const { - if (Kind != k_Imm) - return false; - int64_t Val; - if (!Imm->evaluateAsAbsolute(Val)) + if (!isImm() || !Imm->evaluateAsAbsolute(Val)) return false; if (Val == 0 || Val == 1 || Val == 2 || Val == 4 || Val == 8 || Val == -1) @@ -176,6 +185,32 @@ return false; } + /// Return true if the immediate value can be used for a 2-bit reptition + /// count after it is encoded. + bool isRpt2Imm() const { + int64_t Val; + if (!isImm() || !Imm->evaluateAsAbsolute(Val)) + return false; + + if (Val >= 1 && Val <= 4) + return true; + + return false; + } + + /// Return true if the immediate value can be used for a 4-bit reptition + /// count after it is encoded. + bool isRpt4Imm() const { + int64_t Val; + if (!isImm() || !Imm->evaluateAsAbsolute(Val)) + return false; + + if (Val >= 1 && Val <= 16) + return true; + + return false; + } + StringRef getToken() const { assert(Kind == k_Tok && "Invalid access!"); return Tok; @@ -386,6 +421,59 @@ return false; } +bool MSP430AsmParser::parseRptDirective(ParseInstructionInfo &Info, + StringRef Name, SMLoc NameLoc, + OperandVector &Operands) { + assert(Name.startswith_insensitive("rpt") && "\"rpt\" directive expected"); + + SMLoc ExprLoc = getLexer().getLoc(); + if (!getLexer().is(AsmToken::Hash)) + return Error(ExprLoc, "expected immediate operand"); + + getLexer().Lex(); + + const MCExpr *Val; + if (getParser().parseExpression(Val)) + return Error(ExprLoc, "expected expression operand"); + + int64_t Res; + if (Val->evaluateAsAbsolute(Res)) + if (Res < 1 || Res > 16) + return Error(ExprLoc, "invalid repetition count"); + + // The repetition count operand is the second operand to the parsed + // instruction, so save it for later. + std::unique_ptr CntOp = + MSP430Operand::CreateImm(Val, ExprLoc, getLexer().getLoc()); + + if (getLexer().isNot(AsmToken::EndOfStatement)) { + SMLoc Loc = getLexer().getLoc(); + getParser().eatToEndOfStatement(); + return Error(Loc, "expected '{' or a newline after \"rpt #\""); + } + + getParser().eatToEndOfStatement(); + + // Get the name of the actual mnemonic. + Operands.push_back(MSP430Operand::CreateToken(getLexer().getTok().getString(), + getLexer().getLoc())); + getLexer().Lex(); + + // This is the first asm operand to the mnemonic, and to the parsed + // instruction. + if (ParseOperand(Operands)) + return true; + + Operands.push_back(std::move(CntOp)); + + if (getLexer().isNot(AsmToken::EndOfStatement)) { + SMLoc Loc = getLexer().getLoc(); + getParser().eatToEndOfStatement(); + return Error(Loc, "only one operand expected"); + } + return false; +} + bool MSP430AsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, OperandVector &Operands) { @@ -396,6 +484,9 @@ if (!parseJccInstruction(Info, Name, NameLoc, Operands)) return false; + if (Name.startswith_insensitive("rpt")) + return parseRptDirective(Info, Name, NameLoc, Operands); + // First operand is instruction mnemonic Operands.push_back(MSP430Operand::CreateToken(Name, NameLoc)); diff --git a/llvm/lib/Target/MSP430/Disassembler/MSP430Disassembler.cpp b/llvm/lib/Target/MSP430/Disassembler/MSP430Disassembler.cpp --- a/llvm/lib/Target/MSP430/Disassembler/MSP430Disassembler.cpp +++ b/llvm/lib/Target/MSP430/Disassembler/MSP430Disassembler.cpp @@ -102,6 +102,11 @@ static DecodeStatus DecodeCGImm(MCInst &MI, uint64_t Bits, uint64_t Address, const void *Decoder); +/// Decode the repetition count from the extension word in a 430X extended +/// instruction. +static DecodeStatus decodeRptImm(MCInst &MI, uint64_t Bits, uint64_t Address, + const void *Decoder); + static DecodeStatus DecodeMemOperand(MCInst &MI, uint64_t Bits, uint64_t Address, const void *Decoder); @@ -125,6 +130,15 @@ return MCDisassembler::Success; } +static DecodeStatus decodeRptImm(MCInst &MI, uint64_t Bits, uint64_t Address, + const void *Decoder) { + if (Bits >= 16) + return MCDisassembler::Fail; + int64_t Imm = Bits + 1; + MI.addOperand(MCOperand::createImm(Imm)); + return MCDisassembler::Success; +} + static DecodeStatus DecodeMemOperand(MCInst &MI, uint64_t Bits, uint64_t Address, const void *Decoder) { diff --git a/llvm/lib/Target/MSP430/MCTargetDesc/MSP430InstPrinter.cpp b/llvm/lib/Target/MSP430/MCTargetDesc/MSP430InstPrinter.cpp --- a/llvm/lib/Target/MSP430/MCTargetDesc/MSP430InstPrinter.cpp +++ b/llvm/lib/Target/MSP430/MCTargetDesc/MSP430InstPrinter.cpp @@ -12,6 +12,7 @@ #include "MSP430InstPrinter.h" #include "MSP430.h" +#include "MSP430InstrInfo.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" @@ -26,9 +27,29 @@ #define PRINT_ALIAS_INSTR #include "MSP430GenAsmWriter.inc" +/// Print the "rpt" directive and associated repetition count for a 430X +/// extended instruction that has the repetition flag set. +static void printRptDirective(const MCInst *MI, raw_ostream &O) { + assert(MI->getNumOperands() == 3 && "expected 3 operands"); + + const MCOperand &Op = MI->getOperand(2); + assert(Op.isImm() && "expected immediate operand for shift count"); + + unsigned Cnt = Op.getImm(); + // Accept a reptition count of 0, for when the instruction was parsed from + // assembly, without an rpt directive. + if (Cnt == 0 || Cnt == 1) + return; + O << "\trpt\t#" << Cnt << "\t{"; +} + void MSP430InstPrinter::printInst(const MCInst *MI, uint64_t Address, StringRef Annot, const MCSubtargetInfo &STI, raw_ostream &O) { + const MCInstrDesc &Desc = MII.get(MI->getOpcode()); + if (Desc.TSFlags & MSP430TSFlags::RptCount) + printRptDirective(MI, O); + if (!printAliasInstr(MI, Address, O)) printInstruction(MI, Address, O); printAnnotation(O, Annot); diff --git a/llvm/lib/Target/MSP430/MCTargetDesc/MSP430MCCodeEmitter.cpp b/llvm/lib/Target/MSP430/MCTargetDesc/MSP430MCCodeEmitter.cpp --- a/llvm/lib/Target/MSP430/MCTargetDesc/MSP430MCCodeEmitter.cpp +++ b/llvm/lib/Target/MSP430/MCTargetDesc/MSP430MCCodeEmitter.cpp @@ -10,9 +10,10 @@ // //===----------------------------------------------------------------------===// -#include "MSP430.h" -#include "MCTargetDesc/MSP430MCTargetDesc.h" #include "MCTargetDesc/MSP430FixupKinds.h" +#include "MCTargetDesc/MSP430MCTargetDesc.h" +#include "MSP430.h" +#include "MSP430InstrInfo.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/SmallVector.h" @@ -66,6 +67,17 @@ SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; + /// Encode the repetition count for a 430X extended format exception + /// instruction. + unsigned getRpt2ImmOpValue(const MCInst &MI, unsigned Op, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + /// Encode the repetition count for a 430X extended instruction. + unsigned getRpt4ImmOpValue(const MCInst &MI, unsigned Op, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + unsigned getCCOpValue(const MCInst &MI, unsigned Op, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; @@ -79,6 +91,15 @@ const MCSubtargetInfo &STI) const override; }; +/// Move the extension word from the last word of the 64-bit encoded value, to +/// the first word of the encoded value. +static uint64_t fixExtensionWord(uint64_t EncodedValue) { + uint64_t ExtensionWord = EncodedValue >> 48; + EncodedValue <<= 16; + EncodedValue |= ExtensionWord; + return EncodedValue; +} + void MSP430MCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const { @@ -87,9 +108,12 @@ unsigned Size = Desc.getSize(); // Initialize fixup offset - Offset = 2; + Offset = ((Desc.TSFlags & MSP430TSFlags::ExtensionWord) ? 4 : 2); uint64_t BinaryOpCode = getBinaryCodeForInstr(MI, Fixups, STI); + if (Desc.TSFlags & MSP430TSFlags::ExtensionWord) + BinaryOpCode = fixExtensionWord(BinaryOpCode); + size_t WordCount = Size / 2; while (WordCount--) { @@ -181,6 +205,34 @@ } } +unsigned +MSP430MCCodeEmitter::getRpt2ImmOpValue(const MCInst &MI, unsigned Op, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(Op); + assert(MO.isImm() && "expr operand expected"); + + int64_t Imm = MO.getImm(); + assert((Imm >= 1 && Imm <= 4) && "invalid repetition count"); + return Imm - 1; +} + +unsigned +MSP430MCCodeEmitter::getRpt4ImmOpValue(const MCInst &MI, unsigned Op, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(Op); + assert(MO.isImm() && "expr operand expected"); + + int64_t Imm = MO.getImm(); + assert((Imm >= 0 && Imm <= 16) && "invalid repetition count"); + // Imm is zero when there is no repetition count, because the instruction has + // been used without the rpt directive. + if (Imm == 0) + return Imm; + return Imm - 1; +} + unsigned MSP430MCCodeEmitter::getCCOpValue(const MCInst &MI, unsigned Op, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const { diff --git a/llvm/lib/Target/MSP430/MSP430ISelDAGToDAG.cpp b/llvm/lib/Target/MSP430/MSP430ISelDAGToDAG.cpp --- a/llvm/lib/Target/MSP430/MSP430ISelDAGToDAG.cpp +++ b/llvm/lib/Target/MSP430/MSP430ISelDAGToDAG.cpp @@ -92,13 +92,17 @@ class MSP430DAGToDAGISel : public SelectionDAGISel { public: MSP430DAGToDAGISel(MSP430TargetMachine &TM, CodeGenOpt::Level OptLevel) - : SelectionDAGISel(TM, OptLevel) {} + : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) {} private: + const MSP430Subtarget *Subtarget; + StringRef getPassName() const override { return "MSP430 DAG->DAG Pattern Instruction Selection"; } + bool runOnMachineFunction(MachineFunction &MF) override; + bool MatchAddress(SDValue N, MSP430ISelAddressMode &AM); bool MatchWrapper(SDValue N, MSP430ISelAddressMode &AM); bool MatchAddressBase(SDValue N, MSP430ISelAddressMode &AM); @@ -120,6 +124,11 @@ }; } // end anonymous namespace +bool MSP430DAGToDAGISel::runOnMachineFunction(MachineFunction &MF) { + Subtarget = &MF.getSubtarget(); + return SelectionDAGISel::runOnMachineFunction(MF); +} + /// createMSP430ISelDag - This pass converts a legalized DAG into a /// MSP430-specific DAG, ready for instruction scheduling. /// diff --git a/llvm/lib/Target/MSP430/MSP430ISelLowering.h b/llvm/lib/Target/MSP430/MSP430ISelLowering.h --- a/llvm/lib/Target/MSP430/MSP430ISelLowering.h +++ b/llvm/lib/Target/MSP430/MSP430ISelLowering.h @@ -20,57 +20,68 @@ namespace llvm { namespace MSP430ISD { - enum NodeType : unsigned { - FIRST_NUMBER = ISD::BUILTIN_OP_END, + enum NodeType : unsigned { + FIRST_NUMBER = ISD::BUILTIN_OP_END, - /// Return with a flag operand. Operand 0 is the chain operand. - RET_FLAG, + /// Return with a flag operand. Operand 0 is the chain operand. + RET_FLAG, - /// Same as RET_FLAG, but used for returning from ISRs. - RETI_FLAG, + /// Same as RET_FLAG, but used for returning from ISRs. + RETI_FLAG, - /// Y = R{R,L}A X, rotate right (left) arithmetically - RRA, RLA, + /// Y = R{R,L}A X, rotate right (left) arithmetically + RRA, + RLA, - /// Y = RRC X, rotate right via carry - RRC, + /// Y = RRC X, rotate right via carry + RRC, - /// Rotate right via carry, carry gets cleared beforehand by clrc - RRCL, + /// Rotate right via carry, carry gets cleared beforehand by clrc + RRCL, - /// CALL - These operations represent an abstract call - /// instruction, which includes a bunch of information. - CALL, + /// CALL - These operations represent an abstract call + /// instruction, which includes a bunch of information. + CALL, - /// Wrapper - A wrapper node for TargetConstantPool, TargetExternalSymbol, - /// and TargetGlobalAddress. - Wrapper, + /// Wrapper - A wrapper node for TargetConstantPool, TargetExternalSymbol, + /// and TargetGlobalAddress. + Wrapper, - /// CMP - Compare instruction. - CMP, + /// CMP - Compare instruction. + CMP, - /// SetCC - Operand 0 is condition code, and operand 1 is the flag - /// operand produced by a CMP instruction. - SETCC, + /// SetCC - Operand 0 is condition code, and operand 1 is the flag + /// operand produced by a CMP instruction. + SETCC, - /// MSP430 conditional branches. Operand 0 is the chain operand, operand 1 - /// is the block to branch if condition is true, operand 2 is the - /// condition code, and operand 3 is the flag operand produced by a CMP - /// instruction. - BR_CC, + /// MSP430 conditional branches. Operand 0 is the chain operand, operand 1 + /// is the block to branch if condition is true, operand 2 is the + /// condition code, and operand 3 is the flag operand produced by a CMP + /// instruction. + BR_CC, - /// SELECT_CC - Operand 0 and operand 1 are selection variable, operand 3 - /// is condition code and operand 4 is flag operand. - SELECT_CC, + /// SELECT_CC - Operand 0 and operand 1 are selection variable, operand 3 + /// is condition code and operand 4 is flag operand. + SELECT_CC, - /// DADD - Decimal addition with carry - /// TODO Nothing generates a node of this type yet. - DADD, - }; - } + /// DADD - Decimal addition with carry + /// TODO Nothing generates a node of this type yet. + DADD, + + /// Extended versions of the shift instructions, supported by the 430X CPU. + RRAM, + RRUM, + RLAM, + RRAX, + RLAX, + RRUX + }; + } // namespace MSP430ISD class MSP430Subtarget; class MSP430TargetLowering : public TargetLowering { + const MSP430Subtarget *Subtarget; + public: explicit MSP430TargetLowering(const TargetMachine &TM, const MSP430Subtarget &STI); diff --git a/llvm/lib/Target/MSP430/MSP430ISelLowering.cpp b/llvm/lib/Target/MSP430/MSP430ISelLowering.cpp --- a/llvm/lib/Target/MSP430/MSP430ISelLowering.cpp +++ b/llvm/lib/Target/MSP430/MSP430ISelLowering.cpp @@ -43,7 +43,7 @@ MSP430TargetLowering::MSP430TargetLowering(const TargetMachine &TM, const MSP430Subtarget &STI) - : TargetLowering(TM) { + : TargetLowering(TM), Subtarget(&STI) { // Set up the register classes. addRegisterClass(MVT::i8, &MSP430::GR8RegClass); @@ -360,7 +360,8 @@ // Define non profitable transforms into shifts bool MSP430TargetLowering::shouldAvoidTransformToShift(EVT VT, unsigned Amount) const { - return !(Amount == 8 || Amount == 9 || Amount<=2); + return !(Subtarget->hasMSP430X() || Amount == 8 || Amount == 9 || + Amount <= 2); } // Implemented to verify test case assertions in @@ -953,59 +954,183 @@ return Chain; } -SDValue MSP430TargetLowering::LowerShifts(SDValue Op, - SelectionDAG &DAG) const { +/// Efficiently lower a shift count of 8 by swapping bytes of the value to be +/// shifted, instead of using shift instructions. +static SDValue shiftBy8(SDValue Op, SelectionDAG &DAG) { unsigned Opc = Op.getOpcode(); - SDNode* N = Op.getNode(); EVT VT = Op.getValueType(); - SDLoc dl(N); + SDLoc Dl(Op); + SDValue Victim = Op.getOperand(0); - // Expand non-constant shifts to loops: - if (!isa(N->getOperand(1))) - return Op; - - uint64_t ShiftAmount = cast(N->getOperand(1))->getZExtValue(); + switch (Opc) { + default: + llvm_unreachable("Unknown shift"); + case ISD::SHL: + // foo << (8 + N) => swpb(zext(foo)) << N + Victim = DAG.getZeroExtendInReg(Victim, Dl, MVT::i8); + Victim = DAG.getNode(ISD::BSWAP, Dl, VT, Victim); + break; + case ISD::SRA: + case ISD::SRL: + // foo >> (8 + N) => sxt(swpb(foo)) >> N + Victim = DAG.getNode(ISD::BSWAP, Dl, VT, Victim); + Victim = (Opc == ISD::SRA) ? DAG.getNode(ISD::SIGN_EXTEND_INREG, Dl, VT, + Victim, DAG.getValueType(MVT::i8)) + : DAG.getZeroExtendInReg(Victim, Dl, MVT::i8); + break; + } + return Victim; +} - // Expand the stuff into sequence of shifts. - SDValue Victim = N->getOperand(0); +/// Lower shifts of byte or word-sized operands, using the 430 instructions RRC, +/// RLA and RRA. +static SDValue lowerShiftToRxx(SDValue Op, SelectionDAG &DAG) { + unsigned Opc = Op.getOpcode(); + EVT VT = Op.getValueType(); + SDLoc Dl(Op); + SDValue Victim = Op.getOperand(0); + assert(isa(Op.getOperand(1))); + int ShiftAmount = cast(Op.getOperand(1))->getZExtValue(); if (ShiftAmount >= 8) { - assert(VT == MVT::i16 && "Can not shift i8 by 8 and more"); - switch(Opc) { - default: - llvm_unreachable("Unknown shift"); - case ISD::SHL: - // foo << (8 + N) => swpb(zext(foo)) << N - Victim = DAG.getZeroExtendInReg(Victim, dl, MVT::i8); - Victim = DAG.getNode(ISD::BSWAP, dl, VT, Victim); - break; - case ISD::SRA: - case ISD::SRL: - // foo >> (8 + N) => sxt(swpb(foo)) >> N - Victim = DAG.getNode(ISD::BSWAP, dl, VT, Victim); - Victim = (Opc == ISD::SRA) - ? DAG.getNode(ISD::SIGN_EXTEND_INREG, dl, VT, Victim, - DAG.getValueType(MVT::i8)) - : DAG.getZeroExtendInReg(Victim, dl, MVT::i8); - break; - } + Victim = shiftBy8(Op, DAG); + // For SRL, the top byte is now 0, so we can go straight to emitting RRA. + // CLRC, RRC is not required. ShiftAmount -= 8; - } - - if (Opc == ISD::SRL && ShiftAmount) { + } else if (Opc == ISD::SRL) { // Emit a special goodness here: // srl A, 1 => clrc; rrc A - Victim = DAG.getNode(MSP430ISD::RRCL, dl, VT, Victim); + Victim = DAG.getNode(MSP430ISD::RRCL, Dl, VT, Victim); ShiftAmount -= 1; } while (ShiftAmount--) Victim = DAG.getNode((Opc == ISD::SHL ? MSP430ISD::RLA : MSP430ISD::RRA), - dl, VT, Victim); + Dl, VT, Victim); + return Victim; +} + +/// Lower shifts of byte or word-sized operands, using the 430X extended +/// instructions RLAX, RRUX and RRAX. +static SDValue lowerShiftToRxxX(SDValue Op, SelectionDAG &DAG) { + EVT VT = Op.getValueType(); + SDLoc Dl(Op); + SDValue Victim = Op.getOperand(0); + assert(isa(Op.getOperand(1))); + int ShiftAmount = cast(Op.getOperand(1))->getZExtValue(); + + if (ShiftAmount == 8) + return shiftBy8(Op, DAG); + + switch (Op.getOpcode()) { + case ISD::SHL: + return DAG.getNode(MSP430ISD::RLAX, Dl, VT, Victim, + DAG.getConstant(ShiftAmount, Dl, MVT::i8)); + case ISD::SRA: + return DAG.getNode(MSP430ISD::RRAX, Dl, VT, Victim, + DAG.getConstant(ShiftAmount, Dl, MVT::i8)); + case ISD::SRL: + return DAG.getNode(MSP430ISD::RRUX, Dl, VT, Victim, + DAG.getConstant(ShiftAmount, Dl, MVT::i8)); + default: + llvm_unreachable("unhandled shift instruction"); + } +} + +/// Lower shifts of word-sized operands, using the 430X extended +/// format exception instructions RLAM, RRUM and RRAM. +static SDValue lowerShiftToRxxM(SDValue Op, SelectionDAG &DAG) { + assert(Op.getValueSizeInBits() == 16 && + "can only lower shifts to RxxM for word-sized ops"); + EVT VT = Op.getValueType(); + SDLoc Dl(Op); + SDValue Victim = Op.getOperand(0); + assert(isa(Op.getOperand(1))); + int ShiftAmount = cast(Op.getOperand(1))->getZExtValue(); + + if (ShiftAmount > 8) { + Victim = shiftBy8(Op, DAG); + ShiftAmount -= 8; + } + + while (ShiftAmount > 0) { + int CurrentShiftAmount = ShiftAmount; + if (CurrentShiftAmount > 4) + CurrentShiftAmount = 4; + switch (Op.getOpcode()) { + case ISD::SHL: + Victim = DAG.getNode(MSP430ISD::RLAM, Dl, VT, Victim, + DAG.getConstant(CurrentShiftAmount, Dl, MVT::i8)); + break; + case ISD::SRA: + Victim = DAG.getNode(MSP430ISD::RRAM, Dl, VT, Victim, + DAG.getConstant(CurrentShiftAmount, Dl, MVT::i8)); + break; + case ISD::SRL: + Victim = DAG.getNode(MSP430ISD::RRUM, Dl, VT, Victim, + DAG.getConstant(CurrentShiftAmount, Dl, MVT::i8)); + break; + default: + llvm_unreachable("unhandled shift instruction"); + break; + } + ShiftAmount -= CurrentShiftAmount; + } return Victim; } +SDValue MSP430TargetLowering::LowerShifts(SDValue Op, SelectionDAG &DAG) const { + + // Expand non-constant shifts to loops. + // + // TODO: For the MSP430X CPU, if the non-constant shift amount is in a + // register, we can use the RPT flag in the extension word to shift by the + // amount stored in the register, without using loops. + if (!isa(Op.getOperand(1))) + return Op; + + // Let the middle-end handle shifts of 32-bit/64-bit operands. + TypeSize OpSize = Op.getValueSizeInBits(); + if (OpSize > 16) + return Op; + + if (!Subtarget->hasMSP430X()) + return lowerShiftToRxx(Op, DAG); + + int ShiftAmount = cast(Op.getOperand(1))->getZExtValue(); + if (ShiftAmount == 1) { + // SRL -> RRC requires a CLRC first, for a total of 2 words. We can do it in + // 1 word with RRUM. + // + // TODO: If op0 is not a register, it would be more efficient to use an Rxx + // shift. + if (OpSize == 16 && Op.getOpcode() == ISD::SRL) + return lowerShiftToRxxM(Op, DAG); + return lowerShiftToRxx(Op, DAG); + } + if (ShiftAmount == 8) + return shiftBy8(Op, DAG); + if (OpSize == 8) { + // RxxM shifts can't handle byte-sized operands. + return lowerShiftToRxxX(Op, DAG); + } + + // Even though RxxM can only shift by <= 4, for shift counts < 8 it's cheaper + // to chain the RxxM shifts than to use the RxxX shifts with rpt. This saves + // one cycle and uses the same number of words. + // + // Only when not optimizing for size, use shiftBy8 followed by an RxxM chain + // to shift by > 8. Compared to using an RxxX insn, this saves 7 cycles but + // adds 2 words. + if (Op.getValueSizeInBits() == 16 && + (ShiftAmount < 8 || !DAG.getMachineFunction().getFunction().hasOptSize())) + return lowerShiftToRxxM(Op, DAG); + + assert(OpSize == 16 && ShiftAmount > 8); + return lowerShiftToRxxX(Op, DAG); +} + SDValue MSP430TargetLowering::LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const { const GlobalValue *GV = cast(Op)->getGlobal(); @@ -1366,20 +1491,46 @@ const char *MSP430TargetLowering::getTargetNodeName(unsigned Opcode) const { switch ((MSP430ISD::NodeType)Opcode) { - case MSP430ISD::FIRST_NUMBER: break; - case MSP430ISD::RET_FLAG: return "MSP430ISD::RET_FLAG"; - case MSP430ISD::RETI_FLAG: return "MSP430ISD::RETI_FLAG"; - case MSP430ISD::RRA: return "MSP430ISD::RRA"; - case MSP430ISD::RLA: return "MSP430ISD::RLA"; - case MSP430ISD::RRC: return "MSP430ISD::RRC"; - case MSP430ISD::RRCL: return "MSP430ISD::RRCL"; - case MSP430ISD::CALL: return "MSP430ISD::CALL"; - case MSP430ISD::Wrapper: return "MSP430ISD::Wrapper"; - case MSP430ISD::BR_CC: return "MSP430ISD::BR_CC"; - case MSP430ISD::CMP: return "MSP430ISD::CMP"; - case MSP430ISD::SETCC: return "MSP430ISD::SETCC"; - case MSP430ISD::SELECT_CC: return "MSP430ISD::SELECT_CC"; - case MSP430ISD::DADD: return "MSP430ISD::DADD"; + case MSP430ISD::FIRST_NUMBER: + break; + case MSP430ISD::RET_FLAG: + return "MSP430ISD::RET_FLAG"; + case MSP430ISD::RETI_FLAG: + return "MSP430ISD::RETI_FLAG"; + case MSP430ISD::RRA: + return "MSP430ISD::RRA"; + case MSP430ISD::RLA: + return "MSP430ISD::RLA"; + case MSP430ISD::RRC: + return "MSP430ISD::RRC"; + case MSP430ISD::RRCL: + return "MSP430ISD::RRCL"; + case MSP430ISD::CALL: + return "MSP430ISD::CALL"; + case MSP430ISD::Wrapper: + return "MSP430ISD::Wrapper"; + case MSP430ISD::BR_CC: + return "MSP430ISD::BR_CC"; + case MSP430ISD::CMP: + return "MSP430ISD::CMP"; + case MSP430ISD::SETCC: + return "MSP430ISD::SETCC"; + case MSP430ISD::SELECT_CC: + return "MSP430ISD::SELECT_CC"; + case MSP430ISD::DADD: + return "MSP430ISD::DADD"; + case MSP430ISD::RRAM: + return "MSP430ISD::RRAM"; + case MSP430ISD::RRUM: + return "MSP430ISD::RRUM"; + case MSP430ISD::RLAM: + return "MSP430ISD::RLAM"; + case MSP430ISD::RRAX: + return "MSP430ISD::RRAX"; + case MSP430ISD::RLAX: + return "MSP430ISD::RLAX"; + case MSP430ISD::RRUX: + return "MSP430ISD::RRUX"; } return nullptr; } diff --git a/llvm/lib/Target/MSP430/MSP430InstrFormats.td b/llvm/lib/Target/MSP430/MSP430InstrFormats.td --- a/llvm/lib/Target/MSP430/MSP430InstrFormats.td +++ b/llvm/lib/Target/MSP430/MSP430InstrFormats.td @@ -28,10 +28,17 @@ def DstReg : DestMode<0>; // r def DstMem : DestMode<1>; // m +class ALKind { + bit Value = val; +} + +def ALAddr : ALKind<0>; +def ALWord : ALKind<1>; + // Generic MSP430 Format class MSP430Inst : Instruction { - field bits<48> Inst; - field bits<48> SoftFail = 0; + field bits<64> Inst; + field bits<64> SoftFail = 0; let Namespace = "MSP430"; @@ -40,6 +47,11 @@ let AsmString = asmstr; let Size = size; + + bit RptCountFlag = 0; + bit ExtensionWordFlag = 0; + let TSFlags{0} = ExtensionWordFlag; + let TSFlags{1} = RptCountFlag; } // MSP430 Double Operand (Format I) Instructions @@ -433,6 +445,84 @@ let Inst{9-0} = dst; } +// MSP430 Extended Format II Instruction Format Exception - Rotation. +class IIExtExcRotForm opcode, ALKind al, + dag outs, dag ins, string asmstr, list pattern> + : MSP430Inst { + let Pattern = pattern; + + bits<4> rd; + bits<2> cnt; + + let Inst{15-12} = 0b0000; + let Inst{11-10} = cnt; + let Inst{9-8} = opcode; + let Inst{7-5} = 0b010; + let Inst{4} = al.Value; + let Inst{3-0} = rd; +} + +class IIExtExcRotForm16 opcode, dag outs, dag ins, string asmstr, list pattern> + : IIExtExcRotForm; + +// The 430X extension word for register addressing modes. +// +// MSP430MCCodeEmitter expects the ExtensionWord field to be positioned at bits +// [63:48] of the Inst field in the MSP430Inst that the instruction corresponds +// to. encodeInstruction will rearrange the bits of the instruction to ensure +// the ExtensionWord is positioned correctly in the final binary encoding. +// +// Until the MSP430 large memory model is supported, there's no benefit to using +// non-register mode extended instructions, so they are not currently +// implemented. +class ExtensionWordRegForm { + bits<16> ExtensionWord; + bits<4> cnt; + + let ExtensionWord{15-9} = 0b0001100; + let ExtensionWord{8} = zc; + let ExtensionWord{7} = rpt; + let ExtensionWord{6} = al.Value; + let ExtensionWord{5-4} = 0b00; + let ExtensionWord{3-0} = cnt; +} + +class II16rExtended opcode, bit zc, bit rpt, + dag outs, dag ins, string asmstr, list pattern> + : IIForm16, + ExtensionWordRegForm { + let Pattern = pattern; + let ExtensionWordFlag = 1; + let Inst{63-48} = ExtensionWord; +} + +class I16rExtended opcode, bit zc, bit rpt, + dag outs, dag ins, string asmstr, list pattern> + : IForm16, + ExtensionWordRegForm { + let Pattern = pattern; + let ExtensionWordFlag = 1; + let Inst{63-48} = ExtensionWord; +} + +class II8rExtended opcode, bit zc, bit rpt, + dag outs, dag ins, string asmstr, list pattern> + : IIForm8, + ExtensionWordRegForm { + let Pattern = pattern; + let ExtensionWordFlag = 1; + let Inst{63-48} = ExtensionWord; +} + +class I8rExtended opcode, bit zc, bit rpt, + dag outs, dag ins, string asmstr, list pattern> + : IForm8, + ExtensionWordRegForm { + let Pattern = pattern; + let ExtensionWordFlag = 1; + let Inst{63-48} = ExtensionWord; +} + // Pseudo instructions class Pseudo pattern> : MSP430Inst { diff --git a/llvm/lib/Target/MSP430/MSP430InstrInfo.h b/llvm/lib/Target/MSP430/MSP430InstrInfo.h --- a/llvm/lib/Target/MSP430/MSP430InstrInfo.h +++ b/llvm/lib/Target/MSP430/MSP430InstrInfo.h @@ -20,6 +20,20 @@ #include "MSP430GenInstrInfo.inc" namespace llvm { +/// Namespace to describe the target-specific flags of an MSP430Inst. +/// +/// The flag bit positions need to match the TSFlag bits set in the MSP430Inst +/// TableGen class defined in MSP430InstrFormats.td. +namespace MSP430TSFlags { +enum { + /// This bit is set if the instruction is an MSP430X extended instruction with + /// an extension word. + ExtensionWord = 0x1, + /// This bit is set if the instruction uses the reptition count field of the + /// the extension word. + RptCount = 0x2 +}; +} // namespace MSP430TSFlags class MSP430Subtarget; diff --git a/llvm/lib/Target/MSP430/MSP430InstrInfo.td b/llvm/lib/Target/MSP430/MSP430InstrInfo.td --- a/llvm/lib/Target/MSP430/MSP430InstrInfo.td +++ b/llvm/lib/Target/MSP430/MSP430InstrInfo.td @@ -12,6 +12,13 @@ include "MSP430InstrFormats.td" +//===----------------------------------------------------------------------===// +// Feature predicates. +//===----------------------------------------------------------------------===// + +// True when MSP430X extended instructions are available. +def HasMSP430X : Predicate<"Subtarget->hasMSP430X()">; + //===----------------------------------------------------------------------===// // Type Constraints. //===----------------------------------------------------------------------===// @@ -1039,3 +1046,5 @@ def : Pat<(MSP430cmp (trunc (and_su GR16:$src, GR16:$src2)), 0), (BIT8rr (EXTRACT_SUBREG GR16:$src, subreg_8bit), (EXTRACT_SUBREG GR16:$src2, subreg_8bit))>; + +include "MSP430InstrInfoExtended.td" diff --git a/llvm/lib/Target/MSP430/MSP430InstrInfoExtended.td b/llvm/lib/Target/MSP430/MSP430InstrInfoExtended.td new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/MSP430/MSP430InstrInfoExtended.td @@ -0,0 +1,127 @@ +//===-- MSP430InstrInfoExtended.td - MSP430X Instruction defs -------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file describes the MSP430X extended instructions in TableGen format. +// +//===----------------------------------------------------------------------===// + +// TODO: Most extended instructions have a .A version that enables 20-bit +// operands. This requires support the large memory model to be implemented. +// +// Only the extended instructions that are useful in the small memory model +// (16-bit pointers) are implemented so far. Instructions like MOVX are not +// required in the small memory model, and have increased size/cycle count +// compared to their 430 versions. + +def MSP430rram : SDNode<"MSP430ISD::RRAM", SDTIntShiftOp>; +def MSP430rrum : SDNode<"MSP430ISD::RRUM", SDTIntShiftOp>; +def MSP430rlam : SDNode<"MSP430ISD::RLAM", SDTIntShiftOp>; + +def MSP430rrax : SDNode<"MSP430ISD::RRAX", SDTIntShiftOp>; +def MSP430rlax : SDNode<"MSP430ISD::RLAX", SDTIntShiftOp>; +def MSP430rrux : SDNode<"MSP430ISD::RRUX", SDTIntShiftOp>; + +// Differentiate between rpt2 and rpt4 to catch shift counts in IIExtExcRot +// instructions that are too large. +def Rpt2ImmAsmOperand : AsmOperandClass { + let Name = "Rpt2Imm"; + let RenderMethod = "addImmOperands"; +} + +def Rpt4ImmAsmOperand : AsmOperandClass { + let Name = "Rpt4Imm"; + let RenderMethod = "addImmOperands"; +} + +def rpt2imm : Operand, + ImmLeaf= 1 && Imm <= 4;}]> { + let ParserMatchClass = Rpt2ImmAsmOperand; + let EncoderMethod = "getRpt2ImmOpValue"; + let DecoderMethod = "decodeRptImm"; +} + +def rpt4imm : Operand, + ImmLeaf= 1 && Imm <= 16;}]> { + let ParserMatchClass = Rpt4ImmAsmOperand; + let EncoderMethod = "getRpt4ImmOpValue"; + let DecoderMethod = "decodeRptImm"; +} + +//===----------------------------------------------------------------------===// +// Shift Instructions +// +let Predicates = [HasMSP430X] in { +let Constraints = "$rs = $rd", Defs = [SR] in { + + multiclass IIExtExcRot OpCode, SDNode OpNode> { + def _16 : IIExtExcRotForm16; + } + + defm RRAM : IIExtExcRot<"rram", 0b01, MSP430rram>; + defm RRUM : IIExtExcRot<"rrum", 0b11, MSP430rrum>; + defm RLAM : IIExtExcRot<"rlam", 0b10, MSP430rlam>; + + multiclass IIExtRot OpCode, bit ZC, SDNode OpNode> { + let RptCountFlag = 1 in { + def _8 : II8rExtended; + def _16 : II16rExtended; + } + } + defm RRAX : IIExtRot<"rrax", 0b010, 0, MSP430rrax>; + // RRUX is encoded as RRCX with the ZC flag set. + defm RRUX : IIExtRot<"rrux", 0b000, 1, MSP430rrux>; + + multiclass IExtRot OpCode, SDNode OpNode> { + let RptCountFlag = 1 in { + def _8 : I8rExtended; + def _16 : I16rExtended; + } + } + + // RLAX is encoded as ADDX. + defm RLAX : IExtRot<"rlax", 0b0101, MSP430rlax>; + +} // Defs = [SR], Constraints = "$rs = $rd" + +// These aliases are required so the DAG node created from +// "rpt # { " can be matched. +// +// When parsing the "rpt" directive and associated instruction, the repetition +// count is set up as the 2nd operand of the instruction. The default assembly +// string for these instructions only has one operand because the count is set +// separately by the rpt directive. +// +// The instructions don't actually have this format, so don't ever print them. +def : InstAlias<"rrax\t$rd, $cnt", (RRAX_16 GR16:$rd, rpt4imm:$cnt), 0>; +def : InstAlias<"rrux\t$rd, $cnt", (RRUX_16 GR16:$rd, rpt4imm:$cnt), 0>; +def : InstAlias<"rlax\t$rd, $cnt", (RLAX_16 GR16:$rd, rpt4imm:$cnt), 0>; +def : InstAlias<"rrax.b\t$rd, $cnt", (RRAX_8 GR8:$rd, rpt4imm:$cnt), 0>; +def : InstAlias<"rrux.b\t$rd, $cnt", (RRUX_8 GR8:$rd, rpt4imm:$cnt), 0>; +def : InstAlias<"rlax.b\t$rd, $cnt", (RLAX_8 GR8:$rd, rpt4imm:$cnt), 0>; + +} // Predicates = [HasMSP430X] diff --git a/llvm/test/CodeGen/MSP430/shift-amount-threshold.ll b/llvm/test/CodeGen/MSP430/shift-amount-threshold.ll --- a/llvm/test/CodeGen/MSP430/shift-amount-threshold.ll +++ b/llvm/test/CodeGen/MSP430/shift-amount-threshold.ll @@ -199,8 +199,7 @@ ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: swpb r12 ; CHECK-NEXT: mov.b r12, r12 -; CHECK-NEXT: clrc -; CHECK-NEXT: rrc r12 +; CHECK-NEXT: rra r12 ; CHECK-NEXT: and #64, r12 ; CHECK-NEXT: ret entry: diff --git a/llvm/test/CodeGen/MSP430/shifts-430x-byte.ll b/llvm/test/CodeGen/MSP430/shifts-430x-byte.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/MSP430/shifts-430x-byte.ll @@ -0,0 +1,178 @@ +; Tests for constant shifts of 8-bit register operands, for the 430X CPU. +; +; RUN: llc -mcpu=msp430x < %s | FileCheck %s +target datalayout = "e-p:16:8:8-i8:8:8-i8:8:8-i32:8:8-n8:16" +target triple = "msp430-elf" + +;; Tests for logical right shifts. +define i8 @lshr1(i8 %value) { +; CHECK-LABEL: lshr1: +; CHECK: clrc +; CHECK-NEXT: rrc.b r12 + %result = lshr i8 %value, 1 + ret i8 %result +} + +define i8 @lshr2(i8 %value) { +; CHECK-LABEL: lshr2: +; CHECK: rpt #2 { rrux.b r12 + %result = lshr i8 %value, 2 + ret i8 %result +} + +define i8 @lshr3(i8 %value) { +; CHECK-LABEL: lshr3: +; CHECK: rpt #3 { rrux.b r12 + %result = lshr i8 %value, 3 + ret i8 %result +} + +define i8 @lshr4(i8 %value) { +; CHECK-LABEL: lshr4: +; CHECK: rpt #4 { rrux.b r12 + %result = lshr i8 %value, 4 + ret i8 %result +} + +define i8 @lshr5(i8 %value) { +; CHECK-LABEL: lshr5: +; CHECK: rpt #5 { rrux.b r12 + %result = lshr i8 %value, 5 + ret i8 %result +} + +define i8 @lshr6(i8 %value) { +; CHECK-LABEL: lshr6: +; CHECK: rpt #6 { rrux.b r12 + %result = lshr i8 %value, 6 + ret i8 %result +} + +define i8 @lshr7(i8 %value) { +; CHECK-LABEL: lshr7: +; CHECK: rpt #7 { rrux.b r12 + %result = lshr i8 %value, 7 + ret i8 %result +} + +define i8 @lshr8(i8 %value) { +; CHECK-LABEL: lshr8: +; CHECK-NOT: rrum +; CHECK-NOT: rrux + %result = lshr i8 %value, 8 + ret i8 %result +} + +;; Tests for arithmetic right shifts. +define i8 @ashr1(i8 %value) { +; CHECK-LABEL: ashr1: +; CHECK: rra.b r12 + %result = ashr i8 %value, 1 + ret i8 %result +} + +define i8 @ashr2(i8 %value) { +; CHECK-LABEL: ashr2: +; CHECK: rpt #2 { rrax.b r12 + %result = ashr i8 %value, 2 + ret i8 %result +} + +define i8 @ashr3(i8 %value) { +; CHECK-LABEL: ashr3: +; CHECK: rpt #3 { rrax.b r12 + %result = ashr i8 %value, 3 + ret i8 %result +} + +define i8 @ashr4(i8 %value) { +; CHECK-LABEL: ashr4: +; CHECK: rpt #4 { rrax.b r12 + %result = ashr i8 %value, 4 + ret i8 %result +} + +define i8 @ashr5(i8 %value) { +; CHECK-LABEL: ashr5: +; CHECK: rpt #5 { rrax.b r12 + %result = ashr i8 %value, 5 + ret i8 %result +} + +define i8 @ashr6(i8 %value) { +; CHECK-LABEL: ashr6: +; CHECK: rpt #6 { rrax.b r12 + %result = ashr i8 %value, 6 + ret i8 %result +} + +define i8 @ashr7(i8 %value) { +; CHECK-LABEL: ashr7: +; CHECK: rpt #7 { rrax.b r12 + %result = ashr i8 %value, 7 + ret i8 %result +} + +define i8 @ashr8(i8 %value) { +; CHECK-LABEL: ashr8: +; CHECK-NOT: rra + %result = ashr i8 %value, 8 + ret i8 %result +} + +;; Tests for logical left shifts. +define i8 @shl1(i8 %value) { +; CHECK-LABEL: shl1: +; CHECK: add.b r12, r12 + %result = shl i8 %value, 1 + ret i8 %result +} + +define i8 @shl2(i8 %value) { +; CHECK-LABEL: shl2: +; CHECK: rpt #2 { rlax.b r12 + %result = shl i8 %value, 2 + ret i8 %result +} + +define i8 @shl3(i8 %value) { +; CHECK-LABEL: shl3: +; CHECK: rpt #3 { rlax.b r12 + %result = shl i8 %value, 3 + ret i8 %result +} + +define i8 @shl4(i8 %value) { +; CHECK-LABEL: shl4: +; CHECK: rpt #4 { rlax.b r12 + %result = shl i8 %value, 4 + ret i8 %result +} + +define i8 @shl5(i8 %value) { +; CHECK-LABEL: shl5: +; CHECK: rpt #5 { rlax.b r12 + %result = shl i8 %value, 5 + ret i8 %result +} + +define i8 @shl6(i8 %value) { +; CHECK-LABEL: shl6: +; CHECK: rpt #6 { rlax.b r12 + %result = shl i8 %value, 6 + ret i8 %result +} + +define i8 @shl7(i8 %value) { +; CHECK-LABEL: shl7: +; CHECK: rpt #7 { rlax.b r12 + %result = shl i8 %value, 7 + ret i8 %result +} + +define i8 @shl8(i8 %value) { +; CHECK-LABEL: shl8: +; CHECK-NOT: rla + %result = shl i8 %value, 8 + ret i8 %result +} diff --git a/llvm/test/CodeGen/MSP430/shifts-430x-word.ll b/llvm/test/CodeGen/MSP430/shifts-430x-word.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/MSP430/shifts-430x-word.ll @@ -0,0 +1,766 @@ +; Tests for constant shifts of 16-bit register operands, for the 430X CPU. +; +; RUN: llc -mcpu=msp430x < %s | FileCheck %s +target datalayout = "e-p:16:8:8-i8:8:8-i16:8:8-i32:8:8-n8:16" +target triple = "msp430-elf" + +;; Tests for logical right shifts. +define i16 @lshr1(i16 %value) { +; CHECK-LABEL: lshr1: +; CHECK: rrum #1, r12 + %result = lshr i16 %value, 1 + ret i16 %result +} + +define i16 @lshr2(i16 %value) { +; CHECK-LABEL: lshr2: +; CHECK: rrum #2, r12 + %result = lshr i16 %value, 2 + ret i16 %result +} + +define i16 @lshr3(i16 %value) { +; CHECK-LABEL: lshr3: +; CHECK: rrum #3, r12 + %result = lshr i16 %value, 3 + ret i16 %result +} + +define i16 @lshr4(i16 %value) { +; CHECK-LABEL: lshr4: +; CHECK: rrum #4, r12 + %result = lshr i16 %value, 4 + ret i16 %result +} + +define i16 @lshr5(i16 %value) { +; CHECK-LABEL: lshr5: +; CHECK: rrum #4, r12 +; CHECK-NEXT: rrum #1, r12 + %result = lshr i16 %value, 5 + ret i16 %result +} + +define i16 @lshr6(i16 %value) { +; CHECK-LABEL: lshr6: +; CHECK: rrum #4, r12 +; CHECK-NEXT: rrum #2, r12 + %result = lshr i16 %value, 6 + ret i16 %result +} + +define i16 @lshr7(i16 %value) { +; CHECK-LABEL: lshr7: +; CHECK: rrum #4, r12 +; CHECK-NEXT: rrum #3, r12 + %result = lshr i16 %value, 7 + ret i16 %result +} + +define i16 @lshr8(i16 %value) { +; CHECK-LABEL: lshr8: +; CHECK: swpb r12 +; CHECK-NEXT: mov.b r12, r12 + %result = lshr i16 %value, 8 + ret i16 %result +} + +define i16 @lshr9(i16 %value) { +; CHECK-LABEL: lshr9: +; CHECK: swpb r12 +; CHECK-NEXT: mov.b r12, r12 +; CHECK-NEXT: rrum #1, r12 + %result = lshr i16 %value, 9 + ret i16 %result +} + +define i16 @lshr10(i16 %value) { +; CHECK-LABEL: lshr10: +; CHECK: swpb r12 +; CHECK-NEXT: mov.b r12, r12 +; CHECK-NEXT: rrum #2, r12 + %result = lshr i16 %value, 10 + ret i16 %result +} + +define i16 @lshr11(i16 %value) { +; CHECK-LABEL: lshr11: +; CHECK: swpb r12 +; CHECK-NEXT: mov.b r12, r12 +; CHECK-NEXT: rrum #3, r12 + %result = lshr i16 %value, 11 + ret i16 %result +} + +define i16 @lshr12(i16 %value) { +; CHECK-LABEL: lshr12: +; CHECK: swpb r12 +; CHECK-NEXT: mov.b r12, r12 +; CHECK-NEXT: rrum #4, r12 + %result = lshr i16 %value, 12 + ret i16 %result +} + +define i16 @lshr13(i16 %value) { +; CHECK-LABEL: lshr13: +; CHECK: swpb r12 +; CHECK-NEXT: mov.b r12, r12 +; CHECK-NEXT: rrum #4, r12 +; CHECK-NEXT: rrum #1, r12 + %result = lshr i16 %value, 13 + ret i16 %result +} + +define i16 @lshr14(i16 %value) { +; CHECK-LABEL: lshr14: +; CHECK: swpb r12 +; CHECK-NEXT: mov.b r12, r12 +; CHECK-NEXT: rrum #4, r12 +; CHECK-NEXT: rrum #2, r12 + %result = lshr i16 %value, 14 + ret i16 %result +} + +define i16 @lshr15(i16 %value) { +; CHECK-LABEL: lshr15: +; CHECK: swpb r12 +; CHECK-NEXT: mov.b r12, r12 +; CHECK-NEXT: rrum #4, r12 +; CHECK-NEXT: rrum #3, r12 + %result = lshr i16 %value, 15 + ret i16 %result +} + +define i16 @lshr16(i16 %value) { +; CHECK-LABEL: lshr16: +; CHECK-NOT: rrum +; CHECK-NOT: rrux + %result = lshr i16 %value, 16 + ret i16 %result +} + +;; Tests for arithmetic right shifts. +define i16 @ashr1(i16 %value) { +; CHECK-LABEL: ashr1: +; CHECK: rra r12 + %result = ashr i16 %value, 1 + ret i16 %result +} + +define i16 @ashr2(i16 %value) { +; CHECK-LABEL: ashr2: +; CHECK: rram #2, r12 + %result = ashr i16 %value, 2 + ret i16 %result +} + +define i16 @ashr3(i16 %value) { +; CHECK-LABEL: ashr3: +; CHECK: rram #3, r12 + %result = ashr i16 %value, 3 + ret i16 %result +} + +define i16 @ashr4(i16 %value) { +; CHECK-LABEL: ashr4: +; CHECK: rram #4, r12 + %result = ashr i16 %value, 4 + ret i16 %result +} + +define i16 @ashr5(i16 %value) { +; CHECK-LABEL: ashr5: +; CHECK: rram #4, r12 +; CHECK-NEXT: rram #1, r12 + %result = ashr i16 %value, 5 + ret i16 %result +} + +define i16 @ashr6(i16 %value) { +; CHECK-LABEL: ashr6: +; CHECK: rram #4, r12 +; CHECK-NEXT: rram #2, r12 + %result = ashr i16 %value, 6 + ret i16 %result +} + +define i16 @ashr7(i16 %value) { +; CHECK-LABEL: ashr7: +; CHECK: rram #4, r12 +; CHECK-NEXT: rram #3, r12 + %result = ashr i16 %value, 7 + ret i16 %result +} + +define i16 @ashr8(i16 %value) { +; CHECK-LABEL: ashr8: +; CHECK: swpb r12 +; CHECK-NEXT: sxt r12 + %result = ashr i16 %value, 8 + ret i16 %result +} + +define i16 @ashr9(i16 %value) { +; CHECK-LABEL: ashr9: +; CHECK: swpb r12 +; CHECK-NEXT: sxt r12 +; CHECK-NEXT: rram #1, r12 + %result = ashr i16 %value, 9 + ret i16 %result +} + +define i16 @ashr10(i16 %value) { +; CHECK-LABEL: ashr10: +; CHECK: swpb r12 +; CHECK-NEXT: sxt r12 +; CHECK-NEXT: rram #2, r12 + %result = ashr i16 %value, 10 + ret i16 %result +} + +define i16 @ashr11(i16 %value) { +; CHECK-LABEL: ashr11: +; CHECK: swpb r12 +; CHECK-NEXT: sxt r12 +; CHECK-NEXT: rram #3, r12 + %result = ashr i16 %value, 11 + ret i16 %result +} + +define i16 @ashr12(i16 %value) { +; CHECK-LABEL: ashr12: +; CHECK: swpb r12 +; CHECK-NEXT: sxt r12 +; CHECK-NEXT: rram #4, r12 + %result = ashr i16 %value, 12 + ret i16 %result +} + +define i16 @ashr13(i16 %value) { +; CHECK-LABEL: ashr13: +; CHECK: swpb r12 +; CHECK-NEXT: sxt r12 +; CHECK-NEXT: rram #4, r12 +; CHECK-NEXT: rram #1, r12 + %result = ashr i16 %value, 13 + ret i16 %result +} + +define i16 @ashr14(i16 %value) { +; CHECK-LABEL: ashr14: +; CHECK: swpb r12 +; CHECK-NEXT: sxt r12 +; CHECK-NEXT: rram #4, r12 +; CHECK-NEXT: rram #2, r12 + %result = ashr i16 %value, 14 + ret i16 %result +} + +define i16 @ashr15(i16 %value) { +; CHECK-LABEL: ashr15: +; CHECK: swpb r12 +; CHECK-NEXT: sxt r12 +; CHECK-NEXT: rram #4, r12 +; CHECK-NEXT: rram #3, r12 + %result = ashr i16 %value, 15 + ret i16 %result +} + +define i16 @ashr16(i16 %value) { +; CHECK-LABEL: ashr16: +; CHECK-NOT: rram +; CHECK-NOT: rrax + %result = ashr i16 %value, 16 + ret i16 %result +} + +;; Tests for logical left shifts. +define i16 @shl1(i16 %value) { +; CHECK-LABEL: shl1: +; CHECK: add r12, r12 + %result = shl i16 %value, 1 + ret i16 %result +} + +define i16 @shl2(i16 %value) { +; CHECK-LABEL: shl2: +; CHECK: rlam #2, r12 + %result = shl i16 %value, 2 + ret i16 %result +} + +define i16 @shl3(i16 %value) { +; CHECK-LABEL: shl3: +; CHECK: rlam #3, r12 + %result = shl i16 %value, 3 + ret i16 %result +} + +define i16 @shl4(i16 %value) { +; CHECK-LABEL: shl4: +; CHECK: rlam #4, r12 + %result = shl i16 %value, 4 + ret i16 %result +} + +define i16 @shl5(i16 %value) { +; CHECK-LABEL: shl5: +; CHECK: rlam #4, r12 +; CHECK-NEXT: rlam #1, r12 + %result = shl i16 %value, 5 + ret i16 %result +} + +define i16 @shl6(i16 %value) { +; CHECK-LABEL: shl6: +; CHECK: rlam #4, r12 +; CHECK-NEXT: rlam #2, r12 + %result = shl i16 %value, 6 + ret i16 %result +} + +define i16 @shl7(i16 %value) { +; CHECK-LABEL: shl7: +; CHECK: rlam #4, r12 +; CHECK-NEXT: rlam #3, r12 + %result = shl i16 %value, 7 + ret i16 %result +} + +define i16 @shl8(i16 %value) { +; CHECK-LABEL: shl8: +; CHECK: mov.b r12, r12 +; CHECK-NEXT: swpb r12 + %result = shl i16 %value, 8 + ret i16 %result +} + +define i16 @shl9(i16 %value) { +; CHECK-LABEL: shl9: +; CHECK: mov.b r12, r12 +; CHECK-NEXT: swpb r12 +; CHECK-NEXT: rlam #1, r12 + %result = shl i16 %value, 9 + ret i16 %result +} + +define i16 @shl10(i16 %value) { +; CHECK-LABEL: shl10: +; CHECK: mov.b r12, r12 +; CHECK-NEXT: swpb r12 +; CHECK-NEXT: rlam #2, r12 + %result = shl i16 %value, 10 + ret i16 %result +} + +define i16 @shl11(i16 %value) { +; CHECK-LABEL: shl11: +; CHECK: mov.b r12, r12 +; CHECK-NEXT: swpb r12 +; CHECK-NEXT: rlam #3, r12 + %result = shl i16 %value, 11 + ret i16 %result +} + +define i16 @shl12(i16 %value) { +; CHECK-LABEL: shl12: +; CHECK: mov.b r12, r12 +; CHECK-NEXT: swpb r12 +; CHECK-NEXT: rlam #4, r12 + %result = shl i16 %value, 12 + ret i16 %result +} + +define i16 @shl13(i16 %value) { +; CHECK-LABEL: shl13: +; CHECK: mov.b r12, r12 +; CHECK-NEXT: swpb r12 +; CHECK-NEXT: rlam #4, r12 +; CHECK-NEXT: rlam #1, r12 + %result = shl i16 %value, 13 + ret i16 %result +} + +define i16 @shl14(i16 %value) { +; CHECK-LABEL: shl14: +; CHECK: mov.b r12, r12 +; CHECK-NEXT: swpb r12 +; CHECK-NEXT: rlam #4, r12 +; CHECK-NEXT: rlam #2, r12 + %result = shl i16 %value, 14 + ret i16 %result +} + +define i16 @shl15(i16 %value) { +; CHECK-LABEL: shl15: +; CHECK: mov.b r12, r12 +; CHECK-NEXT: swpb r12 +; CHECK-NEXT: rlam #4, r12 +; CHECK-NEXT: rlam #3, r12 + %result = shl i16 %value, 15 + ret i16 %result +} + +define i16 @shl16(i16 %value) { +; CHECK-LABEL: shl16: +; CHECK-NOT: rlam +; CHECK-NOT: rlax + %result = shl i16 %value, 16 + ret i16 %result +} + +;; Begin tests for shifts, when optimizing for size. +;;-------------------------------------------------- +;; Tests for logical right shifts, when optimizing for size. +define i16 @lshr1_s(i16 %value) optsize { +; CHECK-LABEL: lshr1_s: +; CHECK: rrum #1, r12 + %result = lshr i16 %value, 1 + ret i16 %result +} + +define i16 @lshr2_s(i16 %value) optsize { +; CHECK-LABEL: lshr2_s: +; CHECK: rrum #2, r12 + %result = lshr i16 %value, 2 + ret i16 %result +} + +define i16 @lshr3_s(i16 %value) optsize { +; CHECK-LABEL: lshr3_s: +; CHECK: rrum #3, r12 + %result = lshr i16 %value, 3 + ret i16 %result +} + +define i16 @lshr4_s(i16 %value) optsize { +; CHECK-LABEL: lshr4_s: +; CHECK: rrum #4, r12 + %result = lshr i16 %value, 4 + ret i16 %result +} + +define i16 @lshr5_s(i16 %value) optsize { +; CHECK-LABEL: lshr5_s: +; CHECK: rrum #4, r12 +; CHECK-NEXT: rrum #1, r12 + %result = lshr i16 %value, 5 + ret i16 %result +} + +define i16 @lshr6_s(i16 %value) optsize { +; CHECK-LABEL: lshr6_s: +; CHECK: rrum #4, r12 +; CHECK-NEXT: rrum #2, r12 + %result = lshr i16 %value, 6 + ret i16 %result +} + +define i16 @lshr7_s(i16 %value) optsize { +; CHECK-LABEL: lshr7_s: +; CHECK: rrum #4, r12 +; CHECK-NEXT: rrum #3, r12 + %result = lshr i16 %value, 7 + ret i16 %result +} + +define i16 @lshr8_s(i16 %value) optsize { +; CHECK-LABEL: lshr8_s: +; CHECK: swpb r12 +; CHECK-NEXT: mov.b r12, r12 + %result = lshr i16 %value, 8 + ret i16 %result +} + +define i16 @lshr9_s(i16 %value) optsize { +; CHECK-LABEL: lshr9_s: +; CHECK: rpt #9 { rrux r12 + %result = lshr i16 %value, 9 + ret i16 %result +} + +define i16 @lshr10_s(i16 %value) optsize { +; CHECK-LABEL: lshr10_s: +; CHECK: rpt #10 { rrux r12 + %result = lshr i16 %value, 10 + ret i16 %result +} + +define i16 @lshr11_s(i16 %value) optsize { +; CHECK-LABEL: lshr11_s: +; CHECK: rpt #11 { rrux r12 + %result = lshr i16 %value, 11 + ret i16 %result +} + +define i16 @lshr12_s(i16 %value) optsize { +; CHECK-LABEL: lshr12_s: +; CHECK: rpt #12 { rrux r12 + %result = lshr i16 %value, 12 + ret i16 %result +} + +define i16 @lshr13_s(i16 %value) optsize { +; CHECK-LABEL: lshr13_s: +; CHECK: rpt #13 { rrux r12 + %result = lshr i16 %value, 13 + ret i16 %result +} + +define i16 @lshr14_s(i16 %value) optsize { +; CHECK-LABEL: lshr14_s: +; CHECK: rpt #14 { rrux r12 + %result = lshr i16 %value, 14 + ret i16 %result +} + +define i16 @lshr15_s(i16 %value) optsize { +; CHECK-LABEL: lshr15_s: +; CHECK: rpt #15 { rrux r12 + %result = lshr i16 %value, 15 + ret i16 %result +} + +define i16 @lshr16_s(i16 %value) optsize { +; CHECK-LABEL: lshr16_s: +; CHECK-NOT: rrum +; CHECK-NOT: rrux + %result = lshr i16 %value, 16 + ret i16 %result +} + +;; Tests for arithmetic right shifts, when optimizing for size. +define i16 @ashr1_s(i16 %value) optsize { +; CHECK-LABEL: ashr1_s: +; CHECK: rra r12 + %result = ashr i16 %value, 1 + ret i16 %result +} + +define i16 @ashr2_s(i16 %value) optsize { +; CHECK-LABEL: ashr2_s: +; CHECK: rram #2, r12 + %result = ashr i16 %value, 2 + ret i16 %result +} + +define i16 @ashr3_s(i16 %value) optsize { +; CHECK-LABEL: ashr3_s: +; CHECK: rram #3, r12 + %result = ashr i16 %value, 3 + ret i16 %result +} + +define i16 @ashr4_s(i16 %value) optsize { +; CHECK-LABEL: ashr4_s: +; CHECK: rram #4, r12 + %result = ashr i16 %value, 4 + ret i16 %result +} + +define i16 @ashr5_s(i16 %value) optsize { +; CHECK-LABEL: ashr5_s: +; CHECK: rram #4, r12 +; CHECK-NEXT: rram #1, r12 + %result = ashr i16 %value, 5 + ret i16 %result +} + +define i16 @ashr6_s(i16 %value) optsize { +; CHECK-LABEL: ashr6_s: +; CHECK: rram #4, r12 +; CHECK-NEXT: rram #2, r12 + %result = ashr i16 %value, 6 + ret i16 %result +} + +define i16 @ashr7_s(i16 %value) optsize { +; CHECK-LABEL: ashr7_s: +; CHECK: rram #4, r12 +; CHECK-NEXT: rram #3, r12 + %result = ashr i16 %value, 7 + ret i16 %result +} + +define i16 @ashr8_s(i16 %value) optsize { +; CHECK-LABEL: ashr8_s: +; CHECK: swpb r12 +; CHECK-NEXT: sxt r12 + %result = ashr i16 %value, 8 + ret i16 %result +} + +define i16 @ashr9_s(i16 %value) optsize { +; CHECK-LABEL: ashr9_s: +; CHECK: rpt #9 { rrax r12 + %result = ashr i16 %value, 9 + ret i16 %result +} + +define i16 @ashr10_s(i16 %value) optsize { +; CHECK-LABEL: ashr10_s: +; CHECK: rpt #10 { rrax r12 + %result = ashr i16 %value, 10 + ret i16 %result +} + +define i16 @ashr11_s(i16 %value) optsize { +; CHECK-LABEL: ashr11_s: +; CHECK: rpt #11 { rrax r12 + %result = ashr i16 %value, 11 + ret i16 %result +} + +define i16 @ashr12_s(i16 %value) optsize { +; CHECK-LABEL: ashr12_s: +; CHECK: rpt #12 { rrax r12 + %result = ashr i16 %value, 12 + ret i16 %result +} + +define i16 @ashr13_s(i16 %value) optsize { +; CHECK-LABEL: ashr13_s: +; CHECK: rpt #13 { rrax r12 + %result = ashr i16 %value, 13 + ret i16 %result +} + +define i16 @ashr14_s(i16 %value) optsize { +; CHECK-LABEL: ashr14_s: +; CHECK: rpt #14 { rrax r12 + %result = ashr i16 %value, 14 + ret i16 %result +} + +define i16 @ashr15_s(i16 %value) optsize { +; CHECK-LABEL: ashr15_s: +; CHECK: rpt #15 { rrax r12 + %result = ashr i16 %value, 15 + ret i16 %result +} + +define i16 @ashr16_s(i16 %value) optsize { +; CHECK-LABEL: ashr16_s: +; CHECK-NOT: rram +; CHECK-NOT: rrax + %result = ashr i16 %value, 16 + ret i16 %result +} + +;; Tests for logical left shifts, when optimizing for size. +define i16 @shl1_s(i16 %value) optsize { +; CHECK-LABEL: shl1_s: +; CHECK: add r12, r12 + %result = shl i16 %value, 1 + ret i16 %result +} + +define i16 @shl2_s(i16 %value) optsize { +; CHECK-LABEL: shl2_s: +; CHECK: rlam #2, r12 + %result = shl i16 %value, 2 + ret i16 %result +} + +define i16 @shl3_s(i16 %value) optsize { +; CHECK-LABEL: shl3_s: +; CHECK: rlam #3, r12 + %result = shl i16 %value, 3 + ret i16 %result +} + +define i16 @shl4_s(i16 %value) optsize { +; CHECK-LABEL: shl4_s: +; CHECK: rlam #4, r12 + %result = shl i16 %value, 4 + ret i16 %result +} + +define i16 @shl5_s(i16 %value) optsize { +; CHECK-LABEL: shl5_s: +; CHECK: rlam #4, r12 +; CHECK-NEXT: rlam #1, r12 + %result = shl i16 %value, 5 + ret i16 %result +} + +define i16 @shl6_s(i16 %value) optsize { +; CHECK-LABEL: shl6_s: +; CHECK: rlam #4, r12 +; CHECK-NEXT: rlam #2, r12 + %result = shl i16 %value, 6 + ret i16 %result +} + +define i16 @shl7_s(i16 %value) optsize { +; CHECK-LABEL: shl7_s: +; CHECK: rlam #4, r12 +; CHECK-NEXT: rlam #3, r12 + %result = shl i16 %value, 7 + ret i16 %result +} + +define i16 @shl8_s(i16 %value) optsize { +; CHECK-LABEL: shl8_s: +; CHECK: mov.b r12, r12 +; CHECK-NEXT: swpb r12 + %result = shl i16 %value, 8 + ret i16 %result +} + +define i16 @shl9_s(i16 %value) optsize { +; CHECK-LABEL: shl9_s: +; CHECK: rpt #9 { rlax r12 + %result = shl i16 %value, 9 + ret i16 %result +} + +define i16 @shl10_s(i16 %value) optsize { +; CHECK-LABEL: shl10_s: +; CHECK: rpt #10 { rlax r12 + %result = shl i16 %value, 10 + ret i16 %result +} + +define i16 @shl11_s(i16 %value) optsize { +; CHECK-LABEL: shl11_s: +; CHECK: rpt #11 { rlax r12 + %result = shl i16 %value, 11 + ret i16 %result +} + +define i16 @shl12_s(i16 %value) optsize { +; CHECK-LABEL: shl12_s: +; CHECK: rpt #12 { rlax r12 + %result = shl i16 %value, 12 + ret i16 %result +} + +define i16 @shl13_s(i16 %value) optsize { +; CHECK-LABEL: shl13_s: +; CHECK: rpt #13 { rlax r12 + %result = shl i16 %value, 13 + ret i16 %result +} + +define i16 @shl14_s(i16 %value) optsize { +; CHECK-LABEL: shl14_s: +; CHECK: rpt #14 { rlax r12 + %result = shl i16 %value, 14 + ret i16 %result +} + +define i16 @shl15_s(i16 %value) optsize { +; CHECK-LABEL: shl15_s: +; CHECK: rpt #15 { rlax r12 + %result = shl i16 %value, 15 + ret i16 %result +} + +define i16 @shl16_s(i16 %value) optsize { +; CHECK-LABEL: shl16_s: +; CHECK-NOT: rlam +; CHECK-NOT: rlax + %result = shl i16 %value, 16 + ret i16 %result +} diff --git a/llvm/test/CodeGen/MSP430/shifts.ll b/llvm/test/CodeGen/MSP430/shifts.ll --- a/llvm/test/CodeGen/MSP430/shifts.ll +++ b/llvm/test/CodeGen/MSP430/shifts.ll @@ -68,8 +68,7 @@ ; CHECK-LABEL: lshr10_i16: ; CHECK: swpb r12 ; CHECK-NEXT: mov.b r12, r12 -; CHECK-NEXT: clrc -; CHECK-NEXT: rrc r12 +; CHECK-NEXT: rra r12 ; CHECK-NEXT: rra r12 %shr = lshr i16 %a, 10 ret i16 %shr diff --git a/llvm/test/MC/MSP430/shifts-430x-invalid.s b/llvm/test/MC/MSP430/shifts-430x-invalid.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/MSP430/shifts-430x-invalid.s @@ -0,0 +1,63 @@ +// RUN: not llvm-mc --triple=msp430 -mcpu=msp430x < %s 2>&1 | FileCheck %s + +foo: +// Check for an error when the reptition count is too large. + rrum #5, r12 + rram #5, r12 + rlam #5, r12 +// CHECK: :8: error: invalid operand for instruction +// CHECK: :8: error: invalid operand for instruction +// CHECK: :8: error: invalid operand for instruction + +// Check for an error when the reptition count is too large. + rpt #17 { rrux r12 + rpt #17 { rrax r12 + rpt #17 { rlax r12 +// CHECK: :7: error: invalid repetition count +// CHECK: :7: error: invalid repetition count +// CHECK: :7: error: invalid repetition count + +// Check for an error when RxxX shifts have a shift amount operand. + rrux #3, r12 + rrax #3, r12 + rlax #3, r12 +// CHECK: :8: error: invalid operand for instruction +// CHECK: :8: error: invalid operand for instruction +// CHECK: :8: error: invalid operand for instruction + +// Check for an error when there is no separator between rpt and the extended +// instruction. + rpt #3 rrux r12 + rpt #3 rrax r12 + rpt #3 rlax r12 +// CHECK: :10: error: expected '{' or a newline after "rpt #" +// CHECK: :10: error: expected '{' or a newline after "rpt #" +// CHECK: :10: error: expected '{' or a newline after "rpt #" + +// Check that rpt cannot be used with RxxM shifts. + rpt #3 { rrum #3, r12 + rpt #3 { rram #3, r12 + rpt #3 { rlam #3, r12 +// CHECK: :19: error: only one operand expected +// CHECK: :19: error: only one operand expected +// CHECK: :19: error: only one operand expected + +// Check that rpt cannot be used with 430 instructions. + rpt #3 { rrc r12 + rpt #3 { rra r12 + rpt #3 { rla r12 +// CHECK: :7: error: invalid operand for instruction +// CHECK: :7: error: invalid operand for instruction +// CHECK: :7: error: invalid operand for instruction + +// Check that the operand to rpt must be an immediate. + rpt foo { rrux r12 + rpt r13 { rrax r12 + rpt @r13 { rrax r12 + rpt &bar { rrax r12 + rpt 2(r14) { rlax r12 +// CHECK: :7: error: expected immediate operand +// CHECK: :7: error: expected immediate operand +// CHECK: :7: error: expected immediate operand +// CHECK: :7: error: expected immediate operand +// CHECK: :7: error: expected immediate operand diff --git a/llvm/test/MC/MSP430/shifts-430x.s b/llvm/test/MC/MSP430/shifts-430x.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/MSP430/shifts-430x.s @@ -0,0 +1,418 @@ +// Test the encoding of shift instructions for the 430X CPU. + +// RUN: llvm-mc -triple msp430 -mcpu=msp430x -show-encoding < %s | FileCheck %s +rxxm: + rrum #1, r12 ; encoding: [0x5c,0x03] + rrum #2, r12 ; encoding: [0x5c,0x07] + rrum #3, r12 ; encoding: [0x5c,0x0b] + rrum #4, r12 ; encoding: [0x5c,0x0f] + + rram #1, r12 ; encoding: [0x5c,0x01] + rram #2, r12 ; encoding: [0x5c,0x05] + rram #3, r12 ; encoding: [0x5c,0x09] + rram #4, r12 ; encoding: [0x5c,0x0d] + + rlam #1, r12 ; encoding: [0x5c,0x02] + rlam #2, r12 ; encoding: [0x5c,0x06] + rlam #3, r12 ; encoding: [0x5c,0x0a] + rlam #4, r12 ; encoding: [0x5c,0x0e] + +// CHECK-LABEL: rxxm: +// CHECK-NEXT: rrum #1, r12 ; encoding: [0x5c,0x03] +// CHECK: rrum #2, r12 ; encoding: [0x5c,0x07] +// CHECK: rrum #3, r12 ; encoding: [0x5c,0x0b] +// CHECK: rrum #4, r12 ; encoding: [0x5c,0x0f] + +// CHECK: rram #1, r12 ; encoding: [0x5c,0x01] +// CHECK: rram #2, r12 ; encoding: [0x5c,0x05] +// CHECK: rram #3, r12 ; encoding: [0x5c,0x09] +// CHECK: rram #4, r12 ; encoding: [0x5c,0x0d] + +// CHECK: rlam #1, r12 ; encoding: [0x5c,0x02] +// CHECK: rlam #2, r12 ; encoding: [0x5c,0x06] +// CHECK: rlam #3, r12 ; encoding: [0x5c,0x0a] +// CHECK: rlam #4, r12 ; encoding: [0x5c,0x0e] + +rrux: + rrux r12 + rpt #1 { rrux r12 + rpt #2 { rrux r12 + rpt #3 { rrux r12 + rpt #4 { rrux r12 + rpt #5 { rrux r12 + rpt #6 { rrux r12 + rpt #7 { rrux r12 + rpt #8 { rrux r12 + rpt #9 { rrux r12 + rpt #10 { rrux r12 + rpt #11 { rrux r12 + rpt #12 { rrux r12 + rpt #13 { rrux r12 + rpt #14 { rrux r12 + rpt #15 { rrux r12 + rpt #16 { rrux r12 + +// CHECK-LABEL: rrux: +// CHECK-NEXT: rrux r12 ; encoding: [0x40,0x19,0x0c,0x10] +// CHECK: rrux r12 ; encoding: [0x40,0x19,0x0c,0x10] +// CHECK: rpt #2 { rrux r12 ; encoding: [0x41,0x19,0x0c,0x10] +// CHECK: rpt #3 { rrux r12 ; encoding: [0x42,0x19,0x0c,0x10] +// CHECK: rpt #4 { rrux r12 ; encoding: [0x43,0x19,0x0c,0x10] +// CHECK: rpt #5 { rrux r12 ; encoding: [0x44,0x19,0x0c,0x10] +// CHECK: rpt #6 { rrux r12 ; encoding: [0x45,0x19,0x0c,0x10] +// CHECK: rpt #7 { rrux r12 ; encoding: [0x46,0x19,0x0c,0x10] +// CHECK: rpt #8 { rrux r12 ; encoding: [0x47,0x19,0x0c,0x10] +// CHECK: rpt #9 { rrux r12 ; encoding: [0x48,0x19,0x0c,0x10] +// CHECK: rpt #10 { rrux r12 ; encoding: [0x49,0x19,0x0c,0x10] +// CHECK: rpt #11 { rrux r12 ; encoding: [0x4a,0x19,0x0c,0x10] +// CHECK: rpt #12 { rrux r12 ; encoding: [0x4b,0x19,0x0c,0x10] +// CHECK: rpt #13 { rrux r12 ; encoding: [0x4c,0x19,0x0c,0x10] +// CHECK: rpt #14 { rrux r12 ; encoding: [0x4d,0x19,0x0c,0x10] +// CHECK: rpt #15 { rrux r12 ; encoding: [0x4e,0x19,0x0c,0x10] +// CHECK: rpt #16 { rrux r12 ; encoding: [0x4f,0x19,0x0c,0x10] + +rrux_b: + rrux.b r12 + rpt #1 { rrux.b r12 + rpt #2 { rrux.b r12 + rpt #3 { rrux.b r12 + rpt #4 { rrux.b r12 + rpt #5 { rrux.b r12 + rpt #6 { rrux.b r12 + rpt #7 { rrux.b r12 + rpt #8 { rrux.b r12 + rpt #9 { rrux.b r12 + rpt #10 { rrux.b r12 + rpt #11 { rrux.b r12 + rpt #12 { rrux.b r12 + rpt #13 { rrux.b r12 + rpt #14 { rrux.b r12 + rpt #15 { rrux.b r12 + rpt #16 { rrux.b r12 + +// CHECK-LABEL: rrux_b: +// CHECK-NEXT: rrux.b r12 ; encoding: [0x40,0x19,0x4c,0x10] +// CHECK: rrux.b r12 ; encoding: [0x40,0x19,0x4c,0x10] +// CHECK: rpt #2 { rrux.b r12 ; encoding: [0x41,0x19,0x4c,0x10] +// CHECK: rpt #3 { rrux.b r12 ; encoding: [0x42,0x19,0x4c,0x10] +// CHECK: rpt #4 { rrux.b r12 ; encoding: [0x43,0x19,0x4c,0x10] +// CHECK: rpt #5 { rrux.b r12 ; encoding: [0x44,0x19,0x4c,0x10] +// CHECK: rpt #6 { rrux.b r12 ; encoding: [0x45,0x19,0x4c,0x10] +// CHECK: rpt #7 { rrux.b r12 ; encoding: [0x46,0x19,0x4c,0x10] +// CHECK: rpt #8 { rrux.b r12 ; encoding: [0x47,0x19,0x4c,0x10] +// CHECK: rpt #9 { rrux.b r12 ; encoding: [0x48,0x19,0x4c,0x10] +// CHECK: rpt #10 { rrux.b r12 ; encoding: [0x49,0x19,0x4c,0x10] +// CHECK: rpt #11 { rrux.b r12 ; encoding: [0x4a,0x19,0x4c,0x10] +// CHECK: rpt #12 { rrux.b r12 ; encoding: [0x4b,0x19,0x4c,0x10] +// CHECK: rpt #13 { rrux.b r12 ; encoding: [0x4c,0x19,0x4c,0x10] +// CHECK: rpt #14 { rrux.b r12 ; encoding: [0x4d,0x19,0x4c,0x10] +// CHECK: rpt #15 { rrux.b r12 ; encoding: [0x4e,0x19,0x4c,0x10] +// CHECK: rpt #16 { rrux.b r12 ; encoding: [0x4f,0x19,0x4c,0x10] + +rrax: + rrax r12 + rpt #1 { rrax r12 + rpt #2 { rrax r12 + rpt #3 { rrax r12 + rpt #4 { rrax r12 + rpt #5 { rrax r12 + rpt #6 { rrax r12 + rpt #7 { rrax r12 + rpt #8 { rrax r12 + rpt #9 { rrax r12 + rpt #10 { rrax r12 + rpt #11 { rrax r12 + rpt #12 { rrax r12 + rpt #13 { rrax r12 + rpt #14 { rrax r12 + rpt #15 { rrax r12 + rpt #16 { rrax r12 + +// CHECK-LABEL: rrax: +// CHECK-NEXT: rrax r12 ; encoding: [0x40,0x18,0x0c,0x11] +// CHECK: rrax r12 ; encoding: [0x40,0x18,0x0c,0x11] +// CHECK: rpt #2 { rrax r12 ; encoding: [0x41,0x18,0x0c,0x11] +// CHECK: rpt #3 { rrax r12 ; encoding: [0x42,0x18,0x0c,0x11] +// CHECK: rpt #4 { rrax r12 ; encoding: [0x43,0x18,0x0c,0x11] +// CHECK: rpt #5 { rrax r12 ; encoding: [0x44,0x18,0x0c,0x11] +// CHECK: rpt #6 { rrax r12 ; encoding: [0x45,0x18,0x0c,0x11] +// CHECK: rpt #7 { rrax r12 ; encoding: [0x46,0x18,0x0c,0x11] +// CHECK: rpt #8 { rrax r12 ; encoding: [0x47,0x18,0x0c,0x11] +// CHECK: rpt #9 { rrax r12 ; encoding: [0x48,0x18,0x0c,0x11] +// CHECK: rpt #10 { rrax r12 ; encoding: [0x49,0x18,0x0c,0x11] +// CHECK: rpt #11 { rrax r12 ; encoding: [0x4a,0x18,0x0c,0x11] +// CHECK: rpt #12 { rrax r12 ; encoding: [0x4b,0x18,0x0c,0x11] +// CHECK: rpt #13 { rrax r12 ; encoding: [0x4c,0x18,0x0c,0x11] +// CHECK: rpt #14 { rrax r12 ; encoding: [0x4d,0x18,0x0c,0x11] +// CHECK: rpt #15 { rrax r12 ; encoding: [0x4e,0x18,0x0c,0x11] +// CHECK: rpt #16 { rrax r12 ; encoding: [0x4f,0x18,0x0c,0x11] + +rrax_b: + rrax.b r12 + rpt #1 { rrax.b r12 + rpt #2 { rrax.b r12 + rpt #3 { rrax.b r12 + rpt #4 { rrax.b r12 + rpt #5 { rrax.b r12 + rpt #6 { rrax.b r12 + rpt #7 { rrax.b r12 + rpt #8 { rrax.b r12 + rpt #9 { rrax.b r12 + rpt #10 { rrax.b r12 + rpt #11 { rrax.b r12 + rpt #12 { rrax.b r12 + rpt #13 { rrax.b r12 + rpt #14 { rrax.b r12 + rpt #15 { rrax.b r12 + rpt #16 { rrax.b r12 + +// CHECK-LABEL: rrax_b: +// CHECK-NEXT: rrax.b r12 ; encoding: [0x40,0x18,0x4c,0x11] +// CHECK: rrax.b r12 ; encoding: [0x40,0x18,0x4c,0x11] +// CHECK: rpt #2 { rrax.b r12 ; encoding: [0x41,0x18,0x4c,0x11] +// CHECK: rpt #3 { rrax.b r12 ; encoding: [0x42,0x18,0x4c,0x11] +// CHECK: rpt #4 { rrax.b r12 ; encoding: [0x43,0x18,0x4c,0x11] +// CHECK: rpt #5 { rrax.b r12 ; encoding: [0x44,0x18,0x4c,0x11] +// CHECK: rpt #6 { rrax.b r12 ; encoding: [0x45,0x18,0x4c,0x11] +// CHECK: rpt #7 { rrax.b r12 ; encoding: [0x46,0x18,0x4c,0x11] +// CHECK: rpt #8 { rrax.b r12 ; encoding: [0x47,0x18,0x4c,0x11] +// CHECK: rpt #9 { rrax.b r12 ; encoding: [0x48,0x18,0x4c,0x11] +// CHECK: rpt #10 { rrax.b r12 ; encoding: [0x49,0x18,0x4c,0x11] +// CHECK: rpt #11 { rrax.b r12 ; encoding: [0x4a,0x18,0x4c,0x11] +// CHECK: rpt #12 { rrax.b r12 ; encoding: [0x4b,0x18,0x4c,0x11] +// CHECK: rpt #13 { rrax.b r12 ; encoding: [0x4c,0x18,0x4c,0x11] +// CHECK: rpt #14 { rrax.b r12 ; encoding: [0x4d,0x18,0x4c,0x11] +// CHECK: rpt #15 { rrax.b r12 ; encoding: [0x4e,0x18,0x4c,0x11] +// CHECK: rpt #16 { rrax.b r12 ; encoding: [0x4f,0x18,0x4c,0x11] + +rlax: + rlax r12 + rpt #1 { rlax r12 + rpt #2 { rlax r12 + rpt #3 { rlax r12 + rpt #4 { rlax r12 + rpt #5 { rlax r12 + rpt #6 { rlax r12 + rpt #7 { rlax r12 + rpt #8 { rlax r12 + rpt #9 { rlax r12 + rpt #10 { rlax r12 + rpt #11 { rlax r12 + rpt #12 { rlax r12 + rpt #13 { rlax r12 + rpt #14 { rlax r12 + rpt #15 { rlax r12 + rpt #16 { rlax r12 + +// CHECK: rlax r12 ; encoding: [0x40,0x18,0x0c,0x5c] +// CHECK: rlax r12 ; encoding: [0x40,0x18,0x0c,0x5c] +// CHECK: rpt #2 { rlax r12 ; encoding: [0x41,0x18,0x0c,0x5c] +// CHECK: rpt #3 { rlax r12 ; encoding: [0x42,0x18,0x0c,0x5c] +// CHECK: rpt #4 { rlax r12 ; encoding: [0x43,0x18,0x0c,0x5c] +// CHECK: rpt #5 { rlax r12 ; encoding: [0x44,0x18,0x0c,0x5c] +// CHECK: rpt #6 { rlax r12 ; encoding: [0x45,0x18,0x0c,0x5c] +// CHECK: rpt #7 { rlax r12 ; encoding: [0x46,0x18,0x0c,0x5c] +// CHECK: rpt #8 { rlax r12 ; encoding: [0x47,0x18,0x0c,0x5c] +// CHECK: rpt #9 { rlax r12 ; encoding: [0x48,0x18,0x0c,0x5c] +// CHECK: rpt #10 { rlax r12 ; encoding: [0x49,0x18,0x0c,0x5c] +// CHECK: rpt #11 { rlax r12 ; encoding: [0x4a,0x18,0x0c,0x5c] +// CHECK: rpt #12 { rlax r12 ; encoding: [0x4b,0x18,0x0c,0x5c] +// CHECK: rpt #13 { rlax r12 ; encoding: [0x4c,0x18,0x0c,0x5c] +// CHECK: rpt #14 { rlax r12 ; encoding: [0x4d,0x18,0x0c,0x5c] +// CHECK: rpt #15 { rlax r12 ; encoding: [0x4e,0x18,0x0c,0x5c] +// CHECK: rpt #16 { rlax r12 ; encoding: [0x4f,0x18,0x0c,0x5c] + +rlax_b: + rlax.b r12 + rpt #1 { rlax.b r12 + rpt #2 { rlax.b r12 + rpt #3 { rlax.b r12 + rpt #4 { rlax.b r12 + rpt #5 { rlax.b r12 + rpt #6 { rlax.b r12 + rpt #7 { rlax.b r12 + rpt #8 { rlax.b r12 + rpt #9 { rlax.b r12 + rpt #10 { rlax.b r12 + rpt #11 { rlax.b r12 + rpt #12 { rlax.b r12 + rpt #13 { rlax.b r12 + rpt #14 { rlax.b r12 + rpt #15 { rlax.b r12 + rpt #16 { rlax.b r12 + +// CHECK-LABEL: rlax_b: +// CHECK-NEXT: rlax.b r12 ; encoding: [0x40,0x18,0x4c,0x5c] +// CHECK: rlax.b r12 ; encoding: [0x40,0x18,0x4c,0x5c] +// CHECK: rpt #2 { rlax.b r12 ; encoding: [0x41,0x18,0x4c,0x5c] +// CHECK: rpt #3 { rlax.b r12 ; encoding: [0x42,0x18,0x4c,0x5c] +// CHECK: rpt #4 { rlax.b r12 ; encoding: [0x43,0x18,0x4c,0x5c] +// CHECK: rpt #5 { rlax.b r12 ; encoding: [0x44,0x18,0x4c,0x5c] +// CHECK: rpt #6 { rlax.b r12 ; encoding: [0x45,0x18,0x4c,0x5c] +// CHECK: rpt #7 { rlax.b r12 ; encoding: [0x46,0x18,0x4c,0x5c] +// CHECK: rpt #8 { rlax.b r12 ; encoding: [0x47,0x18,0x4c,0x5c] +// CHECK: rpt #9 { rlax.b r12 ; encoding: [0x48,0x18,0x4c,0x5c] +// CHECK: rpt #10 { rlax.b r12 ; encoding: [0x49,0x18,0x4c,0x5c] +// CHECK: rpt #11 { rlax.b r12 ; encoding: [0x4a,0x18,0x4c,0x5c] +// CHECK: rpt #12 { rlax.b r12 ; encoding: [0x4b,0x18,0x4c,0x5c] +// CHECK: rpt #13 { rlax.b r12 ; encoding: [0x4c,0x18,0x4c,0x5c] +// CHECK: rpt #14 { rlax.b r12 ; encoding: [0x4d,0x18,0x4c,0x5c] +// CHECK: rpt #15 { rlax.b r12 ; encoding: [0x4e,0x18,0x4c,0x5c] +// CHECK: rpt #16 { rlax.b r12 ; encoding: [0x4f,0x18,0x4c,0x5c] + +// Test parsing of "rpt" when it's on it's own line. +rrux2: + rpt #1 + rrux r12 + rpt #2 + rrux r12 + rpt #3 + rrux r12 + rpt #4 + rrux r12 + rpt #5 + rrux r12 + rpt #6 + rrux r12 + rpt #7 + rrux r12 + rpt #8 + rrux r12 + rpt #9 + rrux r12 + rpt #10 + rrux r12 + rpt #11 + rrux r12 + rpt #12 + rrux r12 + rpt #13 + rrux r12 + rpt #14 + rrux r12 + rpt #15 + rrux r12 + rpt #16 + rrux r12 + +// CHECK-LABEL: rrux2: +// CHECK-NEXT: rrux r12 ; encoding: [0x40,0x19,0x0c,0x10] +// CHECK: rpt #2 { rrux r12 ; encoding: [0x41,0x19,0x0c,0x10] +// CHECK: rpt #3 { rrux r12 ; encoding: [0x42,0x19,0x0c,0x10] +// CHECK: rpt #4 { rrux r12 ; encoding: [0x43,0x19,0x0c,0x10] +// CHECK: rpt #5 { rrux r12 ; encoding: [0x44,0x19,0x0c,0x10] +// CHECK: rpt #6 { rrux r12 ; encoding: [0x45,0x19,0x0c,0x10] +// CHECK: rpt #7 { rrux r12 ; encoding: [0x46,0x19,0x0c,0x10] +// CHECK: rpt #8 { rrux r12 ; encoding: [0x47,0x19,0x0c,0x10] +// CHECK: rpt #9 { rrux r12 ; encoding: [0x48,0x19,0x0c,0x10] +// CHECK: rpt #10 { rrux r12 ; encoding: [0x49,0x19,0x0c,0x10] +// CHECK: rpt #11 { rrux r12 ; encoding: [0x4a,0x19,0x0c,0x10] +// CHECK: rpt #12 { rrux r12 ; encoding: [0x4b,0x19,0x0c,0x10] +// CHECK: rpt #13 { rrux r12 ; encoding: [0x4c,0x19,0x0c,0x10] +// CHECK: rpt #14 { rrux r12 ; encoding: [0x4d,0x19,0x0c,0x10] +// CHECK: rpt #15 { rrux r12 ; encoding: [0x4e,0x19,0x0c,0x10] +// CHECK: rpt #16 { rrux r12 ; encoding: [0x4f,0x19,0x0c,0x10] + +rrax2: + rpt #1 + rrax r12 + rpt #2 + rrax r12 + rpt #3 + rrax r12 + rpt #4 + rrax r12 + rpt #5 + rrax r12 + rpt #6 + rrax r12 + rpt #7 + rrax r12 + rpt #8 + rrax r12 + rpt #9 + rrax r12 + rpt #10 + rrax r12 + rpt #11 + rrax r12 + rpt #12 + rrax r12 + rpt #13 + rrax r12 + rpt #14 + rrax r12 + rpt #15 + rrax r12 + rpt #16 + rrax r12 + +// CHECK-LABEL: rrax2: +// CHECK-NEXT: rrax r12 ; encoding: [0x40,0x18,0x0c,0x11] +// CHECK: rpt #2 { rrax r12 ; encoding: [0x41,0x18,0x0c,0x11] +// CHECK: rpt #3 { rrax r12 ; encoding: [0x42,0x18,0x0c,0x11] +// CHECK: rpt #4 { rrax r12 ; encoding: [0x43,0x18,0x0c,0x11] +// CHECK: rpt #5 { rrax r12 ; encoding: [0x44,0x18,0x0c,0x11] +// CHECK: rpt #6 { rrax r12 ; encoding: [0x45,0x18,0x0c,0x11] +// CHECK: rpt #7 { rrax r12 ; encoding: [0x46,0x18,0x0c,0x11] +// CHECK: rpt #8 { rrax r12 ; encoding: [0x47,0x18,0x0c,0x11] +// CHECK: rpt #9 { rrax r12 ; encoding: [0x48,0x18,0x0c,0x11] +// CHECK: rpt #10 { rrax r12 ; encoding: [0x49,0x18,0x0c,0x11] +// CHECK: rpt #11 { rrax r12 ; encoding: [0x4a,0x18,0x0c,0x11] +// CHECK: rpt #12 { rrax r12 ; encoding: [0x4b,0x18,0x0c,0x11] +// CHECK: rpt #13 { rrax r12 ; encoding: [0x4c,0x18,0x0c,0x11] +// CHECK: rpt #14 { rrax r12 ; encoding: [0x4d,0x18,0x0c,0x11] +// CHECK: rpt #15 { rrax r12 ; encoding: [0x4e,0x18,0x0c,0x11] +// CHECK: rpt #16 { rrax r12 ; encoding: [0x4f,0x18,0x0c,0x11] + +rlax2: + rpt #1 + rlax r12 + rpt #2 + rlax r12 + rpt #3 + rlax r12 + rpt #4 + rlax r12 + rpt #5 + rlax r12 + rpt #6 + rlax r12 + rpt #7 + rlax r12 + rpt #8 + rlax r12 + rpt #9 + rlax r12 + rpt #10 + rlax r12 + rpt #11 + rlax r12 + rpt #12 + rlax r12 + rpt #13 + rlax r12 + rpt #14 + rlax r12 + rpt #15 + rlax r12 + rpt #16 + rlax r12 + +// CHECK-LABEL: rlax2: +// CHECK-NEXT: rlax r12 ; encoding: [0x40,0x18,0x0c,0x5c] +// CHECK: rpt #2 { rlax r12 ; encoding: [0x41,0x18,0x0c,0x5c] +// CHECK: rpt #3 { rlax r12 ; encoding: [0x42,0x18,0x0c,0x5c] +// CHECK: rpt #4 { rlax r12 ; encoding: [0x43,0x18,0x0c,0x5c] +// CHECK: rpt #5 { rlax r12 ; encoding: [0x44,0x18,0x0c,0x5c] +// CHECK: rpt #6 { rlax r12 ; encoding: [0x45,0x18,0x0c,0x5c] +// CHECK: rpt #7 { rlax r12 ; encoding: [0x46,0x18,0x0c,0x5c] +// CHECK: rpt #8 { rlax r12 ; encoding: [0x47,0x18,0x0c,0x5c] +// CHECK: rpt #9 { rlax r12 ; encoding: [0x48,0x18,0x0c,0x5c] +// CHECK: rpt #10 { rlax r12 ; encoding: [0x49,0x18,0x0c,0x5c] +// CHECK: rpt #11 { rlax r12 ; encoding: [0x4a,0x18,0x0c,0x5c] +// CHECK: rpt #12 { rlax r12 ; encoding: [0x4b,0x18,0x0c,0x5c] +// CHECK: rpt #13 { rlax r12 ; encoding: [0x4c,0x18,0x0c,0x5c] +// CHECK: rpt #14 { rlax r12 ; encoding: [0x4d,0x18,0x0c,0x5c] +// CHECK: rpt #15 { rlax r12 ; encoding: [0x4e,0x18,0x0c,0x5c] +// CHECK: rpt #16 { rlax r12 ; encoding: [0x4f,0x18,0x0c,0x5c]