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 (40/69)


Refactor thumb_add_sub in a similar way to the previous patch's
treatment of thumb_mov_compare, and convert it to parse_operands.
Again, code redundant with md_apply_fix3 gets deleted.

zw

	* config/tc-arm.c (OP_oRR_iEX): New operand parse code.
	(parse_operands): Implement it.
	(thumb_add_sub): Rename do_t_add_sub.  Remove second argument. Look at
	inst.instruction to see if this is a subtraction.  Use parse_operands.
	Reorganize code for clarity.  Defer most processing of immediate
	operands to md_apply_fix3.
	(do_t_shift): Correct comment.
	(do_t_add, do_t_sub): Delete.
	(tinsns): Use do_t_add_sub for add and sub.  Indicate that sub is
	a subtraction in its opcode.
	(md_apply_fix3 <case BFD_RELOC_ARM_THUMB_ADD>): Check for invalid Hi
	registers here.  Handle value being negative.  Issue less jargon-y
	diagnostics.

===================================================================
Index: gas/config/tc-arm.c
--- gas/config/tc-arm.c	(revision 42)
+++ gas/config/tc-arm.c	(revision 43)
@@ -4321,6 +4321,7 @@
 #define OP_oRL     211  /* optional Thumb low register */
 
 #define OP_oRL_iEX 300  /* optional Thumb low reg or expression */
+#define OP_oRR_iEX 301  /* optional ARM reg or expression */
 
 /* Macro for referring to one of the above constants as a number.
    Should appear solely in parse_operands().  */
@@ -4525,6 +4526,7 @@
 	case OP_(RR_EX):  po_reg_or_goto (REG_TYPE_RN, EXP);  break;
 	case OP_(RR_EXr): po_reg_or_goto (REG_TYPE_RN, EXPr); break;
 	case OP_(oRL_iEX):
+	case OP_(oRR_iEX):
 	case OP_(RL_iEX):
 	case OP_(RR_iEX): po_reg_or_goto (REG_TYPE_RN, iEXP); break;
 
@@ -6198,171 +6200,75 @@
    (thumb_*) come first, in alphabetical order, then the
    per-instruction routines (do_t_*), also in alphabetical order. */
 
-/* Parse an add or subtract instruction, SUBTRACT is non-zero if the opcode
-   was SUB.  */
+/* Parse an add or subtract instruction.  The high bit of inst.instruction
+   is set if the opcode was SUB.  */
 
 static void
-thumb_add_sub (char * str, int subtract)
+do_t_add_sub (char * str)
 {
-  int Rd, Rs, Rn = FAIL;
+  int Rd, Rs, Rn;
+  int subtract = !!(inst.instruction & 0x8000);
 
-  if ((Rd = thumb_reg (&str, THUMB_REG_ANY)) == FAIL
-      || skip_past_comma (&str) == FAIL)
+  if (parse_operands (str, OPERANDS3(RR,RR_iEX,oRR_iEX)))
+    return;
+  Rd = inst.operands[0].reg;
+  if (inst.operands[2].present)
     {
-      if (! inst.error)
-	inst.error = BAD_ARGS;
-      return;
-    }
+      /* If there were three operands, operand 1 must be a register.  */
+      if (!inst.operands[1].isreg)
+        {
+	  inst.error = BAD_ARGS;
+	  return;
+	}
 
-  if (is_immediate_prefix (*str))
-    {
-      Rs = Rd;
-      str++;
-      expression_or_fail (&inst.reloc.exp, &str);
-    }
-  else
-    {
-      if ((Rs = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
-	return;
-
-      if (skip_past_comma (&str) == FAIL)
+      Rs = inst.operands[1].reg;
+      if (!inst.operands[2].isreg) /* Rd, Rs, #imm */
 	{
-	  /* Two operand format, shuffle the registers
-	     and pretend there are 3.  */
-	  Rn = Rs;
-	  Rs = Rd;
+	  inst.instruction |= (Rd << 4) | Rs;
+	  inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+	  return;
 	}
-      else if (is_immediate_prefix (*str))
-	{
-	  str++;
-	  expression_or_fail (&inst.reloc.exp, &str);
-	}
-      else if ((Rn = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
-	return;
+      else /* Rd, Rs, Rn */
+	Rn = inst.operands[2].reg;
     }
-
-  /* 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)
+  else if (!inst.operands[1].isreg)  /* Rd, #imm -> Rd, Rd, #imm */
     {
-      /* All register format.  */
-      if (Rd > 7 || Rs > 7 || Rn > 7)
-	{
-	  if (Rs != Rd)
-	    {
-	      inst.error = _("dest and source1 must be the same register");
-	      return;
-	    }
-
-	  /* Can't do this for SUB.  */
-	  if (subtract)
-	    {
-	      inst.error = _("subtract valid only on lo regs");
-	      return;
-	    }
-
-	  inst.instruction = (T_OPCODE_ADD_HI
-			      | (Rd > 7 ? THUMB_H1 : 0)
-			      | (Rn > 7 ? THUMB_H2 : 0));
-	  inst.instruction |= (Rd & 7) | ((Rn & 7) << 3);
-	}
-      else
-	{
-	  inst.instruction = subtract ? T_OPCODE_SUB_R3 : T_OPCODE_ADD_R3;
-	  inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
-	}
+      inst.instruction |= (Rd << 4) | Rd;
+      inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+      return;
     }
-  else
+  else  /* Rd, Rs -> Rd, Rd, Rs */
     {
-      /* Immediate expression, now things start to get nasty.  */
+      Rs = inst.operands[0].reg;
+      Rn = inst.operands[1].reg;
+    }
 
-      /* First deal with HI regs, only very restricted cases allowed:
-	 Adjusting SP, and using PC or SP to get an address.  */
-      if ((Rd > 7 && (Rd != REG_SP || Rs != REG_SP))
-	  || (Rs > 7 && Rs != REG_SP && Rs != REG_PC))
+  /* We now have Rd and Rs, and Rn set to registers.  */
+  if (Rd > 7 || Rs > 7 || Rn > 7)
+    {
+      if (Rs != Rd)
 	{
-	  inst.error = _("invalid Hi register with immediate");
+	  inst.error = _("dest and source1 must be the same register");
 	  return;
 	}
 
-      if (inst.reloc.exp.X_op != O_constant)
+      /* Can't do this for SUB.  */
+      if (subtract)
 	{
-	  /* Value isn't known yet, all we can do is store all the fragments
-	     we know about in the instruction and let the reloc hacking
-	     work it all out.  */
-	  inst.instruction = (subtract ? 0x8000 : 0) | (Rd << 4) | Rs;
-	  inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+	  inst.error = _("subtract valid only on lo regs");
+	  return;
 	}
-      else
-	{
-	  int offset = inst.reloc.exp.X_add_number;
 
-	  if (subtract)
-	    offset = - offset;
-
-	  if (offset < 0)
-	    {
-	      offset = - offset;
-	      subtract = 1;
-
-	      /* Quick check, in case offset is MIN_INT.  */
-	      if (offset < 0)
-		{
-		  inst.error = _("immediate value out of range");
-		  return;
-		}
-	    }
-	  /* Note - you cannot convert a subtract of 0 into an
-	     add of 0 because the carry flag is set differently.  */
-	  else if (offset > 0)
-	    subtract = 0;
-
-	  if (Rd == REG_SP)
-	    {
-	      if (offset & ~0x1fc)
-		{
-		  inst.error = _("invalid immediate value for stack adjust");
-		  return;
-		}
-	      inst.instruction = subtract ? T_OPCODE_SUB_ST : T_OPCODE_ADD_ST;
-	      inst.instruction |= offset >> 2;
-	    }
-	  else if (Rs == REG_PC || Rs == REG_SP)
-	    {
-	      if (subtract
-		  || (offset & ~0x3fc))
-		{
-		  inst.error = _("invalid immediate for address calculation");
-		  return;
-		}
-	      inst.instruction = (Rs == REG_PC ? T_OPCODE_ADD_PC
-				  : T_OPCODE_ADD_SP);
-	      inst.instruction |= (Rd << 8) | (offset >> 2);
-	    }
-	  else if (Rs == Rd)
-	    {
-	      if (offset & ~0xff)
-		{
-		  inst.error = _("immediate value out of range");
-		  return;
-		}
-	      inst.instruction = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
-	      inst.instruction |= (Rd << 8) | offset;
-	    }
-	  else
-	    {
-	      if (offset & ~0x7)
-		{
-		  inst.error = _("immediate value out of range");
-		  return;
-		}
-	      inst.instruction = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
-	      inst.instruction |= Rd | (Rs << 3) | (offset << 6);
-	    }
-	}
+      inst.instruction = T_OPCODE_ADD_HI;
+      inst.instruction |= (Rd & 8) << 4;
+      inst.instruction |= (Rd & 7);
+      inst.instruction |= Rn << 3;
     }
-
-  end_of_line (str);
+  else
+    {
+      inst.instruction = subtract ? T_OPCODE_SUB_R3 : T_OPCODE_ADD_R3;
+      inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
+    }
 }
 
 static void
@@ -6645,8 +6551,8 @@
   else /* Rd, imm -> Rd, Rd, imm */
     Rs = inst.operands[0].reg;
 
-  /* If we get here, we are doing a shift by an immediate.  Rd and Rs
-     are the registers, and inst.reloc is the immediate.  */
+  /* If we get here, we are doing a shift by an immediate. inst.operands[0].reg
+     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;
@@ -6658,12 +6564,6 @@
 }
 
 static void
-do_t_add (char * str)
-{
-  thumb_add_sub (str, 0);
-}
-
-static void
 do_t_adr (char * str)
 {
   if (parse_operands (str, OPERANDS2(RL,EXP)))
@@ -6958,12 +6858,6 @@
 }
 
 static void
-do_t_sub (char * str)
-{
-  thumb_add_sub (str, 1);
-}
-
-static void
 do_t_swi (char * str)
 {
   if (parse_operands (str, OPERANDS1(EXP)))
@@ -9707,7 +9601,7 @@
 {
   /* Thumb v1 (ARMv4T).  */
   {"adc",	0x4140,		2,	ARM_EXT_V4T, do_t_arit},
-  {"add",	0x0000,		2,	ARM_EXT_V4T, do_t_add},
+  {"add",	0x0000,		2,	ARM_EXT_V4T, do_t_add_sub},
   {"and",	0x4000,		2,	ARM_EXT_V4T, do_t_arit},
   {"asr",	0x4100,		2,	ARM_EXT_V4T, do_t_shift},
   {"b",		T_OPCODE_BRANCH, 2,	ARM_EXT_V4T, do_t_branch12},
@@ -9759,7 +9653,7 @@
   {"strb",	0x0000,		2,	ARM_EXT_V4T, do_t_strb},
   {"strh",	0x0000,		2,	ARM_EXT_V4T, do_t_strh},
   {"swi",	0xdf00,		2,	ARM_EXT_V4T, do_t_swi},
-  {"sub",	0x0000,		2,	ARM_EXT_V4T, do_t_sub},
+  {"sub",	0x8000,		2,	ARM_EXT_V4T, do_t_add_sub},
   {"tst",	T_OPCODE_TST,	2,	ARM_EXT_V4T, do_t_arit},
   /* Pseudo ops:  */
   {"adr",       0x000f,         2,      ARM_EXT_V4T, do_t_adr},
@@ -11083,8 +10977,25 @@
       {
 	int rd = (newval >> 4) & 0xf;
 	int rs = newval & 0xf;
-	int subtract = newval & 0x8000;
+	int subtract = !!(newval & 0x8000);
 
+	/* Check for HI regs, only very restricted cases allowed:
+	   Adjusting SP, and using PC or SP to get an address.  */
+	if ((rd > 7 && (rd != REG_SP || rs != REG_SP))
+	    || (rs > 7 && rs != REG_SP && rs != REG_PC))
+	  as_bad_where (fixP->fx_file, fixP->fx_line,
+			_("invalid Hi register with immediate"));
+
+	/* If value is negative, choose the opposite instruction.  */
+	if (value < 0)
+	  {
+	    value = -value;
+	    subtract = !subtract;
+	    if (value < 0)
+	      as_bad_where (fixP->fx_file, fixP->fx_line,
+			    _("immediate value out of range"));
+	  }
+
 	if (rd == REG_SP)
 	  {
 	    if (value & ~0x1fc)
@@ -11095,8 +11006,7 @@
 	  }
 	else if (rs == REG_PC || rs == REG_SP)
 	  {
-	    if (subtract ||
-		value & ~0x3fc)
+	    if (subtract || value & ~0x3fc)
 	      as_bad_where (fixP->fx_file, fixP->fx_line,
 			    _("invalid immediate for address calculation (value = 0x%08lX)"),
 			    (unsigned long) value);
@@ -11108,7 +11018,7 @@
 	  {
 	    if (value & ~0xff)
 	      as_bad_where (fixP->fx_file, fixP->fx_line,
-			    _("invalid 8bit immediate"));
+			    _("immediate value out of range"));
 	    newval = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
 	    newval |= (rd << 8) | value;
 	  }
@@ -11116,7 +11026,7 @@
 	  {
 	    if (value & ~0x7)
 	      as_bad_where (fixP->fx_file, fixP->fx_line,
-			    _("invalid 3bit immediate"));
+			    _("immediate value out of range"));
 	    newval = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
 	    newval |= rd | (rs << 3) | (value << 6);
 	  }

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