Index: lib/Target/ARM/AsmParser/ARMAsmParser.cpp =================================================================== --- lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -5645,6 +5645,48 @@ } } + // If first 2 operands of a 3 operand instruction are the same + // then transform to 2 operand version of the same instruction + // e.g. 'adds r0, r0, #1' transforms to 'adds r0, #1' + // FIXME: We would really like to be able to tablegen'erate this. + if (isThumbOne() && Operands.size() == 6 && + (Mnemonic == "add" || Mnemonic == "sub" || Mnemonic == "and" || + Mnemonic == "eor" || Mnemonic == "lsl" || Mnemonic == "lsr" || + Mnemonic == "asr" || Mnemonic == "adc" || Mnemonic == "sbc" || + Mnemonic == "ror" || Mnemonic == "orr" || Mnemonic == "bic")) { + ARMOperand &Op3 = static_cast(*Operands[3]); + ARMOperand &Op4 = static_cast(*Operands[4]); + ARMOperand &Op5 = static_cast(*Operands[5]); + + // If both registers are the same then remove one of them from + // the operand list. + if (Op3.isReg() && Op4.isReg() && Op3.getReg() == Op4.getReg()) { + // If 3rd operand (variable Op5) is a register and the instruction is adds/sub + // then do not transform as the backend already handles this instruction + // correctly. + if (!Op5.isReg() || !((Mnemonic == "add" && CarrySetting) || Mnemonic == "sub")) { + Operands.erase(Operands.begin() + 3); + if (Mnemonic == "add" && !CarrySetting) { + // Special case for 'add' (not 'adds') instruction must + // remove the CCOut operand as well. + Operands.erase(Operands.begin() + 1); + } + } + } + } + + // If instruction is 'add' and first two register operands + // use SP register, then remove one of the SP registers from + // the instruction. + // FIXME: We would really like to be able to tablegen'erate this. + if (isThumbOne() && Operands.size() == 5 && Mnemonic == "add" && !CarrySetting) { + ARMOperand &Op2 = static_cast(*Operands[2]); + ARMOperand &Op3 = static_cast(*Operands[3]); + if (Op2.isReg() && Op3.isReg() && Op2.getReg() == ARM::SP && Op3.getReg() == ARM::SP) { + Operands.erase(Operands.begin() + 2); + } + } + // GNU Assembler extension (compatibility) if ((Mnemonic == "ldrd" || Mnemonic == "strd")) { ARMOperand &Op2 = static_cast(*Operands[2]); @@ -8151,7 +8193,7 @@ } // Some high-register supporting Thumb1 encodings only allow both registers // to be from r0-r7 when in Thumb2. - else if (Opc == ARM::tADDhirr && isThumbOne() && + else if (Opc == ARM::tADDhirr && isThumbOne() && !hasV6MOps() && isARMLowRegister(Inst.getOperand(1).getReg()) && isARMLowRegister(Inst.getOperand(2).getReg())) return Match_RequiresThumb2; Index: test/MC/ARM/thumb_rewrites.s =================================================================== --- /dev/null +++ test/MC/ARM/thumb_rewrites.s @@ -0,0 +1,52 @@ +@ RUN: llvm-mc -triple thumbv6m -show-encoding < %s | FileCheck %s + + adds r0, r0, #8 +@ CHECK: adds r0, #8 @ encoding: [0x08,0x30] + + adds r0, r0, r0 +@ CHECK: adds r0, r0, r0 @ encoding: [0x00,0x18] + + add r0, r0, r8 +@ CHECK: add r0, r8 @ encoding: [0x40,0x44] + + add sp, sp, r0 +@ CHECK: add sp, r0 @ encoding: [0x85,0x44] + + add r0, r0, r1 +@ CHECK: add r0, r1 @ encoding: [0x08,0x44] + + add r2, r2, r3 +@ CHECK: add r2, r3 @ encoding: [0x1a,0x44] + + subs r0, r0, r0 +@ CHECK: subs r0, r0, r0 @ encoding: [0x00,0x1a] + + ands r0, r0, r1 +@ CHECK: ands r0, r1 @ encoding: [0x08,0x40] + + eors r0, r0, r1 +@ CHECK: eors r0, r1 @ encoding: [0x48,0x40] + + lsls r0, r0, r1 +@ CHECK: lsls r0, r1 @ encoding: [0x88,0x40] + + lsrs r0, r0, r1 +@ CHECK: lsrs r0, r1 @ encoding: [0xc8,0x40] + + asrs r0, r0, r1 +@ CHECK: asrs r0, r1 @ encoding: [0x08,0x41] + + adcs r0, r0, r1 +@ CHECK: adcs r0, r1 @ encoding: [0x48,0x41] + + sbcs r0, r0, r1 +@ CHECK: sbcs r0, r1 @ encoding: [0x88,0x41] + + rors r0, r0, r1 +@ CHECK: rors r0, r1 @ encoding: [0xc8,0x41] + + orrs r0, r0, r1 +@ CHECK: orrs r0, r1 @ encoding: [0x08,0x43] + + bics r0, r0, r1 +@ CHECK: bics r0, r1 @ encoding: [0x88,0x43]