| From 1e204ac78952cf7e4fcf335423e80007388d3979 Mon Sep 17 00:00:00 2001 |
| From: Nick Desaulniers <ndesaulniers@google.com> |
| Date: Tue, 23 Feb 2021 09:11:23 -0800 |
| Subject: [PATCH] [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 |
| --- |
| llvm/lib/Target/ARM/ARMInstrThumb2.td | 17 ++ |
| .../lib/Target/ARM/AsmParser/ARMAsmParser.cpp | 55 +++++ |
| llvm/test/MC/ARM/thumb2-ldr.w-str.w.s | 205 ++++++++++++++++++ |
| 3 files changed, 277 insertions(+) |
| create mode 100644 llvm/test/MC/ARM/thumb2-ldr.w-str.w.s |
| |
| diff --git a/llvm/lib/Target/ARM/ARMInstrThumb2.td b/llvm/lib/Target/ARM/ARMInstrThumb2.td |
| index b70d66869b06..23758fa18ab5 100644 |
| --- a/llvm/lib/Target/ARM/ARMInstrThumb2.td |
| +++ b/llvm/lib/Target/ARM/ARMInstrThumb2.td |
| @@ -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 |
| diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp |
| index 52577d75ddf5..b64637cfb37b 100644 |
| --- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp |
| +++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp |
| @@ -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. |
| diff --git a/llvm/test/MC/ARM/thumb2-ldr.w-str.w.s b/llvm/test/MC/ARM/thumb2-ldr.w-str.w.s |
| new file mode 100644 |
| index 000000000000..437d28fb278f |
| --- /dev/null |
| +++ b/llvm/test/MC/ARM/thumb2-ldr.w-str.w.s |
| @@ -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] |
| -- |
| 2.30.0.617.g56c4b15f3c-goog |
| |