This is the mail archive of the binutils@sources.redhat.com 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]

Thumb32 assembler (30/69)


Teach parse_operands about register-or-expression operands, as appear
in the Thumb shift instructions and a few other places.  OP_RR_EX
isn't used for anything yet, but will be soon.  Also, fix bugs in the
handling of shifts by 0 and 32 in both md_apply_fix3 and the
disassembler.

zw

gas:
	* config/tc-arm.c (struct arm_it): Add isreg field to operands.
	(THUMB_ASR, THUMB_ASL, THUMB_LSR): Delete.
	(BAD_HIREG): New canned diagnostic.
	(OP_RR_EX, OP_RL_iEX, OP_oRL_iEX): New operand parse codes.
	(parse_operands): Implement the latter two.  Use BAD_HIREG throughout.
	(po_reg_or_fail): Set isreg field as well.
	(thumb_shift): Rename do_t_shift.  Remove second argument.
	Use parse_operands.  Leave patching of immediate value to md_apply_fix3.
	(do_t_asr, do_t_lsl, do_t_lsr): Delete.
	(tinsns): Use do_t_shift for asr, lsl, lsr.
	(md_apply_fix3 <case BFD_RELOC_ARM_THUMB_SHIFT>): Handle 0 and 32
	correctly.
opcodes:
	* arm-dis.c (print_insn_thumb): New %s escape sequence.
	(thumb_opcodes): Use %s for asr and lsr.

===================================================================
Index: gas/config/tc-arm.c
--- gas/config/tc-arm.c	(revision 31)
+++ gas/config/tc-arm.c	(revision 32)
@@ -196,6 +196,7 @@
     int reg;
     int imm;
     int present    : 1;  /* operand present */
+    int isreg	   : 1;  /* operand was a register */
     int writeback  : 1;  /* operand has trailing ! */
   } operands[6];
 };
@@ -552,10 +553,6 @@
 #define THUMB_H1	0x0080
 #define THUMB_H2	0x0040
 
-#define THUMB_ASR 0
-#define THUMB_LSL 1
-#define THUMB_LSR 2
-
 #define THUMB_MOVE 0
 #define THUMB_COMPARE 1
 #define THUMB_CPY 2
@@ -591,6 +588,7 @@
 #define BAD_PC 		_("r15 not allowed here")
 #define BAD_COND 	_("instruction is not conditional")
 #define BAD_OVERLAP	_("registers may not be the same")
+#define BAD_HIREG	_("lo register required")
 
 static struct hash_control *arm_ops_hsh;
 static struct hash_control *arm_tops_hsh;
@@ -4351,6 +4349,10 @@
 #define OP_CPSF	   060  /* CPS flags */
 #define OP_ENDI	   061	/* Endianness specifier */
 
+/* This-or-that operands.  All have bit 7 set.  */
+#define OP_RR_EX   100	/* ARM register or expression */
+#define OP_RL_iEX  101	/* Thumb low register or expression with imm prefix */
+
 /* Optional operands.  All have the high bit set.  */
 #define OP_obI7    200  /* optional, prefix optional, immediate 0 .. 7 */
 #define OP_obI31   201  /*                                      0 .. 31 */
@@ -4361,6 +4363,8 @@
 #define OP_oROR	   210  /* optional rotate right 0/8/16/24 */
 #define OP_oRL	   211  /* optional Thumb low register */
 
+#define OP_oRL_iEX 300	/* optional Thumb low reg or expression */
+
 /* Macro for referring to one of the above constants as a number.
    Should appear solely in parse_operands().  */
 #define OP_(x) OP__(OP_##x)
@@ -4405,6 +4409,7 @@
       return FAIL;					\
     }							\
   inst.operands[i].reg = reg_;				\
+  inst.operands[i].isreg = 1;				\
 } while (0)
 
 #define po_imm_or_fail(min, max, popt) do {				\
@@ -4475,21 +4480,21 @@
 	case OP_(RL):
 	  po_reg_or_fail (REG_TYPE_RN);
 	  if (inst.operands[i].reg > 7)
-	    inst.error = _("lo register required");
+	    inst.error = BAD_HIREG;
 	  break;
 	  
 	case OP_(RLlb):
 	  po_char_or_fail ('[');
 	  po_reg_or_fail (REG_TYPE_RN);
 	  if (inst.operands[i].reg > 7)
-	    inst.error = _("lo register required");
+	    inst.error = BAD_HIREG;
 	  break;
 
 	case OP_(RLtb):
 	  po_reg_or_fail (REG_TYPE_RN);
 	  po_char_or_fail (']');
 	  if (inst.operands[i].reg > 7)
-	    inst.error = _("lo register required");
+	    inst.error = BAD_HIREG;
 	  break;
 	  
 	  /* Immediates */
@@ -4548,6 +4553,26 @@
 	    return FAIL;
 	  break;
 
+	  /* Register or expression */
+	case OP_(RR_EX):
+	  abort ();
+
+	case OP_(oRL_iEX):
+	case OP_(RL_iEX):
+	  if (is_immediate_prefix (*str))
+	    {
+	      str++;
+	      if (my_get_expression (&inst.reloc.exp, &str))
+		return FAIL;
+	    }
+	  else
+	    {
+	      po_reg_or_fail (REG_TYPE_RN);
+	      if (inst.operands[i].reg > 7)
+		inst.error = BAD_HIREG;
+	    }
+	  break;
+
 	  /* Misc */
 	case OP_(CPSF):
 	  if (parse_cps_flags (&inst.operands[i].imm, &str))
@@ -6748,106 +6773,53 @@
 }
 
 static void
-thumb_shift (char * str, int shift)
+do_t_shift (char * str)
 {
-  int Rd, Rs, Rn = FAIL;
+  int Rs;
+  if (parse_operands (str, OPERANDS3(RL,RL_iEX,oRL_iEX)))
+    return;
 
-  if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
-      || skip_past_comma (&str) == FAIL)
+  if (inst.operands[2].present)
     {
-      if (! inst.error)
-	inst.error = BAD_ARGS;
-      return;
-    }
-
-  if (is_immediate_prefix (*str))
-    {
-      /* Two operand immediate format, set Rs to Rd.  */
-      Rs = Rd;
-      str ++;
-      expression_or_fail (&inst.reloc.exp, &str);
-    }
-  else
-    {
-      if ((Rs = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
-	return;
-
-      if (skip_past_comma (&str) == FAIL)
-	{
-	  /* Two operand format, shuffle the registers
-	     and pretend there are 3.  */
-	  Rn = Rs;
-	  Rs = Rd;
-	}
-      else if (is_immediate_prefix (*str))
-	{
-	  str++;
-	  expression_or_fail (&inst.reloc.exp, &str);
-	}
-      else if ((Rn = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
-	return;
-    }
-
-  /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL;
-     for the latter case, EXPR contains the immediate that was found.  */
-
-  if (Rn != FAIL)
-    {
-      if (Rs != Rd)
-	{
-	  inst.error = _("source1 and dest must be same register");
+      /* If there were three operands, operand 1 must be a register.  */
+      if (!inst.operands[1].isreg)
+        {
+	  inst.error = BAD_ARGS;
 	  return;
 	}
-
-      switch (shift)
+      else if (inst.operands[2].isreg)  /* Rd, Rs, Rn */
 	{
-	case THUMB_ASR: inst.instruction = T_OPCODE_ASR_R; break;
-	case THUMB_LSL: inst.instruction = T_OPCODE_LSL_R; break;
-	case THUMB_LSR: inst.instruction = T_OPCODE_LSR_R; break;
-	}
-
-      inst.instruction |= Rd | (Rn << 3);
-    }
-  else
-    {
-      switch (shift)
-	{
-	case THUMB_ASR: inst.instruction = T_OPCODE_ASR_I; break;
-	case THUMB_LSL: inst.instruction = T_OPCODE_LSL_I; break;
-	case THUMB_LSR: inst.instruction = T_OPCODE_LSR_I; break;
-	}
-
-      if (inst.reloc.exp.X_op != O_constant)
-	{
-	  /* Value isn't known yet, create a dummy reloc and let reloc
-	     hacking fix it up.  */
-	  inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
-	}
-      else
-	{
-	  unsigned shift_value = inst.reloc.exp.X_add_number;
-
-	  if (shift_value > 32 || (shift_value == 32 && shift == THUMB_LSL))
+	  if (inst.operands[0].reg != inst.operands[1].reg)
 	    {
-	      inst.error = _("invalid immediate for shift");
+	      inst.error = _("source1 and dest must be same register");
 	      return;
 	    }
-
-	  /* Shifts of zero are handled by converting to LSL.  */
-	  if (shift_value == 0)
-	    inst.instruction = T_OPCODE_LSL_I;
-
-	  /* Shifts of 32 are encoded as a shift of zero.  */
-	  if (shift_value == 32)
-	    shift_value = 0;
-
-	  inst.instruction |= shift_value << 6;
+	  inst.instruction |= inst.operands[0].reg;
+	  inst.instruction |= inst.operands[2].reg << 3;
+	  return;
 	}
-
-      inst.instruction |= Rd | (Rs << 3);
+      else /* Rd, Rs, imm */
+	Rs = inst.operands[1].reg;
     }
+  else if (inst.operands[1].isreg)  /* Rd, Rs -> Rd, Rd, Rs */
+    {
+      inst.instruction |= inst.operands[0].reg;
+      inst.instruction |= inst.operands[2].reg << 3;
+      return;
+    }
+  else /* Rd, imm -> Rd, Rd, imm */
+    Rs = inst.operands[0].reg;
 
-  end_of_line (str);
+  /* If we get here, we are doing a shift by an immediate.  Rd and Rs
+     are the registers, and inst.reloc is the immediate.  */
+  switch (inst.instruction)
+    {
+    case T_OPCODE_ASR_R: inst.instruction = T_OPCODE_ASR_I; break;
+    case T_OPCODE_LSL_R: inst.instruction = T_OPCODE_LSL_I; break;
+    case T_OPCODE_LSR_R: inst.instruction = T_OPCODE_LSR_I; break;
+    }
+  inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
+  inst.instruction |= inst.operands[0].reg | (Rs << 3);
 }
 
 static void
@@ -6907,12 +6879,6 @@
   inst.instruction |= inst.operands[1].reg << 3;
 }
 
-static void
-do_t_asr (char * str)
-{
-  thumb_shift (str, THUMB_ASR);
-}
-
 /* THUMB V5 breakpoint instruction (argument parse)
 	BKPT <immed_8>.  */
 
@@ -7111,18 +7077,6 @@
 }
 
 static void
-do_t_lsl (char * str)
-{
-  thumb_shift (str, THUMB_LSL);
-}
-
-static void
-do_t_lsr (char * str)
-{
-  thumb_shift (str, THUMB_LSR);
-}
-
-static void
 do_t_mov (char * str)
 {
   thumb_mov_compare (str, THUMB_MOVE);
@@ -9977,7 +9931,7 @@
   {"adc",	0x4140,		2,	ARM_EXT_V4T, do_t_arit},
   {"add",	0x0000,		2,	ARM_EXT_V4T, do_t_add},
   {"and",	0x4000,		2,	ARM_EXT_V4T, do_t_arit},
-  {"asr",	0x0000,		2,	ARM_EXT_V4T, do_t_asr},
+  {"asr",	0x4100,		2,	ARM_EXT_V4T, do_t_shift},
   {"b",		T_OPCODE_BRANCH, 2,	ARM_EXT_V4T, do_t_branch12},
   {"beq",	0xd0fe,		2,	ARM_EXT_V4T, do_t_branch9},
   {"bne",	0xd1fe,		2,	ARM_EXT_V4T, do_t_branch9},
@@ -10011,8 +9965,8 @@
   {"ldrsh",	0x5e00,		2,	ARM_EXT_V4T, do_t_lds},
   {"ldsb",	0x5600,		2,	ARM_EXT_V4T, do_t_lds},
   {"ldsh",	0x5e00,		2,	ARM_EXT_V4T, do_t_lds},
-  {"lsl",	0x0000,		2,	ARM_EXT_V4T, do_t_lsl},
-  {"lsr",	0x0000,		2,	ARM_EXT_V4T, do_t_lsr},
+  {"lsl",	0x4080,		2,	ARM_EXT_V4T, do_t_shift},
+  {"lsr",	0x40c0,		2,	ARM_EXT_V4T, do_t_shift},
   {"mov",	0x0000,		2,	ARM_EXT_V4T, do_t_mov},
   {"mul",	T_OPCODE_MUL,	2,	ARM_EXT_V4T, do_t_arit},
   {"mvn",	T_OPCODE_MVN,	2,	ARM_EXT_V4T, do_t_arit},
@@ -11412,11 +11366,18 @@
       break;
 
     case BFD_RELOC_ARM_THUMB_SHIFT:
-      /* 5bit shift value (0..31).  */
-      if (value < 0 || value > 31)
+      /* 5bit shift value (0..32).  LSL cannot take 32.  */
+      newval = md_chars_to_number (buf, THUMB_SIZE) & 0xf83f;
+      temp = newval & 0xf800;
+      if (value < 0 || value > 32 || (value == 32 && temp == T_OPCODE_LSL_I))
 	as_bad_where (fixP->fx_file, fixP->fx_line,
-		      _("illegal Thumb shift value: %ld"), (long) value);
-      newval = md_chars_to_number (buf, THUMB_SIZE) & 0xf03f;
+		      _("invalid shift value: %ld"), (long) value);
+      /* Shifts of zero must be encoded as LSL.  */
+      if (value == 0)
+	newval = (newval & 0x003f) | T_OPCODE_LSL_I;
+      /* Shifts of 32 are encoded as zero.  */
+      else if (value == 32)
+	value = 0;
       newval |= value << 6;
       md_number_to_chars (buf, newval, THUMB_SIZE);
       break;
===================================================================
Index: opcodes/arm-dis.c
--- opcodes/arm-dis.c	(revision 31)
+++ opcodes/arm-dis.c	(revision 32)
@@ -104,7 +104,8 @@
    %<bitfield>W         print (bitfield * 4) as a decimal
    %<bitfield>H         print (bitfield * 2) as a decimal
    %<bitfield>a         print (bitfield * 4) as a pc-rel offset + decoded symbol
-   %e                   print arm SMI operand (bits 0..7,8..19).  */
+   %e                   print arm SMI operand (bits 0..7,8..19).
+   %s			print Thumb right-shift immediate (6..10; 0 == 32). */
 
 /* Note: There is a partial ordering in this table - it must be searched from
    the top to obtain a correct match.  */
@@ -710,8 +711,8 @@
   {ARM_EXT_V4T, 0x5800, 0xFA00, "ldr%10'b\t%0-2r, [%3-5r, %6-8r]"},
   /* format 1 */
   {ARM_EXT_V4T, 0x0000, 0xF800, "lsl\t%0-2r, %3-5r, #%6-10d"},
-  {ARM_EXT_V4T, 0x0800, 0xF800, "lsr\t%0-2r, %3-5r, #%6-10d"},
-  {ARM_EXT_V4T, 0x1000, 0xF800, "asr\t%0-2r, %3-5r, #%6-10d"},
+  {ARM_EXT_V4T, 0x0800, 0xF800, "lsr\t%0-2r, %3-5r, %s"},
+  {ARM_EXT_V4T, 0x1000, 0xF800, "asr\t%0-2r, %3-5r, %s"},
   /* format 3 */
   {ARM_EXT_V4T, 0x2000, 0xF800, "mov\t%8-10r, #%0-7d"},
   {ARM_EXT_V4T, 0x2800, 0xF800, "cmp\t%8-10r, #%0-7d"},
@@ -1814,6 +1815,16 @@
                           }
                           break;
 
+			case 's':
+			  /* Right shift immediate -- bits 6..10; 1-31 print
+			     as themselves, 0 prints as 32.  */
+			  {
+			    long imm = (given & 0x07c0) >> 6;
+			    if (imm == 0)
+			      imm = 32;
+			    func (stream, "#%d", imm);
+			  }
+			  break;
 
                         case '0': case '1': case '2': case '3': case '4':
                         case '5': case '6': case '7': case '8': case '9':

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