From edd67638687a06788c8c69c75e139bca8f94f1a3 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Fri, 31 Mar 2023 08:18:58 +0200 Subject: [PATCH] x86: introduce .insn directive For starters this deals with only very basic constructs. --- gas/config/tc-i386.c | 165 +++++++++++++++++++++++++++++-- gas/testsuite/gas/i386/i386.exp | 2 + gas/testsuite/gas/i386/insn-32.d | 14 +++ gas/testsuite/gas/i386/insn-32.s | 14 +++ gas/testsuite/gas/i386/insn-64.d | 14 +++ gas/testsuite/gas/i386/insn-64.s | 14 +++ opcodes/i386-gen.c | 3 + opcodes/i386-mnem.h | 1 + opcodes/i386-tbl.h | 1 + 9 files changed, 218 insertions(+), 10 deletions(-) create mode 100644 gas/testsuite/gas/i386/insn-32.d create mode 100644 gas/testsuite/gas/i386/insn-32.s create mode 100644 gas/testsuite/gas/i386/insn-64.d create mode 100644 gas/testsuite/gas/i386/insn-64.s diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c index 44efad73e5d..2098e206ea9 100644 --- a/gas/config/tc-i386.c +++ b/gas/config/tc-i386.c @@ -137,6 +137,7 @@ typedef struct arch_entry; static void update_code_flag (int, int); +static void s_insn (int); static void set_code_flag (int); static void set_16bit_gcc_code_flag (int); static void set_intel_syntax (int); @@ -159,7 +160,7 @@ static int i386_intel_operand (char *, int); static int i386_intel_simplify (expressionS *); static int i386_intel_parse_name (const char *, expressionS *); static const reg_entry *parse_register (char *, char **); -static const char *parse_insn (const char *, char *); +static const char *parse_insn (const char *, char *, bool); static char *parse_operands (char *, const char *); static void swap_operands (void); static void swap_2_operands (unsigned int, unsigned int); @@ -1196,6 +1197,7 @@ const pseudo_typeS md_pseudo_table[] = {"bfloat16", float_cons, 'b'}, {"value", cons, 2}, {"slong", signed_cons, 4}, + {"insn", s_insn, 0}, {"noopt", s_ignore, 0}, {"optim", s_ignore, 0}, {"code16gcc", set_16bit_gcc_code_flag, CODE_16BIT}, @@ -4841,6 +4843,20 @@ insert_lfence_before (void) } } +/* Shared helper for md_assemble() and s_insn(). */ +static void init_globals (void) +{ + unsigned int j; + + memset (&i, '\0', sizeof (i)); + i.rounding.type = rc_none; + for (j = 0; j < MAX_OPERANDS; j++) + i.reloc[j] = NO_RELOC; + memset (disp_expressions, '\0', sizeof (disp_expressions)); + memset (im_expressions, '\0', sizeof (im_expressions)); + save_stack_p = save_stack; +} + /* Helper for md_assemble() to decide whether to prepare for a possible 2nd parsing pass. Instead of introducing a rarely use new insn attribute this utilizes a common pattern between affected templates. It is deemed @@ -4873,19 +4889,13 @@ md_assemble (char *line) /* Initialize globals. */ current_templates = NULL; retry: - memset (&i, '\0', sizeof (i)); - i.rounding.type = rc_none; - for (j = 0; j < MAX_OPERANDS; j++) - i.reloc[j] = NO_RELOC; - memset (disp_expressions, '\0', sizeof (disp_expressions)); - memset (im_expressions, '\0', sizeof (im_expressions)); - save_stack_p = save_stack; + init_globals (); /* First parse an instruction mnemonic & call i386_operand for the operands. We assume that the scrubber has arranged it so that line[0] is the valid start of a (possibly prefixed) mnemonic. */ - end = parse_insn (line, mnemonic); + end = parse_insn (line, mnemonic, false); if (end == NULL) { if (pass1_mnem != NULL) @@ -5424,7 +5434,7 @@ static INLINE bool q_suffix_allowed(const insn_template *t) } static const char * -parse_insn (const char *line, char *mnemonic) +parse_insn (const char *line, char *mnemonic, bool prefix_only) { const char *l = line, *token_start = l; char *mnem_p; @@ -5454,6 +5464,8 @@ parse_insn (const char *line, char *mnemonic) || (*l != PREFIX_SEPARATOR && *l != ','))) { + if (prefix_only) + break; as_bad (_("invalid character %s in mnemonic"), output_invalid (*l)); return NULL; @@ -5575,6 +5587,9 @@ parse_insn (const char *line, char *mnemonic) break; } + if (prefix_only) + return token_start; + if (!current_templates) { /* Deprecated functionality (new code should use pseudo-prefixes instead): @@ -10515,6 +10530,136 @@ signed_cons (int size) cons_sign = -1; } +static void +s_insn (int dummy ATTRIBUTE_UNUSED) +{ + char mnemonic[MAX_MNEM_SIZE], *line = input_line_pointer; + char *saved_ilp = find_end_of_line (line, false), saved_char; + const char *end; + unsigned int j; + valueT val; + bool vex = false, xop = false, evex = false; + static const templates tt = { &i.tm, &i.tm + 1 }; + + init_globals (); + + saved_char = *saved_ilp; + *saved_ilp = 0; + + end = parse_insn (line, mnemonic, true); + if (end == NULL) + { + bad: + *saved_ilp = saved_char; + ignore_rest_of_line (); + return; + } + line += end - line; + + current_templates = &tt; + i.tm.mnem_off = MN__insn; + + if (startswith (line, "VEX") + && (line[3] == '.' || is_space_char (line[3]))) + { + vex = true; + line += 3; + } + else if (startswith (line, "XOP") && ISDIGIT (line[3])) + { + char *e; + unsigned long n = strtoul (line + 3, &e, 16); + + if (e == line + 5 && n >= 0x08 && n <= 0x1f + && (*e == '.' || is_space_char (*e))) + { + xop = true; + line = e; + } + } + else if (startswith (line, "EVEX") + && (line[4] == '.' || is_space_char (line[4]))) + { + evex = true; + line += 4; + } + + if (vex || xop + ? i.vec_encoding == vex_encoding_evex + : evex + ? i.vec_encoding == vex_encoding_vex + || i.vec_encoding == vex_encoding_vex3 + : i.vec_encoding != vex_encoding_default) + { + as_bad (_("pseudo-prefix conflicts with encoding specifier")); + goto bad; + } + + if (line > end && *line == '.') + { + } + + input_line_pointer = line; + val = get_absolute_expression (); + line = input_line_pointer; + + for (j = 1; j < sizeof(val); ++j) + if (!(val >> (j * 8))) + break; + + /* Trim off a prefix if present. */ + if (j > 1 && !vex && !xop && !evex) + { + uint8_t byte = val >> ((j - 1) * 8); + + switch (byte) + { + case DATA_PREFIX_OPCODE: + case REPE_PREFIX_OPCODE: + case REPNE_PREFIX_OPCODE: + if (!add_prefix (byte)) + goto bad; + val &= ((uint64_t)1 << (--j * 8)) - 1; + break; + } + } + + /* Trim off encoding space. */ + if (j > 1 && !i.tm.opcode_space && (val >> ((j - 1) * 8)) == 0x0f) + { + uint8_t byte = val >> ((--j - 1) * 8); + + i.tm.opcode_space = SPACE_0F; + switch (byte & -(j > 1)) + { + case 0x38: + i.tm.opcode_space = SPACE_0F38; + --j; + break; + case 0x3a: + i.tm.opcode_space = SPACE_0F3A; + --j; + break; + } + val &= ((uint64_t)1 << (j * 8)) - 1; + } + + if (j > 2) + { + as_bad (_("opcode residual (%#"PRIx64") too wide"), (uint64_t) val); + goto bad; + } + i.opcode_length = j; + i.tm.base_opcode = val; + + output_insn (); + + *saved_ilp = saved_char; + input_line_pointer = line; + + demand_empty_rest_of_line (); +} + #ifdef TE_PE static void pe_directive_secrel (int dummy ATTRIBUTE_UNUSED) diff --git a/gas/testsuite/gas/i386/i386.exp b/gas/testsuite/gas/i386/i386.exp index 4d2150f9c68..c44f071a0e2 100644 --- a/gas/testsuite/gas/i386/i386.exp +++ b/gas/testsuite/gas/i386/i386.exp @@ -68,6 +68,7 @@ if [gas_32_check] then { run_dump_test "intelok" run_dump_test "prefix" run_list_test "prefix32" "-al" + run_dump_test "insn-32" run_dump_test "lea" run_dump_test "lea16" run_dump_test "amd" @@ -874,6 +875,7 @@ if [gas_64_check] then { run_dump_test "x86-64-sysenter-mixed" run_dump_test "x86-64-sysenter-amd" run_list_test "x86-64-sysenter-amd" "-mamd64" + run_dump_test "insn-64" run_dump_test "noreg64" run_list_test "noreg64" run_dump_test "noreg64-data16" diff --git a/gas/testsuite/gas/i386/insn-32.d b/gas/testsuite/gas/i386/insn-32.d new file mode 100644 index 00000000000..d1b761c35ed --- /dev/null +++ b/gas/testsuite/gas/i386/insn-32.d @@ -0,0 +1,14 @@ +#objdump: -dw +#name: .insn (32-bit code) + +.*: +file format .* + +Disassembly of section .text: + +0+ : +[ ]*[a-f0-9]+: 90[ ]+nop +[ ]*[a-f0-9]+: f3 90[ ]+pause +[ ]*[a-f0-9]+: f3 90[ ]+pause +[ ]*[a-f0-9]+: d9 ee[ ]+fldz +[ ]*[a-f0-9]+: f3 0f 01 e8[ ]+setssbsy +#pass diff --git a/gas/testsuite/gas/i386/insn-32.s b/gas/testsuite/gas/i386/insn-32.s new file mode 100644 index 00000000000..71e8427c5dc --- /dev/null +++ b/gas/testsuite/gas/i386/insn-32.s @@ -0,0 +1,14 @@ + .text +insn: + # nop + .insn 0x90 + + # pause + .insn 0xf390 + .insn repe 0x90 + + # fldz + .insn 0xd9ee + + # setssbsy + .insn 0xf30f01e8 diff --git a/gas/testsuite/gas/i386/insn-64.d b/gas/testsuite/gas/i386/insn-64.d new file mode 100644 index 00000000000..716e2a81079 --- /dev/null +++ b/gas/testsuite/gas/i386/insn-64.d @@ -0,0 +1,14 @@ +#objdump: -dw +#name: .insn (64-bit code) + +.*: +file format .* + +Disassembly of section .text: + +0+ : +[ ]*[a-f0-9]+: 90[ ]+nop +[ ]*[a-f0-9]+: f3 90[ ]+pause +[ ]*[a-f0-9]+: f3 90[ ]+pause +[ ]*[a-f0-9]+: d9 ee[ ]+fldz +[ ]*[a-f0-9]+: f3 0f 01 e8[ ]+setssbsy +#pass diff --git a/gas/testsuite/gas/i386/insn-64.s b/gas/testsuite/gas/i386/insn-64.s new file mode 100644 index 00000000000..71e8427c5dc --- /dev/null +++ b/gas/testsuite/gas/i386/insn-64.s @@ -0,0 +1,14 @@ + .text +insn: + # nop + .insn 0x90 + + # pause + .insn 0xf390 + .insn repe 0x90 + + # fldz + .insn 0xd9ee + + # setssbsy + .insn 0xf30f01e8 diff --git a/opcodes/i386-gen.c b/opcodes/i386-gen.c index 76ccd32a97b..489ae3429c9 100644 --- a/opcodes/i386-gen.c +++ b/opcodes/i386-gen.c @@ -1814,6 +1814,9 @@ process_i386_opcodes (FILE *table) l = l1; } + fprintf (table, " \"\\0\"\".insn\"\n"); + fprintf (fp, "#define MN__insn %#x\n", offs + 1); + fprintf (table, ";\n"); fclose (fp); diff --git a/opcodes/i386-mnem.h b/opcodes/i386-mnem.h index 4ca786a1957..3e0af793fa8 100644 --- a/opcodes/i386-mnem.h +++ b/opcodes/i386-mnem.h @@ -2339,3 +2339,4 @@ extern const char i386_mnemonics[]; #define MN__rex_ 0x469c #define MN__evex_ 0x46a2 #define MN__vex_ 0x46a9 +#define MN__insn 0x46af diff --git a/opcodes/i386-tbl.h b/opcodes/i386-tbl.h index bc830af8640..ed7c43c8e4d 100644 --- a/opcodes/i386-tbl.h +++ b/opcodes/i386-tbl.h @@ -60199,6 +60199,7 @@ const char i386_mnemonics[] = "\0""{rex}" "\0""{evex}" "\0""{vex}" + "\0"".insn" ; /* i386 register table. */ -- 2.30.2