]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
arm: fix incorrect assembly of stm{,ia} as push [PR32363]
authorRichard Earnshaw <rearnsha@arm.com>
Fri, 20 Dec 2024 18:28:05 +0000 (18:28 +0000)
committerRichard Earnshaw <rearnsha@arm.com>
Fri, 20 Dec 2024 18:41:36 +0000 (18:41 +0000)
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.

gas/config/tc-arm.c
gas/testsuite/gas/arm/thumb2_ldmstm.d
gas/testsuite/gas/arm/thumb2_ldmstm.s

index 67b431c1591c81ffe0f5369dc57c5d7d28e27e17..aee5c8d89edf9cccf5cec823f085b9951459b515 100644 (file)
@@ -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;
-               }
            }
        }
 
index 295c6a2a12975dd62e3214419f46c83ebbfceaa4..8662a0490e6e5ca65765e46c4865625a82ae7d0a 100644 (file)
@@ -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}
index ab7701c7babfc5a76f512182f9e812c2d6ecb5a1..953e77e4209e012fcbf17f233390c2febecb8188 100644 (file)
@@ -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