This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Thumb ldm/stm of a single register


The 32-bit form of the Thumb-2 load/store multiple instructions are only valid 
when more than one register is transferred. The assembler should encode 
single register transfers as plain ldr/str. This is similar to how the 
push/pop instructions work. The patch below implements this.

Tested on arm-none-eabi.
Applied to CVS head.

Paul

2007-03-30  Paul Brook  <paul@codesourcery.com>

	gas/
	* config/tc-arm.c (encode_thumb2_ldmstm): New function.
	(do_t_ldmstm): Generate 16-bit push/pop.  Use encode_thumb2_ldmstm.
	(do_t_push_pop):  Use encode_thumb2_ldmstm.

	gas/testsuite/
	* gas/arm/thumb2_ldmstm.d: New test.
	* gas/arm/thumb2_ldmstm.s: New test.

Index: gas/testsuite/gas/arm/thumb2_ldmstm.d
===================================================================
--- gas/testsuite/gas/arm/thumb2_ldmstm.d	(revision 0)
+++ gas/testsuite/gas/arm/thumb2_ldmstm.d	(revision 0)
@@ -0,0 +1,27 @@
+# name: Thumb-2 LDM/STM single reg
+# as: -march=armv6t2
+# objdump: -dr --prefix-addresses --show-raw-insn
+
+.*: +file format .*arm.*
+
+Disassembly of section .text:
+0[0-9a-f]+ <[^>]+> bc01      	pop	{r0}
+0[0-9a-f]+ <[^>]+> f85d 8b04 	ldr.w	r8, \[sp\], #4
+0[0-9a-f]+ <[^>]+> f8d1 9000 	ldr.w	r9, \[r1\]
+0[0-9a-f]+ <[^>]+> f852 cb04 	ldr.w	ip, \[r2\], #4
+0[0-9a-f]+ <[^>]+> f85d 2d04 	ldr.w	r2, \[sp, #-4\]!
+0[0-9a-f]+ <[^>]+> f85d 8d04 	ldr.w	r8, \[sp, #-4\]!
+0[0-9a-f]+ <[^>]+> f856 4c04 	ldr.w	r4, \[r6, #-4\]
+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 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]+ <[^>]+> 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\]!
Index: gas/testsuite/gas/arm/thumb2_ldmstm.s
===================================================================
--- gas/testsuite/gas/arm/thumb2_ldmstm.s	(revision 0)
+++ gas/testsuite/gas/arm/thumb2_ldmstm.s	(revision 0)
@@ -0,0 +1,24 @@
+.syntax unified
+.thumb
+ldmstm:
+	ldmia sp!, {r0}
+	ldmia sp!, {r8}
+	ldmia r1, {r9}
+	ldmia r2!, {ip}
+	ldmdb sp!, {r2}
+	ldmdb sp!, {r8}
+	ldmdb r6, {r4}
+	ldmdb r6, {r8}
+	ldmdb r2!, {r4}
+	ldmdb r2!, {ip}
+	stmia sp!, {r3}
+	stmia sp!, {r9}
+	stmia r3, {ip}
+	stmia r4!, {ip}
+	stmdb sp!, {r3}
+	stmdb sp!, {r9}
+	stmdb r7, {r5}
+	stmdb r6, {ip}
+	stmdb r6!, {fp}
+	stmdb r5!, {r8}
+
Index: gas/config/tc-arm.c
===================================================================
--- gas/config/tc-arm.c	(revision 167327)
+++ gas/config/tc-arm.c	(working copy)
@@ -9071,6 +9071,68 @@ do_t_it (void)
   inst.instruction |= cond << 4;
 }
 
+/* Helper function used for both push/pop and ldm/stm.  */
+static void
+encode_thumb2_ldmstm (int base, unsigned mask, bfd_boolean writeback)
+{
+  bfd_boolean load;
+
+  load = (inst.instruction & (1 << 20)) != 0;
+
+  if (mask & (1 << 13))
+    inst.error =  _("SP not allowed in register list");
+  if (load)
+    {
+      if (mask & (1 << 14)
+	  && mask & (1 << 15))
+	inst.error = _("LR and PC should not both be in register list");
+
+      if ((mask & (1 << base)) != 0
+	  && writeback)
+	as_warn (_("base register should not be in register list "
+		   "when written back"));
+    }
+  else
+    {
+      if (mask & (1 << 15))
+	inst.error = _("PC not allowed in register list");
+
+      if (mask & (1 << base))
+	as_warn (_("value stored for r%d is UNPREDICTABLE"), base);
+    }
+
+  if ((mask & (mask - 1)) == 0)
+    {
+      /* Single register transfers implemented as str/ldr.  */
+      if (writeback)
+	{
+	  if (inst.instruction & (1 << 23))
+	    inst.instruction = 0x00000b04; /* ia! -> [base], #4 */
+	  else
+	    inst.instruction = 0x00000d04; /* db! -> [base, #-4]! */
+	}
+      else
+	{
+	  if (inst.instruction & (1 << 23))
+	    inst.instruction = 0x00800000; /* ia -> [base] */
+	  else
+	    inst.instruction = 0x00000c04; /* db -> [base, #-4] */
+	}
+
+      inst.instruction |= 0xf8400000;
+      if (load)
+	inst.instruction |= 0x00100000;
+
+      mask = ffs(mask) - 1;
+      mask <<= 12;
+    }
+  else if (writeback)
+    inst.instruction |= WRITE_BACK;
+
+  inst.instruction |= mask;
+  inst.instruction |= base << 16;
+}
+
 static void
 do_t_ldmstm (void)
 {
@@ -9082,54 +9144,51 @@ do_t_ldmstm (void)
 
   if (unified_syntax)
     {
+      bfd_boolean narrow;
+      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[0].reg <= 7
-	  && !(inst.operands[1].imm & ~0xff)
-	  && (inst.instruction == T_MNEM_stmia
-	      ? inst.operands[0].writeback
-	      : (inst.operands[0].writeback
-		 == !(inst.operands[1].imm & (1 << inst.operands[0].reg)))))
+	  && !(inst.operands[1].imm & ~0xff))
 	{
-	  if (inst.instruction == T_MNEM_stmia
-	      && (inst.operands[1].imm & (1 << inst.operands[0].reg))
-	      && (inst.operands[1].imm & ((1 << inst.operands[0].reg) - 1)))
-	    as_warn (_("value stored for r%d is UNPREDICTABLE"),
-		     inst.operands[0].reg);
+	  mask = 1 << inst.operands[0].reg;
 
-	  inst.instruction = THUMB_OP16 (inst.instruction);
-	  inst.instruction |= inst.operands[0].reg << 8;
-	  inst.instruction |= inst.operands[1].imm;
-	}
-      else
-	{
-	  if (inst.operands[1].imm & (1 << 13))
-	    as_warn (_("SP should not be in register list"));
-	  if (inst.instruction == T_MNEM_stmia)
-	    {
-	      if (inst.operands[1].imm & (1 << 15))
-		as_warn (_("PC should not be in register list"));
-	      if (inst.operands[1].imm & (1 << inst.operands[0].reg))
+	  if (inst.operands[0].reg <= 7
+	      && (inst.instruction == T_MNEM_stmia
+		  ? inst.operands[0].writeback
+		  : (inst.operands[0].writeback
+		     == !(inst.operands[1].imm & mask))))
+	    {
+	      if (inst.instruction == T_MNEM_stmia
+		  && (inst.operands[1].imm & mask)
+		  && (inst.operands[1].imm & (mask - 1)))
 		as_warn (_("value stored for r%d is UNPREDICTABLE"),
 			 inst.operands[0].reg);
+
+	      inst.instruction = THUMB_OP16 (inst.instruction);
+	      inst.instruction |= inst.operands[0].reg << 8;
+	      inst.instruction |= inst.operands[1].imm;
+	      narrow = TRUE;
 	    }
-	  else
+	  else if (inst.operands[0] .reg == REG_SP
+		   && inst.operands[0].writeback)
 	    {
-	      if (inst.operands[1].imm & (1 << 14)
-		  && inst.operands[1].imm & (1 << 15))
-		as_warn (_("LR and PC should not both be in register list"));
-	      if ((inst.operands[1].imm & (1 << inst.operands[0].reg))
-		  && inst.operands[0].writeback)
-		as_warn (_("base register should not be in register list "
-			   "when written back"));
+	      inst.instruction = THUMB_OP16 (inst.instruction == T_MNEM_stmia
+					     ? T_MNEM_push : T_MNEM_pop);
+	      inst.instruction |= inst.operands[1].imm;
+	      narrow = TRUE;
 	    }
+	}
+
+      if (!narrow)
+	{
 	  if (inst.instruction < 0xffff)
 	    inst.instruction = THUMB_OP32 (inst.instruction);
-	  inst.instruction |= inst.operands[0].reg << 16;
-	  inst.instruction |= inst.operands[1].imm;
-	  if (inst.operands[0].writeback)
-	    inst.instruction |= WRITE_BACK;
+
+	  encode_thumb2_ldmstm(inst.operands[0].reg, inst.operands[1].imm,
+			       inst.operands[0].writeback);
 	}
     }
   else
@@ -9818,7 +9877,7 @@ do_t_push_pop (void)
 
   mask = inst.operands[0].imm;
   if ((mask & ~0xff) == 0)
-    inst.instruction = THUMB_OP16 (inst.instruction);
+    inst.instruction = THUMB_OP16 (inst.instruction) | mask;
   else if ((inst.instruction == T_MNEM_push
 	    && (mask & ~0xff) == 1 << REG_LR)
 	   || (inst.instruction == T_MNEM_pop
@@ -9826,43 +9885,18 @@ do_t_push_pop (void)
     {
       inst.instruction = THUMB_OP16 (inst.instruction);
       inst.instruction |= THUMB_PP_PC_LR;
-      mask &= 0xff;
+      inst.instruction |= mask & 0xff;
     }
   else if (unified_syntax)
     {
-      if (mask & (1 << 13))
-	inst.error =  _("SP not allowed in register list");
-      if (inst.instruction == T_MNEM_push)
-	{
-	  if (mask & (1 << 15))
-	    inst.error = _("PC not allowed in register list");
-	}
-      else
-	{
-	  if (mask & (1 << 14)
-	      && mask & (1 << 15))
-	    inst.error = _("LR and PC should not both be in register list");
-	}
-      if ((mask & (mask - 1)) == 0)
-	{
-	  /* Single register push/pop implemented as str/ldr.  */
-	  if (inst.instruction == T_MNEM_push)
-	    inst.instruction = 0xf84d0d04; /* str reg, [sp, #-4]! */
-	  else
-	    inst.instruction = 0xf85d0b04; /* ldr reg, [sp], #4 */
-	  mask = ffs(mask) - 1;
-	  mask <<= 12;
-	}
-      else
-	inst.instruction = THUMB_OP32 (inst.instruction);
+      inst.instruction = THUMB_OP32 (inst.instruction);
+      encode_thumb2_ldmstm(13, mask, TRUE);
     }
   else
     {
       inst.error = _("invalid register list to push/pop instruction");
       return;
     }
-
-  inst.instruction |= mask;
 }
 
 static void


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]