--- a/opcodes/i386-gen.c
+++ b/opcodes/i386-gen.c
@@ -1672,6 +1672,69 @@ parse_template (char *buf, int lineno)
   return true;
 }
 
+static void
+expand_template (const struct template *tmpl,
+		 const struct template_instance *inst,
+		 char *dst, const char *src, int lineno)
+{
+  while (*src)
+    {
+      const char *ident = tmpl->name, *end;
+      const struct template_param *param;
+      const struct template_arg *arg;
+
+      if ((*dst = *src++) != '<')
+	{
+	  ++dst;
+	  continue;
+	}
+      while (ISSPACE(*src))
+	++src;
+      while (*ident && *src == *ident)
+	++src, ++ident;
+      while (ISSPACE(*src))
+	++src;
+      if (*src != ':' || *ident != '\0')
+	{
+	  memcpy (++dst, tmpl->name, ident - tmpl->name);
+	  dst += ident - tmpl->name;
+	  continue;
+	}
+      while (ISSPACE(*++src))
+	;
+
+      end = src;
+      while (*end != '\0' && !ISSPACE(*end) && *end != '>')
+	++end;
+
+      for (param = tmpl->params, arg = inst->args; param;
+	   param = param->next, arg = arg->next)
+	{
+	  if (end - src == strlen (param->name)
+	      && !memcmp (src, param->name, end - src))
+	    {
+	      src = end;
+	      break;
+	    }
+	}
+
+      if (param == NULL)
+	fail ("template '%s' has no parameter '%.*s'\n",
+	      tmpl->name, (int)(end - src), src);
+
+      while (ISSPACE(*src))
+	++src;
+      if (*src != '>')
+	fail ("%s: %d: missing '>'\n", filename, lineno);
+
+      memcpy(dst, arg->val, strlen(arg->val));
+      dst += strlen(arg->val);
+      ++src;
+    }
+
+  *dst = '\0';
+}
+
 static unsigned int
 expand_templates (char *name, const char *str, htab_t opcode_hash_table,
 		  struct opcode_hash_entry ***opcode_array_p, int lineno)
@@ -1746,71 +1809,20 @@ expand_templates (char *name, const char
 
       for (inst = tmpl->instances; inst; inst = inst->next)
 	{
-	  char *name2 = xmalloc(strlen(name) + strlen(inst->name) + strlen(ptr2) + 1);
+	  char *name2 = xmalloc(strlen(name) + strlen(inst->name)
+			+ 2 * strlen(ptr2) + 1);
 	  char *str2 = xmalloc(2 * strlen(str));
-	  const char *src;
 
-	  strcpy (name2, name);
-	  strcat (name2, inst->name);
-	  strcat (name2, ptr2);
+	  ptr1 = stpcpy (name2, name);
+	  ptr1 = stpcpy (ptr1, inst->name);
 
-	  for (ptr1 = str2, src = str; *src; )
-	    {
-	      const char *ident = tmpl->name, *end;
-	      const struct template_param *param;
-	      const struct template_arg *arg;
-
-	      if ((*ptr1 = *src++) != '<')
-		{
-		  ++ptr1;
-		  continue;
-		}
-	      while (ISSPACE(*src))
-		++src;
-	      while (*ident && *src == *ident)
-		++src, ++ident;
-	      while (ISSPACE(*src))
-		++src;
-	      if (*src != ':' || *ident != '\0')
-		{
-		  memcpy (++ptr1, tmpl->name, ident - tmpl->name);
-		  ptr1 += ident - tmpl->name;
-		  continue;
-		}
-	      while (ISSPACE(*++src))
-		;
-
-	      end = src;
-	      while (*end != '\0' && !ISSPACE(*end) && *end != '>')
-		++end;
-
-	      for (param = tmpl->params, arg = inst->args; param;
-		   param = param->next, arg = arg->next)
-		{
-		  if (end - src == strlen (param->name)
-		      && !memcmp (src, param->name, end - src))
-		    {
-		      src = end;
-		      break;
-		    }
-		}
-
-	      if (param == NULL)
-		fail ("template '%s' has no parameter '%.*s'\n",
-		      tmpl->name, (int)(end - src), src);
-
-	      while (ISSPACE(*src))
-		++src;
-	      if (*src != '>')
-		fail ("%s: %d: missing '>'\n", filename, lineno);
-
-	      memcpy(ptr1, arg->val, strlen(arg->val));
-	      ptr1 += strlen(arg->val);
-	      ++src;
-	    }
+	  /* Expand this template in trailing portion of mnemonic.  */
+	  expand_template (tmpl, inst, ptr1, ptr2, lineno);
 
-	  *ptr1 = '\0';
+	  /* Expand this template in attributes and operands.  */
+	  expand_template (tmpl, inst, str2, str, lineno);
 
+	  /* Expand further templates, if any. */
 	  expand_templates (name2, str2, opcode_hash_table, opcode_array_p,
 			    lineno);
 
