Index: lib/Target/ARM/ARMInstrInfo.td =================================================================== --- lib/Target/ARM/ARMInstrInfo.td +++ lib/Target/ARM/ARMInstrInfo.td @@ -587,6 +587,21 @@ let DecoderMethod = "DecodeSOImmOperand"; } +// mod_imm: match a 32-bit immediate operand, which is encoded as a 12-bit +// immediate (See ARMARM - "Modified Immediate Constants"). Unlike so_imm, +// mod_imm keeps the immediate in its encoded form (within the MC layer). +def ModImmAsmOperand: AsmOperandClass { + let Name = "ModImm"; + let ParserMethod = "parseModImm"; +} +def mod_imm : Operand, ImmLeaf { + let EncoderMethod = "getModImmOpValue"; + let PrintMethod = "printModImmOperand"; + let ParserMatchClass = ModImmAsmOperand; +} + // Break so_imm's up into two pieces. This handles immediates with up to 16 // bits set in them. This uses so_imm2part to match and so_imm2part_[12] to // get the first/second pieces. @@ -1417,7 +1432,7 @@ multiclass AI1_cmp_irs opcod, string opc, InstrItinClass iii, InstrItinClass iir, InstrItinClass iis, PatFrag opnode, bit Commutable = 0> { - def ri : AI1, Sched<[WriteCMP, ReadALU]> { @@ -3197,7 +3212,7 @@ } let isReMaterializable = 1, isAsCheapAsAMove = 1, isMoveImm = 1 in -def MOVi : AsI1<0b1101, (outs GPR:$Rd), (ins so_imm:$imm), DPFrm, IIC_iMOVi, +def MOVi : AsI1<0b1101, (outs GPR:$Rd), (ins mod_imm:$imm), DPFrm, IIC_iMOVi, "mov", "\t$Rd, $imm", [(set GPR:$Rd, so_imm:$imm)]>, UnaryDP, Sched<[WriteALU]> { bits<4> Rd; @@ -3705,7 +3720,7 @@ let Inst{3-0} = shift{3-0}; } let isReMaterializable = 1, isAsCheapAsAMove = 1, isMoveImm = 1 in -def MVNi : AsI1<0b1111, (outs GPR:$Rd), (ins so_imm:$imm), DPFrm, +def MVNi : AsI1<0b1111, (outs GPR:$Rd), (ins mod_imm:$imm), DPFrm, IIC_iMVNi, "mvn", "\t$Rd, $imm", [(set GPR:$Rd, so_imm_not:$imm)]>,UnaryDP, Sched<[WriteALU]> { bits<4> Rd; @@ -4253,7 +4268,7 @@ // CMN register-integer let isCompare = 1, Defs = [CPSR] in { -def CMNri : AI1<0b1011, (outs), (ins GPR:$Rn, so_imm:$imm), DPFrm, IIC_iCMPi, +def CMNri : AI1<0b1011, (outs), (ins GPR:$Rn, mod_imm:$imm), DPFrm, IIC_iCMPi, "cmn", "\t$Rn, $imm", [(ARMcmn GPR:$Rn, so_imm:$imm)]>, Sched<[WriteCMP, ReadALU]> { Index: lib/Target/ARM/ARMMCInstLower.cpp =================================================================== --- lib/Target/ARM/ARMMCInstLower.cpp +++ lib/Target/ARM/ARMMCInstLower.cpp @@ -119,11 +119,29 @@ ARMAsmPrinter &AP) { OutMI.setOpcode(MI->getOpcode()); + // In the MC layer we keep immediates in their encoded format + bool EncodeImms = false; + switch (MI->getOpcode()) { + default: break; + case ARM::MOVi: + case ARM::MVNi: + case ARM::CMPri: + case ARM::CMNri: + EncodeImms = true; + break; + } + for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) { const MachineOperand &MO = MI->getOperand(i); MCOperand MCOp; - if (AP.lowerOperand(MO, MCOp)) + if (AP.lowerOperand(MO, MCOp)) { + if (MCOp.isImm() && EncodeImms) { + int32_t Enc = ARM_AM::getSOImmVal(MCOp.getImm()); + if (Enc != -1) + MCOp.setImm(Enc); + } OutMI.addOperand(MCOp); + } } } Index: lib/Target/ARM/AsmParser/ARMAsmParser.cpp =================================================================== --- lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -305,6 +305,7 @@ OperandMatchResultTy parseSetEndImm(OperandVector &); OperandMatchResultTy parseShifterImm(OperandVector &); OperandMatchResultTy parseRotImm(OperandVector &); + OperandMatchResultTy parseModImm(OperandVector &); OperandMatchResultTy parseBitfield(OperandVector &); OperandMatchResultTy parsePostIdxReg(OperandVector &); OperandMatchResultTy parseAM3Offset(OperandVector &); @@ -400,6 +401,7 @@ k_ShiftedImmediate, k_ShifterImmediate, k_RotateImmediate, + k_ModifiedImmediate, k_BitfieldDescriptor, k_Token } Kind; @@ -511,6 +513,10 @@ unsigned Imm; }; + struct ModImmOp { + unsigned Enc; + }; + struct BitfieldOp { unsigned LSB; unsigned Width; @@ -537,6 +543,7 @@ struct RegShiftedRegOp RegShiftedReg; struct RegShiftedImmOp RegShiftedImm; struct RotImmOp RotImm; + struct ModImmOp ModImm; struct BitfieldOp Bitfield; }; @@ -612,6 +619,9 @@ case k_RotateImmediate: RotImm = o.RotImm; break; + case k_ModifiedImmediate: + ModImm = o.ModImm; + break; case k_BitfieldDescriptor: Bitfield = o.Bitfield; break; @@ -1091,6 +1101,7 @@ bool isRegShiftedReg() const { return Kind == k_ShiftedRegister; } bool isRegShiftedImm() const { return Kind == k_ShiftedImmediate; } bool isRotImm() const { return Kind == k_RotateImmediate; } + bool isModImm() const { return Kind == k_ModifiedImmediate; } bool isBitfield() const { return Kind == k_BitfieldDescriptor; } bool isPostIdxRegShifted() const { return Kind == k_PostIndexRegister; } bool isPostIdxReg() const { @@ -1826,6 +1837,14 @@ Inst.addOperand(MCOperand::CreateImm(RotImm.Imm >> 3)); } + void addModImmOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + if (isImm()) // Support for fixups (MCFixup) + addImmOperands(Inst, N); + else + Inst.addOperand(MCOperand::CreateImm(ModImm.Enc)); + } + void addBitfieldOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); // Munge the lsb/width into a bitfield mask. @@ -2630,6 +2649,15 @@ return Op; } + static std::unique_ptr CreateModImm(unsigned Enc, SMLoc S, + SMLoc E) { + auto Op = make_unique(k_ModifiedImmediate); + Op->ModImm.Enc = Enc; + Op->StartLoc = S; + Op->EndLoc = E; + return Op; + } + static std::unique_ptr CreateBitfield(unsigned LSB, unsigned Width, SMLoc S, SMLoc E) { auto Op = make_unique(k_BitfieldDescriptor); @@ -2883,6 +2911,10 @@ case k_RotateImmediate: OS << ""; break; + case k_ModifiedImmediate: + OS << "> 7) << ")>"; + break; case k_BitfieldDescriptor: OS << ""; @@ -4339,6 +4371,99 @@ } ARMAsmParser::OperandMatchResultTy +ARMAsmParser::parseModImm(OperandVector &Operands) { + MCAsmParser &Parser = getParser(); + MCAsmLexer &Lexer = getLexer(); + int32_t Imm1, Imm2; + + if ((Parser.getTok().isNot(AsmToken::Hash) && + Parser.getTok().isNot(AsmToken::Dollar) /* looking for an immediate */ ) + || Lexer.peekTok().is(AsmToken::Colon) + || Lexer.peekTok().is(AsmToken::LParen) /* avoid complex operands */ ) + return MatchOperand_NoMatch; + + SMLoc S = Parser.getTok().getLoc(); + + // Eat the hash (or dollar) + Parser.Lex(); + + SMLoc Sx1, Ex1; + Sx1 = Parser.getTok().getLoc(); + const MCExpr *Imm1Exp; + if (getParser().parseExpression(Imm1Exp, Ex1)) { + Error(Sx1, "malformed expression"); + return MatchOperand_ParseFail; + } + + const MCConstantExpr *CE = dyn_cast(Imm1Exp); + + if (CE) { + Imm1 = CE->getValue(); + int Enc = ARM_AM::getSOImmVal(Imm1); + if (Enc != -1 && Parser.getTok().is(AsmToken::EndOfStatement)) { + // We have a match! + Operands.push_back(ARMOperand::CreateModImm(Enc, Sx1, Ex1)); + return MatchOperand_Success; + } + } else { + Error(Sx1, "constant expression expected"); + return MatchOperand_ParseFail; + } + + if (Parser.getTok().isNot(AsmToken::Comma)) { + // Consider [mov r0, #-10], which is aliased with mvn. We cannot fail + // the parse here. + Operands.push_back(ARMOperand::CreateImm(Imm1Exp, Sx1, Ex1)); + return MatchOperand_Success; + } + + // From this point onward, we expect the input to be a (#bits, #rot) pair + if (Imm1 & ~0xFF) { + Error(Sx1, "immediate operand must a number in the range [0, 255]"); + return MatchOperand_ParseFail; + } + + if (Lexer.peekTok().isNot(AsmToken::Hash) && + Lexer.peekTok().isNot(AsmToken::Dollar)) { + Error(Lexer.peekTok().getLoc(), "immediate operand expected"); + return MatchOperand_ParseFail; + } + + // Eat the comma + Parser.Lex(); + + // Repeat for #rot + SMLoc Sx2, Ex2; + Sx2 = Parser.getTok().getLoc(); + + // Eat the hash (or dollar) + Parser.Lex(); + + const MCExpr *Imm2Exp; + if (getParser().parseExpression(Imm2Exp, Ex2)) { + Error(Sx2, "malformed expression"); + return MatchOperand_ParseFail; + } + + CE = dyn_cast(Imm2Exp); + + if (CE) { + Imm2 = CE->getValue(); + if (!(Imm2 & ~0x1E)) { + // We have a match! + unsigned Enc = ((Imm2 & 0x1E) << 7) | Imm1; + Operands.push_back(ARMOperand::CreateModImm(Enc, S, Ex2)); + return MatchOperand_Success; + } + Error(Sx2, "immediate operand must an even number in the range [0, 30]"); + return MatchOperand_ParseFail; + } else { + Error(Sx2, "constant expression expected"); + return MatchOperand_ParseFail; + } +} + +ARMAsmParser::OperandMatchResultTy ARMAsmParser::parseBitfield(OperandVector &Operands) { MCAsmParser &Parser = getParser(); SMLoc S = Parser.getTok().getLoc(); @@ -9786,6 +9911,7 @@ if (CE->getValue() == 0) return Match_Success; break; + case MCK_ModImm: case MCK_ARMSOImm: if (Op.isImm()) { const MCExpr *SOExpr = Op.getImm(); Index: lib/Target/ARM/InstPrinter/ARMInstPrinter.h =================================================================== --- lib/Target/ARM/InstPrinter/ARMInstPrinter.h +++ lib/Target/ARM/InstPrinter/ARMInstPrinter.h @@ -131,6 +131,7 @@ void printNEONModImmOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); void printImmPlusOneOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); void printRotImmOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); + void printModImmOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); void printGPRPairOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O); void printPCLabel(const MCInst *MI, unsigned OpNum, raw_ostream &O); Index: lib/Target/ARM/InstPrinter/ARMInstPrinter.cpp =================================================================== --- lib/Target/ARM/InstPrinter/ARMInstPrinter.cpp +++ lib/Target/ARM/InstPrinter/ARMInstPrinter.cpp @@ -1301,6 +1301,56 @@ O << markup(">"); } +void ARMInstPrinter::printModImmOperand(const MCInst *MI, unsigned OpNum, + raw_ostream &O) { + MCOperand Op = MI->getOperand(OpNum); + + // Support for fixups (MCFixup) + if (Op.isExpr()) + return printOperand(MI, OpNum, O); + + unsigned Bits = Op.getImm() & 0xFF; + unsigned Rot = (Op.getImm() & 0xF00) >> 7; + + if (Rot == 0) { + O << "#" + << markup(""); + return; + } + + // Invariant: Rot > 0 + int32_t Rotated = ARM_AM::rotr32(Bits, Rot); + bool PrintUnsigned = false; + switch (MI->getOpcode()){ + case ARM::MOVi: + // Movs to PC should be treated unsigned + PrintUnsigned = (MI->getOperand(OpNum - 1).getReg() == ARM::PC); + } + + if ((Bits & 0x3) && (Rotated & ~0xFF)) { + // #rot has the least possible value + O << "#" << markup("(Rotated); + else + O << Rotated; + O << markup(">"); + return; + } + + // Explicit #bits, #rot implied + O << "#" + << markup("") + << ", #" + << markup(""); +} + void ARMInstPrinter::printFBits16(const MCInst *MI, unsigned OpNum, raw_ostream &O) { O << markup(" &Fixups, + const MCSubtargetInfo &ST) const { + const MCOperand &MO = MI.getOperand(Op); + + // Support for fixups (MCFixup) + if (MO.isExpr()) { + const MCExpr *Expr = MO.getExpr(); + // In instruction code this value always encoded as lowest 12 bits, + // so we don't have to perform any specific adjustments. + // Due to requirements of relocatable records we have to use FK_Data_4. + // See ARMELFObjectWriter::ExplicitRelSym and + // ARMELFObjectWriter::GetRelocTypeInner for more details. + MCFixupKind Kind = MCFixupKind(FK_Data_4); + Fixups.push_back(MCFixup::Create(0, Expr, Kind, MI.getLoc())); + return 0; + } + + // Immediate is already in its encoded format + return MO.getImm(); + } + /// getT2SOImmOpValue - Return an encoded 12-bit shifted-immediate value. unsigned getT2SOImmOpValue(const MCInst &MI, unsigned Op, SmallVectorImpl &Fixups, Index: test/MC/ARM/basic-arm-instructions.s =================================================================== --- test/MC/ARM/basic-arm-instructions.s +++ test/MC/ARM/basic-arm-instructions.s @@ -511,6 +511,10 @@ @ CMN @------------------------------------------------------------------------------ cmn r1, #0xf + cmn r7, #42, #2 + cmn r7, #-2147483638 + cmn r7, #40, #2 + cmn r7, #-128 cmn r1, r6 cmn r1, r6, lsl #10 cmn r1, r6, lsr #10 @@ -524,6 +528,10 @@ cmn r1, r6, rrx @ CHECK: cmn r1, #15 @ encoding: [0x0f,0x00,0x71,0xe3] +@ CHECK: cmn r7, #-2147483638 @ encoding: [0x2a,0x01,0x77,0xe3] +@ CHECK: cmn r7, #-2147483638 @ encoding: [0x2a,0x01,0x77,0xe3] +@ CHECK: cmn r7, #40, #2 @ encoding: [0x28,0x01,0x77,0xe3] +@ CHECK: cmp r7, #128 @ encoding: [0x80,0x00,0x57,0xe3] @ CHECK: cmn r1, r6 @ encoding: [0x06,0x00,0x71,0xe1] @ CHECK: cmn r1, r6, lsl #10 @ encoding: [0x06,0x05,0x71,0xe1] @ CHECK: cmn r1, r6, lsr #10 @ encoding: [0x26,0x05,0x71,0xe1] @@ -540,6 +548,10 @@ @ CMP @------------------------------------------------------------------------------ cmp r1, #0xf + cmp r7, #42, #2 + cmp r7, #-2147483638 + cmp r7, #40, #2 + cmp r7, #-128 cmp r1, r6 cmp r1, r6, lsl #10 cmp r1, r6, lsr #10 @@ -555,6 +567,10 @@ cmp lr, #0 @ CHECK: cmp r1, #15 @ encoding: [0x0f,0x00,0x51,0xe3] +@ CHECK: cmp r7, #-2147483638 @ encoding: [0x2a,0x01,0x57,0xe3] +@ CHECK: cmp r7, #-2147483638 @ encoding: [0x2a,0x01,0x57,0xe3] +@ CHECK: cmp r7, #40, #2 @ encoding: [0x28,0x01,0x57,0xe3] +@ CHECK: cmn r7, #128 @ encoding: [0x80,0x00,0x77,0xe3] @ CHECK: cmp r1, r6 @ encoding: [0x06,0x00,0x51,0xe1] @ CHECK: cmp r1, r6, lsl #10 @ encoding: [0x06,0x05,0x51,0xe1] @ CHECK: cmp r1, r6, lsr #10 @ encoding: [0x26,0x05,0x51,0xe1] @@ -1036,6 +1052,15 @@ mov r3, #7 mov r4, #0xff0 mov r5, #0xff0000 + mov r7, #0, #2 + mov r7, #42, #0 + mov r7, #40, #2 + mov r7, #42, #10 + mov r7, #42, #30 + mov r7, #42, #2 + mov r7, #-2147483638 + mov pc, #42, #2 + mov r7, #-128 mov r6, #0xffff movw r9, #0xffff movs r3, #7 @@ -1045,6 +1070,15 @@ @ CHECK: mov r3, #7 @ encoding: [0x07,0x30,0xa0,0xe3] @ CHECK: mov r4, #4080 @ encoding: [0xff,0x4e,0xa0,0xe3] @ CHECK: mov r5, #16711680 @ encoding: [0xff,0x58,0xa0,0xe3] +@ CHECK: mov r7, #0, #2 @ encoding: [0x00,0x71,0xa0,0xe3] +@ CHECK: mov r7, #42 @ encoding: [0x2a,0x70,0xa0,0xe3] +@ CHECK: mov r7, #40, #2 @ encoding: [0x28,0x71,0xa0,0xe3] +@ CHECK: mov r7, #176160768 @ encoding: [0x2a,0x75,0xa0,0xe3] +@ CHECK: mov r7, #42, #30 @ encoding: [0x2a,0x7f,0xa0,0xe3] +@ CHECK: mov r7, #-2147483638 @ encoding: [0x2a,0x71,0xa0,0xe3] +@ CHECK: mov r7, #-2147483638 @ encoding: [0x2a,0x71,0xa0,0xe3] +@ CHECK: mov pc, #2147483658 @ encoding: [0x2a,0xf1,0xa0,0xe3] +@ CHECK: mvn r7, #127 @ encoding: [0x7f,0x70,0xe0,0xe3] @ CHECK: movw r6, #65535 @ encoding: [0xff,0x6f,0x0f,0xe3] @ CHECK: movw r9, #65535 @ encoding: [0xff,0x9f,0x0f,0xe3] @ CHECK: movs r3, #7 @ encoding: [0x07,0x30,0xb0,0xe3] @@ -1218,6 +1252,10 @@ mvn r3, #7 mvn r4, #0xff0 mvn r5, #0xff0000 + mvn r7, #42, #2 + mvn r7, #-2147483638 + mvn r7, #40, #2 + mvn r7, #-128 mvns r3, #7 mvneq r4, #0xff0 mvnseq r5, #0xff0000 @@ -1225,6 +1263,10 @@ @ CHECK: mvn r3, #7 @ encoding: [0x07,0x30,0xe0,0xe3] @ CHECK: mvn r4, #4080 @ encoding: [0xff,0x4e,0xe0,0xe3] @ CHECK: mvn r5, #16711680 @ encoding: [0xff,0x58,0xe0,0xe3] +@ CHECK: mvn r7, #-2147483638 @ encoding: [0x2a,0x71,0xe0,0xe3] +@ CHECK: mvn r7, #-2147483638 @ encoding: [0x2a,0x71,0xe0,0xe3] +@ CHECK: mvn r7, #40, #2 @ encoding: [0x28,0x71,0xe0,0xe3] +@ CHECK: mov r7, #127 @ encoding: [0x7f,0x70,0xa0,0xe3] @ CHECK: mvns r3, #7 @ encoding: [0x07,0x30,0xf0,0xe3] @ CHECK: mvneq r4, #4080 @ encoding: [0xff,0x4e,0xe0,0x03] @ CHECK: mvnseq r5, #16711680 @ encoding: [0xff,0x58,0xf0,0x03] Index: test/MC/ARM/diagnostics.s =================================================================== --- test/MC/ARM/diagnostics.s +++ test/MC/ARM/diagnostics.s @@ -621,3 +621,21 @@ @ CHECK-ERRORS: error: destination register and base register can't be identical @ CHECK-ERRORS: ldrsb r0, [r0], r1 @ CHECK-ERRORS: ^ + + @ Out of range modified immediate values + mov r5, #-256, #6 + mov r6, #42, #7 + mvn r5, #256, #6 + mvn r6, #42, #298 + cmp r5, #65535, #6 + cmp r6, #42, #31 + cmn r5, #-1, #6 + cmn r6, #42, #32 +@ CHECK-ERRORS: error: immediate operand must a number in the range [0, 255] +@ CHECK-ERRORS: error: immediate operand must an even number in the range [0, 30] +@ CHECK-ERRORS: error: immediate operand must a number in the range [0, 255] +@ CHECK-ERRORS: error: immediate operand must an even number in the range [0, 30] +@ CHECK-ERRORS: error: immediate operand must a number in the range [0, 255] +@ CHECK-ERRORS: error: immediate operand must an even number in the range [0, 30] +@ CHECK-ERRORS: error: immediate operand must a number in the range [0, 255] +@ CHECK-ERRORS: error: immediate operand must an even number in the range [0, 30]