| /* tc-i386.c -- Assemble code for the Intel 80386 |
| Copyright (C) 1989-2024 Free Software Foundation, Inc. |
| |
| This file is part of GAS, the GNU Assembler. |
| |
| GAS is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GAS is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GAS; see the file COPYING. If not, write to the Free |
| Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA |
| 02110-1301, USA. */ |
| |
| /* Intel 80386 machine specific gas. |
| Written by Eliot Dresselhaus (eliot@mgm.mit.edu). |
| x86_64 support by Jan Hubicka (jh@suse.cz) |
| VIA PadLock support by Michal Ludvig (mludvig@suse.cz) |
| Bugs & suggestions are completely welcome. This is free software. |
| Please help us make it better. */ |
| |
| #include "as.h" |
| #include "safe-ctype.h" |
| #include "subsegs.h" |
| #include "dwarf2dbg.h" |
| #include "dw2gencfi.h" |
| #include "scfi.h" |
| #include "gen-sframe.h" |
| #include "sframe.h" |
| #include "elf/x86-64.h" |
| #include "opcodes/i386-init.h" |
| #include "opcodes/i386-mnem.h" |
| #include <limits.h> |
| |
| #ifndef INFER_ADDR_PREFIX |
| #define INFER_ADDR_PREFIX 1 |
| #endif |
| |
| #ifndef DEFAULT_ARCH |
| #define DEFAULT_ARCH "i386" |
| #endif |
| |
| #ifndef INLINE |
| #if __GNUC__ >= 2 |
| #define INLINE __inline__ |
| #else |
| #define INLINE |
| #endif |
| #endif |
| |
| /* Prefixes will be emitted in the order defined below. |
| WAIT_PREFIX must be the first prefix since FWAIT is really is an |
| instruction, and so must come before any prefixes. |
| The preferred prefix order is SEG_PREFIX, ADDR_PREFIX, DATA_PREFIX, |
| REP_PREFIX/HLE_PREFIX, LOCK_PREFIX. */ |
| #define WAIT_PREFIX 0 |
| #define SEG_PREFIX 1 |
| #define ADDR_PREFIX 2 |
| #define DATA_PREFIX 3 |
| #define REP_PREFIX 4 |
| #define HLE_PREFIX REP_PREFIX |
| #define BND_PREFIX REP_PREFIX |
| #define LOCK_PREFIX 5 |
| #define REX_PREFIX 6 /* must come last. */ |
| #define MAX_PREFIXES 7 /* max prefixes per opcode */ |
| |
| /* we define the syntax here (modulo base,index,scale syntax) */ |
| #define REGISTER_PREFIX '%' |
| #define IMMEDIATE_PREFIX '$' |
| #define ABSOLUTE_PREFIX '*' |
| |
| /* these are the instruction mnemonic suffixes in AT&T syntax or |
| memory operand size in Intel syntax. */ |
| #define WORD_MNEM_SUFFIX 'w' |
| #define BYTE_MNEM_SUFFIX 'b' |
| #define SHORT_MNEM_SUFFIX 's' |
| #define LONG_MNEM_SUFFIX 'l' |
| #define QWORD_MNEM_SUFFIX 'q' |
| |
| #define END_OF_INSN '\0' |
| |
| #define OPERAND_TYPE_NONE { .bitfield = { .class = ClassNone } } |
| |
| /* This matches the C -> StaticRounding alias in the opcode table. */ |
| #define commutative staticrounding |
| |
| /* |
| 'templates' is for grouping together 'template' structures for opcodes |
| of the same name. This is only used for storing the insns in the grand |
| ole hash table of insns. |
| The templates themselves start at START and range up to (but not including) |
| END. |
| */ |
| typedef struct |
| { |
| const insn_template *start; |
| const insn_template *end; |
| } |
| templates; |
| |
| /* 386 operand encoding bytes: see 386 book for details of this. */ |
| typedef struct |
| { |
| unsigned int regmem; /* codes register or memory operand */ |
| unsigned int reg; /* codes register operand (or extended opcode) */ |
| unsigned int mode; /* how to interpret regmem & reg */ |
| } |
| modrm_byte; |
| |
| /* x86-64 extension prefix. */ |
| typedef int rex_byte; |
| |
| /* 386 opcode byte to code indirect addressing. */ |
| typedef struct |
| { |
| unsigned base; |
| unsigned index; |
| unsigned scale; |
| } |
| sib_byte; |
| |
| /* x86 arch names, types and features */ |
| typedef struct |
| { |
| const char *name; /* arch name */ |
| unsigned int len:8; /* arch string length */ |
| bool skip:1; /* show_arch should skip this. */ |
| enum processor_type type; /* arch type */ |
| enum { vsz_none, vsz_set, vsz_reset } vsz; /* vector size control */ |
| i386_cpu_flags enable; /* cpu feature enable flags */ |
| i386_cpu_flags disable; /* cpu feature disable flags */ |
| } |
| arch_entry; |
| |
| /* Modes for parse_insn() to operate in. */ |
| enum parse_mode { |
| parse_all, |
| parse_prefix, |
| parse_pseudo_prefix, |
| }; |
| |
| static void update_code_flag (int, int); |
| static void s_insn (int); |
| static void s_noopt (int); |
| static void set_code_flag (int); |
| static void set_16bit_gcc_code_flag (int); |
| static void set_intel_syntax (int); |
| static void set_intel_mnemonic (int); |
| static void set_allow_index_reg (int); |
| static void set_check (int); |
| static void set_cpu_arch (int); |
| #ifdef TE_PE |
| static void pe_directive_secrel (int); |
| static void pe_directive_secidx (int); |
| #endif |
| static void signed_cons (int); |
| static char *output_invalid (int c); |
| static int i386_finalize_immediate (segT, expressionS *, i386_operand_type, |
| const char *); |
| static int i386_finalize_displacement (segT, expressionS *, i386_operand_type, |
| const char *); |
| static int i386_att_operand (char *); |
| 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 (const char *, char **); |
| static const char *parse_insn (const char *, char *, enum parse_mode); |
| static char *parse_operands (char *, const char *); |
| static void swap_operands (void); |
| static void swap_2_operands (unsigned int, unsigned int); |
| static enum i386_flag_code i386_addressing_mode (void); |
| static void optimize_imm (void); |
| static bool optimize_disp (const insn_template *t); |
| static const insn_template *match_template (char); |
| static int check_string (void); |
| static int process_suffix (const insn_template *); |
| static int check_byte_reg (void); |
| static int check_long_reg (void); |
| static int check_qword_reg (void); |
| static int check_word_reg (void); |
| static int finalize_imm (void); |
| static int process_operands (void); |
| static const reg_entry *build_modrm_byte (void); |
| static void output_insn (const struct last_insn *); |
| static void output_imm (fragS *, offsetT); |
| static void output_disp (fragS *, offsetT); |
| #ifdef OBJ_AOUT |
| static void s_bss (int); |
| #endif |
| #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
| static void handle_large_common (int small ATTRIBUTE_UNUSED); |
| |
| /* GNU_PROPERTY_X86_ISA_1_USED. */ |
| static unsigned int x86_isa_1_used; |
| /* GNU_PROPERTY_X86_FEATURE_2_USED. */ |
| static unsigned int x86_feature_2_used; |
| /* Generate x86 used ISA and feature properties. */ |
| static unsigned int x86_used_note = DEFAULT_X86_USED_NOTE; |
| #endif |
| |
| static const char *default_arch = DEFAULT_ARCH; |
| |
| /* parse_register() returns this when a register alias cannot be used. */ |
| static const reg_entry bad_reg = { "<bad>", OPERAND_TYPE_NONE, 0, 0, |
| { Dw2Inval, Dw2Inval } }; |
| |
| static const reg_entry *reg_eax; |
| static const reg_entry *reg_ds; |
| static const reg_entry *reg_es; |
| static const reg_entry *reg_ss; |
| static const reg_entry *reg_st0; |
| static const reg_entry *reg_k0; |
| |
| /* VEX prefix. */ |
| typedef struct |
| { |
| /* VEX prefix is either 2 byte or 3 byte. EVEX is 4 byte. */ |
| unsigned char bytes[4]; |
| unsigned int length; |
| /* Destination or source register specifier. */ |
| const reg_entry *register_specifier; |
| } vex_prefix; |
| |
| /* 'md_assemble ()' gathers together information and puts it into a |
| i386_insn. */ |
| |
| union i386_op |
| { |
| expressionS *disps; |
| expressionS *imms; |
| const reg_entry *regs; |
| }; |
| |
| enum i386_error |
| { |
| no_error, /* Must be first. */ |
| operand_size_mismatch, |
| operand_type_mismatch, |
| register_type_mismatch, |
| number_of_operands_mismatch, |
| invalid_instruction_suffix, |
| bad_imm4, |
| unsupported_with_intel_mnemonic, |
| unsupported_syntax, |
| unsupported_EGPR_for_addressing, |
| unsupported_nf, |
| unsupported, |
| unsupported_on_arch, |
| unsupported_64bit, |
| no_vex_encoding, |
| no_evex_encoding, |
| invalid_sib_address, |
| invalid_vsib_address, |
| invalid_vector_register_set, |
| invalid_tmm_register_set, |
| invalid_dest_and_src_register_set, |
| invalid_dest_register_set, |
| invalid_pseudo_prefix, |
| unsupported_vector_index_register, |
| unsupported_broadcast, |
| broadcast_needed, |
| unsupported_masking, |
| mask_not_on_destination, |
| no_default_mask, |
| unsupported_rc_sae, |
| unsupported_vector_size, |
| unsupported_rsp_register, |
| internal_error, |
| }; |
| |
| struct _i386_insn |
| { |
| /* TM holds the template for the insn were currently assembling. */ |
| insn_template tm; |
| |
| /* SUFFIX holds the instruction size suffix for byte, word, dword |
| or qword, if given. */ |
| char suffix; |
| |
| /* OPCODE_LENGTH holds the number of base opcode bytes. */ |
| unsigned char opcode_length; |
| |
| /* OPERANDS gives the number of given operands. */ |
| unsigned int operands; |
| |
| /* REG_OPERANDS, DISP_OPERANDS, MEM_OPERANDS, IMM_OPERANDS give the number |
| of given register, displacement, memory operands and immediate |
| operands. */ |
| unsigned int reg_operands, disp_operands, mem_operands, imm_operands; |
| |
| /* TYPES [i] is the type (see above #defines) which tells us how to |
| use OP[i] for the corresponding operand. */ |
| i386_operand_type types[MAX_OPERANDS]; |
| |
| /* Displacement expression, immediate expression, or register for each |
| operand. */ |
| union i386_op op[MAX_OPERANDS]; |
| |
| /* Flags for operands. */ |
| unsigned int flags[MAX_OPERANDS]; |
| #define Operand_PCrel 1 |
| #define Operand_Mem 2 |
| #define Operand_Signed 4 /* .insn only */ |
| |
| /* Relocation type for operand */ |
| enum bfd_reloc_code_real reloc[MAX_OPERANDS]; |
| |
| /* BASE_REG, INDEX_REG, and LOG2_SCALE_FACTOR are used to encode |
| the base index byte below. */ |
| const reg_entry *base_reg; |
| const reg_entry *index_reg; |
| unsigned int log2_scale_factor; |
| |
| /* SEG gives the seg_entries of this insn. They are zero unless |
| explicit segment overrides are given. */ |
| const reg_entry *seg[2]; |
| |
| /* PREFIX holds all the given prefix opcodes (usually null). |
| PREFIXES is the number of prefix opcodes. */ |
| unsigned int prefixes; |
| unsigned char prefix[MAX_PREFIXES]; |
| |
| /* .insn allows for reserved opcode spaces. */ |
| unsigned char insn_opcode_space; |
| |
| /* .insn also allows (requires) specifying immediate size. */ |
| unsigned char imm_bits[MAX_OPERANDS]; |
| |
| /* Register is in low 3 bits of opcode. */ |
| bool short_form; |
| |
| /* The operand to a branch insn indicates an absolute branch. */ |
| bool jumpabsolute; |
| |
| /* The operand to a branch insn indicates a far branch. */ |
| bool far_branch; |
| |
| /* There is a memory operand of (%dx) which should be only used |
| with input/output instructions. */ |
| bool input_output_operand; |
| |
| /* Extended states. */ |
| enum |
| { |
| /* Use MMX state. */ |
| xstate_mmx = 1 << 0, |
| /* Use XMM state. */ |
| xstate_xmm = 1 << 1, |
| /* Use YMM state. */ |
| xstate_ymm = 1 << 2 | xstate_xmm, |
| /* Use ZMM state. */ |
| xstate_zmm = 1 << 3 | xstate_ymm, |
| /* Use TMM state. */ |
| xstate_tmm = 1 << 4, |
| /* Use MASK state. */ |
| xstate_mask = 1 << 5 |
| } xstate; |
| |
| /* Has GOTPC or TLS relocation. */ |
| bool has_gotpc_tls_reloc; |
| |
| /* RM and SIB are the modrm byte and the sib byte where the |
| addressing modes of this insn are encoded. */ |
| modrm_byte rm; |
| rex_byte rex; |
| rex_byte vrex; |
| rex_byte rex2; |
| sib_byte sib; |
| vex_prefix vex; |
| |
| /* Masking attributes. |
| |
| The struct describes masking, applied to OPERAND in the instruction. |
| REG is a pointer to the corresponding mask register. ZEROING tells |
| whether merging or zeroing mask is used. */ |
| struct Mask_Operation |
| { |
| const reg_entry *reg; |
| unsigned int zeroing; |
| /* The operand where this operation is associated. */ |
| unsigned int operand; |
| } mask; |
| |
| /* Rounding control and SAE attributes. */ |
| struct RC_Operation |
| { |
| enum rc_type |
| { |
| rc_none = -1, |
| rne, |
| rd, |
| ru, |
| rz, |
| saeonly |
| } type; |
| /* In Intel syntax the operand modifier form is supposed to be used, but |
| we continue to accept the immediate forms as well. */ |
| bool modifier; |
| } rounding; |
| |
| /* Broadcasting attributes. |
| |
| The struct describes broadcasting, applied to OPERAND. TYPE is |
| expresses the broadcast factor. */ |
| struct Broadcast_Operation |
| { |
| /* Type of broadcast: {1to2}, {1to4}, {1to8}, {1to16} or {1to32}. */ |
| unsigned int type; |
| |
| /* Index of broadcasted operand. */ |
| unsigned int operand; |
| |
| /* Number of bytes to broadcast. */ |
| unsigned int bytes; |
| } broadcast; |
| |
| /* Compressed disp8*N attribute. */ |
| unsigned int memshift; |
| |
| /* SCC = EVEX.[SC3,SC2,SC1,SC0]. */ |
| unsigned int scc; |
| |
| /* Store 4 bits of EVEX.[OF,SF,ZF,CF]. */ |
| #define OSZC_CF 1 |
| #define OSZC_ZF 2 |
| #define OSZC_SF 4 |
| #define OSZC_OF 8 |
| unsigned int oszc_flags; |
| |
| /* Invert the condition encoded in a base opcode. */ |
| bool invert_cond; |
| |
| /* REP prefix. */ |
| const char *rep_prefix; |
| |
| /* HLE prefix. */ |
| const char *hle_prefix; |
| |
| /* Have BND prefix. */ |
| const char *bnd_prefix; |
| |
| /* Have NOTRACK prefix. */ |
| const char *notrack_prefix; |
| |
| /* Error message. */ |
| enum i386_error error; |
| }; |
| |
| typedef struct _i386_insn i386_insn; |
| |
| /* Pseudo-prefix recording state, separate from i386_insn. */ |
| static struct pseudo_prefixes { |
| /* How to encode instructions. */ |
| enum { |
| encoding_default = 0, |
| encoding_vex, |
| encoding_vex3, |
| encoding_egpr, /* REX2 or EVEX. */ |
| encoding_evex, |
| encoding_evex512, |
| encoding_error |
| } encoding; |
| |
| /* Prefer load or store in encoding. */ |
| enum { |
| dir_encoding_default = 0, |
| dir_encoding_load, |
| dir_encoding_store, |
| dir_encoding_swap |
| } dir_encoding; |
| |
| /* Prefer 8bit, 16bit, 32bit displacement in encoding. */ |
| enum { |
| disp_encoding_default = 0, |
| disp_encoding_8bit, |
| disp_encoding_16bit, |
| disp_encoding_32bit |
| } disp_encoding; |
| |
| /* Prefer the REX byte in encoding. */ |
| bool rex_encoding; |
| |
| /* Prefer the REX2 prefix in encoding. */ |
| bool rex2_encoding; |
| |
| /* No CSPAZO flags update. */ |
| bool has_nf; |
| |
| /* Disable instruction size optimization. */ |
| bool no_optimize; |
| } pp; |
| |
| /* Link RC type with corresponding string, that'll be looked for in |
| asm. */ |
| struct RC_name |
| { |
| enum rc_type type; |
| const char *name; |
| unsigned int len; |
| }; |
| |
| static const struct RC_name RC_NamesTable[] = |
| { |
| { rne, STRING_COMMA_LEN ("rn-sae") }, |
| { rd, STRING_COMMA_LEN ("rd-sae") }, |
| { ru, STRING_COMMA_LEN ("ru-sae") }, |
| { rz, STRING_COMMA_LEN ("rz-sae") }, |
| { saeonly, STRING_COMMA_LEN ("sae") }, |
| }; |
| |
| /* To be indexed by segment register number. */ |
| static const unsigned char i386_seg_prefixes[] = { |
| ES_PREFIX_OPCODE, |
| CS_PREFIX_OPCODE, |
| SS_PREFIX_OPCODE, |
| DS_PREFIX_OPCODE, |
| FS_PREFIX_OPCODE, |
| GS_PREFIX_OPCODE |
| }; |
| |
| /* List of chars besides those in app.c:symbol_chars that can start an |
| operand. Used to prevent the scrubber eating vital white-space. */ |
| const char extra_symbol_chars[] = "*%-([" |
| #ifdef LEX_AT |
| "@" |
| #endif |
| #ifdef LEX_QM |
| "?" |
| #endif |
| ; |
| |
| #if ((defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) \ |
| && !defined (TE_GNU) \ |
| && !defined (TE_LINUX) \ |
| && !defined (TE_Haiku) \ |
| && !defined (TE_FreeBSD) \ |
| && !defined (TE_DragonFly) \ |
| && !defined (TE_NetBSD)) |
| /* This array holds the chars that always start a comment. If the |
| pre-processor is disabled, these aren't very useful. The option |
| --divide will remove '/' from this list. */ |
| const char *i386_comment_chars = "#/"; |
| #define SVR4_COMMENT_CHARS 1 |
| #define PREFIX_SEPARATOR '\\' |
| |
| #else |
| const char *i386_comment_chars = "#"; |
| #define PREFIX_SEPARATOR '/' |
| #endif |
| |
| /* This array holds the chars that only start a comment at the beginning of |
| a line. If the line seems to have the form '# 123 filename' |
| .line and .file directives will appear in the pre-processed output. |
| Note that input_file.c hand checks for '#' at the beginning of the |
| first line of the input file. This is because the compiler outputs |
| #NO_APP at the beginning of its output. |
| Also note that comments started like this one will always work if |
| '/' isn't otherwise defined. */ |
| const char line_comment_chars[] = "#/"; |
| |
| const char line_separator_chars[] = ";"; |
| |
| /* Chars that can be used to separate mant from exp in floating point |
| nums. */ |
| const char EXP_CHARS[] = "eE"; |
| |
| /* Chars that mean this number is a floating point constant |
| As in 0f12.456 |
| or 0d1.2345e12. */ |
| const char FLT_CHARS[] = "fFdDxXhHbB"; |
| |
| /* Tables for lexical analysis. */ |
| static char mnemonic_chars[256]; |
| static char register_chars[256]; |
| static char operand_chars[256]; |
| |
| /* Lexical macros. */ |
| #define is_operand_char(x) (operand_chars[(unsigned char) x]) |
| #define is_register_char(x) (register_chars[(unsigned char) x]) |
| #define is_space_char(x) ((x) == ' ') |
| |
| /* All non-digit non-letter characters that may occur in an operand and |
| which aren't already in extra_symbol_chars[]. */ |
| static const char operand_special_chars[] = "$+,)._~/<>|&^!=:@]{}"; |
| |
| /* md_assemble() always leaves the strings it's passed unaltered. To |
| effect this we maintain a stack of saved characters that we've smashed |
| with '\0's (indicating end of strings for various sub-fields of the |
| assembler instruction). */ |
| static char save_stack[32]; |
| static char *save_stack_p; |
| #define END_STRING_AND_SAVE(s) \ |
| do { *save_stack_p++ = *(s); *(s) = '\0'; } while (0) |
| #define RESTORE_END_STRING(s) \ |
| do { *(s) = *--save_stack_p; } while (0) |
| |
| /* The instruction we're assembling. */ |
| static i386_insn i; |
| |
| /* Possible templates for current insn. */ |
| static templates current_templates; |
| |
| /* Per instruction expressionS buffers: max displacements & immediates. */ |
| static expressionS disp_expressions[MAX_MEMORY_OPERANDS]; |
| static expressionS im_expressions[MAX_IMMEDIATE_OPERANDS]; |
| |
| /* Current operand we are working on. */ |
| static int this_operand = -1; |
| |
| /* Are we processing a .insn directive? */ |
| #define dot_insn() (i.tm.mnem_off == MN__insn) |
| |
| enum i386_flag_code i386_flag_code; |
| #define flag_code i386_flag_code /* Permit to continue using original name. */ |
| static unsigned int object_64bit; |
| static unsigned int disallow_64bit_reloc; |
| static int use_rela_relocations = 0; |
| /* __tls_get_addr/___tls_get_addr symbol for TLS. */ |
| static const char *tls_get_addr; |
| |
| #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
| |
| /* The ELF ABI to use. */ |
| enum x86_elf_abi |
| { |
| I386_ABI, |
| X86_64_ABI, |
| X86_64_X32_ABI |
| }; |
| |
| static enum x86_elf_abi x86_elf_abi = I386_ABI; |
| #endif |
| |
| #if defined (TE_PE) || defined (TE_PEP) |
| /* Use big object file format. */ |
| static int use_big_obj = 0; |
| #endif |
| |
| #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
| /* 1 if generating code for a shared library. */ |
| static int shared = 0; |
| |
| unsigned int x86_sframe_cfa_sp_reg; |
| /* The other CFA base register for SFrame stack trace info. */ |
| unsigned int x86_sframe_cfa_fp_reg; |
| |
| static ginsnS *x86_ginsn_new (const symbolS *, enum ginsn_gen_mode); |
| #endif |
| |
| /* 1 for intel syntax, |
| 0 if att syntax. */ |
| static int intel_syntax = 0; |
| |
| static enum x86_64_isa |
| { |
| amd64 = 1, /* AMD64 ISA. */ |
| intel64 /* Intel64 ISA. */ |
| } isa64; |
| |
| /* 1 for intel mnemonic, |
| 0 if att mnemonic. */ |
| static int intel_mnemonic = !SYSV386_COMPAT; |
| |
| /* 1 if pseudo registers are permitted. */ |
| static int allow_pseudo_reg = 0; |
| |
| /* 1 if register prefix % not required. */ |
| static int allow_naked_reg = 0; |
| |
| /* 1 if the assembler should add BND prefix for all control-transferring |
| instructions supporting it, even if this prefix wasn't specified |
| explicitly. */ |
| static int add_bnd_prefix = 0; |
| |
| /* 1 if pseudo index register, eiz/riz, is allowed . */ |
| static int allow_index_reg = 0; |
| |
| /* 1 if the assembler should ignore LOCK prefix, even if it was |
| specified explicitly. */ |
| static int omit_lock_prefix = 0; |
| |
| /* 1 if the assembler should encode lfence, mfence, and sfence as |
| "lock addl $0, (%{re}sp)". */ |
| static int avoid_fence = 0; |
| |
| /* 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; |
| |
| /* 1 if the assembler should generate relax relocations. */ |
| |
| static int generate_relax_relocations |
| = DEFAULT_GENERATE_X86_RELAX_RELOCATIONS; |
| |
| static enum check_kind |
| { |
| check_none = 0, |
| check_warning, |
| check_error |
| } |
| sse_check, operand_check = check_warning; |
| |
| /* Non-zero if branches should be aligned within power of 2 boundary. */ |
| static int align_branch_power = 0; |
| |
| /* Types of branches to align. */ |
| enum align_branch_kind |
| { |
| align_branch_none = 0, |
| align_branch_jcc = 1, |
| align_branch_fused = 2, |
| align_branch_jmp = 3, |
| align_branch_call = 4, |
| align_branch_indirect = 5, |
| align_branch_ret = 6 |
| }; |
| |
| /* Type bits of branches to align. */ |
| enum align_branch_bit |
| { |
| align_branch_jcc_bit = 1 << align_branch_jcc, |
| align_branch_fused_bit = 1 << align_branch_fused, |
| align_branch_jmp_bit = 1 << align_branch_jmp, |
| align_branch_call_bit = 1 << align_branch_call, |
| align_branch_indirect_bit = 1 << align_branch_indirect, |
| align_branch_ret_bit = 1 << align_branch_ret |
| }; |
| |
| static unsigned int align_branch = (align_branch_jcc_bit |
| | 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. */ |
| #define MAX_FUSED_JCC_PADDING_SIZE 20 |
| |
| /* The maximum number of prefixes added for an instruction. */ |
| static unsigned int align_branch_prefix_size = 5; |
| |
| /* Optimization: |
| 1. Clear the REX_W bit with register operand if possible. |
| 2. Above plus use 128bit vector instruction to clear the full vector |
| register. |
| */ |
| static int optimize = 0; |
| |
| /* Optimization: |
| 1. Clear the REX_W bit with register operand if possible. |
| 2. Above plus use 128bit vector instruction to clear the full vector |
| register. |
| 3. Above plus optimize "test{q,l,w} $imm8,%r{64,32,16}" to |
| "testb $imm7,%r8". |
| */ |
| static int optimize_for_space = 0; |
| |
| /* Register prefix used for error message. */ |
| static const char *register_prefix = "%"; |
| |
| /* Used in 16 bit gcc mode to add an l suffix to call, ret, enter, |
| leave, push, and pop instructions so that gcc has the same stack |
| frame as in 32 bit mode. */ |
| static char stackop_size = '\0'; |
| |
| /* Non-zero to optimize code alignment. */ |
| int optimize_align_code = 1; |
| |
| /* Non-zero to quieten some warnings. */ |
| static int quiet_warnings = 0; |
| |
| /* Guard to avoid repeated warnings about non-16-bit code on 16-bit CPUs. */ |
| static bool pre_386_16bit_warned; |
| |
| /* CPU name. */ |
| static const char *cpu_arch_name = NULL; |
| static char *cpu_sub_arch_name = NULL; |
| |
| /* CPU feature flags. */ |
| i386_cpu_flags cpu_arch_flags = CPU_UNKNOWN_FLAGS; |
| |
| /* ISA extensions available in 64-bit mode only. */ |
| static const i386_cpu_flags cpu_64_flags = CPU_ANY_64_FLAGS; |
| |
| /* If we have selected a cpu we are generating instructions for. */ |
| static int cpu_arch_tune_set = 0; |
| |
| /* Cpu we are generating instructions for. */ |
| enum processor_type cpu_arch_tune = PROCESSOR_UNKNOWN; |
| |
| /* CPU instruction set architecture used. */ |
| enum processor_type cpu_arch_isa = PROCESSOR_UNKNOWN; |
| |
| /* CPU feature flags of instruction set architecture used. */ |
| i386_cpu_flags cpu_arch_isa_flags; |
| |
| /* If set, conditional jumps are not automatically promoted to handle |
| larger than a byte offset. */ |
| static bool no_cond_jump_promotion = false; |
| |
| /* This will be set from an expression parser hook if there's any |
| applicable operator involved in an expression. */ |
| static enum { |
| expr_operator_none, |
| expr_operator_present, |
| expr_large_value, |
| } expr_mode; |
| |
| /* Encode SSE instructions with VEX prefix. */ |
| static unsigned int sse2avx; |
| |
| /* Encode aligned vector move as unaligned vector move. */ |
| static unsigned int use_unaligned_vector_move; |
| |
| /* Maximum permitted vector size. */ |
| #define VSZ128 0 |
| #define VSZ256 1 |
| #define VSZ512 2 |
| #define VSZ_DEFAULT VSZ512 |
| static unsigned int vector_size = VSZ_DEFAULT; |
| |
| /* Encode scalar AVX instructions with specific vector length. */ |
| static enum |
| { |
| vex128 = 0, |
| vex256 |
| } avxscalar; |
| |
| /* Encode VEX WIG instructions with specific vex.w. */ |
| static enum |
| { |
| vexw0 = 0, |
| vexw1 |
| } vexwig; |
| |
| /* Encode scalar EVEX LIG instructions with specific vector length. */ |
| static enum |
| { |
| evexl128 = 0, |
| evexl256, |
| evexl512 |
| } evexlig; |
| |
| /* Encode EVEX WIG instructions with specific evex.w. */ |
| static enum |
| { |
| evexw0 = 0, |
| evexw1 |
| } evexwig; |
| |
| /* Value to encode in EVEX RC bits, for SAE-only instructions. */ |
| static enum rc_type evexrcig = rne; |
| |
| /* Pre-defined "_GLOBAL_OFFSET_TABLE_". */ |
| static symbolS *GOT_symbol; |
| |
| /* The dwarf2 return column, adjusted for 32 or 64 bit. */ |
| unsigned int x86_dwarf2_return_column; |
| |
| /* The dwarf2 data alignment, adjusted for 32 or 64 bit. */ |
| int x86_cie_data_alignment; |
| |
| /* Interface to relax_segment. |
| There are 3 major relax states for 386 jump insns because the |
| different types of jumps add different sizes to frags when we're |
| figuring out what sort of jump to choose to reach a given label. |
| |
| BRANCH_PADDING, BRANCH_PREFIX and FUSED_JCC_PADDING are used to align |
| branches which are handled by md_estimate_size_before_relax() and |
| i386_generic_table_relax_frag(). */ |
| |
| /* Types. */ |
| #define UNCOND_JUMP 0 |
| #define COND_JUMP 1 |
| #define COND_JUMP86 2 |
| #define BRANCH_PADDING 3 |
| #define BRANCH_PREFIX 4 |
| #define FUSED_JCC_PADDING 5 |
| |
| /* Sizes. */ |
| #define CODE16 1 |
| #define SMALL 0 |
| #define SMALL16 (SMALL | CODE16) |
| #define BIG 2 |
| #define BIG16 (BIG | CODE16) |
| |
| #ifndef INLINE |
| #ifdef __GNUC__ |
| #define INLINE __inline__ |
| #else |
| #define INLINE |
| #endif |
| #endif |
| |
| #define ENCODE_RELAX_STATE(type, size) \ |
| ((relax_substateT) (((type) << 2) | (size))) |
| #define TYPE_FROM_RELAX_STATE(s) \ |
| ((s) >> 2) |
| #define DISP_SIZE_FROM_RELAX_STATE(s) \ |
| ((((s) & 3) == BIG ? 4 : (((s) & 3) == BIG16 ? 2 : 1))) |
| |
| /* This table is used by relax_frag to promote short jumps to long |
| ones where necessary. SMALL (short) jumps may be promoted to BIG |
| (32 bit long) ones, and SMALL16 jumps to BIG16 (16 bit long). We |
| don't allow a short jump in a 32 bit code segment to be promoted to |
| a 16 bit offset jump because it's slower (requires data size |
| prefix), and doesn't work, unless the destination is in the bottom |
| 64k of the code segment (The top 16 bits of eip are zeroed). */ |
| |
| const relax_typeS md_relax_table[] = |
| { |
| /* The fields are: |
| 1) most positive reach of this state, |
| 2) most negative reach of this state, |
| 3) how many bytes this mode will have in the variable part of the frag |
| 4) which index into the table to try if we can't fit into this one. */ |
| |
| /* UNCOND_JUMP states. */ |
| {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (UNCOND_JUMP, BIG)}, |
| {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (UNCOND_JUMP, BIG16)}, |
| /* dword jmp adds 4 bytes to frag: |
| 0 extra opcode bytes, 4 displacement bytes. */ |
| {0, 0, 4, 0}, |
| /* word jmp adds 2 byte2 to frag: |
| 0 extra opcode bytes, 2 displacement bytes. */ |
| {0, 0, 2, 0}, |
| |
| /* COND_JUMP states. */ |
| {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (COND_JUMP, BIG)}, |
| {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (COND_JUMP, BIG16)}, |
| /* dword conditionals adds 5 bytes to frag: |
| 1 extra opcode byte, 4 displacement bytes. */ |
| {0, 0, 5, 0}, |
| /* word conditionals add 3 bytes to frag: |
| 1 extra opcode byte, 2 displacement bytes. */ |
| {0, 0, 3, 0}, |
| |
| /* COND_JUMP86 states. */ |
| {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (COND_JUMP86, BIG)}, |
| {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (COND_JUMP86, BIG16)}, |
| /* dword conditionals adds 5 bytes to frag: |
| 1 extra opcode byte, 4 displacement bytes. */ |
| {0, 0, 5, 0}, |
| /* word conditionals add 4 bytes to frag: |
| 1 displacement byte and a 3 byte long branch insn. */ |
| {0, 0, 4, 0} |
| }; |
| |
| #define ARCH(n, t, f, s) \ |
| { STRING_COMMA_LEN (#n), s, PROCESSOR_ ## t, vsz_none, CPU_ ## f ## _FLAGS, \ |
| CPU_NONE_FLAGS } |
| #define SUBARCH(n, e, d, s) \ |
| { STRING_COMMA_LEN (#n), s, PROCESSOR_NONE, vsz_none, CPU_ ## e ## _FLAGS, \ |
| CPU_ ## d ## _FLAGS } |
| #define VECARCH(n, e, d, v) \ |
| { STRING_COMMA_LEN (#n), false, PROCESSOR_NONE, vsz_ ## v, \ |
| CPU_ ## e ## _FLAGS, CPU_ ## d ## _FLAGS } |
| |
| static const arch_entry cpu_arch[] = |
| { |
| /* Do not replace the first two entries - i386_target_format() and |
| set_cpu_arch() rely on them being there in this order. */ |
| ARCH (generic32, GENERIC32, GENERIC32, false), |
| ARCH (generic64, GENERIC64, GENERIC64, false), |
| ARCH (i8086, UNKNOWN, NONE, 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 (pentium, PENTIUM, 586, false), |
| ARCH (i686, I686, 686, false), |
| ARCH (pentiumpro, PENTIUMPRO, PENTIUMPRO, false), |
| ARCH (pentiumii, PENTIUMPRO, P2, false), |
| ARCH (pentiumiii, PENTIUMPRO, P3, false), |
| ARCH (pentium4, PENTIUM4, P4, false), |
| ARCH (prescott, NOCONA, CORE, false), |
| ARCH (nocona, NOCONA, NOCONA, false), |
| ARCH (yonah, CORE, CORE, true), |
| ARCH (core, CORE, CORE, false), |
| ARCH (merom, CORE2, CORE2, true), |
| ARCH (core2, CORE2, CORE2, false), |
| ARCH (corei7, COREI7, COREI7, false), |
| ARCH (iamcu, IAMCU, IAMCU, false), |
| ARCH (k6, K6, K6, false), |
| ARCH (k6_2, K6, K6_2, false), |
| ARCH (athlon, ATHLON, ATHLON, false), |
| ARCH (sledgehammer, K8, K8, true), |
| ARCH (opteron, K8, K8, false), |
| ARCH (k8, K8, K8, false), |
| ARCH (amdfam10, AMDFAM10, AMDFAM10, false), |
| ARCH (bdver1, BD, BDVER1, false), |
| ARCH (bdver2, BD, BDVER2, false), |
| ARCH (bdver3, BD, BDVER3, false), |
| ARCH (bdver4, BD, BDVER4, false), |
| ARCH (znver1, ZNVER, ZNVER1, false), |
| ARCH (znver2, ZNVER, ZNVER2, false), |
| ARCH (znver3, ZNVER, ZNVER3, false), |
| ARCH (znver4, ZNVER, ZNVER4, false), |
| ARCH (znver5, ZNVER, ZNVER5, false), |
| ARCH (btver1, BT, BTVER1, false), |
| ARCH (btver2, BT, BTVER2, false), |
| |
| 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, CMOV, false), |
| SUBARCH (fxsr, FXSR, ANY_FXSR, false), |
| SUBARCH (mmx, MMX, ANY_MMX, false), |
| SUBARCH (sse, SSE, ANY_SSE, false), |
| SUBARCH (sse2, SSE2, ANY_SSE2, false), |
| SUBARCH (sse3, SSE3, ANY_SSE3, false), |
| SUBARCH (sse4a, SSE4A, ANY_SSE4A, false), |
| SUBARCH (ssse3, SSSE3, ANY_SSSE3, false), |
| SUBARCH (sse4.1, SSE4_1, ANY_SSE4_1, false), |
| SUBARCH (sse4.2, SSE4_2, ANY_SSE4_2, false), |
| SUBARCH (sse4, SSE4_2, ANY_SSE4_1, false), |
| VECARCH (avx, AVX, ANY_AVX, reset), |
| VECARCH (avx2, AVX2, ANY_AVX2, reset), |
| VECARCH (avx512f, AVX512F, ANY_AVX512F, reset), |
| VECARCH (avx512cd, AVX512CD, ANY_AVX512CD, reset), |
| VECARCH (avx512er, AVX512ER, ANY_AVX512ER, reset), |
| VECARCH (avx512pf, AVX512PF, ANY_AVX512PF, reset), |
| VECARCH (avx512dq, AVX512DQ, ANY_AVX512DQ, reset), |
| VECARCH (avx512bw, AVX512BW, ANY_AVX512BW, reset), |
| VECARCH (avx512vl, AVX512VL, ANY_AVX512VL, reset), |
| SUBARCH (monitor, MONITOR, MONITOR, false), |
| SUBARCH (vmx, VMX, ANY_VMX, false), |
| SUBARCH (vmfunc, VMFUNC, ANY_VMFUNC, false), |
| SUBARCH (smx, SMX, SMX, false), |
| 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, PCLMULQDQ, ANY_PCLMULQDQ, false), |
| SUBARCH (clmul, PCLMULQDQ, ANY_PCLMULQDQ, true), |
| SUBARCH (fsgsbase, FSGSBASE, FSGSBASE, false), |
| SUBARCH (rdrnd, RDRND, RDRND, false), |
| SUBARCH (f16c, F16C, ANY_F16C, false), |
| SUBARCH (bmi2, BMI2, BMI2, 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 (lahf_sahf, LAHF_SAHF, LAHF_SAHF, 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, 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, ANY_3DNOW, false), |
| SUBARCH (3dnowa, 3DNOWA, ANY_3DNOWA, false), |
| SUBARCH (padlock, PADLOCK, PADLOCK, 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 (adx, ADX, ADX, false), |
| SUBARCH (rdseed, RDSEED, RDSEED, false), |
| SUBARCH (prfchw, PRFCHW, PRFCHW, false), |
| SUBARCH (smap, SMAP, SMAP, 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 (clwb, CLWB, CLWB, false), |
| VECARCH (avx512ifma, AVX512IFMA, ANY_AVX512IFMA, reset), |
| VECARCH (avx512vbmi, AVX512VBMI, ANY_AVX512VBMI, reset), |
| VECARCH (avx512_4fmaps, AVX512_4FMAPS, ANY_AVX512_4FMAPS, reset), |
| VECARCH (avx512_4vnniw, AVX512_4VNNIW, ANY_AVX512_4VNNIW, reset), |
| VECARCH (avx512_vpopcntdq, AVX512_VPOPCNTDQ, ANY_AVX512_VPOPCNTDQ, reset), |
| VECARCH (avx512_vbmi2, AVX512_VBMI2, ANY_AVX512_VBMI2, reset), |
| VECARCH (avx512_vnni, AVX512_VNNI, ANY_AVX512_VNNI, reset), |
| VECARCH (avx512_bitalg, AVX512_BITALG, ANY_AVX512_BITALG, reset), |
| VECARCH (avx_vnni, AVX_VNNI, ANY_AVX_VNNI, reset), |
| SUBARCH (clzero, CLZERO, CLZERO, false), |
| SUBARCH (mwaitx, MWAITX, MWAITX, false), |
| SUBARCH (ospke, OSPKE, ANY_OSPKE, false), |
| SUBARCH (rdpid, RDPID, RDPID, false), |
| SUBARCH (ptwrite, PTWRITE, PTWRITE, false), |
| SUBARCH (ibt, IBT, IBT, false), |
| SUBARCH (shstk, SHSTK, SHSTK, false), |
| SUBARCH (gfni, GFNI, ANY_GFNI, false), |
| VECARCH (vaes, VAES, ANY_VAES, reset), |
| VECARCH (vpclmulqdq, VPCLMULQDQ, ANY_VPCLMULQDQ, reset), |
| 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, ANY_AMX_FP16, false), |
| SUBARCH (amx_complex, AMX_COMPLEX, ANY_AMX_COMPLEX, false), |
| SUBARCH (amx_tile, AMX_TILE, ANY_AMX_TILE, false), |
| SUBARCH (movdiri, MOVDIRI, MOVDIRI, false), |
| SUBARCH (movdir64b, MOVDIR64B, MOVDIR64B, false), |
| VECARCH (avx512_bf16, AVX512_BF16, ANY_AVX512_BF16, reset), |
| VECARCH (avx512_vp2intersect, AVX512_VP2INTERSECT, |
| ANY_AVX512_VP2INTERSECT, reset), |
| 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, 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, UINTR, false), |
| SUBARCH (hreset, HRESET, HRESET, false), |
| VECARCH (avx512_fp16, AVX512_FP16, ANY_AVX512_FP16, reset), |
| SUBARCH (prefetchi, PREFETCHI, PREFETCHI, false), |
| VECARCH (avx_ifma, AVX_IFMA, ANY_AVX_IFMA, reset), |
| VECARCH (avx_vnni_int8, AVX_VNNI_INT8, ANY_AVX_VNNI_INT8, reset), |
| SUBARCH (cmpccxadd, CMPCCXADD, CMPCCXADD, false), |
| SUBARCH (wrmsrns, WRMSRNS, WRMSRNS, false), |
| SUBARCH (msrlist, MSRLIST, MSRLIST, false), |
| VECARCH (avx_ne_convert, AVX_NE_CONVERT, ANY_AVX_NE_CONVERT, reset), |
| SUBARCH (rao_int, RAO_INT, RAO_INT, false), |
| SUBARCH (rmpquery, RMPQUERY, ANY_RMPQUERY, false), |
| SUBARCH (fred, FRED, ANY_FRED, false), |
| SUBARCH (lkgs, LKGS, ANY_LKGS, false), |
| VECARCH (avx_vnni_int16, AVX_VNNI_INT16, ANY_AVX_VNNI_INT16, reset), |
| VECARCH (sha512, SHA512, ANY_SHA512, reset), |
| VECARCH (sm3, SM3, ANY_SM3, reset), |
| VECARCH (sm4, SM4, ANY_SM4, reset), |
| SUBARCH (pbndkb, PBNDKB, PBNDKB, false), |
| VECARCH (avx10.1, AVX10_1, ANY_AVX512F, set), |
| SUBARCH (user_msr, USER_MSR, USER_MSR, false), |
| SUBARCH (apx_f, APX_F, APX_F, false), |
| VECARCH (avx10.2, AVX10_2, ANY_AVX10_2, set), |
| }; |
| |
| #undef SUBARCH |
| #undef ARCH |
| |
| #ifdef I386COFF |
| /* Like s_lcomm_internal in gas/read.c but the alignment string |
| is allowed to be optional. */ |
| |
| static symbolS * |
| pe_lcomm_internal (int needs_align, symbolS *symbolP, addressT size) |
| { |
| addressT align = 0; |
| |
| SKIP_WHITESPACE (); |
| |
| if (needs_align |
| && *input_line_pointer == ',') |
| { |
| align = parse_align (needs_align - 1); |
| |
| if (align == (addressT) -1) |
| return NULL; |
| } |
| else |
| { |
| if (size >= 8) |
| align = 3; |
| else if (size >= 4) |
| align = 2; |
| else if (size >= 2) |
| align = 1; |
| else |
| align = 0; |
| } |
| |
| bss_alloc (symbolP, size, align); |
| return symbolP; |
| } |
| |
| static void |
| pe_lcomm (int needs_align) |
| { |
| s_comm_internal (needs_align * 2, pe_lcomm_internal); |
| } |
| #endif |
| |
| const pseudo_typeS md_pseudo_table[] = |
| { |
| #if !defined(OBJ_AOUT) && !defined(USE_ALIGN_PTWO) |
| {"align", s_align_bytes, 0}, |
| #else |
| {"align", s_align_ptwo, 0}, |
| #endif |
| {"arch", set_cpu_arch, 0}, |
| #ifdef OBJ_AOUT |
| {"bss", s_bss, 0}, |
| #endif |
| #ifdef I386COFF |
| {"lcomm", pe_lcomm, 1}, |
| #endif |
| {"ffloat", float_cons, 'f'}, |
| {"dfloat", float_cons, 'd'}, |
| {"tfloat", float_cons, 'x'}, |
| {"hfloat", float_cons, 'h'}, |
| {"bfloat16", float_cons, 'b'}, |
| {"value", cons, 2}, |
| {"slong", signed_cons, 4}, |
| {"insn", s_insn, 0}, |
| {"noopt", s_noopt, 0}, |
| {"optim", s_ignore, 0}, |
| {"code16gcc", set_16bit_gcc_code_flag, CODE_16BIT}, |
| {"code16", set_code_flag, CODE_16BIT}, |
| {"code32", set_code_flag, CODE_32BIT}, |
| #ifdef BFD64 |
| {"code64", set_code_flag, CODE_64BIT}, |
| #endif |
| {"intel_syntax", set_intel_syntax, 1}, |
| {"att_syntax", set_intel_syntax, 0}, |
| {"intel_mnemonic", set_intel_mnemonic, 1}, |
| {"att_mnemonic", set_intel_mnemonic, 0}, |
| {"allow_index_reg", set_allow_index_reg, 1}, |
| {"disallow_index_reg", set_allow_index_reg, 0}, |
| {"sse_check", set_check, 0}, |
| {"operand_check", set_check, 1}, |
| #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
| {"largecomm", handle_large_common, 0}, |
| #else |
| {"file", dwarf2_directive_file, 0}, |
| {"loc", dwarf2_directive_loc, 0}, |
| {"loc_mark_labels", dwarf2_directive_loc_mark_labels, 0}, |
| #endif |
| #ifdef TE_PE |
| {"secrel32", pe_directive_secrel, 0}, |
| {"secidx", pe_directive_secidx, 0}, |
| #endif |
| {0, 0, 0} |
| }; |
| |
| /* For interface with expression (). */ |
| extern char *input_line_pointer; |
| |
| /* Hash table for instruction mnemonic lookup. */ |
| static htab_t op_hash; |
| |
| /* Hash table for register lookup. */ |
| static htab_t reg_hash; |
| |
| static const struct |
| { |
| const char *str; |
| unsigned int len; |
| const enum bfd_reloc_code_real rel[2]; |
| const i386_operand_type types64; |
| bool need_GOT_symbol; |
| } |
| gotrel[] = |
| { |
| #define OPERAND_TYPE_IMM32_32S_DISP32 { .bitfield = \ |
| { .imm32 = 1, .imm32s = 1, .disp32 = 1 } } |
| #define OPERAND_TYPE_IMM32_32S_64_DISP32 { .bitfield = \ |
| { .imm32 = 1, .imm32s = 1, .imm64 = 1, .disp32 = 1 } } |
| #define OPERAND_TYPE_IMM32_32S_64_DISP32_64 { .bitfield = \ |
| { .imm32 = 1, .imm32s = 1, .imm64 = 1, .disp32 = 1, .disp64 = 1 } } |
| #define OPERAND_TYPE_IMM64_DISP64 { .bitfield = \ |
| { .imm64 = 1, .disp64 = 1 } } |
| |
| #ifndef TE_PE |
| #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
| { STRING_COMMA_LEN ("SIZE"), { BFD_RELOC_SIZE32, |
| BFD_RELOC_SIZE32 }, |
| { .bitfield = { .imm32 = 1, .imm64 = 1 } }, false }, |
| #endif |
| { STRING_COMMA_LEN ("PLTOFF"), { _dummy_first_bfd_reloc_code_real, |
| BFD_RELOC_X86_64_PLTOFF64 }, |
| { .bitfield = { .imm64 = 1 } }, true }, |
| { STRING_COMMA_LEN ("PLT"), { BFD_RELOC_386_PLT32, |
| BFD_RELOC_X86_64_PLT32 }, |
| OPERAND_TYPE_IMM32_32S_DISP32, false }, |
| { STRING_COMMA_LEN ("GOTPLT"), { _dummy_first_bfd_reloc_code_real, |
| BFD_RELOC_X86_64_GOTPLT64 }, |
| OPERAND_TYPE_IMM64_DISP64, true }, |
| { STRING_COMMA_LEN ("GOTOFF"), { BFD_RELOC_386_GOTOFF, |
| BFD_RELOC_X86_64_GOTOFF64 }, |
| OPERAND_TYPE_IMM64_DISP64, true }, |
| { STRING_COMMA_LEN ("GOTPCREL"), { _dummy_first_bfd_reloc_code_real, |
| BFD_RELOC_X86_64_GOTPCREL }, |
| OPERAND_TYPE_IMM32_32S_DISP32, true }, |
| { STRING_COMMA_LEN ("TLSGD"), { BFD_RELOC_386_TLS_GD, |
| BFD_RELOC_X86_64_TLSGD }, |
| OPERAND_TYPE_IMM32_32S_DISP32, true }, |
| { STRING_COMMA_LEN ("TLSLDM"), { BFD_RELOC_386_TLS_LDM, |
| _dummy_first_bfd_reloc_code_real }, |
| OPERAND_TYPE_NONE, true }, |
| { STRING_COMMA_LEN ("TLSLD"), { _dummy_first_bfd_reloc_code_real, |
| BFD_RELOC_X86_64_TLSLD }, |
| OPERAND_TYPE_IMM32_32S_DISP32, true }, |
| { STRING_COMMA_LEN ("GOTTPOFF"), { BFD_RELOC_386_TLS_IE_32, |
| BFD_RELOC_X86_64_GOTTPOFF }, |
| OPERAND_TYPE_IMM32_32S_DISP32, true }, |
| { STRING_COMMA_LEN ("TPOFF"), { BFD_RELOC_386_TLS_LE_32, |
| BFD_RELOC_X86_64_TPOFF32 }, |
| OPERAND_TYPE_IMM32_32S_64_DISP32_64, true }, |
| { STRING_COMMA_LEN ("NTPOFF"), { BFD_RELOC_386_TLS_LE, |
| _dummy_first_bfd_reloc_code_real }, |
| OPERAND_TYPE_NONE, true }, |
| { STRING_COMMA_LEN ("DTPOFF"), { BFD_RELOC_386_TLS_LDO_32, |
| BFD_RELOC_X86_64_DTPOFF32 }, |
| OPERAND_TYPE_IMM32_32S_64_DISP32_64, true }, |
| { STRING_COMMA_LEN ("GOTNTPOFF"),{ BFD_RELOC_386_TLS_GOTIE, |
| _dummy_first_bfd_reloc_code_real }, |
| OPERAND_TYPE_NONE, true }, |
| { STRING_COMMA_LEN ("INDNTPOFF"),{ BFD_RELOC_386_TLS_IE, |
| _dummy_first_bfd_reloc_code_real }, |
| OPERAND_TYPE_NONE, true }, |
| { STRING_COMMA_LEN ("GOT"), { BFD_RELOC_386_GOT32, |
| BFD_RELOC_X86_64_GOT32 }, |
| OPERAND_TYPE_IMM32_32S_64_DISP32, true }, |
| { STRING_COMMA_LEN ("TLSDESC"), { BFD_RELOC_386_TLS_GOTDESC, |
| BFD_RELOC_X86_64_GOTPC32_TLSDESC }, |
| OPERAND_TYPE_IMM32_32S_DISP32, true }, |
| { STRING_COMMA_LEN ("TLSCALL"), { BFD_RELOC_386_TLS_DESC_CALL, |
| BFD_RELOC_X86_64_TLSDESC_CALL }, |
| OPERAND_TYPE_IMM32_32S_DISP32, true }, |
| #else /* TE_PE */ |
| { STRING_COMMA_LEN ("SECREL32"), { BFD_RELOC_32_SECREL, |
| BFD_RELOC_32_SECREL }, |
| OPERAND_TYPE_IMM32_32S_64_DISP32_64, false }, |
| #endif |
| |
| #undef OPERAND_TYPE_IMM32_32S_DISP32 |
| #undef OPERAND_TYPE_IMM32_32S_64_DISP32 |
| #undef OPERAND_TYPE_IMM32_32S_64_DISP32_64 |
| #undef OPERAND_TYPE_IMM64_DISP64 |
| }; |
| |
| /* Various efficient no-op patterns for aligning code labels. |
| Note: Don't try to assemble the instructions in the comments. |
| 0L and 0w are not legal. */ |
| static const unsigned char f32_1[] = |
| {0x90}; /* nop */ |
| static const unsigned char f32_2[] = |
| {0x66,0x90}; /* xchg %ax,%ax */ |
| static const unsigned char f32_3[] = |
| {0x8d,0x76,0x00}; /* leal 0(%esi),%esi */ |
| #define f32_4 (f32_5 + 1) /* leal 0(%esi,%eiz),%esi */ |
| static const unsigned char f32_5[] = |
| {0x2e,0x8d,0x74,0x26,0x00}; /* leal %cs:0(%esi,%eiz),%esi */ |
| static const unsigned char f32_6[] = |
| {0x8d,0xb6,0x00,0x00,0x00,0x00}; /* leal 0L(%esi),%esi */ |
| #define f32_7 (f32_8 + 1) /* leal 0L(%esi,%eiz),%esi */ |
| static const unsigned char f32_8[] = |
| {0x2e,0x8d,0xb4,0x26,0x00,0x00,0x00,0x00}; /* leal %cs:0L(%esi,%eiz),%esi */ |
| static const unsigned char f64_3[] = |
| {0x48,0x89,0xf6}; /* mov %rsi,%rsi */ |
| static const unsigned char f64_4[] = |
| {0x48,0x8d,0x76,0x00}; /* lea 0(%rsi),%rsi */ |
| #define f64_5 (f64_6 + 1) /* lea 0(%rsi,%riz),%rsi */ |
| static const unsigned char f64_6[] = |
| {0x2e,0x48,0x8d,0x74,0x26,0x00}; /* lea %cs:0(%rsi,%riz),%rsi */ |
| static const unsigned char f64_7[] = |
| {0x48,0x8d,0xb6,0x00,0x00,0x00,0x00}; /* lea 0L(%rsi),%rsi */ |
| #define f64_8 (f64_9 + 1) /* lea 0L(%rsi,%riz),%rsi */ |
| static const unsigned char f64_9[] = |
| {0x2e,0x48,0x8d,0xb4,0x26,0x00,0x00,0x00,0x00}; /* lea %cs:0L(%rsi,%riz),%rsi */ |
| #define f16_2 (f64_3 + 1) /* mov %si,%si */ |
| static const unsigned char f16_3[] = |
| {0x8d,0x74,0x00}; /* lea 0(%si),%si */ |
| #define f16_4 (f16_5 + 1) /* lea 0W(%si),%si */ |
| static const unsigned char f16_5[] = |
| {0x2e,0x8d,0xb4,0x00,0x00}; /* lea %cs:0W(%si),%si */ |
| static const unsigned char jump_disp8[] = |
| {0xeb}; /* jmp disp8 */ |
| static const unsigned char jump32_disp32[] = |
| {0xe9}; /* jmp disp32 */ |
| static const unsigned char jump16_disp32[] = |
| {0x66,0xe9}; /* jmp disp32 */ |
| /* 32-bit NOPs patterns. */ |
| static const unsigned char *const f32_patt[] = { |
| f32_1, f32_2, f32_3, f32_4, f32_5, f32_6, f32_7, f32_8 |
| }; |
| /* 64-bit NOPs patterns. */ |
| static const unsigned char *const f64_patt[] = { |
| f32_1, f32_2, f64_3, f64_4, f64_5, f64_6, f64_7, f64_8, f64_9 |
| }; |
| /* 16-bit NOPs patterns. */ |
| static const unsigned char *const f16_patt[] = { |
| f32_1, f16_2, f16_3, f16_4, f16_5 |
| }; |
| /* nopl (%[re]ax) */ |
| static const unsigned char alt_3[] = |
| {0x0f,0x1f,0x00}; |
| /* nopl 0(%[re]ax) */ |
| static const unsigned char alt_4[] = |
| {0x0f,0x1f,0x40,0x00}; |
| /* nopl 0(%[re]ax,%[re]ax,1) */ |
| #define alt_5 (alt_6 + 1) |
| /* nopw 0(%[re]ax,%[re]ax,1) */ |
| static const unsigned char alt_6[] = |
| {0x66,0x0f,0x1f,0x44,0x00,0x00}; |
| /* nopl 0L(%[re]ax) */ |
| static const unsigned char alt_7[] = |
| {0x0f,0x1f,0x80,0x00,0x00,0x00,0x00}; |
| /* nopl 0L(%[re]ax,%[re]ax,1) */ |
| #define alt_8 (alt_9 + 1) |
| /* nopw 0L(%[re]ax,%[re]ax,1) */ |
| static const unsigned char alt_9[] = |
| {0x66,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; |
| /* nopw %cs:0L(%[re]ax,%[re]ax,1) */ |
| #define alt_10 (alt_11 + 1) |
| /* data16 nopw %cs:0L(%eax,%eax,1) */ |
| static const unsigned char alt_11[] = |
| {0x66,0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; |
| /* 32-bit and 64-bit NOPs patterns. */ |
| static const unsigned char *const alt_patt[] = { |
| f32_1, f32_2, alt_3, alt_4, alt_5, alt_6, alt_7, alt_8, |
| alt_9, alt_10, alt_11 |
| }; |
| #define alt64_9 (alt64_15 + 6) /* nopq 0L(%rax,%rax,1) */ |
| #define alt64_10 (alt64_15 + 5) /* cs nopq 0L(%rax,%rax,1) */ |
| /* data16 cs nopq 0L(%rax,%rax,1) */ |
| #define alt64_11 (alt64_15 + 4) |
| /* data16 data16 cs nopq 0L(%rax,%rax,1) */ |
| #define alt64_12 (alt64_15 + 3) |
| /* data16 data16 data16 cs nopq 0L(%rax,%rax,1) */ |
| #define alt64_13 (alt64_15 + 2) |
| /* data16 data16 data16 data16 cs nopq 0L(%rax,%rax,1) */ |
| #define alt64_14 (alt64_15 + 1) |
| /* data16 data16 data16 data16 data16 cs nopq 0L(%rax,%rax,1) */ |
| static const unsigned char alt64_15[] = |
| {0x66,0x66,0x66,0x66,0x66,0x2e,0x48, |
| 0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; |
| /* Long 64-bit NOPs patterns. */ |
| static const unsigned char *const alt64_patt[] = { |
| f32_1, f32_2, alt_3, alt_4, alt_5, alt_6, alt_7, alt_8, |
| alt64_9, alt64_10, alt64_11,alt64_12, alt64_13, alt64_14, alt64_15 |
| }; |
| |
| /* Genenerate COUNT bytes of NOPs to WHERE from PATT with the maximum |
| size of a single NOP instruction MAX_SINGLE_NOP_SIZE. */ |
| |
| static void |
| i386_output_nops (char *where, const unsigned char *const *patt, |
| int count, int max_single_nop_size) |
| |
| { |
| /* Place the longer NOP first. */ |
| int last; |
| int offset; |
| const unsigned char *nops; |
| |
| if (max_single_nop_size < 1) |
| { |
| as_fatal (_("i386_output_nops called to generate nops of at most %d bytes!"), |
| max_single_nop_size); |
| return; |
| } |
| |
| nops = patt[max_single_nop_size - 1]; |
| last = count % max_single_nop_size; |
| |
| count -= last; |
| for (offset = 0; offset < count; offset += max_single_nop_size) |
| memcpy (where + offset, nops, max_single_nop_size); |
| |
| if (last) |
| { |
| nops = patt[last - 1]; |
| memcpy (where + offset, nops, last); |
| } |
| } |
| |
| static INLINE int |
| fits_in_imm7 (offsetT num) |
| { |
| return (num & 0x7f) == num; |
| } |
| |
| static INLINE int |
| fits_in_imm31 (offsetT num) |
| { |
| return (num & 0x7fffffff) == num; |
| } |
| |
| /* Genenerate COUNT bytes of NOPs to WHERE with the maximum size of a |
| single NOP instruction LIMIT. */ |
| |
| void |
| i386_generate_nops (fragS *fragP, char *where, offsetT count, int limit) |
| { |
| const unsigned char *const *patt = NULL; |
| int max_single_nop_size; |
| /* Maximum number of NOPs before switching to jump over NOPs. */ |
| int max_number_of_nops; |
| |
| switch (fragP->fr_type) |
| { |
| case rs_fill_nop: |
| case rs_align_code: |
| break; |
| case rs_machine_dependent: |
| /* Allow NOP padding for jumps and calls. */ |
| if (TYPE_FROM_RELAX_STATE (fragP->fr_subtype) == BRANCH_PADDING |
| || TYPE_FROM_RELAX_STATE (fragP->fr_subtype) == FUSED_JCC_PADDING) |
| break; |
| /* Fall through. */ |
| default: |
| return; |
| } |
| |
| /* We need to decide which NOP sequence to use for 32bit and |
| 64bit. When -mtune= is used: |
| |
| 1. For PROCESSOR_I?86, PROCESSOR_PENTIUM, PROCESSOR_IAMCU, and |
| PROCESSOR_GENERIC32, f32_patt will be used. |
| 2. For the rest, alt_patt will be used. |
| |
| When -mtune= isn't used, alt_patt will be used if |
| cpu_arch_isa_flags has CpuNop. Otherwise, f32_patt/f64_patt will |
| be used. |
| |
| When -march= or .arch is used, we can't use anything beyond |
| cpu_arch_isa_flags. */ |
| |
| if (fragP->tc_frag_data.code == CODE_16BIT) |
| { |
| patt = f16_patt; |
| max_single_nop_size = sizeof (f16_patt) / sizeof (f16_patt[0]); |
| /* Limit number of NOPs to 2 in 16-bit mode. */ |
| max_number_of_nops = 2; |
| } |
| else |
| { |
| patt = fragP->tc_frag_data.code == CODE_64BIT ? f64_patt : f32_patt; |
| if (fragP->tc_frag_data.isa == PROCESSOR_UNKNOWN) |
| { |
| /* PROCESSOR_UNKNOWN means that all ISAs may be used, unless |
| explicitly disabled. */ |
| switch (fragP->tc_frag_data.tune) |
| { |
| case PROCESSOR_UNKNOWN: |
| /* We use cpu_arch_isa_flags to check if we SHOULD |
| optimize with nops. */ |
| if (fragP->tc_frag_data.isanop) |
| patt = alt_patt; |
| break; |
| |
| case PROCESSOR_CORE: |
| case PROCESSOR_CORE2: |
| case PROCESSOR_COREI7: |
| if (fragP->tc_frag_data.cpunop) |
| { |
| if (fragP->tc_frag_data.code == CODE_64BIT) |
| patt = alt64_patt; |
| else |
| patt = alt_patt; |
| } |
| break; |
| |
| case PROCESSOR_PENTIUMPRO: |
| case PROCESSOR_PENTIUM4: |
| case PROCESSOR_NOCONA: |
| case PROCESSOR_GENERIC64: |
| case PROCESSOR_K6: |
| case PROCESSOR_ATHLON: |
| case PROCESSOR_K8: |
| case PROCESSOR_AMDFAM10: |
| case PROCESSOR_BD: |
| case PROCESSOR_ZNVER: |
| case PROCESSOR_BT: |
| if (fragP->tc_frag_data.cpunop) |
| patt = alt_patt; |
| break; |
| |
| case PROCESSOR_I386: |
| case PROCESSOR_I486: |
| case PROCESSOR_PENTIUM: |
| case PROCESSOR_I686: |
| case PROCESSOR_IAMCU: |
| case PROCESSOR_GENERIC32: |
| break; |
| case PROCESSOR_NONE: |
| abort (); |
| } |
| } |
| else |
| { |
| switch (fragP->tc_frag_data.tune) |
| { |
| case PROCESSOR_UNKNOWN: |
| /* When cpu_arch_isa is set, cpu_arch_tune shouldn't be |
| PROCESSOR_UNKNOWN. */ |
| abort (); |
| break; |
| |
| default: |
| /* We use cpu_arch_isa_flags to check if we CAN optimize |
| with nops. */ |
| if (fragP->tc_frag_data.isanop) |
| patt = alt_patt; |
| break; |
| |
| case PROCESSOR_NONE: |
| abort (); |
| } |
| } |
| |
| if (patt != alt_patt && patt != alt64_patt) |
| { |
| max_single_nop_size = patt == f32_patt ? ARRAY_SIZE (f32_patt) |
| : ARRAY_SIZE (f64_patt); |
| /* Limit number of NOPs to 2 for older processors. */ |
| max_number_of_nops = 2; |
| } |
| else |
| { |
| max_single_nop_size = patt == alt_patt |
| ? ARRAY_SIZE (alt_patt) |
| : ARRAY_SIZE (alt64_patt); |
| /* Limit number of NOPs to 7 for newer processors. */ |
| max_number_of_nops = 7; |
| } |
| } |
| |
| if (limit == 0) |
| limit = max_single_nop_size; |
| |
| if (fragP->fr_type == rs_fill_nop) |
| { |
| /* Output NOPs for .nop directive. */ |
| if (limit > max_single_nop_size) |
| { |
| as_bad_where (fragP->fr_file, fragP->fr_line, |
| _("invalid single nop size: %d " |
| "(expect within [0, %d])"), |
| limit, max_single_nop_size); |
| return; |
| } |
| } |
| else if (fragP->fr_type != rs_machine_dependent) |
| fragP->fr_var = count; |
| |
| /* Emit a plain NOP first when the last thing we saw may not have been |
| a proper instruction (e.g. a stand-alone prefix or .byte). */ |
| if (!fragP->tc_frag_data.last_insn_normal) |
| { |
| *where++ = 0x90; |
| --count; |
| } |
| |
| if ((count / max_single_nop_size) > max_number_of_nops) |
| { |
| /* Generate jump over NOPs. */ |
| offsetT disp = count - 2; |
| if (fits_in_imm7 (disp)) |
| { |
| /* Use "jmp disp8" if possible. */ |
| count = disp; |
| where[0] = jump_disp8[0]; |
| where[1] = count; |
| where += 2; |
| } |
| else |
| { |
| unsigned int size_of_jump; |
| |
| if (flag_code == CODE_16BIT) |
| { |
| where[0] = jump16_disp32[0]; |
| where[1] = jump16_disp32[1]; |
| size_of_jump = 2; |
| } |
| else |
| { |
| where[0] = jump32_disp32[0]; |
| size_of_jump = 1; |
| } |
| |
| count -= size_of_jump + 4; |
| if (!fits_in_imm31 (count)) |
| { |
| as_bad_where (fragP->fr_file, fragP->fr_line, |
| _("jump over nop padding out of range")); |
| return; |
| } |
| |
| md_number_to_chars (where + size_of_jump, count, 4); |
| where += size_of_jump + 4; |
| } |
| } |
| |
| /* Generate multiple NOPs. */ |
| i386_output_nops (where, patt, count, limit); |
| } |
| |
| static INLINE int |
| operand_type_all_zero (const union i386_operand_type *x) |
| { |
| switch (ARRAY_SIZE(x->array)) |
| { |
| case 3: |
| if (x->array[2]) |
| return 0; |
| /* Fall through. */ |
| case 2: |
| if (x->array[1]) |
| return 0; |
| /* Fall through. */ |
| case 1: |
| return !x->array[0]; |
| default: |
| abort (); |
| } |
| } |
| |
| static INLINE void |
| operand_type_set (union i386_operand_type *x, unsigned int v) |
| { |
| switch (ARRAY_SIZE(x->array)) |
| { |
| case 3: |
| x->array[2] = v; |
| /* Fall through. */ |
| case 2: |
| x->array[1] = v; |
| /* Fall through. */ |
| case 1: |
| x->array[0] = v; |
| /* Fall through. */ |
| break; |
| default: |
| abort (); |
| } |
| |
| x->bitfield.class = ClassNone; |
| x->bitfield.instance = InstanceNone; |
| } |
| |
| static INLINE int |
| operand_type_equal (const union i386_operand_type *x, |
| const union i386_operand_type *y) |
| { |
| switch (ARRAY_SIZE(x->array)) |
| { |
| case 3: |
| if (x->array[2] != y->array[2]) |
| return 0; |
| /* Fall through. */ |
| case 2: |
| if (x->array[1] != y->array[1]) |
| return 0; |
| /* Fall through. */ |
| case 1: |
| return x->array[0] == y->array[0]; |
| break; |
| default: |
| abort (); |
| } |
| } |
| |
| static INLINE bool |
| _is_cpu (const i386_cpu_attr *a, enum i386_cpu cpu) |
| { |
| switch (cpu) |
| { |
| case Cpu287: return a->bitfield.cpu287; |
| case Cpu387: return a->bitfield.cpu387; |
| case Cpu3dnow: return a->bitfield.cpu3dnow; |
| case Cpu3dnowA: return a->bitfield.cpu3dnowa; |
| case CpuAVX: return a->bitfield.cpuavx; |
| case CpuHLE: return a->bitfield.cpuhle; |
| case CpuAVX512F: return a->bitfield.cpuavx512f; |
| case CpuAVX512VL: return a->bitfield.cpuavx512vl; |
| case CpuAPX_F: return a->bitfield.cpuapx_f; |
| case Cpu64: return a->bitfield.cpu64; |
| case CpuNo64: return a->bitfield.cpuno64; |
| default: |
| gas_assert (cpu < CpuAttrEnums); |
| } |
| return a->bitfield.isa == cpu + 1u; |
| } |
| |
| static INLINE bool |
| is_cpu (const insn_template *t, enum i386_cpu cpu) |
| { |
| return _is_cpu(&t->cpu, cpu); |
| } |
| |
| static INLINE bool |
| maybe_cpu (const insn_template *t, enum i386_cpu cpu) |
| { |
| return _is_cpu(&t->cpu_any, cpu); |
| } |
| |
| static i386_cpu_flags cpu_flags_from_attr (i386_cpu_attr a) |
| { |
| const unsigned int bps = sizeof (a.array[0]) * CHAR_BIT; |
| i386_cpu_flags f = { .array[0] = 0 }; |
| |
| switch (ARRAY_SIZE (a.array)) |
| { |
| case 1: |
| f.array[CpuAttrEnums / bps] |
| #ifndef WORDS_BIGENDIAN |
| |= (a.array[0] >> CpuIsaBits) << (CpuAttrEnums % bps); |
| #else |
| |= (a.array[0] << CpuIsaBits) >> (CpuAttrEnums % bps); |
| #endif |
| if (CpuMax / bps > CpuAttrEnums / bps) |
| f.array[CpuAttrEnums / bps + 1] |
| #ifndef WORDS_BIGENDIAN |
| = (a.array[0] >> CpuIsaBits) >> (bps - CpuAttrEnums % bps); |
| #else |
| = (a.array[0] << CpuIsaBits) << (bps - CpuAttrEnums % bps); |
| #endif |
| break; |
| |
| default: |
| abort (); |
| } |
| |
| if (a.bitfield.isa) |
| #ifndef WORDS_BIGENDIAN |
| f.array[(a.bitfield.isa - 1) / bps] |= 1u << ((a.bitfield.isa - 1) % bps); |
| #else |
| f.array[(a.bitfield.isa - 1) / bps] |= 1u << (~(a.bitfield.isa - 1) % bps); |
| #endif |
| |
| return f; |
| } |
| |
| static INLINE int |
| cpu_flags_all_zero (const union i386_cpu_flags *x) |
| { |
| switch (ARRAY_SIZE(x->array)) |
| { |
| case 5: |
| if (x->array[4]) |
| return 0; |
| /* Fall through. */ |
| case 4: |
| if (x->array[3]) |
| return 0; |
| /* Fall through. */ |
| case 3: |
| if (x->array[2]) |
| return 0; |
| /* Fall through. */ |
| case 2: |
| if (x->array[1]) |
| return 0; |
| /* Fall through. */ |
| case 1: |
| return !x->array[0]; |
| default: |
| abort (); |
| } |
| } |
| |
| static INLINE int |
| cpu_flags_equal (const union i386_cpu_flags *x, |
| const union i386_cpu_flags *y) |
| { |
| switch (ARRAY_SIZE(x->array)) |
| { |
| case 5: |
| if (x->array[4] != y->array[4]) |
| return 0; |
| /* Fall through. */ |
| case 4: |
| if (x->array[3] != y->array[3]) |
| return 0; |
| /* Fall through. */ |
| case 3: |
| if (x->array[2] != y->array[2]) |
| return 0; |
| /* Fall through. */ |
| case 2: |
| if (x->array[1] != y->array[1]) |
| return 0; |
| /* Fall through. */ |
| case 1: |
| return x->array[0] == y->array[0]; |
| break; |
| default: |
| abort (); |
| } |
| } |
| |
| static INLINE int |
| cpu_flags_check_cpu64 (const insn_template *t) |
| { |
| return flag_code == CODE_64BIT |
| ? !t->cpu.bitfield.cpuno64 |
| : !t->cpu.bitfield.cpu64; |
| } |
| |
| static INLINE i386_cpu_flags |
| cpu_flags_and (i386_cpu_flags x, i386_cpu_flags y) |
| { |
| switch (ARRAY_SIZE (x.array)) |
| { |
| case 5: |
| x.array [4] &= y.array [4]; |
| /* Fall through. */ |
| case 4: |
| x.array [3] &= y.array [3]; |
| /* Fall through. */ |
| case 3: |
| x.array [2] &= y.array [2]; |
| /* Fall through. */ |
| case 2: |
| x.array [1] &= y.array [1]; |
| /* Fall through. */ |
| case 1: |
| x.array [0] &= y.array [0]; |
| break; |
| default: |
| abort (); |
| } |
| return x; |
| } |
| |
| static INLINE i386_cpu_flags |
| cpu_flags_or (i386_cpu_flags x, i386_cpu_flags y) |
| { |
| switch (ARRAY_SIZE (x.array)) |
| { |
| case 5: |
| x.array [4] |= y.array [4]; |
| /* Fall through. */ |
| case 4: |
| x.array [3] |= y.array [3]; |
| /* Fall through. */ |
| case 3: |
| x.array [2] |= y.array [2]; |
| /* Fall through. */ |
| case 2: |
| x.array [1] |= y.array [1]; |
| /* Fall through. */ |
| case 1: |
| x.array [0] |= y.array [0]; |
| break; |
| default: |
| abort (); |
| } |
| return x; |
| } |
| |
| static INLINE i386_cpu_flags |
| cpu_flags_and_not (i386_cpu_flags x, i386_cpu_flags y) |
| { |
| switch (ARRAY_SIZE (x.array)) |
| { |
| case 5: |
| x.array [4] &= ~y.array [4]; |
| /* Fall through. */ |
| case 4: |
| x.array [3] &= ~y.array [3]; |
| /* Fall through. */ |
| case 3: |
| x.array [2] &= ~y.array [2]; |
| /* Fall through. */ |
| case 2: |
| x.array [1] &= ~y.array [1]; |
| /* Fall through. */ |
| case 1: |
| x.array [0] &= ~y.array [0]; |
| break; |
| default: |
| abort (); |
| } |
| return x; |
| } |
| |
| static const i386_cpu_flags avx512 = CPU_ANY_AVX512F_FLAGS; |
| |
| static INLINE bool need_evex_encoding (const insn_template *t) |
| { |
| return pp.encoding == encoding_evex |
| || pp.encoding == encoding_evex512 |
| || pp.has_nf |
| || (t->opcode_modifier.vex && pp.encoding == encoding_egpr) |
| || i.mask.reg; |
| } |
| |
| #define CPU_FLAGS_ARCH_MATCH 0x1 |
| #define CPU_FLAGS_64BIT_MATCH 0x2 |
| |
| #define CPU_FLAGS_PERFECT_MATCH \ |
| (CPU_FLAGS_ARCH_MATCH | CPU_FLAGS_64BIT_MATCH) |
| |
| static INLINE bool set_oszc_flags (unsigned int oszc_shift) |
| { |
| if (i.oszc_flags & oszc_shift) |
| { |
| as_bad (_("same oszc flag used twice")); |
| return false; |
| } |
| i.oszc_flags |= oszc_shift; |
| return true; |
| } |
| |
| /* Handle SCC OSZC flags. */ |
| |
| static int |
| check_Scc_OszcOperations (const char *l) |
| { |
| const char *suffix_string = l; |
| |
| while (is_space_char (*suffix_string)) |
| suffix_string++; |
| |
| /* If {oszc flags} is absent, just return. */ |
| if (*suffix_string != '{') |
| return 0; |
| |
| /* Skip '{'. */ |
| suffix_string++; |
| |
| /* Parse 'dfv='. */ |
| while (is_space_char (*suffix_string)) |
| suffix_string++; |
| |
| if (strncasecmp (suffix_string, "dfv", 3) == 0) |
| suffix_string += 3; |
| else |
| { |
| as_bad (_("unrecognized pseudo-suffix")); |
| return -1; |
| } |
| |
| while (is_space_char (*suffix_string)) |
| suffix_string++; |
| |
| if (*suffix_string == '=') |
| suffix_string++; |
| else |
| { |
| as_bad (_("unrecognized pseudo-suffix")); |
| return -1; |
| } |
| |
| /* Parse 'of, sf, zf, cf}'. */ |
| while (*suffix_string) |
| { |
| while (is_space_char (*suffix_string)) |
| suffix_string++; |
| |
| /* Return for '{dfv=}'. */ |
| if (*suffix_string == '}') |
| return suffix_string + 1 - l; |
| |
| if (strncasecmp (suffix_string, "of", 2) == 0) |
| { |
| if (!set_oszc_flags (OSZC_OF)) |
| return -1; |
| } |
| else if (strncasecmp (suffix_string, "sf", 2) == 0) |
| { |
| if (!set_oszc_flags (OSZC_SF)) |
| return -1; |
| } |
| else if (strncasecmp (suffix_string, "zf", 2) == 0) |
| { |
| if (!set_oszc_flags (OSZC_ZF)) |
| return -1; |
| } |
| else if (strncasecmp (suffix_string, "cf", 2) == 0) |
| { |
| if (!set_oszc_flags (OSZC_CF)) |
| return -1; |
| } |
| else |
| { |
| as_bad (_("unrecognized oszc flags or illegal `,' in pseudo-suffix")); |
| return -1; |
| } |
| |
| suffix_string += 2; |
| |
| while (is_space_char (*suffix_string)) |
| suffix_string++; |
| |
| if (*suffix_string == '}') |
| return ++suffix_string - l; |
| |
| if (*suffix_string != ',') |
| break; |
| suffix_string ++; |
| } |
| |
| as_bad (_("missing `}' or `,' in pseudo-suffix")); |
| return -1; |
| } |
| |
| /* Return CPU flags match bits. */ |
| |
| static int |
| cpu_flags_match (const insn_template *t) |
| { |
| i386_cpu_flags cpu, active, all = cpu_flags_from_attr (t->cpu); |
| i386_cpu_flags any = cpu_flags_from_attr (t->cpu_any); |
| int match = cpu_flags_check_cpu64 (t) ? CPU_FLAGS_64BIT_MATCH : 0; |
| |
| all.bitfield.cpu64 = 0; |
| all.bitfield.cpuno64 = 0; |
| gas_assert (!any.bitfield.cpu64); |
| gas_assert (!any.bitfield.cpuno64); |
| |
| if (cpu_flags_all_zero (&all) && cpu_flags_all_zero (&any)) |
| { |
| /* This instruction is available on all archs. */ |
| return match | CPU_FLAGS_ARCH_MATCH; |
| } |
| |
| /* This instruction is available only on some archs. */ |
| |
| /* Dual VEX/EVEX templates may need stripping of one of the flags. */ |
| if (t->opcode_modifier.vex && t->opcode_modifier.evex) |
| { |
| /* Dual AVX/AVX512 templates need to retain AVX512* only if we already |
| know that EVEX encoding will be needed. */ |
| if ((any.bitfield.cpuavx || any.bitfield.cpuavx2 || any.bitfield.cpufma) |
| && (any.bitfield.cpuavx512f || any.bitfield.cpuavx512vl)) |
| { |
| if (need_evex_encoding (t)) |
| { |
| any.bitfield.cpuavx = 0; |
| any.bitfield.cpuavx2 = 0; |
| any.bitfield.cpufma = 0; |
| } |
| /* need_evex_encoding(t) isn't reliable before operands were |
| parsed. */ |
| else if (i.operands) |
| { |
| any.bitfield.cpuavx512f = 0; |
| any.bitfield.cpuavx512vl = 0; |
| } |
| } |
| |
| /* Dual non-APX/APX templates need massaging from what APX_F() in the |
| opcode table has produced. While the direct transformation of the |
| incoming cpuid&(cpuid|APX_F) would be to cpuid&(cpuid) / cpuid&(APX_F) |
| respectively, it's cheaper to move to just cpuid / cpuid&APX_F |
| instead. */ |
| if (any.bitfield.cpuapx_f |
| && (any.bitfield.cpubmi || any.bitfield.cpubmi2 |
| || any.bitfield.cpuavx512f || any.bitfield.cpuavx512bw |
| || any.bitfield.cpuavx512dq || any.bitfield.cpuamx_tile |
| || any.bitfield.cpucmpccxadd || any.bitfield.cpuuser_msr)) |
| { |
| /* These checks (verifying that APX_F() was properly used in the |
| opcode table entry) make sure there's no need for an "else" to |
| the "if()" below. */ |
| gas_assert (!cpu_flags_all_zero (&all)); |
| cpu = cpu_flags_and (all, any); |
| gas_assert (cpu_flags_equal (&cpu, &all)); |
| |
| if (need_evex_encoding (t)) |
| all = any; |
| |
| memset (&any, 0, sizeof (any)); |
| } |
| } |
| |
| if (flag_code != CODE_64BIT) |
| active = cpu_flags_and_not (cpu_arch_flags, cpu_64_flags); |
| else |
| active = cpu_arch_flags; |
| cpu = cpu_flags_and (all, active); |
| if (cpu_flags_equal (&cpu, &all)) |
| { |
| /* AVX and AVX2 present at the same time express an operand size |
| dependency - strip AVX2 for the purposes here. The operand size |
| dependent check occurs in check_vecOperands(). */ |
| if (any.bitfield.cpuavx && any.bitfield.cpuavx2) |
| any.bitfield.cpuavx2 = 0; |
| |
| cpu = cpu_flags_and (any, active); |
| if (cpu_flags_all_zero (&any) || !cpu_flags_all_zero (&cpu)) |
| match |= CPU_FLAGS_ARCH_MATCH; |
| } |
| return match; |
| } |
| |
| static INLINE i386_operand_type |
| operand_type_and (i386_operand_type x, i386_operand_type y) |
| { |
| if (x.bitfield.class != y.bitfield.class) |
| x.bitfield.class = ClassNone; |
| if (x.bitfield.instance != y.bitfield.instance) |
| x.bitfield.instance = InstanceNone; |
| |
| switch (ARRAY_SIZE (x.array)) |
| { |
| case 3: |
| x.array [2] &= y.array [2]; |
| /* Fall through. */ |
| case 2: |
| x.array [1] &= y.array [1]; |
| /* Fall through. */ |
| case 1: |
| x.array [0] &= y.array [0]; |
| break; |
| default: |
| abort (); |
| } |
| return x; |
| } |
| |
| static INLINE i386_operand_type |
| operand_type_and_not (i386_operand_type x, i386_operand_type y) |
| { |
| gas_assert (y.bitfield.class == ClassNone); |
| gas_assert (y.bitfield.instance == InstanceNone); |
| |
| switch (ARRAY_SIZE (x.array)) |
| { |
| case 3: |
| x.array [2] &= ~y.array [2]; |
| /* Fall through. */ |
| case 2: |
| x.array [1] &= ~y.array [1]; |
| /* Fall through. */ |
| case 1: |
| x.array [0] &= ~y.array [0]; |
| break; |
| default: |
| abort (); |
| } |
| return x; |
| } |
| |
| static INLINE i386_operand_type |
| operand_type_or (i386_operand_type x, i386_operand_type y) |
| { |
| gas_assert (x.bitfield.class == ClassNone || |
| y.bitfield.class == ClassNone || |
| x.bitfield.class == y.bitfield.class); |
| gas_assert (x.bitfield.instance == InstanceNone || |
| y.bitfield.instance == InstanceNone || |
| x.bitfield.instance == y.bitfield.instance); |
| |
| switch (ARRAY_SIZE (x.array)) |
| { |
| case 3: |
| x.array [2] |= y.array [2]; |
| /* Fall through. */ |
| case 2: |
| x.array [1] |= y.array [1]; |
| /* Fall through. */ |
| case 1: |
| x.array [0] |= y.array [0]; |
| break; |
| default: |
| abort (); |
| } |
| return x; |
| } |
| |
| static INLINE i386_operand_type |
| operand_type_xor (i386_operand_type x, i386_operand_type y) |
| { |
| gas_assert (y.bitfield.class == ClassNone); |
| gas_assert (y.bitfield.instance == InstanceNone); |
| |
| switch (ARRAY_SIZE (x.array)) |
| { |
| case 3: |
| x.array [2] ^= y.array [2]; |
| /* Fall through. */ |
| case 2: |
| x.array [1] ^= y.array [1]; |
| /* Fall through. */ |
| case 1: |
| x.array [0] ^= y.array [0]; |
| break; |
| default: |
| abort (); |
| } |
| return x; |
| } |
| |
| static const i386_operand_type anydisp = { |
| .bitfield = { .disp8 = 1, .disp16 = 1, .disp32 = 1, .disp64 = 1 } |
| }; |
| |
| enum operand_type |
| { |
| reg, |
| imm, |
| disp, |
| anymem |
| }; |
| |
| static INLINE int |
| operand_type_check (i386_operand_type t, enum operand_type c) |
| { |
| switch (c) |
| { |
| case reg: |
| return t.bitfield.class == Reg; |
| |
| case imm: |
| return (t.bitfield.imm8 |
| || t.bitfield.imm8s |
| || t.bitfield.imm16 |
| || t.bitfield.imm32 |
| || t.bitfield.imm32s |
| || t.bitfield.imm64); |
| |
| case disp: |
| return (t.bitfield.disp8 |
| || t.bitfield.disp16 |
| || t.bitfield.disp32 |
| || t.bitfield.disp64); |
| |
| case anymem: |
| return (t.bitfield.disp8 |
| || t.bitfield.disp16 |
| || t.bitfield.disp32 |
| || t.bitfield.disp64 |
| || t.bitfield.baseindex); |
| |
| default: |
| abort (); |
| } |
| |
| return 0; |
| } |
| |
| /* Return 1 if there is no conflict in 8bit/16bit/32bit/64bit/80bit size |
| between operand GIVEN and opeand WANTED for instruction template T. */ |
| |
| static INLINE int |
| match_operand_size (const insn_template *t, unsigned int wanted, |
| unsigned int given) |
| { |
| return !((i.types[given].bitfield.byte |
| && !t->operand_types[wanted].bitfield.byte) |
| || (i.types[given].bitfield.word |
| && !t->operand_types[wanted].bitfield.word) |
| || (i.types[given].bitfield.dword |
| && !t->operand_types[wanted].bitfield.dword) |
| || (i.types[given].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 1 if there is no conflict in SIMD register between operand |
| GIVEN and opeand WANTED for instruction template T. */ |
| |
| static INLINE int |
| match_simd_size (const insn_template *t, unsigned int wanted, |
| unsigned int given) |
| { |
| return !((i.types[given].bitfield.xmmword |
| && !t->operand_types[wanted].bitfield.xmmword) |
| || (i.types[given].bitfield.ymmword |
| && !t->operand_types[wanted].bitfield.ymmword) |
| || (i.types[given].bitfield.zmmword |
| && !t->operand_types[wanted].bitfield.zmmword) |
| || (i.types[given].bitfield.tmmword |
| && !t->operand_types[wanted].bitfield.tmmword)); |
| } |
| |
| /* Return 1 if there is no conflict in any size between operand GIVEN |
| and opeand WANTED for instruction template T. */ |
| |
| static INLINE int |
| match_mem_size (const insn_template *t, unsigned int wanted, |
| unsigned int given) |
| { |
| return (match_operand_size (t, wanted, given) |
| && !((i.types[given].bitfield.unspecified |
| && !i.broadcast.type |
| && !i.broadcast.bytes |
| && !t->operand_types[wanted].bitfield.unspecified) |
| || (i.types[given].bitfield.fword |
| && !t->operand_types[wanted].bitfield.fword) |
| /* For scalar opcode templates to allow register and memory |
| operands at the same time, some special casing is needed |
| here. Also for v{,p}broadcast*, {,v}pmov{s,z}*, and |
| down-conversion vpmov*. */ |
| || ((t->operand_types[wanted].bitfield.class == RegSIMD |
| && 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) |
| : !match_simd_size(t, wanted, given)))); |
| } |
| |
| /* Return value has MATCH_STRAIGHT set if there is no size conflict on any |
| operands for instruction template T, and it has MATCH_REVERSE set if there |
| is no size conflict on any operands for the template with operands reversed |
| (and the template allows for reversing in the first place). */ |
| |
| #define MATCH_STRAIGHT 1 |
| #define MATCH_REVERSE 2 |
| |
| static INLINE unsigned int |
| operand_size_match (const insn_template *t) |
| { |
| unsigned int j, match = MATCH_STRAIGHT; |
| |
| /* Don't check non-absolute jump instructions. */ |
| if (t->opcode_modifier.jump |
| && t->opcode_modifier.jump != JUMP_ABSOLUTE) |
| return match; |
| |
| /* Check memory and accumulator operand size. */ |
| for (j = 0; j < i.operands; j++) |
| { |
| if (i.types[j].bitfield.class != Reg |
| && i.types[j].bitfield.class != RegSIMD |
| && t->opcode_modifier.operandconstraint == ANY_SIZE) |
| continue; |
| |
| if (t->operand_types[j].bitfield.class == Reg |
| && !match_operand_size (t, j, j)) |
| { |
| match = 0; |
| break; |
| } |
| |
| if (t->operand_types[j].bitfield.class == RegSIMD |
| && !match_simd_size (t, j, j)) |
| { |
| match = 0; |
| break; |
| } |
| |
| if (t->operand_types[j].bitfield.instance == Accum |
| && (!match_operand_size (t, j, j) || !match_simd_size (t, j, j))) |
| { |
| match = 0; |
| break; |
| } |
| |
| if ((i.flags[j] & Operand_Mem) && !match_mem_size (t, j, j)) |
| { |
| match = 0; |
| break; |
| } |
| } |
| |
| if (!t->opcode_modifier.d) |
| return match; |
| |
| /* Check reverse. */ |
| gas_assert (i.operands >= 2); |
| |
| for (j = 0; j < i.operands; j++) |
| { |
| unsigned int given = i.operands - j - 1; |
| |
| /* For FMA4 and XOP insns VEX.W controls just the first two |
| register operands. And APX_F insns just swap the two source operands, |
| with the 3rd one being the destination. */ |
| if (is_cpu (t, CpuFMA4) || is_cpu (t, CpuXOP) |
| || is_cpu (t, CpuAPX_F)) |
| given = j < 2 ? 1 - j : j; |
| |
| if (t->operand_types[j].bitfield.class == Reg |
| && !match_operand_size (t, j, given)) |
| return match; |
| |
| if (t->operand_types[j].bitfield.class == RegSIMD |
| && !match_simd_size (t, j, given)) |
| return match; |
| |
| if (t->operand_types[j].bitfield.instance == Accum |
| && (!match_operand_size (t, j, given) |
| || !match_simd_size (t, j, given))) |
| return match; |
| |
| if ((i.flags[given] & Operand_Mem) && !match_mem_size (t, j, given)) |
| return match; |
| } |
| |
| return match | MATCH_REVERSE; |
| } |
| |
| static INLINE int |
| operand_type_match (i386_operand_type overlap, |
| i386_operand_type given) |
| { |
| i386_operand_type temp = overlap; |
| |
| temp.bitfield.unspecified = 0; |
| temp.bitfield.byte = 0; |
| temp.bitfield.word = 0; |
| temp.bitfield.dword = 0; |
| temp.bitfield.fword = 0; |
| temp.bitfield.qword = 0; |
| temp.bitfield.tbyte = 0; |
| temp.bitfield.xmmword = 0; |
| temp.bitfield.ymmword = 0; |
| temp.bitfield.zmmword = 0; |
| temp.bitfield.tmmword = 0; |
| if (operand_type_all_zero (&temp)) |
| goto mismatch; |
| |
| if (given.bitfield.baseindex == overlap.bitfield.baseindex) |
| return 1; |
| |
| mismatch: |
| i.error = operand_type_mismatch; |
| return 0; |
| } |
| |
| /* If given types g0 and g1 are registers they must be of the same type |
| unless the expected operand type register overlap is null. |
| Intel syntax sized memory operands are also checked here. */ |
| |
| static INLINE int |
| operand_type_register_match (i386_operand_type g0, |
| i386_operand_type t0, |
| i386_operand_type g1, |
| i386_operand_type t1) |
| { |
| if (g0.bitfield.class != Reg |
| && g0.bitfield.class != RegSIMD |
| && (g0.bitfield.unspecified |
| || !operand_type_check (g0, anymem))) |
| return 1; |
| |
| if (g1.bitfield.class != Reg |
| && g1.bitfield.class != RegSIMD |
| && (g1.bitfield.unspecified |
| || !operand_type_check (g1, anymem))) |
| return 1; |
| |
| if (g0.bitfield.byte == g1.bitfield.byte |
| && g0.bitfield.word == g1.bitfield.word |
| && g0.bitfield.dword == g1.bitfield.dword |
| && g0.bitfield.qword == g1.bitfield.qword |
| && g0.bitfield.xmmword == g1.bitfield.xmmword |
| && g0.bitfield.ymmword == g1.bitfield.ymmword |
| && g0.bitfield.zmmword == g1.bitfield.zmmword) |
| return 1; |
| |
| /* If expectations overlap in no more than a single size, all is fine. */ |
| g0 = operand_type_and (t0, t1); |
| if (g0.bitfield.byte |
| + g0.bitfield.word |
| + g0.bitfield.dword |
| + g0.bitfield.qword |
| + g0.bitfield.xmmword |
| + g0.bitfield.ymmword |
| + g0.bitfield.zmmword <= 1) |
| return 1; |
| |
| i.error = register_type_mismatch; |
| |
| return 0; |
| } |
| |
| static INLINE unsigned int |
| register_number (const reg_entry *r) |
| { |
| unsigned int nr = r->reg_num; |
| |
| if (r->reg_flags & RegRex) |
| nr += 8; |
| |
| if (r->reg_flags & (RegVRex | RegRex2)) |
| nr += 16; |
| |
| return nr; |
| } |
| |
| static INLINE unsigned int |
| mode_from_disp_size (i386_operand_type t) |
| { |
| if (t.bitfield.disp8) |
| return 1; |
| else if (t.bitfield.disp16 |
| || t.bitfield.disp32) |
| return 2; |
| else |
| return 0; |
| } |
| |
| static INLINE int |
| fits_in_signed_byte (addressT num) |
| { |
| return num + 0x80 <= 0xff; |
| } |
| |
| static INLINE int |
| fits_in_unsigned_byte (addressT num) |
| { |
| return num <= 0xff; |
| } |
| |
| static INLINE int |
| fits_in_unsigned_word (addressT num) |
| { |
| return num <= 0xffff; |
| } |
| |
| static INLINE int |
| fits_in_signed_word (addressT num) |
| { |
| return num + 0x8000 <= 0xffff; |
| } |
| |
| static INLINE int |
| fits_in_signed_long (addressT num ATTRIBUTE_UNUSED) |
| { |
| #ifndef BFD64 |
| return 1; |
| #else |
| return num + 0x80000000 <= 0xffffffff; |
| #endif |
| } /* fits_in_signed_long() */ |
| |
| static INLINE int |
| fits_in_unsigned_long (addressT num ATTRIBUTE_UNUSED) |
| { |
| #ifndef BFD64 |
| return 1; |
| #else |
| return num <= 0xffffffff; |
| #endif |
| } /* fits_in_unsigned_long() */ |
| |
| static INLINE valueT extend_to_32bit_address (addressT num) |
| { |
| #ifdef BFD64 |
| if (fits_in_unsigned_long(num)) |
| return (num ^ ((addressT) 1 << 31)) - ((addressT) 1 << 31); |
| |
| if (!fits_in_signed_long (num)) |
| return num & 0xffffffff; |
| #endif |
| |
| return num; |
| } |
| |
| static INLINE int |
| fits_in_disp8 (offsetT num) |
| { |
| int shift = i.memshift; |
| unsigned int mask; |
| |
| if (shift == -1) |
| abort (); |
| |
| mask = (1 << shift) - 1; |
| |
| /* Return 0 if NUM isn't properly aligned. */ |
| if ((num & mask)) |
| return 0; |
| |
| /* Check if NUM will fit in 8bit after shift. */ |
| return fits_in_signed_byte (num >> shift); |
| } |
| |
| static INLINE int |
| fits_in_imm4 (offsetT num) |
| { |
| /* Despite the name, check for imm3 if we're dealing with EVEX. */ |
| return (num & (pp.encoding != encoding_evex |
| && pp.encoding != encoding_egpr ? 0xf : 7)) == num; |
| } |
| |
| static i386_operand_type |
| smallest_imm_type (offsetT num) |
| { |
| i386_operand_type t; |
| |
| operand_type_set (&t, 0); |
| t.bitfield.imm64 = 1; |
| |
| if (cpu_arch_tune != PROCESSOR_I486 && num == 1) |
| { |
| /* This code is disabled on the 486 because all the Imm1 forms |
| in the opcode table are slower on the i486. They're the |
| versions with the implicitly specified single-position |
| displacement, which has another syntax if you really want to |
| use that form. */ |
| t.bitfield.imm1 = 1; |
| t.bitfield.imm8 = 1; |
| t.bitfield.imm8s = 1; |
| t.bitfield.imm16 = 1; |
| t.bitfield.imm32 = 1; |
| t.bitfield.imm32s = 1; |
| } |
| else if (fits_in_signed_byte (num)) |
| { |
| if (fits_in_unsigned_byte (num)) |
| t.bitfield.imm8 = 1; |
| t.bitfield.imm8s = 1; |
| t.bitfield.imm16 = 1; |
| if (flag_code != CODE_64BIT || fits_in_unsigned_long (num)) |
| t.bitfield.imm32 = 1; |
| t.bitfield.imm32s = 1; |
| } |
| else if (fits_in_unsigned_byte (num)) |
| { |
| t.bitfield.imm8 = 1; |
| t.bitfield.imm16 = 1; |
| t.bitfield.imm32 = 1; |
| t.bitfield.imm32s = 1; |
| } |
| else if (fits_in_signed_word (num) || fits_in_unsigned_word (num)) |
| { |
| t.bitfield.imm16 = 1; |
| if (flag_code != CODE_64BIT || fits_in_unsigned_long (num)) |
| t.bitfield.imm32 = 1; |
| t.bitfield.imm32s = 1; |
| } |
| else if (fits_in_signed_long (num)) |
| { |
| if (flag_code != CODE_64BIT || fits_in_unsigned_long (num)) |
| t.bitfield.imm32 = 1; |
| t.bitfield.imm32s = 1; |
| } |
| else if (fits_in_unsigned_long (num)) |
| t.bitfield.imm32 = 1; |
| |
| return t; |
| } |
| |
| static offsetT |
| offset_in_range (offsetT val, int size) |
| { |
| addressT mask; |
| |
| switch (size) |
| { |
| case 1: mask = ((addressT) 1 << 8) - 1; break; |
| case 2: mask = ((addressT) 1 << 16) - 1; break; |
| #ifdef BFD64 |
| case 4: mask = ((addressT) 1 << 32) - 1; break; |
| #endif |
| case sizeof (val): return val; |
| default: abort (); |
| } |
| |
| if ((val & ~mask) != 0 && (-val & ~mask) != 0) |
| as_warn (_("0x%" PRIx64 " shortened to 0x%" PRIx64), |
| (uint64_t) val, (uint64_t) (val & mask)); |
| |
| return val & mask; |
| } |
| |
| static INLINE const char *insn_name (const insn_template *t) |
| { |
| return &i386_mnemonics[t->mnem_off]; |
| } |
| |
| enum PREFIX_GROUP |
| { |
| PREFIX_EXIST = 0, |
| PREFIX_LOCK, |
| PREFIX_REP, |
| PREFIX_DS, |
| PREFIX_OTHER |
| }; |
| |
| /* Returns |
| a. PREFIX_EXIST if attempting to add a prefix where one from the |
| same class already exists. |
| b. PREFIX_LOCK if lock prefix is added. |
| c. PREFIX_REP if rep/repne prefix is added. |
| d. PREFIX_DS if ds prefix is added. |
| e. PREFIX_OTHER if other prefix is added. |
| */ |
| |
| static enum PREFIX_GROUP |
| add_prefix (unsigned int prefix) |
| { |
| enum PREFIX_GROUP ret = PREFIX_OTHER; |
| unsigned int q; |
| |
| if (prefix >= REX_OPCODE && prefix < REX_OPCODE + 16 |
| && flag_code == CODE_64BIT) |
| { |
| if ((i.prefix[REX_PREFIX] & prefix & REX_W) |
| || (i.prefix[REX_PREFIX] & prefix & REX_R) |
| || (i.prefix[REX_PREFIX] & prefix & REX_X) |
| || (i.prefix[REX_PREFIX] & prefix & REX_B)) |
| ret = PREFIX_EXIST; |
| q = REX_PREFIX; |
| } |
| else |
| { |
| switch (prefix) |
| { |
| default: |
| abort (); |
| |
| case DS_PREFIX_OPCODE: |
| ret = PREFIX_DS; |
| /* Fall through. */ |
| case CS_PREFIX_OPCODE: |
| case ES_PREFIX_OPCODE: |
| case FS_PREFIX_OPCODE: |
| case GS_PREFIX_OPCODE: |
| case SS_PREFIX_OPCODE: |
| q = SEG_PREFIX; |
| break; |
| |
| case REPNE_PREFIX_OPCODE: |
| case REPE_PREFIX_OPCODE: |
| q = REP_PREFIX; |
| ret = PREFIX_REP; |
| break; |
| |
| case LOCK_PREFIX_OPCODE: |
| q = LOCK_PREFIX; |
| ret = PREFIX_LOCK; |
| break; |
| |
| case FWAIT_OPCODE: |
| q = WAIT_PREFIX; |
| break; |
| |
| case ADDR_PREFIX_OPCODE: |
| q = ADDR_PREFIX; |
| break; |
| |
| case DATA_PREFIX_OPCODE: |
| q = DATA_PREFIX; |
| break; |
| } |
| if (i.prefix[q] != 0) |
| ret = PREFIX_EXIST; |
| } |
| |
| if (ret) |
| { |
| if (!i.prefix[q]) |
| ++i.prefixes; |
| i.prefix[q] |= prefix; |
| } |
| else |
| as_bad (_("same type of prefix used twice")); |
| |
| return ret; |
| } |
| |
| static void |
| update_code_flag (int value, int check) |
| { |
| PRINTF_LIKE ((*as_error)) = check ? as_fatal : as_bad; |
| |
| if (value == CODE_64BIT && !cpu_arch_flags.bitfield.cpu64 ) |
| { |
| as_error (_("64bit mode not supported on `%s'."), |
| cpu_arch_name ? cpu_arch_name : default_arch); |
| return; |
| } |
| |
| if (value == CODE_32BIT && !cpu_arch_flags.bitfield.cpui386) |
| { |
| as_error (_("32bit mode not supported on `%s'."), |
| cpu_arch_name ? cpu_arch_name : default_arch); |
| return; |
| } |
| |
| flag_code = (enum flag_code) value; |
| |
| stackop_size = '\0'; |
| } |
| |
| static void |
| set_code_flag (int value) |
| { |
| update_code_flag (value, 0); |
| } |
| |
| static void |
| set_16bit_gcc_code_flag (int new_code_flag) |
| { |
| flag_code = (enum flag_code) new_code_flag; |
| if (flag_code != CODE_16BIT) |
| abort (); |
| stackop_size = LONG_MNEM_SUFFIX; |
| } |
| |
| static void |
| _set_intel_syntax (int syntax_flag) |
| { |
| intel_syntax = syntax_flag; |
| |
| expr_set_rank (O_full_ptr, syntax_flag ? 10 : 0); |
| |
| register_prefix = allow_naked_reg ? "" : "%"; |
| } |
| |
| static void |
| set_intel_syntax (int syntax_flag) |
| { |
| /* Find out if register prefixing is specified. */ |
| int ask_naked_reg = 0; |
| |
| SKIP_WHITESPACE (); |
| if (!is_end_of_line[(unsigned char) *input_line_pointer]) |
| { |
| char *string; |
| int e = get_symbol_name (&string); |
| |
| if (strcmp (string, "prefix") == 0) |
| ask_naked_reg = 1; |
| else if (strcmp (string, "noprefix") == 0) |
| ask_naked_reg = -1; |
| else |
| as_bad (_("bad argument to syntax directive.")); |
| (void) restore_line_pointer (e); |
| } |
| demand_empty_rest_of_line (); |
| |
| if (ask_naked_reg == 0) |
| allow_naked_reg = (syntax_flag |
| && (bfd_get_symbol_leading_char (stdoutput) != '\0')); |
| else |
| allow_naked_reg = (ask_naked_reg < 0); |
| |
| _set_intel_syntax (syntax_flag); |
| } |
| |
| static void |
| set_intel_mnemonic (int mnemonic_flag) |
| { |
| intel_mnemonic = mnemonic_flag; |
| } |
| |
| static void |
| set_allow_index_reg (int flag) |
| { |
| allow_index_reg = flag; |
| } |
| |
| static void |
| set_check (int what) |
| { |
| enum check_kind *kind; |
| const char *str; |
| |
| if (what) |
| { |
| kind = &operand_check; |
| str = "operand"; |
| } |
| else |
| { |
| kind = &sse_check; |
| str = "sse"; |
| } |
| |
| SKIP_WHITESPACE (); |
| |
| if (!is_end_of_line[(unsigned char) *input_line_pointer]) |
| { |
| char *string; |
| int e = get_symbol_name (&string); |
| |
| if (strcmp (string, "none") == 0) |
| *kind = check_none; |
| else if (strcmp (string, "warning") == 0) |
| *kind = check_warning; |
| else if (strcmp (string, "error") == 0) |
| *kind = check_error; |
| else |
| as_bad (_("bad argument to %s_check directive."), str); |
| (void) restore_line_pointer (e); |
| } |
| else |
| as_bad (_("missing argument for %s_check directive"), str); |
| |
| demand_empty_rest_of_line (); |
| } |
| |
| static void |
| check_cpu_arch_compatible (const char *name ATTRIBUTE_UNUSED, |
| i386_cpu_flags new_flag ATTRIBUTE_UNUSED) |
| { |
| #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
| static const char *arch; |
| |
| /* Intel MCU is only supported on ELF. */ |
| if (!IS_ELF) |
| return; |
| |
| if (!arch) |
| { |
| /* Use cpu_arch_name if it is set in md_parse_option. Otherwise |
| use default_arch. */ |
| arch = cpu_arch_name; |
| if (!arch) |
| arch = default_arch; |
| } |
| |