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


Refactor shift operand handling for use from parse_operands, and defer
more constant handling to md_apply_fix3.  Unfortunately, this means
that we can no longer test the out-of-range diagnostic for that
constant in the same file as other diagnostics, because if the first
assembly pass produces errors, we don't bother running fixups.  This
isn't the only occurence of this problem in this patch series, and I
have not yet written replacement test cases for them.

It may not be obvious why the fields of the operands structure need to
be unsigned - it's so that they are not sign-extended, and therefore
do not clobber inst.instruction when or'ed in.  This hasn't been a
problem before because the values being stored in all prior operand
fields didn't ever get big enough to appear to be negative.
shift_kind, however, does.

zw

	* config/tc-arm.c (struct arm_it): Make all fields of operands
	unsigned.  Add shift_kind.
	(struct asm_shift_properties, shift_properties): Delete.
	(rotate_left): Pull out of validate_immediate.
	(enum asm_shift_index): Rename enum shift_kind, move next to
	decode_shift.
	(struct asm_shift_name): Move next to decode_shift.  Make second
	argument an enum shift_kind.
	(shift_names): Move next to psr table.  Reformat.  Change values
	to match above changes.
	(NO_SHIFT_RESTRICT, SHIFT_IMMEDIATE, SHIFT_LSL_OR_ASR_IMMEDIATE)
	(SHIFT_ASR_IMMEDIATE, SHIFT_LSL_IMMEDIATE): Change from macros into
	enumerators of a new enum type, parse_shift_mode, also defined next
	to decode_shift.
	(decode_shift): Rename parse_shift; restructure for use from
	parse_operands; defer validation of constants to md_apply_fix3.
	(data_op2): Rename parse_shifter_operand; restructure for use from
	parse_operands; defer validation of constants to md_apply_fix3.
	(encode_shift_arm, encode_shifter_operand_arm): New functions.
	(ldst_extend): Use inst.operands[5] as a scratchpad when calling
	parse_shift/encode_shift -- temporary kludge until core load/store
	is converted to parse_operands.
	(OP_SHOP, OP_oSHll, OP_oSHar, OP_oSHllar): New operand parse codes.
	(parse_operands): Handle them.  Add forgotten #undef of po_reg_or_goto.
	(do_arit, do_cmp, do_mov, do_pkhbt, do_pkhtb, do_ssat, do_usat):
	Use parse_operands.
	(do_pkh_core, do_sat): Delete.
	(negate_data_op): Move next to md_apply_fix3, sole remaining caller.

	* testsuite/gas/arm/armv1-bad.s: Remove test for out-of-range
	immediate; this must now go in its own file.
	* testsuite/gas/arm/armv1-bad.l: Update to match.

===================================================================
Index: gas/testsuite/gas/arm/armv1-bad.l
--- gas/testsuite/gas/arm/armv1-bad.l	(revision 49)
+++ gas/testsuite/gas/arm/armv1-bad.l	(revision 50)
@@ -3,10 +3,9 @@
 [^:]*:5: Error: bad expression -- `ldr r0,{r1}'
 [^:]*:6: Error: address offset too large -- `ldr r0,\[r1,#4096\]'
 [^:]*:7: Error: address offset too large -- `ldr r0,\[r1,#-4096\]'
-[^:]*:8: Error: invalid constant -- `mov r0,#0x1ff'
-[^:]*:9: Error: bad instruction `cmpl r0,r0'
-[^:]*:10: Error: selected processor does not support `strh r0,\[r1\]'
-[^:]*:11: Warning: writeback of base register is UNPREDICTABLE
-[^:]*:12: Warning: writeback of base register when in register list is UNPREDICTABLE
-[^:]*:13: Warning: writeback of base register is UNPREDICTABLE
-[^:]*:15: Warning: if writeback register is in list, it must be the lowest reg in the list
+[^:]*:8: Error: bad instruction `cmpl r0,r0'
+[^:]*:9: Error: selected processor does not support `strh r0,\[r1\]'
+[^:]*:10: Warning: writeback of base register is UNPREDICTABLE
+[^:]*:11: Warning: writeback of base register when in register list is UNPREDICTABLE
+[^:]*:12: Warning: writeback of base register is UNPREDICTABLE
+[^:]*:14: Warning: if writeback register is in list, it must be the lowest reg in the list
===================================================================
Index: gas/testsuite/gas/arm/armv1-bad.s
--- gas/testsuite/gas/arm/armv1-bad.s	(revision 49)
+++ gas/testsuite/gas/arm/armv1-bad.s	(revision 50)
@@ -5,7 +5,6 @@
 	ldr	r0, {r1}
 	ldr	r0, [r1, #4096]
 	ldr	r0, [r1, #-4096]
-	mov	r0, #0x1ff
 	cmpl	r0, r0
 	strh	r0, [r1]
 	ldmfa	r4!, {r8, r9}^
===================================================================
Index: gas/config/tc-arm.c
--- gas/config/tc-arm.c	(revision 49)
+++ gas/config/tc-arm.c	(revision 50)
@@ -193,80 +193,22 @@
 
   struct
   {
-    int reg;
-    int imm;
-    int present    : 1;  /* operand present */
-    int isreg	   : 1;  /* operand was a register */
-    int immisreg   : 1;  /* .imm field is a second register */
-    int hasreloc   : 1;  /* operand has relocation suffix */
-    int writeback  : 1;  /* operand has trailing ! */
-    int preind     : 1;  /* preindexed address */
-    int postind    : 1;  /* postindexed address */
-    int negative   : 1;  /* index register was negated */
+    unsigned reg;
+    unsigned imm;
+    unsigned present    : 1;  /* operand present */
+    unsigned isreg	   : 1;  /* operand was a register */
+    unsigned immisreg   : 1;  /* .imm field is a second register */
+    unsigned hasreloc   : 1;  /* operand has relocation suffix */
+    unsigned writeback  : 1;  /* operand has trailing ! */
+    unsigned preind     : 1;  /* preindexed address */
+    unsigned postind    : 1;  /* postindexed address */
+    unsigned negative   : 1;  /* index register was negated */
+    unsigned shift_kind : 2;  /* shift operation (enum shift_kind, not RRX) */
   } operands[6];
 };
 
 static struct arm_it inst;
 
-enum asm_shift_index
-{
-  SHIFT_LSL = 0,
-  SHIFT_LSR,
-  SHIFT_ASR,
-  SHIFT_ROR,
-  SHIFT_RRX
-};
-
-struct asm_shift_properties
-{
-  enum asm_shift_index index;
-  unsigned long        bit_field;
-  unsigned int         allows_0  : 1;
-  unsigned int         allows_32 : 1;
-};
-
-static const struct asm_shift_properties shift_properties [] =
-{
-  { SHIFT_LSL, 0,    1, 0},
-  { SHIFT_LSR, 0x20, 0, 1},
-  { SHIFT_ASR, 0x40, 0, 1},
-  { SHIFT_ROR, 0x60, 0, 0},
-  { SHIFT_RRX, 0x60, 0, 0}
-};
-
-struct asm_shift_name
-{
-  const char *                        name;
-  const struct asm_shift_properties * properties;
-};
-
-static const struct asm_shift_name shift_names [] =
-{
-  { "asl", shift_properties + SHIFT_LSL },
-  { "lsl", shift_properties + SHIFT_LSL },
-  { "lsr", shift_properties + SHIFT_LSR },
-  { "asr", shift_properties + SHIFT_ASR },
-  { "ror", shift_properties + SHIFT_ROR },
-  { "rrx", shift_properties + SHIFT_RRX },
-  { "ASL", shift_properties + SHIFT_LSL },
-  { "LSL", shift_properties + SHIFT_LSL },
-  { "LSR", shift_properties + SHIFT_LSR },
-  { "ASR", shift_properties + SHIFT_ASR },
-  { "ROR", shift_properties + SHIFT_ROR },
-  { "RRX", shift_properties + SHIFT_RRX }
-};
-
-/* Any kind of shift is accepted.  */
-#define NO_SHIFT_RESTRICT 1
-/* The shift operand must be an immediate value, not a register.  */
-#define SHIFT_IMMEDIATE	  0
-/* The shift must be LSL or ASR and the operand must be an immediate.  */
-#define SHIFT_LSL_OR_ASR_IMMEDIATE 2
-/* The shift must be ASR and the operand must be an immediate.  */
-#define SHIFT_ASR_IMMEDIATE 3
-/* The shift must be LSL and the operand must be an immediate.  */
-#define SHIFT_LSL_IMMEDIATE 4
-
 #define NUM_FLOAT_VALS 8
 
 const char * fp_const[] =
@@ -1042,6 +984,8 @@
   return SUCCESS;
 }
 
+#define rotate_left(v, n) (v << n | v >> (32 - n))
+
 /* Check that an immediate is valid.
    If so, convert it to the right format.  */
 
@@ -1051,8 +995,6 @@
   unsigned int a;
   unsigned int i;
 
-#define rotate_left(v, n) (v << n | v >> (32 - n))
-
   for (i = 0; i < 32; i += 2)
     if ((a = rotate_left (val, i)) <= 0xff)
       return a | (i << 7); /* 12-bit pack: [shift-cnt,const].  */
@@ -1512,17 +1454,46 @@
   return count;
 }
 
-/* Non-register data operands: shifts, reg-or-immediate operands,
-   relocation tags, load/store expressions.  */
+/* Shift operands.  */
+enum shift_kind
+{
+  SHIFT_LSL, SHIFT_LSR, SHIFT_ASR, SHIFT_ROR, SHIFT_RRX
+};
 
-/* KIND indicates what kind of shifts are accepted.  */
+struct asm_shift_name
+{
+  const char      *name;
+  enum shift_kind  kind;
+};
 
+/* Third argument to parse_shift.  */
+enum parse_shift_mode
+{
+  NO_SHIFT_RESTRICT,		/* Any kind of shift is accepted.  */
+  SHIFT_IMMEDIATE,    		/* Shift operand must be an immediate.  */
+  SHIFT_LSL_OR_ASR_IMMEDIATE,	/* Shift must be LSL or ASR immediate.  */
+  SHIFT_ASR_IMMEDIATE,		/* Shift must be ASR immediate.  */
+  SHIFT_LSL_IMMEDIATE,		/* Shift must be LSL immediate.  */
+};
+
+/* Parse a <shift> specifier on an ARM data processing instruction.
+   This has three forms:
+
+     (LSL|LSR|ASL|ASR|ROR) Rs
+     (LSL|LSR|ASL|ASR|ROR) #imm
+     RRX
+
+   Note that ASL is assimilated to LSL in the instruction encoding, and
+   RRX to ROR #0 (which cannot be written as such).  */
+
 static int
-decode_shift (char **str, int kind)
+parse_shift (char **str, int i, enum parse_shift_mode mode)
 {
-  const struct asm_shift_name *shift;
+  const struct asm_shift_name *shift_name;
+  enum shift_kind shift;
+  char *s = *str;
+  char *p = s;
   int reg;
-  char *p;
 
   for (p = *str; ISALPHA (*p); p++)
     ;
@@ -1533,267 +1504,174 @@
       return FAIL;
     }
 
-  shift = hash_find_n (arm_shift_hsh, *str, p - *str);
+  shift_name = hash_find_n (arm_shift_hsh, *str, p - *str);
 
-  if (shift == NULL)
+  if (shift_name == NULL)
     {
       inst.error = _("shift expression expected");
       return FAIL;
     }
 
-  assert (shift->properties->index ==
-	  shift_properties[shift->properties->index].index);
+  shift = shift_name->kind;
 
-  if (kind == SHIFT_LSL_OR_ASR_IMMEDIATE
-      && shift->properties->index != SHIFT_LSL
-      && shift->properties->index != SHIFT_ASR)
+  switch (mode)
     {
-      inst.error = _("'LSL' or 'ASR' required");
-      return FAIL;
+    case NO_SHIFT_RESTRICT:
+    case SHIFT_IMMEDIATE:   break;
+
+    case SHIFT_LSL_OR_ASR_IMMEDIATE:
+      if (shift != SHIFT_LSL && shift != SHIFT_ASR)
+	{
+	  inst.error = _("'LSL' or 'ASR' required");
+	  return FAIL;
+	}
+      break;
+
+    case SHIFT_LSL_IMMEDIATE:
+      if (shift != SHIFT_LSL)
+	{
+	  inst.error = _("'LSL' required");
+	  return FAIL;
+	}
+      break;
+
+    case SHIFT_ASR_IMMEDIATE:
+      if (shift != SHIFT_ASR)
+	{
+	  inst.error = _("'ASR' required");
+	  return FAIL;
+	}
+      break;
+
+    default: abort ();
     }
-  else if (kind == SHIFT_LSL_IMMEDIATE
-	   && shift->properties->index != SHIFT_LSL)
+      
+  if (shift == SHIFT_RRX)
     {
-      inst.error = _("'LSL' required");
-      return FAIL;
-    }
-  else if (kind == SHIFT_ASR_IMMEDIATE
-	   && shift->properties->index != SHIFT_ASR)
-    {
-      inst.error = _("'ASR' required");
-      return FAIL;
-    }
-
-  if (shift->properties->index == SHIFT_RRX)
-    {
+      inst.operands[i].shift_kind = SHIFT_ROR;
       *str = p;
-      inst.instruction |= shift->properties->bit_field;
       return SUCCESS;
     }
 
+  /* Whitespace can appear here if the next thing is a bare digit.  */
   skip_whitespace (p);
 
-  if (kind == NO_SHIFT_RESTRICT
+  if (mode == NO_SHIFT_RESTRICT
       && (reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
     {
-      inst.instruction |= shift->properties->bit_field | SHIFT_BY_REG;
-      inst.instruction |= reg << 8;
+      inst.operands[i].imm = reg;
+      inst.operands[i].immisreg = 1;
+      inst.operands[i].shift_kind = shift;
       *str = p;
       return SUCCESS;
     }
   else if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
     return FAIL;
 
-  /* Validate some simple #expressions.  */
-  if (inst.reloc.exp.X_op == O_constant)
-    {
-      unsigned num = inst.reloc.exp.X_add_number;
-
-      /* Reject operations greater than 32.  */
-      if (num > 32
-	  /* Reject a shift of 0 unless the mode allows it.  */
-	  || (num == 0 && shift->properties->allows_0 == 0)
-	  /* Reject a shift of 32 unless the mode allows it.  */
-	  || (num == 32 && shift->properties->allows_32 == 0)
-	  )
-	{
-	  /* As a special case we allow a shift of zero for
-	     modes that do not support it to be recoded as an
-	     logical shift left of zero (ie nothing).  We warn
-	     about this though.  */
-	  if (num == 0)
-	    {
-	      as_warn (_("shift of 0 ignored."));
-	      shift = & shift_names[0];
-	      assert (shift->properties->index == SHIFT_LSL);
-	    }
-	  else
-	    {
-	      inst.error = _("invalid immediate shift");
-	      return FAIL;
-	    }
-	}
-
-      /* Shifts of 32 are encoded as 0, for those shifts that
-	 support it.  */
-      if (num == 32)
-	num = 0;
-
-      inst.instruction |= (num << 7) | shift->properties->bit_field;
-    }
-  else
-    {
-      inst.reloc.type   = BFD_RELOC_ARM_SHIFT_IMM;
-      inst.reloc.pc_rel = 0;
-      inst.instruction |= shift->properties->bit_field;
-    }
-
-  * str = p;
+  inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
+  inst.reloc.pc_rel = 0;
+  inst.operands[i].shift_kind = shift;
+  *str = p;
   return SUCCESS;
 }
 
-/* Do those data_ops which can take a negative immediate constant
-   by altering the instruction.  A bit of a hack really.
-        MOV <-> MVN
-        AND <-> BIC
-        ADC <-> SBC
-        by inverting the second operand, and
-        ADD <-> SUB
-        CMP <-> CMN
-        by negating the second operand.  */
-
-static int
-negate_data_op (unsigned long * instruction,
-		unsigned long   value)
+/* Encode a <shift> in an ARM-format instruction.  The immediate,
+   if any, is handled by md_apply_fix3.  */
+static void
+encode_shift_arm (int i)
 {
-  int op, new_inst;
-  unsigned long negated, inverted;
-
-  negated = validate_immediate (-value);
-  inverted = validate_immediate (~value);
-
-  op = (*instruction >> DATA_OP_SHIFT) & 0xf;
-  switch (op)
+  inst.instruction |= inst.operands[i].shift_kind << 5;
+  if (inst.operands[i].immisreg)
     {
-      /* First negates.  */
-    case OPCODE_SUB:             /* ADD <-> SUB  */
-      new_inst = OPCODE_ADD;
-      value = negated;
-      break;
+      inst.instruction |= SHIFT_BY_REG;
+      inst.instruction |= inst.operands[i].imm << 8;
+    }
+}
 
-    case OPCODE_ADD:
-      new_inst = OPCODE_SUB;
-      value = negated;
-      break;
+/* Non-register data operands: reg-or-immediate operands,
+   relocation tags, load/store expressions.  */
 
-    case OPCODE_CMP:             /* CMP <-> CMN  */
-      new_inst = OPCODE_CMN;
-      value = negated;
-      break;
 
-    case OPCODE_CMN:
-      new_inst = OPCODE_CMP;
-      value = negated;
-      break;
 
-      /* Now Inverted ops.  */
-    case OPCODE_MOV:             /* MOV <-> MVN  */
-      new_inst = OPCODE_MVN;
-      value = inverted;
-      break;
+/* Parse a <shifter_operand> for an ARM data processing instruction:
 
-    case OPCODE_MVN:
-      new_inst = OPCODE_MOV;
-      value = inverted;
-      break;
+      #<immediate>
+      #<immediate>, <rotate>
+      <Rm>
+      <Rm>, <shift>
 
-    case OPCODE_AND:             /* AND <-> BIC  */
-      new_inst = OPCODE_BIC;
-      value = inverted;
-      break;
+   where <shift> is defined by parse_shift above, and <rotate> is a
+   multiple of 2 between 0 and 30.  Validation of immediate operands
+   is deferred to md_apply_fix3.  */
 
-    case OPCODE_BIC:
-      new_inst = OPCODE_AND;
-      value = inverted;
-      break;
-
-    case OPCODE_ADC:              /* ADC <-> SBC  */
-      new_inst = OPCODE_SBC;
-      value = inverted;
-      break;
-
-    case OPCODE_SBC:
-      new_inst = OPCODE_ADC;
-      value = inverted;
-      break;
-
-      /* We cannot do anything.  */
-    default:
-      return FAIL;
-    }
-
-  if (value == (unsigned) FAIL)
-    return FAIL;
-
-  *instruction &= OPCODE_MASK;
-  *instruction |= new_inst << DATA_OP_SHIFT;
-  return value;
-}
-
 static int
-data_op2 (char ** str)
+parse_shifter_operand (char **str, int i)
 {
   int value;
   expressionS expr;
 
   if ((value = arm_reg_parse (str, REG_TYPE_RN)) != FAIL)
     {
-      inst.instruction |= value;
-      if (skip_past_comma (str) == SUCCESS)
-	/* Shift operation on register.  */
-	return decode_shift (str, NO_SHIFT_RESTRICT);
+      inst.operands[i].reg = value;
+      inst.operands[i].isreg = 1;
 
-      return SUCCESS;
+      if (skip_past_comma (str) == FAIL)
+	return SUCCESS;
+
+      /* Shift operation on register.  */
+      return parse_shift (str, i, NO_SHIFT_RESTRICT);
     }
-  else if (my_get_expression (&inst.reloc.exp, str, GE_IMM_PREFIX))
+
+  if (my_get_expression (&inst.reloc.exp, str, GE_IMM_PREFIX))
     return FAIL;
 
-  if (inst.reloc.exp.X_add_symbol)
+  if (skip_past_comma (str) == SUCCESS)
     {
-      inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
-      inst.reloc.pc_rel = 0;
-    }
-  else
-    {
-      if (skip_past_comma (str) == SUCCESS)
-	{
-	  /* #x, y -- ie explicit rotation by Y.  */
-	  if (my_get_expression (&expr, str, GE_NO_PREFIX))
-	    return FAIL;
+      /* #x, y -- ie explicit rotation by Y.  */
+      if (my_get_expression (&expr, str, GE_NO_PREFIX))
+	return FAIL;
 
-	  if (expr.X_op != O_constant)
-	    {
-	      inst.error = _("constant expression expected");
-	      return FAIL;
-	    }
-
-	  /* Rotate must be a multiple of 2.  */
-	  if (((unsigned) expr.X_add_number) > 30
-	      || (expr.X_add_number & 1) != 0
-	      || ((unsigned) inst.reloc.exp.X_add_number) > 255)
-	    {
-	      inst.error = _("invalid constant");
-	      return FAIL;
-	    }
-	  inst.instruction |= INST_IMMEDIATE;
-	  inst.instruction |= inst.reloc.exp.X_add_number;
-	  inst.instruction |= expr.X_add_number << 7;
-	  return SUCCESS;
+      if (expr.X_op != O_constant || inst.reloc.exp.X_op != O_constant)
+	{
+	  inst.error = _("constant expression expected");
+	  return FAIL;
 	}
 
-      /* Implicit rotation, select a suitable one.  */
-      value = validate_immediate (inst.reloc.exp.X_add_number);
-
-      if (value == FAIL)
+      value = expr.X_add_number;
+      if (value < 0 || value > 30 || value % 2 != 0)
 	{
-	  /* Can't be done.  Perhaps the code reads something like
-	     "add Rd, Rn, #-n", where "sub Rd, Rn, #n" would be OK.  */
-	  if ((value = negate_data_op (&inst.instruction,
-				       inst.reloc.exp.X_add_number))
-	      == FAIL)
-	    {
-	      inst.error = _("invalid constant");
-	      return FAIL;
-	    }
+	  inst.error = _("invalid rotation");
+	  return FAIL;
 	}
+      if (inst.reloc.exp.X_add_number < 0 || inst.reloc.exp.X_add_number > 255)
+	{
+	  inst.error = _("invalid constant");
+	  return FAIL;
+	}
 
-      inst.instruction |= value;
+      /* Convert to decoded value.  md_apply_fix3 will put it back.  */
+      inst.reloc.exp.X_add_number
+	= (((inst.reloc.exp.X_add_number << (32 - value))
+	    | (inst.reloc.exp.X_add_number >> value)) & 0xffffffff);
     }
 
-  inst.instruction |= INST_IMMEDIATE;
+  inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+  inst.reloc.pc_rel = 0;
   return SUCCESS;
 }
 
+static void
+encode_shifter_operand_arm (int i)
+{
+  if (inst.operands[i].isreg)
+    {
+      inst.instruction |= inst.operands[i].reg << 0;
+      encode_shift_arm (i);
+    }
+  else
+    inst.instruction |= INST_IMMEDIATE;
+}
+
 /* Parse an explicit relocation suffix on an expression.  This is
    either nothing, or a word in parentheses.  Note that if !OBJ_ELF,
    arm_reloc_hsh contains no entries, so this function can only
@@ -1875,8 +1753,11 @@
 
       inst.instruction |= add | OFFSET_REG;
       if (skip_past_comma (str) == SUCCESS)
-	return decode_shift (str, SHIFT_IMMEDIATE);
-
+	{
+	  if (parse_shift (str, 5, SHIFT_IMMEDIATE)) /* XXX Kludge */
+	    return FAIL;
+	  encode_shift_arm (5);
+	}
       return SUCCESS;
     }
 }
@@ -2109,7 +1990,8 @@
 static int
 parse_cp_address (char **str, int i)
 {
-  char  *p = *str;
+  char *p = *str;
+  int reg;
 
   if (skip_past_char (&p, '[') == FAIL)
     { /* bare address - translate to PC-relative offset */
@@ -2124,8 +2006,9 @@
       return SUCCESS;
     }
 
-  if ((inst.operands[i].reg = reg_required_here (&p, -1, REG_TYPE_RN)) == FAIL)
+  if ((reg = reg_required_here (&p, -1, REG_TYPE_RN)) == FAIL)
     return FAIL;
+  inst.operands[i].reg = reg;
 
   if (skip_past_char (&p, ']') == SUCCESS)
     {
@@ -4162,6 +4045,8 @@
 
 #define OP_TADDR   070	/* Thumb memory address expression */
 #define OP_CADDR   071  /* Co-processor memory address expression */
+#define OP_ADDR    072  /* ARM memory address expression (mode 2 or 3) */
+#define OP_SHOP	   073  /* shifter_operand */
 
 /* This-or-that operands.  All have bit 7 set.  */
 #define OP_RR_EX   100  /* ARM register or expression */
@@ -4181,6 +4066,9 @@
 
 #define OP_oROR    210  /* optional rotate right 0/8/16/24 */
 #define OP_oRL     211  /* optional Thumb low register */
+#define OP_oSHll   212	/* bare LSL immediate */
+#define OP_oSHar   213	/* bare ASR immediate */
+#define OP_oSHllar 214	/* bare LSL or ASR immediate */
 
 #define OP_oRL_iEX 300  /* optional Thumb low reg or expression */
 #define OP_oRR_iEX 301  /* optional ARM reg or expression */
@@ -4450,6 +4338,26 @@
 	    return FAIL;
 	  break;
 
+	case OP_(SHOP):
+	  if (parse_shifter_operand (&str, i))
+	    return FAIL;
+	  break;
+
+	case OP_(oSHll):
+	  if (parse_shift (&str, i, SHIFT_LSL_IMMEDIATE))
+	    return FAIL;
+	  break;
+
+	case OP_(oSHar):
+	  if (parse_shift (&str, i, SHIFT_ASR_IMMEDIATE))
+	    return FAIL;
+	  break;
+
+	case OP_(oSHllar):
+	  if (parse_shift (&str, i, SHIFT_LSL_OR_ASR_IMMEDIATE))
+	    return FAIL;
+	  break;
+
 	default:
 	  as_fatal ("unhandled operand code %03o", *p);
 	}
@@ -4517,6 +4425,7 @@
 
 #undef po_char_or_fail
 #undef po_reg_or_fail
+#undef po_reg_or_goto
 #undef po_imm_or_fail
 }
 
@@ -4607,19 +4516,11 @@
 static void
 do_arit (char * str)
 {
-  reg_or_fail (&str, 12, REG_TYPE_RN);
-  comma_or_fail (&str);
-  reg_or_fail (&str, 16, REG_TYPE_RN);
-  comma_or_fail (&str);
-
-  if (data_op2 (&str) == FAIL)
-    {
-      if (!inst.error)
-	inst.error = BAD_ARGS;
-      return;
-    }
-
-  end_of_line (str);
+  if (parse_operands (str, OPERANDS3(RR,RR,SHOP)))
+    return;
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
+  encode_shifter_operand_arm (2);
 }
 
 static void
@@ -4837,17 +4738,10 @@
 static void
 do_cmp (char * str)
 {
-  reg_or_fail (&str, 16, REG_TYPE_RN);
-  comma_or_fail (&str);
-
-  if (data_op2 (&str) == FAIL)
-    {
-      if (!inst.error)
-	inst.error = BAD_ARGS;
-      return;
-    }
-
-  end_of_line (str);
+  if (parse_operands (str, OPERANDS2(RR,SHOP)))
+    return;
+  inst.instruction |= inst.operands[0].reg << 16;
+  encode_shifter_operand_arm (1);
 }
 
 /* Transfer between coprocessor and ARM registers.
@@ -5525,20 +5419,13 @@
 static void
 do_mov (char * str)
 {
-  reg_or_fail (&str, 12, REG_TYPE_RN);
-  comma_or_fail (&str);
+  if (parse_operands (str, OPERANDS2(RR,SHOP)))
+    return;
 
-  if (data_op2 (&str) == FAIL)
-    {
-      if (!inst.error)
-	inst.error = BAD_ARGS;
-      return;
-    }
-
-  end_of_line (str);
+  inst.instruction |= inst.operands[0].reg << 12;
+  encode_shifter_operand_arm (1);
 }
 
-
 /* ARM V6T2 16-bit immediate register load: MOV[WT]{cond} Rd, #<imm16>.  */
 static void
 do_mov16 (char *str)
@@ -5642,38 +5529,6 @@
     }
 }
 
-static void
-do_pkh_core (char * str, int shift)
-{
-  int rm, rn;
-
-  reg_nonpc_or_fail (&str, 12);
-  comma_or_fail (&str);
-
-  note_reg_nonpc_or_fail (rn, &str, 16);
-  comma_or_fail (&str);
-
-  note_reg_nonpc_or_fail (rm, &str, 0);
-
-
-  /* Check for optional shift immediate constant.  */
-  if (skip_past_comma (&str) == FAIL)
-    {
-      if (shift == SHIFT_ASR_IMMEDIATE)
-	{
-	  /* If the shift specifier is ommited, turn the instruction
-	     into pkhbt rd, rm, rn.  First, switch the instruction
-	     code, and clear the rn and rm fields.  */
-	  inst.instruction &= 0xfff0f010;
-	  /* Now, re-encode the registers.  */
-	  inst.instruction |= (rm << 16) | rn;
-	}
-      return;
-    }
-
-  decode_shift (&str, shift);
-}
-
 /* ARM V6 Pack Halfword Bottom Top instruction (argument parse).
    PKHBT {<cond>} <Rd>, <Rn>, <Rm> {, LSL #<shift_imm>}
    Condition defaults to COND_ALWAYS.
@@ -5682,7 +5537,13 @@
 static void
 do_pkhbt (char * str)
 {
-  do_pkh_core (str, SHIFT_LSL_IMMEDIATE);
+  if (parse_operands (str, OPERANDS4(RRnpc,RRnpc,RRnpc,oSHll)))
+    return;
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= inst.operands[2].reg << 0;
+  if (inst.operands[3].present)
+    encode_shift_arm (3);
 }
 
 /* ARM V6 PKHTB (Argument Parse).  */
@@ -5690,7 +5551,25 @@
 static void
 do_pkhtb (char * str)
 {
-  do_pkh_core (str, SHIFT_ASR_IMMEDIATE);
+  if (parse_operands (str, OPERANDS4(RRnpc,RRnpc,RRnpc,oSHar)))
+    return;
+
+  if (!inst.operands[3].present)
+    {
+      /* If the shift specifier is omitted, turn the instruction
+	 into pkhbt rd, rm, rn. */
+      inst.instruction &= 0xfff00010;
+      inst.instruction |= inst.operands[0].reg << 12;
+      inst.instruction |= inst.operands[1].reg << 0;
+      inst.instruction |= inst.operands[2].reg << 16;
+    }
+  else
+    {
+      inst.instruction |= inst.operands[0].reg << 12;
+      inst.instruction |= inst.operands[1].reg << 16;
+      inst.instruction |= inst.operands[2].reg << 0;
+      encode_shift_arm (3);
+    }
 }
 
 /* ARMv5TE: Preload-Cache
@@ -5839,35 +5718,20 @@
     inst.instruction |= WRITE_BACK;
 }
 
-static void
-do_sat (char *str, int bias)
-{
-  int val;
-
-  /* Parse <Rd>, field.  */
-  reg_nonpc_or_fail (&str, 12);
-  comma_or_fail (&str);
-
-  /* Parse #<immed>,  field.  */
-  immediate_or_fail (&str, &val, 0 - bias, 31 - bias, FALSE);
-  comma_or_fail (&str);
-  inst.instruction |= (val + bias) << 16;
-
-  /* Parse <Rm> field.  */
-  reg_nonpc_or_fail (&str, 0);
-
-  if (skip_past_comma (&str) == SUCCESS)
-    decode_shift (&str, SHIFT_LSL_OR_ASR_IMMEDIATE);
-
-  end_of_line (str);
-}
-
 /* ARM V6 ssat (argument parse).  */
 
 static void
 do_ssat (char * str)
 {
-  do_sat (str, /*bias=*/-1);
+  if (parse_operands (str, OPERANDS4(RRnpc,I32,RRnpc,oSHllar)))
+    return;
+
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= (inst.operands[1].imm - 1) << 16;
+  inst.instruction |= inst.operands[2].reg << 0;
+
+  if (inst.operands[3].present)
+    encode_shift_arm (3);
 }
 
 /* ARM V6 usat (argument parse).  */
@@ -5875,7 +5739,15 @@
 static void
 do_usat (char * str)
 {
-  do_sat (str, /*bias=*/0);
+  if (parse_operands (str, OPERANDS4(RRnpc,I31,RRnpc,oSHllar)))
+    return;
+
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].imm << 16;
+  inst.instruction |= inst.operands[2].reg << 0;
+
+  if (inst.operands[3].present)
+    encode_shift_arm (3);
 }
 
 /* ARM V6 ssat16 (argument parse).  */
@@ -8080,6 +7952,18 @@
   {"cxsf", PSR_c | PSR_x | PSR_s | PSR_f},
 };
 
+/* Table of all shift-in-operand names.  */
+static const struct asm_shift_name shift_names [] =
+{
+  { "asl", SHIFT_LSL },  { "ASL", SHIFT_LSL },
+  { "lsl", SHIFT_LSL },  { "LSL", SHIFT_LSL },
+  { "lsr", SHIFT_LSR },  { "LSR", SHIFT_LSR },
+  { "asr", SHIFT_ASR },  { "ASR", SHIFT_ASR },
+  { "ror", SHIFT_ROR },  { "ROR", SHIFT_ROR },
+  { "rrx", SHIFT_RRX },  { "RRX", SHIFT_RRX } 
+};
+
+/* Table of all explicit relocation names.  */
 static struct reloc_entry reloc_names[] =
 {
 #ifdef OBJ_ELF
@@ -9962,6 +9846,95 @@
   return 0;
 }
 
+/* Subroutine of md_apply_fix3.  Do those data_ops which can take a
+   negative immediate constant by altering the instruction.  A bit of
+   a hack really.
+        MOV <-> MVN
+        AND <-> BIC
+        ADC <-> SBC
+        by inverting the second operand, and
+        ADD <-> SUB
+        CMP <-> CMN
+        by negating the second operand.  */
+
+static int
+negate_data_op (unsigned long * instruction,
+		unsigned long   value)
+{
+  int op, new_inst;
+  unsigned long negated, inverted;
+
+  negated = validate_immediate (-value);
+  inverted = validate_immediate (~value);
+
+  op = (*instruction >> DATA_OP_SHIFT) & 0xf;
+  switch (op)
+    {
+      /* First negates.  */
+    case OPCODE_SUB:             /* ADD <-> SUB  */
+      new_inst = OPCODE_ADD;
+      value = negated;
+      break;
+
+    case OPCODE_ADD:
+      new_inst = OPCODE_SUB;
+      value = negated;
+      break;
+
+    case OPCODE_CMP:             /* CMP <-> CMN  */
+      new_inst = OPCODE_CMN;
+      value = negated;
+      break;
+
+    case OPCODE_CMN:
+      new_inst = OPCODE_CMP;
+      value = negated;
+      break;
+
+      /* Now Inverted ops.  */
+    case OPCODE_MOV:             /* MOV <-> MVN  */
+      new_inst = OPCODE_MVN;
+      value = inverted;
+      break;
+
+    case OPCODE_MVN:
+      new_inst = OPCODE_MOV;
+      value = inverted;
+      break;
+
+    case OPCODE_AND:             /* AND <-> BIC  */
+      new_inst = OPCODE_BIC;
+      value = inverted;
+      break;
+
+    case OPCODE_BIC:
+      new_inst = OPCODE_AND;
+      value = inverted;
+      break;
+
+    case OPCODE_ADC:              /* ADC <-> SBC  */
+      new_inst = OPCODE_SBC;
+      value = inverted;
+      break;
+
+    case OPCODE_SBC:
+      new_inst = OPCODE_ADC;
+      value = inverted;
+      break;
+
+      /* We cannot do anything.  */
+    default:
+      return FAIL;
+    }
+
+  if (value == (unsigned) FAIL)
+    return FAIL;
+
+  *instruction &= OPCODE_MASK;
+  *instruction |= new_inst << DATA_OP_SHIFT;
+  return value;
+}
+
 void
 md_apply_fix3 (fixS *   fixP,
 	       valueT * valP,

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