[THUMB2] add .w suffixes for ldr/str (immediate) T4

The Linux kernel when built with CONFIG_THUMB2_KERNEL makes use of these
instructions with immediate operands and wide encodings.

These are the T4 variants of the follow sections from the Arm ARM.
F5.1.72 LDR (immediate)
F5.1.229 STR (immediate)

I wasn't able to represent these simple aliases using t2InstAlias due to
the Constraints on the non-suffixed existing instructions, which results
in some manual parsing logic needing to be added.

F1.2 Standard assembler syntax fields
describes the use of the .w (wide) vs .n (narrow) encoding suffix.

Link: https://bugs.llvm.org/show_bug.cgi?id=49118
Link: https://github.com/ClangBuiltLinux/linux/issues/1296
Reported-by: Stefan Agner <stefan@agner.ch>
Reported-by: Arnd Bergmann <arnd@kernel.org>
Signed-off-by: Nick Desaulniers <ndesaulniers@google.com>

Reviewed By: DavidSpickett

Differential Revision: https://reviews.llvm.org/D96632
This commit is contained in:
Nick Desaulniers 2021-02-23 09:11:23 -08:00
parent 956c90d347
commit 1e204ac789
3 changed files with 277 additions and 0 deletions

View File

@ -1563,6 +1563,14 @@ def t2LDRSH_POST : T2Ipostldst<1, 0b01, 1, 0, (outs GPR:$Rt, GPR:$Rn_wb),
Sched<[WriteLd]>;
} // mayLoad = 1, hasSideEffects = 0
// F5.1.72 LDR (immediate) T4
// .w suffixes; Constraints can't be used on t2InstAlias to describe
// "$Rn = $Rn_wb" on POST or "$addr.base = $Rn_wb" on PRE.
def t2LDR_PRE_imm : t2AsmPseudo<"ldr${p}.w $Rt, $addr!",
(ins GPR:$Rt, t2addrmode_imm8_pre:$addr, pred:$p)>;
def t2LDR_POST_imm : t2AsmPseudo<"ldr${p}.w $Rt, $Rn, $imm",
(ins GPR:$Rt, addr_offset_none:$Rn, t2am_imm8_offset:$imm, pred:$p)>;
// LDRT, LDRBT, LDRHT, LDRSBT, LDRSHT all have offset mode (PUW=0b110).
// Ref: A8.6.57 LDR (immediate, Thumb) Encoding T4
class T2IldT<bit signed, bits<2> type, string opc, InstrItinClass ii>
@ -1720,6 +1728,15 @@ def t2STRH_preidx: t2PseudoInst<(outs GPRnopc:$Rn_wb),
Sched<[WriteST]>;
}
// F5.1.229 STR (immediate) T4
// .w suffixes; Constraints can't be used on t2InstAlias to describe
// "$Rn = $Rn_wb,@earlyclobber $Rn_wb" on POST or
// "$addr.base = $Rn_wb,@earlyclobber $Rn_wb" on PRE.
def t2STR_PRE_imm : t2AsmPseudo<"str${p}.w $Rt, $addr!",
(ins GPR:$Rt, t2addrmode_imm8_pre:$addr, pred:$p)>;
def t2STR_POST_imm : t2AsmPseudo<"str${p}.w $Rt, $Rn, $imm",
(ins GPR:$Rt, addr_offset_none:$Rn, t2am_imm8_offset:$imm, pred:$p)>;
// STRT, STRBT, STRHT all have offset mode (PUW=0b110) and are for disassembly
// only.
// Ref: A8.6.193 STR (immediate, Thumb) Encoding T4

View File

@ -7658,6 +7658,33 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst,
"source register and base register can't be identical");
return false;
}
case ARM::t2LDR_PRE_imm:
case ARM::t2LDR_POST_imm:
case ARM::t2STR_PRE_imm:
case ARM::t2STR_POST_imm: {
// Rt must be different from Rn.
const unsigned Rt = MRI->getEncodingValue(Inst.getOperand(0).getReg());
const unsigned Rn = MRI->getEncodingValue(Inst.getOperand(1).getReg());
if (Rt == Rn)
return Error(Operands[3]->getStartLoc(),
"destination register and base register can't be identical");
if (Inst.getOpcode() == ARM::t2LDR_POST_imm ||
Inst.getOpcode() == ARM::t2STR_POST_imm) {
int Imm = Inst.getOperand(2).getImm();
if (Imm > 255 || Imm < -255)
return Error(Operands[5]->getStartLoc(),
"operand must be in range [-255, 255]");
}
if (Inst.getOpcode() == ARM::t2STR_PRE_imm ||
Inst.getOpcode() == ARM::t2STR_POST_imm) {
if (Inst.getOperand(0).getReg() == ARM::PC) {
return Error(Operands[3]->getStartLoc(),
"operand must be a register in range [r0, r14]");
}
}
return false;
}
case ARM::LDR_PRE_IMM:
case ARM::LDR_PRE_REG:
case ARM::t2LDR_PRE:
@ -8625,6 +8652,34 @@ bool ARMAsmParser::processInstruction(MCInst &Inst,
Inst = TmpInst;
return true;
}
// Aliases for imm syntax of LDR instructions.
case ARM::t2LDR_PRE_imm:
case ARM::t2LDR_POST_imm: {
MCInst TmpInst;
TmpInst.setOpcode(Inst.getOpcode() == ARM::t2LDR_PRE_imm ? ARM::t2LDR_PRE
: ARM::t2LDR_POST);
TmpInst.addOperand(Inst.getOperand(0)); // Rt
TmpInst.addOperand(Inst.getOperand(4)); // Rt_wb
TmpInst.addOperand(Inst.getOperand(1)); // Rn
TmpInst.addOperand(Inst.getOperand(2)); // imm
TmpInst.addOperand(Inst.getOperand(3)); // CondCode
Inst = TmpInst;
return true;
}
// Aliases for imm syntax of STR instructions.
case ARM::t2STR_PRE_imm:
case ARM::t2STR_POST_imm: {
MCInst TmpInst;
TmpInst.setOpcode(Inst.getOpcode() == ARM::t2STR_PRE_imm ? ARM::t2STR_PRE
: ARM::t2STR_POST);
TmpInst.addOperand(Inst.getOperand(4)); // Rt_wb
TmpInst.addOperand(Inst.getOperand(0)); // Rt
TmpInst.addOperand(Inst.getOperand(1)); // Rn
TmpInst.addOperand(Inst.getOperand(2)); // imm
TmpInst.addOperand(Inst.getOperand(3)); // CondCode
Inst = TmpInst;
return true;
}
// Aliases for alternate PC+imm syntax of LDR instructions.
case ARM::t2LDRpcrel:
// Select the narrow version if the immediate will fit.

View File

@ -0,0 +1,205 @@
@ RUN: not llvm-mc -triple=thumbv7-unknown-linux-gnueabi -arm-implicit-it=thumb -show-encoding < %s 2>&1 | FileCheck %s
.syntax unified
@ Note: The error stream for XFAIL needs to get checked first.
ldr.w r1, [r1, #-4]!
ldr.w r1, [r0, #256]!
ldr.w r1, [r0, #-256]!
ldr.w r1, [pc, #-4]!
ldr.w r1, [r1], #4
ldr.w r0, [r0], #4
ldr.w r0, [r1], #256
ldr.w r0, [r1], #-256
str.w r0, [r0, #-4]!
str.w pc, [r0, #-4]!
str.w r1, [pc, #-4]!
str.w r1, [r2, #256]!
str.w r1, [r2, #-256]!
str.w r0, [r0], #4
str.w pc, [r0], #4
str.w r1, [r0], #256
str.w r1, [r0], #-256
@@ XFAIL
@ CHECK: error: destination register and base register can't be identical
@ CHECK-NEXT: ldr.w r1, [r1, #-4]!
@ CHECK: error: invalid instruction, any one of the following would fix this:
@ CHECK-NEXT: ldr.w r1, [r0, #256]!
@ CHECK: note: invalid operand for instruction
@ CHECK: note: too many operands for instruction
@ CHECK: error: invalid operand for instruction
@ CHECK-NEXT: ldr.w r1, [r0, #-256]!
@ CHECK: error: invalid instruction, any one of the following would fix this:
@ CHECK-NEXT: ldr.w r1, [pc, #-4]!
@ CHECK: note: invalid operand for instruction
@ CHECK: note: too many operands for instruction
@ CHECK: error: destination register and base register can't be identical
@ CHECK-NEXT: ldr.w r1, [r1], #4
@ CHECK: error: destination register and base register can't be identical
@ CHECK-NEXT: ldr.w r0, [r0], #4
@ CHECK: error: operand must be in range [-255, 255]
@ CHECK-NEXT: ldr.w r0, [r1], #256
@ CHECK: error: operand must be in range [-255, 255]
@ CHECK-NEXT: ldr.w r0, [r1], #-256
@ CHECK: error: destination register and base register can't be identical
@ CHECK-NEXT: str.w r0, [r0, #-4]!
@ CHECK: error: operand must be a register in range [r0, r14]
@ CHECK-NEXT: str.w pc, [r0, #-4]!
@ CHECK: error: invalid operand for instruction
@ CHECK-NEXT: str.w r1, [pc, #-4]!
@ CHECK: error: invalid instruction, any one of the following would fix this:
@ CHECK-NEXT: str.w r1, [r2, #256]!
@ CHECK: note: invalid operand for instruction
@ CHECK: note: too many operands for instruction
@ CHECK: error: invalid operand for instruction
@ CHECK-NEXT: str.w r1, [r2, #-256]!
@ CHECK: error: destination register and base register can't be identical
@ CHECK-NEXT: str.w r0, [r0], #4
@ CHECK: error: operand must be a register in range [r0, r14]
@ CHECK-NEXT: str.w pc, [r0], #4
@ CHECK: error: operand must be in range [-255, 255]
@ CHECK-NEXT: str.w r1, [r0], #256
@ CHECK: error: operand must be in range [-255, 255]
@ CHECK-NEXT: str.w r1, [r0], #-256
@@ XPASS
@ Simple checks that we get the same encoding w/ and w/o the .w suffix.
ldr r3, [r1], #4
ldr.w r3, [r1], #4
str r3, [r0], #4
str.w r3, [r0], #4
ldr r3, [r1, #-4]!
ldr.w r3, [r1, #-4]!
str r3, [r0, #-4]!
str.w r3, [r0, #-4]!
@ CHECK: ldr r3, [r1], #4 @ encoding: [0x51,0xf8,0x04,0x3b]
@ CHECK: ldr r3, [r1], #4 @ encoding: [0x51,0xf8,0x04,0x3b]
@ CHECK: str r3, [r0], #4 @ encoding: [0x40,0xf8,0x04,0x3b]
@ CHECK: str r3, [r0], #4 @ encoding: [0x40,0xf8,0x04,0x3b]
@ CHECK: ldr r3, [r1, #-4]! @ encoding: [0x51,0xf8,0x04,0x3d]
@ CHECK: ldr r3, [r1, #-4]! @ encoding: [0x51,0xf8,0x04,0x3d]
@ CHECK: str r3, [r0, #-4]! @ encoding: [0x40,0xf8,0x04,0x3d]
@ CHECK: str r3, [r0, #-4]! @ encoding: [0x40,0xf8,0x04,0x3d]
@@ LDR pre-increment w/ writeback
@ Vary Rt.
ldr.w r0, [r1, #-4]!
ldr.w sp, [r1, #-4]! @ TODO: GAS warns for this
ldr.w pc, [r1, #-4]!
@ Vary Rn.
ldr.w r1, [r0, #-4]!
ldr.w r1, [sp, #-4]!
@ Vary imm.
ldr.w r1, [r0, #255]!
ldr.w r1, [r0, #-255]!
ldr.w r1, [r0, #0]!
@ Condition codes.
ldreq.w r1, [r0, #255]!
ldrle.w r1, [r0, #255]!
@ CHECK: ldr r0, [r1, #-4]! @ encoding: [0x51,0xf8,0x04,0x0d]
@ CHECK: ldr sp, [r1, #-4]! @ encoding: [0x51,0xf8,0x04,0xdd]
@ CHECK: ldr pc, [r1, #-4]! @ encoding: [0x51,0xf8,0x04,0xfd]
@ CHECK: ldr r1, [r0, #-4]! @ encoding: [0x50,0xf8,0x04,0x1d]
@ CHECK: ldr r1, [sp, #-4]! @ encoding: [0x5d,0xf8,0x04,0x1d]
@ CHECK: ldr r1, [r0, #255]! @ encoding: [0x50,0xf8,0xff,0x1f]
@ CHECK: ldr r1, [r0, #-255]! @ encoding: [0x50,0xf8,0xff,0x1d]
@ CHECK: ldr r1, [r0, #0]! @ encoding: [0x50,0xf8,0x00,0x1f]
@ CHECK: it eq @ encoding: [0x08,0xbf]
@ CHECK: ldreq r1, [r0, #255]! @ encoding: [0x50,0xf8,0xff,0x1f]
@ CHECK: it le @ encoding: [0xd8,0xbf]
@ CHECK: ldrle r1, [r0, #255]! @ encoding: [0x50,0xf8,0xff,0x1f]
@@ LDR post-increment
@ Vary Rt.
ldr.w r0, [r1], #4
ldr.w sp, [r1], #4 @ TODO: GAS warns for this
ldr.w pc, [r1], #4
@ Vary Rn.
ldr.w r0, [r1], #4
ldr.w r0, [sp], #4
ldr.w r0, [pc], #4 @ TODO: GAS warns for this
@ Vary imm.
ldr.w r0, [r1], #255
ldr.w r0, [r1], #0
ldr.w r0, [r1], #-255
@ Condition codes.
ldreq.w r0, [r1], #255
ldrle.w r0, [r1], #255
@ CHECK: ldr r0, [r1], #4 @ encoding: [0x51,0xf8,0x04,0x0b]
@ CHECK: ldr sp, [r1], #4 @ encoding: [0x51,0xf8,0x04,0xdb]
@ CHECK: ldr pc, [r1], #4 @ encoding: [0x51,0xf8,0x04,0xfb]
@ CHECK: ldr r0, [r1], #4 @ encoding: [0x51,0xf8,0x04,0x0b]
@ CHECK: ldr r0, [sp], #4 @ encoding: [0x5d,0xf8,0x04,0x0b]
@ CHECK: ldr r0, [pc], #4 @ encoding: [0x5f,0xf8,0x04,0x0b]
@ CHECK: ldr r0, [r1], #255 @ encoding: [0x51,0xf8,0xff,0x0b]
@ CHECK: ldr r0, [r1], #0 @ encoding: [0x51,0xf8,0x00,0x0b]
@ CHECK: ldr r0, [r1], #-255 @ encoding: [0x51,0xf8,0xff,0x09]
@ CHECK: it eq @ encoding: [0x08,0xbf]
@ CHECK: ldreq r0, [r1], #255 @ encoding: [0x51,0xf8,0xff,0x0b]
@ CHECK: it le @ encoding: [0xd8,0xbf]
@ CHECK: ldrle r0, [r1], #255 @ encoding: [0x51,0xf8,0xff,0x0b]
@@ STR pre-increment w/ writeback
@ Vary Rt.
str.w r1, [r0, #-4]!
str.w sp, [r0, #-4]!
@ Vary Rn.
str.w r1, [r2, #-4]!
str.w r1, [sp, #-4]!
@ Vary imm.
str.w r1, [r2, #255]!
str.w r1, [r2, #0]!
str.w r1, [r2, #-255]!
@ Condition codes.
streq.w r1, [r2, #255]!
strle.w r1, [r2, #255]!
@ CHECK: str r1, [r0, #-4]! @ encoding: [0x40,0xf8,0x04,0x1d]
@ CHECK: str sp, [r0, #-4]! @ encoding: [0x40,0xf8,0x04,0xdd]
@ CHECK: str r1, [r2, #-4]! @ encoding: [0x42,0xf8,0x04,0x1d]
@ CHECK: str r1, [sp, #-4]! @ encoding: [0x4d,0xf8,0x04,0x1d]
@ CHECK: str r1, [r2, #255]! @ encoding: [0x42,0xf8,0xff,0x1f]
@ CHECK: str r1, [r2, #0]! @ encoding: [0x42,0xf8,0x00,0x1f]
@ CHECK: str r1, [r2, #-255]! @ encoding: [0x42,0xf8,0xff,0x1d]
@ CHECK: it eq @ encoding: [0x08,0xbf]
@ CHECK: streq r1, [r2, #255]! @ encoding: [0x42,0xf8,0xff,0x1f]
@ CHECK: it le @ encoding: [0xd8,0xbf]
@ CHECK: strle r1, [r2, #255]! @ encoding: [0x42,0xf8,0xff,0x1f]
@@ STR post-increment
@ Vary Rt.
str.w r1, [r0], #4
str.w sp, [r0], #4
@ Vary Rn.
str.w r0, [r1], #4
str.w r0, [sp], #4
str.w r0, [pc], #4 @ TODO: GAS warns for this.
@ Vary imm.
str.w r1, [r0], #255
str.w r1, [r0], #0
str.w r1, [r0], #-255
@ Condition codes.
streq.w r1, [r0], #255
strle.w r1, [r0], #255
@ CHECK: str r1, [r0], #4 @ encoding: [0x40,0xf8,0x04,0x1b]
@ CHECK: str sp, [r0], #4 @ encoding: [0x40,0xf8,0x04,0xdb]
@ CHECK: str r0, [r1], #4 @ encoding: [0x41,0xf8,0x04,0x0b]
@ CHECK: str r0, [sp], #4 @ encoding: [0x4d,0xf8,0x04,0x0b]
@ CHECK: str r0, [pc], #4 @ encoding: [0x4f,0xf8,0x04,0x0b]
@ CHECK: str r1, [r0], #255 @ encoding: [0x40,0xf8,0xff,0x1b]
@ CHECK: str r1, [r0], #0 @ encoding: [0x40,0xf8,0x00,0x1b]
@ CHECK: str r1, [r0], #-255 @ encoding: [0x40,0xf8,0xff,0x19]
@ CHECK: it eq @ encoding: [0x08,0xbf]
@ CHECK: streq r1, [r0], #255 @ encoding: [0x40,0xf8,0xff,0x1b]
@ CHECK: it le @ encoding: [0xd8,0xbf]
@ CHECK: strle r1, [r0], #255 @ encoding: [0x40,0xf8,0xff,0x1b]