aboutsummaryrefslogtreecommitdiff
path: root/gas
diff options
context:
space:
mode:
authorJan Beulich <jbeulich@suse.com>2023-03-31 08:18:58 +0200
committerJan Beulich <jbeulich@suse.com>2023-03-31 08:18:58 +0200
commitedd67638687a06788c8c69c75e139bca8f94f1a3 (patch)
tree2a61dec9a6dc6e5926ccbdbf90224b64f2f8b7c7 /gas
parentaf9eb2ee1b10caa89e1c603cd484bc025e71f4fd (diff)
downloadbinutils-edd67638687a06788c8c69c75e139bca8f94f1a3.zip
binutils-edd67638687a06788c8c69c75e139bca8f94f1a3.tar.gz
binutils-edd67638687a06788c8c69c75e139bca8f94f1a3.tar.bz2
x86: introduce .insn directive
For starters this deals with only very basic constructs.
Diffstat (limited to 'gas')
-rw-r--r--gas/config/tc-i386.c165
-rw-r--r--gas/testsuite/gas/i386/i386.exp2
-rw-r--r--gas/testsuite/gas/i386/insn-32.d14
-rw-r--r--gas/testsuite/gas/i386/insn-32.s14
-rw-r--r--gas/testsuite/gas/i386/insn-64.d14
-rw-r--r--gas/testsuite/gas/i386/insn-64.s14
6 files changed, 213 insertions, 10 deletions
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index 44efad7..2098e20 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 4d2150f..c44f071 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 0000000..d1b761c
--- /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+ <insn>:
+[ ]*[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 0000000..71e8427
--- /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 0000000..716e2a8
--- /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+ <insn>:
+[ ]*[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 0000000..71e8427
--- /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