Index: llvm/trunk/lib/Target/ARM/AsmParser/ARMAsmParser.cpp =================================================================== --- llvm/trunk/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ llvm/trunk/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -564,6 +564,7 @@ bool shouldOmitCCOutOperand(StringRef Mnemonic, OperandVector &Operands); bool shouldOmitPredicateOperand(StringRef Mnemonic, OperandVector &Operands); bool isITBlockTerminator(MCInst &Inst) const; + void fixupGNULDRDAlias(StringRef Mnemonic, OperandVector &Operands); public: enum ARMMatchResultTy { @@ -5865,6 +5866,52 @@ return false; } +// The GNU assembler has aliases of ldrd and strd with the second register +// omitted. We don't have a way to do that in tablegen, so fix it up here. +// +// We have to be careful to not emit an invalid Rt2 here, because the rest of +// the assmebly parser could then generate confusing diagnostics refering to +// it. If we do find anything that prevents us from doing the transformation we +// bail out, and let the assembly parser report an error on the instruction as +// it is written. +void ARMAsmParser::fixupGNULDRDAlias(StringRef Mnemonic, + OperandVector &Operands) { + if (Mnemonic != "ldrd" && Mnemonic != "strd") + return; + if (Operands.size() < 4) + return; + + ARMOperand &Op2 = static_cast(*Operands[2]); + ARMOperand &Op3 = static_cast(*Operands[3]); + + if (!Op2.isReg()) + return; + if (!Op3.isMem()) + return; + + const MCRegisterClass &GPR = MRI->getRegClass(ARM::GPRRegClassID); + if (!GPR.contains(Op2.getReg())) + return; + + unsigned RtEncoding = MRI->getEncodingValue(Op2.getReg()); + if (!isThumb() && (RtEncoding & 1)) { + // In ARM mode, the registers must be from an aligned pair, this + // restriction does not apply in Thumb mode. + return; + } + if (Op2.getReg() == ARM::PC) + return; + unsigned PairedReg = GPR.getRegister(RtEncoding + 1); + if (!PairedReg || PairedReg == ARM::PC || + (PairedReg == ARM::SP && !hasV8Ops())) + return; + + Operands.insert( + Operands.begin() + 3, + ARMOperand::CreateReg(PairedReg, Op2.getStartLoc(), Op2.getEndLoc())); + return; +} + /// Parse an arm instruction mnemonic followed by its operands. bool ARMAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, OperandVector &Operands) { @@ -6107,25 +6154,8 @@ } } - // GNU Assembler extension (compatibility) - if ((Mnemonic == "ldrd" || Mnemonic == "strd")) { - ARMOperand &Op2 = static_cast(*Operands[2]); - ARMOperand &Op3 = static_cast(*Operands[3]); - if (Op3.isMem()) { - assert(Op2.isReg() && "expected register argument"); - - unsigned SuperReg = MRI->getMatchingSuperReg( - Op2.getReg(), ARM::gsub_0, &MRI->getRegClass(ARM::GPRPairRegClassID)); - - assert(SuperReg && "expected register pair"); - - unsigned PairedReg = MRI->getSubReg(SuperReg, ARM::gsub_1); - - Operands.insert( - Operands.begin() + 3, - ARMOperand::CreateReg(PairedReg, Op2.getStartLoc(), Op2.getEndLoc())); - } - } + // GNU Assembler extension (compatibility). + fixupGNULDRDAlias(Mnemonic, Operands); // FIXME: As said above, this is all a pretty gross hack. This instruction // does not fit with other "subs" and tblgen. Index: llvm/trunk/test/MC/ARM/ldrd-strd-gnu-arm-bad-regs.s =================================================================== --- llvm/trunk/test/MC/ARM/ldrd-strd-gnu-arm-bad-regs.s +++ llvm/trunk/test/MC/ARM/ldrd-strd-gnu-arm-bad-regs.s @@ -0,0 +1,19 @@ +@ RUN: not llvm-mc -triple=armv7-linux-gnueabi %s 2>&1 | FileCheck %s + +.text +.arm +@ CHECK: error: invalid instruction +@ CHECK: ldrd r12, [r0, #512] + ldrd r12, [r0, #512] + +@ CHECK: error: invalid instruction +@ CHECK: strd r12, [r0, #512] + strd r12, [r0, #512] + +@ CHECK: error: invalid instruction +@ CHECK: ldrd r1, [r0, #512] + ldrd r1, [r0, #512] + +@ CHECK: error: invalid instruction +@ CHECK: strd r1, [r0, #512] + strd r1, [r0, #512] Index: llvm/trunk/test/MC/ARM/ldrd-strd-gnu-bad-inst.s =================================================================== --- llvm/trunk/test/MC/ARM/ldrd-strd-gnu-bad-inst.s +++ llvm/trunk/test/MC/ARM/ldrd-strd-gnu-bad-inst.s @@ -0,0 +1,29 @@ +@ RUN: not llvm-mc -triple=armv7-linux-gnueabi %s 2>&1 | FileCheck %s + + .text + .thumb +@ CHECK: error: invalid instruction + strd +@ CHECK: error: invalid instruction + ldrd +@ CHECK: error: invalid instruction + strd r0 +@ CHECK: error: invalid instruction + ldrd r0 +@ CHECK: error: invalid instruction + strd s0, [r0] +@ CHECK: error: invalid instruction + ldrd s0, [r0] + .arm +@ CHECK: error: invalid instruction + strd +@ CHECK: error: invalid instruction + ldrd +@ CHECK: error: invalid instruction + strd r0 +@ CHECK: error: invalid instruction + ldrd r0 +@ CHECK: error: invalid instruction + strd s0, [r0] +@ CHECK: error: invalid instruction + ldrd s0, [r0] Index: llvm/trunk/test/MC/ARM/ldrd-strd-gnu-sp.s =================================================================== --- llvm/trunk/test/MC/ARM/ldrd-strd-gnu-sp.s +++ llvm/trunk/test/MC/ARM/ldrd-strd-gnu-sp.s @@ -1,9 +1,27 @@ // PR19320 -// RUN: llvm-mc -triple=armv7-linux-gnueabi -show-encoding < %s | FileCheck %s -.text +// RUN: not llvm-mc -triple=armv7a-linux-gnueabi -show-encoding < %s 2>&1 | FileCheck %s --check-prefix=V7 +// RUN: llvm-mc -triple=armv8a-linux-gnueabi -show-encoding < %s 2>&1 | FileCheck %s --check-prefix=V8 + .text -// CHECK: ldrd r12, sp, [r0, #32] @ encoding: [0xd0,0xc2,0xc0,0xe1] +// This tries to use the GNU ldrd/strd alias to create an ldrd/strd instruction +// using the sp register. This is valid for V8, but not earlier architectures. + + .arm + +// V7: error: invalid instruction +// V8: ldrd r12, sp, [r0, #32] @ encoding: [0xd0,0xc2,0xc0,0xe1] + ldrd r12, [r0, #32] + +// V7: error: invalid instruction +// V8: strd r12, sp, [r0, #32] @ encoding: [0xf0,0xc2,0xc0,0xe1] + strd r12, [r0, #32] + + .thumb + +// V7: error: invalid instruction +// V8: ldrd r12, sp, [r0, #32] @ encoding: [0xd0,0xe9,0x08,0xcd] ldrd r12, [r0, #32] -// CHECK: strd r12, sp, [r0, #32] @ encoding: [0xf0,0xc2,0xc0,0xe1] +// V7: error: invalid instruction +// V8: strd r12, sp, [r0, #32] @ encoding: [0xc0,0xe9,0x08,0xcd] strd r12, [r0, #32] Index: llvm/trunk/test/MC/ARM/ldrd-strd-gnu-thumb-bad-regs.s =================================================================== --- llvm/trunk/test/MC/ARM/ldrd-strd-gnu-thumb-bad-regs.s +++ llvm/trunk/test/MC/ARM/ldrd-strd-gnu-thumb-bad-regs.s @@ -1,14 +1,11 @@ @ RUN: not llvm-mc -triple=armv7-linux-gnueabi %s 2>&1 | FileCheck %s -@ FIXME: These errors are inaccurate because the error is being reported on the -@ implicit r13 operand added after r12. - .text .thumb -@ CHECK: error: operand must be a register in range [r0, r12] or r14 +@ CHECK: error: invalid instruction @ CHECK: ldrd r12, [r0, #512] ldrd r12, [r0, #512] -@ CHECK: error: operand must be a register in range [r0, r12] or r14 +@ CHECK: error: invalid instruction @ CHECK: strd r12, [r0, #512] strd r12, [r0, #512] Index: llvm/trunk/test/MC/ARM/ldrd-strd-gnu-thumb.s =================================================================== --- llvm/trunk/test/MC/ARM/ldrd-strd-gnu-thumb.s +++ llvm/trunk/test/MC/ARM/ldrd-strd-gnu-thumb.s @@ -18,3 +18,18 @@ strd r0, [r10, #512]! strd r0, [r10], #512 strd r0, [r10, #512] + +@ Rt is allowed to be odd for Thumb (but not ARM) +@ CHECK: ldrd r1, r2, [r10, #512]! @ encoding: [0xfa,0xe9,0x80,0x12] +@ CHECK: ldrd r1, r2, [r10], #512 @ encoding: [0xfa,0xe8,0x80,0x12] +@ CHECK: ldrd r1, r2, [r10, #512] @ encoding: [0xda,0xe9,0x80,0x12] + ldrd r1, [r10, #512]! + ldrd r1, [r10], #512 + ldrd r1, [r10, #512] + +@ CHECK: strd r1, r2, [r10, #512]! @ encoding: [0xea,0xe9,0x80,0x12] +@ CHECK: strd r1, r2, [r10], #512 @ encoding: [0xea,0xe8,0x80,0x12] +@ CHECK: strd r1, r2, [r10, #512] @ encoding: [0xca,0xe9,0x80,0x12] + strd r1, [r10, #512]! + strd r1, [r10], #512 + strd r1, [r10, #512]