"lock addl $0, (%{re}sp)". */
static int avoid_fence = 0;
-/* Type of the previous instruction. */
+/* 1 if lfence should be inserted after every load. */
+static int lfence_after_load = 0;
+
+/* Non-zero if lfence should be inserted before indirect branch. */
+static enum lfence_before_indirect_branch_kind
+ {
+ lfence_branch_none = 0,
+ lfence_branch_register,
+ lfence_branch_memory,
+ lfence_branch_all
+ }
+lfence_before_indirect_branch;
+
+/* Non-zero if lfence should be inserted before ret. */
+static enum lfence_before_ret_kind
+ {
+ lfence_before_ret_none = 0,
+ lfence_before_ret_not,
+ lfence_before_ret_or,
+ lfence_before_ret_shl
+ }
+lfence_before_ret;
+
+/* Types of previous instruction is .byte or prefix. */
static struct
{
segT seg;
| align_branch_fused_bit
| align_branch_jmp_bit);
+/* Types of condition jump used by macro-fusion. */
+enum mf_jcc_kind
+ {
+ mf_jcc_jo = 0, /* base opcode 0x70 */
+ mf_jcc_jc, /* base opcode 0x72 */
+ mf_jcc_je, /* base opcode 0x74 */
+ mf_jcc_jna, /* base opcode 0x76 */
+ mf_jcc_js, /* base opcode 0x78 */
+ mf_jcc_jp, /* base opcode 0x7a */
+ mf_jcc_jl, /* base opcode 0x7c */
+ mf_jcc_jle, /* base opcode 0x7e */
+ };
+
+/* Types of compare flag-modifying insntructions used by macro-fusion. */
+enum mf_cmp_kind
+ {
+ mf_cmp_test_and, /* test/cmp */
+ mf_cmp_alu_cmp, /* add/sub/cmp */
+ mf_cmp_incdec /* inc/dec */
+ };
+
/* The maximum padding size for fused jcc. CMP like instruction can
be 9 bytes and jcc can be 6 bytes. Leave room just in case for
prefixes. */
CPU_SSE2_FLAGS, 0 },
{ STRING_COMMA_LEN (".sse3"), PROCESSOR_UNKNOWN,
CPU_SSE3_FLAGS, 0 },
+ { STRING_COMMA_LEN (".sse4a"), PROCESSOR_UNKNOWN,
+ CPU_SSE4A_FLAGS, 0 },
{ STRING_COMMA_LEN (".ssse3"), PROCESSOR_UNKNOWN,
CPU_SSSE3_FLAGS, 0 },
{ STRING_COMMA_LEN (".sse4.1"), PROCESSOR_UNKNOWN,
CPU_EPT_FLAGS, 0 },
{ STRING_COMMA_LEN (".lzcnt"), PROCESSOR_UNKNOWN,
CPU_LZCNT_FLAGS, 0 },
+ { STRING_COMMA_LEN (".popcnt"), PROCESSOR_UNKNOWN,
+ CPU_POPCNT_FLAGS, 0 },
{ STRING_COMMA_LEN (".hle"), PROCESSOR_UNKNOWN,
CPU_HLE_FLAGS, 0 },
{ STRING_COMMA_LEN (".rtm"), PROCESSOR_UNKNOWN,
CPU_AVX512_VP2INTERSECT_FLAGS, 0 },
{ STRING_COMMA_LEN (".enqcmd"), PROCESSOR_UNKNOWN,
CPU_ENQCMD_FLAGS, 0 },
+ { STRING_COMMA_LEN (".serialize"), PROCESSOR_UNKNOWN,
+ CPU_SERIALIZE_FLAGS, 0 },
{ STRING_COMMA_LEN (".rdpru"), PROCESSOR_UNKNOWN,
CPU_RDPRU_FLAGS, 0 },
{ STRING_COMMA_LEN (".mcommit"), PROCESSOR_UNKNOWN,
CPU_MCOMMIT_FLAGS, 0 },
+ { STRING_COMMA_LEN (".sev_es"), PROCESSOR_UNKNOWN,
+ CPU_SEV_ES_FLAGS, 0 },
+ { STRING_COMMA_LEN (".tsxldtrk"), PROCESSOR_UNKNOWN,
+ CPU_TSXLDTRK_FLAGS, 0 },
};
static const noarch_entry cpu_noarch[] =
{ STRING_COMMA_LEN ("nosse"), CPU_ANY_SSE_FLAGS },
{ STRING_COMMA_LEN ("nosse2"), CPU_ANY_SSE2_FLAGS },
{ STRING_COMMA_LEN ("nosse3"), CPU_ANY_SSE3_FLAGS },
+ { STRING_COMMA_LEN ("nosse4a"), CPU_ANY_SSE4A_FLAGS },
{ STRING_COMMA_LEN ("nossse3"), CPU_ANY_SSSE3_FLAGS },
{ STRING_COMMA_LEN ("nosse4.1"), CPU_ANY_SSE4_1_FLAGS },
{ STRING_COMMA_LEN ("nosse4.2"), CPU_ANY_SSE4_2_FLAGS },
- { STRING_COMMA_LEN ("nosse4"), CPU_ANY_SSE4_FLAGS },
+ { STRING_COMMA_LEN ("nosse4"), CPU_ANY_SSE4_1_FLAGS },
{ STRING_COMMA_LEN ("noavx"), CPU_ANY_AVX_FLAGS },
{ STRING_COMMA_LEN ("noavx2"), CPU_ANY_AVX2_FLAGS },
{ STRING_COMMA_LEN ("noavx512f"), CPU_ANY_AVX512F_FLAGS },
{ STRING_COMMA_LEN ("noavx512_bf16"), CPU_ANY_AVX512_BF16_FLAGS },
{ STRING_COMMA_LEN ("noavx512_vp2intersect"), CPU_ANY_SHSTK_FLAGS },
{ STRING_COMMA_LEN ("noenqcmd"), CPU_ANY_ENQCMD_FLAGS },
+ { STRING_COMMA_LEN ("noserialize"), CPU_ANY_SERIALIZE_FLAGS },
+ { STRING_COMMA_LEN ("notsxldtrk"), CPU_ANY_TSXLDTRK_FLAGS },
};
#ifdef I386COFF
here. Also for v{,p}broadcast*, {,v}pmov{s,z}*, and
down-conversion vpmov*. */
|| ((t->operand_types[wanted].bitfield.class == RegSIMD
- && !t->opcode_modifier.broadcast
- && (t->operand_types[wanted].bitfield.byte
- || t->operand_types[wanted].bitfield.word
- || t->operand_types[wanted].bitfield.dword
- || t->operand_types[wanted].bitfield.qword))
+ && t->operand_types[wanted].bitfield.byte
+ + t->operand_types[wanted].bitfield.word
+ + t->operand_types[wanted].bitfield.dword
+ + t->operand_types[wanted].bitfield.qword
+ > !!t->opcode_modifier.broadcast)
? (i.types[given].bitfield.xmmword
|| i.types[given].bitfield.ymmword
|| i.types[given].bitfield.zmmword)
if (!t->opcode_modifier.d)
{
-mismatch:
+ mismatch:
if (!match)
i.error = operand_size_mismatch;
return match;
if (given.bitfield.baseindex == overlap.bitfield.baseindex)
return 1;
-mismatch:
+ mismatch:
i.error = operand_type_mismatch;
return 0;
}
}
}
+/* Return non-zero for load instruction. */
+
+static int
+load_insn_p (void)
+{
+ unsigned int dest;
+ int any_vex_p = is_any_vex_encoding (&i.tm);
+ unsigned int base_opcode = i.tm.base_opcode | 1;
+
+ if (!any_vex_p)
+ {
+ /* Anysize insns: lea, invlpg, clflush, prefetchnta, prefetcht0,
+ prefetcht1, prefetcht2, prefetchtw, bndmk, bndcl, bndcu, bndcn,
+ bndstx, bndldx, prefetchwt1, clflushopt, clwb, cldemote. */
+ if (i.tm.opcode_modifier.anysize)
+ return 0;
+
+ /* pop, popf, popa. */
+ if (strcmp (i.tm.name, "pop") == 0
+ || i.tm.base_opcode == 0x9d
+ || i.tm.base_opcode == 0x61)
+ return 1;
+
+ /* movs, cmps, lods, scas. */
+ if ((i.tm.base_opcode | 0xb) == 0xaf)
+ return 1;
+
+ /* outs, xlatb. */
+ if (base_opcode == 0x6f
+ || i.tm.base_opcode == 0xd7)
+ return 1;
+ /* NB: For AMD-specific insns with implicit memory operands,
+ they're intentionally not covered. */
+ }
+
+ /* No memory operand. */
+ if (!i.mem_operands)
+ return 0;
+
+ if (any_vex_p)
+ {
+ /* vldmxcsr. */
+ if (i.tm.base_opcode == 0xae
+ && i.tm.opcode_modifier.vex
+ && i.tm.opcode_modifier.vexopcode == VEX0F
+ && i.tm.extension_opcode == 2)
+ return 1;
+ }
+ else
+ {
+ /* test, not, neg, mul, imul, div, idiv. */
+ if ((i.tm.base_opcode == 0xf6 || i.tm.base_opcode == 0xf7)
+ && i.tm.extension_opcode != 1)
+ return 1;
+
+ /* inc, dec. */
+ if (base_opcode == 0xff && i.tm.extension_opcode <= 1)
+ return 1;
+
+ /* add, or, adc, sbb, and, sub, xor, cmp. */
+ if (i.tm.base_opcode >= 0x80 && i.tm.base_opcode <= 0x83)
+ return 1;
+
+ /* bt, bts, btr, btc. */
+ if (i.tm.base_opcode == 0xfba
+ && (i.tm.extension_opcode >= 4 && i.tm.extension_opcode <= 7))
+ return 1;
+
+ /* rol, ror, rcl, rcr, shl/sal, shr, sar. */
+ if ((base_opcode == 0xc1
+ || (i.tm.base_opcode >= 0xd0 && i.tm.base_opcode <= 0xd3))
+ && i.tm.extension_opcode != 6)
+ return 1;
+
+ /* cmpxchg8b, cmpxchg16b, xrstors. */
+ if (i.tm.base_opcode == 0xfc7
+ && (i.tm.extension_opcode == 1 || i.tm.extension_opcode == 3))
+ return 1;
+
+ /* fxrstor, ldmxcsr, xrstor. */
+ if (i.tm.base_opcode == 0xfae
+ && (i.tm.extension_opcode == 1
+ || i.tm.extension_opcode == 2
+ || i.tm.extension_opcode == 5))
+ return 1;
+
+ /* lgdt, lidt, lmsw. */
+ if (i.tm.base_opcode == 0xf01
+ && (i.tm.extension_opcode == 2
+ || i.tm.extension_opcode == 3
+ || i.tm.extension_opcode == 6))
+ return 1;
+
+ /* vmptrld */
+ if (i.tm.base_opcode == 0xfc7
+ && i.tm.extension_opcode == 6)
+ return 1;
+
+ /* Check for x87 instructions. */
+ if (i.tm.base_opcode >= 0xd8 && i.tm.base_opcode <= 0xdf)
+ {
+ /* Skip fst, fstp, fstenv, fstcw. */
+ if (i.tm.base_opcode == 0xd9
+ && (i.tm.extension_opcode == 2
+ || i.tm.extension_opcode == 3
+ || i.tm.extension_opcode == 6
+ || i.tm.extension_opcode == 7))
+ return 0;
+
+ /* Skip fisttp, fist, fistp, fstp. */
+ if (i.tm.base_opcode == 0xdb
+ && (i.tm.extension_opcode == 1
+ || i.tm.extension_opcode == 2
+ || i.tm.extension_opcode == 3
+ || i.tm.extension_opcode == 7))
+ return 0;
+
+ /* Skip fisttp, fst, fstp, fsave, fstsw. */
+ if (i.tm.base_opcode == 0xdd
+ && (i.tm.extension_opcode == 1
+ || i.tm.extension_opcode == 2
+ || i.tm.extension_opcode == 3
+ || i.tm.extension_opcode == 6
+ || i.tm.extension_opcode == 7))
+ return 0;
+
+ /* Skip fisttp, fist, fistp, fbstp, fistp. */
+ if (i.tm.base_opcode == 0xdf
+ && (i.tm.extension_opcode == 1
+ || i.tm.extension_opcode == 2
+ || i.tm.extension_opcode == 3
+ || i.tm.extension_opcode == 6
+ || i.tm.extension_opcode == 7))
+ return 0;
+
+ return 1;
+ }
+ }
+
+ dest = i.operands - 1;
+
+ /* Check fake imm8 operand and 3 source operands. */
+ if ((i.tm.opcode_modifier.immext
+ || i.tm.opcode_modifier.vexsources == VEX3SOURCES)
+ && i.types[dest].bitfield.imm8)
+ dest--;
+
+ /* add, or, adc, sbb, and, sub, xor, cmp, test, xchg, xadd */
+ if (!any_vex_p
+ && (base_opcode == 0x1
+ || base_opcode == 0x9
+ || base_opcode == 0x11
+ || base_opcode == 0x19
+ || base_opcode == 0x21
+ || base_opcode == 0x29
+ || base_opcode == 0x31
+ || base_opcode == 0x39
+ || (i.tm.base_opcode >= 0x84 && i.tm.base_opcode <= 0x87)
+ || base_opcode == 0xfc1))
+ return 1;
+
+ /* Check for load instruction. */
+ return (i.types[dest].bitfield.class != ClassNone
+ || i.types[dest].bitfield.instance == Accum);
+}
+
+/* Output lfence, 0xfaee8, after instruction. */
+
+static void
+insert_lfence_after (void)
+{
+ if (lfence_after_load && load_insn_p ())
+ {
+ /* There are also two REP string instructions that require
+ special treatment. Specifically, the compare string (CMPS)
+ and scan string (SCAS) instructions set EFLAGS in a manner
+ that depends on the data being compared/scanned. When used
+ with a REP prefix, the number of iterations may therefore
+ vary depending on this data. If the data is a program secret
+ chosen by the adversary using an LVI method,
+ then this data-dependent behavior may leak some aspect
+ of the secret. */
+ if (((i.tm.base_opcode | 0x1) == 0xa7
+ || (i.tm.base_opcode | 0x1) == 0xaf)
+ && i.prefix[REP_PREFIX])
+ {
+ as_warn (_("`%s` changes flags which would affect control flow behavior"),
+ i.tm.name);
+ }
+ char *p = frag_more (3);
+ *p++ = 0xf;
+ *p++ = 0xae;
+ *p = 0xe8;
+ }
+}
+
+/* Output lfence, 0xfaee8, before instruction. */
+
+static void
+insert_lfence_before (void)
+{
+ char *p;
+
+ if (is_any_vex_encoding (&i.tm))
+ return;
+
+ if (i.tm.base_opcode == 0xff
+ && (i.tm.extension_opcode == 2 || i.tm.extension_opcode == 4))
+ {
+ /* Insert lfence before indirect branch if needed. */
+
+ if (lfence_before_indirect_branch == lfence_branch_none)
+ return;
+
+ if (i.operands != 1)
+ abort ();
+
+ if (i.reg_operands == 1)
+ {
+ /* Indirect branch via register. Don't insert lfence with
+ -mlfence-after-load=yes. */
+ if (lfence_after_load
+ || lfence_before_indirect_branch == lfence_branch_memory)
+ return;
+ }
+ else if (i.mem_operands == 1
+ && lfence_before_indirect_branch != lfence_branch_register)
+ {
+ as_warn (_("indirect `%s` with memory operand should be avoided"),
+ i.tm.name);
+ return;
+ }
+ else
+ return;
+
+ if (last_insn.kind != last_insn_other
+ && last_insn.seg == now_seg)
+ {
+ as_warn_where (last_insn.file, last_insn.line,
+ _("`%s` skips -mlfence-before-indirect-branch on `%s`"),
+ last_insn.name, i.tm.name);
+ return;
+ }
+
+ p = frag_more (3);
+ *p++ = 0xf;
+ *p++ = 0xae;
+ *p = 0xe8;
+ return;
+ }
+
+ /* Output or/not/shl and lfence before ret/lret/iret. */
+ if (lfence_before_ret != lfence_before_ret_none
+ && (i.tm.base_opcode == 0xc2
+ || i.tm.base_opcode == 0xc3
+ || i.tm.base_opcode == 0xca
+ || i.tm.base_opcode == 0xcb
+ || i.tm.base_opcode == 0xcf))
+ {
+ if (last_insn.kind != last_insn_other
+ && last_insn.seg == now_seg)
+ {
+ as_warn_where (last_insn.file, last_insn.line,
+ _("`%s` skips -mlfence-before-ret on `%s`"),
+ last_insn.name, i.tm.name);
+ return;
+ }
+
+ /* lret or iret. */
+ bfd_boolean lret = (i.tm.base_opcode | 0x5) == 0xcf;
+ bfd_boolean has_rexw = i.prefix[REX_PREFIX] & REX_W;
+ char prefix = 0x0;
+ /* Default operand size for far return is 32 bits,
+ 64 bits for near return. */
+ /* Near ret ingore operand size override under CPU64. */
+ if ((!lret && flag_code == CODE_64BIT) || has_rexw)
+ prefix = 0x48;
+ else if (i.prefix[DATA_PREFIX])
+ prefix = 0x66;
+
+ if (lfence_before_ret == lfence_before_ret_not)
+ {
+ /* not: 0xf71424, may add prefix
+ for operand size override or 64-bit code. */
+ p = frag_more ((prefix ? 2 : 0) + 6 + 3);
+ if (prefix)
+ *p++ = prefix;
+ *p++ = 0xf7;
+ *p++ = 0x14;
+ *p++ = 0x24;
+ if (prefix)
+ *p++ = prefix;
+ *p++ = 0xf7;
+ *p++ = 0x14;
+ *p++ = 0x24;
+ }
+ else
+ {
+ p = frag_more ((prefix ? 1 : 0) + 4 + 3);
+ if (prefix)
+ *p++ = prefix;
+ if (lfence_before_ret == lfence_before_ret_or)
+ {
+ /* or: 0x830c2400, may add prefix
+ for operand size override or 64-bit code. */
+ *p++ = 0x83;
+ *p++ = 0x0c;
+ }
+ else
+ {
+ /* shl: 0xc1242400, may add prefix
+ for operand size override or 64-bit code. */
+ *p++ = 0xc1;
+ *p++ = 0x24;
+ }
+
+ *p++ = 0x24;
+ *p++ = 0x0;
+ }
+
+ *p++ = 0xf;
+ *p++ = 0xae;
+ *p = 0xe8;
+ }
+}
+
/* This is the guts of the machine-dependent assembler. LINE points to a
machine dependent instruction. This function is supposed to emit
the frags/bytes it assembles to. */
/* Now we've parsed the mnemonic into a set of templates, and have the
operands at hand. */
- /* All intel opcodes have reversed operands except for "bound" and
- "enter". We also don't reverse intersegment "jmp" and "call"
- instructions with 2 immediate operands so that the immediate segment
- precedes the offset, as it does when in AT&T mode. */
+ /* All Intel opcodes have reversed operands except for "bound", "enter",
+ "monitor*", "mwait*", "tpause", and "umwait". We also don't reverse
+ intersegment "jmp" and "call" instructions with 2 immediate operands so
+ that the immediate segment precedes the offset, as it does when in AT&T
+ mode. */
if (intel_syntax
&& i.operands > 1
&& (strcmp (mnemonic, "bound") != 0)
&& (strcmp (mnemonic, "invlpga") != 0)
+ && (strncmp (mnemonic, "monitor", 7) != 0)
+ && (strncmp (mnemonic, "mwait", 5) != 0)
+ && (strcmp (mnemonic, "tpause") != 0)
+ && (strcmp (mnemonic, "umwait") != 0)
&& !(operand_type_check (i.types[0], imm)
&& operand_type_check (i.types[1], imm)))
swap_operands ();
: as_bad) (_("SSE instruction `%s' is used"), i.tm.name);
}
- /* Zap movzx and movsx suffix. The suffix has been set from
- "word ptr" or "byte ptr" on the source operand in Intel syntax
- or extracted from mnemonic in AT&T syntax. But we'll use
- the destination register to choose the suffix for encoding. */
- if ((i.tm.base_opcode & ~9) == 0x0fb6)
- {
- /* In Intel syntax, there must be a suffix. In AT&T syntax, if
- there is no suffix, the default will be byte extension. */
- if (i.reg_operands != 2
- && !i.suffix
- && intel_syntax)
- as_bad (_("ambiguous operand size for `%s'"), i.tm.name);
-
- i.suffix = 0;
- }
-
if (i.tm.opcode_modifier.fwait)
if (!add_prefix (FWAIT_OPCODE))
return;
i.op[0].disps->X_op = O_symbol;
}
- if (i.tm.opcode_modifier.rex64)
- i.rex |= REX_W;
-
/* For 8 bit registers we need an empty rex prefix. Also if the
instruction already has a prefix, we need to convert old
registers to new ones. */
if (i.rex != 0)
add_prefix (REX_OPCODE | i.rex);
+ insert_lfence_before ();
+
/* We are ready to output the insn. */
output_insn ();
+ insert_lfence_after ();
+
last_insn.seg = now_seg;
if (i.tm.opcode_modifier.isprefix)
if (!current_templates)
{
-check_suffix:
+ check_suffix:
if (mnem_p > mnemonic)
{
/* See if we can get a match by trimming off a suffix. */
}
overlap = operand_type_and (type, t->operand_types[op]);
+ if (t->operand_types[op].bitfield.class == RegSIMD
+ && t->operand_types[op].bitfield.byte
+ + t->operand_types[op].bitfield.word
+ + t->operand_types[op].bitfield.dword
+ + t->operand_types[op].bitfield.qword > 1)
+ {
+ overlap.bitfield.xmmword = 0;
+ overlap.bitfield.ymmword = 0;
+ overlap.bitfield.zmmword = 0;
+ }
if (operand_type_all_zero (&overlap))
goto bad_broadcast;
for (j = 0; j < MAX_OPERANDS; j++)
operand_types[j] = t->operand_types[j];
- /* In general, don't allow 64-bit operands in 32-bit mode. */
- if (i.suffix == QWORD_MNEM_SUFFIX
- && flag_code != CODE_64BIT
+ /* In general, don't allow
+ - 64-bit operands outside of 64-bit mode,
+ - 32-bit operands on pre-386. */
+ j = i.imm_operands + (t->operands > i.imm_operands + 1);
+ if (((i.suffix == QWORD_MNEM_SUFFIX
+ && flag_code != CODE_64BIT
+ && (t->base_opcode != 0x0fc7
+ || t->extension_opcode != 1 /* cmpxchg8b */))
+ || (i.suffix == LONG_MNEM_SUFFIX
+ && !cpu_arch_flags.bitfield.cpui386))
&& (intel_syntax
- ? (!t->opcode_modifier.ignoresize
- && !t->opcode_modifier.broadcast
+ ? (t->opcode_modifier.mnemonicsize != IGNORESIZE
&& !intel_float_operand (t->name))
: intel_float_operand (t->name) != 2)
- && ((operand_types[0].bitfield.class != RegMMX
- && operand_types[0].bitfield.class != RegSIMD)
- || (operand_types[t->operands > 1].bitfield.class != RegMMX
- && operand_types[t->operands > 1].bitfield.class != RegSIMD))
- && (t->base_opcode != 0x0fc7
- || t->extension_opcode != 1 /* cmpxchg8b */))
- continue;
-
- /* In general, don't allow 32-bit operands on pre-386. */
- else if (i.suffix == LONG_MNEM_SUFFIX
- && !cpu_arch_flags.bitfield.cpui386
- && (intel_syntax
- ? (!t->opcode_modifier.ignoresize
- && !intel_float_operand (t->name))
- : intel_float_operand (t->name) != 2)
- && ((operand_types[0].bitfield.class != RegMMX
- && operand_types[0].bitfield.class != RegSIMD)
- || (operand_types[t->operands > 1].bitfield.class != RegMMX
- && operand_types[t->operands > 1].bitfield.class
- != RegSIMD)))
+ && (t->operands == i.imm_operands
+ || (operand_types[i.imm_operands].bitfield.class != RegMMX
+ && operand_types[i.imm_operands].bitfield.class != RegSIMD
+ && operand_types[i.imm_operands].bitfield.class != RegMask)
+ || (operand_types[j].bitfield.class != RegMMX
+ && operand_types[j].bitfield.class != RegSIMD
+ && operand_types[j].bitfield.class != RegMask))
+ && !t->opcode_modifier.vecsib)
continue;
/* Do not verify operands when there are none. */
- else
- {
- if (!t->operands)
- /* We've found a match; break out of loop. */
- break;
- }
+ if (!t->operands)
+ /* We've found a match; break out of loop. */
+ break;
if (!t->opcode_modifier.jump
|| t->opcode_modifier.jump == JUMP_ABSOLUTE)
if (!t->opcode_modifier.d)
continue;
-check_reverse:
+ check_reverse:
if (!(size_match & MATCH_REVERSE))
continue;
/* Try reversing direction of operands. */
as_warn (_("indirect %s without `*'"), t->name);
if (t->opcode_modifier.isprefix
- && t->opcode_modifier.ignoresize)
+ && t->opcode_modifier.mnemonicsize == IGNORESIZE)
{
/* Warn them that a data or address size prefix doesn't
affect assembly of the next line of code. */
else if (i.tm.opcode_modifier.size == SIZE64)
i.suffix = QWORD_MNEM_SUFFIX;
else if (i.reg_operands
- && (i.operands > 1 || i.types[0].bitfield.class == Reg))
+ && (i.operands > 1 || i.types[0].bitfield.class == Reg)
+ && !i.tm.opcode_modifier.addrprefixopreg)
{
+ unsigned int numop = i.operands;
+
+ /* movsx/movzx want only their source operand considered here, for the
+ ambiguity checking below. The suffix will be replaced afterwards
+ to represent the destination (register). */
+ if (((i.tm.base_opcode | 8) == 0xfbe && i.tm.opcode_modifier.w)
+ || (i.tm.base_opcode == 0x63 && i.tm.cpu_flags.bitfield.cpu64))
+ --i.operands;
+
+ /* crc32 needs REX.W set regardless of suffix / source operand size. */
+ if (i.tm.base_opcode == 0xf20f38f0
+ && i.tm.operand_types[1].bitfield.qword)
+ i.rex |= REX_W;
+
/* If there's no instruction mnemonic suffix we try to invent one
based on GPR operands. */
if (!i.suffix)
continue;
break;
}
+
+ /* As an exception, movsx/movzx silently default to a byte source
+ in AT&T mode. */
+ if ((i.tm.base_opcode | 8) == 0xfbe && i.tm.opcode_modifier.w
+ && !i.suffix && !intel_syntax)
+ i.suffix = BYTE_MNEM_SUFFIX;
}
else if (i.suffix == BYTE_MNEM_SUFFIX)
{
if (intel_syntax
- && i.tm.opcode_modifier.ignoresize
+ && i.tm.opcode_modifier.mnemonicsize == IGNORESIZE
&& i.tm.opcode_modifier.no_bsuf)
i.suffix = 0;
else if (!check_byte_reg ())
else if (i.suffix == LONG_MNEM_SUFFIX)
{
if (intel_syntax
- && i.tm.opcode_modifier.ignoresize
+ && i.tm.opcode_modifier.mnemonicsize == IGNORESIZE
&& i.tm.opcode_modifier.no_lsuf
&& !i.tm.opcode_modifier.todword
&& !i.tm.opcode_modifier.toqword)
else if (i.suffix == QWORD_MNEM_SUFFIX)
{
if (intel_syntax
- && i.tm.opcode_modifier.ignoresize
+ && i.tm.opcode_modifier.mnemonicsize == IGNORESIZE
&& i.tm.opcode_modifier.no_qsuf
&& !i.tm.opcode_modifier.todword
&& !i.tm.opcode_modifier.toqword)
else if (i.suffix == WORD_MNEM_SUFFIX)
{
if (intel_syntax
- && i.tm.opcode_modifier.ignoresize
+ && i.tm.opcode_modifier.mnemonicsize == IGNORESIZE
&& i.tm.opcode_modifier.no_wsuf)
i.suffix = 0;
else if (!check_word_reg ())
return 0;
}
- else if (intel_syntax && i.tm.opcode_modifier.ignoresize)
+ else if (intel_syntax
+ && i.tm.opcode_modifier.mnemonicsize == IGNORESIZE)
/* Do nothing if the instruction is going to ignore the prefix. */
;
else
abort ();
+
+ /* Undo the movsx/movzx change done above. */
+ i.operands = numop;
}
- else if (i.tm.opcode_modifier.defaultsize && !i.suffix)
+ else if (i.tm.opcode_modifier.mnemonicsize == DEFAULTSIZE
+ && !i.suffix)
{
i.suffix = stackop_size;
if (stackop_size == LONG_MNEM_SUFFIX)
}
if (!i.suffix
- && (!i.tm.opcode_modifier.defaultsize
+ && (i.tm.opcode_modifier.mnemonicsize != DEFAULTSIZE
/* Also cover lret/retf/iret in 64-bit mode. */
|| (flag_code == CODE_64BIT
&& !i.tm.opcode_modifier.no_lsuf
&& !i.tm.opcode_modifier.no_qsuf))
- && !i.tm.opcode_modifier.ignoresize
+ && i.tm.opcode_modifier.mnemonicsize != IGNORESIZE
/* Accept FLDENV et al without suffix. */
&& (i.tm.opcode_modifier.no_ssuf || i.tm.opcode_modifier.floatmf))
{
/* For [XYZ]MMWORD operands inspect operand sizes. While generally
also suitable for AT&T syntax mode, it was requested that this be
restricted to just Intel syntax. */
- if (intel_syntax)
+ if (intel_syntax && is_any_vex_encoding (&i.tm) && !i.broadcast)
{
- i386_cpu_flags cpu = cpu_flags_and (i.tm.cpu_flags, avx512);
+ unsigned int op;
- if (!cpu_flags_all_zero (&cpu) && !i.broadcast)
+ for (op = 0; op < i.tm.operands; ++op)
{
- unsigned int op;
-
- for (op = 0; op < i.tm.operands; ++op)
+ if (is_evex_encoding (&i.tm)
+ && !cpu_arch_flags.bitfield.cpuavx512vl)
{
- if (!cpu_arch_flags.bitfield.cpuavx512vl)
- {
- if (i.tm.operand_types[op].bitfield.ymmword)
- i.tm.operand_types[op].bitfield.xmmword = 0;
- if (i.tm.operand_types[op].bitfield.zmmword)
- i.tm.operand_types[op].bitfield.ymmword = 0;
- if (!i.tm.opcode_modifier.evex
- || i.tm.opcode_modifier.evex == EVEXDYN)
- i.tm.opcode_modifier.evex = EVEX512;
- }
+ if (i.tm.operand_types[op].bitfield.ymmword)
+ i.tm.operand_types[op].bitfield.xmmword = 0;
+ if (i.tm.operand_types[op].bitfield.zmmword)
+ i.tm.operand_types[op].bitfield.ymmword = 0;
+ if (!i.tm.opcode_modifier.evex
+ || i.tm.opcode_modifier.evex == EVEXDYN)
+ i.tm.opcode_modifier.evex = EVEX512;
+ }
- if (i.tm.operand_types[op].bitfield.xmmword
- + i.tm.operand_types[op].bitfield.ymmword
- + i.tm.operand_types[op].bitfield.zmmword < 2)
- continue;
+ if (i.tm.operand_types[op].bitfield.xmmword
+ + i.tm.operand_types[op].bitfield.ymmword
+ + i.tm.operand_types[op].bitfield.zmmword < 2)
+ continue;
- /* Any properly sized operand disambiguates the insn. */
- if (i.types[op].bitfield.xmmword
- || i.types[op].bitfield.ymmword
- || i.types[op].bitfield.zmmword)
- {
- suffixes &= ~(7 << 6);
- evex = 0;
- break;
- }
+ /* Any properly sized operand disambiguates the insn. */
+ if (i.types[op].bitfield.xmmword
+ || i.types[op].bitfield.ymmword
+ || i.types[op].bitfield.zmmword)
+ {
+ suffixes &= ~(7 << 6);
+ evex = 0;
+ break;
+ }
- if ((i.flags[op] & Operand_Mem)
- && i.tm.operand_types[op].bitfield.unspecified)
- {
- if (i.tm.operand_types[op].bitfield.xmmword)
- suffixes |= 1 << 6;
- if (i.tm.operand_types[op].bitfield.ymmword)
- suffixes |= 1 << 7;
- if (i.tm.operand_types[op].bitfield.zmmword)
- suffixes |= 1 << 8;
- evex = EVEX512;
- }
+ if ((i.flags[op] & Operand_Mem)
+ && i.tm.operand_types[op].bitfield.unspecified)
+ {
+ if (i.tm.operand_types[op].bitfield.xmmword)
+ suffixes |= 1 << 6;
+ if (i.tm.operand_types[op].bitfield.ymmword)
+ suffixes |= 1 << 7;
+ if (i.tm.operand_types[op].bitfield.zmmword)
+ suffixes |= 1 << 8;
+ if (is_evex_encoding (&i.tm))
+ evex = EVEX512;
}
}
}
if (suffixes & (suffixes - 1))
{
if (intel_syntax
- && (!i.tm.opcode_modifier.defaultsize
+ && (i.tm.opcode_modifier.mnemonicsize != DEFAULTSIZE
|| operand_check == check_error))
{
as_bad (_("ambiguous operand size for `%s'"), i.tm.name);
if (i.tm.opcode_modifier.floatmf)
i.suffix = SHORT_MNEM_SUFFIX;
+ else if ((i.tm.base_opcode | 8) == 0xfbe
+ || (i.tm.base_opcode == 0x63
+ && i.tm.cpu_flags.bitfield.cpu64))
+ /* handled below */;
else if (evex)
i.tm.opcode_modifier.evex = evex;
else if (flag_code == CODE_16BIT)
}
}
+ if ((i.tm.base_opcode | 8) == 0xfbe
+ || (i.tm.base_opcode == 0x63 && i.tm.cpu_flags.bitfield.cpu64))
+ {
+ /* In Intel syntax, movsx/movzx must have a "suffix" (checked above).
+ In AT&T syntax, if there is no suffix (warned about above), the default
+ will be byte extension. */
+ if (i.tm.opcode_modifier.w && i.suffix && i.suffix != BYTE_MNEM_SUFFIX)
+ i.tm.base_opcode |= 1;
+
+ /* For further processing, the suffix should represent the destination
+ (register). This is already the case when one was used with
+ mov[sz][bw]*, but we need to replace it for mov[sz]x, or if there was
+ no suffix to begin with. */
+ if (i.tm.opcode_modifier.w || i.tm.base_opcode == 0x63 || !i.suffix)
+ {
+ if (i.types[1].bitfield.word)
+ i.suffix = WORD_MNEM_SUFFIX;
+ else if (i.types[1].bitfield.qword)
+ i.suffix = QWORD_MNEM_SUFFIX;
+ else
+ i.suffix = LONG_MNEM_SUFFIX;
+
+ i.tm.opcode_modifier.w = 0;
+ }
+ }
+
if (!i.tm.opcode_modifier.modrm && i.reg_operands && i.tm.operands < 3)
i.short_form = (i.tm.operand_types[0].bitfield.class == Reg)
!= (i.tm.operand_types[1].bitfield.class == Reg);
/* Now select between word & dword operations via the operand
size prefix, except for instructions that will ignore this
prefix anyway. */
- if (i.reg_operands > 0
- && i.types[0].bitfield.class == Reg
- && i.tm.opcode_modifier.addrprefixopreg
- && (i.tm.operand_types[0].bitfield.instance == Accum
- || i.operands == 1))
- {
- /* The address size override prefix changes the size of the
- first operand. */
- if ((flag_code == CODE_32BIT
- && i.op[0].regs->reg_type.bitfield.word)
- || (flag_code != CODE_32BIT
- && i.op[0].regs->reg_type.bitfield.dword))
- if (!add_prefix (ADDR_PREFIX_OPCODE))
- return 0;
- }
- else if (i.suffix != QWORD_MNEM_SUFFIX
- && !i.tm.opcode_modifier.ignoresize
- && !i.tm.opcode_modifier.floatmf
- && !is_any_vex_encoding (&i.tm)
- && ((i.suffix == LONG_MNEM_SUFFIX) == (flag_code == CODE_16BIT)
- || (flag_code == CODE_64BIT
- && i.tm.opcode_modifier.jump == JUMP_BYTE)))
+ if (i.suffix != QWORD_MNEM_SUFFIX
+ && i.tm.opcode_modifier.mnemonicsize != IGNORESIZE
+ && !i.tm.opcode_modifier.floatmf
+ && !is_any_vex_encoding (&i.tm)
+ && ((i.suffix == LONG_MNEM_SUFFIX) == (flag_code == CODE_16BIT)
+ || (flag_code == CODE_64BIT
+ && i.tm.opcode_modifier.jump == JUMP_BYTE)))
{
unsigned int prefix = DATA_PREFIX_OPCODE;
if (i.suffix == QWORD_MNEM_SUFFIX
&& flag_code == CODE_64BIT
&& !i.tm.opcode_modifier.norex64
+ && !i.tm.opcode_modifier.vexw
/* Special case for xchg %rax,%rax. It is NOP and doesn't
need rex64. */
&& ! (i.operands == 2
break;
}
- if (i.reg_operands != 0
- && i.operands > 1
- && i.tm.opcode_modifier.addrprefixopreg
- && i.tm.operand_types[0].bitfield.instance != Accum)
+ if (i.tm.opcode_modifier.addrprefixopreg)
{
- /* Check invalid register operand when the address size override
- prefix changes the size of register operands. */
- unsigned int op;
- enum { need_word, need_dword, need_qword } need;
+ gas_assert (!i.suffix);
+ gas_assert (i.reg_operands);
- if (flag_code == CODE_32BIT)
- need = i.prefix[ADDR_PREFIX] ? need_word : need_dword;
+ if (i.tm.operand_types[0].bitfield.instance == Accum
+ || i.operands == 1)
+ {
+ /* The address size override prefix changes the size of the
+ first operand. */
+ if (flag_code == CODE_64BIT
+ && i.op[0].regs->reg_type.bitfield.word)
+ {
+ as_bad (_("16-bit addressing unavailable for `%s'"),
+ i.tm.name);
+ return 0;
+ }
+
+ if ((flag_code == CODE_32BIT
+ ? i.op[0].regs->reg_type.bitfield.word
+ : i.op[0].regs->reg_type.bitfield.dword)
+ && !add_prefix (ADDR_PREFIX_OPCODE))
+ return 0;
+ }
else
{
- if (i.prefix[ADDR_PREFIX])
+ /* Check invalid register operand when the address size override
+ prefix changes the size of register operands. */
+ unsigned int op;
+ enum { need_word, need_dword, need_qword } need;
+
+ if (flag_code == CODE_32BIT)
+ need = i.prefix[ADDR_PREFIX] ? need_word : need_dword;
+ else if (i.prefix[ADDR_PREFIX])
need = need_dword;
else
need = flag_code == CODE_64BIT ? need_qword : need_word;
- }
- for (op = 0; op < i.operands; op++)
- if (i.types[op].bitfield.class == Reg
- && ((need == need_word
- && !i.op[op].regs->reg_type.bitfield.word)
- || (need == need_dword
- && !i.op[op].regs->reg_type.bitfield.dword)
- || (need == need_qword
- && !i.op[op].regs->reg_type.bitfield.qword)))
- {
- as_bad (_("invalid register operand size for `%s'"),
- i.tm.name);
- return 0;
- }
+ for (op = 0; op < i.operands; op++)
+ {
+ if (i.types[op].bitfield.class != Reg)
+ continue;
+
+ switch (need)
+ {
+ case need_word:
+ if (i.op[op].regs->reg_type.bitfield.word)
+ continue;
+ break;
+ case need_dword:
+ if (i.op[op].regs->reg_type.bitfield.dword)
+ continue;
+ break;
+ case need_qword:
+ if (i.op[op].regs->reg_type.bitfield.qword)
+ continue;
+ break;
+ }
+
+ as_bad (_("invalid register operand size for `%s'"),
+ i.tm.name);
+ return 0;
+ }
+ }
}
return 1;
&& i.tm.operand_types[op].bitfield.dword)
{
if (intel_syntax
- && (i.tm.opcode_modifier.toqword
- /* Also convert to QWORD for MOVSXD. */
- || i.tm.base_opcode == 0x63)
+ && i.tm.opcode_modifier.toqword
&& i.types[0].bitfield.class != RegSIMD)
{
/* Convert to QWORD. We want REX byte. */
}
else
{
-duplicate:
+ duplicate:
i.operands++;
i.reg_operands++;
i.tm.operands++;
}
/* If a segment was explicitly specified, and the specified segment
- is not the default, use an opcode prefix to select it. If we
- never figured out what the default segment is, then default_seg
- will be zero at this point, and the specified segment prefix will
- always be used. */
- if ((i.seg[0]) && (i.seg[0] != default_seg))
+ is neither the default nor the one already recorded from a prefix,
+ use an opcode prefix to select it. If we never figured out what
+ the default segment is, then default_seg will be zero at this
+ point, and the specified segment prefix will always be used. */
+ if (i.seg[0]
+ && i.seg[0] != default_seg
+ && i.seg[0]->seg_prefix != i.prefix[SEG_PREFIX])
{
if (!add_prefix (i.seg[0]->seg_prefix))
return 0;
}
/* Return 1 for test, and, cmp, add, sub, inc and dec which may
- be macro-fused with conditional jumps. */
+ be macro-fused with conditional jumps.
+ NB: If TEST/AND/CMP/ADD/SUB/INC/DEC is of RIP relative address,
+ or is one of the following format:
+
+ cmp m, imm
+ add m, imm
+ sub m, imm
+ test m, imm
+ and m, imm
+ inc m
+ dec m
+
+ it is unfusible. */
static int
-maybe_fused_with_jcc_p (void)
+maybe_fused_with_jcc_p (enum mf_cmp_kind* mf_cmp_p)
{
/* No RIP address. */
if (i.base_reg && i.base_reg->reg_num == RegIP)
if (is_any_vex_encoding (&i.tm))
return 0;
- /* and, add, sub with destination register. */
- if ((i.tm.base_opcode >= 0x20 && i.tm.base_opcode <= 0x25)
- || i.tm.base_opcode <= 5
+ /* add, sub without add/sub m, imm. */
+ if (i.tm.base_opcode <= 5
|| (i.tm.base_opcode >= 0x28 && i.tm.base_opcode <= 0x2d)
|| ((i.tm.base_opcode | 3) == 0x83
- && ((i.tm.extension_opcode | 1) == 0x5
+ && (i.tm.extension_opcode == 0x5
|| i.tm.extension_opcode == 0x0)))
- return (i.types[1].bitfield.class == Reg
- || i.types[1].bitfield.instance == Accum);
+ {
+ *mf_cmp_p = mf_cmp_alu_cmp;
+ return !(i.mem_operands && i.imm_operands);
+ }
- /* test, cmp with any register. */
+ /* and without and m, imm. */
+ if ((i.tm.base_opcode >= 0x20 && i.tm.base_opcode <= 0x25)
+ || ((i.tm.base_opcode | 3) == 0x83
+ && i.tm.extension_opcode == 0x4))
+ {
+ *mf_cmp_p = mf_cmp_test_and;
+ return !(i.mem_operands && i.imm_operands);
+ }
+
+ /* test without test m imm. */
if ((i.tm.base_opcode | 1) == 0x85
|| (i.tm.base_opcode | 1) == 0xa9
|| ((i.tm.base_opcode | 1) == 0xf7
- && i.tm.extension_opcode == 0)
- || (i.tm.base_opcode >= 0x38 && i.tm.base_opcode <= 0x3d)
+ && i.tm.extension_opcode == 0))
+ {
+ *mf_cmp_p = mf_cmp_test_and;
+ return !(i.mem_operands && i.imm_operands);
+ }
+
+ /* cmp without cmp m, imm. */
+ if ((i.tm.base_opcode >= 0x38 && i.tm.base_opcode <= 0x3d)
|| ((i.tm.base_opcode | 3) == 0x83
&& (i.tm.extension_opcode == 0x7)))
- return (i.types[0].bitfield.class == Reg
- || i.types[0].bitfield.instance == Accum
- || i.types[1].bitfield.class == Reg
- || i.types[1].bitfield.instance == Accum);
+ {
+ *mf_cmp_p = mf_cmp_alu_cmp;
+ return !(i.mem_operands && i.imm_operands);
+ }
- /* inc, dec with any register. */
+ /* inc, dec without inc/dec m. */
if ((i.tm.cpu_flags.bitfield.cpuno64
&& (i.tm.base_opcode | 0xf) == 0x4f)
|| ((i.tm.base_opcode | 1) == 0xff
&& i.tm.extension_opcode <= 0x1))
- return (i.types[0].bitfield.class == Reg
- || i.types[0].bitfield.instance == Accum);
+ {
+ *mf_cmp_p = mf_cmp_incdec;
+ return !i.mem_operands;
+ }
return 0;
}
/* Return 1 if a FUSED_JCC_PADDING frag should be generated. */
static int
-add_fused_jcc_padding_frag_p (void)
+add_fused_jcc_padding_frag_p (enum mf_cmp_kind* mf_cmp_p)
{
/* NB: Don't work with COND_JUMP86 without i386. */
if (!align_branch_power
|| !(align_branch & align_branch_fused_bit))
return 0;
- if (maybe_fused_with_jcc_p ())
+ if (maybe_fused_with_jcc_p (mf_cmp_p))
{
if (last_insn.kind == last_insn_other
|| last_insn.seg != now_seg)
/* Return 1 if a BRANCH_PADDING frag should be generated. */
static int
-add_branch_padding_frag_p (enum align_branch_kind *branch_p)
+add_branch_padding_frag_p (enum align_branch_kind *branch_p,
+ enum mf_jcc_kind *mf_jcc_p)
{
int add_padding;
}
else
{
+ /* Because J<cc> and JN<cc> share same group in macro-fusible table,
+ igore the lowest bit. */
+ *mf_jcc_p = (i.tm.base_opcode & 0x0e) >> 1;
*branch_p = align_branch_jcc;
if ((align_branch & align_branch_jcc_bit))
add_padding = 1;
offsetT insn_start_off;
fragS *fragP = NULL;
enum align_branch_kind branch = align_branch_none;
+ /* The initializer is arbitrary just to avoid uninitialized error.
+ it's actually either assigned in add_branch_padding_frag_p
+ or never be used. */
+ enum mf_jcc_kind mf_jcc = mf_jcc_jo;
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
if (IS_ELF && x86_used_note)
x86_feature_2_used |= GNU_PROPERTY_X86_FEATURE_2_X87;
if (i.has_regmmx
|| i.tm.base_opcode == 0xf77 /* emms */
- || i.tm.base_opcode == 0xf0e /* femms */)
+ || i.tm.base_opcode == 0xf0e /* femms */
+ || i.tm.base_opcode == 0xf2a /* cvtpi2ps */
+ || i.tm.base_opcode == 0x660f2a /* cvtpi2pd */)
x86_feature_2_used |= GNU_PROPERTY_X86_FEATURE_2_MMX;
if (i.has_regxmm)
x86_feature_2_used |= GNU_PROPERTY_X86_FEATURE_2_XMM;
insn_start_frag = frag_now;
insn_start_off = frag_now_fix ();
- if (add_branch_padding_frag_p (&branch))
+ if (add_branch_padding_frag_p (&branch, &mf_jcc))
{
char *p;
/* Branch can be 8 bytes. Leave some room for prefixes. */
ENCODE_RELAX_STATE (BRANCH_PADDING, 0),
NULL, 0, p);
+ fragP->tc_frag_data.mf_type = mf_jcc;
fragP->tc_frag_data.branch_type = branch;
fragP->tc_frag_data.max_bytes = max_branch_padding_size;
}
unsigned char *q;
unsigned int j;
unsigned int prefix;
+ enum mf_cmp_kind mf_cmp;
if (avoid_fence
&& (i.tm.base_opcode == 0xfaee8
if (branch)
/* Skip if this is a branch. */
;
- else if (add_fused_jcc_padding_frag_p ())
+ else if (add_fused_jcc_padding_frag_p (&mf_cmp))
{
/* Make room for padding. */
frag_grow (MAX_FUSED_JCC_PADDING_SIZE);
ENCODE_RELAX_STATE (FUSED_JCC_PADDING, 0),
NULL, 0, p);
+ fragP->tc_frag_data.mf_type = mf_cmp;
fragP->tc_frag_data.branch_type = align_branch_fused;
fragP->tc_frag_data.max_bytes = MAX_FUSED_JCC_PADDING_SIZE;
}
if (i.prefix[ADDR_PREFIX])
addr_mode = flag_code == CODE_32BIT ? CODE_16BIT : CODE_32BIT;
+ else if (flag_code == CODE_16BIT
+ && current_templates->start->cpu_flags.bitfield.cpumpx
+ /* Avoid replacing the "16-bit addressing not allowed" diagnostic
+ from md_assemble() by "is not a valid base/index expression"
+ when there is a base and/or index. */
+ && !i.types[this_operand].bitfield.baseindex)
+ {
+ /* MPX insn memory operands with neither base nor index must be forced
+ to use 32-bit addressing in 16-bit mode. */
+ addr_mode = CODE_32BIT;
+ i.prefix[ADDR_PREFIX] = ADDR_PREFIX_OPCODE;
+ ++i.prefixes;
+ gas_assert (!i.types[this_operand].bitfield.disp16);
+ gas_assert (!i.types[this_operand].bitfield.disp32);
+ }
else
{
addr_mode = flag_code;
else
return 1;
-bad_address:
+ bad_address:
as_bad (_("`%s' is not a valid %s expression"),
operand_string, kind);
return 0;
}
#endif
+/* Table 3-2. Macro-Fusible Instructions in Haswell Microarchitecture
+ Note also work for Skylake and Cascadelake.
+---------------------------------------------------------------------
+| JCC | ADD/SUB/CMP | INC/DEC | TEST/AND |
+| ------ | ----------- | ------- | -------- |
+| Jo | N | N | Y |
+| Jno | N | N | Y |
+| Jc/Jb | Y | N | Y |
+| Jae/Jnb | Y | N | Y |
+| Je/Jz | Y | Y | Y |
+| Jne/Jnz | Y | Y | Y |
+| Jna/Jbe | Y | N | Y |
+| Ja/Jnbe | Y | N | Y |
+| Js | N | N | Y |
+| Jns | N | N | Y |
+| Jp/Jpe | N | N | Y |
+| Jnp/Jpo | N | N | Y |
+| Jl/Jnge | Y | Y | Y |
+| Jge/Jnl | Y | Y | Y |
+| Jle/Jng | Y | Y | Y |
+| Jg/Jnle | Y | Y | Y |
+--------------------------------------------------------------------- */
+static int
+i386_macro_fusible_p (enum mf_cmp_kind mf_cmp, enum mf_jcc_kind mf_jcc)
+{
+ if (mf_cmp == mf_cmp_alu_cmp)
+ return ((mf_jcc >= mf_jcc_jc && mf_jcc <= mf_jcc_jna)
+ || mf_jcc == mf_jcc_jl || mf_jcc == mf_jcc_jle);
+ if (mf_cmp == mf_cmp_incdec)
+ return (mf_jcc == mf_jcc_je || mf_jcc == mf_jcc_jl
+ || mf_jcc == mf_jcc_jle);
+ if (mf_cmp == mf_cmp_test_and)
+ return 1;
+ return 0;
+}
+
/* Return the next non-empty frag. */
static fragS *
/* Return the next jcc frag after BRANCH_PADDING. */
static fragS *
-i386_next_jcc_frag (fragS *fragP)
+i386_next_fusible_jcc_frag (fragS *maybe_cmp_fragP, fragS *pad_fragP)
{
- if (!fragP)
+ fragS *branch_fragP;
+ if (!pad_fragP)
return NULL;
- if (fragP->fr_type == rs_machine_dependent
- && (TYPE_FROM_RELAX_STATE (fragP->fr_subtype)
+ if (pad_fragP->fr_type == rs_machine_dependent
+ && (TYPE_FROM_RELAX_STATE (pad_fragP->fr_subtype)
== BRANCH_PADDING))
{
- fragP = i386_next_non_empty_frag (fragP);
- if (fragP->fr_type != rs_machine_dependent)
+ branch_fragP = i386_next_non_empty_frag (pad_fragP);
+ if (branch_fragP->fr_type != rs_machine_dependent)
return NULL;
- if (TYPE_FROM_RELAX_STATE (fragP->fr_subtype) == COND_JUMP)
- return fragP;
+ if (TYPE_FROM_RELAX_STATE (branch_fragP->fr_subtype) == COND_JUMP
+ && i386_macro_fusible_p (maybe_cmp_fragP->tc_frag_data.mf_type,
+ pad_fragP->tc_frag_data.mf_type))
+ return branch_fragP;
}
return NULL;
*/
cmp_fragP = i386_next_non_empty_frag (next_fragP);
pad_fragP = i386_next_non_empty_frag (cmp_fragP);
- branch_fragP = i386_next_jcc_frag (pad_fragP);
+ branch_fragP = i386_next_fusible_jcc_frag (next_fragP, pad_fragP);
if (branch_fragP)
{
/* The BRANCH_PADDING frag is merged with the
#define OPTION_MALIGN_BRANCH_PREFIX_SIZE (OPTION_MD_BASE + 28)
#define OPTION_MALIGN_BRANCH (OPTION_MD_BASE + 29)
#define OPTION_MBRANCHES_WITH_32B_BOUNDARIES (OPTION_MD_BASE + 30)
+#define OPTION_MLFENCE_AFTER_LOAD (OPTION_MD_BASE + 31)
+#define OPTION_MLFENCE_BEFORE_INDIRECT_BRANCH (OPTION_MD_BASE + 32)
+#define OPTION_MLFENCE_BEFORE_RET (OPTION_MD_BASE + 33)
struct option md_longopts[] =
{
{"malign-branch-prefix-size", required_argument, NULL, OPTION_MALIGN_BRANCH_PREFIX_SIZE},
{"malign-branch", required_argument, NULL, OPTION_MALIGN_BRANCH},
{"mbranches-within-32B-boundaries", no_argument, NULL, OPTION_MBRANCHES_WITH_32B_BOUNDARIES},
+ {"mlfence-after-load", required_argument, NULL, OPTION_MLFENCE_AFTER_LOAD},
+ {"mlfence-before-indirect-branch", required_argument, NULL,
+ OPTION_MLFENCE_BEFORE_INDIRECT_BRANCH},
+ {"mlfence-before-ret", required_argument, NULL, OPTION_MLFENCE_BEFORE_RET},
{"mamd64", no_argument, NULL, OPTION_MAMD64},
{"mintel64", no_argument, NULL, OPTION_MINTEL64},
{NULL, no_argument, NULL, 0}
as_fatal (_("invalid -mfence-as-lock-add= option: `%s'"), arg);
break;
+ case OPTION_MLFENCE_AFTER_LOAD:
+ if (strcasecmp (arg, "yes") == 0)
+ lfence_after_load = 1;
+ else if (strcasecmp (arg, "no") == 0)
+ lfence_after_load = 0;
+ else
+ as_fatal (_("invalid -mlfence-after-load= option: `%s'"), arg);
+ break;
+
+ case OPTION_MLFENCE_BEFORE_INDIRECT_BRANCH:
+ if (strcasecmp (arg, "all") == 0)
+ {
+ lfence_before_indirect_branch = lfence_branch_all;
+ if (lfence_before_ret == lfence_before_ret_none)
+ lfence_before_ret = lfence_before_ret_shl;
+ }
+ else if (strcasecmp (arg, "memory") == 0)
+ lfence_before_indirect_branch = lfence_branch_memory;
+ else if (strcasecmp (arg, "register") == 0)
+ lfence_before_indirect_branch = lfence_branch_register;
+ else if (strcasecmp (arg, "none") == 0)
+ lfence_before_indirect_branch = lfence_branch_none;
+ else
+ as_fatal (_("invalid -mlfence-before-indirect-branch= option: `%s'"),
+ arg);
+ break;
+
+ case OPTION_MLFENCE_BEFORE_RET:
+ if (strcasecmp (arg, "or") == 0)
+ lfence_before_ret = lfence_before_ret_or;
+ else if (strcasecmp (arg, "not") == 0)
+ lfence_before_ret = lfence_before_ret_not;
+ else if (strcasecmp (arg, "shl") == 0 || strcasecmp (arg, "yes") == 0)
+ lfence_before_ret = lfence_before_ret_shl;
+ else if (strcasecmp (arg, "none") == 0)
+ lfence_before_ret = lfence_before_ret_none;
+ else
+ as_fatal (_("invalid -mlfence-before-ret= option: `%s'"),
+ arg);
+ break;
+
case OPTION_MRELAX_RELOCATIONS:
if (strcasecmp (arg, "yes") == 0)
generate_relax_relocations = 1;
-mbranches-within-32B-boundaries\n\
align branches within 32 byte boundary\n"));
fprintf (stream, _("\
+ -mlfence-after-load=[no|yes] (default: no)\n\
+ generate lfence after load\n"));
+ fprintf (stream, _("\
+ -mlfence-before-indirect-branch=[none|all|register|memory] (default: none)\n\
+ generate lfence before indirect near branch\n"));
+ fprintf (stream, _("\
+ -mlfence-before-ret=[none|or|not|shl|yes] (default: none)\n\
+ generate lfence before ret\n"));
+ fprintf (stream, _("\
-mamd64 accept only AMD64 ISA [default]\n"));
fprintf (stream, _("\
-mintel64 accept only Intel64 ISA\n"));
last_insn.kind = last_insn_directive;
last_insn.name = "constant directive";
last_insn.file = as_where (&last_insn.line);
+ if (lfence_before_ret != lfence_before_ret_none)
+ {
+ if (lfence_before_indirect_branch != lfence_branch_none)
+ as_warn (_("constant directive skips -mlfence-before-ret "
+ "and -mlfence-before-indirect-branch"));
+ else
+ as_warn (_("constant directive skips -mlfence-before-ret"));
+ }
+ else if (lfence_before_indirect_branch != lfence_branch_none)
+ as_warn (_("constant directive skips -mlfence-before-indirect-branch"));
}
}