Index: llvm/lib/Target/ARM/ARMInstrThumb2.td =================================================================== --- llvm/lib/Target/ARM/ARMInstrThumb2.td +++ llvm/lib/Target/ARM/ARMInstrThumb2.td @@ -1401,6 +1401,8 @@ GPRnopc, zextloadi16>; defm t2LDRB : T2I_ld<0, 0b00, "ldrb", IIC_iLoad_bh_i, IIC_iLoad_bh_si, GPRnopc, zextloadi8>; +def : t2InstAlias<"ldrb${p}.w $Rt, $addr", (t2LDRBi8 GPRnopc:$Rt, t2addrmode_negimm8:$addr, + pred:$p)>; // Loads with sign extension defm t2LDRSH : T2I_ld<1, 0b01, "ldrsh", IIC_iLoad_bh_i, IIC_iLoad_bh_si, @@ -1480,13 +1482,17 @@ (ins t2addrmode_imm8_pre:$addr), AddrModeT2_i8, IndexModePre, IIC_iLoad_bh_iu, "ldrb", "\t$Rt, $addr!", "$addr.base = $Rn_wb", []>, - Sched<[WriteLd]>; + Sched<[WriteLd]> { + let AsmMatchConverter = "cvtThumb2LDRB_PRE"; +} def t2LDRB_POST : T2Ipostldst<0, 0b00, 1, 0, (outs GPR:$Rt, GPR:$Rn_wb), (ins addr_offset_none:$Rn, t2am_imm8_offset:$offset), AddrModeT2_i8, IndexModePost, IIC_iLoad_bh_iu, "ldrb", "\t$Rt, $Rn$offset", "$Rn = $Rn_wb", []>, - Sched<[WriteLd]>; + Sched<[WriteLd]> { + let AsmMatchConverter = "cvtThumb2LDRB_POST"; +} def t2LDRH_PRE : T2Ipreldst<0, 0b01, 1, 1, (outs GPR:$Rt, GPR:$Rn_wb), (ins t2addrmode_imm8_pre:$addr), @@ -1525,6 +1531,22 @@ Sched<[WriteLd]>; } // mayLoad = 1, hasSideEffects = 0 +// In the following aliases we need something to match the writeback out +// operand of t2LDRB_PRE and t2LDRB_POST. For t2LDR_PRE the writeback is well +// hidden in $addr operand, for t2LDR_POST the $Rn operand is not a GPR, +// it's a memory operand. +// In both case $Rt is just a placeholder and we have an AsmMatchCoverter +// to replace that $Rt with the correct writeback register, extracted from +// the memory operand. +def : t2InstAlias< + "ldrb${p}.w $Rt, $addr!", + (t2LDRB_PRE GPR:$Rt, GPR:$Rt, t2addrmode_imm8_pre:$addr, pred:$p)>; + +def : t2InstAlias< + "ldrb${p}.w $Rt, $Rn$offset", + (t2LDRB_POST GPR:$Rt, GPR:$Rt, addr_offset_none:$Rn, + t2am_imm8_offset:$offset, pred:$p)>; + // LDRT, LDRBT, LDRHT, LDRSBT, LDRSHT all have offset mode (PUW=0b110). // Ref: A8.6.57 LDR (immediate, Thumb) Encoding T4 class T2IldT type, string opc, InstrItinClass ii> Index: llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp =================================================================== --- llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -579,6 +579,8 @@ void cvtThumbMultiply(MCInst &Inst, const OperandVector &); void cvtThumbBranches(MCInst &Inst, const OperandVector &); void cvtMVEVMOVQtoDReg(MCInst &Inst, const OperandVector &); + void cvtThumb2LDRB_PRE(MCInst &Inst, const OperandVector &); + void cvtThumb2LDRB_POST(MCInst &Inst, const OperandVector &); bool validateInstruction(MCInst &Inst, const OperandVector &Ops); bool processInstruction(MCInst &Inst, const OperandVector &Ops, MCStreamer &Out); @@ -5637,6 +5639,32 @@ ((ARMOperand &)*Operands[1]).addCondCodeOperands(Inst, 2); // condition code } +// Aliases of t2LRDB_PRE/POST currently have no way of specifying the writeback +// register, so they use the destination register as a placeholder. +// These converters replace the writeback operand with the correct +// register from the memory operand. +void ARMAsmParser::cvtThumb2LDRB_PRE(MCInst &Inst, + const OperandVector &Operands) { + assert(Operands.size() == 5 || Operands.size() == 6); // ldrb vs. ldrb.w + const int N = Operands.size() == 5 ? 2 : 3; + ((ARMOperand &)*Operands[N]).addRegOperands(Inst, 1); // Rt + ((ARMOperand &)*Operands[N + 1]).addMemNoOffsetOperands(Inst, 1); // Rn_wb + ((ARMOperand &)*Operands[N + 1]) + .addMemImmOffsetOperands(Inst, 2); // addr == Rn +/- imm + ((ARMOperand &)*Operands[1]).addCondCodeOperands(Inst, 2); // condition code +} + +void ARMAsmParser::cvtThumb2LDRB_POST(MCInst &Inst, + const OperandVector &Operands) { + assert(Operands.size() == 5 || Operands.size() == 6); // ldrb vs. ldrb.w + const int N = Operands.size() == 5 ? 2 : 3; + ((ARMOperand &)*Operands[N]).addRegOperands(Inst, 1); // Rt + ((ARMOperand &)*Operands[N + 1]).addMemNoOffsetOperands(Inst, 1); // Rn_wb + ((ARMOperand &)*Operands[N + 1]).addMemNoOffsetOperands(Inst, 1); // Rn + ((ARMOperand &)*Operands[N + 2]).addImmOperands(Inst, 1); // offset + ((ARMOperand &)*Operands[1]).addCondCodeOperands(Inst, 2); // condition code +} + /// Parse an ARM memory expression, return false if successful else return true /// or an error. The first token must be a '[' when called. bool ARMAsmParser::parseMemory(OperandVector &Operands) { Index: llvm/test/MC/ARM/basic-thumb2-instructions.s =================================================================== --- llvm/test/MC/ARM/basic-thumb2-instructions.s +++ llvm/test/MC/ARM/basic-thumb2-instructions.s @@ -976,27 +976,35 @@ @ LDRB(immediate) @------------------------------------------------------------------------------ ldrb r5, [r5, #-4] + ldrb.w r5, [r5, #-4] ldrb r5, [r6, #32] + ldrb.w r5, [r6, #32] ldrb r5, [r6, #33] ldrb r5, [r6, #257] ldrb.w lr, [r7, #257] ldrb r5, [r8, #255]! + ldrb.w r5, [r8, #255]! ldrb r2, [r5, #4]! ldrb r1, [r4, #-4]! ldrb lr, [r3], #255 ldrb r9, [r2], #4 + ldrb.w r9, [r2], #4 ldrb r3, [sp], #-4 @ CHECK: ldrb r5, [r5, #-4] @ encoding: [0x15,0xf8,0x04,0x5c] +@ CHECK: ldrb r5, [r5, #-4] @ encoding: [0x15,0xf8,0x04,0x5c] +@ CHECK: ldrb.w r5, [r6, #32] @ encoding: [0x96,0xf8,0x20,0x50] @ CHECK: ldrb.w r5, [r6, #32] @ encoding: [0x96,0xf8,0x20,0x50] @ CHECK: ldrb.w r5, [r6, #33] @ encoding: [0x96,0xf8,0x21,0x50] @ CHECK: ldrb.w r5, [r6, #257] @ encoding: [0x96,0xf8,0x01,0x51] @ CHECK: ldrb.w lr, [r7, #257] @ encoding: [0x97,0xf8,0x01,0xe1] @ CHECK: ldrb r5, [r8, #255]! @ encoding: [0x18,0xf8,0xff,0x5f] +@ CHECK: ldrb r5, [r8, #255]! @ encoding: [0x18,0xf8,0xff,0x5f] @ CHECK: ldrb r2, [r5, #4]! @ encoding: [0x15,0xf8,0x04,0x2f] @ CHECK: ldrb r1, [r4, #-4]! @ encoding: [0x14,0xf8,0x04,0x1d] @ CHECK: ldrb lr, [r3], #255 @ encoding: [0x13,0xf8,0xff,0xeb] @ CHECK: ldrb r9, [r2], #4 @ encoding: [0x12,0xf8,0x04,0x9b] +@ CHECK: ldrb r9, [r2], #4 @ encoding: [0x12,0xf8,0x04,0x9b] @ CHECK: ldrb r3, [sp], #-4 @ encoding: [0x1d,0xf8,0x04,0x39]