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


Here we are going after bigger game: Thumb addresses can now be
handled by parse_operands.  This, plus some adjustments to tinsns,
allows the removal of lots of tiny wrapper functions.  And, again,
constant handling is deferred to md_apply_fix3 so that code is not
duplicated between there and the encoder functions.

zw

	* tc-arm.c (THUMB_LOAD_BIT): New constant.
	(parse_thumb_address): New function.
	(OP_TADDR): New operand parse code.
	(parse_operands): Handle it.  Delete code for OP_RLlb and OP_RLtb.
	(thumb_load_store): Rename do_t_ldst.  Use parse_operands.  Remove
	second and third arguments; determine size and direction of operation
	from inst.instruction.  Defer most constant handling to md_apply_fix3.
	(do_t_lds): Use TADDR, not RLlb and RLtb.
	(THUMB_REG_LO, THUMB_REG_ANY, THUMB_LOAD, THUMB_STORE, THUMB_WORD)
	(THUMB_HALFWORD, THUMB_BYTE, thumb_reg, OP_RLlb, OP_RLtb, do_t_ldr)
	(do_t_ldrb, do_t_ldrh, do_t_str, do_t_strb, do_t_strh):	Delete.
	(tinsns): Use do_t_ldst for ldr ldrb ldrh str strb strh.  Set opcode
	for these to the opcode for the 3-register form.
	(md_apply_fix3 <case BFD_RELOC_ARM_THUMB_OFFSET>): Correct check
	for non-word-aligned offset from PC.

===================================================================
Index: gas/config/tc-arm.c
--- gas/config/tc-arm.c	(revision 44)
+++ gas/config/tc-arm.c	(revision 45)
@@ -197,6 +197,7 @@
     int imm;
     int present    : 1;  /* operand present */
     int isreg	   : 1;  /* operand was a register */
+    int immisreg   : 1;  /* .imm field is a second register */
     int writeback  : 1;  /* operand has trailing ! */
     int hasreloc   : 1;  /* operand has relocation suffix */
     int postind    : 1;  /* operand is post-indexed */
@@ -555,22 +556,10 @@
 #define T_OPCODE_BRANCH 0xe7fe
 
 #define THUMB_SIZE	2	/* Size of thumb instruction.  */
-#define THUMB_REG_LO	0x1
-#define THUMB_REG_ANY	0x3
 
-#define THUMB_H1	0x0080
-#define THUMB_H2	0x0040
-
-#define THUMB_LOAD 0
-#define THUMB_STORE 1
-
 #define THUMB_PP_PC_LR 0x0100
+#define THUMB_LOAD_BIT 0x0800
 
-/* These three are used for immediate shifts, do not alter.  */
-#define THUMB_WORD 2
-#define THUMB_HALFWORD 1
-#define THUMB_BYTE 0
-
 struct thumb_opcode
 {
   /* Basic string to match.  */
@@ -1275,28 +1264,6 @@
     }							\
 } while (0)
 
-/* Parse and validate that a register is of the right form, this saves
-   repeated checking of this information in many similar cases.
-   Unlike the 32-bit case we do not insert the register into the opcode
-   here, since the position is often unknown until the full instruction
-   has been parsed.  */
-
-static int
-thumb_reg (char ** strp, int hi_lo)
-{
-  int reg;
-
-  if ((reg = reg_required_here (strp, -1, REG_TYPE_RN)) == FAIL)
-    return FAIL;
-
-  if (hi_lo == THUMB_REG_LO && reg > 7)
-    {
-      inst.error = BAD_HIREG;
-      return FAIL;
-    }
-  return reg;
-}
-
 /* Register lists.  */
 
 static long
@@ -2451,6 +2418,100 @@
   return SUCCESS;
 }
 
+/* Parse a Thumb address expression:
+
+      [Rl]
+      [Rl, Rl]
+      [Rl, #imm]     ; 5 bits, *4
+      [SP/PC, #imm]  ; 8 bits, *4
+      =imm           ; pseudo (mov or lit-pool load)
+      label	     ; translated to [PC, offset]
+
+   Data is written to inst.operands[i] and/or inst.reloc.exp.
+   Returns SUCCESS or FAIL.  */
+
+static int
+parse_thumb_address (char **str, int i)
+{
+  char *s = *str;
+  int Rb, Ro;
+
+  if (*s == '[')
+    {
+      s++;
+      if ((Rb = arm_reg_parse (&s, REG_TYPE_RN)) == FAIL)
+	return FAIL;
+      if (Rb > 7 && Rb != REG_SP && Rb != REG_PC)
+	{
+	  inst.error = BAD_HIREG;
+	  return FAIL;
+	}
+      inst.operands[i].reg = Rb;
+      inst.operands[i].isreg = 1;
+      if (skip_past_comma (&s) != FAIL)
+	{
+	  if (is_immediate_prefix (*s))
+	    {
+	      s++;
+	      if (my_get_expression (&inst.reloc.exp, &s))
+		return FAIL;
+	    }
+	  else
+	    {
+	      if ((Ro = arm_reg_parse (&s, REG_TYPE_RN)) == FAIL)
+		return FAIL;
+	      if (Ro > 7 || Rb > 7)
+		{
+		  inst.error = BAD_HIREG;
+		  return FAIL;
+		}
+	      inst.operands[i].imm = Ro;
+	      inst.operands[i].immisreg = 1;
+	    }
+	}
+      else
+	{
+	  /* [Rd] == [Rd,#0] */
+	  inst.reloc.exp.X_op = O_constant;
+	  inst.reloc.exp.X_add_number = 0;
+	}
+      if (*s++ != ']')
+	{
+	  inst.error = BAD_ARGS;
+	  return FAIL;
+	}
+    }
+  else if (*s == '=')
+    {
+      s++;
+      if (my_get_expression (&inst.reloc.exp, &s))
+	return FAIL;
+
+      if (   inst.reloc.exp.X_op != O_constant
+	  && inst.reloc.exp.X_op != O_symbol)
+	{
+	  inst.error = "Constant expression expected";
+	  return FAIL;
+	}
+    }
+  else
+    {
+      if (my_get_expression (&inst.reloc.exp, &s))
+	return FAIL;
+
+      inst.reloc.exp.X_add_number -= 4; /* Pipeline offset.  */
+      if (inst.reloc.exp.X_op != O_constant)
+	inst.reloc.pc_rel = 1;
+      inst.operands[i].reg = REG_PC;
+      inst.operands[i].isreg = 1;
+    }
+
+  if (!inst.operands[i].immisreg)
+    inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+  *str = s;
+  return SUCCESS;
+}
+
 /* Parse the flags argument to CPSI[ED].  Returns FAIL on error, or a
    value suitable for splatting into the AIF field of the instruction.  */
 
@@ -4270,8 +4331,6 @@
 #define OP_RRw     051  /* ARM register, not the PC, optional trailing ! */
 
 #define OP_RL      052  /* Thumb low register */
-#define OP_RLlb    053  /* Thumb low register, leading [ */
-#define OP_RLtb    054  /* Thumb low register, trailing ] */
 #define OP_RLw	   055	/* Thumb low register, optional trailing ! */
 
 #define OP_CPSF    060  /* CPS flags */
@@ -4281,6 +4340,8 @@
 #define OP_VRSLST  064	/* VFP single-precision register list */
 #define OP_VRDLST  065	/* VFP double-precision register list */
 
+#define OP_TADDR   070	/* Thumb memory address expression */
+
 /* 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 */
@@ -4412,16 +4473,6 @@
 	    }
 	  break;
 
-	case OP_(RLlb):
-	  po_char_or_fail ('[');
-	  po_reg_or_fail (REG_TYPE_RN);
-	  break;
-
-	case OP_(RLtb):
-	  po_reg_or_fail (REG_TYPE_RN);
-	  po_char_or_fail (']');
-	  break;
-
 	  /* Immediates */
 	I0:
 	case OP_(I0):	 po_imm_or_fail (  0,      0, FALSE);	break;
@@ -4552,6 +4603,12 @@
 	  val = vfp_parse_reg_list (&str, &inst.operands[i].reg, 1);
 	  break;
 
+	  /* Addressing modes */
+	case OP_(TADDR):
+	  if (parse_thumb_address (&str, i))
+	    return FAIL;
+	  break;
+
 	default:
 	  as_fatal ("unhandled operand code %03o", *p);
 	}
@@ -4571,8 +4628,6 @@
 
 	case OP_(oRL):
 	case OP_(RL):
-	case OP_(RLlb):
-	case OP_(RLtb):
 	case OP_(RLw):
 	case OP_(oRL_iEX):
 	case OP_(RL_iEX):
@@ -6251,75 +6306,24 @@
 }
 
 static void
-thumb_load_store (char * str, int load_store, int size)
+do_t_ldst (char *str)
 {
-  int Rd, Rb, Ro = FAIL;
+  if (parse_operands (str, OPERANDS2(RL,TADDR)))
+    return;
 
-  if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
-      || skip_past_comma (&str) == FAIL)
+  if (!inst.operands[1].isreg) /* =expr pseudo */
     {
-      if (! inst.error)
-	inst.error = BAD_ARGS;
-      return;
-    }
-
-  if (*str == '[')
-    {
-      str++;
-      if ((Rb = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
-	return;
-
-      if (skip_past_comma (&str) != FAIL)
+      if (inst.instruction & THUMB_LOAD_BIT)
 	{
-	  if (is_immediate_prefix (*str))
-	    {
-	      str++;
-	      expression_or_fail (&inst.reloc.exp, &str);
-	    }
-	  else if ((Ro = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
-	    return;
-	}
-      else
-	{
-	  inst.reloc.exp.X_op = O_constant;
-	  inst.reloc.exp.X_add_number = 0;
-	}
-
-      if (*str != ']')
-	{
-	  inst.error = _("expected ']'");
-	  return;
-	}
-      str++;
-    }
-  else if (*str == '=')
-    {
-      if (load_store != THUMB_LOAD)
-	{
 	  inst.error = _("invalid pseudo operation");
 	  return;
 	}
 
-      /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op.  */
-      str++;
-
-      expression_or_fail (& inst.reloc.exp, & str);
-
-      end_of_line (str);
-
-      if (   inst.reloc.exp.X_op != O_constant
-	  && inst.reloc.exp.X_op != O_symbol)
-	{
-	  inst.error = "Constant expression expected";
-	  return;
-	}
-
       if (inst.reloc.exp.X_op == O_constant
 	  && ((inst.reloc.exp.X_add_number & ~0xFF) == 0))
 	{
 	  /* This can be done with a mov instruction.  */
-
-	  inst.instruction  = T_OPCODE_MOV_I8 | (Rd << 8);
+	  inst.instruction  = T_OPCODE_MOV_I8 | (inst.operands[0].reg << 8);
 	  inst.instruction |= inst.reloc.exp.X_add_number;
 	  return;
 	}
@@ -6327,123 +6331,67 @@
       /* Insert into literal pool.  */
       if (add_to_lit_pool () == FAIL)
 	{
-	  if (!inst.error)
-	    inst.error = "literal pool insertion failed";
+	  inst.error = _("literal pool insertion failed");
 	  return;
 	}
 
       inst.reloc.type   = BFD_RELOC_ARM_THUMB_OFFSET;
-      inst.reloc.pc_rel = 1;
-      inst.instruction  = T_OPCODE_LDR_PC | (Rd << 8);
-      /* Adjust ARM pipeline offset to Thumb.  */
-      inst.reloc.exp.X_add_number += 4;
-
-      return;
+      inst.reloc.exp.X_add_number += 4;  /* pipeline offset */
+      inst.operands[1].reg = REG_PC;
     }
-  else
-    {
-      expression_or_fail (&inst.reloc.exp, &str);
 
-      inst.instruction = T_OPCODE_LDR_PC | (Rd << 8);
-      inst.reloc.pc_rel = 1;
-      inst.reloc.exp.X_add_number -= 4; /* Pipeline offset.  */
-      inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
-      end_of_line (str);
-      return;
-    }
-
-  if (Rb == REG_PC || Rb == REG_SP)
+  if (inst.operands[1].reg == REG_PC || inst.operands[1].reg == REG_SP)
     {
-      if (size != THUMB_WORD)
+      if (inst.instruction & 0x0600)
 	{
 	  inst.error = _("byte or halfword not valid for base register");
 	  return;
 	}
-      else if (Rb == REG_PC && load_store != THUMB_LOAD)
+      else if (inst.operands[1].reg == REG_PC
+	       && !(inst.instruction & THUMB_LOAD_BIT))
 	{
 	  inst.error = _("r15 based store not allowed");
 	  return;
 	}
-      else if (Ro != FAIL)
+      else if (inst.operands[1].immisreg)
 	{
 	  inst.error = _("invalid base register for register offset");
 	  return;
 	}
 
-      if (Rb == REG_PC)
+      if (inst.operands[1].reg == REG_PC)
 	inst.instruction = T_OPCODE_LDR_PC;
-      else if (load_store == THUMB_LOAD)
+      else if (inst.instruction & THUMB_LOAD_BIT)
 	inst.instruction = T_OPCODE_LDR_SP;
       else
 	inst.instruction = T_OPCODE_STR_SP;
 
-      inst.instruction |= Rd << 8;
-      if (inst.reloc.exp.X_op == O_constant)
-	{
-	  unsigned offset = inst.reloc.exp.X_add_number;
-
-	  if (offset & ~0x3fc)
-	    {
-	      inst.error = _("invalid offset");
-	      return;
-	    }
-
-	  inst.instruction |= offset >> 2;
-	}
-      else
-	inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+      inst.instruction |= inst.operands[0].reg << 8;
     }
-  else if (Rb > 7)
+  else if (!inst.operands[1].immisreg)
     {
-      inst.error = _("invalid base register in load/store");
-      return;
-    }
-  else if (Ro == FAIL)
-    {
       /* Immediate offset.  */
-      if (size == THUMB_WORD)
-	inst.instruction = (load_store == THUMB_LOAD
-			    ? T_OPCODE_LDR_IW : T_OPCODE_STR_IW);
-      else if (size == THUMB_HALFWORD)
-	inst.instruction = (load_store == THUMB_LOAD
-			    ? T_OPCODE_LDR_IH : T_OPCODE_STR_IH);
-      else
-	inst.instruction = (load_store == THUMB_LOAD
-			    ? T_OPCODE_LDR_IB : T_OPCODE_STR_IB);
-
-      inst.instruction |= Rd | (Rb << 3);
-
-      if (inst.reloc.exp.X_op == O_constant)
+      switch (inst.instruction)
 	{
-	  unsigned offset = inst.reloc.exp.X_add_number;
-
-	  if (offset & ~(0x1f << size))
-	    {
-	      inst.error = _("invalid offset");
-	      return;
-	    }
-	  inst.instruction |= (offset >> size) << 6;
+	case T_OPCODE_STR_RW: inst.instruction = T_OPCODE_STR_IW; break;
+	case T_OPCODE_STR_RH: inst.instruction = T_OPCODE_STR_IH; break;
+	case T_OPCODE_STR_RB: inst.instruction = T_OPCODE_STR_IB; break;
+	case T_OPCODE_LDR_RW: inst.instruction = T_OPCODE_LDR_IW; break;
+	case T_OPCODE_LDR_RH: inst.instruction = T_OPCODE_LDR_IH; break;
+	case T_OPCODE_LDR_RB: inst.instruction = T_OPCODE_LDR_IB; break;
+	default: abort ();
 	}
-      else
-	inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+
+      inst.instruction |= inst.operands[0].reg;
+      inst.instruction |= inst.operands[1].reg << 3;
     }
   else
     {
-      /* Register offset.  */
-      if (size == THUMB_WORD)
-	inst.instruction = (load_store == THUMB_LOAD
-			    ? T_OPCODE_LDR_RW : T_OPCODE_STR_RW);
-      else if (size == THUMB_HALFWORD)
-	inst.instruction = (load_store == THUMB_LOAD
-			    ? T_OPCODE_LDR_RH : T_OPCODE_STR_RH);
-      else
-	inst.instruction = (load_store == THUMB_LOAD
-			    ? T_OPCODE_LDR_RB : T_OPCODE_STR_RB);
-
-      inst.instruction |= Rd | (Rb << 3) | (Ro << 6);
+      /* Register offset.  Opcode is already correct.  */
+      inst.instruction |= inst.operands[0].reg;
+      inst.instruction |= inst.operands[1].reg << 3;
+      inst.instruction |= inst.operands[1].imm << 6;
     }
-
-  end_of_line (str);
 }
 
 static void
@@ -6678,9 +6626,7 @@
   if (parse_operands (str, OPERANDS1(RR)))
     return;
 
-  /* This sets THUMB_H2 from the top bit of reg.  */
   inst.instruction |= (inst.operands[0].reg << 3);
-
   /* ??? FIXME: Should add a hacky reloc here if reg is REG_PC.  The reloc
      should cause the alignment to be checked once it is known.  This is
      because BX PC only works if the instruction is word aligned.  */
@@ -6741,32 +6687,19 @@
 }
 
 static void
-do_t_ldr (char * str)
-{
-  thumb_load_store (str, THUMB_LOAD, THUMB_WORD);
-}
-
-static void
-do_t_ldrb (char * str)
-{
-  thumb_load_store (str, THUMB_LOAD, THUMB_BYTE);
-}
-
-static void
-do_t_ldrh (char * str)
-{
-  thumb_load_store (str, THUMB_LOAD, THUMB_HALFWORD);
-}
-
-static void
 do_t_lds (char * str)
 {
-  if (parse_operands (str, OPERANDS3(RL,RLlb,RLtb)))
+  if (parse_operands (str, OPERANDS2(RL,TADDR)))
     return;
+  if (!inst.operands[1].isreg || !inst.operands[1].immisreg)
+    {
+      inst.error = _("invalid addressing mode");
+      return;
+    }
 
   inst.instruction |= inst.operands[0].reg;
   inst.instruction |= inst.operands[1].reg << 3;
-  inst.instruction |= inst.operands[2].reg << 6;
+  inst.instruction |= inst.operands[1].imm << 6;
 }
 
 static void
@@ -6819,24 +6752,6 @@
 }
 
 static void
-do_t_str (char * str)
-{
-  thumb_load_store (str, THUMB_STORE, THUMB_WORD);
-}
-
-static void
-do_t_strb (char * str)
-{
-  thumb_load_store (str, THUMB_STORE, THUMB_BYTE);
-}
-
-static void
-do_t_strh (char * str)
-{
-  thumb_load_store (str, THUMB_STORE, THUMB_HALFWORD);
-}
-
-static void
 do_t_swi (char * str)
 {
   if (parse_operands (str, OPERANDS1(EXP)))
@@ -9568,9 +9483,9 @@
   {"cmp",	T_OPCODE_CMP_HR,2,	ARM_EXT_V4T, do_t_mov_cmp},
   {"eor",	0x4040,		2,	ARM_EXT_V4T, do_t_arit},
   {"ldmia",	0xc800,		2,	ARM_EXT_V4T, do_t_ldmstm},
-  {"ldr",	0x0000,		2,	ARM_EXT_V4T, do_t_ldr},
-  {"ldrb",	0x0000,		2,	ARM_EXT_V4T, do_t_ldrb},
-  {"ldrh",	0x0000,		2,	ARM_EXT_V4T, do_t_ldrh},
+  {"ldr",	T_OPCODE_LDR_RW,2,	ARM_EXT_V4T, do_t_ldst},
+  {"ldrb",	T_OPCODE_LDR_RB,2,	ARM_EXT_V4T, do_t_ldst},
+  {"ldrh",	T_OPCODE_LDR_RH,2,	ARM_EXT_V4T, do_t_ldst},
   {"ldrsb",	0x5600,		2,	ARM_EXT_V4T, do_t_lds},
   {"ldrsh",	0x5e00,		2,	ARM_EXT_V4T, do_t_lds},
   {"ldsb",	0x5600,		2,	ARM_EXT_V4T, do_t_lds},
@@ -9587,9 +9502,9 @@
   {"ror",	0x41c0,		2,	ARM_EXT_V4T, do_t_arit},
   {"sbc",	0x4180,		2,	ARM_EXT_V4T, do_t_arit},
   {"stmia",	0xc000,		2,	ARM_EXT_V4T, do_t_ldmstm},
-  {"str",	0x0000,		2,	ARM_EXT_V4T, do_t_str},
-  {"strb",	0x0000,		2,	ARM_EXT_V4T, do_t_strb},
-  {"strh",	0x0000,		2,	ARM_EXT_V4T, do_t_strh},
+  {"str",	T_OPCODE_STR_RW,2,	ARM_EXT_V4T, do_t_ldst},
+  {"strb",	T_OPCODE_STR_RB,2,	ARM_EXT_V4T, do_t_ldst},
+  {"strh",	T_OPCODE_STR_RH,2,	ARM_EXT_V4T, do_t_ldst},
   {"swi",	0xdf00,		2,	ARM_EXT_V4T, do_t_swi},
   {"sub",	0x8000,		2,	ARM_EXT_V4T, do_t_add_sub},
   {"tst",	T_OPCODE_TST,	2,	ARM_EXT_V4T, do_t_arit},
@@ -10839,11 +10754,11 @@
 	     aligned (since the final address produced must be, and
 	     we can only describe word-aligned immediate offsets).  */
 
-	  if ((fixP->fx_frag->fr_address + fixP->fx_where + value) & 3)
+	  if (value & 3)
 	    as_bad_where (fixP->fx_file, fixP->fx_line,
-			  _("invalid offset, target not word aligned (0x%08X)"),
-			  (unsigned int) (fixP->fx_frag->fr_address
-					  + fixP->fx_where + value));
+			  _("invalid offset, target not word aligned (0x%08lX)"),
+			  (((unsigned int) fixP->fx_frag->fr_address
+			    + (unsigned int) fixP->fx_where) & ~3) + value);
 
 	  if ((value + 2) & ~0x3fe)
 	    as_bad_where (fixP->fx_file, fixP->fx_line,

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