public inbox for binutils-cvs@sourceware.org
 help / color / mirror / Atom feed
* [binutils-gdb] gas: have scrubber retain more whitespace
@ 2024-08-09 10:00 Jan Beulich
  0 siblings, 0 replies; only message in thread
From: Jan Beulich @ 2024-08-09 10:00 UTC (permalink / raw)
  To: binutils-cvs

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=6ae8a30d44f016cafb46a75843b5109316eb1996

commit 6ae8a30d44f016cafb46a75843b5109316eb1996
Author: Jan Beulich <jbeulich@suse.com>
Date:   Fri Aug 9 11:59:31 2024 +0200

    gas: have scrubber retain more whitespace
    
    According to the description of the state machine, the expectation
    appears to be that (leaving aside labels) any insn mnemonic or
    directive would be followed by a comma separated list of operands. That
    may have been true very long ago, but the latest with the advent of more
    elaborate macros this isn't rhe case anymore. Neither macro parameters
    in macro definitions nor macro arguments in macro invocations are
    required to be separated by commas. Hence whitespace serves a crucial
    role there. Plus even without "real" macros issues exist, in e.g.
    
            .irp n, ...
            insn\n\(suffix) operand1, operand2
            .endr
    
    Whitespace following the closing parenthesis would have been removed
    (ahead of even processing the .irp), as the "opcode" was deemed to have
    ended earlier already.
    
    Therefore, squash the distinction between "opcode" and operands, i.e.
    fold state 10 back into state 3. Also drop most of the distinction
    between "symbol chars" and "relatively normal" ones. Not entirely
    unexpectedly this results in the need to skip whitespace in a few more
    places in arch-specific code (and quite likely more changes are needed
    for insn forms not covered by the testsuite).
    
    As a result the D10V special case is no longer necessary.
    
    In config/tc-sparc.c also move a comment to be next to the code being
    commented.
    
    In opcodes/cgen-asm.in some further cleanup is done, following the local
    var adjustments.

Diff:
---
 gas/NEWS                               |   7 ++
 gas/app.c                              | 171 ++++++++++-----------------------
 gas/config/tc-aarch64.c                |  19 ++--
 gas/config/tc-arm.c                    |   2 +
 gas/config/tc-crx.c                    |   6 +-
 gas/config/tc-csky.c                   |  23 ++++-
 gas/config/tc-pru.c                    |  10 +-
 gas/config/tc-sparc.c                  |  48 +++++++--
 gas/config/tc-v850.c                   |   4 +
 gas/testsuite/gas/all/macro.l          |  10 ++
 gas/testsuite/gas/all/macro.s          |   4 +-
 gas/testsuite/gas/i386/x86-64-apx-nf.s |  40 ++++----
 opcodes/cgen-asm.in                    |  52 ++++++----
 opcodes/epiphany-asm.c                 |  52 ++++++----
 opcodes/fr30-asm.c                     |  52 ++++++----
 opcodes/frv-asm.c                      |  52 ++++++----
 opcodes/ip2k-asm.c                     |  52 ++++++----
 opcodes/iq2000-asm.c                   |  52 ++++++----
 opcodes/lm32-asm.c                     |  52 ++++++----
 opcodes/m32c-asm.c                     |  52 ++++++----
 opcodes/m32r-asm.c                     |  52 ++++++----
 opcodes/mep-asm.c                      |  52 ++++++----
 opcodes/mt-asm.c                       |  52 ++++++----
 opcodes/nds32-asm.c                    |   5 +
 opcodes/or1k-asm.c                     |  52 ++++++----
 opcodes/xstormy16-asm.c                |  52 ++++++----
 26 files changed, 620 insertions(+), 405 deletions(-)

diff --git a/gas/NEWS b/gas/NEWS
index 69c6317c486..66f8cc5cc8f 100644
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -2,6 +2,13 @@
 
 * Add support for RISC-V CORE-V extension (XCvBitmanip) with version 1.0.
 
+* The scrubber (pre-processor) now leaves in place more whitespace, to permit
+  various constructs not fitting the basic "insn opnd1,opnd2[,...]" scheme to
+  work.  This, however, means that macro invocations like "m 1 + 2", i.e. not
+  using double quotes or parentheses around the (apparently) sole argument,
+  will now be treated as passing three arguments.  Such lack of quotation /
+  parenthesization was never reliable to use.
+
 Changes in 2.43:
 
 * Add support for LoongArch .option for fine-grained control of assembly
diff --git a/gas/app.c b/gas/app.c
index 41ba4163a2a..b88b4c96137 100644
--- a/gas/app.c
+++ b/gas/app.c
@@ -467,16 +467,18 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen,
 
   /*State 0: beginning of normal line
 	  1: After first whitespace on line (flush more white)
-	  2: After first non-white (opcode) on line (keep 1white)
-	  3: after second white on line (into operands) (flush white)
+	  2: After first non-white (opcode or maybe label when they're followed
+	     by colons) on line (keep 1white)
+	  3: after subsequent white on line (typically into operands)
+	     (flush more white)
 	  4: after putting out a .linefile, put out digits
 	  5: parsing a string, then go to old-state
 	  6: putting out \ escape in a "d string.
 	  7: no longer used
 	  8: no longer used
-	  9: After seeing symbol char in state 3 (keep 1white after symchar)
-	 10: After seeing whitespace in state 9 (keep white before symchar)
-	 11: After seeing a symbol character in state 0 (eg a label definition)
+	  9: After seeing non-white in state 3 (keep 1white)
+	 10: no longer used
+	 11: After seeing a non-white character in state 0 (eg a label definition)
 	 -1: output string in out_string and go to the state in old_state
 	 12: no longer used
 #ifdef DOUBLEBAR_PARALLEL
@@ -939,7 +941,11 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen,
 	          && (state < 1 || strchr (tc_comment_chars, ch)))
 	      || IS_NEWLINE (ch)
 	      || IS_LINE_SEPARATOR (ch)
-	      || IS_PARALLEL_SEPARATOR (ch))
+	      || IS_PARALLEL_SEPARATOR (ch)
+	      /* See comma related comment near the bottom of the function.
+		 Reasoning equally applies to whitespace preceding a comma in
+		 most cases.  */
+	      || (ch == ',' && state > 2 && state != 11))
 	    {
 	      if (scrub_m68k_mri)
 		{
@@ -982,6 +988,7 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen,
 		 character at the beginning of a line.  */
 	      goto recycle;
 	    case 2:
+	    case 9:
 	      state = 3;
 	      if (to + 1 < toend)
 		{
@@ -1005,20 +1012,6 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen,
 		  break;
 		}
 	      goto recycle;	/* Sp in operands */
-	    case 9:
-	    case 10:
-#ifndef TC_KEEP_OPERAND_SPACES
-	      if (scrub_m68k_mri)
-#endif
-		{
-		  /* In MRI mode, we keep these spaces.  */
-		  state = 3;
-		  UNGET (ch);
-		  PUT (' ');
-		  break;
-		}
-	      state = 10;	/* Sp after symbol char */
-	      goto recycle;
 	    case 11:
 	      if (LABELS_WITHOUT_COLONS || flag_m68k_mri)
 		state = 1;
@@ -1089,27 +1082,17 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen,
 	    {
 	      if (ch2 != EOF)
 		UNGET (ch2);
-	      if (state == 9 || state == 10)
-		state = 3;
+	      if (state == 1)
+		state = 2;
+	      else if (state == 3)
+		state = 9;
 	      PUT (ch);
 	    }
 	  break;
 
 	case LEX_IS_STRINGQUOTE:
 	  quotechar = ch;
-	  if (state == 10)
-	    {
-	      /* Preserve the whitespace in foo "bar".  */
-	      UNGET (ch);
-	      state = 3;
-	      PUT (' ');
-
-	      /* PUT didn't jump out.  We could just break, but we
-		 know what will happen, so optimize a bit.  */
-	      ch = GET ();
-	      old_state = 9;
-	    }
-	  else if (state == 3)
+	  if (state == 3)
 	    old_state = 9;
 	  else if (state == 0)
 	    old_state = 11; /* Now seeing label definition.  */
@@ -1130,14 +1113,6 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen,
 	      UNGET (c);
 	    }
 #endif
-	  if (state == 10)
-	    {
-	      /* Preserve the whitespace in foo 'b'.  */
-	      UNGET (ch);
-	      state = 3;
-	      PUT (' ');
-	      break;
-	    }
 	  ch = GET ();
 	  if (ch == EOF)
 	    {
@@ -1172,10 +1147,7 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen,
 	      PUT (out_buf[0]);
 	      break;
 	    }
-	  if (state == 9)
-	    old_state = 3;
-	  else
-	    old_state = state;
+	  old_state = state;
 	  state = -1;
 	  out_string = out_buf;
 	  PUT (*out_string++);
@@ -1185,10 +1157,10 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen,
 #ifdef KEEP_WHITE_AROUND_COLON
 	  state = 9;
 #else
-	  if (state == 9 || state == 10)
-	    state = 3;
-	  else if (state != 3)
+	  if (state == 2 || state == 11)
 	    state = 1;
+	  else
+	    state = 9;
 #endif
 	  PUT (ch);
 	  break;
@@ -1313,20 +1285,6 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen,
 	      break;
 	    }
 
-#ifdef TC_D10V
-	  /* All insns end in a char for which LEX_IS_SYMBOL_COMPONENT is true.
-	     Trap is the only short insn that has a first operand that is
-	     neither register nor label.
-	     We must prevent exef0f ||trap #1 to degenerate to exef0f ||trap#1 .
-	     We can't make '#' LEX_IS_SYMBOL_COMPONENT because it is
-	     already LEX_IS_LINE_COMMENT_START.  However, it is the
-	     only character in line_comment_chars for d10v, hence we
-	     can recognize it as such.  */
-	  /* An alternative approach would be to reset the state to 1 when
-	     we see '||', '<'- or '->', but that seems to be overkill.  */
-	  if (state == 10)
-	    PUT (' ');
-#endif
 	  /* We have a line comment character which is not at the
 	     start of a line.  If this is also a normal comment
 	     character, fall through.  Otherwise treat it as a default
@@ -1390,17 +1348,6 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen,
 	  /* Fall through.  */
 
 	case LEX_IS_SYMBOL_COMPONENT:
-	  if (state == 10)
-	    {
-	      /* This is a symbol character following another symbol
-		 character, with whitespace in between.  We skipped
-		 the whitespace earlier, so output it now.  */
-	      UNGET (ch);
-	      state = 3;
-	      PUT (' ');
-	      break;
-	    }
-
 #ifdef TC_Z80
 	  /* "af'" is a symbol containing '\''.  */
 	  if (state == 3 && (ch == 'a' || ch == 'A'))
@@ -1426,7 +1373,16 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen,
 		}
 	    }
 #endif
-	  if (state == 3)
+
+	  /* Fall through.  */
+	default:
+	de_fault:
+	  /* Some relatively `normal' character.  */
+	  if (state == 0)
+	    state = 11;	/* Now seeing label definition.  */
+	  else if (state == 1)
+	    state = 2;	/* Ditto.  */
+	  else if (state == 3)
 	    state = 9;
 
 	  /* This is a common case.  Quickly copy CH and all the
@@ -1435,6 +1391,10 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen,
 	      && mri_state == NULL
 #if defined TC_ARM && defined OBJ_ELF
 	      && symver_state == NULL
+#endif
+#ifdef TC_Z80
+	      /* See comma related comment below.  */
+	      && ch != ','
 #endif
 	      )
 	    {
@@ -1450,6 +1410,12 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen,
 		  if (type != 0
 		      && type != LEX_IS_SYMBOL_COMPONENT)
 		    break;
+#ifdef TC_Z80
+		  /* Need to split at commas, to be able to enter state 16
+		     when needed.  */
+		  if (ch2 == ',')
+		    break;
+#endif
 		}
 
 	      if (s > from)
@@ -1474,52 +1440,15 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen,
 		}
 	    }
 
-	  /* Fall through.  */
-	default:
-	de_fault:
-	  /* Some relatively `normal' character.  */
-	  if (state == 0)
-	    {
-	      state = 11;	/* Now seeing label definition.  */
-	    }
-	  else if (state == 1)
-	    {
-	      state = 2;	/* Ditto.  */
-	    }
-	  else if (state == 9)
-	    {
-	      if (!IS_SYMBOL_COMPONENT (ch))
-		state = 3;
-	    }
-	  else if (state == 10)
-	    {
-	      if (ch == '\\')
-		{
-		  /* Special handling for backslash: a backslash may
-		     be the beginning of a formal parameter (of a
-		     macro) following another symbol character, with
-		     whitespace in between.  If that is the case, we
-		     output a space before the parameter.  Strictly
-		     speaking, correct handling depends upon what the
-		     macro parameter expands into; if the parameter
-		     expands into something which does not start with
-		     an operand character, then we don't want to keep
-		     the space.  We don't have enough information to
-		     make the right choice, so here we are making the
-		     choice which is more likely to be correct.  */
-		  if (to + 1 >= toend)
-		    {
-		      /* If we're near the end of the buffer, save the
-		         character for the next time round.  Otherwise
-		         we'll lose our state.  */
-		      UNGET (ch);
-		      goto tofull;
-		    }
-		  *to++ = ' ';
-		}
+	  /* As a special case, to limit the delta to previous behavior, e.g.
+	     also affecting listings, go straight to state 3 when seeing a
+	     comma. Commas are special: While they can be used to separate
+	     macro parameters or arguments, they cannot (on their own, i.e.
+	     without quoting) be arguments (or parameter default values).
+	     Hence successive whitespace is not meaningful there.  */
+	  if (ch == ',' && state == 9)
+	    state = 3;
 
-	      state = 3;
-	    }
 	  PUT (ch);
 	  break;
 	}
diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c
index e94a0cff406..c6ac66bcc5c 100644
--- a/gas/config/tc-aarch64.c
+++ b/gas/config/tc-aarch64.c
@@ -643,6 +643,7 @@ const char FLT_CHARS[] = "rRsSfFdDxXeEpPhHb";
 static inline bool
 skip_past_char (char **str, char c)
 {
+  skip_whitespace (*str);
   if (**str == c)
     {
       (*str)++;
@@ -893,6 +894,7 @@ parse_reg (char **ccp)
   start++;
 #endif
 
+  skip_whitespace (start);
   p = start;
   if (!ISALPHA (*p) || !is_name_beginner (*p))
     return NULL;
@@ -1202,13 +1204,17 @@ parse_typed_reg (char **ccp, aarch64_reg_type type,
 		 struct vector_type_el *typeinfo, unsigned int flags)
 {
   char *str = *ccp;
-  bool is_alpha = ISALPHA (*str);
-  const reg_entry *reg = parse_reg (&str);
+  bool is_alpha;
+  const reg_entry *reg;
   struct vector_type_el atype;
   struct vector_type_el parsetype;
   bool is_typed_vecreg = false;
   unsigned int err_flags = (flags & PTR_IN_REGLIST) ? SEF_IN_REGLIST : 0;
 
+  skip_whitespace (str);
+  is_alpha = ISALPHA (*str);
+  reg = parse_reg (&str);
+
   atype.defined = 0;
   atype.type = NT_invtype;
   atype.width = -1;
@@ -1429,10 +1435,7 @@ parse_vector_reg_list (char **ccp, aarch64_reg_type type,
   do
     {
       if (in_range)
-	{
-	  str++;		/* skip over '-' */
-	  val_range = val;
-	}
+	val_range = val;
 
       const reg_entry *reg;
       if (has_qualifier)
@@ -1494,7 +1497,8 @@ parse_vector_reg_list (char **ccp, aarch64_reg_type type,
       in_range = 0;
       ptr_flags |= PTR_GOOD_MATCH;
     }
-  while (skip_past_comma (&str) || (in_range = 1, *str == '-'));
+  while (skip_past_comma (&str)
+	 || (in_range = 1, skip_past_char (&str, '-')));
 
   skip_whitespace (str);
   if (*str != '}')
@@ -8289,6 +8293,7 @@ parse_operands (char *str, const aarch64_opcode *opcode)
     }
 
   /* Check if we have parsed all the operands.  */
+  skip_whitespace (str);
   if (*str != '\0' && ! error_p ())
     {
       /* Set I to the index of the last present operand; this is
diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c
index 540ab487384..14d729558c5 100644
--- a/gas/config/tc-arm.c
+++ b/gas/config/tc-arm.c
@@ -1148,6 +1148,8 @@ my_get_expression (expressionS * ep, char ** str, int prefix_mode)
     prefix_mode = (prefix_mode == GE_OPT_PREFIX_BIG) ? prefix_mode
 		  : GE_OPT_PREFIX;
 
+  skip_whitespace (*str);
+
   switch (prefix_mode)
     {
     case GE_NO_PREFIX: break;
diff --git a/gas/config/tc-crx.c b/gas/config/tc-crx.c
index 3a47354c473..4673c947f2f 100644
--- a/gas/config/tc-crx.c
+++ b/gas/config/tc-crx.c
@@ -1723,9 +1723,13 @@ preprocess_reglist (char *param, int *allocated)
 
   while (*paramP != '}')
     {
-      regP = paramP;
       memset (&reg_name, '\0', sizeof (reg_name));
 
+      while (ISSPACE (*paramP))
+	paramP++;
+
+      regP = paramP;
+
       while (ISALNUM (*paramP))
 	paramP++;
 
diff --git a/gas/config/tc-csky.c b/gas/config/tc-csky.c
index 0dc8bfd45dc..0eb1805aacf 100644
--- a/gas/config/tc-csky.c
+++ b/gas/config/tc-csky.c
@@ -2409,10 +2409,18 @@ parse_rt (char *s,
     /* Indicate nothing there.  */
     ep->X_op = O_absent;
 
+  /* Skip whitespace.  */
+  while (ISSPACE (*s))
+    ++s;
+
   if (*s == '[')
     {
       s = parse_exp (s + 1, &e);
 
+      /* Skip whitespace.  */
+      while (ISSPACE (*s))
+	++s;
+
       if (*s == ']')
 	s++;
       else
@@ -2935,6 +2943,11 @@ is_reg_lshift_illegal (char **oper, int is_float)
     }
 
   *oper += len;
+
+  /* Skip whitespace.  */
+  while (ISSPACE (**oper))
+    ++*oper;
+
   if ((*oper)[0] != '<' || (*oper)[1] != '<')
     {
       SET_ERROR_STRING (ERROR_UNDEFINE,
@@ -3461,6 +3474,9 @@ get_operand_value (struct csky_opcode_info *op,
 	  return false;
 	}
 
+      while (ISSPACE (**oper))
+	++*oper;
+
       if (!get_operand_value (op, oper, &soprnd->subs[0]))
 	{
 	  *s = rc;
@@ -3481,7 +3497,7 @@ get_operand_value (struct csky_opcode_info *op,
 	}
 
       *s = rc;
-      *oper += 1;
+      *oper = s + 1;
       return true;
     }
 
@@ -4277,11 +4293,16 @@ get_operand_value (struct csky_opcode_info *op,
     case OPRND_TYPE_VREG_WITH_INDEX:
       if (parse_type_freg (oper, 0))
 	{
+	  /* Skip whitespace.  */
+	  while (ISSPACE (**oper))
+	    ++*oper;
 	  if (**oper == '[')
 	    {
 	      (*oper)++;
 	      if (is_imm_within_range (oper, 0, 0xf))
 		{
+		  while (ISSPACE (**oper))
+		    ++*oper;
 		  if (**oper == ']')
 		    {
 		      unsigned int idx = --csky_insn.idx;
diff --git a/gas/config/tc-pru.c b/gas/config/tc-pru.c
index 99a3c1ef88c..6789686718a 100644
--- a/gas/config/tc-pru.c
+++ b/gas/config/tc-pru.c
@@ -1399,7 +1399,6 @@ pru_parse_args (pru_insn_infoS *insn ATTRIBUTE_UNUSED, char *argstr,
 		  const char *parsestr, char **parsed_args)
 {
   char *p;
-  char *end = NULL;
   int i;
   p = argstr;
   i = 0;
@@ -1426,14 +1425,7 @@ pru_parse_args (pru_insn_infoS *insn ATTRIBUTE_UNUSED, char *argstr,
       else
 	{
 	  /* Check that the argument string has no trailing arguments.  */
-	  /* If we've got a %pmem relocation, we've zapped the parens with
-	     spaces.  */
-	  if (strprefix (p, "%pmem") || strprefix (p, "%label"))
-	    end = strpbrk (p, ",");
-	  else
-	    end = strpbrk (p, " ,");
-
-	  if (end != NULL)
+	  if (strpbrk (p, ",") != NULL)
 	    as_bad (_("too many arguments"));
 	}
 
diff --git a/gas/config/tc-sparc.c b/gas/config/tc-sparc.c
index e37189e7c5e..d78aa0e6e3d 100644
--- a/gas/config/tc-sparc.c
+++ b/gas/config/tc-sparc.c
@@ -1778,6 +1778,9 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
          operands match.  */
       for (args = insn->args;; ++args)
 	{
+	  if (*s == ' ' && *args != ' ')
+	    ++s;
+
 	  switch (*args)
 	    {
 	    case 'K':
@@ -2718,11 +2721,6 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
 		   'symbols' in the input string.  Try not to create U
 		   symbols for registers, etc.  */
 
-		/* This stuff checks to see if the expression ends in
-		   +%reg.  If it does, it removes the register from
-		   the expression, and re-sets 's' to point to the
-		   right place.  */
-
 		if (op_arg)
 		  {
 		    int npar = 0;
@@ -2752,6 +2750,8 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
 			return special_case;
 		      }
 		    s = s1 + 1;
+		    if (*s == ' ')
+		      s++;
 		    if (*s == ',' || *s == ']' || !*s)
 		      continue;
 		    if (*s != '+' && *s != '-')
@@ -2765,17 +2765,45 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn)
 		    memset (&the_insn.exp, 0, sizeof (the_insn.exp));
 		  }
 
+		/* This stuff checks to see if the expression ends in
+		   +%reg.  If it does, it removes the register from
+		   the expression, and re-sets 's' to point to the
+		   right place.  */
+
 		for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++)
 		  ;
 
+		if (s1 != s && s1[-1] == ' ')
+		  --s1;
 		if (s1 != s && ISDIGIT (s1[-1]))
 		  {
 		    if (s1[-2] == '%' && s1[-3] == '+')
-		      s1 -= 3;
-		    else if (strchr ("golir0123456789", s1[-2]) && s1[-3] == '%' && s1[-4] == '+')
-		      s1 -= 4;
-		    else if (s1[-3] == 'r' && s1[-4] == '%' && s1[-5] == '+')
-		      s1 -= 5;
+		      {
+			if (s1[-3] == '+')
+			  s1 -= 3;
+			else if (s1[-3] == ' ' && s1[-4] == '+')
+			  s1 -= 4;
+			else
+			  s1 = NULL;
+		      }
+		    else if (strchr ("golir0123456789", s1[-2]) && s1[-3] == '%')
+		      {
+			if (s1[-4] == '+')
+			  s1 -= 4;
+			else if (s1[-4] == ' ' && s1[-5] == '+')
+			  s1 -= 5;
+			else
+			  s1 = NULL;
+		      }
+		    else if (s1[-3] == 'r' && s1[-4] == '%')
+		      {
+			if (s1[-5] == '+')
+			  s1 -= 5;
+			else if (s1[-5] == ' ' && s1[-6] == '+')
+			  s1 -= 6;
+			else
+			  s1 = NULL;
+		      }
 		    else
 		      s1 = NULL;
 		    if (s1)
diff --git a/gas/config/tc-v850.c b/gas/config/tc-v850.c
index cb518a7da05..63b69a7e1f9 100644
--- a/gas/config/tc-v850.c
+++ b/gas/config/tc-v850.c
@@ -1456,6 +1456,8 @@ parse_register_list (unsigned long *insn,
 	    }
 	}
 
+      skip_white_space ();
+
       if (*input_line_pointer == '}')
 	{
 	  input_line_pointer++;
@@ -1475,6 +1477,8 @@ parse_register_list (unsigned long *insn,
 	  /* Skip the dash.  */
 	  ++input_line_pointer;
 
+	  skip_white_space ();
+
 	  /* Get the second register in the range.  */
 	  if (! register_name (&exp2))
 	    {
diff --git a/gas/testsuite/gas/all/macro.l b/gas/testsuite/gas/all/macro.l
index 75fe338f132..c62d34d2baa 100644
--- a/gas/testsuite/gas/all/macro.l
+++ b/gas/testsuite/gas/all/macro.l
@@ -22,4 +22,14 @@
 [ 	]*[1-9][0-9]*[ 	]+.... 0+70*[ 	]+>  .byte 7
 [ 	]*[1-9][0-9]*[ 	]+.... 0+80*[ 	]+>  .byte 8
 [ 	]*[1-9][0-9]*[ 	]+m[ 	]+""[ 	]+""[ 	]+""
+[ 	]*[1-9][0-9]*[ 	]+
+[ 	]*[1-9][0-9]*[ 	]+m[ 	]+1[ 	]+\+2
+[ 	]*[1-9][0-9]*[ 	]+.... 0+10*[ 	]+>  .byte 1
+[ 	]*[1-9][0-9]*[ 	]+.... 0+20*[ 	]+>  .byte \+2
+[ 	]*[1-9][0-9]*[ 	]+m[ 	]+\(3\)[ 	]+\+4
+[ 	]*[1-9][0-9]*[ 	]+.... 0+30*[ 	]+>  .byte \(3\)
+[ 	]*[1-9][0-9]*[ 	]+.... 0+40*[ 	]+>  .byte \+4
+[ 	]*[1-9][0-9]*[ 	]+m[ 	]+\(5\)[ 	]+\(6\)
+[ 	]*[1-9][0-9]*[ 	]+.... 0+50*[ 	]+>  .byte \(5\)
+[ 	]*[1-9][0-9]*[ 	]+.... 0+60*[ 	]+>  .byte \(6\)
 #pass
diff --git a/gas/testsuite/gas/all/macro.s b/gas/testsuite/gas/all/macro.s
index 9e70f3067b7..109bcc56c99 100644
--- a/gas/testsuite/gas/all/macro.s
+++ b/gas/testsuite/gas/all/macro.s
@@ -9,8 +9,8 @@
 	m "7" "8"
 	m "" "" ""
 
-	.if 0
 	m 1 +2
 	m (3) +4
 	m (5) (6)
-	.endif
+
+	.byte -1
diff --git a/gas/testsuite/gas/i386/x86-64-apx-nf.s b/gas/testsuite/gas/i386/x86-64-apx-nf.s
index fbd4cadd983..99ae1c7aaa6 100644
--- a/gas/testsuite/gas/i386/x86-64-apx-nf.s
+++ b/gas/testsuite/gas/i386/x86-64-apx-nf.s
@@ -1390,13 +1390,13 @@ optimize:
 	{nf}	\op	$128, %ecx, %edx
 	{nf}	\op	$128, %r9
 	{nf}	\op	$128, %r9, %r31
-	{nf}	\op\()b	$128, (%rax)
+	{nf}	\op\(b)	$128, (%rax)
 	{nf}	\op	$128, (%rax), %bl
-	{nf}	\op\()w	$128, (%rax)
+	{nf}	\op\(w)	$128, (%rax)
 	{nf}	\op	$128, (%rax), %dx
-	{nf}	\op\()l	$128, (%rax)
+	{nf}	\op\(l)	$128, (%rax)
 	{nf}	\op	$128, (%rax), %ecx
-	{nf}	\op\()q	$128, (%rax)
+	{nf}	\op\(q)	$128, (%rax)
 	{nf}	\op	$128, (%rax), %r9
 
 	{nf}	\op	$1, %bl
@@ -1407,13 +1407,13 @@ optimize:
 	{nf}	\op	$1, %ecx, %edx
 	{nf}	\op	$1, %r9
 	{nf}	\op	$1, %r9, %r31
-	{nf}	\op\()b	$1, (%rax)
+	{nf}	\op\(b)	$1, (%rax)
 	{nf}	\op	$1, (%rax), %bl
-	{nf}	\op\()w	$1, (%rax)
+	{nf}	\op\(w)	$1, (%rax)
 	{nf}	\op	$1, (%rax), %dx
-	{nf}	\op\()l	$1, (%rax)
+	{nf}	\op\(l)	$1, (%rax)
 	{nf}	\op	$1, (%rax), %ecx
-	{nf}	\op\()q	$1, (%rax)
+	{nf}	\op\(q)	$1, (%rax)
 	{nf}	\op	$1, (%rax), %r9
 
 	{nf}	\op	$0xff, %bl
@@ -1424,13 +1424,13 @@ optimize:
 	{nf}	\op	$-1, %ecx, %edx
 	{nf}	\op	$-1, %r9
 	{nf}	\op	$-1, %r9, %r31
-	{nf}	\op\()b	$0xff, (%rax)
+	{nf}	\op\(b)	$0xff, (%rax)
 	{nf}	\op	$-1, (%rax), %bl
-	{nf}	\op\()w	$0xffff, (%rax)
+	{nf}	\op\(w)	$0xffff, (%rax)
 	{nf}	\op	$-1, (%rax), %dx
-	{nf}	\op\()l	$0xffffffff, (%rax)
+	{nf}	\op\(l)	$0xffffffff, (%rax)
 	{nf}	\op	$-1, (%rax), %ecx
-	{nf}	\op\()q	$-1, (%rax)
+	{nf}	\op\(q)	$-1, (%rax)
 	{nf}	\op	$-1, (%rax), %r9
 	.endr
 
@@ -1444,13 +1444,13 @@ optimize:
 	{nf}	ro\dir	$63, %rdx
 	{nf}	ro\dir	$63, %rdx, %rax
 
-	{nf}	ro\dir\()b	$7, (%rdx)
+	{nf}	ro\dir\(b)	$7, (%rdx)
 	{nf}	ro\dir		$7, (%rdx), %al
-	{nf}	ro\dir\()w	$15, (%rdx)
+	{nf}	ro\dir\(w)	$15, (%rdx)
 	{nf}	ro\dir		$15, (%rdx), %ax
-	{nf}	ro\dir\()l	$31, (%rdx)
+	{nf}	ro\dir\(l)	$31, (%rdx)
 	{nf}	ro\dir		$31, (%rdx), %eax
-	{nf}	ro\dir\()q	$63, (%rdx)
+	{nf}	ro\dir\(q)	$63, (%rdx)
 	{nf}	ro\dir		$63, (%rdx), %rax
 	.endr
 
@@ -1476,10 +1476,10 @@ optimize:
 	# Note: 2-6 want leaving alone with -Os.
 	.irp n, 1, 2, 6, 7
 	# Note: 16-bit 3-operand src!=dst non-ZU form needs leaving alone.
-	{nf} imul $1<<\n, %\r\()dx, %\r\()cx
-	{nf} imul $1<<\n, (%rdx), %\r\()cx
-	{nf} imul $1<<\n, %\r\()cx, %\r\()cx
-	{nf} imul $1<<\n, %\r\()cx
+	{nf} imul $1<<\n, %\r\(dx), %\r\(cx)
+	{nf} imul $1<<\n, (%rdx), %\r\(cx)
+	{nf} imul $1<<\n, %\r\(cx), %\r\(cx)
+	{nf} imul $1<<\n, %\r\(cx)
 
 	.ifeqs "\r",""
 	{nf} imulzu $1<<\n, %dx, %cx
diff --git a/opcodes/cgen-asm.in b/opcodes/cgen-asm.in
index 3f5180434df..8aab7e1ab83 100644
--- a/opcodes/cgen-asm.in
+++ b/opcodes/cgen-asm.in
@@ -68,6 +68,7 @@ char *
   char rxbuf[CGEN_MAX_RX_ELEMENTS];
   char *rx = rxbuf;
   const CGEN_SYNTAX_CHAR_TYPE *syn;
+  char prev_syntax_char = 0;
   int reg_err;
 
   syn = CGEN_SYNTAX_STRING (CGEN_OPCODE_SYNTAX (opc));
@@ -105,6 +106,15 @@ char *
 	{
 	  char c = CGEN_SYNTAX_CHAR (* syn);
 
+	  /* See whitespace related comments in parse_insn_normal().  */
+	  if (c != ' ' && prev_syntax_char != ' '
+	      && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	    {
+	      *rx++ = ' ';
+	      *rx++ = '*';
+	    }
+	  prev_syntax_char = c;
+
 	  switch (c)
 	    {
 	      /* Escape any regex metacharacters in the syntax.  */
@@ -138,6 +148,7 @@ char *
 	  /* Replace non-syntax fields with globs.  */
 	  *rx++ = '.';
 	  *rx++ = '*';
+	  prev_syntax_char = 0;
 	}
     }
 
@@ -195,10 +206,8 @@ parse_insn_normal (CGEN_CPU_DESC cd,
   const char *errmsg;
   const char *p;
   const CGEN_SYNTAX_CHAR_TYPE * syn;
-#ifdef CGEN_MNEMONIC_OPERANDS
-  /* FIXME: wip */
-  int past_opcode_p;
-#endif
+  char prev_syntax_char = 0;
+  bool past_opcode_p;
 
   /* For now we assume the mnemonic is first (there are no leading operands).
      We can parse it without needing to set up operand parsing.
@@ -214,13 +223,13 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 #ifndef CGEN_MNEMONIC_OPERANDS
   if (* str && ! ISSPACE (* str))
     return _("unrecognized instruction");
+  past_opcode_p = true;
+#else
+  past_opcode_p = false;
 #endif
 
   CGEN_INIT_PARSE (cd);
   cgen_init_parse_operand (cd);
-#ifdef CGEN_MNEMONIC_OPERANDS
-  past_opcode_p = 0;
-#endif
 
   /* We don't check for (*str != '\0') here because we want to parse
      any trailing fake arguments in the syntax string.  */
@@ -234,18 +243,28 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
   while (* syn != 0)
     {
+      char c = CGEN_SYNTAX_CHAR_P (*syn) ? CGEN_SYNTAX_CHAR (*syn) : 0;
+
+      /* FIXME: Despite this check we may still take inappropriate advantage of
+	 the fact that GAS's input scrubber will remove extraneous whitespace.
+	 We may also be a little too lax with this now, yet being more strict
+	 would require targets to indicate where whitespace is permissible.  */
+      if (past_opcode_p && c != ' ' && ISSPACE (*str)
+	  /* No whitespace between consecutive alphanumeric syntax elements.  */
+	  && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	++str;
+      prev_syntax_char = c;
+
       /* Non operand chars must match exactly.  */
-      if (CGEN_SYNTAX_CHAR_P (* syn))
+      if (c != 0)
 	{
 	  /* FIXME: While we allow for non-GAS callers above, we assume the
 	     first char after the mnemonic part is a space.  */
-	  /* FIXME: We also take inappropriate advantage of the fact that
-	     GAS's input scrubber will remove extraneous blanks.  */
-	  if (TOLOWER (*str) == TOLOWER (CGEN_SYNTAX_CHAR (* syn)))
+	  if (TOLOWER (*str) == TOLOWER (c))
 	    {
 #ifdef CGEN_MNEMONIC_OPERANDS
-	      if (CGEN_SYNTAX_CHAR(* syn) == ' ')
-		past_opcode_p = 1;
+	      if (c == ' ')
+		past_opcode_p = true;
 #endif
 	      ++ syn;
 	      ++ str;
@@ -257,7 +276,7 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found `%c')"),
-		       CGEN_SYNTAX_CHAR(*syn), *str);
+		       c, *str);
 	      return msg;
 	    }
 	  else
@@ -267,15 +286,12 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found end of instruction)"),
-		       CGEN_SYNTAX_CHAR(*syn));
+		       c);
 	      return msg;
 	    }
 	  continue;
 	}
 
-#ifdef CGEN_MNEMONIC_OPERANDS
-      (void) past_opcode_p;
-#endif
       /* We have an operand of some sort.  */
       errmsg = cd->parse_operand (cd, CGEN_SYNTAX_FIELD (*syn), &str, fields);
       if (errmsg)
diff --git a/opcodes/epiphany-asm.c b/opcodes/epiphany-asm.c
index 6c3f549705d..ce10f48d6b3 100644
--- a/opcodes/epiphany-asm.c
+++ b/opcodes/epiphany-asm.c
@@ -499,6 +499,7 @@ epiphany_cgen_build_insn_regex (CGEN_INSN *insn)
   char rxbuf[CGEN_MAX_RX_ELEMENTS];
   char *rx = rxbuf;
   const CGEN_SYNTAX_CHAR_TYPE *syn;
+  char prev_syntax_char = 0;
   int reg_err;
 
   syn = CGEN_SYNTAX_STRING (CGEN_OPCODE_SYNTAX (opc));
@@ -536,6 +537,15 @@ epiphany_cgen_build_insn_regex (CGEN_INSN *insn)
 	{
 	  char c = CGEN_SYNTAX_CHAR (* syn);
 
+	  /* See whitespace related comments in parse_insn_normal().  */
+	  if (c != ' ' && prev_syntax_char != ' '
+	      && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	    {
+	      *rx++ = ' ';
+	      *rx++ = '*';
+	    }
+	  prev_syntax_char = c;
+
 	  switch (c)
 	    {
 	      /* Escape any regex metacharacters in the syntax.  */
@@ -569,6 +579,7 @@ epiphany_cgen_build_insn_regex (CGEN_INSN *insn)
 	  /* Replace non-syntax fields with globs.  */
 	  *rx++ = '.';
 	  *rx++ = '*';
+	  prev_syntax_char = 0;
 	}
     }
 
@@ -626,10 +637,8 @@ parse_insn_normal (CGEN_CPU_DESC cd,
   const char *errmsg;
   const char *p;
   const CGEN_SYNTAX_CHAR_TYPE * syn;
-#ifdef CGEN_MNEMONIC_OPERANDS
-  /* FIXME: wip */
-  int past_opcode_p;
-#endif
+  char prev_syntax_char = 0;
+  bool past_opcode_p;
 
   /* For now we assume the mnemonic is first (there are no leading operands).
      We can parse it without needing to set up operand parsing.
@@ -645,13 +654,13 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 #ifndef CGEN_MNEMONIC_OPERANDS
   if (* str && ! ISSPACE (* str))
     return _("unrecognized instruction");
+  past_opcode_p = true;
+#else
+  past_opcode_p = false;
 #endif
 
   CGEN_INIT_PARSE (cd);
   cgen_init_parse_operand (cd);
-#ifdef CGEN_MNEMONIC_OPERANDS
-  past_opcode_p = 0;
-#endif
 
   /* We don't check for (*str != '\0') here because we want to parse
      any trailing fake arguments in the syntax string.  */
@@ -665,18 +674,28 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
   while (* syn != 0)
     {
+      char c = CGEN_SYNTAX_CHAR_P (*syn) ? CGEN_SYNTAX_CHAR (*syn) : 0;
+
+      /* FIXME: Despite this check we may still take inappropriate advantage of
+	 the fact that GAS's input scrubber will remove extraneous whitespace.
+	 We may also be a little too lax with this now, yet being more strict
+	 would require targets to indicate where whitespace is permissible.  */
+      if (past_opcode_p && c != ' ' && ISSPACE (*str)
+	  /* No whitespace between consecutive alphanumeric syntax elements.  */
+	  && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	++str;
+      prev_syntax_char = c;
+
       /* Non operand chars must match exactly.  */
-      if (CGEN_SYNTAX_CHAR_P (* syn))
+      if (c != 0)
 	{
 	  /* FIXME: While we allow for non-GAS callers above, we assume the
 	     first char after the mnemonic part is a space.  */
-	  /* FIXME: We also take inappropriate advantage of the fact that
-	     GAS's input scrubber will remove extraneous blanks.  */
-	  if (TOLOWER (*str) == TOLOWER (CGEN_SYNTAX_CHAR (* syn)))
+	  if (TOLOWER (*str) == TOLOWER (c))
 	    {
 #ifdef CGEN_MNEMONIC_OPERANDS
-	      if (CGEN_SYNTAX_CHAR(* syn) == ' ')
-		past_opcode_p = 1;
+	      if (c == ' ')
+		past_opcode_p = true;
 #endif
 	      ++ syn;
 	      ++ str;
@@ -688,7 +707,7 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found `%c')"),
-		       CGEN_SYNTAX_CHAR(*syn), *str);
+		       c, *str);
 	      return msg;
 	    }
 	  else
@@ -698,15 +717,12 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found end of instruction)"),
-		       CGEN_SYNTAX_CHAR(*syn));
+		       c);
 	      return msg;
 	    }
 	  continue;
 	}
 
-#ifdef CGEN_MNEMONIC_OPERANDS
-      (void) past_opcode_p;
-#endif
       /* We have an operand of some sort.  */
       errmsg = cd->parse_operand (cd, CGEN_SYNTAX_FIELD (*syn), &str, fields);
       if (errmsg)
diff --git a/opcodes/fr30-asm.c b/opcodes/fr30-asm.c
index 9ef15fed4c2..966d36fbe2c 100644
--- a/opcodes/fr30-asm.c
+++ b/opcodes/fr30-asm.c
@@ -354,6 +354,7 @@ fr30_cgen_build_insn_regex (CGEN_INSN *insn)
   char rxbuf[CGEN_MAX_RX_ELEMENTS];
   char *rx = rxbuf;
   const CGEN_SYNTAX_CHAR_TYPE *syn;
+  char prev_syntax_char = 0;
   int reg_err;
 
   syn = CGEN_SYNTAX_STRING (CGEN_OPCODE_SYNTAX (opc));
@@ -391,6 +392,15 @@ fr30_cgen_build_insn_regex (CGEN_INSN *insn)
 	{
 	  char c = CGEN_SYNTAX_CHAR (* syn);
 
+	  /* See whitespace related comments in parse_insn_normal().  */
+	  if (c != ' ' && prev_syntax_char != ' '
+	      && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	    {
+	      *rx++ = ' ';
+	      *rx++ = '*';
+	    }
+	  prev_syntax_char = c;
+
 	  switch (c)
 	    {
 	      /* Escape any regex metacharacters in the syntax.  */
@@ -424,6 +434,7 @@ fr30_cgen_build_insn_regex (CGEN_INSN *insn)
 	  /* Replace non-syntax fields with globs.  */
 	  *rx++ = '.';
 	  *rx++ = '*';
+	  prev_syntax_char = 0;
 	}
     }
 
@@ -481,10 +492,8 @@ parse_insn_normal (CGEN_CPU_DESC cd,
   const char *errmsg;
   const char *p;
   const CGEN_SYNTAX_CHAR_TYPE * syn;
-#ifdef CGEN_MNEMONIC_OPERANDS
-  /* FIXME: wip */
-  int past_opcode_p;
-#endif
+  char prev_syntax_char = 0;
+  bool past_opcode_p;
 
   /* For now we assume the mnemonic is first (there are no leading operands).
      We can parse it without needing to set up operand parsing.
@@ -500,13 +509,13 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 #ifndef CGEN_MNEMONIC_OPERANDS
   if (* str && ! ISSPACE (* str))
     return _("unrecognized instruction");
+  past_opcode_p = true;
+#else
+  past_opcode_p = false;
 #endif
 
   CGEN_INIT_PARSE (cd);
   cgen_init_parse_operand (cd);
-#ifdef CGEN_MNEMONIC_OPERANDS
-  past_opcode_p = 0;
-#endif
 
   /* We don't check for (*str != '\0') here because we want to parse
      any trailing fake arguments in the syntax string.  */
@@ -520,18 +529,28 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
   while (* syn != 0)
     {
+      char c = CGEN_SYNTAX_CHAR_P (*syn) ? CGEN_SYNTAX_CHAR (*syn) : 0;
+
+      /* FIXME: Despite this check we may still take inappropriate advantage of
+	 the fact that GAS's input scrubber will remove extraneous whitespace.
+	 We may also be a little too lax with this now, yet being more strict
+	 would require targets to indicate where whitespace is permissible.  */
+      if (past_opcode_p && c != ' ' && ISSPACE (*str)
+	  /* No whitespace between consecutive alphanumeric syntax elements.  */
+	  && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	++str;
+      prev_syntax_char = c;
+
       /* Non operand chars must match exactly.  */
-      if (CGEN_SYNTAX_CHAR_P (* syn))
+      if (c != 0)
 	{
 	  /* FIXME: While we allow for non-GAS callers above, we assume the
 	     first char after the mnemonic part is a space.  */
-	  /* FIXME: We also take inappropriate advantage of the fact that
-	     GAS's input scrubber will remove extraneous blanks.  */
-	  if (TOLOWER (*str) == TOLOWER (CGEN_SYNTAX_CHAR (* syn)))
+	  if (TOLOWER (*str) == TOLOWER (c))
 	    {
 #ifdef CGEN_MNEMONIC_OPERANDS
-	      if (CGEN_SYNTAX_CHAR(* syn) == ' ')
-		past_opcode_p = 1;
+	      if (c == ' ')
+		past_opcode_p = true;
 #endif
 	      ++ syn;
 	      ++ str;
@@ -543,7 +562,7 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found `%c')"),
-		       CGEN_SYNTAX_CHAR(*syn), *str);
+		       c, *str);
 	      return msg;
 	    }
 	  else
@@ -553,15 +572,12 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found end of instruction)"),
-		       CGEN_SYNTAX_CHAR(*syn));
+		       c);
 	      return msg;
 	    }
 	  continue;
 	}
 
-#ifdef CGEN_MNEMONIC_OPERANDS
-      (void) past_opcode_p;
-#endif
       /* We have an operand of some sort.  */
       errmsg = cd->parse_operand (cd, CGEN_SYNTAX_FIELD (*syn), &str, fields);
       if (errmsg)
diff --git a/opcodes/frv-asm.c b/opcodes/frv-asm.c
index 6b64bf45299..9d125345048 100644
--- a/opcodes/frv-asm.c
+++ b/opcodes/frv-asm.c
@@ -1307,6 +1307,7 @@ frv_cgen_build_insn_regex (CGEN_INSN *insn)
   char rxbuf[CGEN_MAX_RX_ELEMENTS];
   char *rx = rxbuf;
   const CGEN_SYNTAX_CHAR_TYPE *syn;
+  char prev_syntax_char = 0;
   int reg_err;
 
   syn = CGEN_SYNTAX_STRING (CGEN_OPCODE_SYNTAX (opc));
@@ -1344,6 +1345,15 @@ frv_cgen_build_insn_regex (CGEN_INSN *insn)
 	{
 	  char c = CGEN_SYNTAX_CHAR (* syn);
 
+	  /* See whitespace related comments in parse_insn_normal().  */
+	  if (c != ' ' && prev_syntax_char != ' '
+	      && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	    {
+	      *rx++ = ' ';
+	      *rx++ = '*';
+	    }
+	  prev_syntax_char = c;
+
 	  switch (c)
 	    {
 	      /* Escape any regex metacharacters in the syntax.  */
@@ -1377,6 +1387,7 @@ frv_cgen_build_insn_regex (CGEN_INSN *insn)
 	  /* Replace non-syntax fields with globs.  */
 	  *rx++ = '.';
 	  *rx++ = '*';
+	  prev_syntax_char = 0;
 	}
     }
 
@@ -1434,10 +1445,8 @@ parse_insn_normal (CGEN_CPU_DESC cd,
   const char *errmsg;
   const char *p;
   const CGEN_SYNTAX_CHAR_TYPE * syn;
-#ifdef CGEN_MNEMONIC_OPERANDS
-  /* FIXME: wip */
-  int past_opcode_p;
-#endif
+  char prev_syntax_char = 0;
+  bool past_opcode_p;
 
   /* For now we assume the mnemonic is first (there are no leading operands).
      We can parse it without needing to set up operand parsing.
@@ -1453,13 +1462,13 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 #ifndef CGEN_MNEMONIC_OPERANDS
   if (* str && ! ISSPACE (* str))
     return _("unrecognized instruction");
+  past_opcode_p = true;
+#else
+  past_opcode_p = false;
 #endif
 
   CGEN_INIT_PARSE (cd);
   cgen_init_parse_operand (cd);
-#ifdef CGEN_MNEMONIC_OPERANDS
-  past_opcode_p = 0;
-#endif
 
   /* We don't check for (*str != '\0') here because we want to parse
      any trailing fake arguments in the syntax string.  */
@@ -1473,18 +1482,28 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
   while (* syn != 0)
     {
+      char c = CGEN_SYNTAX_CHAR_P (*syn) ? CGEN_SYNTAX_CHAR (*syn) : 0;
+
+      /* FIXME: Despite this check we may still take inappropriate advantage of
+	 the fact that GAS's input scrubber will remove extraneous whitespace.
+	 We may also be a little too lax with this now, yet being more strict
+	 would require targets to indicate where whitespace is permissible.  */
+      if (past_opcode_p && c != ' ' && ISSPACE (*str)
+	  /* No whitespace between consecutive alphanumeric syntax elements.  */
+	  && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	++str;
+      prev_syntax_char = c;
+
       /* Non operand chars must match exactly.  */
-      if (CGEN_SYNTAX_CHAR_P (* syn))
+      if (c != 0)
 	{
 	  /* FIXME: While we allow for non-GAS callers above, we assume the
 	     first char after the mnemonic part is a space.  */
-	  /* FIXME: We also take inappropriate advantage of the fact that
-	     GAS's input scrubber will remove extraneous blanks.  */
-	  if (TOLOWER (*str) == TOLOWER (CGEN_SYNTAX_CHAR (* syn)))
+	  if (TOLOWER (*str) == TOLOWER (c))
 	    {
 #ifdef CGEN_MNEMONIC_OPERANDS
-	      if (CGEN_SYNTAX_CHAR(* syn) == ' ')
-		past_opcode_p = 1;
+	      if (c == ' ')
+		past_opcode_p = true;
 #endif
 	      ++ syn;
 	      ++ str;
@@ -1496,7 +1515,7 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found `%c')"),
-		       CGEN_SYNTAX_CHAR(*syn), *str);
+		       c, *str);
 	      return msg;
 	    }
 	  else
@@ -1506,15 +1525,12 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found end of instruction)"),
-		       CGEN_SYNTAX_CHAR(*syn));
+		       c);
 	      return msg;
 	    }
 	  continue;
 	}
 
-#ifdef CGEN_MNEMONIC_OPERANDS
-      (void) past_opcode_p;
-#endif
       /* We have an operand of some sort.  */
       errmsg = cd->parse_operand (cd, CGEN_SYNTAX_FIELD (*syn), &str, fields);
       if (errmsg)
diff --git a/opcodes/ip2k-asm.c b/opcodes/ip2k-asm.c
index 003b78d9714..1799025e5a4 100644
--- a/opcodes/ip2k-asm.c
+++ b/opcodes/ip2k-asm.c
@@ -555,6 +555,7 @@ ip2k_cgen_build_insn_regex (CGEN_INSN *insn)
   char rxbuf[CGEN_MAX_RX_ELEMENTS];
   char *rx = rxbuf;
   const CGEN_SYNTAX_CHAR_TYPE *syn;
+  char prev_syntax_char = 0;
   int reg_err;
 
   syn = CGEN_SYNTAX_STRING (CGEN_OPCODE_SYNTAX (opc));
@@ -592,6 +593,15 @@ ip2k_cgen_build_insn_regex (CGEN_INSN *insn)
 	{
 	  char c = CGEN_SYNTAX_CHAR (* syn);
 
+	  /* See whitespace related comments in parse_insn_normal().  */
+	  if (c != ' ' && prev_syntax_char != ' '
+	      && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	    {
+	      *rx++ = ' ';
+	      *rx++ = '*';
+	    }
+	  prev_syntax_char = c;
+
 	  switch (c)
 	    {
 	      /* Escape any regex metacharacters in the syntax.  */
@@ -625,6 +635,7 @@ ip2k_cgen_build_insn_regex (CGEN_INSN *insn)
 	  /* Replace non-syntax fields with globs.  */
 	  *rx++ = '.';
 	  *rx++ = '*';
+	  prev_syntax_char = 0;
 	}
     }
 
@@ -682,10 +693,8 @@ parse_insn_normal (CGEN_CPU_DESC cd,
   const char *errmsg;
   const char *p;
   const CGEN_SYNTAX_CHAR_TYPE * syn;
-#ifdef CGEN_MNEMONIC_OPERANDS
-  /* FIXME: wip */
-  int past_opcode_p;
-#endif
+  char prev_syntax_char = 0;
+  bool past_opcode_p;
 
   /* For now we assume the mnemonic is first (there are no leading operands).
      We can parse it without needing to set up operand parsing.
@@ -701,13 +710,13 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 #ifndef CGEN_MNEMONIC_OPERANDS
   if (* str && ! ISSPACE (* str))
     return _("unrecognized instruction");
+  past_opcode_p = true;
+#else
+  past_opcode_p = false;
 #endif
 
   CGEN_INIT_PARSE (cd);
   cgen_init_parse_operand (cd);
-#ifdef CGEN_MNEMONIC_OPERANDS
-  past_opcode_p = 0;
-#endif
 
   /* We don't check for (*str != '\0') here because we want to parse
      any trailing fake arguments in the syntax string.  */
@@ -721,18 +730,28 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
   while (* syn != 0)
     {
+      char c = CGEN_SYNTAX_CHAR_P (*syn) ? CGEN_SYNTAX_CHAR (*syn) : 0;
+
+      /* FIXME: Despite this check we may still take inappropriate advantage of
+	 the fact that GAS's input scrubber will remove extraneous whitespace.
+	 We may also be a little too lax with this now, yet being more strict
+	 would require targets to indicate where whitespace is permissible.  */
+      if (past_opcode_p && c != ' ' && ISSPACE (*str)
+	  /* No whitespace between consecutive alphanumeric syntax elements.  */
+	  && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	++str;
+      prev_syntax_char = c;
+
       /* Non operand chars must match exactly.  */
-      if (CGEN_SYNTAX_CHAR_P (* syn))
+      if (c != 0)
 	{
 	  /* FIXME: While we allow for non-GAS callers above, we assume the
 	     first char after the mnemonic part is a space.  */
-	  /* FIXME: We also take inappropriate advantage of the fact that
-	     GAS's input scrubber will remove extraneous blanks.  */
-	  if (TOLOWER (*str) == TOLOWER (CGEN_SYNTAX_CHAR (* syn)))
+	  if (TOLOWER (*str) == TOLOWER (c))
 	    {
 #ifdef CGEN_MNEMONIC_OPERANDS
-	      if (CGEN_SYNTAX_CHAR(* syn) == ' ')
-		past_opcode_p = 1;
+	      if (c == ' ')
+		past_opcode_p = true;
 #endif
 	      ++ syn;
 	      ++ str;
@@ -744,7 +763,7 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found `%c')"),
-		       CGEN_SYNTAX_CHAR(*syn), *str);
+		       c, *str);
 	      return msg;
 	    }
 	  else
@@ -754,15 +773,12 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found end of instruction)"),
-		       CGEN_SYNTAX_CHAR(*syn));
+		       c);
 	      return msg;
 	    }
 	  continue;
 	}
 
-#ifdef CGEN_MNEMONIC_OPERANDS
-      (void) past_opcode_p;
-#endif
       /* We have an operand of some sort.  */
       errmsg = cd->parse_operand (cd, CGEN_SYNTAX_FIELD (*syn), &str, fields);
       if (errmsg)
diff --git a/opcodes/iq2000-asm.c b/opcodes/iq2000-asm.c
index 66d828fd258..1a329c2cf85 100644
--- a/opcodes/iq2000-asm.c
+++ b/opcodes/iq2000-asm.c
@@ -503,6 +503,7 @@ iq2000_cgen_build_insn_regex (CGEN_INSN *insn)
   char rxbuf[CGEN_MAX_RX_ELEMENTS];
   char *rx = rxbuf;
   const CGEN_SYNTAX_CHAR_TYPE *syn;
+  char prev_syntax_char = 0;
   int reg_err;
 
   syn = CGEN_SYNTAX_STRING (CGEN_OPCODE_SYNTAX (opc));
@@ -540,6 +541,15 @@ iq2000_cgen_build_insn_regex (CGEN_INSN *insn)
 	{
 	  char c = CGEN_SYNTAX_CHAR (* syn);
 
+	  /* See whitespace related comments in parse_insn_normal().  */
+	  if (c != ' ' && prev_syntax_char != ' '
+	      && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	    {
+	      *rx++ = ' ';
+	      *rx++ = '*';
+	    }
+	  prev_syntax_char = c;
+
 	  switch (c)
 	    {
 	      /* Escape any regex metacharacters in the syntax.  */
@@ -573,6 +583,7 @@ iq2000_cgen_build_insn_regex (CGEN_INSN *insn)
 	  /* Replace non-syntax fields with globs.  */
 	  *rx++ = '.';
 	  *rx++ = '*';
+	  prev_syntax_char = 0;
 	}
     }
 
@@ -630,10 +641,8 @@ parse_insn_normal (CGEN_CPU_DESC cd,
   const char *errmsg;
   const char *p;
   const CGEN_SYNTAX_CHAR_TYPE * syn;
-#ifdef CGEN_MNEMONIC_OPERANDS
-  /* FIXME: wip */
-  int past_opcode_p;
-#endif
+  char prev_syntax_char = 0;
+  bool past_opcode_p;
 
   /* For now we assume the mnemonic is first (there are no leading operands).
      We can parse it without needing to set up operand parsing.
@@ -649,13 +658,13 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 #ifndef CGEN_MNEMONIC_OPERANDS
   if (* str && ! ISSPACE (* str))
     return _("unrecognized instruction");
+  past_opcode_p = true;
+#else
+  past_opcode_p = false;
 #endif
 
   CGEN_INIT_PARSE (cd);
   cgen_init_parse_operand (cd);
-#ifdef CGEN_MNEMONIC_OPERANDS
-  past_opcode_p = 0;
-#endif
 
   /* We don't check for (*str != '\0') here because we want to parse
      any trailing fake arguments in the syntax string.  */
@@ -669,18 +678,28 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
   while (* syn != 0)
     {
+      char c = CGEN_SYNTAX_CHAR_P (*syn) ? CGEN_SYNTAX_CHAR (*syn) : 0;
+
+      /* FIXME: Despite this check we may still take inappropriate advantage of
+	 the fact that GAS's input scrubber will remove extraneous whitespace.
+	 We may also be a little too lax with this now, yet being more strict
+	 would require targets to indicate where whitespace is permissible.  */
+      if (past_opcode_p && c != ' ' && ISSPACE (*str)
+	  /* No whitespace between consecutive alphanumeric syntax elements.  */
+	  && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	++str;
+      prev_syntax_char = c;
+
       /* Non operand chars must match exactly.  */
-      if (CGEN_SYNTAX_CHAR_P (* syn))
+      if (c != 0)
 	{
 	  /* FIXME: While we allow for non-GAS callers above, we assume the
 	     first char after the mnemonic part is a space.  */
-	  /* FIXME: We also take inappropriate advantage of the fact that
-	     GAS's input scrubber will remove extraneous blanks.  */
-	  if (TOLOWER (*str) == TOLOWER (CGEN_SYNTAX_CHAR (* syn)))
+	  if (TOLOWER (*str) == TOLOWER (c))
 	    {
 #ifdef CGEN_MNEMONIC_OPERANDS
-	      if (CGEN_SYNTAX_CHAR(* syn) == ' ')
-		past_opcode_p = 1;
+	      if (c == ' ')
+		past_opcode_p = true;
 #endif
 	      ++ syn;
 	      ++ str;
@@ -692,7 +711,7 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found `%c')"),
-		       CGEN_SYNTAX_CHAR(*syn), *str);
+		       c, *str);
 	      return msg;
 	    }
 	  else
@@ -702,15 +721,12 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found end of instruction)"),
-		       CGEN_SYNTAX_CHAR(*syn));
+		       c);
 	      return msg;
 	    }
 	  continue;
 	}
 
-#ifdef CGEN_MNEMONIC_OPERANDS
-      (void) past_opcode_p;
-#endif
       /* We have an operand of some sort.  */
       errmsg = cd->parse_operand (cd, CGEN_SYNTAX_FIELD (*syn), &str, fields);
       if (errmsg)
diff --git a/opcodes/lm32-asm.c b/opcodes/lm32-asm.c
index d5e25f9e8b9..5c4f9f4b69f 100644
--- a/opcodes/lm32-asm.c
+++ b/opcodes/lm32-asm.c
@@ -393,6 +393,7 @@ lm32_cgen_build_insn_regex (CGEN_INSN *insn)
   char rxbuf[CGEN_MAX_RX_ELEMENTS];
   char *rx = rxbuf;
   const CGEN_SYNTAX_CHAR_TYPE *syn;
+  char prev_syntax_char = 0;
   int reg_err;
 
   syn = CGEN_SYNTAX_STRING (CGEN_OPCODE_SYNTAX (opc));
@@ -430,6 +431,15 @@ lm32_cgen_build_insn_regex (CGEN_INSN *insn)
 	{
 	  char c = CGEN_SYNTAX_CHAR (* syn);
 
+	  /* See whitespace related comments in parse_insn_normal().  */
+	  if (c != ' ' && prev_syntax_char != ' '
+	      && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	    {
+	      *rx++ = ' ';
+	      *rx++ = '*';
+	    }
+	  prev_syntax_char = c;
+
 	  switch (c)
 	    {
 	      /* Escape any regex metacharacters in the syntax.  */
@@ -463,6 +473,7 @@ lm32_cgen_build_insn_regex (CGEN_INSN *insn)
 	  /* Replace non-syntax fields with globs.  */
 	  *rx++ = '.';
 	  *rx++ = '*';
+	  prev_syntax_char = 0;
 	}
     }
 
@@ -520,10 +531,8 @@ parse_insn_normal (CGEN_CPU_DESC cd,
   const char *errmsg;
   const char *p;
   const CGEN_SYNTAX_CHAR_TYPE * syn;
-#ifdef CGEN_MNEMONIC_OPERANDS
-  /* FIXME: wip */
-  int past_opcode_p;
-#endif
+  char prev_syntax_char = 0;
+  bool past_opcode_p;
 
   /* For now we assume the mnemonic is first (there are no leading operands).
      We can parse it without needing to set up operand parsing.
@@ -539,13 +548,13 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 #ifndef CGEN_MNEMONIC_OPERANDS
   if (* str && ! ISSPACE (* str))
     return _("unrecognized instruction");
+  past_opcode_p = true;
+#else
+  past_opcode_p = false;
 #endif
 
   CGEN_INIT_PARSE (cd);
   cgen_init_parse_operand (cd);
-#ifdef CGEN_MNEMONIC_OPERANDS
-  past_opcode_p = 0;
-#endif
 
   /* We don't check for (*str != '\0') here because we want to parse
      any trailing fake arguments in the syntax string.  */
@@ -559,18 +568,28 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
   while (* syn != 0)
     {
+      char c = CGEN_SYNTAX_CHAR_P (*syn) ? CGEN_SYNTAX_CHAR (*syn) : 0;
+
+      /* FIXME: Despite this check we may still take inappropriate advantage of
+	 the fact that GAS's input scrubber will remove extraneous whitespace.
+	 We may also be a little too lax with this now, yet being more strict
+	 would require targets to indicate where whitespace is permissible.  */
+      if (past_opcode_p && c != ' ' && ISSPACE (*str)
+	  /* No whitespace between consecutive alphanumeric syntax elements.  */
+	  && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	++str;
+      prev_syntax_char = c;
+
       /* Non operand chars must match exactly.  */
-      if (CGEN_SYNTAX_CHAR_P (* syn))
+      if (c != 0)
 	{
 	  /* FIXME: While we allow for non-GAS callers above, we assume the
 	     first char after the mnemonic part is a space.  */
-	  /* FIXME: We also take inappropriate advantage of the fact that
-	     GAS's input scrubber will remove extraneous blanks.  */
-	  if (TOLOWER (*str) == TOLOWER (CGEN_SYNTAX_CHAR (* syn)))
+	  if (TOLOWER (*str) == TOLOWER (c))
 	    {
 #ifdef CGEN_MNEMONIC_OPERANDS
-	      if (CGEN_SYNTAX_CHAR(* syn) == ' ')
-		past_opcode_p = 1;
+	      if (c == ' ')
+		past_opcode_p = true;
 #endif
 	      ++ syn;
 	      ++ str;
@@ -582,7 +601,7 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found `%c')"),
-		       CGEN_SYNTAX_CHAR(*syn), *str);
+		       c, *str);
 	      return msg;
 	    }
 	  else
@@ -592,15 +611,12 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found end of instruction)"),
-		       CGEN_SYNTAX_CHAR(*syn));
+		       c);
 	      return msg;
 	    }
 	  continue;
 	}
 
-#ifdef CGEN_MNEMONIC_OPERANDS
-      (void) past_opcode_p;
-#endif
       /* We have an operand of some sort.  */
       errmsg = cd->parse_operand (cd, CGEN_SYNTAX_FIELD (*syn), &str, fields);
       if (errmsg)
diff --git a/opcodes/m32c-asm.c b/opcodes/m32c-asm.c
index 8a7983c4c5b..aebb59f0e26 100644
--- a/opcodes/m32c-asm.c
+++ b/opcodes/m32c-asm.c
@@ -1628,6 +1628,7 @@ m32c_cgen_build_insn_regex (CGEN_INSN *insn)
   char rxbuf[CGEN_MAX_RX_ELEMENTS];
   char *rx = rxbuf;
   const CGEN_SYNTAX_CHAR_TYPE *syn;
+  char prev_syntax_char = 0;
   int reg_err;
 
   syn = CGEN_SYNTAX_STRING (CGEN_OPCODE_SYNTAX (opc));
@@ -1665,6 +1666,15 @@ m32c_cgen_build_insn_regex (CGEN_INSN *insn)
 	{
 	  char c = CGEN_SYNTAX_CHAR (* syn);
 
+	  /* See whitespace related comments in parse_insn_normal().  */
+	  if (c != ' ' && prev_syntax_char != ' '
+	      && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	    {
+	      *rx++ = ' ';
+	      *rx++ = '*';
+	    }
+	  prev_syntax_char = c;
+
 	  switch (c)
 	    {
 	      /* Escape any regex metacharacters in the syntax.  */
@@ -1698,6 +1708,7 @@ m32c_cgen_build_insn_regex (CGEN_INSN *insn)
 	  /* Replace non-syntax fields with globs.  */
 	  *rx++ = '.';
 	  *rx++ = '*';
+	  prev_syntax_char = 0;
 	}
     }
 
@@ -1755,10 +1766,8 @@ parse_insn_normal (CGEN_CPU_DESC cd,
   const char *errmsg;
   const char *p;
   const CGEN_SYNTAX_CHAR_TYPE * syn;
-#ifdef CGEN_MNEMONIC_OPERANDS
-  /* FIXME: wip */
-  int past_opcode_p;
-#endif
+  char prev_syntax_char = 0;
+  bool past_opcode_p;
 
   /* For now we assume the mnemonic is first (there are no leading operands).
      We can parse it without needing to set up operand parsing.
@@ -1774,13 +1783,13 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 #ifndef CGEN_MNEMONIC_OPERANDS
   if (* str && ! ISSPACE (* str))
     return _("unrecognized instruction");
+  past_opcode_p = true;
+#else
+  past_opcode_p = false;
 #endif
 
   CGEN_INIT_PARSE (cd);
   cgen_init_parse_operand (cd);
-#ifdef CGEN_MNEMONIC_OPERANDS
-  past_opcode_p = 0;
-#endif
 
   /* We don't check for (*str != '\0') here because we want to parse
      any trailing fake arguments in the syntax string.  */
@@ -1794,18 +1803,28 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
   while (* syn != 0)
     {
+      char c = CGEN_SYNTAX_CHAR_P (*syn) ? CGEN_SYNTAX_CHAR (*syn) : 0;
+
+      /* FIXME: Despite this check we may still take inappropriate advantage of
+	 the fact that GAS's input scrubber will remove extraneous whitespace.
+	 We may also be a little too lax with this now, yet being more strict
+	 would require targets to indicate where whitespace is permissible.  */
+      if (past_opcode_p && c != ' ' && ISSPACE (*str)
+	  /* No whitespace between consecutive alphanumeric syntax elements.  */
+	  && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	++str;
+      prev_syntax_char = c;
+
       /* Non operand chars must match exactly.  */
-      if (CGEN_SYNTAX_CHAR_P (* syn))
+      if (c != 0)
 	{
 	  /* FIXME: While we allow for non-GAS callers above, we assume the
 	     first char after the mnemonic part is a space.  */
-	  /* FIXME: We also take inappropriate advantage of the fact that
-	     GAS's input scrubber will remove extraneous blanks.  */
-	  if (TOLOWER (*str) == TOLOWER (CGEN_SYNTAX_CHAR (* syn)))
+	  if (TOLOWER (*str) == TOLOWER (c))
 	    {
 #ifdef CGEN_MNEMONIC_OPERANDS
-	      if (CGEN_SYNTAX_CHAR(* syn) == ' ')
-		past_opcode_p = 1;
+	      if (c == ' ')
+		past_opcode_p = true;
 #endif
 	      ++ syn;
 	      ++ str;
@@ -1817,7 +1836,7 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found `%c')"),
-		       CGEN_SYNTAX_CHAR(*syn), *str);
+		       c, *str);
 	      return msg;
 	    }
 	  else
@@ -1827,15 +1846,12 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found end of instruction)"),
-		       CGEN_SYNTAX_CHAR(*syn));
+		       c);
 	      return msg;
 	    }
 	  continue;
 	}
 
-#ifdef CGEN_MNEMONIC_OPERANDS
-      (void) past_opcode_p;
-#endif
       /* We have an operand of some sort.  */
       errmsg = cd->parse_operand (cd, CGEN_SYNTAX_FIELD (*syn), &str, fields);
       if (errmsg)
diff --git a/opcodes/m32r-asm.c b/opcodes/m32r-asm.c
index 4d19babdb00..ae6ed499142 100644
--- a/opcodes/m32r-asm.c
+++ b/opcodes/m32r-asm.c
@@ -372,6 +372,7 @@ m32r_cgen_build_insn_regex (CGEN_INSN *insn)
   char rxbuf[CGEN_MAX_RX_ELEMENTS];
   char *rx = rxbuf;
   const CGEN_SYNTAX_CHAR_TYPE *syn;
+  char prev_syntax_char = 0;
   int reg_err;
 
   syn = CGEN_SYNTAX_STRING (CGEN_OPCODE_SYNTAX (opc));
@@ -409,6 +410,15 @@ m32r_cgen_build_insn_regex (CGEN_INSN *insn)
 	{
 	  char c = CGEN_SYNTAX_CHAR (* syn);
 
+	  /* See whitespace related comments in parse_insn_normal().  */
+	  if (c != ' ' && prev_syntax_char != ' '
+	      && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	    {
+	      *rx++ = ' ';
+	      *rx++ = '*';
+	    }
+	  prev_syntax_char = c;
+
 	  switch (c)
 	    {
 	      /* Escape any regex metacharacters in the syntax.  */
@@ -442,6 +452,7 @@ m32r_cgen_build_insn_regex (CGEN_INSN *insn)
 	  /* Replace non-syntax fields with globs.  */
 	  *rx++ = '.';
 	  *rx++ = '*';
+	  prev_syntax_char = 0;
 	}
     }
 
@@ -499,10 +510,8 @@ parse_insn_normal (CGEN_CPU_DESC cd,
   const char *errmsg;
   const char *p;
   const CGEN_SYNTAX_CHAR_TYPE * syn;
-#ifdef CGEN_MNEMONIC_OPERANDS
-  /* FIXME: wip */
-  int past_opcode_p;
-#endif
+  char prev_syntax_char = 0;
+  bool past_opcode_p;
 
   /* For now we assume the mnemonic is first (there are no leading operands).
      We can parse it without needing to set up operand parsing.
@@ -518,13 +527,13 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 #ifndef CGEN_MNEMONIC_OPERANDS
   if (* str && ! ISSPACE (* str))
     return _("unrecognized instruction");
+  past_opcode_p = true;
+#else
+  past_opcode_p = false;
 #endif
 
   CGEN_INIT_PARSE (cd);
   cgen_init_parse_operand (cd);
-#ifdef CGEN_MNEMONIC_OPERANDS
-  past_opcode_p = 0;
-#endif
 
   /* We don't check for (*str != '\0') here because we want to parse
      any trailing fake arguments in the syntax string.  */
@@ -538,18 +547,28 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
   while (* syn != 0)
     {
+      char c = CGEN_SYNTAX_CHAR_P (*syn) ? CGEN_SYNTAX_CHAR (*syn) : 0;
+
+      /* FIXME: Despite this check we may still take inappropriate advantage of
+	 the fact that GAS's input scrubber will remove extraneous whitespace.
+	 We may also be a little too lax with this now, yet being more strict
+	 would require targets to indicate where whitespace is permissible.  */
+      if (past_opcode_p && c != ' ' && ISSPACE (*str)
+	  /* No whitespace between consecutive alphanumeric syntax elements.  */
+	  && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	++str;
+      prev_syntax_char = c;
+
       /* Non operand chars must match exactly.  */
-      if (CGEN_SYNTAX_CHAR_P (* syn))
+      if (c != 0)
 	{
 	  /* FIXME: While we allow for non-GAS callers above, we assume the
 	     first char after the mnemonic part is a space.  */
-	  /* FIXME: We also take inappropriate advantage of the fact that
-	     GAS's input scrubber will remove extraneous blanks.  */
-	  if (TOLOWER (*str) == TOLOWER (CGEN_SYNTAX_CHAR (* syn)))
+	  if (TOLOWER (*str) == TOLOWER (c))
 	    {
 #ifdef CGEN_MNEMONIC_OPERANDS
-	      if (CGEN_SYNTAX_CHAR(* syn) == ' ')
-		past_opcode_p = 1;
+	      if (c == ' ')
+		past_opcode_p = true;
 #endif
 	      ++ syn;
 	      ++ str;
@@ -561,7 +580,7 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found `%c')"),
-		       CGEN_SYNTAX_CHAR(*syn), *str);
+		       c, *str);
 	      return msg;
 	    }
 	  else
@@ -571,15 +590,12 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found end of instruction)"),
-		       CGEN_SYNTAX_CHAR(*syn));
+		       c);
 	      return msg;
 	    }
 	  continue;
 	}
 
-#ifdef CGEN_MNEMONIC_OPERANDS
-      (void) past_opcode_p;
-#endif
       /* We have an operand of some sort.  */
       errmsg = cd->parse_operand (cd, CGEN_SYNTAX_FIELD (*syn), &str, fields);
       if (errmsg)
diff --git a/opcodes/mep-asm.c b/opcodes/mep-asm.c
index 6e72ddba5a1..87af40ca844 100644
--- a/opcodes/mep-asm.c
+++ b/opcodes/mep-asm.c
@@ -1331,6 +1331,7 @@ mep_cgen_build_insn_regex (CGEN_INSN *insn)
   char rxbuf[CGEN_MAX_RX_ELEMENTS];
   char *rx = rxbuf;
   const CGEN_SYNTAX_CHAR_TYPE *syn;
+  char prev_syntax_char = 0;
   int reg_err;
 
   syn = CGEN_SYNTAX_STRING (CGEN_OPCODE_SYNTAX (opc));
@@ -1368,6 +1369,15 @@ mep_cgen_build_insn_regex (CGEN_INSN *insn)
 	{
 	  char c = CGEN_SYNTAX_CHAR (* syn);
 
+	  /* See whitespace related comments in parse_insn_normal().  */
+	  if (c != ' ' && prev_syntax_char != ' '
+	      && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	    {
+	      *rx++ = ' ';
+	      *rx++ = '*';
+	    }
+	  prev_syntax_char = c;
+
 	  switch (c)
 	    {
 	      /* Escape any regex metacharacters in the syntax.  */
@@ -1401,6 +1411,7 @@ mep_cgen_build_insn_regex (CGEN_INSN *insn)
 	  /* Replace non-syntax fields with globs.  */
 	  *rx++ = '.';
 	  *rx++ = '*';
+	  prev_syntax_char = 0;
 	}
     }
 
@@ -1458,10 +1469,8 @@ parse_insn_normal (CGEN_CPU_DESC cd,
   const char *errmsg;
   const char *p;
   const CGEN_SYNTAX_CHAR_TYPE * syn;
-#ifdef CGEN_MNEMONIC_OPERANDS
-  /* FIXME: wip */
-  int past_opcode_p;
-#endif
+  char prev_syntax_char = 0;
+  bool past_opcode_p;
 
   /* For now we assume the mnemonic is first (there are no leading operands).
      We can parse it without needing to set up operand parsing.
@@ -1477,13 +1486,13 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 #ifndef CGEN_MNEMONIC_OPERANDS
   if (* str && ! ISSPACE (* str))
     return _("unrecognized instruction");
+  past_opcode_p = true;
+#else
+  past_opcode_p = false;
 #endif
 
   CGEN_INIT_PARSE (cd);
   cgen_init_parse_operand (cd);
-#ifdef CGEN_MNEMONIC_OPERANDS
-  past_opcode_p = 0;
-#endif
 
   /* We don't check for (*str != '\0') here because we want to parse
      any trailing fake arguments in the syntax string.  */
@@ -1497,18 +1506,28 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
   while (* syn != 0)
     {
+      char c = CGEN_SYNTAX_CHAR_P (*syn) ? CGEN_SYNTAX_CHAR (*syn) : 0;
+
+      /* FIXME: Despite this check we may still take inappropriate advantage of
+	 the fact that GAS's input scrubber will remove extraneous whitespace.
+	 We may also be a little too lax with this now, yet being more strict
+	 would require targets to indicate where whitespace is permissible.  */
+      if (past_opcode_p && c != ' ' && ISSPACE (*str)
+	  /* No whitespace between consecutive alphanumeric syntax elements.  */
+	  && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	++str;
+      prev_syntax_char = c;
+
       /* Non operand chars must match exactly.  */
-      if (CGEN_SYNTAX_CHAR_P (* syn))
+      if (c != 0)
 	{
 	  /* FIXME: While we allow for non-GAS callers above, we assume the
 	     first char after the mnemonic part is a space.  */
-	  /* FIXME: We also take inappropriate advantage of the fact that
-	     GAS's input scrubber will remove extraneous blanks.  */
-	  if (TOLOWER (*str) == TOLOWER (CGEN_SYNTAX_CHAR (* syn)))
+	  if (TOLOWER (*str) == TOLOWER (c))
 	    {
 #ifdef CGEN_MNEMONIC_OPERANDS
-	      if (CGEN_SYNTAX_CHAR(* syn) == ' ')
-		past_opcode_p = 1;
+	      if (c == ' ')
+		past_opcode_p = true;
 #endif
 	      ++ syn;
 	      ++ str;
@@ -1520,7 +1539,7 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found `%c')"),
-		       CGEN_SYNTAX_CHAR(*syn), *str);
+		       c, *str);
 	      return msg;
 	    }
 	  else
@@ -1530,15 +1549,12 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found end of instruction)"),
-		       CGEN_SYNTAX_CHAR(*syn));
+		       c);
 	      return msg;
 	    }
 	  continue;
 	}
 
-#ifdef CGEN_MNEMONIC_OPERANDS
-      (void) past_opcode_p;
-#endif
       /* We have an operand of some sort.  */
       errmsg = cd->parse_operand (cd, CGEN_SYNTAX_FIELD (*syn), &str, fields);
       if (errmsg)
diff --git a/opcodes/mt-asm.c b/opcodes/mt-asm.c
index 074c1ab57ac..8263670f7ac 100644
--- a/opcodes/mt-asm.c
+++ b/opcodes/mt-asm.c
@@ -639,6 +639,7 @@ mt_cgen_build_insn_regex (CGEN_INSN *insn)
   char rxbuf[CGEN_MAX_RX_ELEMENTS];
   char *rx = rxbuf;
   const CGEN_SYNTAX_CHAR_TYPE *syn;
+  char prev_syntax_char = 0;
   int reg_err;
 
   syn = CGEN_SYNTAX_STRING (CGEN_OPCODE_SYNTAX (opc));
@@ -676,6 +677,15 @@ mt_cgen_build_insn_regex (CGEN_INSN *insn)
 	{
 	  char c = CGEN_SYNTAX_CHAR (* syn);
 
+	  /* See whitespace related comments in parse_insn_normal().  */
+	  if (c != ' ' && prev_syntax_char != ' '
+	      && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	    {
+	      *rx++ = ' ';
+	      *rx++ = '*';
+	    }
+	  prev_syntax_char = c;
+
 	  switch (c)
 	    {
 	      /* Escape any regex metacharacters in the syntax.  */
@@ -709,6 +719,7 @@ mt_cgen_build_insn_regex (CGEN_INSN *insn)
 	  /* Replace non-syntax fields with globs.  */
 	  *rx++ = '.';
 	  *rx++ = '*';
+	  prev_syntax_char = 0;
 	}
     }
 
@@ -766,10 +777,8 @@ parse_insn_normal (CGEN_CPU_DESC cd,
   const char *errmsg;
   const char *p;
   const CGEN_SYNTAX_CHAR_TYPE * syn;
-#ifdef CGEN_MNEMONIC_OPERANDS
-  /* FIXME: wip */
-  int past_opcode_p;
-#endif
+  char prev_syntax_char = 0;
+  bool past_opcode_p;
 
   /* For now we assume the mnemonic is first (there are no leading operands).
      We can parse it without needing to set up operand parsing.
@@ -785,13 +794,13 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 #ifndef CGEN_MNEMONIC_OPERANDS
   if (* str && ! ISSPACE (* str))
     return _("unrecognized instruction");
+  past_opcode_p = true;
+#else
+  past_opcode_p = false;
 #endif
 
   CGEN_INIT_PARSE (cd);
   cgen_init_parse_operand (cd);
-#ifdef CGEN_MNEMONIC_OPERANDS
-  past_opcode_p = 0;
-#endif
 
   /* We don't check for (*str != '\0') here because we want to parse
      any trailing fake arguments in the syntax string.  */
@@ -805,18 +814,28 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
   while (* syn != 0)
     {
+      char c = CGEN_SYNTAX_CHAR_P (*syn) ? CGEN_SYNTAX_CHAR (*syn) : 0;
+
+      /* FIXME: Despite this check we may still take inappropriate advantage of
+	 the fact that GAS's input scrubber will remove extraneous whitespace.
+	 We may also be a little too lax with this now, yet being more strict
+	 would require targets to indicate where whitespace is permissible.  */
+      if (past_opcode_p && c != ' ' && ISSPACE (*str)
+	  /* No whitespace between consecutive alphanumeric syntax elements.  */
+	  && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	++str;
+      prev_syntax_char = c;
+
       /* Non operand chars must match exactly.  */
-      if (CGEN_SYNTAX_CHAR_P (* syn))
+      if (c != 0)
 	{
 	  /* FIXME: While we allow for non-GAS callers above, we assume the
 	     first char after the mnemonic part is a space.  */
-	  /* FIXME: We also take inappropriate advantage of the fact that
-	     GAS's input scrubber will remove extraneous blanks.  */
-	  if (TOLOWER (*str) == TOLOWER (CGEN_SYNTAX_CHAR (* syn)))
+	  if (TOLOWER (*str) == TOLOWER (c))
 	    {
 #ifdef CGEN_MNEMONIC_OPERANDS
-	      if (CGEN_SYNTAX_CHAR(* syn) == ' ')
-		past_opcode_p = 1;
+	      if (c == ' ')
+		past_opcode_p = true;
 #endif
 	      ++ syn;
 	      ++ str;
@@ -828,7 +847,7 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found `%c')"),
-		       CGEN_SYNTAX_CHAR(*syn), *str);
+		       c, *str);
 	      return msg;
 	    }
 	  else
@@ -838,15 +857,12 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found end of instruction)"),
-		       CGEN_SYNTAX_CHAR(*syn));
+		       c);
 	      return msg;
 	    }
 	  continue;
 	}
 
-#ifdef CGEN_MNEMONIC_OPERANDS
-      (void) past_opcode_p;
-#endif
       /* We have an operand of some sort.  */
       errmsg = cd->parse_operand (cd, CGEN_SYNTAX_FIELD (*syn), &str, fields);
       if (errmsg)
diff --git a/opcodes/nds32-asm.c b/opcodes/nds32-asm.c
index 67a98336530..f7a5e54f985 100644
--- a/opcodes/nds32-asm.c
+++ b/opcodes/nds32-asm.c
@@ -2486,6 +2486,9 @@ parse_insn (nds32_asm_desc_t *pdesc, nds32_asm_insn_t *pinsn,
 
       while (*plex)
 	{
+	  if (ISSPACE (*p))
+	    ++p;
+
 	  if (IS_LEX_CHAR (*plex))
 	    {
 	      /* If it's a plain char, just compare it.  */
@@ -2530,6 +2533,8 @@ parse_insn (nds32_asm_desc_t *pdesc, nds32_asm_insn_t *pinsn,
 	}
 
       /* Check whether this syntax is accepted.  */
+      if (ISSPACE (*p))
+	++p;
       if (*plex == 0 && (*p == '\0' || *p == '!' || *p == '#'))
 	return 1;
 
diff --git a/opcodes/or1k-asm.c b/opcodes/or1k-asm.c
index 0839366788d..9351c1b407b 100644
--- a/opcodes/or1k-asm.c
+++ b/opcodes/or1k-asm.c
@@ -619,6 +619,7 @@ or1k_cgen_build_insn_regex (CGEN_INSN *insn)
   char rxbuf[CGEN_MAX_RX_ELEMENTS];
   char *rx = rxbuf;
   const CGEN_SYNTAX_CHAR_TYPE *syn;
+  char prev_syntax_char = 0;
   int reg_err;
 
   syn = CGEN_SYNTAX_STRING (CGEN_OPCODE_SYNTAX (opc));
@@ -656,6 +657,15 @@ or1k_cgen_build_insn_regex (CGEN_INSN *insn)
 	{
 	  char c = CGEN_SYNTAX_CHAR (* syn);
 
+	  /* See whitespace related comments in parse_insn_normal().  */
+	  if (c != ' ' && prev_syntax_char != ' '
+	      && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	    {
+	      *rx++ = ' ';
+	      *rx++ = '*';
+	    }
+	  prev_syntax_char = c;
+
 	  switch (c)
 	    {
 	      /* Escape any regex metacharacters in the syntax.  */
@@ -689,6 +699,7 @@ or1k_cgen_build_insn_regex (CGEN_INSN *insn)
 	  /* Replace non-syntax fields with globs.  */
 	  *rx++ = '.';
 	  *rx++ = '*';
+	  prev_syntax_char = 0;
 	}
     }
 
@@ -746,10 +757,8 @@ parse_insn_normal (CGEN_CPU_DESC cd,
   const char *errmsg;
   const char *p;
   const CGEN_SYNTAX_CHAR_TYPE * syn;
-#ifdef CGEN_MNEMONIC_OPERANDS
-  /* FIXME: wip */
-  int past_opcode_p;
-#endif
+  char prev_syntax_char = 0;
+  bool past_opcode_p;
 
   /* For now we assume the mnemonic is first (there are no leading operands).
      We can parse it without needing to set up operand parsing.
@@ -765,13 +774,13 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 #ifndef CGEN_MNEMONIC_OPERANDS
   if (* str && ! ISSPACE (* str))
     return _("unrecognized instruction");
+  past_opcode_p = true;
+#else
+  past_opcode_p = false;
 #endif
 
   CGEN_INIT_PARSE (cd);
   cgen_init_parse_operand (cd);
-#ifdef CGEN_MNEMONIC_OPERANDS
-  past_opcode_p = 0;
-#endif
 
   /* We don't check for (*str != '\0') here because we want to parse
      any trailing fake arguments in the syntax string.  */
@@ -785,18 +794,28 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
   while (* syn != 0)
     {
+      char c = CGEN_SYNTAX_CHAR_P (*syn) ? CGEN_SYNTAX_CHAR (*syn) : 0;
+
+      /* FIXME: Despite this check we may still take inappropriate advantage of
+	 the fact that GAS's input scrubber will remove extraneous whitespace.
+	 We may also be a little too lax with this now, yet being more strict
+	 would require targets to indicate where whitespace is permissible.  */
+      if (past_opcode_p && c != ' ' && ISSPACE (*str)
+	  /* No whitespace between consecutive alphanumeric syntax elements.  */
+	  && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	++str;
+      prev_syntax_char = c;
+
       /* Non operand chars must match exactly.  */
-      if (CGEN_SYNTAX_CHAR_P (* syn))
+      if (c != 0)
 	{
 	  /* FIXME: While we allow for non-GAS callers above, we assume the
 	     first char after the mnemonic part is a space.  */
-	  /* FIXME: We also take inappropriate advantage of the fact that
-	     GAS's input scrubber will remove extraneous blanks.  */
-	  if (TOLOWER (*str) == TOLOWER (CGEN_SYNTAX_CHAR (* syn)))
+	  if (TOLOWER (*str) == TOLOWER (c))
 	    {
 #ifdef CGEN_MNEMONIC_OPERANDS
-	      if (CGEN_SYNTAX_CHAR(* syn) == ' ')
-		past_opcode_p = 1;
+	      if (c == ' ')
+		past_opcode_p = true;
 #endif
 	      ++ syn;
 	      ++ str;
@@ -808,7 +827,7 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found `%c')"),
-		       CGEN_SYNTAX_CHAR(*syn), *str);
+		       c, *str);
 	      return msg;
 	    }
 	  else
@@ -818,15 +837,12 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found end of instruction)"),
-		       CGEN_SYNTAX_CHAR(*syn));
+		       c);
 	      return msg;
 	    }
 	  continue;
 	}
 
-#ifdef CGEN_MNEMONIC_OPERANDS
-      (void) past_opcode_p;
-#endif
       /* We have an operand of some sort.  */
       errmsg = cd->parse_operand (cd, CGEN_SYNTAX_FIELD (*syn), &str, fields);
       if (errmsg)
diff --git a/opcodes/xstormy16-asm.c b/opcodes/xstormy16-asm.c
index 8a9f33cdf93..40c9265da18 100644
--- a/opcodes/xstormy16-asm.c
+++ b/opcodes/xstormy16-asm.c
@@ -320,6 +320,7 @@ xstormy16_cgen_build_insn_regex (CGEN_INSN *insn)
   char rxbuf[CGEN_MAX_RX_ELEMENTS];
   char *rx = rxbuf;
   const CGEN_SYNTAX_CHAR_TYPE *syn;
+  char prev_syntax_char = 0;
   int reg_err;
 
   syn = CGEN_SYNTAX_STRING (CGEN_OPCODE_SYNTAX (opc));
@@ -357,6 +358,15 @@ xstormy16_cgen_build_insn_regex (CGEN_INSN *insn)
 	{
 	  char c = CGEN_SYNTAX_CHAR (* syn);
 
+	  /* See whitespace related comments in parse_insn_normal().  */
+	  if (c != ' ' && prev_syntax_char != ' '
+	      && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	    {
+	      *rx++ = ' ';
+	      *rx++ = '*';
+	    }
+	  prev_syntax_char = c;
+
 	  switch (c)
 	    {
 	      /* Escape any regex metacharacters in the syntax.  */
@@ -390,6 +400,7 @@ xstormy16_cgen_build_insn_regex (CGEN_INSN *insn)
 	  /* Replace non-syntax fields with globs.  */
 	  *rx++ = '.';
 	  *rx++ = '*';
+	  prev_syntax_char = 0;
 	}
     }
 
@@ -447,10 +458,8 @@ parse_insn_normal (CGEN_CPU_DESC cd,
   const char *errmsg;
   const char *p;
   const CGEN_SYNTAX_CHAR_TYPE * syn;
-#ifdef CGEN_MNEMONIC_OPERANDS
-  /* FIXME: wip */
-  int past_opcode_p;
-#endif
+  char prev_syntax_char = 0;
+  bool past_opcode_p;
 
   /* For now we assume the mnemonic is first (there are no leading operands).
      We can parse it without needing to set up operand parsing.
@@ -466,13 +475,13 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 #ifndef CGEN_MNEMONIC_OPERANDS
   if (* str && ! ISSPACE (* str))
     return _("unrecognized instruction");
+  past_opcode_p = true;
+#else
+  past_opcode_p = false;
 #endif
 
   CGEN_INIT_PARSE (cd);
   cgen_init_parse_operand (cd);
-#ifdef CGEN_MNEMONIC_OPERANDS
-  past_opcode_p = 0;
-#endif
 
   /* We don't check for (*str != '\0') here because we want to parse
      any trailing fake arguments in the syntax string.  */
@@ -486,18 +495,28 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
   while (* syn != 0)
     {
+      char c = CGEN_SYNTAX_CHAR_P (*syn) ? CGEN_SYNTAX_CHAR (*syn) : 0;
+
+      /* FIXME: Despite this check we may still take inappropriate advantage of
+	 the fact that GAS's input scrubber will remove extraneous whitespace.
+	 We may also be a little too lax with this now, yet being more strict
+	 would require targets to indicate where whitespace is permissible.  */
+      if (past_opcode_p && c != ' ' && ISSPACE (*str)
+	  /* No whitespace between consecutive alphanumeric syntax elements.  */
+	  && (!ISALNUM (c) || !ISALNUM (prev_syntax_char)))
+	++str;
+      prev_syntax_char = c;
+
       /* Non operand chars must match exactly.  */
-      if (CGEN_SYNTAX_CHAR_P (* syn))
+      if (c != 0)
 	{
 	  /* FIXME: While we allow for non-GAS callers above, we assume the
 	     first char after the mnemonic part is a space.  */
-	  /* FIXME: We also take inappropriate advantage of the fact that
-	     GAS's input scrubber will remove extraneous blanks.  */
-	  if (TOLOWER (*str) == TOLOWER (CGEN_SYNTAX_CHAR (* syn)))
+	  if (TOLOWER (*str) == TOLOWER (c))
 	    {
 #ifdef CGEN_MNEMONIC_OPERANDS
-	      if (CGEN_SYNTAX_CHAR(* syn) == ' ')
-		past_opcode_p = 1;
+	      if (c == ' ')
+		past_opcode_p = true;
 #endif
 	      ++ syn;
 	      ++ str;
@@ -509,7 +528,7 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found `%c')"),
-		       CGEN_SYNTAX_CHAR(*syn), *str);
+		       c, *str);
 	      return msg;
 	    }
 	  else
@@ -519,15 +538,12 @@ parse_insn_normal (CGEN_CPU_DESC cd,
 
 	      /* xgettext:c-format */
 	      sprintf (msg, _("syntax error (expected char `%c', found end of instruction)"),
-		       CGEN_SYNTAX_CHAR(*syn));
+		       c);
 	      return msg;
 	    }
 	  continue;
 	}
 
-#ifdef CGEN_MNEMONIC_OPERANDS
-      (void) past_opcode_p;
-#endif
       /* We have an operand of some sort.  */
       errmsg = cd->parse_operand (cd, CGEN_SYNTAX_FIELD (*syn), &str, fields);
       if (errmsg)

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2024-08-09 10:00 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-08-09 10:00 [binutils-gdb] gas: have scrubber retain more whitespace Jan Beulich

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).