* gas/config/tc-arm.c (neon_el_type): Make NT_invtype be the zero (so
authorJulian Brown <julian@codesourcery.com>
Fri, 7 Apr 2006 15:46:21 +0000 (15:46 +0000)
committerJulian Brown <julian@codesourcery.com>
Fri, 7 Apr 2006 15:46:21 +0000 (15:46 +0000)
zero-initialising structures containing it will lead to invalid
types).
(arm_it): Add vectype to each operand.
(NTA_HASTYPE, NTA_HASINDEX): Constants used in neon_typed_alias
defined field.
(neon_typed_alias): New structure. Extra information for typed
register aliases.
(reg_entry): Add neon type info field.
(arm_reg_parse): Remove RTYPE argument (revert to previous arguments).
Break out alternative syntax for coprocessor registers, etc. into...
(arm_reg_alt_syntax): New function. Alternate syntax handling broken
out from arm_reg_parse.
(parse_neon_type): Move. Return SUCCESS/FAIL.
(first_error): New function. Call to ensure first error which occurs
is reported.
(parse_neon_operand_type): Parse exactly one type.
(NEON_ALL_LANES, NEON_INTERLEAVE_LANES): Move.
(parse_typed_reg_or_scalar): New function. Handle core of both
arm_typed_reg_parse and parse_scalar.
(arm_typed_reg_parse): Parse a register with an optional type.
(NEON_SCALAR_REG, NEON_SCALAR_INDEX): Extract parts of parse_scalar
result.
(parse_scalar): Parse a Neon scalar with optional type.
(parse_reg_list): Use first_error.
(parse_vfp_reg_list): Use arm_typed_reg_parse instead of arm_reg_parse.
(neon_alias_types_same): New function. Return true if two (alias) types
are the same.
(parse_neon_el_struct_list): Use parse_typed_reg_or_scalar. Return type
of elements.
(insert_reg_alias): Return new reg_entry not void.
(insert_neon_reg_alias): New function. Insert type/index information as
well as register for alias.
(create_neon_reg_alias): New function. Parse .dn/.qn directives and
make typed register aliases accordingly.
(s_dn, s_qn): New functions. Handle incorrectly used .dn/.qn at start
of line.
(s_unreq): Delete type information if present.
(s_arm_unwind_save_mmxwr): Remove arg 3 from arm_reg_parse calls.
(s_arm_unwind_save_mmxwcg): Likewise.
(s_arm_unwind_movsp): Likewise.
(s_arm_unwind_setfp): Likewise.
(parse_shift): Likewise.
(parse_shifter_operand): Likewise.
(parse_address): Likewise.
(parse_tb): Likewise.
(tc_arm_regname_to_dw2regnum): Likewise.
(md_pseudo_table): Add dn, qn.
(parse_neon_mov): Handle typed operands.
(parse_operands): Likewise.
(neon_type_mask): Add N_SIZ.
(N_ALLMODS): New macro.
(neon_check_shape): Fix typo in NS_DDD_QQQ case. Use first_error.
(el_type_of_type_chk): Add some safeguards.
(modify_types_allowed): Fix logic bug.
(neon_check_type): Handle operands with types.
(neon_three_same): Remove redundant optional arg handling.
(do_neon_dyadic_i64_su, do_neon_shl_imm, do_neon_qshl_imm)
(do_neon_logic, do_neon_qdmulh, do_neon_fcmp_absolute)
(do_neon_step): Adjust accordingly.
(neon_cmode_for_logic_imm): Use first_error.
(do_neon_bitfield): Call neon_check_type.
(neon_dyadic): Rename to...
(neon_dyadic_misc): ...this. New name for neon_dyadic. Add bitfield to
allow modification of type of the destination.
(do_neon_dyadic_if_su, do_neon_dyadic_if_i, do_neon_dyadic_if_i_d)
(do_neon_addsub_if_i, do_neon_mul): Adjust accordingly.
(do_neon_compare): Make destination be an untyped bitfield.
(neon_scalar_for_mul): Use NEON_SCALAR_REG, NEON_SCALAR_INDEX.
(neon_mul_mac): Return early in case of errors.
(neon_move_immediate): Use first_error.
(neon_mac_reg_scalar_long): Fix type to include scalar.
(do_neon_dup): Likewise.
(do_neon_mov): Likewise (in several places).
(do_neon_tbl_tbx): Fix type.
(do_neon_ld_st_interleave, neon_alignment_bit, do_neon_ld_st_lane)
(do_neon_ld_dup): Exit early in case of errors and/or use first_error.
(opcode_lookup): Update for parse_neon_type returning SUCCESS/FAIL.
Handle .dn/.qn directives.
(REGDEF): Add zero for reg_entry neon field.

* gas/testsuite/gas/arm/neon-psyn.s: Basic test of programmers syntax.
* gas/testsuite/gas/arm/neon-psyn.d: Expected output of above.

ChangeLog.csl
gas/config/tc-arm.c
gas/testsuite/gas/arm/neon-psyn.d [new file with mode: 0644]
gas/testsuite/gas/arm/neon-psyn.s [new file with mode: 0644]

index 45f56ce..696b146 100644 (file)
@@ -1,3 +1,89 @@
+2006-04-07  Julian Brown  <julian@codesourcery.com>
+
+       * gas/config/tc-arm.c (neon_el_type): Make NT_invtype be the zero (so
+       zero-initialising structures containing it will lead to invalid
+       types).
+       (arm_it): Add vectype to each operand.
+       (NTA_HASTYPE, NTA_HASINDEX): Constants used in neon_typed_alias
+       defined field.
+       (neon_typed_alias): New structure. Extra information for typed
+       register aliases.
+       (reg_entry): Add neon type info field.
+       (arm_reg_parse): Remove RTYPE argument (revert to previous arguments).
+       Break out alternative syntax for coprocessor registers, etc. into...
+       (arm_reg_alt_syntax): New function. Alternate syntax handling broken
+       out from arm_reg_parse.
+       (parse_neon_type): Move. Return SUCCESS/FAIL.
+       (first_error): New function. Call to ensure first error which occurs
+       is reported.
+       (parse_neon_operand_type): Parse exactly one type.
+       (NEON_ALL_LANES, NEON_INTERLEAVE_LANES): Move.
+       (parse_typed_reg_or_scalar): New function. Handle core of both
+       arm_typed_reg_parse and parse_scalar.
+       (arm_typed_reg_parse): Parse a register with an optional type.
+       (NEON_SCALAR_REG, NEON_SCALAR_INDEX): Extract parts of parse_scalar
+       result.
+       (parse_scalar): Parse a Neon scalar with optional type.
+       (parse_reg_list): Use first_error.
+       (parse_vfp_reg_list): Use arm_typed_reg_parse instead of arm_reg_parse.
+       (neon_alias_types_same): New function. Return true if two (alias) types
+       are the same.
+       (parse_neon_el_struct_list): Use parse_typed_reg_or_scalar. Return type
+       of elements.
+       (insert_reg_alias): Return new reg_entry not void.
+       (insert_neon_reg_alias): New function. Insert type/index information as
+       well as register for alias.
+       (create_neon_reg_alias): New function. Parse .dn/.qn directives and
+       make typed register aliases accordingly.
+       (s_dn, s_qn): New functions. Handle incorrectly used .dn/.qn at start
+       of line.
+       (s_unreq): Delete type information if present.
+       (s_arm_unwind_save_mmxwr): Remove arg 3 from arm_reg_parse calls.
+       (s_arm_unwind_save_mmxwcg): Likewise.
+       (s_arm_unwind_movsp): Likewise.
+       (s_arm_unwind_setfp): Likewise.
+       (parse_shift): Likewise.
+       (parse_shifter_operand): Likewise.
+       (parse_address): Likewise.
+       (parse_tb): Likewise.
+       (tc_arm_regname_to_dw2regnum): Likewise.
+       (md_pseudo_table): Add dn, qn.
+       (parse_neon_mov): Handle typed operands.
+       (parse_operands): Likewise.
+       (neon_type_mask): Add N_SIZ.
+       (N_ALLMODS): New macro.
+       (neon_check_shape): Fix typo in NS_DDD_QQQ case. Use first_error.
+       (el_type_of_type_chk): Add some safeguards.
+       (modify_types_allowed): Fix logic bug.
+       (neon_check_type): Handle operands with types.
+       (neon_three_same): Remove redundant optional arg handling.
+       (do_neon_dyadic_i64_su, do_neon_shl_imm, do_neon_qshl_imm)
+       (do_neon_logic, do_neon_qdmulh, do_neon_fcmp_absolute)
+       (do_neon_step): Adjust accordingly.
+       (neon_cmode_for_logic_imm): Use first_error.
+       (do_neon_bitfield): Call neon_check_type.
+       (neon_dyadic): Rename to...
+       (neon_dyadic_misc): ...this. New name for neon_dyadic. Add bitfield to
+       allow modification of type of the destination.
+       (do_neon_dyadic_if_su, do_neon_dyadic_if_i, do_neon_dyadic_if_i_d)
+       (do_neon_addsub_if_i, do_neon_mul): Adjust accordingly.
+       (do_neon_compare): Make destination be an untyped bitfield.
+       (neon_scalar_for_mul): Use NEON_SCALAR_REG, NEON_SCALAR_INDEX.
+       (neon_mul_mac): Return early in case of errors.
+       (neon_move_immediate): Use first_error.
+       (neon_mac_reg_scalar_long): Fix type to include scalar.
+       (do_neon_dup): Likewise.
+       (do_neon_mov): Likewise (in several places).
+       (do_neon_tbl_tbx): Fix type.
+       (do_neon_ld_st_interleave, neon_alignment_bit, do_neon_ld_st_lane)
+       (do_neon_ld_dup): Exit early in case of errors and/or use first_error.
+       (opcode_lookup): Update for parse_neon_type returning SUCCESS/FAIL.
+       Handle .dn/.qn directives.
+       (REGDEF): Add zero for reg_entry neon field.
+
+       * gas/testsuite/gas/arm/neon-psyn.s: Basic test of programmers syntax.
+       * gas/testsuite/gas/arm/neon-psyn.d: Expected output of above.
+
 2006-04-03  Carlos O'Donell <carlos@codesourcery.com>
 
        * Makefile.tpl: Add install-html target.
 2006-04-03  Carlos O'Donell <carlos@codesourcery.com>
 
        * Makefile.tpl: Add install-html target.
index b2541fe..44c7d99 100644 (file)
@@ -265,13 +265,13 @@ static bfd_boolean unified_syntax = FALSE;
 
 enum neon_el_type
 {
 
 enum neon_el_type
 {
+  NT_invtype,
   NT_untyped,
   NT_integer,
   NT_float,
   NT_poly,
   NT_signed,
   NT_untyped,
   NT_integer,
   NT_float,
   NT_poly,
   NT_signed,
-  NT_unsigned,
-  NT_invtype
+  NT_unsigned
 };
 
 struct neon_type_el
 };
 
 struct neon_type_el
@@ -310,6 +310,7 @@ struct arm_it
   {
     unsigned reg;
     signed int imm;
   {
     unsigned reg;
     signed int imm;
+    struct neon_type_el vectype;
     unsigned present   : 1;  /* Operand present.  */
     unsigned isreg     : 1;  /* Operand was a register.  */
     unsigned immisreg  : 1;  /* .imm field is a second register.  */
     unsigned present   : 1;  /* Operand present.  */
     unsigned isreg     : 1;  /* Operand was a register.  */
     unsigned immisreg  : 1;  /* .imm field is a second register.  */
@@ -405,6 +406,17 @@ enum vfp_ldstm_type
   VFP_LDSTMIA, VFP_LDSTMDB, VFP_LDSTMIAX, VFP_LDSTMDBX
 };
 
   VFP_LDSTMIA, VFP_LDSTMDB, VFP_LDSTMIAX, VFP_LDSTMDBX
 };
 
+/* Bits for DEFINED field in neon_typed_alias.  */
+#define NTA_HASTYPE  1
+#define NTA_HASINDEX 2
+
+struct neon_typed_alias
+{
+  unsigned char defined;
+  unsigned char index;
+  struct neon_type_el eltype;
+};
+
 /* ARM register categories.  This includes coprocessor numbers and various
    architecture extensions' registers. */
 enum arm_reg_type
 /* ARM register categories.  This includes coprocessor numbers and various
    architecture extensions' registers. */
 enum arm_reg_type
@@ -430,13 +442,17 @@ enum arm_reg_type
   REG_TYPE_XSCALE,
 };
 
   REG_TYPE_XSCALE,
 };
 
-/* Structure for a hash table entry for a register.  */
+/* Structure for a hash table entry for a register.
+   If TYPE is REG_TYPE_VFD or REG_TYPE_NQ, the NEON field can point to extra
+   information which states whether a vector type or index is specified (for a
+   register alias created with .dn or .qn). Otherwise NEON should be NULL.  */
 struct reg_entry
 {
 struct reg_entry
 {
-  const char   *name;
-  unsigned char number;
-  unsigned char type;
-  unsigned char builtin;
+  const char        *name;
+  unsigned char      number;
+  unsigned char      type;
+  unsigned char      builtin;
+  struct neon_typed_alias *neon;
 };
 
 /* Diagnostics used when we don't get a register of the expected type. */
 };
 
 /* Diagnostics used when we don't get a register of the expected type. */
@@ -989,28 +1005,10 @@ arm_reg_parse_multi (char **ccp)
   return reg;
 }
 
   return reg;
 }
 
-/* As above, but the register must be of type TYPE, and the return
-   value is the register number or FAIL.
-   If RTYPE is non-zero, return the (possibly restricted) type of the
-   register (e.g. Neon double or quad reg when either has been requested).  */
-
 static int
 static int
-arm_reg_parse (char **ccp, enum arm_reg_type type, enum arm_reg_type *rtype)
+arm_reg_alt_syntax (char **ccp, char *start, struct reg_entry *reg,
+                    enum arm_reg_type type)
 {
 {
-  char *start = *ccp;
-  struct reg_entry *reg = arm_reg_parse_multi (ccp);
-
-  /* Undo polymorphism for Neon D and Q registers.  */
-  if (reg && type == REG_TYPE_NDQ
-      && (reg->type == REG_TYPE_NQ || reg->type == REG_TYPE_VFD))
-    type = reg->type;
-
-  if (rtype)
-    *rtype = type;
-
-  if (reg && reg->type == type)
-    return reg->number;
-
   /* Alternative syntaxes are accepted for a few register classes.  */
   switch (type)
     {
   /* Alternative syntaxes are accepted for a few register classes.  */
   switch (type)
     {
@@ -1042,48 +1040,339 @@ arm_reg_parse (char **ccp, enum arm_reg_type type, enum arm_reg_type *rtype)
       break;
     }
 
       break;
     }
 
+  return FAIL;
+}
+
+/* As arm_reg_parse_multi, but the register must be of type TYPE, and the
+   return value is the register number or FAIL.  */
+
+static int
+arm_reg_parse (char **ccp, enum arm_reg_type type)
+{
+  char *start = *ccp;
+  struct reg_entry *reg = arm_reg_parse_multi (ccp);
+  int ret;
+
+  /* Do not allow a scalar (reg+index) to parse as a register.  */
+  if (reg && reg->neon && (reg->neon->defined & NTA_HASINDEX))
+    return FAIL;
+
+  if (reg && reg->type == type)
+    return reg->number;
+
+  if ((ret = arm_reg_alt_syntax (ccp, start, reg, type)) != FAIL)
+    return ret;
+
   *ccp = start;
   return FAIL;
 }
 
   *ccp = start;
   return FAIL;
 }
 
+/* Parse a Neon type specifier. *STR should point at the leading '.'
+   character. Does no verification at this stage that the type fits the opcode
+   properly. E.g.,
+
+     .i32.i32.s16
+     .s32.f32
+     .u16
+
+   Can all be legally parsed by this function.
+
+   Fills in neon_type struct pointer with parsed information, and updates STR
+   to point after the parsed type specifier. Returns SUCCESS if this was a legal
+   type, FAIL if not.  */
+
+static int
+parse_neon_type (struct neon_type *type, char **str)
+{
+  char *ptr = *str;
+
+  if (type)
+    type->elems = 0;
+
+  while (type->elems < NEON_MAX_TYPE_ELS)
+    {
+      enum neon_el_type thistype = NT_untyped;
+      unsigned thissize = -1u;
+
+      if (*ptr != '.')
+       break;
+
+      ptr++;
+
+      /* Just a size without an explicit type.  */
+      if (ISDIGIT (*ptr))
+       goto parsesize;
+
+      switch (TOLOWER (*ptr))
+       {
+       case 'i': thistype = NT_integer; break;
+       case 'f': thistype = NT_float; break;
+       case 'p': thistype = NT_poly; break;
+       case 's': thistype = NT_signed; break;
+       case 'u': thistype = NT_unsigned; break;
+       default:
+         as_bad (_("unexpected character `%c' in type specifier"), *ptr);
+         return FAIL;
+       }
+
+      ptr++;
+
+      /* .f is an abbreviation for .f32.  */
+      if (thistype == NT_float && !ISDIGIT (*ptr))
+       thissize = 32;
+      else
+       {
+       parsesize:
+         thissize = strtoul (ptr, &ptr, 10);
+
+         if (thissize != 8 && thissize != 16 && thissize != 32
+              && thissize != 64)
+            {
+              as_bad (_("bad size %d in type specifier"), thissize);
+             return FAIL;
+           }
+       }
+
+      if (type)
+        {
+          type->el[type->elems].type = thistype;
+         type->el[type->elems].size = thissize;
+         type->elems++;
+       }
+    }
+
+  /* Empty/missing type is not a successful parse.  */
+  if (type->elems == 0)
+    return FAIL;
+
+  *str = ptr;
+
+  return SUCCESS;
+}
+
+/* Errors may be set multiple times during parsing or bit encoding
+   (particularly in the Neon bits), but usually the earliest error which is set
+   will be the most meaningful. Avoid overwriting it with later (cascading)
+   errors by calling this function.  */
+
+static void
+first_error (const char *err)
+{
+  if (!inst.error)
+    inst.error = err;
+}
+
+/* Parse a single type, e.g. ".s32", leading period included.  */
+static int
+parse_neon_operand_type (struct neon_type_el *vectype, char **ccp)
+{
+  char *str = *ccp;
+  struct neon_type optype;
+
+  if (*str == '.')
+    {
+      if (parse_neon_type (&optype, &str) == SUCCESS)
+        {
+          if (optype.elems == 1)
+            *vectype = optype.el[0];
+          else
+            {
+              first_error (_("only one type should be specified for operand"));
+              return FAIL;
+            }
+        }
+      else
+        {
+          first_error (_("vector type expected"));
+          return FAIL;
+        }
+    }
+  else
+    return FAIL;
+  
+  *ccp = str;
+  
+  return SUCCESS;
+}
+
+/* Special meanings for indices (which have a range of 0-7), which will fit into
+   a 4-bit integer.  */
+
+#define NEON_ALL_LANES         15
+#define NEON_INTERLEAVE_LANES  14
+
+/* Parse either a register or a scalar, with an optional type. Return the
+   register number, and optionally fill in the actual type of the register
+   when multiple alternatives were given (NEON_TYPE_NDQ) in *RTYPE, and
+   type/index information in *TYPEINFO.  */
+
+static int
+parse_typed_reg_or_scalar (char **ccp, enum arm_reg_type type,
+                           enum arm_reg_type *rtype,
+                           struct neon_typed_alias *typeinfo)
+{
+  char *str = *ccp;
+  struct reg_entry *reg = arm_reg_parse_multi (&str);
+  struct neon_typed_alias atype;
+  struct neon_type_el parsetype;
+
+  atype.defined = 0;
+  atype.index = -1;
+  atype.eltype.type = NT_invtype;
+  atype.eltype.size = -1;
+
+  /* Try alternate syntax for some types of register. Note these are mutually
+     exclusive with the Neon syntax extensions.  */
+  if (reg == NULL)
+    {
+      int altreg = arm_reg_alt_syntax (&str, *ccp, reg, type);
+      if (altreg != FAIL)
+        *ccp = str;
+      if (typeinfo)
+        *typeinfo = atype;
+      return altreg;
+    }
+
+  /* Undo polymorphism for Neon D and Q registers.  */
+  if (type == REG_TYPE_NDQ
+      && (reg->type == REG_TYPE_NQ || reg->type == REG_TYPE_VFD))
+    type = reg->type;
+
+  if (type != reg->type)
+    return FAIL;
+
+  if (reg->neon)
+    atype = *reg->neon;
+  
+  if (parse_neon_operand_type (&parsetype, &str) == SUCCESS)
+    {
+      if ((atype.defined & NTA_HASTYPE) != 0)
+        {
+          first_error (_("can't redefine type for operand"));
+          return FAIL;
+        }
+      atype.defined |= NTA_HASTYPE;
+      atype.eltype = parsetype;
+    }
+    
+  if (skip_past_char (&str, '[') == SUCCESS)
+    {
+      if (type != REG_TYPE_VFD)
+        {
+          first_error (_("only D registers may be indexed"));
+          return FAIL;
+        }
+    
+      if ((atype.defined & NTA_HASINDEX) != 0)
+        {
+          first_error (_("can't change index for operand"));
+          return FAIL;
+        }
+
+      atype.defined |= NTA_HASINDEX;
+
+      if (skip_past_char (&str, ']') == SUCCESS)
+        atype.index = NEON_ALL_LANES;
+      else
+        {
+          expressionS exp;
+
+          my_get_expression (&exp, &str, GE_NO_PREFIX);
+
+          if (exp.X_op != O_constant)
+            {
+              first_error (_("constant expression required"));
+              return FAIL;
+            }
+
+          if (skip_past_char (&str, ']') == FAIL)
+            return FAIL;
+
+          atype.index = exp.X_add_number;
+        }
+    }
+  
+  if (typeinfo)
+    *typeinfo = atype;
+  
+  if (rtype)
+    *rtype = type;
+  
+  *ccp = str;
+  
+  return reg->number;
+}
+
+/* Like arm_reg_parse, but allow allow the following extra features:
+    - If RTYPE is non-zero, return the (possibly restricted) type of the
+      register (e.g. Neon double or quad reg when either has been requested).
+    - If this is a Neon vector type with additional type information, fill
+      in the struct pointed to by VECTYPE (if non-NULL).
+   This function will fault on encountering a scalar.
+*/
+
+static int
+arm_typed_reg_parse (char **ccp, enum arm_reg_type type,
+                     enum arm_reg_type *rtype, struct neon_type_el *vectype)
+{
+  struct neon_typed_alias atype;
+  char *str = *ccp;
+  int reg = parse_typed_reg_or_scalar (&str, type, rtype, &atype);
+
+  if (reg == FAIL)
+    return FAIL;
+
+  /* Do not allow a scalar (reg+index) to parse as a register.  */
+  if ((atype.defined & NTA_HASINDEX) != 0)
+    {
+      first_error (_("register operand expected, but got scalar"));
+      return FAIL;
+    }
+
+  if (vectype)
+    *vectype = atype.eltype;
+
+  *ccp = str;
+
+  return reg;
+}
+
+#define NEON_SCALAR_REG(X)     ((X) >> 4)
+#define NEON_SCALAR_INDEX(X)   ((X) & 15)
+
 /* Parse a Neon scalar. Most of the time when we're parsing a scalar, we don't
    have enough information to be able to do a good job bounds-checking. So, we
    just do easy checks here, and do further checks later.  */
 
 static int
 /* Parse a Neon scalar. Most of the time when we're parsing a scalar, we don't
    have enough information to be able to do a good job bounds-checking. So, we
    just do easy checks here, and do further checks later.  */
 
 static int
-parse_scalar (char **ccp, int elsize)
+parse_scalar (char **ccp, int elsize, struct neon_type_el *type)
 {
 {
-  int regno, elno;
+  int reg;
   char *str = *ccp;
   char *str = *ccp;
-  expressionS exp;
+  struct neon_typed_alias atype;
   
   
-  if ((regno = arm_reg_parse (&str, REG_TYPE_VFD, NULL)) == FAIL)
-    return FAIL;
+  reg = parse_typed_reg_or_scalar (&str, REG_TYPE_VFD, NULL, &atype);
   
   
-  if (skip_past_char (&str, '[') == FAIL)
+  if (reg == FAIL || (atype.defined & NTA_HASINDEX) == 0)
     return FAIL;
   
     return FAIL;
   
-  my_get_expression (&exp, &str, GE_NO_PREFIX);
-  if (exp.X_op != O_constant)
+  if (atype.index == NEON_ALL_LANES)
     {
     {
-      inst.error = _("constant expression required");
+      first_error (_("scalar must have an index"));
       return FAIL;
     }
       return FAIL;
     }
-  elno = exp.X_add_number;
-  
-  if (elno >= 64 / elsize)
+  else if (atype.index >= 64 / elsize)
     {
     {
-      inst.error = _("scalar index out of range");
+      first_error (_("scalar index out of range"));
       return FAIL;
     }
   
       return FAIL;
     }
   
-  if (skip_past_char (&str, ']') == FAIL)
-    return FAIL;
+  if (type)
+    *type = atype.eltype;
   
   
-  /* Parsed scalar successfully. Skip over it.  */
   *ccp = str;
   
   *ccp = str;
   
-  return (regno * 8) + elno;
+  return reg * 16 + atype.index;
 }
 
 /* Parse an ARM register list.  Returns the bitmask, or FAIL.  */
 }
 
 /* Parse an ARM register list.  Returns the bitmask, or FAIL.  */
@@ -1109,9 +1398,9 @@ parse_reg_list (char ** strp)
            {
              int reg;
 
            {
              int reg;
 
-             if ((reg = arm_reg_parse (&str, REG_TYPE_RN, NULL)) == FAIL)
+             if ((reg = arm_reg_parse (&str, REG_TYPE_RN)) == FAIL)
                {
                {
-                 inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
+                 first_error (_(reg_expected_msgs[REG_TYPE_RN]));
                  return FAIL;
                }
 
                  return FAIL;
                }
 
@@ -1121,7 +1410,7 @@ parse_reg_list (char ** strp)
 
                  if (reg <= cur_reg)
                    {
 
                  if (reg <= cur_reg)
                    {
-                     inst.error = _("bad range in register list");
+                     first_error (_("bad range in register list"));
                      return FAIL;
                    }
 
                      return FAIL;
                    }
 
@@ -1152,7 +1441,7 @@ parse_reg_list (char ** strp)
 
          if (*str++ != '}')
            {
 
          if (*str++ != '}')
            {
-             inst.error = _("missing `}'");
+             first_error (_("missing `}'"));
              return FAIL;
            }
        }
              return FAIL;
            }
        }
@@ -1231,7 +1520,9 @@ enum reg_list_els
            vtbl.8 d3,d4,d5
          This could be done (the meaning isn't really ambiguous), but doesn't
          fit in well with the current parsing framework.
            vtbl.8 d3,d4,d5
          This could be done (the meaning isn't really ambiguous), but doesn't
          fit in well with the current parsing framework.
-     - 32 D registers may be used (also true for VFPv3).  */
+     - 32 D registers may be used (also true for VFPv3).
+   FIXME: Types are ignored in these register lists, which is probably a
+   bug.  */
 
 static int
 parse_vfp_reg_list (char **str, unsigned int *pbase, enum reg_list_els etype)
 
 static int
 parse_vfp_reg_list (char **str, unsigned int *pbase, enum reg_list_els etype)
@@ -1288,13 +1579,15 @@ parse_vfp_reg_list (char **str, unsigned int *pbase, enum reg_list_els etype)
   do
     {
       int setmask = 1, addregs = 1;
   do
     {
       int setmask = 1, addregs = 1;
-      new_base = arm_reg_parse (str, regtype, &regtype);
+
+      new_base = arm_typed_reg_parse (str, regtype, &regtype, NULL);
+
       if (new_base == FAIL)
        {
       if (new_base == FAIL)
        {
-         inst.error = gettext (reg_expected_msgs[regtype]);
+         first_error (_(reg_expected_msgs[regtype]));
          return FAIL;
        }
          return FAIL;
        }
-
       /* Note: a value of 2 * n is returned for the register Q<n>.  */
       if (regtype == REG_TYPE_NQ)
         {
       /* Note: a value of 2 * n is returned for the register Q<n>.  */
       if (regtype == REG_TYPE_NQ)
         {
@@ -1307,7 +1600,7 @@ parse_vfp_reg_list (char **str, unsigned int *pbase, enum reg_list_els etype)
 
       if (mask & (setmask << new_base))
        {
 
       if (mask & (setmask << new_base))
        {
-         inst.error = _("invalid register list");
+         first_error (_("invalid register list"));
          return FAIL;
        }
 
          return FAIL;
        }
 
@@ -1326,7 +1619,8 @@ parse_vfp_reg_list (char **str, unsigned int *pbase, enum reg_list_els etype)
 
          (*str)++;
 
 
          (*str)++;
 
-         if ((high_range = arm_reg_parse (str, regtype, NULL)) == FAIL)
+         if ((high_range = arm_typed_reg_parse (str, regtype, NULL, NULL))
+              == FAIL)
            {
              inst.error = gettext (reg_expected_msgs[regtype]);
              return FAIL;
            {
              inst.error = gettext (reg_expected_msgs[regtype]);
              return FAIL;
@@ -1378,21 +1672,47 @@ parse_vfp_reg_list (char **str, unsigned int *pbase, enum reg_list_els etype)
   return count;
 }
 
   return count;
 }
 
+/* True if two alias types are the same.  */
+
+static int
+neon_alias_types_same (struct neon_typed_alias *a, struct neon_typed_alias *b)
+{
+  if (!a && !b)
+    return 1;
+    
+  if (!a || !b)
+    return 0;
+
+  if (a->defined != b->defined)
+    return 0;
+  
+  if ((a->defined & NTA_HASTYPE) != 0
+      && (a->eltype.type != b->eltype.type
+          || a->eltype.size != b->eltype.size))
+    return 0;
+
+  if ((a->defined & NTA_HASINDEX) != 0
+      && (a->index != b->index))
+    return 0;
+  
+  return 1;
+}
+
 /* Parse element/structure lists for Neon VLD<n> and VST<n> instructions.
    The base register is put in *PBASE.
 /* Parse element/structure lists for Neon VLD<n> and VST<n> instructions.
    The base register is put in *PBASE.
-   The lane (or one of the #defined constants below) is placed in bits [3:0] of
+   The lane (or one of the NEON_*_LANES constants) is placed in bits [3:0] of
    the return value.
    The register stride (minus one) is put in bit 4 of the return value.
    the return value.
    The register stride (minus one) is put in bit 4 of the return value.
-   Bits [6:5] encode the list length (minus one).  */
+   Bits [6:5] encode the list length (minus one).
+   The type of the list elements is put in *ELTYPE, if non-NULL.  */
 
 
-#define NEON_ALL_LANES         15
-#define NEON_INTERLEAVE_LANES  14
 #define NEON_LANE(X)           ((X) & 0xf)
 #define NEON_LANE(X)           ((X) & 0xf)
-#define NEON_REG_STRIDE(X)     (((X) & (1 << 4)) ? 2 : 1)
+#define NEON_REG_STRIDE(X)     ((((X) >> 4) & 1) + 1)
 #define NEON_REGLIST_LENGTH(X) ((((X) >> 5) & 3) + 1)
 
 static int
 #define NEON_REGLIST_LENGTH(X) ((((X) >> 5) & 3) + 1)
 
 static int
-parse_neon_el_struct_list (char **str, unsigned *pbase)
+parse_neon_el_struct_list (char **str, unsigned *pbase,
+                           struct neon_type_el *eltype)
 {
   char *ptr = *str;
   int base_reg = -1;
 {
   char *ptr = *str;
   int base_reg = -1;
@@ -1404,16 +1724,19 @@ parse_neon_el_struct_list (char **str, unsigned *pbase)
   int addregs = 1;
   const char *const incr_error = "register stride must be 1 or 2";
   const char *const type_error = "mismatched element/structure types in list";
   int addregs = 1;
   const char *const incr_error = "register stride must be 1 or 2";
   const char *const type_error = "mismatched element/structure types in list";
+  struct neon_typed_alias firsttype;
   
   if (skip_past_char (&ptr, '{') == SUCCESS)
     leading_brace = 1;
   
   do
     {
   
   if (skip_past_char (&ptr, '{') == SUCCESS)
     leading_brace = 1;
   
   do
     {
-      int getreg = arm_reg_parse (&ptr, rtype, &rtype);
+      struct neon_typed_alias atype;
+      int getreg = parse_typed_reg_or_scalar (&ptr, rtype, &rtype, &atype);
+
       if (getreg == FAIL)
         {
       if (getreg == FAIL)
         {
-          inst.error = _(reg_expected_msgs[rtype]);
+          first_error (_(reg_expected_msgs[rtype]));
           return FAIL;
         }
       
           return FAIL;
         }
       
@@ -1425,19 +1748,26 @@ parse_neon_el_struct_list (char **str, unsigned *pbase)
               reg_incr = 1;
               addregs = 2;
             }
               reg_incr = 1;
               addregs = 2;
             }
+          firsttype = atype;
         }
       else if (reg_incr == -1)
         {
           reg_incr = getreg - base_reg;
           if (reg_incr < 1 || reg_incr > 2)
             {
         }
       else if (reg_incr == -1)
         {
           reg_incr = getreg - base_reg;
           if (reg_incr < 1 || reg_incr > 2)
             {
-              inst.error = _(incr_error);
+              first_error (_(incr_error));
               return FAIL;
             }
         }
       else if (getreg != base_reg + reg_incr * count)
         {
               return FAIL;
             }
         }
       else if (getreg != base_reg + reg_incr * count)
         {
-          inst.error = _(incr_error);
+          first_error (_(incr_error));
+          return FAIL;
+        }
+
+      if (!neon_alias_types_same (&atype, &firsttype))
+        {
+          first_error (_(type_error));
           return FAIL;
         }
       
           return FAIL;
         }
       
@@ -1445,26 +1775,32 @@ parse_neon_el_struct_list (char **str, unsigned *pbase)
          modes.  */
       if (ptr[0] == '-')
         {
          modes.  */
       if (ptr[0] == '-')
         {
+          struct neon_typed_alias htype;
           int hireg, dregs = (rtype == REG_TYPE_NQ) ? 2 : 1;
           if (lane == -1)
             lane = NEON_INTERLEAVE_LANES;
           else if (lane != NEON_INTERLEAVE_LANES)
             {
           int hireg, dregs = (rtype == REG_TYPE_NQ) ? 2 : 1;
           if (lane == -1)
             lane = NEON_INTERLEAVE_LANES;
           else if (lane != NEON_INTERLEAVE_LANES)
             {
-              inst.error = _(type_error);
+              first_error (_(type_error));
               return FAIL;
             }
           if (reg_incr == -1)
             reg_incr = 1;
           else if (reg_incr != 1)
             {
               return FAIL;
             }
           if (reg_incr == -1)
             reg_incr = 1;
           else if (reg_incr != 1)
             {
-              inst.error = _("don't use Rn-Rm syntax with non-unit stride");
+              first_error (_("don't use Rn-Rm syntax with non-unit stride"));
               return FAIL;
             }
           ptr++;
               return FAIL;
             }
           ptr++;
-          hireg = arm_reg_parse (&ptr, rtype, NULL);
+          hireg = parse_typed_reg_or_scalar (&ptr, rtype, NULL, &htype);
           if (hireg == FAIL)
             {
           if (hireg == FAIL)
             {
-              inst.error = _(reg_expected_msgs[rtype]);
+              first_error (_(reg_expected_msgs[rtype]));
+              return FAIL;
+            }
+          if (!neon_alias_types_same (&htype, &firsttype))
+            {
+              first_error (_(type_error));
               return FAIL;
             }
           count += hireg + dregs - getreg;
               return FAIL;
             }
           count += hireg + dregs - getreg;
@@ -1478,47 +1814,21 @@ parse_neon_el_struct_list (char **str, unsigned *pbase)
           continue;
         }
       
           continue;
         }
       
-      if (skip_past_char (&ptr, '[') == SUCCESS)
+      if ((atype.defined & NTA_HASINDEX) != 0)
         {
         {
-          if (skip_past_char (&ptr, ']') == SUCCESS)
-            {
-              if (lane == -1)
-                lane = NEON_ALL_LANES;
-              else if (lane != NEON_ALL_LANES)
-                {
-                  inst.error = _(type_error);
-                  return FAIL;
-                }
-            }
-          else
+          if (lane == -1)
+            lane = atype.index;
+          else if (lane != atype.index)
             {
             {
-              expressionS exp;
-              my_get_expression (&exp, &ptr, GE_NO_PREFIX);
-              if (exp.X_op != O_constant)
-                {
-                  inst.error = _("constant expression required");
-                  return FAIL;
-                }
-              if (lane == -1)
-                lane = exp.X_add_number;
-              else if (lane != exp.X_add_number)
-                {
-                  inst.error = _(type_error);
-                  return FAIL;
-                }
-              
-              if (skip_past_char (&ptr, ']') == FAIL)
-                {
-                  inst.error = _("expected ]");
-                  return FAIL;
-                }
+              first_error (_(type_error));
+              return FAIL;
             }
         }
       else if (lane == -1)
         lane = NEON_INTERLEAVE_LANES;
       else if (lane != NEON_INTERLEAVE_LANES)
         {
             }
         }
       else if (lane == -1)
         lane = NEON_INTERLEAVE_LANES;
       else if (lane != NEON_INTERLEAVE_LANES)
         {
-          inst.error = _(type_error);
+          first_error (_(type_error));
           return FAIL;
         }
       count++;
           return FAIL;
         }
       count++;
@@ -1533,19 +1843,22 @@ parse_neon_el_struct_list (char **str, unsigned *pbase)
   if (lane == -1 || base_reg == -1 || count < 1 || count > 4
       || (count > 1 && reg_incr == -1))
     {
   if (lane == -1 || base_reg == -1 || count < 1 || count > 4
       || (count > 1 && reg_incr == -1))
     {
-      inst.error = _("error parsing element/structure list");
+      first_error (_("error parsing element/structure list"));
       return FAIL;
     }
 
   if ((count > 1 || leading_brace) && skip_past_char (&ptr, '}') == FAIL)
     {
       return FAIL;
     }
 
   if ((count > 1 || leading_brace) && skip_past_char (&ptr, '}') == FAIL)
     {
-      inst.error = _("expected }");
+      first_error (_("expected }"));
       return FAIL;
     }
   
   if (reg_incr == -1)
     reg_incr = 1;
 
       return FAIL;
     }
   
   if (reg_incr == -1)
     reg_incr = 1;
 
+  if (eltype)
+    *eltype = firsttype.eltype;
+
   *pbase = base_reg;
   *str = ptr;
   
   *pbase = base_reg;
   *str = ptr;
   
@@ -1583,7 +1896,7 @@ parse_reloc (char **str)
 
 /* Directives: register aliases.  */
 
 
 /* Directives: register aliases.  */
 
-static void
+static struct reg_entry *
 insert_reg_alias (char *str, int number, int type)
 {
   struct reg_entry *new;
 insert_reg_alias (char *str, int number, int type)
 {
   struct reg_entry *new;
@@ -1599,7 +1912,7 @@ insert_reg_alias (char *str, int number, int type)
       else if (new->number != number || new->type != type)
        as_warn (_("ignoring redefinition of register alias '%s'"), str);
 
       else if (new->number != number || new->type != type)
        as_warn (_("ignoring redefinition of register alias '%s'"), str);
 
-      return;
+      return 0;
     }
 
   name = xstrdup (str);
     }
 
   name = xstrdup (str);
@@ -1609,9 +1922,31 @@ insert_reg_alias (char *str, int number, int type)
   new->number = number;
   new->type = type;
   new->builtin = FALSE;
   new->number = number;
   new->type = type;
   new->builtin = FALSE;
+  new->neon = NULL;
 
   if (hash_insert (arm_reg_hsh, name, (PTR) new))
     abort ();
 
   if (hash_insert (arm_reg_hsh, name, (PTR) new))
     abort ();
+  
+  return new;
+}
+
+static void
+insert_neon_reg_alias (char *str, int number, int type,
+                       struct neon_typed_alias *atype)
+{
+  struct reg_entry *reg = insert_reg_alias (str, number, type);
+  
+  if (!reg)
+    {
+      first_error (_("attempt to redefine typed alias"));
+      return;
+    }
+  
+  if (atype)
+    {
+      reg->neon = xmalloc (sizeof (struct neon_typed_alias));
+      *reg->neon = *atype;
+    }
 }
 
 /* Look for the .req directive.         This is of the form:
 }
 
 /* Look for the .req directive.         This is of the form:
@@ -1679,6 +2014,148 @@ create_register_alias (char * newname, char *p)
   return 1;
 }
 
   return 1;
 }
 
+/* Create a Neon typed/indexed register alias using directives, e.g.:
+     X .dn d5.s32[1]
+     Y .qn 6.s16
+     Z .dn d7
+     T .dn Z[0]
+   These typed registers can be used instead of the types specified after the
+   Neon mnemonic, so long as all operands given have types. Types can also be
+   specified directly, e.g.:
+     vadd d0.s32, d1.s32, d2.s32
+*/
+
+static int
+create_neon_reg_alias (char *newname, char *p)
+{
+  enum arm_reg_type basetype;
+  struct reg_entry *basereg;
+  struct reg_entry mybasereg;
+  struct neon_type ntype;
+  struct neon_typed_alias typeinfo;
+  char *namebuf, *nameend;
+  int namelen;
+  
+  typeinfo.defined = 0;
+  typeinfo.eltype.type = NT_invtype;
+  typeinfo.eltype.size = -1;
+  typeinfo.index = -1;
+  
+  nameend = p;
+  
+  if (strncmp (p, " .dn ", 5) == 0)
+    basetype = REG_TYPE_VFD;
+  else if (strncmp (p, " .qn ", 5) == 0)
+    basetype = REG_TYPE_NQ;
+  else
+    return 0;
+  
+  p += 5;
+  
+  if (*p == '\0')
+    return 0;
+  
+  basereg = arm_reg_parse_multi (&p);
+
+  if (basereg && basereg->type != basetype)
+    {
+      as_bad (_("bad type for register"));
+      return 0;
+    }
+
+  if (basereg == NULL)
+    {
+      expressionS exp;
+      /* Try parsing as an integer.  */
+      my_get_expression (&exp, &p, GE_NO_PREFIX);
+      if (exp.X_op != O_constant)
+        {
+          as_bad (_("expression must be constant"));
+          return 0;
+        }
+      basereg = &mybasereg;
+      basereg->number = (basetype == REG_TYPE_NQ) ? exp.X_add_number * 2
+                                                  : exp.X_add_number;
+      basereg->neon = 0;
+    }
+
+  if (basereg->neon)
+    typeinfo = *basereg->neon;
+
+  if (parse_neon_type (&ntype, &p) == SUCCESS)
+    {
+      /* We got a type.  */
+      if (typeinfo.defined & NTA_HASTYPE)
+        {
+          as_bad (_("can't redefine the type of a register alias"));
+          return 0;
+        }
+      
+      typeinfo.defined |= NTA_HASTYPE;
+      if (ntype.elems != 1)
+        {
+          as_bad (_("you must specify a single type only"));
+          return 0;
+        }
+      typeinfo.eltype = ntype.el[0];
+    }
+  
+  if (skip_past_char (&p, '[') == SUCCESS)
+    {
+      expressionS exp;
+      /* We got a scalar index.  */
+    
+      if (typeinfo.defined & NTA_HASINDEX)
+        {
+          as_bad (_("can't redefine the index of a scalar alias"));
+          return 0;
+        }
+    
+      my_get_expression (&exp, &p, GE_NO_PREFIX);
+    
+      if (exp.X_op != O_constant)
+        {
+          as_bad (_("scalar index must be constant"));
+          return 0;
+        }
+      
+      typeinfo.defined |= NTA_HASINDEX;
+      typeinfo.index = exp.X_add_number;
+    
+      if (skip_past_char (&p, ']') == FAIL)
+        {
+          as_bad (_("expecting ]"));
+          return 0;
+        }
+    }
+
+  namelen = nameend - newname;
+  namebuf = alloca (namelen + 1);
+  strncpy (namebuf, newname, namelen);
+  namebuf[namelen] = '\0';
+  
+  insert_neon_reg_alias (namebuf, basereg->number, basetype,
+                         typeinfo.defined != 0 ? &typeinfo : NULL);
+    
+  /* Insert name in all uppercase.  */
+  for (p = namebuf; *p; p++)
+    *p = TOUPPER (*p);
+  
+  if (strncmp (namebuf, newname, namelen))
+    insert_neon_reg_alias (namebuf, basereg->number, basetype,
+                           typeinfo.defined != 0 ? &typeinfo : NULL);
+  
+  /* Insert name in all lowercase.  */
+  for (p = namebuf; *p; p++)
+    *p = TOLOWER (*p);
+  
+  if (strncmp (namebuf, newname, namelen))
+    insert_neon_reg_alias (namebuf, basereg->number, basetype,
+                           typeinfo.defined != 0 ? &typeinfo : NULL);
+  
+  return 1;
+}
+
 /* Should never be called, as .req goes between the alias and the
    register name, not at the beginning of the line.  */
 static void
 /* Should never be called, as .req goes between the alias and the
    register name, not at the beginning of the line.  */
 static void
@@ -1687,6 +2164,18 @@ s_req (int a ATTRIBUTE_UNUSED)
   as_bad (_("invalid syntax for .req directive"));
 }
 
   as_bad (_("invalid syntax for .req directive"));
 }
 
+static void
+s_dn (int a ATTRIBUTE_UNUSED)
+{
+  as_bad (_("invalid syntax for .dn directive"));
+}
+
+static void
+s_qn (int a ATTRIBUTE_UNUSED)
+{
+  as_bad (_("invalid syntax for .qn directive"));
+}
+
 /* The .unreq directive deletes an alias which was previously defined
    by .req.  For example:
 
 /* The .unreq directive deletes an alias which was previously defined
    by .req.  For example:
 
@@ -1724,6 +2213,8 @@ s_unreq (int a ATTRIBUTE_UNUSED)
        {
          hash_delete (arm_reg_hsh, name);
          free ((char *) reg->name);
        {
          hash_delete (arm_reg_hsh, name);
          free ((char *) reg->name);
+          if (reg->neon)
+            free (reg->neon);
          free (reg);
        }
     }
          free (reg);
        }
     }
@@ -2790,7 +3281,7 @@ s_arm_unwind_save_mmxwr (void)
 
   do
     {
 
   do
     {
-      reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWR, NULL);
+      reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWR);
 
       if (reg == FAIL)
        {
 
       if (reg == FAIL)
        {
@@ -2805,7 +3296,7 @@ s_arm_unwind_save_mmxwr (void)
       if (*input_line_pointer == '-')
        {
          input_line_pointer++;
       if (*input_line_pointer == '-')
        {
          input_line_pointer++;
-         hi_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWR, NULL);
+         hi_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWR);
          if (hi_reg == FAIL)
            {
              as_bad (_(reg_expected_msgs[REG_TYPE_MMXWR]));
          if (hi_reg == FAIL)
            {
              as_bad (_(reg_expected_msgs[REG_TYPE_MMXWR]));
@@ -2922,7 +3413,7 @@ s_arm_unwind_save_mmxwcg (void)
 
   do
     {
 
   do
     {
-      reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWCG, NULL);
+      reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWCG);
 
       if (reg == FAIL)
        {
 
       if (reg == FAIL)
        {
@@ -2938,7 +3429,7 @@ s_arm_unwind_save_mmxwcg (void)
       if (*input_line_pointer == '-')
        {
          input_line_pointer++;
       if (*input_line_pointer == '-')
        {
          input_line_pointer++;
-         hi_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWCG, NULL);
+         hi_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWCG);
          if (hi_reg == FAIL)
            {
              as_bad (_(reg_expected_msgs[REG_TYPE_MMXWCG]));
          if (hi_reg == FAIL)
            {
              as_bad (_(reg_expected_msgs[REG_TYPE_MMXWCG]));
@@ -3036,7 +3527,7 @@ s_arm_unwind_movsp (int ignored ATTRIBUTE_UNUSED)
   int reg;
   valueT op;
 
   int reg;
   valueT op;
 
-  reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN, NULL);
+  reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN);
   if (reg == FAIL)
     {
       as_bad (_(reg_expected_msgs[REG_TYPE_RN]));
   if (reg == FAIL)
     {
       as_bad (_(reg_expected_msgs[REG_TYPE_RN]));
@@ -3097,11 +3588,11 @@ s_arm_unwind_setfp (int ignored ATTRIBUTE_UNUSED)
   int fp_reg;
   int offset;
 
   int fp_reg;
   int offset;
 
-  fp_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN, NULL);
+  fp_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN);
   if (skip_past_comma (&input_line_pointer) == FAIL)
     sp_reg = FAIL;
   else
   if (skip_past_comma (&input_line_pointer) == FAIL)
     sp_reg = FAIL;
   else
-    sp_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN, NULL);
+    sp_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN);
 
   if (fp_reg == FAIL || sp_reg == FAIL)
     {
 
   if (fp_reg == FAIL || sp_reg == FAIL)
     {
@@ -3297,6 +3788,9 @@ const pseudo_typeS md_pseudo_table[] =
 {
   /* Never called because '.req' does not start a line.         */
   { "req",        s_req,         0 },
 {
   /* Never called because '.req' does not start a line.         */
   { "req",        s_req,         0 },
+  /* Following two are likewise never called.  */
+  { "dn",         s_dn,          0 },
+  { "qn",          s_qn,          0 },
   { "unreq",      s_unreq,       0 },
   { "bss",        s_bss,         0 },
   { "align",      s_align,       0 },
   { "unreq",      s_unreq,       0 },
   { "bss",        s_bss,         0 },
   { "align",      s_align,       0 },
@@ -3598,7 +4092,7 @@ parse_shift (char **str, int i, enum parse_shift_mode mode)
       skip_whitespace (p);
 
       if (mode == NO_SHIFT_RESTRICT
       skip_whitespace (p);
 
       if (mode == NO_SHIFT_RESTRICT
-         && (reg = arm_reg_parse (&p, REG_TYPE_RN, NULL)) != FAIL)
+         && (reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
        {
          inst.operands[i].imm = reg;
          inst.operands[i].immisreg = 1;
        {
          inst.operands[i].imm = reg;
          inst.operands[i].immisreg = 1;
@@ -3629,7 +4123,7 @@ parse_shifter_operand (char **str, int i)
   int value;
   expressionS expr;
 
   int value;
   expressionS expr;
 
-  if ((value = arm_reg_parse (str, REG_TYPE_RN, NULL)) != FAIL)
+  if ((value = arm_reg_parse (str, REG_TYPE_RN)) != FAIL)
     {
       inst.operands[i].reg = value;
       inst.operands[i].isreg = 1;
     {
       inst.operands[i].reg = value;
       inst.operands[i].isreg = 1;
@@ -3740,7 +4234,7 @@ parse_address (char **str, int i)
       return SUCCESS;
     }
 
       return SUCCESS;
     }
 
-  if ((reg = arm_reg_parse (&p, REG_TYPE_RN, NULL)) == FAIL)
+  if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL)
     {
       inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
       return FAIL;
     {
       inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
       return FAIL;
@@ -3755,7 +4249,7 @@ parse_address (char **str, int i)
       if (*p == '+') p++;
       else if (*p == '-') p++, inst.operands[i].negative = 1;
 
       if (*p == '+') p++;
       else if (*p == '-') p++, inst.operands[i].negative = 1;
 
-      if ((reg = arm_reg_parse (&p, REG_TYPE_RN, NULL)) != FAIL)
+      if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
        {
          inst.operands[i].imm = reg;
          inst.operands[i].immisreg = 1;
        {
          inst.operands[i].imm = reg;
          inst.operands[i].immisreg = 1;
@@ -3838,7 +4332,7 @@ parse_address (char **str, int i)
          if (*p == '+') p++;
          else if (*p == '-') p++, inst.operands[i].negative = 1;
 
          if (*p == '+') p++;
          else if (*p == '-') p++, inst.operands[i].negative = 1;
 
-         if ((reg = arm_reg_parse (&p, REG_TYPE_RN, NULL)) != FAIL)
+         if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
            {
               /* We might be using the immediate for alignment already. If we
                  are, OR the register number into the low-order bits.  */
            {
               /* We might be using the immediate for alignment already. If we
                  are, OR the register number into the low-order bits.  */
@@ -4099,7 +4593,7 @@ parse_tb (char **str)
       return FAIL;
     }
 
       return FAIL;
     }
 
-  if ((reg = arm_reg_parse (&p, REG_TYPE_RN, NULL)) == FAIL)
+  if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL)
     {
       inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
       return FAIL;
     {
       inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
       return FAIL;
@@ -4112,7 +4606,7 @@ parse_tb (char **str)
       return FAIL;
     }
   
       return FAIL;
     }
   
-  if ((reg = arm_reg_parse (&p, REG_TYPE_RN, NULL)) == FAIL)
+  if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL)
     {
       inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
       return FAIL;
     {
       inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
       return FAIL;
@@ -4154,25 +4648,28 @@ parse_neon_mov (char **str, int *which_operand)
   int i = *which_operand, val;
   enum arm_reg_type rtype;
   char *ptr = *str;
   int i = *which_operand, val;
   enum arm_reg_type rtype;
   char *ptr = *str;
+  struct neon_type_el optype;
   
   
-  if ((val = parse_scalar (&ptr, 8)) != FAIL)
+  if ((val = parse_scalar (&ptr, 8, &optype)) != FAIL)
     {
       /* Case 4: VMOV<c><q>.<size> <Dn[x]>, <Rd>.  */
       inst.operands[i].reg = val;
       inst.operands[i].isscalar = 1;
     {
       /* Case 4: VMOV<c><q>.<size> <Dn[x]>, <Rd>.  */
       inst.operands[i].reg = val;
       inst.operands[i].isscalar = 1;
+      inst.operands[i].vectype = optype;
       inst.operands[i++].present = 1;
 
       if (skip_past_comma (&ptr) == FAIL)
         goto wanted_comma;
       
       inst.operands[i++].present = 1;
 
       if (skip_past_comma (&ptr) == FAIL)
         goto wanted_comma;
       
-      if ((val = arm_reg_parse (&ptr, REG_TYPE_RN, NULL)) == FAIL)
+      if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL)
         goto wanted_arm;
       
       inst.operands[i].reg = val;
       inst.operands[i].isreg = 1;
       inst.operands[i].present = 1;
     }
         goto wanted_arm;
       
       inst.operands[i].reg = val;
       inst.operands[i].isreg = 1;
       inst.operands[i].present = 1;
     }
-  else if ((val = arm_reg_parse (&ptr, REG_TYPE_NDQ, &rtype)) != FAIL)
+  else if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_NDQ, &rtype, &optype))
+           != FAIL)
     {
       /* Cases 0, 1, 2, 3, 5 (D only).  */
       if (skip_past_comma (&ptr) == FAIL)
     {
       /* Cases 0, 1, 2, 3, 5 (D only).  */
       if (skip_past_comma (&ptr) == FAIL)
@@ -4181,9 +4678,10 @@ parse_neon_mov (char **str, int *which_operand)
       inst.operands[i].reg = val;
       inst.operands[i].isreg = 1;
       inst.operands[i].isquad = (rtype == REG_TYPE_NQ);
       inst.operands[i].reg = val;
       inst.operands[i].isreg = 1;
       inst.operands[i].isquad = (rtype == REG_TYPE_NQ);
+      inst.operands[i].vectype = optype;
       inst.operands[i++].present = 1;
 
       inst.operands[i++].present = 1;
 
-      if ((val = arm_reg_parse (&ptr, REG_TYPE_RN, NULL)) != FAIL)
+      if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) != FAIL)
         {
           /* Case 5: VMOV<c><q> <Dm>, <Rd>, <Rn>.  */
           inst.operands[i-1].regisimm = 1;
         {
           /* Case 5: VMOV<c><q> <Dm>, <Rd>, <Rn>.  */
           inst.operands[i-1].regisimm = 1;
@@ -4193,12 +4691,12 @@ parse_neon_mov (char **str, int *which_operand)
 
           if (rtype == REG_TYPE_NQ)
             {
 
           if (rtype == REG_TYPE_NQ)
             {
-              inst.error = _("can't use Neon quad register here");
+              first_error (_("can't use Neon quad register here"));
               return FAIL;
             }
           if (skip_past_comma (&ptr) == FAIL)
             goto wanted_comma;
               return FAIL;
             }
           if (skip_past_comma (&ptr) == FAIL)
             goto wanted_comma;
-          if ((val = arm_reg_parse (&ptr, REG_TYPE_RN, NULL)) == FAIL)
+          if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL)
             goto wanted_arm;
           inst.operands[i].reg = val;
           inst.operands[i].isreg = 1;
             goto wanted_arm;
           inst.operands[i].reg = val;
           inst.operands[i].isreg = 1;
@@ -4211,7 +4709,8 @@ parse_neon_mov (char **str, int *which_operand)
           if (!thumb_mode && (inst.instruction & 0xf0000000) != 0xe0000000)
             goto bad_cond;
         }
           if (!thumb_mode && (inst.instruction & 0xf0000000) != 0xe0000000)
             goto bad_cond;
         }
-      else if ((val = arm_reg_parse (&ptr, REG_TYPE_NDQ, &rtype)) != FAIL)
+      else if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_NDQ, &rtype, &optype))
+               != FAIL)
         {
           /* Case 0: VMOV<c><q> <Qd>, <Qm>
              Case 1: VMOV<c><q> <Dd>, <Dm>  */
         {
           /* Case 0: VMOV<c><q> <Qd>, <Qm>
              Case 1: VMOV<c><q> <Dd>, <Dm>  */
@@ -4221,15 +4720,16 @@ parse_neon_mov (char **str, int *which_operand)
           inst.operands[i].reg = val;
           inst.operands[i].isreg = 1;
           inst.operands[i].isquad = (rtype == REG_TYPE_NQ);
           inst.operands[i].reg = val;
           inst.operands[i].isreg = 1;
           inst.operands[i].isquad = (rtype == REG_TYPE_NQ);
+          inst.operands[i].vectype = optype;
           inst.operands[i].present = 1;
         }
       else
         {
           inst.operands[i].present = 1;
         }
       else
         {
-          inst.error = _("expected <Rm> or <Dm> or <Qm> operand");
+          first_error (_("expected <Rm> or <Dm> or <Qm> operand"));
           return FAIL;
         }
     }
           return FAIL;
         }
     }
-  else if ((val = arm_reg_parse (&ptr, REG_TYPE_RN, NULL)) != FAIL)
+  else if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) != FAIL)
     {
       /* Cases 6, 7.  */
       inst.operands[i].reg = val;
     {
       /* Cases 6, 7.  */
       inst.operands[i].reg = val;
@@ -4239,14 +4739,15 @@ parse_neon_mov (char **str, int *which_operand)
       if (skip_past_comma (&ptr) == FAIL)
         goto wanted_comma;
       
       if (skip_past_comma (&ptr) == FAIL)
         goto wanted_comma;
       
-      if ((val = parse_scalar (&ptr, 8)) != FAIL)
+      if ((val = parse_scalar (&ptr, 8, &optype)) != FAIL)
         {
           /* Case 6: VMOV<c><q>.<dt> <Rd>, <Dn[x]>  */
           inst.operands[i].reg = val;
           inst.operands[i].isscalar = 1;
           inst.operands[i].present = 1;
         {
           /* Case 6: VMOV<c><q>.<dt> <Rd>, <Dn[x]>  */
           inst.operands[i].reg = val;
           inst.operands[i].isscalar = 1;
           inst.operands[i].present = 1;
+          inst.operands[i].vectype = optype;
         }
         }
-      else if ((val = arm_reg_parse (&ptr, REG_TYPE_RN, NULL)) != FAIL)
+      else if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) != FAIL)
         {
           /* Case 7: VMOV<c><q> <Rd>, <Rn>, <Dm>  */
           inst.operands[i].reg = val;
         {
           /* Case 7: VMOV<c><q> <Rd>, <Rn>, <Dm>  */
           inst.operands[i].reg = val;
@@ -4256,21 +4757,23 @@ parse_neon_mov (char **str, int *which_operand)
           if (skip_past_comma (&ptr) == FAIL)
             goto wanted_comma;
           
           if (skip_past_comma (&ptr) == FAIL)
             goto wanted_comma;
           
-          if ((val = arm_reg_parse (&ptr, REG_TYPE_VFD, NULL)) == FAIL)
+          if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_VFD, NULL, &optype))
+              == FAIL)
             {
             {
-              inst.error = _(reg_expected_msgs[REG_TYPE_VFD]);
+              first_error (_(reg_expected_msgs[REG_TYPE_VFD]));
               return FAIL;
             }
 
           inst.operands[i].reg = val;
           inst.operands[i].isreg = 1;
           inst.operands[i].regisimm = 1;
               return FAIL;
             }
 
           inst.operands[i].reg = val;
           inst.operands[i].isreg = 1;
           inst.operands[i].regisimm = 1;
+          inst.operands[i].vectype = optype;
           inst.operands[i].present = 1;
         }
     }
   else
     {
           inst.operands[i].present = 1;
         }
     }
   else
     {
-      inst.error = _("parse error");
+      first_error (_("parse error"));
       return FAIL;
     }
 
       return FAIL;
     }
 
@@ -4280,15 +4783,15 @@ parse_neon_mov (char **str, int *which_operand)
   return SUCCESS;
 
   wanted_comma:
   return SUCCESS;
 
   wanted_comma:
-  inst.error = _("expected comma");
+  first_error (_("expected comma"));
   return FAIL;
   
   wanted_arm:
   return FAIL;
   
   wanted_arm:
-  inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
+  first_error (_(reg_expected_msgs[REG_TYPE_RN]));
   return FAIL;
 
   bad_cond:
   return FAIL;
 
   bad_cond:
-  inst.error = _("instruction cannot be conditionalized");
+  first_error (_("instruction cannot be conditionalized"));
   return FAIL;
 }
 
   return FAIL;
 }
 
@@ -4415,26 +4918,28 @@ parse_operands (char *str, const unsigned char *pattern)
     goto bad_args;                             \
 } while (0)
 
     goto bad_args;                             \
 } while (0)
 
-#define po_reg_or_fail(regtype) do {                   \
-  val = arm_reg_parse (&str, regtype, &rtype);         \
-  if (val == FAIL)                                     \
-    {                                                  \
-      inst.error = _(reg_expected_msgs[regtype]);      \
-      goto failure;                                    \
-    }                                                  \
-  inst.operands[i].reg = val;                          \
-  inst.operands[i].isreg = 1;                          \
-  inst.operands[i].isquad = (rtype == REG_TYPE_NQ);    \
+#define po_reg_or_fail(regtype) do {                           \
+  val = arm_typed_reg_parse (&str, regtype, &rtype,            \
+                            &inst.operands[i].vectype);        \
+  if (val == FAIL)                                             \
+    {                                                          \
+      first_error (_(reg_expected_msgs[regtype]));             \
+      goto failure;                                            \
+    }                                                          \
+  inst.operands[i].reg = val;                                  \
+  inst.operands[i].isreg = 1;                                  \
+  inst.operands[i].isquad = (rtype == REG_TYPE_NQ);            \
 } while (0)
 
 } while (0)
 
-#define po_reg_or_goto(regtype, label) do {            \
-  val = arm_reg_parse (&str, regtype, &rtype);         \
-  if (val == FAIL)                                     \
-    goto label;                                                \
-                                                       \
-  inst.operands[i].reg = val;                          \
-  inst.operands[i].isreg = 1;                          \
-  inst.operands[i].isquad = (rtype == REG_TYPE_NQ);    \
+#define po_reg_or_goto(regtype, label) do {                    \
+  val = arm_typed_reg_parse (&str, regtype, &rtype,            \
+                             &inst.operands[i].vectype);       \
+  if (val == FAIL)                                             \
+    goto label;                                                        \
+                                                               \
+  inst.operands[i].reg = val;                                  \
+  inst.operands[i].isreg = 1;                                  \
+  inst.operands[i].isquad = (rtype == REG_TYPE_NQ);            \
 } while (0)
 
 #define po_imm_or_fail(min, max, popt) do {                    \
 } while (0)
 
 #define po_imm_or_fail(min, max, popt) do {                    \
@@ -4443,12 +4948,12 @@ parse_operands (char *str, const unsigned char *pattern)
   inst.operands[i].imm = val;                                  \
 } while (0)
 
   inst.operands[i].imm = val;                                  \
 } while (0)
 
-#define po_scalar_or_goto(elsz, label) do {    \
-  val = parse_scalar (&str, elsz);             \
-  if (val == FAIL)                             \
-    goto label;                                        \
-  inst.operands[i].reg = val;                  \
-  inst.operands[i].isscalar = 1;               \
+#define po_scalar_or_goto(elsz, label) do {                    \
+  val = parse_scalar (&str, elsz, &inst.operands[i].vectype);  \
+  if (val == FAIL)                                             \
+    goto label;                                                        \
+  inst.operands[i].reg = val;                                  \
+  inst.operands[i].isscalar = 1;                               \
 } while (0)
 
 #define po_misc_or_fail(expr) do {             \
 } while (0)
 
 #define po_misc_or_fail(expr) do {             \
@@ -4761,7 +5266,8 @@ parse_operands (char *str, const unsigned char *pattern)
           break;
 
        case OP_NSTRLST:
           break;
 
        case OP_NSTRLST:
-          val = parse_neon_el_struct_list (&str, &inst.operands[i].reg);
+          val = parse_neon_el_struct_list (&str, &inst.operands[i].reg,
+                                           &inst.operands[i].vectype);
           break;
 
          /* Addressing modes */
           break;
 
          /* Addressing modes */
@@ -4949,7 +5455,7 @@ encode_arm_vfp_reg (int reg, enum vfp_reg_pos pos)
         }
       else
         {
         }
       else
         {
-          inst.error = _("D register out of range for selected VFP version");
+          first_error (_("D register out of range for selected VFP version"));
           return;
         }
     }
           return;
         }
     }
@@ -8996,10 +9502,13 @@ enum neon_type_mask
   N_UNS  = 0x000008, /* if N_EQK, this operand is forced to be unsigned.  */
   N_INT  = 0x000010, /* if N_EQK, this operand is forced to be integer.  */
   N_FLT  = 0x000020, /* if N_EQK, this operand is forced to be float.  */
   N_UNS  = 0x000008, /* if N_EQK, this operand is forced to be unsigned.  */
   N_INT  = 0x000010, /* if N_EQK, this operand is forced to be integer.  */
   N_FLT  = 0x000020, /* if N_EQK, this operand is forced to be float.  */
+  N_SIZ  = 0x000040, /* if N_EQK, this operand is forced to be size-only.  */
   N_UTYP = 0,
   N_MAX_NONSPECIAL = N_F32
 };
 
   N_UTYP = 0,
   N_MAX_NONSPECIAL = N_F32
 };
 
+#define N_ALLMODS  (N_DBL | N_HLF | N_SGN | N_UNS | N_INT | N_FLT | N_SIZ)
+
 #define N_SU_ALL   (N_S8 | N_S16 | N_S32 | N_S64 | N_U8 | N_U16 | N_U32 | N_U64)
 #define N_SU_32    (N_S8 | N_S16 | N_S32 | N_U8 | N_U16 | N_U32)
 #define N_SU_16_64 (N_S16 | N_S32 | N_S64 | N_U16 | N_U32 | N_U64)
 #define N_SU_ALL   (N_S8 | N_S16 | N_S32 | N_S64 | N_U8 | N_U16 | N_U32 | N_U64)
 #define N_SU_32    (N_S8 | N_S16 | N_S32 | N_U8 | N_U16 | N_U32)
 #define N_SU_16_64 (N_S16 | N_S32 | N_S64 | N_U16 | N_U32 | N_U64)
@@ -9037,11 +9546,11 @@ neon_check_shape (enum neon_shape req)
       {
         if (RD(0) && RD(1) && RD(2))
           return NS_DDD;
       {
         if (RD(0) && RD(1) && RD(2))
           return NS_DDD;
-        else if (RQ(0) && RQ(1) && RQ(1))
+        else if (RQ(0) && RQ(1) && RQ(2))
           return NS_QQQ;
         else
           return NS_QQQ;
         else
-          inst.error = _("expected <Qd>, <Qn>, <Qm> or <Dd>, <Dn>, <Dm> "
-                         "operands");
+          first_error (_("expected <Qd>, <Qn>, <Qm> or <Dd>, <Dn>, <Dm> "
+                         "operands"));
       }
       break;
     
       }
       break;
     
@@ -9052,8 +9561,8 @@ neon_check_shape (enum neon_shape req)
         else if (RQ(0) && RQ(1) && IM(2))
           return NS_QQI;
         else
         else if (RQ(0) && RQ(1) && IM(2))
           return NS_QQI;
         else
-          inst.error = _("expected <Qd>, <Qn>, #<imm> or <Dd>, <Dn>, #<imm> "
-                         "operands");
+          first_error (_("expected <Qd>, <Qn>, #<imm> or <Dd>, <Dn>, #<imm> "
+                         "operands"));
       }
         break;
   
       }
         break;
   
@@ -9064,8 +9573,8 @@ neon_check_shape (enum neon_shape req)
         if (RQ(0) && RQ(1) && RQ(2) && IM(3))
           return NS_QQQI;
         else
         if (RQ(0) && RQ(1) && RQ(2) && IM(3))
           return NS_QQQI;
         else
-          inst.error = _("expected <Qd>, <Qn>, <Qm>, #<imm> or "
-                         "<Dd>, <Dn>, <Dm>, #<imm> operands");
+          first_error (_("expected <Qd>, <Qn>, <Qm>, #<imm> or "
+                         "<Dd>, <Dn>, <Dm>, #<imm> operands"));
       }
         break;
   
       }
         break;
   
@@ -9076,8 +9585,8 @@ neon_check_shape (enum neon_shape req)
         else if (RQ(0) && RQ(1) && SC(2))
           return NS_QQS;
         else
         else if (RQ(0) && RQ(1) && SC(2))
           return NS_QQS;
         else
-          inst.error = _("expected <Qd>, <Qn>, <Dm[x]> or <Dd>, <Dn>, <Dm[x]> "
-                         "operands");
+          first_error (_("expected <Qd>, <Qn>, <Dm[x]> or <Dd>, <Dn>, <Dm[x]> "
+                         "operands"));
       }
       break;
   
       }
       break;
   
@@ -9088,7 +9597,7 @@ neon_check_shape (enum neon_shape req)
         else if (RQ(0) && RQ(1))
           return NS_QQ;
         else
         else if (RQ(0) && RQ(1))
           return NS_QQ;
         else
-          inst.error = _("expected <Qd>, <Qm> or <Dd>, <Dm> operands");
+          first_error (_("expected <Qd>, <Qm> or <Dd>, <Dm> operands"));
       }
       break;
   
       }
       break;
   
@@ -9099,7 +9608,7 @@ neon_check_shape (enum neon_shape req)
         else if (RQ(0) && SC(1))
           return NS_QS;
         else
         else if (RQ(0) && SC(1))
           return NS_QS;
         else
-          inst.error = _("expected <Qd>, <Dm[x]> or <Dd>, <Dm[x]> operands");
+          first_error (_("expected <Qd>, <Dm[x]> or <Dd>, <Dm[x]> operands"));
       }
       break;
 
       }
       break;
 
@@ -9110,7 +9619,7 @@ neon_check_shape (enum neon_shape req)
         else if (RQ(0) && RR(1))
           return NS_QR;
         else
         else if (RQ(0) && RR(1))
           return NS_QR;
         else
-          inst.error = _("expected <Qd>, <Rm> or <Dd>, <Rm> operands");
+          first_error (_("expected <Qd>, <Rm> or <Dd>, <Rm> operands"));
       }
       break;
 
       }
       break;
 
@@ -9121,7 +9630,7 @@ neon_check_shape (enum neon_shape req)
         else if (RQ(0) && IM(1))
           return NS_QI;
         else
         else if (RQ(0) && IM(1))
           return NS_QI;
         else
-          inst.error = _("expected <Qd>, #<imm> or <Dd>, #<imm> operands");
+          first_error (_("expected <Qd>, #<imm> or <Dd>, #<imm> operands"));
       }
       break;
    
       }
       break;
    
@@ -9157,6 +9666,8 @@ neon_modify_type_size (unsigned typebits, enum neon_el_type *g_type,
         *g_type = NT_integer;
       else if ((typebits & N_FLT) != 0)
         *g_type = NT_float;
         *g_type = NT_integer;
       else if ((typebits & N_FLT) != 0)
         *g_type = NT_float;
+      else if ((typebits & N_SIZ) != 0)
+        *g_type = NT_untyped;
     }
 }
   
     }
 }
   
@@ -9250,30 +9761,40 @@ type_chk_of_el_type (enum neon_el_type type, unsigned size)
 /* Convert compact Neon bitmask type representation to a type and size. Only
    handles the case where a single bit is set in the mask.  */
 
 /* Convert compact Neon bitmask type representation to a type and size. Only
    handles the case where a single bit is set in the mask.  */
 
-static void
+static int
 el_type_of_type_chk (enum neon_el_type *type, unsigned *size,
                      enum neon_type_mask mask)
 {
 el_type_of_type_chk (enum neon_el_type *type, unsigned *size,
                      enum neon_type_mask mask)
 {
+  if ((mask & N_EQK) != 0)
+    return FAIL;
+
   if ((mask & (N_S8 | N_U8 | N_I8 | N_8 | N_P8)) != 0)
     *size = 8;
   if ((mask & (N_S8 | N_U8 | N_I8 | N_8 | N_P8)) != 0)
     *size = 8;
-  if ((mask & (N_S16 | N_U16 | N_I16 | N_16 | N_P16)) != 0)
+  else if ((mask & (N_S16 | N_U16 | N_I16 | N_16 | N_P16)) != 0)
     *size = 16;
     *size = 16;
-  if ((mask & (N_S32 | N_U32 | N_I32 | N_32 | N_F32)) != 0)
+  else if ((mask & (N_S32 | N_U32 | N_I32 | N_32 | N_F32)) != 0)
     *size = 32;
     *size = 32;
-  if ((mask & (N_S64 | N_U64 | N_I64 | N_64)) != 0)
+  else if ((mask & (N_S64 | N_U64 | N_I64 | N_64)) != 0)
     *size = 64;
     *size = 64;
+  else
+    return FAIL;
+
   if ((mask & (N_S8 | N_S16 | N_S32 | N_S64)) != 0)
     *type = NT_signed;
   if ((mask & (N_S8 | N_S16 | N_S32 | N_S64)) != 0)
     *type = NT_signed;
-  if ((mask & (N_U8 | N_U16 | N_U32 | N_U64)) != 0)
+  else if ((mask & (N_U8 | N_U16 | N_U32 | N_U64)) != 0)
     *type = NT_unsigned;
     *type = NT_unsigned;
-  if ((mask & (N_I8 | N_I16 | N_I32 | N_I64)) != 0)
+  else if ((mask & (N_I8 | N_I16 | N_I32 | N_I64)) != 0)
     *type = NT_integer;
     *type = NT_integer;
-  if ((mask & (N_8 | N_16 | N_32 | N_64)) != 0)
+  else if ((mask & (N_8 | N_16 | N_32 | N_64)) != 0)
     *type = NT_untyped;
     *type = NT_untyped;
-  if ((mask & (N_P8 | N_P16)) != 0)
+  else if ((mask & (N_P8 | N_P16)) != 0)
     *type = NT_poly;
     *type = NT_poly;
-  if ((mask & N_F32) != 0)
+  else if ((mask & N_F32) != 0)
     *type = NT_float;
     *type = NT_float;
+  else
+    return FAIL;
+  
+  return SUCCESS;
 }
 
 /* Modify a bitmask of allowed types. This is only needed for type
 }
 
 /* Modify a bitmask of allowed types. This is only needed for type
@@ -9291,9 +9812,11 @@ modify_types_allowed (unsigned allowed, unsigned mods)
   
   for (i = 1; i <= N_MAX_NONSPECIAL; i <<= 1)
     {
   
   for (i = 1; i <= N_MAX_NONSPECIAL; i <<= 1)
     {
-      el_type_of_type_chk (&type, &size, allowed & i);
-      neon_modify_type_size (mods, &type, &size);
-      destmask |= type_chk_of_el_type (type, size);
+      if (el_type_of_type_chk (&type, &size, allowed & i) == SUCCESS)
+        {
+          neon_modify_type_size (mods, &type, &size);
+          destmask |= type_chk_of_el_type (type, size);
+        }
     }
   
   return destmask;
     }
   
   return destmask;
@@ -9344,6 +9867,14 @@ neon_check_type (unsigned els, enum neon_shape ns, ...)
     }
   va_end (ap);
 
     }
   va_end (ap);
 
+  if (inst.vectype.elems > 0)
+    for (i = 0; i < els; i++)
+      if (inst.operands[i].vectype.type != NT_invtype)
+        {
+          first_error (_("types specified in both the mnemonic and operands"));
+          return badtype;
+        }
+
   /* Duplicate inst.vectype elements here as necessary.
      FIXME: No idea if this is exactly the same as the ARM assembler,
      particularly when an insn takes one register and one non-register
   /* Duplicate inst.vectype elements here as necessary.
      FIXME: No idea if this is exactly the same as the ARM assembler,
      particularly when an insn takes one register and one non-register
@@ -9354,15 +9885,36 @@ neon_check_type (unsigned els, enum neon_shape ns, ...)
       inst.vectype.elems = els;
       inst.vectype.el[key_el] = inst.vectype.el[0];
       for (j = 0; j < els; j++)
       inst.vectype.elems = els;
       inst.vectype.el[key_el] = inst.vectype.el[0];
       for (j = 0; j < els; j++)
+        if (j != key_el)
+          inst.vectype.el[j] = neon_type_promote (&inst.vectype.el[key_el],
+                                                  types[j]);
+    }
+  else if (inst.vectype.elems == 0 && els > 0)
+    {
+      unsigned j;
+      /* No types were given after the mnemonic, so look for types specified
+         after each operand. We allow some flexibility here; as long as the
+         "key" operand has a type, we can infer the others.  */
+      for (j = 0; j < els; j++)
+        if (inst.operands[j].vectype.type != NT_invtype)
+          inst.vectype.el[j] = inst.operands[j].vectype;
+
+      if (inst.operands[key_el].vectype.type != NT_invtype)
         {
         {
-          if (j != key_el)
-            inst.vectype.el[j] = neon_type_promote (&inst.vectype.el[key_el],
-                                                    types[j]);
+          for (j = 0; j < els; j++)
+            if (inst.operands[j].vectype.type == NT_invtype)
+              inst.vectype.el[j] = neon_type_promote (&inst.vectype.el[key_el],
+                                                      types[j]);
+        }
+      else
+        {
+          first_error (_("operand types can't be inferred"));
+          return badtype;
         }
     }
   else if (inst.vectype.elems != els)
     {
         }
     }
   else if (inst.vectype.elems != els)
     {
-      inst.error = _("type specifier has the wrong number of parts");
+      first_error (_("type specifier has the wrong number of parts"));
       return badtype;
     }
 
       return badtype;
     }
 
@@ -9408,7 +9960,7 @@ neon_check_type (unsigned els, enum neon_shape ns, ...)
 
                   if ((given_type & types_allowed) == 0)
                     {
 
                   if ((given_type & types_allowed) == 0)
                     {
-                     inst.error = _("bad type in Neon instruction");
+                     first_error (_("bad type in Neon instruction"));
                      return badtype;
                     }
                 }
                      return badtype;
                     }
                 }
@@ -9419,7 +9971,7 @@ neon_check_type (unsigned els, enum neon_shape ns, ...)
                   neon_modify_type_size (thisarg, &mod_k_type, &mod_k_size);
                   if (g_type != mod_k_type || g_size != mod_k_size)
                     {
                   neon_modify_type_size (thisarg, &mod_k_type, &mod_k_size);
                   if (g_type != mod_k_type || g_size != mod_k_size)
                     {
-                      inst.error = _("inconsistent types in Neon instruction");
+                      first_error (_("inconsistent types in Neon instruction"));
                       return badtype;
                     }
                 }
                       return badtype;
                     }
                 }
@@ -9473,12 +10025,8 @@ neon_logbits (unsigned x)
   different meaning for some instruction.  */
 
 static void
   different meaning for some instruction.  */
 
 static void
-neon_three_same (int first_optional, int isquad, int ubit, int size)
+neon_three_same (int isquad, int ubit, int size)
 {
 {
-  /* FIXME optional argument handling.  */
-  if (first_optional && !inst.operands[0].present)
-    inst.operands[0].reg = inst.operands[1].reg;
-
   inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
   inst.instruction |= HI1 (inst.operands[0].reg) << 22;
   inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
   inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
   inst.instruction |= HI1 (inst.operands[0].reg) << 22;
   inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
@@ -9524,7 +10072,7 @@ do_neon_dyadic_i_su (void)
   enum neon_shape rs = neon_check_shape (NS_DDD_QQQ);
   struct neon_type_el et = neon_check_type (3, rs,
     N_EQK, N_EQK, N_SU_32 | N_KEY);
   enum neon_shape rs = neon_check_shape (NS_DDD_QQQ);
   struct neon_type_el et = neon_check_type (3, rs,
     N_EQK, N_EQK, N_SU_32 | N_KEY);
-  neon_three_same (TRUE, rs == NS_QQQ, et.type == NT_unsigned, et.size);
+  neon_three_same (rs == NS_QQQ, et.type == NT_unsigned, et.size);
 }
 
 static void
 }
 
 static void
@@ -9533,7 +10081,7 @@ do_neon_dyadic_i64_su (void)
   enum neon_shape rs = neon_check_shape (NS_DDD_QQQ);
   struct neon_type_el et = neon_check_type (3, rs,
     N_EQK, N_EQK, N_SU_ALL | N_KEY);
   enum neon_shape rs = neon_check_shape (NS_DDD_QQQ);
   struct neon_type_el et = neon_check_type (3, rs,
     N_EQK, N_EQK, N_SU_ALL | N_KEY);
-  neon_three_same (TRUE, rs == NS_QQQ, et.type == NT_unsigned, et.size);
+  neon_three_same (rs == NS_QQQ, et.type == NT_unsigned, et.size);
 }
 
 static void
 }
 
 static void
@@ -9571,7 +10119,7 @@ do_neon_shl_imm (void)
       struct neon_type_el et = neon_check_type (3, rs,
         N_EQK, N_SU_ALL | N_KEY, N_EQK | N_SGN);
       inst.instruction = NEON_ENC_INTEGER (inst.instruction);
       struct neon_type_el et = neon_check_type (3, rs,
         N_EQK, N_SU_ALL | N_KEY, N_EQK | N_SGN);
       inst.instruction = NEON_ENC_INTEGER (inst.instruction);
-      neon_three_same (TRUE, rs == NS_QQQ, et.type == NT_unsigned, et.size);
+      neon_three_same (rs == NS_QQQ, et.type == NT_unsigned, et.size);
     }
 }
 
     }
 }
 
@@ -9592,7 +10140,7 @@ do_neon_qshl_imm (void)
       struct neon_type_el et = neon_check_type (3, rs,
         N_EQK, N_SU_ALL | N_KEY, N_EQK | N_SGN);
       inst.instruction = NEON_ENC_INTEGER (inst.instruction);
       struct neon_type_el et = neon_check_type (3, rs,
         N_EQK, N_SU_ALL | N_KEY, N_EQK | N_SGN);
       inst.instruction = NEON_ENC_INTEGER (inst.instruction);
-      neon_three_same (TRUE, rs == NS_QQQ, et.type == NT_unsigned, et.size);
+      neon_three_same (rs == NS_QQQ, et.type == NT_unsigned, et.size);
     }
 }
 
     }
 }
 
@@ -9641,7 +10189,7 @@ neon_cmode_for_logic_imm (unsigned immediate, unsigned *immbits, int size)
     }
 
   bad_immediate:
     }
 
   bad_immediate:
-  inst.error = _("immediate value out of range");
+  first_error (_("immediate value out of range"));
   return FAIL;
 }
 
   return FAIL;
 }
 
@@ -9827,7 +10375,7 @@ do_neon_logic (void)
       neon_check_type (3, rs, N_IGNORE_TYPE);
       /* U bit and size field were set as part of the bitmask.  */
       inst.instruction = NEON_ENC_INTEGER (inst.instruction);
       neon_check_type (3, rs, N_IGNORE_TYPE);
       /* U bit and size field were set as part of the bitmask.  */
       inst.instruction = NEON_ENC_INTEGER (inst.instruction);
-      neon_three_same (TRUE, rs == NS_QQQ, 0, -1);
+      neon_three_same (rs == NS_QQQ, 0, -1);
     }
   else
     {
     }
   else
     {
@@ -9890,31 +10438,33 @@ static void
 do_neon_bitfield (void)
 {
   enum neon_shape rs = neon_check_shape (NS_DDD_QQQ);
 do_neon_bitfield (void)
 {
   enum neon_shape rs = neon_check_shape (NS_DDD_QQQ);
-  /* FIXME: Check that no type was given.  */
-  neon_three_same (FALSE, rs == NS_QQQ, 0, -1);
+  neon_check_type (3, rs, N_IGNORE_TYPE);
+  neon_three_same (rs == NS_QQQ, 0, -1);
 }
 
 static void
 }
 
 static void
-neon_dyadic (enum neon_el_type ubit_meaning, unsigned types)
+neon_dyadic_misc (enum neon_el_type ubit_meaning, unsigned types,
+                  unsigned destbits)
 {
   enum neon_shape rs = neon_check_shape (NS_DDD_QQQ);
 {
   enum neon_shape rs = neon_check_shape (NS_DDD_QQQ);
-  struct neon_type_el et = neon_check_type (3, rs, N_EQK, N_EQK, types | N_KEY);
+  struct neon_type_el et = neon_check_type (3, rs, N_EQK | destbits, N_EQK,
+                                            types | N_KEY);
   if (et.type == NT_float)
     {
       inst.instruction = NEON_ENC_FLOAT (inst.instruction);
   if (et.type == NT_float)
     {
       inst.instruction = NEON_ENC_FLOAT (inst.instruction);
-      neon_three_same (TRUE, rs == NS_QQQ, 0, -1);
+      neon_three_same (rs == NS_QQQ, 0, -1);
     }
   else
     {
       inst.instruction = NEON_ENC_INTEGER (inst.instruction);
     }
   else
     {
       inst.instruction = NEON_ENC_INTEGER (inst.instruction);
-      neon_three_same (TRUE, rs == NS_QQQ, et.type == ubit_meaning, et.size);
+      neon_three_same (rs == NS_QQQ, et.type == ubit_meaning, et.size);
     }
 }
 
 static void
 do_neon_dyadic_if_su (void)
 {
     }
 }
 
 static void
 do_neon_dyadic_if_su (void)
 {
-  neon_dyadic (NT_unsigned, N_SUF_32);
+  neon_dyadic_misc (NT_unsigned, N_SUF_32, 0);
 }
 
 static void
 }
 
 static void
@@ -9922,19 +10472,19 @@ do_neon_dyadic_if_su_d (void)
 {
   /* This version only allow D registers, but that constraint is enforced during
      operand parsing so we don't need to do anything extra here.  */
 {
   /* This version only allow D registers, but that constraint is enforced during
      operand parsing so we don't need to do anything extra here.  */
-  neon_dyadic (NT_unsigned, N_SUF_32);
+  neon_dyadic_misc (NT_unsigned, N_SUF_32, 0);
 }
 
 static void
 do_neon_dyadic_if_i (void)
 {
 }
 
 static void
 do_neon_dyadic_if_i (void)
 {
-  neon_dyadic (NT_unsigned, N_IF_32);
+  neon_dyadic_misc (NT_unsigned, N_IF_32, 0);
 }
 
 static void
 do_neon_dyadic_if_i_d (void)
 {
 }
 
 static void
 do_neon_dyadic_if_i_d (void)
 {
-  neon_dyadic (NT_unsigned, N_IF_32);
+  neon_dyadic_misc (NT_unsigned, N_IF_32, 0);
 }
 
 static void
 }
 
 static void
@@ -9942,7 +10492,7 @@ do_neon_addsub_if_i (void)
 {
   /* The "untyped" case can't happen. Do this to stop the "U" bit being
      affected if we specify unsigned args.  */
 {
   /* The "untyped" case can't happen. Do this to stop the "U" bit being
      affected if we specify unsigned args.  */
-  neon_dyadic (NT_untyped, N_IF_32 | N_I64);
+  neon_dyadic_misc (NT_untyped, N_IF_32 | N_I64, 0);
 }
 
 /* Swaps operands 1 and 2. If operand 1 (optional arg) was omitted, we want the
 }
 
 /* Swaps operands 1 and 2. If operand 1 (optional arg) was omitted, we want the
@@ -9979,12 +10529,13 @@ neon_compare (unsigned regtypes, unsigned immtypes, int invert)
     {
       if (invert)
         neon_exchange_operands ();
     {
       if (invert)
         neon_exchange_operands ();
-      neon_dyadic (NT_unsigned, regtypes);
+      neon_dyadic_misc (NT_unsigned, regtypes, N_SIZ);
     }
   else
     {
       enum neon_shape rs = neon_check_shape (NS_DDI_QQI);
     }
   else
     {
       enum neon_shape rs = neon_check_shape (NS_DDI_QQI);
-      struct neon_type_el et = neon_check_type (2, rs, N_EQK, immtypes | N_KEY);
+      struct neon_type_el et = neon_check_type (2, rs,
+        N_EQK | N_SIZ, immtypes | N_KEY);
 
       inst.instruction = NEON_ENC_IMMED (inst.instruction);
       inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
 
       inst.instruction = NEON_ENC_IMMED (inst.instruction);
       inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
@@ -10026,8 +10577,8 @@ do_neon_ceq (void)
 static unsigned
 neon_scalar_for_mul (unsigned scalar, unsigned elsize)
 {
 static unsigned
 neon_scalar_for_mul (unsigned scalar, unsigned elsize)
 {
-  unsigned regno = scalar >> 3;
-  unsigned elno = scalar & 7;
+  unsigned regno = NEON_SCALAR_REG (scalar);
+  unsigned elno = NEON_SCALAR_INDEX (scalar);
 
   switch (elsize)
     {
 
   switch (elsize)
     {
@@ -10043,7 +10594,7 @@ neon_scalar_for_mul (unsigned scalar, unsigned elsize)
 
     default:
     bad_scalar:
 
     default:
     bad_scalar:
-      as_bad (_("Scalar out of range for multiply instruction"));
+      first_error (_("scalar out of range for multiply instruction"));
     }
 
   return 0;
     }
 
   return 0;
@@ -10054,7 +10605,13 @@ neon_scalar_for_mul (unsigned scalar, unsigned elsize)
 static void
 neon_mul_mac (struct neon_type_el et, int ubit)
 {
 static void
 neon_mul_mac (struct neon_type_el et, int ubit)
 {
-  unsigned scalar = neon_scalar_for_mul (inst.operands[2].reg, et.size);
+  unsigned scalar;
+
+  /* Give a more helpful error message if we have an invalid type.  */
+  if (et.type == NT_invtype)
+    return;
+  
+  scalar = neon_scalar_for_mul (inst.operands[2].reg, et.size);
   inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
   inst.instruction |= HI1 (inst.operands[0].reg) << 22;
   inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
   inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
   inst.instruction |= HI1 (inst.operands[0].reg) << 22;
   inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
@@ -10089,7 +10646,7 @@ do_neon_tst (void)
   enum neon_shape rs = neon_check_shape (NS_DDD_QQQ);
   struct neon_type_el et = neon_check_type (3, rs,
     N_EQK, N_EQK, N_8 | N_16 | N_32 | N_KEY);
   enum neon_shape rs = neon_check_shape (NS_DDD_QQQ);
   struct neon_type_el et = neon_check_type (3, rs,
     N_EQK, N_EQK, N_8 | N_16 | N_32 | N_KEY);
-  neon_three_same (TRUE, rs == NS_QQQ, 0, et.size);
+  neon_three_same (rs == NS_QQQ, 0, et.size);
 }
 
 /* VMUL with 3 registers allows the P8 type. The scalar version supports the
 }
 
 /* VMUL with 3 registers allows the P8 type. The scalar version supports the
@@ -10102,7 +10659,7 @@ do_neon_mul (void)
   if (inst.operands[2].isscalar)
     do_neon_mac_maybe_scalar ();
   else
   if (inst.operands[2].isscalar)
     do_neon_mac_maybe_scalar ();
   else
-    neon_dyadic (NT_poly, N_I8 | N_I16 | N_I32 | N_F32 | N_P8);
+    neon_dyadic_misc (NT_poly, N_I8 | N_I16 | N_I32 | N_F32 | N_P8, 0);
 }
 
 static void
 }
 
 static void
@@ -10123,7 +10680,7 @@ do_neon_qdmulh (void)
         N_EQK, N_EQK, N_S16 | N_S32 | N_KEY);
       inst.instruction = NEON_ENC_INTEGER (inst.instruction);
       /* The U bit (rounding) comes from bit mask.  */
         N_EQK, N_EQK, N_S16 | N_S32 | N_KEY);
       inst.instruction = NEON_ENC_INTEGER (inst.instruction);
       /* The U bit (rounding) comes from bit mask.  */
-      neon_three_same (TRUE, rs == NS_QQQ, 0, et.size);
+      neon_three_same (rs == NS_QQQ, 0, et.size);
     }
 }
 
     }
 }
 
@@ -10133,7 +10690,7 @@ do_neon_fcmp_absolute (void)
   enum neon_shape rs = neon_check_shape (NS_DDD_QQQ);
   neon_check_type (3, rs, N_EQK, N_EQK, N_F32 | N_KEY);
   /* Size field comes from bit mask.  */
   enum neon_shape rs = neon_check_shape (NS_DDD_QQQ);
   neon_check_type (3, rs, N_EQK, N_EQK, N_F32 | N_KEY);
   /* Size field comes from bit mask.  */
-  neon_three_same (TRUE, rs == NS_QQQ, 1, -1);
+  neon_three_same (rs == NS_QQQ, 1, -1);
 }
 
 static void
 }
 
 static void
@@ -10148,7 +10705,7 @@ do_neon_step (void)
 {
   enum neon_shape rs = neon_check_shape (NS_DDD_QQQ);
   neon_check_type (3, rs, N_EQK, N_EQK, N_F32 | N_KEY);
 {
   enum neon_shape rs = neon_check_shape (NS_DDD_QQQ);
   neon_check_type (3, rs, N_EQK, N_EQK, N_F32 | N_KEY);
-  neon_three_same (TRUE, rs == NS_QQQ, 0, -1);
+  neon_three_same (rs == NS_QQQ, 0, -1);
 }
 
 static void
 }
 
 static void
@@ -10450,7 +11007,7 @@ neon_move_immediate (void)
       if ((cmode = neon_cmode_for_move_imm (immlo, immhi, &immbits, &op,
                                             et.size)) == FAIL)
         {
       if ((cmode = neon_cmode_for_move_imm (immlo, immhi, &immbits, &op,
                                             et.size)) == FAIL)
         {
-          inst.error = _("immediate out of range");
+          first_error (_("immediate out of range"));
           return;
         }
     }
           return;
         }
     }
@@ -10533,8 +11090,8 @@ neon_mac_reg_scalar_long (unsigned regtypes, unsigned scalartypes)
 {
   if (inst.operands[2].isscalar)
     {
 {
   if (inst.operands[2].isscalar)
     {
-      struct neon_type_el et = neon_check_type (2, NS_QDS,
-        N_EQK | N_DBL, regtypes | N_KEY);
+      struct neon_type_el et = neon_check_type (3, NS_QDS,
+        N_EQK | N_DBL, N_EQK, regtypes | N_KEY);
       inst.instruction = NEON_ENC_SCALAR (inst.instruction);
       neon_mul_mac (et, et.type == NT_unsigned);
     }
       inst.instruction = NEON_ENC_SCALAR (inst.instruction);
       neon_mul_mac (et, et.type == NT_unsigned);
     }
@@ -10636,11 +11193,12 @@ do_neon_dup (void)
   if (inst.operands[1].isscalar)
     {
       enum neon_shape rs = neon_check_shape (NS_DS_QS);
   if (inst.operands[1].isscalar)
     {
       enum neon_shape rs = neon_check_shape (NS_DS_QS);
-      struct neon_type_el et = neon_check_type (1, rs, N_8 | N_16 | N_32);
+      struct neon_type_el et = neon_check_type (2, rs,
+        N_EQK, N_8 | N_16 | N_32 | N_KEY);
       unsigned sizebits = et.size >> 3;
       unsigned sizebits = et.size >> 3;
-      unsigned dm = inst.operands[1].reg >> 3;
+      unsigned dm = NEON_SCALAR_REG (inst.operands[1].reg);
       int logsize = neon_logbits (et.size);
       int logsize = neon_logbits (et.size);
-      unsigned x = (inst.operands[1].reg & 7) << logsize;
+      unsigned x = NEON_SCALAR_INDEX (inst.operands[1].reg) << logsize;
       inst.instruction = NEON_ENC_SCALAR (inst.instruction);
       inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
       inst.instruction |= HI1 (inst.operands[0].reg) << 22;
       inst.instruction = NEON_ENC_SCALAR (inst.instruction);
       inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
       inst.instruction |= HI1 (inst.operands[0].reg) << 22;
@@ -10655,7 +11213,8 @@ do_neon_dup (void)
   else
     {
       enum neon_shape rs = neon_check_shape (NS_DR_QR);
   else
     {
       enum neon_shape rs = neon_check_shape (NS_DR_QR);
-      struct neon_type_el et = neon_check_type (1, rs, N_8 | N_16 | N_32);
+      struct neon_type_el et = neon_check_type (1, rs,
+        N_8 | N_16 | N_32 | N_KEY);
       unsigned save_cond = inst.instruction & 0xf0000000;
       /* Duplicate ARM register to lanes of vector.  */
       inst.instruction = NEON_ENC_ARMREG (inst.instruction);
       unsigned save_cond = inst.instruction & 0xf0000000;
       /* Duplicate ARM register to lanes of vector.  */
       inst.instruction = NEON_ENC_ARMREG (inst.instruction);
@@ -10720,13 +11279,14 @@ do_neon_mov (void)
       if (inst.operands[1].isscalar)
         {
           /* Case 6.  */
       if (inst.operands[1].isscalar)
         {
           /* Case 6.  */
-          struct neon_type_el et = neon_check_type (1, NS_IGNORE,
-            N_S8 | N_S16 | N_U8 | N_U16 | N_32);
+          struct neon_type_el et = neon_check_type (2, NS_IGNORE,
+            N_EQK, N_S8 | N_S16 | N_U8 | N_U16 | N_32 | N_KEY);
           unsigned logsize = neon_logbits (et.size);
           unsigned logsize = neon_logbits (et.size);
-          unsigned dn = inst.operands[1].reg >> 3;
-          unsigned x = inst.operands[1].reg & 7;
+          unsigned dn = NEON_SCALAR_REG (inst.operands[1].reg);
+          unsigned x = NEON_SCALAR_INDEX (inst.operands[1].reg);
           unsigned abcdebits = 0;
 
           unsigned abcdebits = 0;
 
+          constraint (et.type == NT_invtype, _("bad type for scalar"));
           constraint (x >= 64 / et.size, _("scalar index out of range"));
 
           switch (et.size)
           constraint (x >= 64 / et.size, _("scalar index out of range"));
 
           switch (et.size)
@@ -10753,12 +11313,13 @@ do_neon_mov (void)
             {
               /* Case 4.  */
               unsigned bcdebits = 0;
             {
               /* Case 4.  */
               unsigned bcdebits = 0;
-              struct neon_type_el et = neon_check_type (1, NS_IGNORE,
-                                                        N_8 | N_16 | N_32);
+              struct neon_type_el et = neon_check_type (2, NS_IGNORE,
+                N_8 | N_16 | N_32 | N_KEY, N_EQK);
               int logsize = neon_logbits (et.size);
               int logsize = neon_logbits (et.size);
-              unsigned dn = inst.operands[0].reg >> 3;
-              unsigned x = inst.operands[0].reg & 7;
+              unsigned dn = NEON_SCALAR_REG (inst.operands[0].reg);
+              unsigned x = NEON_SCALAR_INDEX (inst.operands[0].reg);
 
 
+              constraint (et.type == NT_invtype, _("bad type for scalar"));
               constraint (x >= 64 / et.size, _("scalar index out of range"));
 
               switch (et.size)
               constraint (x >= 64 / et.size, _("scalar index out of range"));
 
               switch (et.size)
@@ -10959,11 +11520,11 @@ static void
 do_neon_tbl_tbx (void)
 {
   unsigned listlenbits;
 do_neon_tbl_tbx (void)
 {
   unsigned listlenbits;
-  neon_check_type (1, NS_DLD, N_8);
+  neon_check_type (3, NS_DLD, N_EQK, N_EQK, N_8 | N_KEY);
   
   if (inst.operands[1].imm < 1 || inst.operands[1].imm > 4)
     {
   
   if (inst.operands[1].imm < 1 || inst.operands[1].imm > 4)
     {
-      inst.error = _("bad list length for table lookup");
+      first_error (_("bad list length for table lookup"));
       return;
     }
   
       return;
     }
   
@@ -11080,6 +11641,9 @@ do_neon_ld_st_interleave (void)
     };
   int typebits;
 
     };
   int typebits;
 
+  if (et.type == NT_invtype)
+    return;
+
   if (inst.operands[1].immisalign)
     switch (inst.operands[1].imm >> 8)
       {
   if (inst.operands[1].immisalign)
     switch (inst.operands[1].imm >> 8)
       {
@@ -11096,7 +11660,7 @@ do_neon_ld_st_interleave (void)
         break;
       default:
       bad_alignment:
         break;
       default:
       bad_alignment:
-        inst.error = _("bad alignment");
+        first_error (_("bad alignment"));
         return;
       }
 
         return;
       }
 
@@ -11155,7 +11719,7 @@ neon_alignment_bit (int size, int align, int *do_align, ...)
   if (result == SUCCESS)
     *do_align = 1;
   else
   if (result == SUCCESS)
     *do_align = 1;
   else
-    inst.error = _("unsupported alignment for instruction");
+    first_error (_("unsupported alignment for instruction"));
     
   return result;
 }
     
   return result;
 }
@@ -11170,6 +11734,9 @@ do_neon_ld_st_lane (void)
   int n = (inst.instruction >> 8) & 3;
   int max_el = 64 / et.size;
   
   int n = (inst.instruction >> 8) & 3;
   int max_el = 64 / et.size;
   
+  if (et.type == NT_invtype)
+    return;
+  
   constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != n + 1,
               _("bad list length"));
   constraint (NEON_LANE (inst.operands[0].imm) >= max_el,
   constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != n + 1,
               _("bad list length"));
   constraint (NEON_LANE (inst.operands[0].imm) >= max_el,
@@ -11250,6 +11817,9 @@ do_neon_ld_dup (void)
   struct neon_type_el et = neon_check_type (1, NS_IGNORE, N_8 | N_16 | N_32);
   int align_good, do_align = 0;
 
   struct neon_type_el et = neon_check_type (1, NS_IGNORE, N_8 | N_16 | N_32);
   int align_good, do_align = 0;
 
+  if (et.type == NT_invtype)
+    return;
+
   switch ((inst.instruction >> 8) & 3)
     {
     case 0:  /* VLD1.  */
   switch ((inst.instruction >> 8) & 3)
     {
     case 0:  /* VLD1.  */
@@ -11262,7 +11832,7 @@ do_neon_ld_dup (void)
         {
         case 1: break;
         case 2: inst.instruction |= 1 << 5; break;
         {
         case 1: break;
         case 2: inst.instruction |= 1 << 5; break;
-        default: inst.error = _("bad list length"); return;
+        default: first_error (_("bad list length")); return;
         }
       inst.instruction |= neon_logbits (et.size) << 6;
       break;
         }
       inst.instruction |= neon_logbits (et.size) << 6;
       break;
@@ -11488,85 +12058,6 @@ output_inst (const char * str)
 #endif
 }
 
 #endif
 }
 
-/* Parse a Neon type specifier. *STR should point at the leading '.'
-   character. Does no verification at this stage that the type fits the opcode
-   properly. E.g.,
-
-     .i32.i32.s16
-     .s32.f32
-     .u16
-
-   Can all be legally parsed by this function.
-
-   Fills in neon_type struct pointer with parsed information, and updates STR
-   to point after the parsed type specifier. Returns TRUE if this was a legal
-   type, FALSE if not.  */
-
-static bfd_boolean
-parse_neon_type (struct neon_type *type, char **str)
-{
-  char *ptr = *str;
-
-  if (type)
-    type->elems = 0;
-
-  while (type->elems < NEON_MAX_TYPE_ELS)
-    {
-      enum neon_el_type thistype = NT_untyped;
-      unsigned thissize = -1u;
-
-      if (*ptr != '.')
-       break;
-
-      ptr++;
-
-      /* Just a size without an explicit type.  */
-      if (ISDIGIT (*ptr))
-       goto parsesize;
-
-      switch (*ptr)
-       {
-       case 'i': thistype = NT_integer; break;
-       case 'f': thistype = NT_float; break;
-       case 'p': thistype = NT_poly; break;
-       case 's': thistype = NT_signed; break;
-       case 'u': thistype = NT_unsigned; break;
-       default:
-         as_bad (_("Unexpected character `%c' in type specifier"), *ptr);
-         return 0;
-       }
-
-      ptr++;
-
-      /* .f is an abbreviation for .f32.  */
-      if (thistype == NT_float && !ISDIGIT (*ptr))
-       thissize = 32;
-      else
-       {
-       parsesize:
-         thissize = strtoul (ptr, &ptr, 10);
-
-         if (thissize != 8 && thissize != 16 && thissize != 32
-              && thissize != 64)
-            {
-              as_bad (_("Bad size %d in type specifier"), thissize);
-             return 0;
-           }
-       }
-
-      if (type)
-        {
-          type->el[type->elems].type = thistype;
-         type->el[type->elems].size = thissize;
-         type->elems++;
-       }
-    }
-
-  *str = ptr;
-
-  return 1;
-}
-
 /* Tag values used in struct asm_opcode's tag field.  */
 enum opcode_tag
 {
 /* Tag values used in struct asm_opcode's tag field.  */
 enum opcode_tag
 {
@@ -11685,7 +12176,7 @@ opcode_lookup (char **str)
       if (end[offset] == '.')      
        {
          /* See if we have a Neon type suffix.  */
       if (end[offset] == '.')      
        {
          /* See if we have a Neon type suffix.  */
-          if (!parse_neon_type (&inst.vectype, str))
+          if (parse_neon_type (&inst.vectype, str) == FAIL)
            return 0;
         }
       else if (end[offset] != '\0' && end[offset] != ' ')
            return 0;
         }
       else if (end[offset] != '\0' && end[offset] != ' ')
@@ -11815,8 +12306,9 @@ md_assemble (char *str)
   if (!opcode)
     {
       /* It wasn't an instruction, but it might be a register alias of
   if (!opcode)
     {
       /* It wasn't an instruction, but it might be a register alias of
-        the form alias .req reg.  */
-      if (!create_register_alias (str, p))
+        the form alias .req reg, or a Neon .dn/.qn directive.  */
+      if (!create_register_alias (str, p)
+          && !create_neon_reg_alias (str, p))
        as_bad (_("bad instruction `%s'"), str);
 
       return;
        as_bad (_("bad instruction `%s'"), str);
 
       return;
@@ -12031,7 +12523,7 @@ arm_canonicalize_symbol_name (char * name)
    should appear in both upper and lowercase variants. Some registers
    also have mixed-case names. */
 
    should appear in both upper and lowercase variants. Some registers
    also have mixed-case names. */
 
-#define REGDEF(s,n,t) { #s, n, REG_TYPE_##t, TRUE }
+#define REGDEF(s,n,t) { #s, n, REG_TYPE_##t, TRUE, 0 }
 #define REGNUM(p,n,t) REGDEF(p##n, n, t)
 #define REGNUM2(p,n,t) REGDEF(p##n, 2 * n, t)
 #define REGSET(p,t) \
 #define REGNUM(p,n,t) REGDEF(p##n, n, t)
 #define REGNUM2(p,n,t) REGDEF(p##n, 2 * n, t)
 #define REGSET(p,t) \
@@ -14932,7 +15424,7 @@ create_unwind_entry (int have_data)
 int
 tc_arm_regname_to_dw2regnum (const char *regname)
 {
 int
 tc_arm_regname_to_dw2regnum (const char *regname)
 {
-  int reg = arm_reg_parse ((char **) &regname, REG_TYPE_RN, NULL);
+  int reg = arm_reg_parse ((char **) &regname, REG_TYPE_RN);
 
   if (reg == FAIL)
     return -1;
 
   if (reg == FAIL)
     return -1;
diff --git a/gas/testsuite/gas/arm/neon-psyn.d b/gas/testsuite/gas/arm/neon-psyn.d
new file mode 100644 (file)
index 0000000..c318672
--- /dev/null
@@ -0,0 +1,37 @@
+# name: Neon programmers syntax
+# as: -mfpu=neon
+# objdump: -dr --prefix-addresses --show-raw-insn
+
+.*: +file format .*arm.*
+
+Disassembly of section .text:
+0[0-9a-f]+ <[^>]+> f2144954    vmul\.i16       q2, q2, q2
+0[0-9a-f]+ <[^>]+> f2a33862    vmul\.i32       d3, d3, d2\[1\]
+0[0-9a-f]+ <[^>]+> f2233912    vmul\.i32       d3, d3, d2
+0[0-9a-f]+ <[^>]+> f2222803    vadd\.i32       d2, d2, d3
+0[0-9a-f]+ <[^>]+> f3924a4a    vmull\.u16      q2, d2, d2\[1\]
+0[0-9a-f]+ <[^>]+> f2910061    vmla\.i16       d0, d1, d1\[2\]
+0[0-9a-f]+ <[^>]+> f2910061    vmla\.i16       d0, d1, d1\[2\]
+0[0-9a-f]+ <[^>]+> f2255805    vadd\.i32       d5, d5, d5
+0[0-9a-f]+ <[^>]+> f2275117    vorr    d5, d7, d7
+0[0-9a-f]+ <[^>]+> ee021b70    vmov\.16        d2\[1\], r1
+0[0-9a-f]+ <[^>]+> ee251b10    vmov\.32        d5\[1\], r1
+0[0-9a-f]+ <[^>]+> ec432b15    vmov    d5, r2, r3
+0[0-9a-f]+ <[^>]+> ee554b30    vmov\.s8        r4, d5\[1\]
+0[0-9a-f]+ <[^>]+> ec565b15    vmov    r5, r6, d5
+0[0-9a-f]+ <[^>]+> f396a507    vabal\.u16      q5, d6, d7
+0[0-9a-f]+ <[^>]+> f3bb2744    vcvt\.s32\.f32  q1, q2
+0[0-9a-f]+ <[^>]+> f3bb4e15    vcvt\.f32\.u32  d4, d5, #5
+0[0-9a-f]+ <[^>]+> f3bc7c05    vdup\.32        d7, d5\[1\]
+0[0-9a-f]+ <[^>]+> f3ba1904    vtbl\.8 d1, {d10-d11}, d4
+0[0-9a-f]+ <[^>]+> f4aa698f    vld2\.32        {d6\[1\],d7\[1\]}, \[sl\]
+0[0-9a-f]+ <[^>]+> f4aa476f    vld4\.16        {d4\[1\],d6\[1\],d8\[1\],d10\[1\]}, \[sl\]
+0[0-9a-f]+ <[^>]+> f4aa6e4f    vld3\.16        {d6\[\]-d8\[\]}, \[sl\]
+0[0-9a-f]+ <[^>]+> ee100b30    vmov\.s16       r0, d0\[0\]
+0[0-9a-f]+ <[^>]+> f42a604f    vld4\.16        {d6-d9}, \[sl\]
+0[0-9a-f]+ <[^>]+> f4aa266f    vld3\.16        {d2\[1\],d4\[1\],d6\[1\]}, \[sl\]
+0[0-9a-f]+ <[^>]+> f3b47908    vtbl\.8 d7, {d4-d5}, d8
+0[0-9a-f]+ <[^>]+> f3142156    vbsl    q1, q2, q3
+0[0-9a-f]+ <[^>]+> f3032e04    vcge\.f32       d2, d3, d4
+0[0-9a-f]+ <[^>]+> f3b52083    vcge\.s16       d2, d3, #0
+0[0-9a-f]+ <[^>]+> ee823b30    vdup\.16        d2, r3
diff --git a/gas/testsuite/gas/arm/neon-psyn.s b/gas/testsuite/gas/arm/neon-psyn.s
new file mode 100644 (file)
index 0000000..5d412a8
--- /dev/null
@@ -0,0 +1,78 @@
+       .arm
+       .syntax unified
+
+fish   .qn     q2
+cow    .dn     d2[1]
+chips  .dn     d2
+banana .dn     d3
+
+       vmul fish.s16, fish.s16, fish.s16
+
+       vmul banana, banana, cow.s32
+       vmul d3.s32, d3.s32, d2.s32
+       vadd d2.s32, d3.s32
+       vmull fish.u32, chips.u16, chips.u16[1]
+
+X      .dn D0.S16
+Y      .dn D1.S16
+Z      .dn Y[2]
+
+       VMLA X, Y, Z
+       VMLA X, Y, Y[2]
+
+foo    .dn d5
+bar    .dn d7
+foos   .dn foo[1]
+
+       vadd foo, foo, foo.u32
+
+       vmov foo, bar
+       vmov d2.s16[1], r1
+       vmov d5.s32[1], r1
+       vmov foo, r2, r3
+       vmov r4, foos.s8
+       vmov r5, r6, foo
+
+baa    .qn     q5
+moo    .dn     d6
+sheep  .dn     d7
+chicken        .dn     d8
+
+       vabal baa, moo.u16, sheep.u16
+
+       vcvt q1.s32, q2.f32
+       vcvt d4.f, d5.u32, #5
+
+       vdup bar, foos.32
+       vtbl d1, {baa}, d4.8
+
+el1    .dn     d4.16[1]
+el2    .dn     d6.16[1]
+el3    .dn     d8.16[1]
+el4    .dn     d10.16[1]
+
+       vld2 {moo.32[1], sheep.32[1]}, [r10]
+       vld4 {el1, el2, el3, el4}, [r10]
+       vld3 {moo.16[], sheep.16[], chicken.16[]}, [r10]
+
+       vmov r0,d0.s16[0]
+
+el5    .qn     q3.16
+el6    .qn     q4.16
+
+       vld4 {el5,el6}, [r10]
+
+       vld3 {d2.s16[1], d4.s16[1], d6.s16[1]}, [r10]
+
+chicken8       .dn     chicken.8
+
+       vtbl d7.8, {d4, d5}, chicken8
+
+       vbsl q1.8, q2.16, q3.8
+
+       vcge d2.32, d3.f, d4.f
+       vcge d2.16, d3.s16, #0
+
+dupme  .dn     d2.s16
+
+       vdup dupme, r3