/* tc-i386.c -- Assemble code for the Intel 80386
- Copyright (C) 1989-2022 Free Software Foundation, Inc.
+ Copyright (C) 1989-2023 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
unsupported_with_intel_mnemonic,
unsupported_syntax,
unsupported,
+ unsupported_on_arch,
+ unsupported_64bit,
invalid_sib_address,
invalid_vsib_address,
invalid_vector_register_set,
explicit segment overrides are given. */
const reg_entry *seg[2];
- /* Copied first memory operand string, for re-checking. */
- char *memop1_string;
-
/* PREFIX holds all the given prefix opcodes (usually null).
PREFIXES is the number of prefix opcodes. */
unsigned int prefixes;
ARCH (generic32, GENERIC32, GENERIC32, false),
ARCH (generic64, GENERIC64, GENERIC64, false),
ARCH (i8086, UNKNOWN, NONE, false),
- ARCH (i186, UNKNOWN, I186, false),
- ARCH (i286, UNKNOWN, I286, false),
- ARCH (i386, I386, I386, false),
- ARCH (i486, I486, I486, false),
- ARCH (i586, PENTIUM, I586, false),
- ARCH (i686, PENTIUMPRO, I686, false),
- ARCH (pentium, PENTIUM, I586, false),
+ ARCH (i186, UNKNOWN, 186, false),
+ ARCH (i286, UNKNOWN, 286, false),
+ ARCH (i386, I386, 386, false),
+ ARCH (i486, I486, 486, false),
+ ARCH (i586, PENTIUM, 586, false),
+ ARCH (i686, PENTIUMPRO, 686, false),
+ ARCH (pentium, PENTIUM, 586, false),
ARCH (pentiumpro, PENTIUMPRO, PENTIUMPRO, false),
ARCH (pentiumii, PENTIUMPRO, P2, false),
ARCH (pentiumiii, PENTIUMPRO, P3, false),
ARCH (btver1, BT, BTVER1, false),
ARCH (btver2, BT, BTVER2, false),
- SUBARCH (8087, 8087, ANY_X87, false),
- SUBARCH (87, NONE, ANY_X87, false), /* Disable only! */
+ SUBARCH (8087, 8087, ANY_8087, false),
+ SUBARCH (87, NONE, ANY_8087, false), /* Disable only! */
SUBARCH (287, 287, ANY_287, false),
SUBARCH (387, 387, ANY_387, false),
SUBARCH (687, 687, ANY_687, false),
- SUBARCH (cmov, CMOV, ANY_CMOV, false),
+ SUBARCH (cmov, CMOV, CMOV, false),
SUBARCH (fxsr, FXSR, ANY_FXSR, false),
SUBARCH (mmx, MMX, ANY_MMX, false),
SUBARCH (sse, SSE, ANY_SSE, false),
SUBARCH (avx512dq, AVX512DQ, ANY_AVX512DQ, false),
SUBARCH (avx512bw, AVX512BW, ANY_AVX512BW, false),
SUBARCH (avx512vl, AVX512VL, ANY_AVX512VL, false),
- SUBARCH (vmx, VMX, VMX, false),
- SUBARCH (vmfunc, VMFUNC, VMFUNC, false),
+ SUBARCH (vmx, VMX, ANY_VMX, false),
+ SUBARCH (vmfunc, VMFUNC, ANY_VMFUNC, false),
SUBARCH (smx, SMX, SMX, false),
- SUBARCH (xsave, XSAVE, XSAVE, false),
- SUBARCH (xsaveopt, XSAVEOPT, XSAVEOPT, false),
- SUBARCH (xsavec, XSAVEC, XSAVEC, false),
- SUBARCH (xsaves, XSAVES, XSAVES, false),
- SUBARCH (aes, AES, AES, false),
- SUBARCH (pclmul, PCLMUL, PCLMUL, false),
- SUBARCH (clmul, PCLMUL, PCLMUL, true),
+ SUBARCH (xsave, XSAVE, ANY_XSAVE, false),
+ SUBARCH (xsaveopt, XSAVEOPT, ANY_XSAVEOPT, false),
+ SUBARCH (xsavec, XSAVEC, ANY_XSAVEC, false),
+ SUBARCH (xsaves, XSAVES, ANY_XSAVES, false),
+ SUBARCH (aes, AES, ANY_AES, false),
+ SUBARCH (pclmul, PCLMUL, ANY_PCLMUL, false),
+ SUBARCH (clmul, PCLMUL, ANY_PCLMUL, true),
SUBARCH (fsgsbase, FSGSBASE, FSGSBASE, false),
SUBARCH (rdrnd, RDRND, RDRND, false),
- SUBARCH (f16c, F16C, F16C, false),
+ SUBARCH (f16c, F16C, ANY_F16C, false),
SUBARCH (bmi2, BMI2, BMI2, false),
- SUBARCH (fma, FMA, FMA, false),
- SUBARCH (fma4, FMA4, FMA4, false),
- SUBARCH (xop, XOP, XOP, false),
- SUBARCH (lwp, LWP, LWP, false),
+ SUBARCH (fma, FMA, ANY_FMA, false),
+ SUBARCH (fma4, FMA4, ANY_FMA4, false),
+ SUBARCH (xop, XOP, ANY_XOP, false),
+ SUBARCH (lwp, LWP, ANY_LWP, false),
SUBARCH (movbe, MOVBE, MOVBE, false),
SUBARCH (cx16, CX16, CX16, false),
- SUBARCH (ept, EPT, EPT, false),
+ SUBARCH (ept, EPT, ANY_EPT, false),
SUBARCH (lzcnt, LZCNT, LZCNT, false),
SUBARCH (popcnt, POPCNT, POPCNT, false),
SUBARCH (hle, HLE, HLE, false),
- SUBARCH (rtm, RTM, RTM, false),
+ SUBARCH (rtm, RTM, ANY_RTM, false),
+ SUBARCH (tsx, TSX, TSX, false),
SUBARCH (invpcid, INVPCID, INVPCID, false),
SUBARCH (clflush, CLFLUSH, CLFLUSH, false),
SUBARCH (nop, NOP, NOP, false),
SUBARCH (syscall, SYSCALL, SYSCALL, false),
SUBARCH (rdtscp, RDTSCP, RDTSCP, false),
- SUBARCH (3dnow, 3DNOW, 3DNOW, false),
- SUBARCH (3dnowa, 3DNOWA, 3DNOWA, false),
+ SUBARCH (3dnow, 3DNOW, ANY_3DNOW, false),
+ SUBARCH (3dnowa, 3DNOWA, ANY_3DNOWA, false),
SUBARCH (padlock, PADLOCK, PADLOCK, false),
- SUBARCH (pacifica, SVME, SVME, true),
- SUBARCH (svme, SVME, SVME, false),
+ SUBARCH (pacifica, SVME, ANY_SVME, true),
+ SUBARCH (svme, SVME, ANY_SVME, false),
SUBARCH (abm, ABM, ABM, false),
SUBARCH (bmi, BMI, BMI, false),
SUBARCH (tbm, TBM, TBM, false),
SUBARCH (rdseed, RDSEED, RDSEED, false),
SUBARCH (prfchw, PRFCHW, PRFCHW, false),
SUBARCH (smap, SMAP, SMAP, false),
- SUBARCH (mpx, MPX, MPX, false),
- SUBARCH (sha, SHA, SHA, false),
+ SUBARCH (mpx, MPX, ANY_MPX, false),
+ SUBARCH (sha, SHA, ANY_SHA, false),
SUBARCH (clflushopt, CLFLUSHOPT, CLFLUSHOPT, false),
SUBARCH (prefetchwt1, PREFETCHWT1, PREFETCHWT1, false),
SUBARCH (se1, SE1, SE1, false),
SUBARCH (avx_vnni, AVX_VNNI, ANY_AVX_VNNI, false),
SUBARCH (clzero, CLZERO, CLZERO, false),
SUBARCH (mwaitx, MWAITX, MWAITX, false),
- SUBARCH (ospke, OSPKE, OSPKE, false),
+ SUBARCH (ospke, OSPKE, ANY_OSPKE, false),
SUBARCH (rdpid, RDPID, RDPID, false),
SUBARCH (ptwrite, PTWRITE, PTWRITE, false),
- SUBARCH (ibt, IBT, ANY_IBT, false),
- SUBARCH (shstk, SHSTK, ANY_SHSTK, false),
- SUBARCH (gfni, GFNI, GFNI, false),
- SUBARCH (vaes, VAES, VAES, false),
- SUBARCH (vpclmulqdq, VPCLMULQDQ, VPCLMULQDQ, false),
+ SUBARCH (ibt, IBT, IBT, false),
+ SUBARCH (shstk, SHSTK, SHSTK, false),
+ SUBARCH (gfni, GFNI, ANY_GFNI, false),
+ SUBARCH (vaes, VAES, ANY_VAES, false),
+ SUBARCH (vpclmulqdq, VPCLMULQDQ, ANY_VPCLMULQDQ, false),
SUBARCH (wbnoinvd, WBNOINVD, WBNOINVD, false),
SUBARCH (pconfig, PCONFIG, PCONFIG, false),
SUBARCH (waitpkg, WAITPKG, WAITPKG, false),
SUBARCH (cldemote, CLDEMOTE, CLDEMOTE, false),
SUBARCH (amx_int8, AMX_INT8, ANY_AMX_INT8, false),
SUBARCH (amx_bf16, AMX_BF16, ANY_AMX_BF16, false),
- SUBARCH (amx_fp16, AMX_FP16, AMX_FP16, false),
+ SUBARCH (amx_fp16, AMX_FP16, ANY_AMX_FP16, false),
SUBARCH (amx_tile, AMX_TILE, ANY_AMX_TILE, false),
- SUBARCH (movdiri, MOVDIRI, ANY_MOVDIRI, false),
- SUBARCH (movdir64b, MOVDIR64B, ANY_MOVDIR64B, false),
+ SUBARCH (movdiri, MOVDIRI, MOVDIRI, false),
+ SUBARCH (movdir64b, MOVDIR64B, MOVDIR64B, false),
SUBARCH (avx512_bf16, AVX512_BF16, ANY_AVX512_BF16, false),
SUBARCH (avx512_vp2intersect, AVX512_VP2INTERSECT,
ANY_AVX512_VP2INTERSECT, false),
- SUBARCH (tdx, TDX, ANY_TDX, false),
- SUBARCH (enqcmd, ENQCMD, ANY_ENQCMD, false),
- SUBARCH (serialize, SERIALIZE, ANY_SERIALIZE, false),
+ SUBARCH (tdx, TDX, TDX, false),
+ SUBARCH (enqcmd, ENQCMD, ENQCMD, false),
+ SUBARCH (serialize, SERIALIZE, SERIALIZE, false),
SUBARCH (rdpru, RDPRU, RDPRU, false),
SUBARCH (mcommit, MCOMMIT, MCOMMIT, false),
- SUBARCH (sev_es, SEV_ES, SEV_ES, false),
+ SUBARCH (sev_es, SEV_ES, ANY_SEV_ES, false),
SUBARCH (tsxldtrk, TSXLDTRK, ANY_TSXLDTRK, false),
SUBARCH (kl, KL, ANY_KL, false),
SUBARCH (widekl, WIDEKL, ANY_WIDEKL, false),
- SUBARCH (uintr, UINTR, ANY_UINTR, false),
- SUBARCH (hreset, HRESET, ANY_HRESET, false),
+ SUBARCH (uintr, UINTR, UINTR, false),
+ SUBARCH (hreset, HRESET, HRESET, false),
SUBARCH (avx512_fp16, AVX512_FP16, ANY_AVX512_FP16, false),
SUBARCH (prefetchi, PREFETCHI, PREFETCHI, false),
SUBARCH (avx_ifma, AVX_IFMA, ANY_AVX_IFMA, false),
SUBARCH (avx_vnni_int8, AVX_VNNI_INT8, ANY_AVX_VNNI_INT8, false),
- SUBARCH (cmpccxadd, CMPCCXADD, ANY_CMPCCXADD, false),
- SUBARCH (wrmsrns, WRMSRNS, ANY_WRMSRNS, false),
- SUBARCH (msrlist, MSRLIST, ANY_MSRLIST, false),
+ SUBARCH (cmpccxadd, CMPCCXADD, CMPCCXADD, false),
+ SUBARCH (wrmsrns, WRMSRNS, WRMSRNS, false),
+ SUBARCH (msrlist, MSRLIST, MSRLIST, false),
SUBARCH (avx_ne_convert, AVX_NE_CONVERT, ANY_AVX_NE_CONVERT, false),
- SUBARCH (rao_int, RAO_INT, ANY_RAO_INT, false),
- SUBARCH (rmpquery, RMPQUERY, RMPQUERY, false),
+ SUBARCH (rao_int, RAO_INT, RAO_INT, false),
+ SUBARCH (rmpquery, RMPQUERY, ANY_RMPQUERY, false),
};
#undef SUBARCH
|| (i.types[given].bitfield.dword
&& !t->operand_types[wanted].bitfield.dword)
|| (i.types[given].bitfield.qword
- && !t->operand_types[wanted].bitfield.qword)
+ && (!t->operand_types[wanted].bitfield.qword
+ /* Don't allow 64-bit (memory) operands outside of 64-bit
+ mode, when they're used where a 64-bit GPR could also
+ be used. Checking is needed for Intel Syntax only. */
+ || (intel_syntax
+ && flag_code != CODE_64BIT
+ && (t->operand_types[wanted].bitfield.class == Reg
+ || t->operand_types[wanted].bitfield.class == Accum
+ || t->opcode_modifier.isstring))))
|| (i.types[given].bitfield.tbyte
&& !t->operand_types[wanted].bitfield.tbyte));
}
return val & mask;
}
+static INLINE const char *insn_name (const insn_template *t)
+{
+ return t->name;
+}
+
enum PREFIX_GROUP
{
PREFIX_EXIST = 0,
/* Type checks to compensate for the conversion through void * which
occurs during hash table insertion / lookup. */
- (void)(sets == ¤t_templates->start);
- (void)(end == ¤t_templates->end);
+ (void) sizeof (sets == ¤t_templates->start);
+ (void) sizeof (end == ¤t_templates->end);
for (; sets < end; ++sets)
- if (str_hash_insert (op_hash, (*sets)->name, sets, 0))
- as_fatal (_("duplicate %s"), (*sets)->name);
+ if (str_hash_insert (op_hash, insn_name (*sets), sets, 0))
+ as_fatal (_("duplicate %s"), insn_name (*sets));
}
/* Initialize reg_hash hash table. */
if (diag)
as_warn (_("ambiguous broadcast for `%s', using %u-bit form"),
- t->name, bytes * 8);
+ insn_name (t), bytes * 8);
return bytes;
}
case PrefixNoTrack:
case PrefixRep:
as_bad (_("invalid instruction `%s' after `%s'"),
- i.tm.name, i.hle_prefix);
+ insn_name (&i.tm), i.hle_prefix);
return 0;
case PrefixHLELock:
if (i.prefix[LOCK_PREFIX])
if (i.prefix[HLE_PREFIX] != XRELEASE_PREFIX_OPCODE)
{
as_bad (_("instruction `%s' after `xacquire' not allowed"),
- i.tm.name);
+ insn_name (&i.tm));
return 0;
}
if (i.mem_operands == 0 || !(i.flags[i.operands - 1] & Operand_Mem))
{
as_bad (_("memory destination needed for instruction `%s'"
- " after `xrelease'"), i.tm.name);
+ " after `xrelease'"), insn_name (&i.tm));
return 0;
}
return 1;
movq $imm31, %r64 -> movl $imm31, %r32
movq $imm32, %r64 -> movl $imm32, %r32
*/
- i.tm.opcode_modifier.norex64 = 1;
+ i.tm.opcode_modifier.size = SIZE32;
+ if (i.imm_operands)
+ {
+ i.types[0].bitfield.imm32 = 1;
+ i.types[0].bitfield.imm32s = 0;
+ i.types[0].bitfield.imm64 = 0;
+ }
+ else
+ {
+ i.types[0].bitfield.dword = 1;
+ i.types[0].bitfield.qword = 0;
+ }
+ i.types[1].bitfield.dword = 1;
+ i.types[1].bitfield.qword = 0;
if (i.tm.base_opcode == 0xb8 || (i.tm.base_opcode | 1) == 0xc7)
{
/* Handle
i.tm.operand_types[0].bitfield.imm32 = 1;
i.tm.operand_types[0].bitfield.imm32s = 0;
i.tm.operand_types[0].bitfield.imm64 = 0;
- i.types[0].bitfield.imm32 = 1;
- i.types[0].bitfield.imm32s = 0;
- i.types[0].bitfield.imm64 = 0;
- i.types[1].bitfield.dword = 1;
- i.types[1].bitfield.qword = 0;
if ((i.tm.base_opcode | 1) == 0xc7)
{
/* Handle
return 0;
/* pop. */
- if (strcmp (i.tm.name, "pop") == 0)
+ if (strcmp (insn_name (&i.tm), "pop") == 0)
return 1;
}
&& i.prefix[REP_PREFIX])
{
as_warn (_("`%s` changes flags which would affect control flow behavior"),
- i.tm.name);
+ insn_name (&i.tm));
}
char *p = frag_more (3);
*p++ = 0xf;
&& lfence_before_indirect_branch != lfence_branch_register)
{
as_warn (_("indirect `%s` with memory operand should be avoided"),
- i.tm.name);
+ insn_name (&i.tm));
return;
}
else
{
as_warn_where (last_insn.file, last_insn.line,
_("`%s` skips -mlfence-before-indirect-branch on `%s`"),
- last_insn.name, i.tm.name);
+ last_insn.name, insn_name (&i.tm));
return;
}
{
as_warn_where (last_insn.file, last_insn.line,
_("`%s` skips -mlfence-before-ret on `%s`"),
- last_insn.name, i.tm.name);
+ last_insn.name, insn_name (&i.tm));
return;
}
}
}
+/* 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
+ acceptable that this will lead to unnecessary pass 2 preparations in a
+ limited set of cases. */
+static INLINE bool may_need_pass2 (const insn_template *t)
+{
+ return t->opcode_modifier.sse2avx
+ /* Note that all SSE2AVX templates have at least one operand. */
+ ? t->operand_types[t->operands - 1].bitfield.class == RegSIMD
+ : (t->opcode_modifier.opcodespace == SPACE_0F
+ && (t->base_opcode | 1) == 0xbf)
+ || (t->opcode_modifier.opcodespace == SPACE_BASE
+ && t->base_opcode == 0x63);
+}
+
/* 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. */
md_assemble (char *line)
{
unsigned int j;
- char mnemonic[MAX_MNEM_SIZE], mnem_suffix;
- const char *end;
+ char mnemonic[MAX_MNEM_SIZE], mnem_suffix = 0, *copy = NULL;
+ const char *end, *pass1_mnem = NULL;
+ enum i386_error pass1_err = 0;
const insn_template *t;
/* Initialize globals. */
+ current_templates = NULL;
+ retry:
memset (&i, '\0', sizeof (i));
i.rounding.type = rc_none;
for (j = 0; j < MAX_OPERANDS; j++)
end = parse_insn (line, mnemonic);
if (end == NULL)
- return;
+ {
+ if (pass1_mnem != NULL)
+ goto match_error;
+ if (i.error != no_error)
+ {
+ gas_assert (current_templates != NULL);
+ if (may_need_pass2 (current_templates->start) && !i.suffix)
+ goto no_match;
+ /* No point in trying a 2nd pass - it'll only find the same suffix
+ again. */
+ mnem_suffix = i.suffix;
+ goto match_error;
+ }
+ return;
+ }
+ if (may_need_pass2 (current_templates->start))
+ {
+ /* Make a copy of the full line in case we need to retry. */
+ copy = xstrdup (line);
+ }
line += end - line;
mnem_suffix = i.suffix;
line = parse_operands (line, mnemonic);
this_operand = -1;
- xfree (i.memop1_string);
- i.memop1_string = NULL;
if (line == NULL)
- return;
+ {
+ free (copy);
+ return;
+ }
/* Now we've parsed the mnemonic into a set of templates, and have the
operands at hand. */
with the template operand types. */
if (!(t = match_template (mnem_suffix)))
- return;
+ {
+ const char *err_msg;
+
+ if (copy && !mnem_suffix)
+ {
+ line = copy;
+ copy = NULL;
+ no_match:
+ pass1_err = i.error;
+ pass1_mnem = insn_name (current_templates->start);
+ goto retry;
+ }
+
+ /* If a non-/only-64bit template (group) was found in pass 1, and if
+ _some_ template (group) was found in pass 2, squash pass 1's
+ error. */
+ if (pass1_err == unsupported_64bit)
+ pass1_mnem = NULL;
+
+ match_error:
+ free (copy);
+
+ switch (pass1_mnem ? pass1_err : i.error)
+ {
+ default:
+ abort ();
+ case operand_size_mismatch:
+ err_msg = _("operand size mismatch");
+ break;
+ case operand_type_mismatch:
+ err_msg = _("operand type mismatch");
+ break;
+ case register_type_mismatch:
+ err_msg = _("register type mismatch");
+ break;
+ case number_of_operands_mismatch:
+ err_msg = _("number of operands mismatch");
+ break;
+ case invalid_instruction_suffix:
+ err_msg = _("invalid instruction suffix");
+ break;
+ case bad_imm4:
+ err_msg = _("constant doesn't fit in 4 bits");
+ break;
+ case unsupported_with_intel_mnemonic:
+ err_msg = _("unsupported with Intel mnemonic");
+ break;
+ case unsupported_syntax:
+ err_msg = _("unsupported syntax");
+ break;
+ case unsupported:
+ as_bad (_("unsupported instruction `%s'"),
+ pass1_mnem ? pass1_mnem : insn_name (current_templates->start));
+ return;
+ case unsupported_on_arch:
+ as_bad (_("`%s' is not supported on `%s%s'"),
+ pass1_mnem ? pass1_mnem : insn_name (current_templates->start),
+ cpu_arch_name ? cpu_arch_name : default_arch,
+ cpu_sub_arch_name ? cpu_sub_arch_name : "");
+ return;
+ case unsupported_64bit:
+ if (ISLOWER (mnem_suffix))
+ {
+ if (flag_code == CODE_64BIT)
+ as_bad (_("`%s%c' is not supported in 64-bit mode"),
+ pass1_mnem ? pass1_mnem : insn_name (current_templates->start),
+ mnem_suffix);
+ else
+ as_bad (_("`%s%c' is only supported in 64-bit mode"),
+ pass1_mnem ? pass1_mnem : insn_name (current_templates->start),
+ mnem_suffix);
+ }
+ else
+ {
+ if (flag_code == CODE_64BIT)
+ as_bad (_("`%s' is not supported in 64-bit mode"),
+ pass1_mnem ? pass1_mnem : insn_name (current_templates->start));
+ else
+ as_bad (_("`%s' is only supported in 64-bit mode"),
+ pass1_mnem ? pass1_mnem : insn_name (current_templates->start));
+ }
+ return;
+ case invalid_sib_address:
+ err_msg = _("invalid SIB address");
+ break;
+ case invalid_vsib_address:
+ err_msg = _("invalid VSIB address");
+ break;
+ case invalid_vector_register_set:
+ err_msg = _("mask, index, and destination registers must be distinct");
+ break;
+ case invalid_tmm_register_set:
+ err_msg = _("all tmm registers must be distinct");
+ break;
+ case invalid_dest_and_src_register_set:
+ err_msg = _("destination and source registers must be distinct");
+ break;
+ case unsupported_vector_index_register:
+ err_msg = _("unsupported vector index register");
+ break;
+ case unsupported_broadcast:
+ err_msg = _("unsupported broadcast");
+ break;
+ case broadcast_needed:
+ err_msg = _("broadcast is needed for operand of such type");
+ break;
+ case unsupported_masking:
+ err_msg = _("unsupported masking");
+ break;
+ case mask_not_on_destination:
+ err_msg = _("mask not on destination operand");
+ break;
+ case no_default_mask:
+ err_msg = _("default mask isn't allowed");
+ break;
+ case unsupported_rc_sae:
+ err_msg = _("unsupported static rounding/sae");
+ break;
+ case invalid_register_operand:
+ err_msg = _("invalid register operand");
+ break;
+ }
+ as_bad (_("%s for `%s'"), err_msg,
+ pass1_mnem ? pass1_mnem : insn_name (current_templates->start));
+ return;
+ }
+
+ free (copy);
if (sse_check != check_none
/* The opcode space check isn't strictly needed; it's there only to
if (j >= t->operands && simd)
(sse_check == check_warning
? as_warn
- : as_bad) (_("SSE instruction `%s' is used"), i.tm.name);
+ : as_bad) (_("SSE instruction `%s' is used"), insn_name (&i.tm));
}
if (i.tm.opcode_modifier.fwait)
if (i.rep_prefix && i.tm.opcode_modifier.prefixok != PrefixRep)
{
as_bad (_("invalid instruction `%s' after `%s'"),
- i.tm.name, i.rep_prefix);
+ insn_name (&i.tm), i.rep_prefix);
return;
}
return;
}
- /* Check for data size prefix on VEX/XOP/EVEX encoded and SIMD insns. */
- if (i.prefix[DATA_PREFIX]
- && (is_any_vex_encoding (&i.tm)
- || i.tm.operand_types[i.imm_operands].bitfield.class >= RegMMX
- || i.tm.operand_types[i.imm_operands + 1].bitfield.class >= RegMMX))
+ if (is_any_vex_encoding (&i.tm)
+ || i.tm.operand_types[i.imm_operands].bitfield.class >= RegMMX
+ || i.tm.operand_types[i.imm_operands + 1].bitfield.class >= RegMMX)
{
- as_bad (_("data size prefix invalid with `%s'"), i.tm.name);
- return;
+ /* Check for data size prefix on VEX/XOP/EVEX encoded and SIMD insns. */
+ if (i.prefix[DATA_PREFIX])
+ {
+ as_bad (_("data size prefix invalid with `%s'"), insn_name (&i.tm));
+ return;
+ }
+
+ /* Don't allow e.g. KMOV in TLS code sequences. */
+ for (j = i.imm_operands; j < i.operands; ++j)
+ switch (i.reloc[j])
+ {
+ case BFD_RELOC_386_TLS_GOTIE:
+ case BFD_RELOC_386_TLS_LE_32:
+ case BFD_RELOC_X86_64_GOTTPOFF:
+ case BFD_RELOC_X86_64_TLSLD:
+ as_bad (_("TLS relocation cannot be used with `%s'"), insn_name (&i.tm));
+ return;
+ default:
+ break;
+ }
}
/* Check if HLE prefix is OK. */
|| i.tm.opcode_modifier.opcodespace != SPACE_BASE))
{
as_bad (_("input/output port address isn't allowed with `%s'"),
- i.tm.name);
+ insn_name (&i.tm));
return;
}
/* Check if IP-relative addressing requirements can be satisfied. */
if (i.tm.cpu_flags.bitfield.cpuprefetchi
&& !(i.base_reg && i.base_reg->reg_num == RegIP))
- as_warn (_("'%s' only supports RIP-relative address"), i.tm.name);
+ as_warn (_("'%s' only supports RIP-relative address"), insn_name (&i.tm));
/* Update operand types and check extended states. */
for (j = 0; j < i.operands; j++)
else if (!quiet_warnings && i.tm.opcode_modifier.operandconstraint == UGH)
{
/* UnixWare fsub no args is alias for fsubp, fadd -> faddp, etc. */
- as_warn (_("translating to `%sp'"), i.tm.name);
+ as_warn (_("translating to `%sp'"), insn_name (&i.tm));
}
if (is_any_vex_encoding (&i.tm))
if (!cpu_arch_flags.bitfield.cpui286)
{
as_bad (_("instruction `%s' isn't supported outside of protected mode."),
- i.tm.name);
+ insn_name (&i.tm));
return;
}
/* Check for explicit REX prefix. */
if (i.prefix[REX_PREFIX] || i.rex_encoding)
{
- as_bad (_("REX prefix invalid with `%s'"), i.tm.name);
+ as_bad (_("REX prefix invalid with `%s'"), insn_name (&i.tm));
return;
}
if (i.tm.opcode_modifier.isprefix)
{
last_insn.kind = last_insn_prefix;
- last_insn.name = i.tm.name;
+ last_insn.name = insn_name (&i.tm);
last_insn.file = as_where (&last_insn.line);
}
else
last_insn.kind = last_insn_other;
}
+/* The Q suffix is generally valid only in 64-bit mode, with very few
+ exceptions: fild, fistp, fisttp, and cmpxchg8b. Note that for fild
+ and fisttp only one of their two templates is matched below: That's
+ sufficient since other relevant attributes are the same between both
+ respective templates. */
+static INLINE bool q_suffix_allowed(const insn_template *t)
+{
+ return flag_code == CODE_64BIT
+ || (t->opcode_modifier.opcodespace == SPACE_BASE
+ && t->base_opcode == 0xdf
+ && (t->extension_opcode & 1)) /* fild / fistp / fisttp */
+ || (t->opcode_modifier.opcodespace == SPACE_0F
+ && t->base_opcode == 0xc7
+ && t->opcode_modifier.opcodeprefix == PREFIX_NONE
+ && t->extension_opcode == 1) /* cmpxchg8b */;
+}
+
static const char *
parse_insn (const char *line, char *mnemonic)
{
const char *l = line, *token_start = l;
char *mnem_p;
+ bool pass1 = !current_templates;
int supported;
const insn_template *t;
char *dot_p = NULL;
as_bad ((flag_code != CODE_64BIT
? _("`%s' is only supported in 64-bit mode")
: _("`%s' is not supported in 64-bit mode")),
- current_templates->start->name);
+ insn_name (current_templates->start));
return NULL;
}
/* If we are in 16-bit mode, do not allow addr16 or data16.
^ (flag_code == CODE_16BIT)))
{
as_bad (_("redundant %s prefix"),
- current_templates->start->name);
+ insn_name (current_templates->start));
return NULL;
}
return NULL;
case PREFIX_DS:
if (current_templates->start->cpu_flags.bitfield.cpuibt)
- i.notrack_prefix = current_templates->start->name;
+ i.notrack_prefix = insn_name (current_templates->start);
break;
case PREFIX_REP:
if (current_templates->start->cpu_flags.bitfield.cpuhle)
- i.hle_prefix = current_templates->start->name;
+ i.hle_prefix = insn_name (current_templates->start);
else if (current_templates->start->cpu_flags.bitfield.cpumpx)
- i.bnd_prefix = current_templates->start->name;
+ i.bnd_prefix = insn_name (current_templates->start);
else
- i.rep_prefix = current_templates->start->name;
+ i.rep_prefix = insn_name (current_templates->start);
break;
default:
break;
current_templates = (const templates *) str_hash_find (op_hash, mnemonic);
}
- if (!current_templates)
+ if (!current_templates || !pass1)
{
+ current_templates = NULL;
+
check_suffix:
if (mnem_p > mnemonic)
{
current_templates
= (const templates *) str_hash_find (op_hash, mnemonic);
}
+ /* For compatibility reasons accept MOVSD and CMPSD without
+ operands even in AT&T mode. */
+ else if (*l == END_OF_INSN
+ || (is_space_char (*l) && l[1] == END_OF_INSN))
+ {
+ mnem_p[-1] = '\0';
+ current_templates
+ = (const templates *) str_hash_find (op_hash, mnemonic);
+ if (current_templates != NULL
+ /* MOVS or CMPS */
+ && (current_templates->start->base_opcode | 2) == 0xa6
+ && current_templates->start->opcode_modifier.opcodespace
+ == SPACE_BASE
+ && mnem_p[-2] == 's')
+ {
+ as_warn (_("found `%sd'; assuming `%sl' was meant"),
+ mnemonic, mnemonic);
+ i.suffix = LONG_MNEM_SUFFIX;
+ }
+ else
+ {
+ current_templates = NULL;
+ mnem_p[-1] = 'd';
+ }
+ }
break;
}
}
if (!current_templates)
{
- as_bad (_("no such instruction: `%s'"), token_start);
+ if (pass1)
+ as_bad (_("no such instruction: `%s'"), token_start);
return NULL;
}
}
for (t = current_templates->start; t < current_templates->end; ++t)
{
supported |= cpu_flags_match (t);
+
+ if (i.suffix == QWORD_MNEM_SUFFIX && !q_suffix_allowed (t))
+ supported &= ~CPU_FLAGS_64BIT_MATCH;
+
if (supported == CPU_FLAGS_PERFECT_MATCH)
return l;
}
- if (!(supported & CPU_FLAGS_64BIT_MATCH))
- as_bad (flag_code == CODE_64BIT
- ? _("`%s' is not supported in 64-bit mode")
- : _("`%s' is only supported in 64-bit mode"),
- current_templates->start->name);
- else
- as_bad (_("`%s' is not supported on `%s%s'"),
- current_templates->start->name,
- cpu_arch_name ? cpu_arch_name : default_arch,
- cpu_sub_arch_name ? cpu_sub_arch_name : "");
+ if (pass1)
+ {
+ if (supported & CPU_FLAGS_64BIT_MATCH)
+ i.error = unsupported_on_arch;
+ else
+ i.error = unsupported_64bit;
+ }
return NULL;
}
if (operand_type_all_zero (&overlap))
goto bad_broadcast;
- if (t->opcode_modifier.checkregsize)
+ if (t->opcode_modifier.checkoperandsize)
{
unsigned int j;
for (j = 0; j < MAX_OPERANDS; j++)
operand_types[j] = t->operand_types[j];
- /* In general, don't allow
- - 64-bit operands outside of 64-bit mode,
- - 32-bit operands on pre-386. */
+ /* In general, don't allow 32-bit operands on pre-386. */
specific_error = progress (mnem_suffix ? invalid_instruction_suffix
: operand_size_mismatch);
j = i.imm_operands + (t->operands > i.imm_operands + 1);
- if (((i.suffix == QWORD_MNEM_SUFFIX
- && flag_code != CODE_64BIT
- && !(t->opcode_modifier.opcodespace == SPACE_0F
- && t->base_opcode == 0xc7
- && t->opcode_modifier.opcodeprefix == PREFIX_NONE
- && t->extension_opcode == 1) /* cmpxchg8b */)
- || (i.suffix == LONG_MNEM_SUFFIX
- && !cpu_arch_flags.bitfield.cpui386))
+ if (i.suffix == LONG_MNEM_SUFFIX
+ && !cpu_arch_flags.bitfield.cpui386
&& (intel_syntax
? (t->opcode_modifier.mnemonicsize != IGNORESIZE
- && !intel_float_operand (t->name))
- : intel_float_operand (t->name) != 2)
+ && !intel_float_operand (insn_name (t)))
+ : intel_float_operand (insn_name (t)) != 2)
&& (t->operands == i.imm_operands
|| (operand_types[i.imm_operands].bitfield.class != RegMMX
&& operand_types[i.imm_operands].bitfield.class != RegSIMD
}
}
- switch (i.reloc[0])
- {
- case BFD_RELOC_386_GOT32:
- /* Force 0x8b encoding for "mov foo@GOT, %eax". */
- if (t->base_opcode == 0xa0
- && t->opcode_modifier.opcodespace == SPACE_BASE)
- continue;
- break;
- case BFD_RELOC_386_TLS_GOTIE:
- case BFD_RELOC_386_TLS_LE_32:
- case BFD_RELOC_X86_64_GOTTPOFF:
- case BFD_RELOC_X86_64_TLSLD:
- /* Don't allow KMOV in TLS code sequences. */
- if (t->opcode_modifier.vex)
- continue;
- break;
- default:
- break;
- }
-
/* We check register size if needed. */
- if (t->opcode_modifier.checkregsize)
+ if (t->opcode_modifier.checkoperandsize)
{
check_register = (1 << t->operands) - 1;
if (i.broadcast.type || i.broadcast.bytes)
&& i.types[0].bitfield.dword
&& i.types[1].bitfield.instance == Accum)
continue;
- /* xrelease mov %eax, <disp> is another special case. It must not
- match the accumulator-only encoding of mov. */
- if (flag_code != CODE_64BIT
- && i.hle_prefix
- && t->base_opcode == 0xa0
- && t->opcode_modifier.opcodespace == SPACE_BASE
- && i.types[0].bitfield.instance == Accum
- && (i.flags[1] & Operand_Mem))
- continue;
+
+ if (t->base_opcode == MOV_AX_DISP32
+ && t->opcode_modifier.opcodespace == SPACE_BASE)
+ {
+ /* Force 0x8b encoding for "mov foo@GOT, %eax". */
+ if (i.reloc[0] == BFD_RELOC_386_GOT32)
+ continue;
+
+ /* xrelease mov %eax, <disp> is another special case. It must not
+ match the accumulator-only encoding of mov. */
+ if (i.hle_prefix)
+ continue;
+ }
/* Fall through. */
case 3:
if (t == current_templates->end)
{
/* We found no match. */
- const char *err_msg;
- switch (specific_error)
- {
- default:
- abort ();
- case operand_size_mismatch:
- err_msg = _("operand size mismatch");
- break;
- case operand_type_mismatch:
- err_msg = _("operand type mismatch");
- break;
- case register_type_mismatch:
- err_msg = _("register type mismatch");
- break;
- case number_of_operands_mismatch:
- err_msg = _("number of operands mismatch");
- break;
- case invalid_instruction_suffix:
- err_msg = _("invalid instruction suffix");
- break;
- case bad_imm4:
- err_msg = _("constant doesn't fit in 4 bits");
- break;
- case unsupported_with_intel_mnemonic:
- err_msg = _("unsupported with Intel mnemonic");
- break;
- case unsupported_syntax:
- err_msg = _("unsupported syntax");
- break;
- case unsupported:
- as_bad (_("unsupported instruction `%s'"),
- current_templates->start->name);
- return NULL;
- case invalid_sib_address:
- err_msg = _("invalid SIB address");
- break;
- case invalid_vsib_address:
- err_msg = _("invalid VSIB address");
- break;
- case invalid_vector_register_set:
- err_msg = _("mask, index, and destination registers must be distinct");
- break;
- case invalid_tmm_register_set:
- err_msg = _("all tmm registers must be distinct");
- break;
- case invalid_dest_and_src_register_set:
- err_msg = _("destination and source registers must be distinct");
- break;
- case unsupported_vector_index_register:
- err_msg = _("unsupported vector index register");
- break;
- case unsupported_broadcast:
- err_msg = _("unsupported broadcast");
- break;
- case broadcast_needed:
- err_msg = _("broadcast is needed for operand of such type");
- break;
- case unsupported_masking:
- err_msg = _("unsupported masking");
- break;
- case mask_not_on_destination:
- err_msg = _("mask not on destination operand");
- break;
- case no_default_mask:
- err_msg = _("default mask isn't allowed");
- break;
- case unsupported_rc_sae:
- err_msg = _("unsupported static rounding/sae");
- break;
- case invalid_register_operand:
- err_msg = _("invalid register operand");
- break;
- }
- as_bad (_("%s for `%s'"), err_msg,
- current_templates->start->name);
+ i.error = specific_error;
return NULL;
}
{
if (!intel_syntax
&& (i.jumpabsolute != (t->opcode_modifier.jump == JUMP_ABSOLUTE)))
- as_warn (_("indirect %s without `*'"), t->name);
+ as_warn (_("indirect %s without `*'"), insn_name (t));
if (t->opcode_modifier.isprefix
&& t->opcode_modifier.mnemonicsize == IGNORESIZE)
{
/* Warn them that a data or address size prefix doesn't
affect assembly of the next line of code. */
- as_warn (_("stand-alone `%s' prefix"), t->name);
+ as_warn (_("stand-alone `%s' prefix"), insn_name (t));
}
}
if (i.seg[op] != NULL && i.seg[op] != reg_es)
{
as_bad (_("`%s' operand %u must use `%ses' segment"),
- i.tm.name,
+ insn_name (&i.tm),
intel_syntax ? i.tm.operands - es_op : es_op + 1,
register_prefix);
return 0;
/* Warn about changed behavior for segment register push/pop. */
else if ((i.tm.base_opcode | 1) == 0x07)
as_warn (_("generating 32-bit `%s', unlike earlier gas versions"),
- i.tm.name);
+ insn_name (&i.tm));
}
}
else if (!i.suffix
&& (i.tm.opcode_modifier.mnemonicsize != DEFAULTSIZE
|| operand_check == check_error))
{
- as_bad (_("ambiguous operand size for `%s'"), i.tm.name);
+ as_bad (_("ambiguous operand size for `%s'"), insn_name (&i.tm));
return 0;
}
if (operand_check == check_error)
{
as_bad (_("no instruction mnemonic suffix given and "
- "no register operands; can't size `%s'"), i.tm.name);
+ "no register operands; can't size `%s'"), insn_name (&i.tm));
return 0;
}
if (operand_check == check_warning)
? _("ambiguous operand size")
: _("no instruction mnemonic suffix given and "
"no register operands"),
- i.tm.name);
+ insn_name (&i.tm));
if (i.tm.opcode_modifier.floatmf)
i.suffix = SHORT_MNEM_SUFFIX;
&& i.op[0].regs->reg_type.bitfield.word)
{
as_bad (_("16-bit addressing unavailable for `%s'"),
- i.tm.name);
+ insn_name (&i.tm));
return 0;
}
}
as_bad (_("invalid register operand size for `%s'"),
- i.tm.name);
+ insn_name (&i.tm));
return 0;
}
}
/* Any other register is bad. */
as_bad (_("`%s%s' not allowed with `%s%c'"),
register_prefix, i.op[op].regs->reg_name,
- i.tm.name, i.suffix);
+ insn_name (&i.tm), i.suffix);
return 0;
}
return 1;
as_bad (_("`%s%s' not allowed with `%s%c'"),
register_prefix,
i.op[op].regs->reg_name,
- i.tm.name,
+ insn_name (&i.tm),
i.suffix);
return 0;
}
as_bad (_("`%s%s' not allowed with `%s%c'"),
register_prefix,
i.op[op].regs->reg_name,
- i.tm.name,
+ insn_name (&i.tm),
i.suffix);
return 0;
}
as_bad (_("`%s%s' not allowed with `%s%c'"),
register_prefix,
i.op[op].regs->reg_name,
- i.tm.name,
+ insn_name (&i.tm),
i.suffix);
return 0;
}
register_prefix, i.op[1].regs->reg_name,
register_prefix, i.op[1].regs->reg_name, first_reg_in_group,
register_prefix, i.op[1].regs->reg_name, last_reg_in_group,
- i.tm.name);
+ insn_name (&i.tm));
}
else if (i.tm.opcode_modifier.operandconstraint == REG_KLUDGE)
{
&& i.op[0].regs->reg_num < 4)
{
as_bad (_("you can't `%s %s%s'"),
- i.tm.name, register_prefix, i.op[0].regs->reg_name);
+ insn_name (&i.tm), register_prefix, i.op[0].regs->reg_name);
return 0;
}
if (i.op[0].regs->reg_num > 3
if (i.operands != 2)
{
/* Extraneous `l' suffix on fp insn. */
- as_warn (_("translating to `%s %s%s'"), i.tm.name,
+ as_warn (_("translating to `%s %s%s'"), insn_name (&i.tm),
register_prefix, i.op[0].regs->reg_name);
}
else if (i.op[0].regs->reg_type.bitfield.instance != Accum)
{
/* Reversed arguments on faddp or fmulp. */
- as_warn (_("translating to `%s %s%s,%s%s'"), i.tm.name,
+ as_warn (_("translating to `%s %s%s,%s%s'"), insn_name (&i.tm),
register_prefix, i.op[!intel_syntax].regs->reg_name,
register_prefix, i.op[intel_syntax].regs->reg_name);
}
&& !is_any_vex_encoding(&i.tm))
{
if (!quiet_warnings)
- as_warn (_("segment override on `%s' is ineffectual"), i.tm.name);
+ as_warn (_("segment override on `%s' is ineffectual"), insn_name (&i.tm));
if (optimize)
{
i.seg[0] = NULL;
}
if (i.prefixes != 0)
- as_warn (_("skipping prefixes on `%s'"), i.tm.name);
+ as_warn (_("skipping prefixes on `%s'"), insn_name (&i.tm));
/* It's always a symbol; End frag & setup for relax.
Make sure there is enough room in this frag for the largest
}
if (i.prefixes != 0)
- as_warn (_("skipping prefixes on `%s'"), i.tm.name);
+ as_warn (_("skipping prefixes on `%s'"), insn_name (&i.tm));
if (now_seg == absolute_section)
{
size = 2;
if (i.prefixes != 0)
- as_warn (_("skipping prefixes on `%s'"), i.tm.name);
+ as_warn (_("skipping prefixes on `%s'"), insn_name (&i.tm));
if (now_seg == absolute_section)
{
if (flag_debug)
as_warn_where (last_insn.file, last_insn.line,
_("`%s` skips -malign-branch-boundary on `%s`"),
- last_insn.name, i.tm.name);
+ last_insn.name, insn_name (&i.tm));
}
return 0;
if (flag_debug)
as_warn_where (last_insn.file, last_insn.line,
_("`%s` skips -malign-branch-boundary on `%s`"),
- last_insn.name, i.tm.name);
+ last_insn.name, insn_name (&i.tm));
return 0;
}
if (flag_debug)
as_warn_where (last_insn.file, last_insn.line,
_("`%s` skips -malign-branch-boundary on `%s`"),
- last_insn.name, i.tm.name);
+ last_insn.name, insn_name (&i.tm));
return 0;
}
/* Encode lfence, mfence, and sfence as
f0 83 04 24 00 lock addl $0x0, (%{re}sp). */
if (flag_code == CODE_16BIT)
- as_bad (_("Cannot convert `%s' in 16-bit mode"), i.tm.name);
+ as_bad (_("Cannot convert `%s' in 16-bit mode"), insn_name (&i.tm));
else if (omit_lock_prefix)
as_bad (_("Cannot convert `%s' with `-momit-lock-prefix=yes' in effect"),
- i.tm.name);
+ insn_name (&i.tm));
else if (now_seg != absolute_section)
{
offsetT val = 0x240483f0ULL;
{
const char *kind = "base/index";
enum flag_code addr_mode = i386_addressing_mode ();
- const insn_template *t = current_templates->start;
+ const insn_template *t = current_templates->end - 1;
- if (t->opcode_modifier.isstring
- && (current_templates->end[-1].opcode_modifier.isstring
- || i.mem_operands))
+ if (t->opcode_modifier.isstring)
{
/* Memory operands of string insns are special in that they only allow
a single register (rDI, rSI, or rBX) as their memory address. */
if (t->opcode_modifier.prefixok == PrefixRep)
{
- int es_op = current_templates->end[-1].opcode_modifier.isstring
- - IS_STRING_ES_OP0;
+ int es_op = t->opcode_modifier.isstring - IS_STRING_ES_OP0;
int op = 0;
- if (!current_templates->end[-1].operand_types[0].bitfield.baseindex
+ if (!t->operand_types[0].bitfield.baseindex
|| ((!i.mem_operands != !intel_syntax)
- && current_templates->end[-1].operand_types[1]
- .bitfield.baseindex))
+ && t->operand_types[1].bitfield.baseindex))
op = 1;
expected_reg
= (const reg_entry *) str_hash_find (reg_hash,
}
else
{
+ t = current_templates->start;
+
if (addr_mode != CODE_16BIT)
{
/* 32-bit/64-bit checks. */
return 1;
}
-/* Only string instructions can have a second memory operand, so
- reduce current_templates to just those if it contains any. */
-static int
-maybe_adjust_templates (void)
-{
- const insn_template *t;
-
- gas_assert (i.mem_operands == 1);
-
- for (t = current_templates->start; t < current_templates->end; ++t)
- if (t->opcode_modifier.isstring)
- break;
-
- if (t < current_templates->end)
- {
- static templates aux_templates;
- bool recheck;
-
- aux_templates.start = t;
- for (; t < current_templates->end; ++t)
- if (!t->opcode_modifier.isstring)
- break;
- aux_templates.end = t;
-
- /* Determine whether to re-check the first memory operand. */
- recheck = (aux_templates.start != current_templates->start
- || t != current_templates->end);
-
- current_templates = &aux_templates;
-
- if (recheck)
- {
- i.mem_operands = 0;
- if (i.memop1_string != NULL
- && i386_index_check (i.memop1_string) == 0)
- return 0;
- i.mem_operands = 1;
- }
- }
-
- return 1;
-}
-
static INLINE bool starts_memory_operand (char c)
{
return ISDIGIT (c)
if (i.rounding.type == RC_NamesTable[j].type)
break;
as_bad (_("`%s': misplaced `{%s}'"),
- current_templates->start->name, RC_NamesTable[j].name);
+ insn_name (current_templates->start), RC_NamesTable[j].name);
return 0;
}
}
if (i.rounding.type != rc_none)
{
as_bad (_("`%s': RC/SAE operand must follow immediate operands"),
- current_templates->start->name);
+ insn_name (current_templates->start));
return 0;
}
}
&& i.op[0].regs->reg_type.bitfield.class != Reg))
{
as_bad (_("`%s': misplaced `%s'"),
- current_templates->start->name, operand_string);
+ insn_name (current_templates->start), operand_string);
return 0;
}
}
char *displacement_string_end;
do_memory_reference:
- if (i.mem_operands == 1 && !maybe_adjust_templates ())
- return 0;
- if ((i.mem_operands == 1
- && !current_templates->start->opcode_modifier.isstring)
- || i.mem_operands == 2)
- {
- as_bad (_("too many memory references for `%s'"),
- current_templates->start->name);
- return 0;
- }
-
/* Check for base index form. We detect the base index form by
looking for an ')' at the end of the operand, searching
for the '(' matching it, and finding a REGISTER_PREFIX or ','
if (i386_index_check (operand_string) == 0)
return 0;
i.flags[this_operand] |= Operand_Mem;
- if (i.mem_operands == 0)
- i.memop1_string = xstrdup (operand_string);
i.mem_operands++;
}
else