x86: derive opcode length from opcode value

In the majority of cases we can easily determine the length from the
encoding, irrespective of whether a prefix is specified there as well.
We further don't even need to record the value in the table entries, as
it's easy enough to determine it (without any guesswork, unless an insn
with major opcode 00 appeared that requires a 2nd opcode byte to be
specified explicitly) when installing the chosen template for further
processing.

Should an encoding appear which
- has a major opcode byte of 66, F3, or F2,
- requires a 2nd opcode byte to be specified explicitly,
- doesn't have a mandatory prefix
we'd need to convert all templates presently encoding a mandatory prefix
this way to the Prefix_0X<nn> model to eliminate the respective guessing
i386-gen does.
This commit is contained in:
Jan Beulich 2021-03-24 08:33:33 +01:00
parent 3564871692
commit 9a182d0461
7 changed files with 7404 additions and 7369 deletions

View File

@ -1,3 +1,15 @@
2021-03-24 Jan Beulich <jbeulich@suse.com>
* config/tc-i386.c (struct _i386_insn): New field
opcode_length.
(md_begin): Drop assertion.
(install_template): New.
(build_vex_prefix): Call install_template.
(match_template): Likewise.
(process_operands): Use new opcode_length field.
(output_jump): Likewise.
(output_insn): Likewise. Adjust psedo prefix check.
2021-03-24 Jan Beulich <jbeulich@suse.com>
* config/tc-i386.c (md_begin): Add assertion.

View File

@ -314,6 +314,9 @@ struct _i386_insn
or qword, if given. */
char suffix;
/* OPCODE_LENGTH holds the number of base opcode bytes. */
unsigned char opcode_length;
/* OPERANDS gives the number of given operands. */
unsigned int operands;
@ -3065,8 +3068,6 @@ md_begin (void)
while (1)
{
gas_assert (optab->opcode_length == 4
|| !(optab->base_opcode >> (8 * optab->opcode_length)));
++optab;
if (optab->name == NULL
|| strcmp (optab->name, (optab - 1)->name) != 0)
@ -3584,6 +3585,22 @@ intel_float_operand (const char *mnemonic)
return 1;
}
static INLINE void
install_template (const insn_template *t)
{
unsigned int l;
i.tm = *t;
/* Note that for pseudo prefixes this produces a length of 1. But for them
the length isn't interesting at all. */
for (l = 1; l < 4; ++l)
if (!(t->base_opcode >> (8 * l)))
break;
i.opcode_length = l;
}
/* Build the VEX prefix. */
static void
@ -3636,7 +3653,7 @@ build_vex_prefix (const insn_template *t)
i.tm.base_opcode ^= (i.tm.base_opcode & 0xee) != 0x6e
? Opcode_SIMD_FloatD : Opcode_SIMD_IntD;
else /* Use the next insn. */
i.tm = t[1];
install_template (&t[1]);
}
/* Use 2-byte VEX prefix by swapping commutative source operands if there
@ -6665,7 +6682,7 @@ match_template (char mnem_suffix)
}
/* Copy the template we found. */
i.tm = *t;
install_template (t);
if (addr_prefix_disp != -1)
i.tm.operand_types[addr_prefix_disp]
@ -7661,10 +7678,10 @@ process_operands (void)
i.tm.name, register_prefix, i.op[0].regs->reg_name);
return 0;
}
if ( i.op[0].regs->reg_num > 3 && i.tm.opcode_length == 1 )
if ( i.op[0].regs->reg_num > 3 && i.opcode_length == 1 )
{
i.tm.base_opcode ^= POP_SEG_SHORT ^ POP_SEG386_SHORT;
i.tm.opcode_length = 2;
i.opcode_length = 2;
}
i.tm.base_opcode |= (i.op[0].regs->reg_num << 3);
}
@ -8657,12 +8674,12 @@ output_jump (void)
if (now_seg == absolute_section)
{
abs_section_offset += i.tm.opcode_length + size;
abs_section_offset += i.opcode_length + size;
return;
}
p = frag_more (i.tm.opcode_length + size);
switch (i.tm.opcode_length)
p = frag_more (i.opcode_length + size);
switch (i.opcode_length)
{
case 2:
*p++ = i.tm.base_opcode >> 8;
@ -9385,14 +9402,15 @@ output_insn (void)
add_prefix (0xf3);
break;
case PREFIX_NONE:
switch (i.tm.opcode_length)
switch (i.opcode_length)
{
case 3:
case 2:
case 1:
break;
case 0:
case 1:
/* Check for pseudo prefixes. */
if (!i.tm.opcode_modifier.isprefix || i.tm.base_opcode)
break;
as_bad_where (insn_start_frag->fr_file,
insn_start_frag->fr_line,
_("pseudo prefix without instruction"));
@ -9458,14 +9476,14 @@ output_insn (void)
/* Now the opcode; be careful about word order here! */
if (now_seg == absolute_section)
abs_section_offset += i.tm.opcode_length;
else if (i.tm.opcode_length == 1)
abs_section_offset += i.opcode_length;
else if (i.opcode_length == 1)
{
FRAG_APPEND_1_CHAR (i.tm.base_opcode);
}
else
{
switch (i.tm.opcode_length)
switch (i.opcode_length)
{
case 4:
p = frag_more (4);
@ -9541,7 +9559,7 @@ output_insn (void)
/* Count prefixes for extended opcode maps. */
if (!i.vex.length)
switch (i.tm.opcode_length)
switch (i.opcode_length)
{
case 3:
if (((i.tm.base_opcode >> 16) & 0xff) == 0xf)

View File

@ -1,3 +1,13 @@
2021-03-24 Jan Beulich <jbeulich@suse.com>
* i386-gen.c (output_i386_opcode): Drop processing of
opcode_length. Calculate length from base_opcode. Adjust prefix
encoding determination.
(process_i386_opcodes): Drop output of fake opcode_length.
* i386-opc.h (struct insn_template): Drop opcode_length field.
* i386-opc.tbl: Drop opcode length field from all templates.
* i386-tbl.h: Re-generate.
2021-03-24 Jan Beulich <jbeulich@suse.com>
* i386-gen.c (process_i386_opcode_modifier): Return void. New

View File

@ -1355,10 +1355,10 @@ static void
output_i386_opcode (FILE *table, const char *name, char *str,
char *last, int lineno)
{
unsigned int i, prefix = 0;
char *base_opcode, *extension_opcode, *opcode_length, *end;
unsigned int i, length, prefix = 0;
char *base_opcode, *extension_opcode, *end;
char *cpu_flags, *opcode_modifier, *operand_types [MAX_OPERANDS];
unsigned long int opcode, length;
unsigned long int opcode;
/* Find base_opcode. */
base_opcode = next_field (str, ',', &str, last);
@ -1366,9 +1366,6 @@ output_i386_opcode (FILE *table, const char *name, char *str,
/* Find extension_opcode. */
extension_opcode = next_field (str, ',', &str, last);
/* Find opcode_length. */
opcode_length = next_field (str, ',', &str, last);
/* Find cpu_flags. */
cpu_flags = next_field (str, ',', &str, last);
@ -1408,29 +1405,30 @@ output_i386_opcode (FILE *table, const char *name, char *str,
}
}
length = strtoul (opcode_length, &end, 0);
opcode = strtoul (base_opcode, &end, 0);
/* Determine opcode length. */
for (length = 1; length < 4; ++length)
if (!(opcode >> (8 * length)))
break;
/* Transform prefixes encoded in the opcode into opcode modifier
representation. */
if (length < 4)
if (length > 1)
{
switch (opcode >> (8 * length))
switch (opcode >> (8 * length - 8))
{
case 0: break;
case 0x66: prefix = PREFIX_0X66; break;
case 0xF3: prefix = PREFIX_0XF3; break;
case 0xF2: prefix = PREFIX_0XF2; break;
default:
fail (_("%s:%d: %s: Unexpected opcode prefix %02lx\n"),
filename, lineno, name, opcode >> (8 * length));
}
opcode &= (1UL << (8 * length)) - 1;
if (prefix)
opcode &= (1UL << (8 * --length)) - 1;
}
fprintf (table, " { \"%s\", 0x%0*lx%s, %s, %lu, %u,\n",
name, 2 * (int)length, opcode, end, extension_opcode, length, i);
fprintf (table, " { \"%s\", 0x%0*lx%s, %s, %lu,\n",
name, 2 * (int)length, opcode, end, extension_opcode, i);
process_i386_opcode_modifier (table, opcode_modifier, prefix,
operand_types, lineno);
@ -1822,7 +1820,7 @@ process_i386_opcodes (FILE *table)
fclose (fp);
fprintf (table, " { NULL, 0, 0, 0, 0,\n");
fprintf (table, " { NULL, 0, 0, 0,\n");
process_i386_opcode_modifier (table, "0", 0, NULL, -1);

View File

@ -949,9 +949,6 @@ typedef struct insn_template
#define Prefix_REX 8 /* {rex} */
#define Prefix_NoOptimize 9 /* {nooptimize} */
/* Opcode length. */
unsigned char opcode_length;
/* how many operands */
unsigned char operands;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff