/* Copyright (C) 2007-2024 Free Software Foundation, Inc.
This file is part of the GNU opcodes library.
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
It is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
MA 02110-1301, USA. */
/* Build-time checks are preferrable over runtime ones. Use this construct
in preference where possible. */
#define static_assert(e) ((void)sizeof (struct { int _:1 - 2 * !(e); }))
static const struct {
const char *name;
enum operand_class value;
} operand_classes[] = {
CLASS (Reg),
CLASS (SReg),
CLASS (RegCR),
CLASS (RegDR),
CLASS (RegTR),
CLASS (RegMMX),
CLASS (RegSIMD),
CLASS (RegMask),
CLASS (RegBND),
};
static void
process_copyright (FILE *fp)
{
fprintf (fp, "/* This file is automatically generated by i386-gen. Do not edit! */\n\
/* Copyright (C) 2007-2024 Free Software Foundation, Inc.\n\
\n\
This file is part of the GNU opcodes library.\n\
\n\
This library is free software; you can redistribute it and/or modify\n\
it under the terms of the GNU General Public License as published by\n\
the Free Software Foundation; either version 3, or (at your option)\n\
any later version.\n\
\n\
It is distributed in the hope that it will be useful, but WITHOUT\n\
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n\
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public\n\
License for more details.\n\
\n\
You should have received a copy of the GNU General Public License\n\
along with this program; if not, write to the Free Software\n\
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,\n\
MA 02110-1301, USA. */\n");
}
/* Need to find base entry for references to auxiliary ones. */
if (strchr (f, ':'))
{
str = xstrdup (f);
*strchr (str, ':') = '\0';
isa = str;
}
/* isa_dependencies[] prefers "LM" over "64". */
else if (!strcmp (f, "LM"))
isa = "64";
for (i = 0; i < CpuMax; ++i)
if (strcasecmp (flags[i].name, isa) == 0)
{
flags[i].value = value;
if (reverse < ARRAY_SIZE (isa_reverse_deps[0])
/* Don't record the feature itself here. */
&& reverse != i
/* Don't record base architectures. */
&& reverse > Cpu686)
isa_reverse_deps[i][reverse] = 1;
is_isa = true;
if (i == CpuAVX || i == CpuXOP || i == CpuVAES || i == CpuVPCLMULQDQ)
is_avx = true;
break;
}
free (str);
/* Do not turn off dependencies. */
if (is_isa && !value)
{
is_avx = orig_is_avx;
return;
}
for (i = 0; i < ARRAY_SIZE (isa_dependencies); ++i)
if (strcasecmp (isa_dependencies[i].name, f) == 0)
{
char *deps = xstrdup (isa_dependencies[i].deps);
char *next = deps;
char *last = deps + strlen (deps);
for (; next && next < last; )
{
char *str = next_field (next, '|', &next, last);
/* ISA extensions with dependencies need CPU_ANY_*_FLAGS emitted,
unless the sole dependency is the "64-bit mode only" one. */
if (reverse < ARRAY_SIZE (isa_reverse_deps[0])
&& strcmp (isa_dependencies[i].deps, "64"))
isa_reverse_deps[reverse][reverse] = 1;
is_avx = orig_is_avx;
return;
}
if (!is_isa)
fail ("unknown bitfield: %s\n", f);
is_avx = orig_is_avx;
}
static void
output_cpu_flags (FILE *table, bitfield *flags, unsigned int size,
int mode, const char *comma, const char *indent, int lineno)
{
unsigned int i = 0, j = 0;
if (mode < 0)
memset (&active_cpu_flags, 0, sizeof(active_cpu_flags));
fprintf (table, "%s{ { ", indent);
if (mode <= 0)
{
for (j = ~0u; i < CpuAttrEnums; i++)
{
if (!flags[i].value)
continue;
if (j < ~0u)
fail ("%s: %d: invalid combination of CPU identifiers\n",
filename, lineno);
j = i;
if (mode)
active_cpu_flags.array[i / 32] |= 1U << (i % 32);
}
/* Write 0 to indicate "no associated flag". */
fprintf (table, "%u, ", j + 1);
j = 1;
}
for (; i < size - 1; i++, j++)
{
if (((j + 1) % 20) != 0)
fprintf (table, "%d, ", flags[i].value);
else
fprintf (table, "%d,", flags[i].value);
if (((j + 1) % 20) == 0)
{
/* We need \\ for macro. */
if (mode > 0)
fprintf (table, " \\\n %s", indent);
else
fprintf (table, "\n %s", indent);
}
if (mode < 0 && flags[i].value)
active_cpu_flags.array[i / 32] |= 1U << (i % 32);
}
static void
process_i386_cpu_flag (FILE *table, char *flag,
const char *name,
const char *comma, const char *indent,
int lineno, unsigned int reverse)
{
char *str, *next = flag, *last;
unsigned int i;
int value = 1;
bool is_isa = false;
bitfield all [ARRAY_SIZE (cpu_flags)];
bitfield any [ARRAY_SIZE (cpu_flags)];
/* Copy the default cpu flags. */
memcpy (all, cpu_flags, sizeof (cpu_flags));
memcpy (any, cpu_flags, sizeof (cpu_flags));
if (flag == NULL)
{
for (i = 0; i < ARRAY_SIZE (isa_reverse_deps[0]); ++i)
any[i].value = isa_reverse_deps[reverse][i];
goto output;
}
if (flag[0] == '~')
{
last = flag + strlen (flag);
if (flag[1] == '(')
{
last -= 1;
next = flag + 2;
if (*last != ')')
fail ("%s: %d: missing `)' in bitfield: %s\n", filename,
lineno, flag);
*last = '\0';
}
else
next = flag + 1;
/* First we turn on everything except for cpuno64 and - if
present - the padding field. */
for (i = 0; i < ARRAY_SIZE (any); i++)
if (any[i].position < CpuNo64)
any[i].value = 1;
/* Turn off selective bits. */
value = 0;
}
if (name != NULL && value != 0)
{
for (i = 0; i < ARRAY_SIZE (any); i++)
if (strcasecmp (any[i].name, name) == 0)
{
add_isa_dependencies (any, name, 1, reverse);
is_isa = true;
break;
}
}
if (strcmp (flag, "0"))
{
bool combined = false;
if (is_isa)
return;
/* Turn on/off selective bits. */
last = flag + strlen (flag);
if (name == NULL && strchr (flag, '&'))
{
for (; next < last && *next != '('; )
{
str = next_field (next, '&', &next, last);
set_bitfield (str, all, value, ARRAY_SIZE (all), lineno);
}
if (*next == '(')
{
if (*--last != ')')
fail ("%s: %d: missing `)' in bitfield: %s\n", filename,
lineno, flag);
++next;
*last = '\0';
}
combined = true;
}
for (; next && next < last; )
{
str = next_field (next, '|', &next, last);
if (name)
add_isa_dependencies (any, str, value, reverse);
else if (combined || next < last)
set_bitfield (str, any, value, ARRAY_SIZE (any), lineno);
else /* Singular specifiers go into "all". */
set_bitfield (str, all, value, ARRAY_SIZE (all), lineno);
combined = true;
}
}
output:
if (name != NULL)
{
size_t len = strlen (name);
char *upper = xmalloc (len + 1);
/* Cpu64 is special: It specifies a mode dependency, not an ISA one. Zap
the flag from ISA initializer macros (and from CPU_ANY_64_FLAGS
itself we only care about tracking its dependents. Also don't emit the
(otherwise all zero) CPU_64_FLAGS. */
if (flag != NULL && reverse == Cpu64)
return;
if (is_isa || flag == NULL)
any[Cpu64].value = 0;
for (i = 0; i < len; ++i)
{
/* Don't emit #define-s for auxiliary entries. */
if (name[i] == ':')
return;
upper[i] = TOUPPER (name[i]);
}
upper[i] = '\0';
fprintf (table, "\n#define CPU_%s%s_FLAGS \\\n",
flag != NULL ? "": "ANY_", upper);
free (upper);
}
else
{
/* Synthesize "64-bit mode only" dependencies from the dependencies we
have accumulated. */
for (i = 0; i < ARRAY_SIZE (isa_reverse_deps[0]); ++i)
if (all[i].value && isa_reverse_deps[Cpu64][i])
all[Cpu64].value = 1;
output_cpu_flags(table, all, ARRAY_SIZE (all), -1, comma, indent, lineno);
}
static void
output_opcode_modifier (FILE *table, bitfield *modifier, unsigned int size)
{
unsigned int i;
fprintf (table, " { ");
for (i = 0; i < size - 1; i++)
{
if (((i + 1) % 20) != 0)
fprintf (table, "%d, ", modifier[i].value);
else
fprintf (table, "%d,", modifier[i].value);
if (((i + 1) % 20) == 0)
fprintf (table, "\n ");
}
fprintf (table, "%d },\n", modifier[i].value);
}
/* Returns LOG2 of element size. */
static int
get_element_size (char **opnd, int lineno)
{
char *str, *next, *last, *op;
const char *full = opnd[0];
int elem_size = INT_MAX;
/* Find the memory operand. */
while (full != NULL && strstr(full, "BaseIndex") == NULL)
full = *++opnd;
if (full == NULL)
fail ("%s: %d: no memory operand\n", filename, lineno);
op = xstrdup (full);
last = op + strlen (op);
for (next = op; next && next < last; )
{
str = next_field (next, '|', &next, last);
if (str)
{
if (strcasecmp(str, "Byte") == 0)
{
/* The smallest element size, no need to check
further. */
elem_size = 0;
break;
}
else if (strcasecmp(str, "Word") == 0)
{
if (elem_size > 1)
elem_size = 1;
}
else if (strcasecmp(str, "Dword") == 0)
{
if (elem_size > 2)
elem_size = 2;
}
else if (strcasecmp(str, "Qword") == 0)
{
if (elem_size > 3)
elem_size = 3;
}
}
}
free (op);
if (elem_size == INT_MAX)
fail ("%s: %d: unknown element size: %s\n", filename, lineno, full);
return elem_size;
}
static bool
rex2_disallowed (const unsigned long long opcode, unsigned int length,
unsigned int space, const char *cpu_flags)
{
/* Some opcodes encode a ModR/M-like byte directly in the opcode. */
unsigned int base_opcode = opcode >> (8 * length - 8);
/* All opcodes listed map0 0x4*, 0x7*, 0xa*, 0xe* and map1 0x3*, 0x8*
are reserved under REX2 and triggers #UD when prefixed with REX2 */
if (space == 0)
switch (base_opcode >> 4)
{
case 0x4:
case 0x7:
case 0xA:
case 0xE:
return true;
default:
return false;
}
if (space == SPACE_0F)
switch (base_opcode >> 4)
{
case 0x3:
case 0x8:
return true;
default:
return false;
}
/* Rather than evaluating multiple conditions at runtime to determine
whether an EVEX encoding is being dealt with, derive that information
right here. A missing EVex attribute means "dynamic". */
if (!modifiers[EVex].value
&& (modifiers[Disp8MemShift].value
|| modifiers[Broadcast].value
|| modifiers[Masking].value
|| modifiers[SAE].value))
modifiers[EVex].value = EVEXDYN;
/* Vex, legacy map2 and map3 and rex2_disallowed do not support EGPR.
For templates supporting both Vex and EVex allowing EGPR. */
if ((modifiers[Vex].value || space > SPACE_0F || rex2_disallowed)
&& !modifiers[EVex].value)
modifiers[NoEgpr].value = 1;
last = op + strlen (op);
for (next = op; next && next < last; )
{
str = next_field (next, '|', &next, last);
if (str)
{
unsigned int i;
if (!strncmp(str, "Class=", 6))
{
for (i = 0; i < ARRAY_SIZE(operand_classes); ++i)
if (!strcmp(str + 6, operand_classes[i].name))
{
class = operand_classes[i].value;
str = NULL;
break;
}
}
if (str && !strncmp(str, "Instance=", 9))
{
for (i = 0; i < ARRAY_SIZE(operand_instances); ++i)
if (!strcmp(str + 9, operand_instances[i].name))
{
instance = operand_instances[i].value;
str = NULL;
break;
}
}
}
if (str)
{
set_bitfield (str, types, 1, ARRAY_SIZE (types), lineno);
if (strcasecmp(str, "BaseIndex") == 0)
baseindex = 1;
}
}
for (i = 0; i < ARRAY_SIZE (operand_types); i++)
{
if (!operand_types[i])
{
if (i == 0)
process_i386_operand_type (table, "0", stage_opcodes, "\t ",
lineno);
break;
}
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;
}
/* Look for line continuation character. */
remove_trailing_whitespaces (p);
j = strlen (buf);
if (!j || buf[j - 1] != '+')
break;
if (j >= sizeof (buf) - 1)
fail ("%s: %d: (continued) line too long\n", filename, lineno);
if (fgets (buf + j - 1, sizeof (buf) - j + 1, fp) == NULL)
{
fprintf (stderr, "%s: Line continuation on last line?\n",
filename);
break;
}
}
switch (p[0])
{
case '#':
if (!strcmp("### MARKER ###", buf))
marker = 1;
else
{
/* Since we ignore all included files (we only care about their
#define-s here), we don't need to monitor filenames. The final
line number directive is going to refer to the main source file
again. */
char *end;
unsigned long ln;
p = remove_leading_whitespaces (p + 1);
if (!strncmp(p, "line", 4))
p += 4;
ln = strtoul (p, &end, 10);
if (ln > 1 && ln < INT_MAX
&& *remove_leading_whitespaces (end) == '"')
lineno = ln - 1;
}
/* Ignore comments. */
case '\0':
continue;
break;
case '<':
parse_template (p, lineno);
continue;
default:
if (!marker)
continue;
break;
}
/* Early x87 is somewhat special: Both 287 and 387 not only add new insns
but also remove some. Hence 8087 isn't a prereq to 287, and 287 isn't
one to 387. We want the reverse to be true though: Disabling 8087 also
is to disable 287+ and later; disabling 287 also means disabling 387+. */
memcpy (isa_reverse_deps[Cpu287], isa_reverse_deps[Cpu387],
sizeof (isa_reverse_deps[0]));
isa_reverse_deps[Cpu287][Cpu387] = 1;
memcpy (isa_reverse_deps[Cpu8087], isa_reverse_deps[Cpu287],
sizeof (isa_reverse_deps[0]));
isa_reverse_deps[Cpu8087][Cpu287] = 1;
/* While we treat POPCNT as a prereq to SSE4.2, its disabling should not
lead to disabling of anything else. */
memset (isa_reverse_deps[CpuPOPCNT], 0, sizeof (isa_reverse_deps[0]));
for (i = Cpu686 + 1; i < ARRAY_SIZE (isa_reverse_deps); i++)
{
size_t len;
char *upper;
if (memchr(isa_reverse_deps[i], 1,
ARRAY_SIZE (isa_reverse_deps[0])) == NULL)
continue;