]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - opcodes/i386-gen.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / opcodes / i386-gen.c
index 09fc5f8b827a0ed041e15c37e945453bc64f62f1..b3c87dbbd662ef29dfb1bdca1456a26f09c944bb 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2007-2019 Free Software Foundation, Inc.
+/* Copyright (C) 2007-2021 Free Software Foundation, Inc.
 
    This file is part of the GNU opcodes library.
 
@@ -90,9 +90,9 @@ static initializer cpu_flag_init[] =
   { "CPU_K8_FLAGS",
     "CPU_ATHLON_FLAGS|CpuRdtscp|CPU_SSE2_FLAGS|CpuLM" },
   { "CPU_AMDFAM10_FLAGS",
-    "CPU_K8_FLAGS|CpuFISTTP|CPU_SSE4A_FLAGS|CpuABM" },
+    "CPU_K8_FLAGS|CpuFISTTP|CPU_SSE4A_FLAGS|CpuLZCNT|CpuPOPCNT" },
   { "CPU_BDVER1_FLAGS",
-    "CPU_GENERIC64_FLAGS|CpuFISTTP|CpuRdtscp|CpuCX16|CPU_XOP_FLAGS|CpuABM|CpuLWP|CpuSVME|CpuAES|CpuPCLMUL|CpuLZCNT|CpuPRFCHW" },
+    "CPU_GENERIC64_FLAGS|CpuFISTTP|CpuRdtscp|CpuCX16|CPU_XOP_FLAGS|CpuLZCNT|CpuPOPCNT|CpuLWP|CpuSVME|CpuAES|CpuPCLMUL|CpuPRFCHW" },
   { "CPU_BDVER2_FLAGS",
     "CPU_BDVER1_FLAGS|CpuFMA|CpuBMI|CpuTBM|CpuF16C" },
   { "CPU_BDVER3_FLAGS",
@@ -100,11 +100,13 @@ static initializer cpu_flag_init[] =
   { "CPU_BDVER4_FLAGS",
     "CPU_BDVER3_FLAGS|CpuAVX2|CpuMovbe|CpuBMI2|CpuRdRnd|CpuMWAITX" },
   { "CPU_ZNVER1_FLAGS",
-    "CPU_GENERIC64_FLAGS|CpuFISTTP|CpuRdtscp|CpuCX16|CPU_AVX2_FLAGS|CpuSSE4A|CpuABM|CpuSVME|CpuAES|CpuPCLMUL|CpuLZCNT|CpuPRFCHW|CpuFMA|CpuBMI|CpuF16C|CpuXsaveopt|CpuFSGSBase|CpuMovbe|CpuBMI2|CpuRdRnd|CpuADX|CpuRdSeed|CpuSMAP|CpuSHA|CpuXSAVEC|CpuXSAVES|CpuClflushOpt|CpuCLZERO|CpuMWAITX" },
+    "CPU_GENERIC64_FLAGS|CpuFISTTP|CpuRdtscp|CpuCX16|CPU_AVX2_FLAGS|CpuSSE4A|CpuLZCNT|CpuPOPCNT|CpuSVME|CpuAES|CpuPCLMUL|CpuPRFCHW|CpuFMA|CpuBMI|CpuF16C|CpuXsaveopt|CpuFSGSBase|CpuMovbe|CpuBMI2|CpuRdRnd|CpuADX|CpuRdSeed|CpuSMAP|CpuSHA|CpuXSAVEC|CpuXSAVES|CpuClflushOpt|CpuCLZERO|CpuMWAITX" },
   { "CPU_ZNVER2_FLAGS",
     "CPU_ZNVER1_FLAGS|CpuCLWB|CpuRDPID|CpuRDPRU|CpuMCOMMIT|CpuWBNOINVD" },
+  { "CPU_ZNVER3_FLAGS",
+    "CPU_ZNVER2_FLAGS|CpuINVLPGB|CpuTLBSYNC|CpuVAES|CpuVPCLMULQDQ|CpuINVPCID|CpuSNP|CpuOSPKE" },
   { "CPU_BTVER1_FLAGS",
-    "CPU_GENERIC64_FLAGS|CpuFISTTP|CpuCX16|CpuRdtscp|CPU_SSSE3_FLAGS|CpuSSE4A|CpuABM|CpuPRFCHW|CpuCX16|CpuClflush|CpuFISTTP|CpuSVME|CpuLZCNT" },
+    "CPU_GENERIC64_FLAGS|CpuFISTTP|CpuCX16|CpuRdtscp|CPU_SSSE3_FLAGS|CpuSSE4A|CpuLZCNT|CpuPOPCNT|CpuPRFCHW|CpuCX16|CpuClflush|CpuFISTTP|CpuSVME" },
   { "CPU_BTVER2_FLAGS",
     "CPU_BTVER1_FLAGS|CPU_AVX_FLAGS|CpuBMI|CpuF16C|CpuAES|CpuPCLMUL|CpuMovbe|CpuXsaveopt|CpuPRFCHW" },
   { "CPU_8087_FLAGS",
@@ -138,7 +140,7 @@ static initializer cpu_flag_init[] =
   { "CPU_SSE4_1_FLAGS",
     "CPU_SSSE3_FLAGS|CpuSSE4_1" },
   { "CPU_SSE4_2_FLAGS",
-    "CPU_SSE4_1_FLAGS|CpuSSE4_2" },
+    "CPU_SSE4_1_FLAGS|CpuSSE4_2|CpuPOPCNT" },
   { "CPU_VMX_FLAGS",
     "CpuVMX" },
   { "CPU_SMX_FLAGS",
@@ -181,6 +183,8 @@ static initializer cpu_flag_init[] =
     "CpuBMI2" },
   { "CPU_LZCNT_FLAGS",
     "CpuLZCNT" },
+  { "CPU_POPCNT_FLAGS",
+    "CpuPOPCNT" },
   { "CPU_HLE_FLAGS",
     "CpuHLE" },
   { "CPU_RTM_FLAGS",
@@ -200,11 +204,13 @@ static initializer cpu_flag_init[] =
   { "CPU_SSE4A_FLAGS",
     "CPU_SSE3_FLAGS|CpuSSE4a" },
   { "CPU_ABM_FLAGS",
-    "CpuABM" },
+    "CpuLZCNT|CpuPOPCNT" },
   { "CPU_AVX_FLAGS",
     "CPU_SSE4_2_FLAGS|CPU_XSAVE_FLAGS|CpuAVX" },
   { "CPU_AVX2_FLAGS",
     "CPU_AVX_FLAGS|CpuAVX2" },
+  { "CPU_AVX_VNNI_FLAGS",
+    "CPU_AVX2_FLAGS|CpuAVX_VNNI" },
   { "CPU_AVX512F_FLAGS",
     "CPU_AVX2_FLAGS|CpuAVX512F" },
   { "CPU_AVX512CD_FLAGS",
@@ -293,20 +299,48 @@ static initializer cpu_flag_init[] =
     "CpuPCONFIG" },
   { "CPU_WAITPKG_FLAGS",
     "CpuWAITPKG" },
+  { "CPU_UINTR_FLAGS",
+    "CpuUINTR" },
   { "CPU_CLDEMOTE_FLAGS",
     "CpuCLDEMOTE" },
+  { "CPU_AMX_INT8_FLAGS",
+    "CpuAMX_INT8" },
+  { "CPU_AMX_BF16_FLAGS",
+    "CpuAMX_BF16" },
+  { "CPU_AMX_TILE_FLAGS",
+    "CpuAMX_TILE" },
   { "CPU_MOVDIRI_FLAGS",
     "CpuMOVDIRI" },
   { "CPU_MOVDIR64B_FLAGS",
     "CpuMOVDIR64B" },
   { "CPU_ENQCMD_FLAGS",
     "CpuENQCMD" },
+  { "CPU_SERIALIZE_FLAGS",
+    "CpuSERIALIZE" },
   { "CPU_AVX512_VP2INTERSECT_FLAGS",
     "CpuAVX512_VP2INTERSECT" },
+  { "CPU_TDX_FLAGS",
+    "CpuTDX" },
   { "CPU_RDPRU_FLAGS",
     "CpuRDPRU" },
   { "CPU_MCOMMIT_FLAGS",
     "CpuMCOMMIT" },
+  { "CPU_SEV_ES_FLAGS",
+    "CpuSEV_ES" },
+  { "CPU_TSXLDTRK_FLAGS",
+    "CpuTSXLDTRK"},
+  { "CPU_KL_FLAGS",
+    "CpuKL" },
+  { "CPU_WIDEKL_FLAGS",
+    "CpuWideKL" },
+  { "CPU_HRESET_FLAGS",
+    "CpuHRESET"},
+  { "CPU_INVLPGB_FLAGS",
+    "CpuINVLPGB" },
+  { "CPU_TLBSYNC_FLAGS",
+    "CpuTLBSYNC" },
+  { "CPU_SNP_FLAGS",
+    "CpuSNP" },
   { "CPU_ANY_X87_FLAGS",
     "CPU_ANY_287_FLAGS|Cpu8087" },
   { "CPU_ANY_287_FLAGS",
@@ -322,17 +356,19 @@ static initializer cpu_flag_init[] =
   { "CPU_ANY_MMX_FLAGS",
     "CPU_3DNOWA_FLAGS" },
   { "CPU_ANY_SSE_FLAGS",
-    "CPU_ANY_SSE2_FLAGS|CpuSSE|CpuSSE4a" },
+    "CPU_ANY_SSE2_FLAGS|CpuSSE" },
   { "CPU_ANY_SSE2_FLAGS",
     "CPU_ANY_SSE3_FLAGS|CpuSSE2" },
   { "CPU_ANY_SSE3_FLAGS",
-    "CPU_ANY_SSSE3_FLAGS|CpuSSE3" },
+    "CPU_ANY_SSSE3_FLAGS|CpuSSE3|CpuSSE4a" },
   { "CPU_ANY_SSSE3_FLAGS",
     "CPU_ANY_SSE4_1_FLAGS|CpuSSSE3" },
   { "CPU_ANY_SSE4_1_FLAGS",
     "CPU_ANY_SSE4_2_FLAGS|CpuSSE4_1" },
   { "CPU_ANY_SSE4_2_FLAGS",
     "CpuSSE4_2" },
+  { "CPU_ANY_SSE4A_FLAGS",
+    "CpuSSE4a" },
   { "CPU_ANY_AVX_FLAGS",
     "CPU_ANY_AVX2_FLAGS|CpuF16C|CpuFMA|CpuFMA4|CpuXOP|CpuAVX" },
   { "CPU_ANY_AVX2_FLAGS",
@@ -373,14 +409,36 @@ static initializer cpu_flag_init[] =
     "CpuAVX512_BITALG" },
   { "CPU_ANY_AVX512_BF16_FLAGS",
     "CpuAVX512_BF16" },
+  { "CPU_ANY_AMX_INT8_FLAGS",
+    "CpuAMX_INT8" },
+  { "CPU_ANY_AMX_BF16_FLAGS",
+    "CpuAMX_BF16" },
+  { "CPU_ANY_AMX_TILE_FLAGS",
+    "CpuAMX_TILE|CpuAMX_INT8|CpuAMX_BF16" },
+  { "CPU_ANY_AVX_VNNI_FLAGS",
+    "CpuAVX_VNNI" },
   { "CPU_ANY_MOVDIRI_FLAGS",
     "CpuMOVDIRI" },
+  { "CPU_ANY_UINTR_FLAGS",
+    "CpuUINTR" },
   { "CPU_ANY_MOVDIR64B_FLAGS",
     "CpuMOVDIR64B" },
   { "CPU_ANY_ENQCMD_FLAGS",
     "CpuENQCMD" },
+  { "CPU_ANY_SERIALIZE_FLAGS",
+    "CpuSERIALIZE" },
   { "CPU_ANY_AVX512_VP2INTERSECT_FLAGS",
     "CpuAVX512_VP2INTERSECT" },
+  { "CPU_ANY_TDX_FLAGS",
+    "CpuTDX" },
+  { "CPU_ANY_TSXLDTRK_FLAGS",
+    "CpuTSXLDTRK" },
+  { "CPU_ANY_KL_FLAGS",
+    "CpuKL|CpuWideKL" },
+  { "CPU_ANY_WIDEKL_FLAGS",
+    "CpuWideKL" },
+  { "CPU_ANY_HRESET_FLAGS",
+    "CpuHRESET" },
 };
 
 static initializer operand_type_init[] =
@@ -445,6 +503,8 @@ static initializer operand_type_init[] =
     "Class=RegSIMD|Ymmword" },
   { "OPERAND_TYPE_REGZMM",
     "Class=RegSIMD|Zmmword" },
+  { "OPERAND_TYPE_REGTMM",
+    "Class=RegSIMD|Tmmword" },
   { "OPERAND_TYPE_REGMASK",
     "Class=RegMask" },
   { "OPERAND_TYPE_REGBND",
@@ -534,7 +594,6 @@ static bitfield cpu_flags[] =
   BITFIELD (CpuSVME),
   BITFIELD (CpuVMX),
   BITFIELD (CpuSMX),
-  BITFIELD (CpuABM),
   BITFIELD (CpuXsave),
   BITFIELD (CpuXsaveopt),
   BITFIELD (CpuAES),
@@ -555,6 +614,7 @@ static bitfield cpu_flags[] =
   BITFIELD (CpuF16C),
   BITFIELD (CpuBMI2),
   BITFIELD (CpuLZCNT),
+  BITFIELD (CpuPOPCNT),
   BITFIELD (CpuHLE),
   BITFIELD (CpuRTM),
   BITFIELD (CpuINVPCID),
@@ -583,6 +643,8 @@ static bitfield cpu_flags[] =
   BITFIELD (CpuAVX512_BITALG),
   BITFIELD (CpuAVX512_BF16),
   BITFIELD (CpuAVX512_VP2INTERSECT),
+  BITFIELD (CpuTDX),
+  BITFIELD (CpuAVX_VNNI),
   BITFIELD (CpuMWAITX),
   BITFIELD (CpuCLZERO),
   BITFIELD (CpuOSPKE),
@@ -596,12 +658,25 @@ static bitfield cpu_flags[] =
   BITFIELD (CpuWBNOINVD),
   BITFIELD (CpuPCONFIG),
   BITFIELD (CpuWAITPKG),
+  BITFIELD (CpuUINTR),
   BITFIELD (CpuCLDEMOTE),
+  BITFIELD (CpuAMX_INT8),
+  BITFIELD (CpuAMX_BF16),
+  BITFIELD (CpuAMX_TILE),
   BITFIELD (CpuMOVDIRI),
   BITFIELD (CpuMOVDIR64B),
   BITFIELD (CpuENQCMD),
+  BITFIELD (CpuSERIALIZE),
   BITFIELD (CpuRDPRU),
   BITFIELD (CpuMCOMMIT),
+  BITFIELD (CpuSEV_ES),
+  BITFIELD (CpuTSXLDTRK),
+  BITFIELD (CpuKL),
+  BITFIELD (CpuWideKL),
+  BITFIELD (CpuHRESET),
+  BITFIELD (CpuINVLPGB),
+  BITFIELD (CpuTLBSYNC),
+  BITFIELD (CpuSNP),
 #ifdef CpuUnused
   BITFIELD (CpuUnused),
 #endif
@@ -613,14 +688,12 @@ static bitfield opcode_modifiers[] =
   BITFIELD (W),
   BITFIELD (Load),
   BITFIELD (Modrm),
-  BITFIELD (ShortForm),
   BITFIELD (Jump),
   BITFIELD (FloatMF),
   BITFIELD (FloatR),
   BITFIELD (Size),
   BITFIELD (CheckRegSize),
-  BITFIELD (IgnoreSize),
-  BITFIELD (DefaultSize),
+  BITFIELD (MnemonicSize),
   BITFIELD (Anysize),
   BITFIELD (No_bSuf),
   BITFIELD (No_wSuf),
@@ -644,14 +717,14 @@ static bitfield opcode_modifiers[] =
   BITFIELD (IsPrefix),
   BITFIELD (ImmExt),
   BITFIELD (NoRex64),
-  BITFIELD (Rex64),
   BITFIELD (Ugh),
+  BITFIELD (PseudoVexPrefix),
   BITFIELD (Vex),
   BITFIELD (VexVVVV),
   BITFIELD (VexW),
-  BITFIELD (VexOpcode),
+  BITFIELD (OpcodePrefix),
   BITFIELD (VexSources),
-  BITFIELD (VecSIB),
+  BITFIELD (SIB),
   BITFIELD (SSE2AVX),
   BITFIELD (NoAVX),
   BITFIELD (EVex),
@@ -662,12 +735,12 @@ static bitfield opcode_modifiers[] =
   BITFIELD (Disp8MemShift),
   BITFIELD (NoDefMask),
   BITFIELD (ImplicitQuadGroup),
+  BITFIELD (SwapSources),
   BITFIELD (Optimize),
   BITFIELD (ATTMnemonic),
   BITFIELD (ATTSyntax),
   BITFIELD (IntelSyntax),
-  BITFIELD (AMD64),
-  BITFIELD (Intel64),
+  BITFIELD (ISA64),
 };
 
 #define CLASS(n) #n, n
@@ -727,6 +800,7 @@ static bitfield operand_types[] =
   BITFIELD (Xmmword),
   BITFIELD (Ymmword),
   BITFIELD (Zmmword),
+  BITFIELD (Tmmword),
   BITFIELD (Unspecified),
 #ifdef OTUnused
   BITFIELD (OTUnused),
@@ -737,6 +811,31 @@ static const char *filename;
 static i386_cpu_flags active_cpu_flags;
 static int active_isstring;
 
+struct template_arg {
+  const struct template_arg *next;
+  const char *val;
+};
+
+struct template_instance {
+  const struct template_instance *next;
+  const char *name;
+  const struct template_arg *args;
+};
+
+struct template_param {
+  const struct template_param *next;
+  const char *name;
+};
+
+struct template {
+  const struct template *next;
+  const char *name;
+  const struct template_instance *instances;
+  const struct template_param *params;
+};
+
+static const struct template *templates;
+
 static int
 compare (const void *x, const void *y)
 {
@@ -761,7 +860,7 @@ static void
 process_copyright (FILE *fp)
 {
   fprintf (fp, "/* This file is automatically generated by i386-gen.  Do not edit!  */\n\
-/* Copyright (C) 2007-2019 Free Software Foundation, Inc.\n\
+/* Copyright (C) 2007-2021 Free Software Foundation, Inc.\n\
 \n\
    This file is part of the GNU opcodes library.\n\
 \n\
@@ -868,6 +967,10 @@ set_bitfield (char *f, bitfield *array, int value,
 {
   unsigned int i;
 
+  /* Ignore empty fields; they may result from template expansions.  */
+  if (*f == '\0')
+    return;
+
   if (strcmp (f, "CpuFP") == 0)
     {
       set_bitfield("Cpu387", array, value, size, lineno);
@@ -1085,11 +1188,12 @@ adjust_broadcast_modifier (char **opnd)
   return bcst_type;
 }
 
-static void
+static int
 process_i386_opcode_modifier (FILE *table, char *mod, char **opnd, int lineno)
 {
   char *str, *next, *last;
   bitfield modifiers [ARRAY_SIZE (opcode_modifiers)];
+  unsigned int regular_encoding = 1;
 
   active_isstring = 0;
 
@@ -1108,9 +1212,22 @@ process_i386_opcode_modifier (FILE *table, char *mod, char **opnd, int lineno)
            {
              int val = 1;
              if (strcasecmp(str, "Broadcast") == 0)
+               {
                  val = adjust_broadcast_modifier (opnd);
+                 regular_encoding = 0;
+               }
+             else if (strcasecmp(str, "Vex") == 0
+                      || strncasecmp(str, "Vex=", 4) == 0
+                      || strcasecmp(str, "EVex") == 0
+                      || strncasecmp(str, "EVex=", 5) == 0
+                      || strncasecmp(str, "Disp8MemShift=", 14) == 0
+                      || strncasecmp(str, "Masking=", 8) == 0
+                      || strcasecmp(str, "SAE") == 0
+                      || strcasecmp(str, "IsPrefix") == 0)
+               regular_encoding = 0;
+
              set_bitfield (str, modifiers, val, ARRAY_SIZE (modifiers),
-                         lineno);
+                           lineno);
              if (strcasecmp(str, "IsString") == 0)
                active_isstring = 1;
 
@@ -1139,6 +1256,8 @@ process_i386_opcode_modifier (FILE *table, char *mod, char **opnd, int lineno)
                 filename, lineno);
     }
   output_opcode_modifier (table, modifiers, ARRAY_SIZE (modifiers));
+
+  return regular_encoding;
 }
 
 enum stage {
@@ -1320,7 +1439,42 @@ output_i386_opcode (FILE *table, const char *name, char *str,
 
   process_i386_cpu_flag (table, cpu_flags, 0, ",", "    ", lineno);
 
-  process_i386_opcode_modifier (table, opcode_modifier, operand_types, lineno);
+  if (process_i386_opcode_modifier (table, opcode_modifier,
+                                   operand_types, lineno))
+    {
+      char *end;
+      unsigned long int length = strtoul (opcode_length, &end, 0);
+      unsigned long int opcode = strtoul (base_opcode, &end, 0);
+      switch (length)
+       {
+       case 4:
+         break;
+       case 3:
+         if ((opcode >> 24) != 0)
+           fail (_("%s: %s: (base_opcode >> 24) != 0: %s\n"),
+                 filename, name, base_opcode);
+         break;
+       case 2:
+         if ((opcode >> 16) != 0)
+           fail (_("%s: %s: (base_opcode >> 16) != 0: %s\n"),
+                 filename, name, base_opcode);
+         break;
+       case 1:
+         if ((opcode >> 8) != 0)
+           fail (_("%s: %s: (base_opcode >> 8) != 0: %s\n"),
+                 filename, name, base_opcode);
+         break;
+       case 0:
+         if (opcode != 0)
+           fail (_("%s: %s: base_opcode != 0: %s\n"),
+                 filename, name, base_opcode);
+         break;
+       default:
+         fail (_("%s: %s: invalid opcode length: %s\n"),
+               filename, name, opcode_length);
+         break;
+       }
+    }
 
   fprintf (table, "    { ");
 
@@ -1370,6 +1524,240 @@ opcode_hash_eq (const void *p, const void *q)
   return strcmp (name, entry->name) == 0;
 }
 
+static void
+parse_template (char *buf, int lineno)
+{
+  char sep, *end, *name;
+  struct template *tmpl = xmalloc (sizeof (*tmpl));
+  struct template_instance *last_inst = NULL;
+
+  buf = remove_leading_whitespaces (buf + 1);
+  end = strchr (buf, ':');
+  if (end == NULL)
+    fail ("%s: %d: missing ':'\n", filename, lineno);
+  *end++ = '\0';
+  remove_trailing_whitespaces (buf);
+
+  if (*buf == '\0')
+    fail ("%s: %d: missing template identifier\n", filename, lineno);
+  tmpl->name = xstrdup (buf);
+
+  tmpl->params = NULL;
+  do {
+      struct template_param *param;
+
+      buf = remove_leading_whitespaces (end);
+      end = strpbrk (buf, ":,");
+      if (end == NULL)
+        fail ("%s: %d: missing ':' or ','\n", filename, lineno);
+
+      sep = *end;
+      *end++ = '\0';
+      remove_trailing_whitespaces (buf);
+
+      param = xmalloc (sizeof (*param));
+      param->name = xstrdup (buf);
+      param->next = tmpl->params;
+      tmpl->params = param;
+  } while (sep == ':');
+
+  tmpl->instances = NULL;
+  do {
+      struct template_instance *inst;
+      char *cur, *next;
+      const struct template_param *param;
+
+      buf = remove_leading_whitespaces (end);
+      end = strpbrk (buf, ",>");
+      if (end == NULL)
+        fail ("%s: %d: missing ',' or '>'\n", filename, lineno);
+
+      sep = *end;
+      *end++ = '\0';
+
+      inst = xmalloc (sizeof (*inst));
+
+      cur = next_field (buf, ':', &next, end);
+      inst->name = xstrdup (cur);
+
+      for (param = tmpl->params; param; param = param->next)
+       {
+         struct template_arg *arg = xmalloc (sizeof (*arg));
+
+         cur = next_field (next, ':', &next, end);
+         if (next > end)
+           fail ("%s: %d: missing argument for '%s'\n", filename, lineno, param->name);
+         arg->val = xstrdup (cur);
+         arg->next = inst->args;
+         inst->args = arg;
+       }
+
+      if (tmpl->instances)
+       last_inst->next = inst;
+      else
+       tmpl->instances = inst;
+      last_inst = inst;
+  } while (sep == ',');
+
+  buf = remove_leading_whitespaces (end);
+  if (*buf)
+    fprintf(stderr, "%s: %d: excess characters '%s'\n",
+           filename, lineno, buf);
+
+  tmpl->next = templates;
+  templates = tmpl;
+}
+
+static unsigned int
+expand_templates (char *name, const char *str, htab_t opcode_hash_table,
+                 struct opcode_hash_entry ***opcode_array_p, int lineno)
+{
+  static unsigned int idx, opcode_array_size;
+  struct opcode_hash_entry **opcode_array = *opcode_array_p;
+  struct opcode_hash_entry **hash_slot, **entry;
+  char *ptr1 = strchr(name, '<'), *ptr2;
+
+  if (ptr1 == NULL)
+    {
+      /* Get the slot in hash table.  */
+      hash_slot = (struct opcode_hash_entry **)
+       htab_find_slot_with_hash (opcode_hash_table, name,
+                                 htab_hash_string (name),
+                                 INSERT);
+
+      if (*hash_slot == NULL)
+       {
+         /* It is the new one.  Put it on opcode array.  */
+         if (idx >= opcode_array_size)
+           {
+             /* Grow the opcode array when needed.  */
+             opcode_array_size += 1024;
+             opcode_array = (struct opcode_hash_entry **)
+               xrealloc (opcode_array,
+                         sizeof (*opcode_array) * opcode_array_size);
+               *opcode_array_p = opcode_array;
+           }
+
+         opcode_array[idx] = (struct opcode_hash_entry *)
+           xmalloc (sizeof (struct opcode_hash_entry));
+         opcode_array[idx]->next = NULL;
+         opcode_array[idx]->name = xstrdup (name);
+         opcode_array[idx]->opcode = xstrdup (str);
+         opcode_array[idx]->lineno = lineno;
+         *hash_slot = opcode_array[idx];
+         idx++;
+       }
+      else
+       {
+         /* Append it to the existing one.  */
+         entry = hash_slot;
+         while ((*entry) != NULL)
+           entry = &(*entry)->next;
+         *entry = (struct opcode_hash_entry *)
+           xmalloc (sizeof (struct opcode_hash_entry));
+         (*entry)->next = NULL;
+         (*entry)->name = (*hash_slot)->name;
+         (*entry)->opcode = xstrdup (str);
+         (*entry)->lineno = lineno;
+       }
+    }
+  else if ((ptr2 = strchr(ptr1 + 1, '>')) == NULL)
+    fail ("%s: %d: missing '>'\n", filename, lineno);
+  else
+    {
+      const struct template *tmpl;
+      const struct template_instance *inst;
+
+      *ptr1 = '\0';
+      ptr1 = remove_leading_whitespaces (ptr1 + 1);
+      remove_trailing_whitespaces (ptr1);
+
+      *ptr2++ = '\0';
+
+      for ( tmpl = templates; tmpl; tmpl = tmpl->next )
+       if (!strcmp(ptr1, tmpl->name))
+         break;
+      if (!tmpl)
+       fail ("reference to unknown template '%s'\n", ptr1);
+
+      for (inst = tmpl->instances; inst; inst = inst->next)
+       {
+         char *name2 = xmalloc(strlen(name) + strlen(inst->name) + strlen(ptr2) + 1);
+         char *str2 = xmalloc(2 * strlen(str));
+         const char *src;
+
+         strcpy (name2, name);
+         strcat (name2, inst->name);
+         strcat (name2, ptr2);
+
+         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;
+           }
+
+         *ptr1 = '\0';
+
+         expand_templates (name2, str2, opcode_hash_table, opcode_array_p,
+                           lineno);
+
+         free (str2);
+         free (name2);
+       }
+    }
+
+  return idx;
+}
+
 static void
 process_i386_opcodes (FILE *table)
 {
@@ -1377,19 +1765,14 @@ process_i386_opcodes (FILE *table)
   char buf[2048];
   unsigned int i, j;
   char *str, *p, *last, *name;
-  struct opcode_hash_entry **hash_slot, **entry, *next;
   htab_t opcode_hash_table;
-  struct opcode_hash_entry **opcode_array;
-  unsigned int opcode_array_size = 1024;
+  struct opcode_hash_entry **opcode_array = NULL;
   int lineno = 0, marker = 0;
 
   filename = "i386-opc.tbl";
   fp = stdin;
 
   i = 0;
-  opcode_array = (struct opcode_hash_entry **)
-    xmalloc (sizeof (*opcode_array) * opcode_array_size);
-
   opcode_hash_table = htab_create_alloc (16, opcode_hash_hash,
                                         opcode_hash_eq, NULL,
                                         xcalloc, free);
@@ -1441,6 +1824,9 @@ process_i386_opcodes (FILE *table)
        case '\0':
          continue;
          break;
+       case '<':
+         parse_template (p, lineno);
+         continue;
        default:
          if (!marker)
            continue;
@@ -1452,51 +1838,15 @@ process_i386_opcodes (FILE *table)
       /* Find name.  */
       name = next_field (p, ',', &str, last);
 
-      /* Get the slot in hash table.  */
-      hash_slot = (struct opcode_hash_entry **)
-       htab_find_slot_with_hash (opcode_hash_table, name,
-                                 htab_hash_string (name),
-                                 INSERT);
-
-      if (*hash_slot == NULL)
-       {
-         /* It is the new one.  Put it on opcode array.  */
-         if (i >= opcode_array_size)
-           {
-             /* Grow the opcode array when needed.  */
-             opcode_array_size += 1024;
-             opcode_array = (struct opcode_hash_entry **)
-               xrealloc (opcode_array,
-                         sizeof (*opcode_array) * opcode_array_size);
-           }
-
-         opcode_array[i] = (struct opcode_hash_entry *)
-           xmalloc (sizeof (struct opcode_hash_entry));
-         opcode_array[i]->next = NULL;
-         opcode_array[i]->name = xstrdup (name);
-         opcode_array[i]->opcode = xstrdup (str);
-         opcode_array[i]->lineno = lineno;
-         *hash_slot = opcode_array[i];
-         i++;
-       }
-      else
-       {
-         /* Append it to the existing one.  */
-         entry = hash_slot;
-         while ((*entry) != NULL)
-           entry = &(*entry)->next;
-         *entry = (struct opcode_hash_entry *)
-           xmalloc (sizeof (struct opcode_hash_entry));
-         (*entry)->next = NULL;
-         (*entry)->name = (*hash_slot)->name;
-         (*entry)->opcode = xstrdup (str);
-         (*entry)->lineno = lineno;
-       }
+      i = expand_templates (name, str, opcode_hash_table, &opcode_array,
+                           lineno);
     }
 
   /* Process opcode array.  */
   for (j = 0; j < i; j++)
     {
+      struct opcode_hash_entry *next;
+
       for (next = opcode_array[j]; next; next = next->next)
        {
          name = next->name;