[binutils-gdb] arm: fix incorrect assembly of stm{, ia} as push [PR32363]

Richard Earnshaw rearnsha@sourceware.org
Fri Dec 20 18:41:56 GMT 2024


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=9edd0af148442f6192529b6edc9017f95ab404d8

commit 9edd0af148442f6192529b6edc9017f95ab404d8
Author: Richard Earnshaw <rearnsha@arm.com>
Date:   Fri Dec 20 18:28:05 2024 +0000

    arm: fix incorrect assembly of stm{,ia} as push [PR32363]
    
    PR/32363.
    
    Gas was incorrectly translating
            stm sp!, {regs}
    into
            push {regs}
    but this is invalid.  Conversely, it was also failing to translate
            stmfd sp!, {lowregs[, lr]}
    into a 16-bit push instruction.  Fortunately stmia SP! is unlikely to be
    a common idiom on a full-descending stack as it writes values to the stack,
    then immediately deallocates that bit of the stack.
    
    Fixed this and cleaned up the logic somewhat.  While there, change some of
    the ordering so that "ldm base, {base}" is transformed preferentially to
    LDR.  This is in keeping with the general preference in the Arm ARM for
    avoiding single register LDM instructions.

Diff:
---
 gas/config/tc-arm.c                   | 113 +++++++++++++++++++---------------
 gas/testsuite/gas/arm/thumb2_ldmstm.d |   5 +-
 gas/testsuite/gas/arm/thumb2_ldmstm.s |   1 +
 3 files changed, 67 insertions(+), 52 deletions(-)

diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c
index 67b431c1591..aee5c8d89ed 100644
--- a/gas/config/tc-arm.c
+++ b/gas/config/tc-arm.c
@@ -878,6 +878,8 @@ struct asm_opcode
 
 #define T_OPCODE_BRANCH 0xe000
 
+#define T_OPCODE_STMFD  0xe9000000
+
 #define THUMB_SIZE	2	/* Size of thumb instruction.  */
 #define THUMB_PP_PC_LR 0x0100
 #define THUMB_LOAD_BIT 0x0800
@@ -12352,6 +12354,8 @@ encode_thumb2_multi (bool do_io, int base, unsigned mask,
     inst.instruction |= base << 16;
 }
 
+static void do_t_push_pop (void);
+
 static void
 do_t_ldmstm (void)
 {
@@ -12367,20 +12371,73 @@ do_t_ldmstm (void)
       unsigned mask;
 
       narrow = false;
-      /* See if we can use a 16-bit instruction.  */
-      if (inst.instruction < 0xffff /* not ldmdb/stmdb */
-	  && inst.size_req != 4
-	  && !(inst.operands[1].imm & ~0xff))
+      /* Try to convert ldm/stm to a 16-bit instruction.  */
+      /* First, handle SP as the base.  */
+      if (inst.operands[0].reg == REG_SP)
+	{
+	  /* Try converting to push/pop.  */
+	  if (inst.operands[0].writeback
+	      && (inst.instruction == T_OPCODE_STMFD  /* Push.  */
+		  || inst.instruction == T_MNEM_ldmia)) /* Pop.  */
+	    {
+	      inst.instruction = (inst.instruction == T_MNEM_ldmia
+				  ? T_MNEM_pop
+				  : T_MNEM_push);
+	      inst.operands[0] = inst.operands[1];
+	      do_t_push_pop ();
+	      return;
+	    }
+	  /* For single registers try a simple ldr/str.  */
+	  if (!inst.operands[0].writeback
+	      && (inst.instruction == T_MNEM_stmia
+		  || inst.instruction == T_MNEM_ldmia)
+	      && (inst.operands[1].imm & (inst.operands[1].imm - 1)) == 0)
+	    {
+	      unsigned long opcode = T_MNEM_str_sp;
+	      if (inst.instruction == T_MNEM_ldmia)
+		opcode = T_MNEM_ldr_sp;
+	      inst.instruction = THUMB_OP16 (opcode);
+	      inst.instruction |= ((ffs (inst.operands[1].imm) - 1) << 8);
+	      narrow = true;
+	    }
+	}
+      /* For ldmia/stmia with all low registers we can try 16-bit
+	 encodings.  */
+      else if (inst.size_req != 4
+	       && (inst.instruction == T_MNEM_ldmia
+		   || inst.instruction == T_MNEM_stmia)
+	       && inst.operands[0].reg <= 7
+	       && !(inst.operands[1].imm & ~0xff))
 	{
-	  mask = 1 << inst.operands[0].reg;
+	  /* For single registers try a simple ldr/str.  */
+	  if (!inst.operands[0].writeback
+	      && (inst.operands[1].imm & (inst.operands[1].imm - 1)) == 0)
+	    {
+	      unsigned long opcode = T_MNEM_ldr;
+	      if (inst.instruction == T_MNEM_stmia)
+		opcode = T_MNEM_str;
 
-	  if (inst.operands[0].reg <= 7)
+	      inst.instruction = THUMB_OP16 (opcode);
+	      inst.instruction |= inst.operands[0].reg << 3;
+	      inst.instruction |= (ffs (inst.operands[1].imm) - 1);
+	      narrow = true;
+	    }
+	  /* Finally, try a 16-bit ldm/stm.  */
+	  else
 	    {
+	      mask = 1 << inst.operands[0].reg;
+
+	      /* STMIA must have writeback; LDMIA must have writeback
+		 if and only if the base register is not in the
+		 transfer list.  */
 	      if (inst.instruction == T_MNEM_stmia
 		  ? inst.operands[0].writeback
 		  : (inst.operands[0].writeback
 		     == !(inst.operands[1].imm & mask)))
 		{
+		  /* For STMIA, if the base register is in the
+		     transfer list, the value stored is UNKOWN if it
+		     is not the first register in the list.  */
 		  if (inst.instruction == T_MNEM_stmia
 		      && (inst.operands[1].imm & mask)
 		      && (inst.operands[1].imm & (mask - 1)))
@@ -12392,50 +12449,6 @@ do_t_ldmstm (void)
 		  inst.instruction |= inst.operands[1].imm;
 		  narrow = true;
 		}
-	      else if ((inst.operands[1].imm & (inst.operands[1].imm-1)) == 0)
-		{
-		  /* This means 1 register in reg list one of 3 situations:
-		     1. Instruction is stmia, but without writeback.
-		     2. lmdia without writeback, but with Rn not in
-			reglist.
-		     3. ldmia with writeback, but with Rn in reglist.
-		     Case 3 is UNPREDICTABLE behaviour, so we handle
-		     case 1 and 2 which can be converted into a 16-bit
-		     str or ldr. The SP cases are handled below.  */
-		  unsigned long opcode;
-		  /* First, record an error for Case 3.  */
-		  if (inst.operands[1].imm & mask
-		      && inst.operands[0].writeback)
-		    inst.error =
-			_("having the base register in the register list when "
-			  "using write back is UNPREDICTABLE");
-
-		  opcode = (inst.instruction == T_MNEM_stmia ? T_MNEM_str
-							     : T_MNEM_ldr);
-		  inst.instruction = THUMB_OP16 (opcode);
-		  inst.instruction |= inst.operands[0].reg << 3;
-		  inst.instruction |= (ffs (inst.operands[1].imm)-1);
-		  narrow = true;
-		}
-	    }
-	  else if (inst.operands[0] .reg == REG_SP)
-	    {
-	      if (inst.operands[0].writeback)
-		{
-		  inst.instruction =
-			THUMB_OP16 (inst.instruction == T_MNEM_stmia
-				    ? T_MNEM_push : T_MNEM_pop);
-		  inst.instruction |= inst.operands[1].imm;
-		  narrow = true;
-		}
-	      else if ((inst.operands[1].imm & (inst.operands[1].imm-1)) == 0)
-		{
-		  inst.instruction =
-			THUMB_OP16 (inst.instruction == T_MNEM_stmia
-				    ? T_MNEM_str_sp : T_MNEM_ldr_sp);
-		  inst.instruction |= ((ffs (inst.operands[1].imm)-1) << 8);
-		  narrow = true;
-		}
 	    }
 	}
 
diff --git a/gas/testsuite/gas/arm/thumb2_ldmstm.d b/gas/testsuite/gas/arm/thumb2_ldmstm.d
index 295c6a2a129..8662a0490e6 100644
--- a/gas/testsuite/gas/arm/thumb2_ldmstm.d
+++ b/gas/testsuite/gas/arm/thumb2_ldmstm.d
@@ -15,16 +15,17 @@ Disassembly of section .text:
 0[0-9a-f]+ <[^>]+> f856 8c04 	ldr.w	r8, \[r6, #-4\]
 0[0-9a-f]+ <[^>]+> f852 4d04 	ldr.w	r4, \[r2, #-4\]!
 0[0-9a-f]+ <[^>]+> f852 cd04 	ldr.w	ip, \[r2, #-4\]!
-0[0-9a-f]+ <[^>]+> b408      	push	{r3}
+0[0-9a-f]+ <[^>]+> f84d 3b04 	str.w	r3, \[sp\], #4
 0[0-9a-f]+ <[^>]+> f84d 9b04 	str.w	r9, \[sp\], #4
 0[0-9a-f]+ <[^>]+> f8c3 c000 	str.w	ip, \[r3\]
 0[0-9a-f]+ <[^>]+> f844 cb04 	str.w	ip, \[r4\], #4
-0[0-9a-f]+ <[^>]+> f84d 3d04 	str.w	r3, \[sp, #-4\]!
+0[0-9a-f]+ <[^>]+> b408      	push	{r3}
 0[0-9a-f]+ <[^>]+> f84d 9d04 	str.w	r9, \[sp, #-4\]!
 0[0-9a-f]+ <[^>]+> f847 5c04 	str.w	r5, \[r7, #-4\]
 0[0-9a-f]+ <[^>]+> f846 cc04 	str.w	ip, \[r6, #-4\]
 0[0-9a-f]+ <[^>]+> f846 bd04 	str.w	fp, \[r6, #-4\]!
 0[0-9a-f]+ <[^>]+> f845 8d04 	str.w	r8, \[r5, #-4\]!
+0[0-9a-f]+ <[^>]+> 6800      	ldr	r0, \[r0, #0\]
 0[0-9a-f]+ <[^>]+> c80e      	ldmia	r0!, {r1, r2, r3}
 0[0-9a-f]+ <[^>]+> c80f      	ldmia	r0, {r0, r1, r2, r3}
 0[0-9a-f]+ <[^>]+> c802      	ldmia	r0!, {r1}
diff --git a/gas/testsuite/gas/arm/thumb2_ldmstm.s b/gas/testsuite/gas/arm/thumb2_ldmstm.s
index ab7701c7bab..953e77e4209 100644
--- a/gas/testsuite/gas/arm/thumb2_ldmstm.s
+++ b/gas/testsuite/gas/arm/thumb2_ldmstm.s
@@ -21,6 +21,7 @@ ldmstm:
 	stmdb r6, {ip}
 	stmdb r6!, {fp}
 	stmdb r5!, {r8}
+	ldmia r0, {r0}		@ Use ldr r0, [r0]
 
 	@ Valid Thumb-2 encodings of LDM/LDMIA/LDMFD as specified by section
 	@ A8.6.53 of the ARM ARM


More information about the Binutils-cvs mailing list