From 00d7defbcae08a58cd7a81eb4890a7458a827699 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 9 Aug 2025 15:05:08 -0700 Subject: [PATCH] WIP: work toward saner handling of CPU feature sets With APX especially, it is going to be more important to have a consistent handling of CPU features. This is a WIP for generating flags and feature masks. --- .gitignore | 2 + Makefile.in | 50 ++++++----- asm/assemble.c | 24 ++++-- asm/assemble.h | 2 + asm/directiv.c | 194 ++++++++++++++++++------------------------- asm/nasm.c | 22 ++--- include/iflag.h | 92 ++++++++++++++------- include/nasmlib.h | 6 ++ x86/cpunames.c | 2 + x86/cpunames.ph | 98 ++++++++++++++++++++++ x86/cpunames.pl | 136 ++++++++++++++++++++++++++++++ x86/featureinfo.c | 2 + x86/featureinfo.pl | 51 ++++++++++++ x86/features.ph | 137 +++++++++++++++++++++++++++++++ x86/iflags.ph | 143 ++------------------------------ x86/insns-iflags.ph | 4 +- x86/insns.dat | 196 ++++++++++++++++++++++---------------------- x86/rex2.pl | 0 x86/x86features.ph | 183 +++++++++++++++++++++++++++++++++++++++++ 19 files changed, 923 insertions(+), 421 deletions(-) create mode 100644 x86/cpunames.c create mode 100644 x86/cpunames.ph create mode 100644 x86/cpunames.pl create mode 100644 x86/featureinfo.c create mode 100755 x86/featureinfo.pl create mode 100644 x86/features.ph mode change 100644 => 100755 x86/rex2.pl create mode 100644 x86/x86features.ph diff --git a/.gitignore b/.gitignore index 6cde4e2b9..ab12cdae2 100644 --- a/.gitignore +++ b/.gitignore @@ -94,6 +94,8 @@ TAGS /version.mac /version.mak /version.sed +/x86/cpunames.h +/x86/featureinfo.h /x86/iflag.c /x86/iflaggen.h /x86/insnsa.c diff --git a/Makefile.in b/Makefile.in index b7d61f210..0f4cfc13c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -137,7 +137,7 @@ LIBOBJ_NW = stdlib/snprintf.$(O) stdlib/vsnprintf.$(O) stdlib/strlcpy.$(O) \ \ x86/insnsa.$(O) x86/insnsb.$(O) x86/insnsd.$(O) x86/insnsn.$(O) \ x86/regs.$(O) x86/regvals.$(O) x86/regflags.$(O) x86/regdis.$(O) \ - x86/iflag.$(O) \ + x86/iflag.$(O) x86/featureinfo.$(O) x86/cpunames.$(O) \ \ asm/error.$(O) \ asm/floats.$(O) \ @@ -211,8 +211,9 @@ WARNSRCS = $(LIBOBJ_NW:.$(O)=.c) asm/nasm.c PERLREQ_CLEANABLE = \ x86/insnsb.c x86/insnsa.c x86/insnsd.c x86/insnsi.h x86/insnsn.c \ x86/regs.c x86/regs.h x86/regflags.c x86/regdis.c x86/regdis.h \ - x86/regvals.c asm/tokhash.c asm/tokens.h asm/pptok.h asm/pptok.c \ - x86/iflag.c x86/iflaggen.h \ + x86/regvals.c x86/iflag.c x86/iflaggen.h \ + x86/featureinfo.h x86/cpunames.h \ + asm/tokhash.c asm/tokens.h asm/pptok.h asm/pptok.c \ macros/macros.c \ asm/pptok.ph asm/directbl.c asm/directiv.h \ $(WARNFILES) \ @@ -223,36 +224,41 @@ PERLREQ_CLEANABLE = \ # by "make spotless"... PERLREQ = config/unconfig.h $(PERLREQ_CLEANABLE) -INSDEP = x86/insns.xda x86/insns.pl x86/insns-iflags.ph x86/iflags.ph - -x86/insns.xda: x86/insns.dat x86/preinsns.pl - $(RUNPERL) '$(srcdir)'/x86/preinsns.pl $< $@ - config/unconfig.h: config/config.h.in autoconf/unconfig.pl $(RUNPERL) '$(srcdir)'/autoconf/unconfig.pl \ '$(srcdir)' config/config.h.in config/unconfig.h +# +# x86 feature flags and CPU names +# +FEATDEP = x86/x86features.ph x86/features.ph + +x86/featureinfo.h: x86/featureinfo.pl $(FEATDEP) + $(RUNPERL) $< $@ +x86/cpunames.h: x86/cpunames.pl x86/cpunames.ph $(FEATDEP) + $(RUNPERL) $< $@ + +# Preprocess insns.dat. This is done as a separate step to make +# debugging of the macro expansions easier. +x86/insns.xda: x86/preinsns.pl x86/insns.dat + $(RUNPERL) $^ $@ + +INSDEP = x86/insns.xda x86/insns-iflags.ph x86/iflags.ph $(FEATDEP) + x86/iflag.c: $(INSDEP) - $(RUNPERL) $(srcdir)/x86/insns.pl -fc \ - $(srcdir)/x86/insns.xda x86/iflag.c + $(RUNPERL) $(srcdir)/x86/insns.pl -fc $< $@ x86/iflaggen.h: $(INSDEP) - $(RUNPERL) $(srcdir)/x86/insns.pl -fh \ - $(srcdir)/x86/insns.xda x86/iflaggen.h + $(RUNPERL) $(srcdir)/x86/insns.pl -fh $< $@ x86/insnsb.c: $(INSDEP) - $(RUNPERL) $(srcdir)/x86/insns.pl -b \ - $(srcdir)/x86/insns.xda x86/insnsb.c + $(RUNPERL) $(srcdir)/x86/insns.pl -b $< $@ x86/insnsa.c: $(INSDEP) - $(RUNPERL) $(srcdir)/x86/insns.pl -a \ - $(srcdir)/x86/insns.xda x86/insnsa.c + $(RUNPERL) $(srcdir)/x86/insns.pl -a $< $@ x86/insnsd.c: $(INSDEP) - $(RUNPERL) $(srcdir)/x86/insns.pl -d \ - $(srcdir)/x86/insns.xda x86/insnsd.c + $(RUNPERL) $(srcdir)/x86/insns.pl -d $< $@ x86/insnsi.h: $(INSDEP) - $(RUNPERL) $(srcdir)/x86/insns.pl -i \ - $(srcdir)/x86/insns.xda x86/insnsi.h + $(RUNPERL) $(srcdir)/x86/insns.pl -i $< $@ x86/insnsn.c: $(INSDEP) - $(RUNPERL) $(srcdir)/x86/insns.pl -n \ - $(srcdir)/x86/insns.xda x86/insnsn.c + $(RUNPERL) $(srcdir)/x86/insns.pl -n $< $@ # These files contains all the standard macros that are derived from # the version number. diff --git a/asm/assemble.c b/asm/assemble.c index 4f2130643..3deb9b246 100644 --- a/asm/assemble.c +++ b/asm/assemble.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- * * - * Copyright 1996-2024 The NASM Authors - All Rights Reserved + * Copyright 1996-2025 The NASM Authors - All Rights Reserved * See the file AUTHORS included with the NASM distribution for * the specific copyright holders. * @@ -125,6 +125,9 @@ static int process_ea(operand *input, int rfield, opflags_t rflags, /* Convert a prefix to a byte value */ static int prefix_byte(enum prefixes pfx, const int bits); +/* Map of feature flag indicies for which the current CPU flags are OK */ +static bool itemp_features_ok[ARRAY_SIZE(insns_flags)]; + /* * Convert operand/address/mode size to a BITS opflag constant. * This is not valid for 80+ bits! @@ -981,7 +984,7 @@ static int64_t assemble(insn *instruction) whathappened = never ? "never implemented" : "obsolete"; - if (!never && !iflag_cmp_cpu_level(&insns_flags[temp->iflag_idx], &cpu)) { + if (!never && 1 /* !iflag_cmp_cpu_level(&insns_flags[temp->iflag_idx], &cpu) */) { warning = WARN_OBSOLETE_VALID; validity = "but valid on"; } else if (itemp_has(temp, IF_NOP)) { @@ -2100,7 +2103,7 @@ static int64_t calcsize(insn *ins, const struct itemplate * const temp) ins->rex &= ~REX_L; ins->rex |= REX_P; } else if ((ins->rex & (REX_L|REX_W|REX_BXR)) == (REX_L|REX_R) && - iflag_cpu_level_ok(&cpu, IF_X86_64)) { + iflag_test(&cpu, IF_LOCKREX)) { /* LOCK-as-REX.R */ if (assert_no_prefix(ins, PPS_LOCK)) return -1; @@ -3315,9 +3318,9 @@ static enum match_result matches(const struct itemplate * const itemp, } /* - * Check template is okay at the set cpu level + * Check CPU feature masking flags */ - if (iflag_cmp_cpu_level(&insns_flags[itemp->iflag_idx], &cpu) > 0) + if (!itemp_features_ok[itemp->iflag_idx]) return MERR_BADCPU; /* @@ -3375,6 +3378,17 @@ static enum match_result matches(const struct itemplate * const itemp, return MOK_GOOD; } +/* + * Recompute the list of valid instruction pattern indicies + */ +void asm_revalidate_cpu(void) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(insns_flags); i++) + itemp_features_ok[i] = iflag_features_ok(&insns_flags[i], &cpu); +} + /* * Select the mod part of modr/m for an memory operand with displacement. * zerook should be clear for the forbidden BP encodings; such instructions diff --git a/asm/assemble.h b/asm/assemble.h index 0423957ba..ef1a63bc3 100644 --- a/asm/assemble.h +++ b/asm/assemble.h @@ -53,6 +53,8 @@ void process_insn(insn *instruction); bool process_directives(char *); void process_pragma(char *); +void asm_revalidate_cpu(void); + /* Is this a compile-time absolute constant? */ static inline bool op_compile_abs(const struct operand * const op) { diff --git a/asm/directiv.c b/asm/directiv.c index 24b10c576..ed87fc2c4 100644 --- a/asm/directiv.c +++ b/asm/directiv.c @@ -52,124 +52,87 @@ #include "listing.h" #include "labels.h" #include "iflag.h" - -struct cpunames { - const char *name; - unsigned int level; - /* Eventually a table of features */ -}; - -static void iflag_set_cpu(iflag_t *a, unsigned int lvl) -{ - a->field[0] = 0; /* Not applicable to the CPU type */ - iflag_set_all_features(a); /* All feature masking bits set for now */ - if (lvl >= IF_ANY) { - /* This is a hack for now */ - iflag_set(a, IF_LATEVEX); - } - a->field[IF_CPU_FIELD] &= ~IF_CPU_LEVEL_MASK; - iflag_set(a, lvl); -} +#include "featureinfo.h" +#include "cpunames.h" void set_cpu(const char *value) { const char *p; char modifier; - const struct cpunames *cpuflag; - static const struct cpunames cpunames[] = { - { "default", IF_DEFAULT }, /* Must be first */ - { "8086", IF_8086 }, - { "186", IF_186 }, - { "286", IF_286 }, - { "386", IF_386 }, - { "486", IF_486 }, - { "586", IF_PENT }, - { "pentium", IF_PENT }, - { "pentiummmx", IF_PENT }, - { "686", IF_P6 }, - { "p6", IF_P6 }, - { "ppro", IF_P6 }, - { "pentiumpro", IF_P6 }, - { "p2", IF_P6 }, /* +MMX */ - { "pentiumii", IF_P6 }, - { "p3", IF_KATMAI }, - { "katmai", IF_KATMAI }, - { "p4", IF_WILLAMETTE }, - { "willamette", IF_WILLAMETTE }, - { "prescott", IF_PRESCOTT }, - { "x64", IF_X86_64 }, - { "x86-64", IF_X86_64 }, - { "ia64", IF_IA64 }, - { "ia-64", IF_IA64 }, - { "itanium", IF_IA64 }, - { "itanic", IF_IA64 }, - { "merced", IF_IA64 }, - { "nehalem", IF_NEHALEM }, - { "westmere", IF_WESTMERE }, - { "sandybridge", IF_SANDYBRIDGE }, - { "ivybridge", IF_FUTURE }, - { "any", IF_ANY }, - { "all", IF_ANY }, - { "latevex", IF_LATEVEX }, - { "apx", IF_APX }, - { "evex", IF_EVEX }, - { "vex", IF_VEX }, - { NULL, 0 } - }; - if (!value) { - iflag_set_cpu(&cpu, cpunames[0].level); - return; - } + if (!value) + value = "default"; p = value; - modifier = '+'; - while (*p) { - int len = strcspn(p, " ,"); - - while (len && (*p == '+' || *p == '-' || *p == '*')) { + modifier = 0; + while (*(p = nasm_skip_spaces(p))) { + if (*p == '+' || *p == '-' || *p == '*' || *p == '^' || *p == ',') { modifier = *p++; - len--; - if (!len && modifier == '*') - cpu = cmd_cpu; + continue; + } + + int len = strcspn(p, " ,+-*^"); + if (!len && modifier == '*') { + cpu = cmd_cpu; + continue; } if (len) { + const struct cpu_feature_info *feat = NULL; bool invert_flag = false; - if (len >= 3 && !nasm_memicmp(p, "no", 2)) { + if (len >= 2 && *p == '!') { + invert_flag = true; + do { + p++; + len--; + } while (nasm_isspace(*p)); + } else if (len >= 3 && !nasm_memicmp(p, "no", 2)) { invert_flag = true; p += 2; len -= 2; } - for (cpuflag = cpunames; cpuflag->name; cpuflag++) - if (!nasm_strnicmp(p, cpuflag->name, len)) - break; - - if (!cpuflag->name) { - nasm_nonfatal("unknown CPU type or flag '%.*s'", len, p); - return; - } - - if (cpuflag->level >= IF_CPU_FIRST && cpuflag->level <= IF_ANY) { - iflag_set_cpu(&cpu, cpuflag->level); - } else { - switch (modifier) { - case '-': - invert_flag = !invert_flag; - break; - case '*': - invert_flag ^= iflag_test(&cmd_cpu, cpuflag->level); - break; - default: - break; + if (!modifier && !invert_flag) { + /* Search list of known CPUs */ + array_foreach (feat, known_cpus) { + if (!nasm_strnicmp(p, feat->name, len)) { + iflag_set_features(&cpu, feat->deps); + goto next; + } } - - iflag_set(&cpu, cpuflag->level); - if (invert_flag) - iflag_clear(&cpu, cpuflag->level); } + + /* Otherwise it should be a single feature flag */ + array_foreach (feat, cpu_feature_info) { + if (!nasm_strnicmp(p, feat->name, len)) + goto found; + } + + /* Otherwise... */ + nasm_nonfatal("unknown CPU type or feature flag '%.*s'", len, p); + return; + + found: + switch (modifier) { + case '-': + invert_flag = !invert_flag; + break; + case '*': + case '^': + invert_flag ^= iflag_test_feature(&cmd_cpu, feat->num); + break; + default: + break; + } + + if (invert_flag) + iflag_del_feature(&cpu, feat->num); + else + iflag_add_feature(&cpu, feat->num); + + next: + modifier = '+'; } p += len; if (!*p) @@ -181,27 +144,24 @@ void set_cpu(const char *value) static int get_bits(const char *value) { int i = atoi(value); + if (!i) + return globalbits; - switch (i) { - case 16: - break; /* Always safe */ - case 32: - if (!iflag_cpu_level_ok(&cpu, IF_386)) { + if (!iflag_bits_ok(&cpu, i)) { + i = globalbits; + + switch (i) { + case 32: nasm_nonfatal("cannot specify 32-bit segment on processor below a 386"); - i = 16; - } - break; - case 64: - if (!iflag_cpu_level_ok(&cpu, IF_X86_64)) { + break; + case 64: nasm_nonfatal("cannot specify 64-bit segment on processor below an x86-64"); - i = 16; + break; + default: + nasm_nonfatal("`%s' is not a valid segment size; must be 16, 32 or 64", + value); + break; } - break; - default: - nasm_nonfatal("`%s' is not a valid segment size; must be 16, 32 or 64", - value); - i = 16; - break; } return i; } @@ -503,6 +463,12 @@ bool process_directives(char *directive) case D_CPU: /* [CPU] */ set_cpu(value); + if (!iflag_bits_ok(&cpu, globalbits)) { + nasm_nonfatal("invalid CPU for %d-bit mode", globalbits); + while (!iflag_bits_ok(&cpu, globalbits >>= 1)) + ; + } + asm_revalidate_cpu(); break; case D_LIST: /* [LIST {+|-}] */ diff --git a/asm/nasm.c b/asm/nasm.c index 9e4c76134..c9e5fc965 100644 --- a/asm/nasm.c +++ b/asm/nasm.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- * * - * Copyright 1996-2024 The NASM Authors - All Rights Reserved + * Copyright 1996-2025 The NASM Authors - All Rights Reserved * See the file AUTHORS included with the NASM distribution for * the specific copyright holders. * @@ -1603,20 +1603,10 @@ static void assemble_file(const char *fname, struct strlist *depend_list) uint64_t prev_offset_changed; int64_t stall_count = 0; /* Make sure we make forward progress... */ - switch (cmd_sb) { - case 16: - break; - case 32: - if (!iflag_cpu_level_ok(&cmd_cpu, IF_386)) - nasm_fatal("command line: 32-bit segment size requires a higher cpu"); - break; - case 64: - if (!iflag_cpu_level_ok(&cmd_cpu, IF_X86_64)) - nasm_fatal("command line: 64-bit segment size requires a higher cpu"); - break; - default: - panic(); - break; + if (!iflag_bits_ok(&cmd_cpu, cmd_sb)) { + nasm_fatal("command line: %d-bit segment size requires a higher cpu", + cmd_sb); + return; } prev_offset_changed = INT64_MAX; @@ -1657,6 +1647,8 @@ static void assemble_file(const char *fname, struct strlist *depend_list) globalbits = cmd_sb; /* set 'bits' to command line default */ cpu = cmd_cpu; + asm_revalidate_cpu(); + if (listname) { if (list_on_this_pass()) { /* diff --git a/include/iflag.h b/include/iflag.h index ef376421e..2df9120f0 100644 --- a/include/iflag.h +++ b/include/iflag.h @@ -4,8 +4,8 @@ #include "compiler.h" #include "ilog2.h" - #include "iflaggen.h" +#include "featureinfo.h" #define IF_GENBIT(bit) (UINT32_C(1) << ((bit) & 31)) @@ -39,6 +39,31 @@ static inline void iflag_set_all(iflag_t *f) memset(f, ~0, sizeof(*f)); } +static inline bool iflag_bits_ok(const iflag_t *f, int bits) +{ + switch (bits) { + case 16: + return true; + case 32: + return iflag_test(f, IF_386); + case 64: + return iflag_test(f, IF_X86_64); + default: + return false; + } +} + +static inline bool iflag_features_ok(const iflag_t *have, const iflag_t *need) +{ + uint32_t bad = 0; + size_t i; + + for (i = IF_FEATURE_FIELD; i < IF_FEATURE_FIELD+IF_FEATURE_NFIELDS; i++) + bad |= need->field[i] & ~have->field[i]; + + return !bad; +} + #define iflag_for_each_field(v) for ((v) = 0; (v) < IF_FIELD_COUNT; (v)++) static inline int iflag_cmp(const iflag_t *a, const iflag_t *b) @@ -91,37 +116,46 @@ IF_GEN_HELPER(xor, ^) #define itemp_arx(itemp) _itemp_arx((itemp)->iflag_idx) #define itemp_smx(itemp) _itemp_smx((itemp)->iflag_idx) -/* - * IF_ANY is the highest CPU level by definition - */ -#define IF_CPU_LEVEL_MASK ((IFM_ANY << 1) - 1) - -static inline int iflag_cmp_cpu(const iflag_t *a, const iflag_t *b) -{ - return ifcomp(a->field[IF_CPU_FIELD], b->field[IF_CPU_FIELD]); -} - -static inline uint32_t _iflag_cpu_level(const iflag_t *a) -{ - return a->field[IF_CPU_FIELD] & IF_CPU_LEVEL_MASK; -} - -static inline int iflag_cmp_cpu_level(const iflag_t *a, const iflag_t *b) -{ - return ifcomp(_iflag_cpu_level(a), _iflag_cpu_level(b)); -} - -/* Returns true if the CPU level is at least a certain value */ -static inline bool iflag_cpu_level_ok(const iflag_t *a, unsigned int bit) -{ - return _iflag_cpu_level(a) >= IF_GENBIT(bit); -} - -static inline void iflag_set_all_features(iflag_t *a) +static inline void iflag_set_features(iflag_t *a, const uint32_t *features) { uint32_t *p = &a->field[IF_FEATURE_FIELD]; - memset(p, -1, IF_FEATURE_NFIELDS * sizeof(uint32_t)); + memcpy(p, features, IF_FEATURE_NFIELDS * sizeof(uint32_t)); +} + +static inline void iflag_clear_features(iflag_t *a) +{ + uint32_t *p = &a->field[IF_FEATURE_FIELD]; + + memset(p, 0, IF_FEATURE_NFIELDS * sizeof(uint32_t)); +} + +static inline bool iflag_test_feature(const iflag_t *a, unsigned int feature) +{ + return iflag_test(a, feature + IF_FEATURE_FIRST); +} + +/* + * When ADDING a feature, enable all features that depend on it as + * well, for user sanity. When REMOVING a feature, delete only that + * feature bit; the instruction feature masks take care of + * dependencies (e.g. AVX512F depends on AVX512, so if the AVX512 bit + * is off, an AVX512F instruction will not match even if the AVX512F + * bit is still set.) + */ +static inline void iflag_add_feature(iflag_t *a, unsigned int feature) +{ + unsigned int i; + const uint32_t *p = cpu_feature_info[feature - IF_FEATURE_FIRST].deps; + uint32_t *q = &a->field[IF_FEATURE_FIELD]; + + for (i = 0; i < IF_FEATURE_NFIELDS; i++) + *q++ |= *p++; +} + +static inline void iflag_del_feature(iflag_t *a, unsigned int feature) +{ + iflag_clear(a, feature + IF_FEATURE_FIRST); } static inline iflag_t _iflag_pfmask(const iflag_t *a) diff --git a/include/nasmlib.h b/include/nasmlib.h index 6488effaf..dd47113b7 100644 --- a/include/nasmlib.h +++ b/include/nasmlib.h @@ -305,6 +305,12 @@ const char *filename_set_extension(const char *inname, const char *extension); */ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +/* + * Iterate over all the elements of a known sized array + */ +#define array_foreach(var, arr) \ + for ((var) = (arr); (var) < &(arr)[ARRAY_SIZE(arr)]; (var)++) + /* * List handling * diff --git a/x86/cpunames.c b/x86/cpunames.c new file mode 100644 index 000000000..4e159472f --- /dev/null +++ b/x86/cpunames.c @@ -0,0 +1,2 @@ +#define COMPILING_CPUNAMES_C 1 +#include "cpunames.h" diff --git a/x86/cpunames.ph b/x86/cpunames.ph new file mode 100644 index 000000000..27250d22c --- /dev/null +++ b/x86/cpunames.ph @@ -0,0 +1,98 @@ +# -*- perl -*- +# +# List of CPU names and their corresponding features +# A + means include the feature set for that CPU, otherwise this +# is the name of a base feature as defined in x86/features.ph. +# + +use integer; +use strict; + +our @cpufeatures; + +# +# Format: +# c_(name, help text, feature set) +# The feature set can include any set of CPU features as defined in +# x86/features.ph, or "+cpu" to add all features present for "cpu" +# (which has to be already defined in this file!), or "-feature" to +# remove a feature. +# +# Dependent features (as defined in x86/x86features.ph) are automatically +# included as well. +# +# a_(name, original, help text) +# Indicate that "name" is an alias for "original" +# If "help text" is undef then the alias is hidden from help text +# and documentation. +# +# CPU and feature names are case insensitive and non-alphanumeric +# characters ignored. +# + +# For legacy reasons FPU is always included +c_("8086", "8086/8088 with optional 8087", qw(8086 8086only fpu undoc)); +c_("186", "80186/80188 with optional 8087", qw(+8086 -8086only 186)); +c_("286", "80286 with optional 80287", qw(+186 286 286only priv)); +c_("386", "80386 with optional 80387", qw(+286 -286only 386only)); +c_("486", "Early 486 without CPUID", qw(+386 -386only smm)); +c_("486dx", "486DX/487SX including CPUID", qw(+486 cpuid)); +c_("486sx", "486SX including CPUID, no FPU", qw(+486dx -fpu)); +c_("586", "Pentium P5/P54C", qw(+486dx pent)); +a_("586", "pentium"); +a_("586", "p5"); +a_("586", "p54"); +a_("586", "p54c"); +c_("ia64", "IA64 (in x86 mode)", qw(+pent jmpe)); +a_("itanium", "ia64"); +a_("itanic", "ia64"); +a_("merced", "ia64"); +c_("pentiummmx", "Pentium MMX (P55C)", qw(+pent mmx)); +a_("p55", "pentiummmx"); +a_("p55c", "pentiummmx"); +c_("p6", "Pentium Pro", qw(+pentiumpro cmov)); +c_("pentiumpro", "Pentium Pro", qw(+pent p6 cmov)); +# Some Pentium II steppings had fxsave support in anticipation of Pentium III +c_("pentiumii", "Pentium II", qw(+p6 mmx sysenter fxsave)); +a_("pentiumii", "pentium2"); +a_("pentiumii", "p2"); +c_("katmai", "Pentium III", qw(+pentium2 sse)); +a_("katmai", "pentiumiii"); +a_("katmai", "pentium3"); +a_("katmai", "p3"); +c_("pentiumm", "Pentium M", qw(+katmai sse2)); +a_("pentiumm", "pm"); +a_("pentiumm", "centrino"); +# c_("k6", "AMD K6", qw(+pentiumii syscall 3dnow)); +# c_("k7", "AMD K7 (Athlon)", qw(+katmai syscall 3dnow)); +c_("willamette", "Intel Willamette", qw(+katmai sse2 willamette)); +a_("willamette", "pentium4"); +c_("prescott", "Intel Prescott", qw(+willamette prescott sse3)); +# For historical reasons this includes LOCKREX +c_("x86-64-v1", "x86-64 level 1", qw(+willamette x86-64 sse2 lockrex)); +a_("x86-64-v1", "x64-1"); +# Legacy definition +c_("x86-64", "Early x86-64 CPUs", qw(+x86-64-v1 cx16 lahf_lm sse3)); +a_("x64", "x86-64"); +c_("x86-64-v2", "x86-64 level 2", qw(+x86-64-v1 cx16 lahf_lm popcnt sse4.2")); +a_("x86-64-v2", "x64-2"); +c_("x86-64-v3", "x86-64 level 3", qw(+x86-64-v2 avx2 vmi2 f16c lzcnt movbe xsave)); +a_("x86-64-v3", "x64-3"); +c_("x86-64-v4", "x86-64 level 4", qw(+x86-64-v3 avx512f avx512bw avx512cd avx512dq avx512vl)); +a_("x86-64-v4", "x64-4"); +c_("core2", qw(+x86-64-v1 +prescott cx16 lahf_lm ssse3)); +c_("nehalem", "Intel Nehalem", qw(+core2 nehalem sse4.2 popcnt)); +a_("nehalem", "corei7"); +c_("westmere", "Intel Westmere", qw(+nehalem westmere pclmul)); +c_("sandybridge", "Intel Sandy Bridge", qw(+westmere sandybridge avx xsave)); +c_("ivybridge", "Intel Ivy Bridge", qw(+sandybridge fsgsbase rdrand f16c)); +c_("haswell", "Intel Haswell", qw(+ivybridge avx2 bmi1 bmi2 lzcnt fma movbe hle)); +c_("broadwell", "Intel Broadwell", qw(+haswell rdseed adcx prefetchw)); +c_("skylake", "Intel Skylake", qw(+broadwell aes clflushopt xsavec xsaves sgx)); + +c_('any', "Enable all known CPU features", + map { $_->{'name'} } @cpufeatures); +a_("any", "all"); +c_("default", "Default CPU feature set", qw(+any -superceded -lateavx)); + +1; diff --git a/x86/cpunames.pl b/x86/cpunames.pl new file mode 100644 index 000000000..f649651c2 --- /dev/null +++ b/x86/cpunames.pl @@ -0,0 +1,136 @@ +#!/usr/bin/perl +# +# Create a list of CPU names and their corresponding CPU feature +# flags. These are different from the corresponding feature flags in that: +# a) their names can overlap with instruction flags. +# b) they are never used for removal. +# +# These are usually used standalone to set the feature set to a +# corresponding CPU. +# + +use integer; +use strict; + +require 'x86/x86features.ph'; + +our @cpufeatures; +our %cpufeature; +our $cpufeature_words; + +my %cpu; +my @cpus; + +sub empty_mask() { + return [(0) x $cpufeature_words]; +} + +sub or_mask($$) { + my($a, $b) = @_; + my @c; + + die if (scalar(@$a) != scalar(@$b)); + + for (my $i = 0; $i < scalar(@$a); $i++) { + $a->[$i] |= $b->[$i]; + } + + return $a; +} + +sub clear_bit($$) { + my($mask, $bit) = @_; + + $mask->[$bit >> 5] &= ~(1 << ($bit & 31)); + return $mask; +} + +my $all_vendors = empty_mask(); +foreach my $feat (@cpufeatures) { + if ($feat->{'vendor'}) { + or_mask($all_vendors, $feat->{'depmask'}); + } +} + +# CPU definition +sub c_($$@) { + my($name, $help, @features) = @_; + + $name = lc($name); + + if (defined($cpu{$name})) { + die "$0: multiple definitions of cpu $name\n"; + } + + my $cdef = { + 'name' => $name, + 'help' => $help + }; + + my $fm = empty_mask(); + + my $found_vendor = 0; + + foreach my $fn (@features) { + my $f = lc($fn); + my $feat; + my $minus = 0; + if ($f =~ s/^\+//) { + $feat = $cpu{$f}; + } else { + $minus = ($f =~ s/^\-//); + $feat = $cpufeature{$f}; + } + if (!defined($feat)) { + die "$0: unknown feature in cpu $name: $fn\n"; + } + + $found_vendor |= $feat->{'vendor'}; + + if ($minus) { + clear_bit($fm, $feat->{'num'}); + } else { + or_mask($fm, $feat->{'depmask'}); + } + } + + if (!$found_vendor) { + or_mask($fm, $all_vendors); + } + + $cdef->{'depmask'} = $fm; + push(@cpus, $cdef); + $cpu{$name} = $cdef; +} + +# CPU alias (implement this later) +sub a_($$;$) { } + +require 'x86/cpunames.ph'; + +my($outfile) = @ARGV; + +open(my $out, '>', $outfile) or die "$0: $outfile: $!\n"; + +print $out "#ifndef X86_CPUNAMES_H\n"; +print $out "#define X86_CPUNAMES_H 1\n\n"; + +print $out "#include \"featureinfo.h\"\n\n"; + +printf $out "extern const struct cpu_feature_info known_cpus[%d];\n\n", + scalar(@cpus); + +print $out "#endif\n\n"; +print $out "#ifdef COMPILING_CPUNAMES_C\n\n"; + +printf $out "const struct cpu_feature_info known_cpus[%d] = {\n", + scalar(@cpus); + +foreach my $cpu (@cpus) { + printf $out " { \"%s\", \"%s\", -1U, {%s} },\n", + $cpu->{'name'}, + $cpu->{'help'}, + join(',', map { sprintf('0x%08x', $_) } @{$cpu->{'depmask'}}); +} +print $out "};\n\n"; +print $out "#endif\n"; diff --git a/x86/featureinfo.c b/x86/featureinfo.c new file mode 100644 index 000000000..78d1b3282 --- /dev/null +++ b/x86/featureinfo.c @@ -0,0 +1,2 @@ +#define COMPILING_FEATUREINFO_C 1 +#include "featureinfo.h" diff --git a/x86/featureinfo.pl b/x86/featureinfo.pl new file mode 100755 index 000000000..9fa595ce1 --- /dev/null +++ b/x86/featureinfo.pl @@ -0,0 +1,51 @@ +#!/usr/bin/perl +# +# Generate CPU feature dependency bitmasks +# + +use integer; +use strict; + +require 'x86/x86features.ph'; + +our @cpufeatures; +our $cpufeature_words; + +my($outfile) = @ARGV; + +open(my $out, '>', $outfile) or die "$0: $outfile: $!\n"; + +print $out "#ifndef X86_FEATUREINFO_H\n"; +print $out "#define X86_FEATUREINFO_H 1\n\n"; + +print $out "#include \"compiler.h\"\n\n"; + +print $out "struct cpu_feature_info {\n"; +print $out " const char \*name;\n"; +print $out " const char \*help;\n"; +print $out " unsigned int num;\n"; +print $out " uint32_t deps[$cpufeature_words];\n"; +print $out "};\n\n"; + +printf $out "extern const struct cpu_feature_info cpu_feature_info[%d];\n\n", + scalar(@cpufeatures); + +printf $out "#endif\n\n"; + +print $out "#ifdef COMPILING_FEATUREINFO_C\n\n"; + +printf $out "const struct cpu_feature_info cpu_feature_info[%d] = {\n", + scalar(@cpufeatures); + +foreach my $feat (@cpufeatures) { + printf $out " { %-15s \"%s\", %d, {%s} },\n", + '"'.$feat->{'name'}.'",', + $feat->{'help'}, + $feat->{'num'}, + join(',', map { sprintf('0x%08x', $_) } @{$feat->{'depmask'}}); +} +print $out "};\n\n"; + +print $out "#endif\n"; + +close($out); diff --git a/x86/features.ph b/x86/features.ph new file mode 100644 index 000000000..117083221 --- /dev/null +++ b/x86/features.ph @@ -0,0 +1,137 @@ + +f_("VEX", "VEX or XOP encoded instruction"); +f_("EVEX", "EVEX encoded instruction", qw(PROT)); + +# +# Encoding formats that can be set with the CPU directive +# +# +# Modes and attributes +# +f_("OBSOLETE", "Instruction removed from architecture"); +f_("NEVER", "Instruction never implemented", qw(obsolete)); +f_("NOP", "Non-explicit noop instructions"); +f_("PRIV", "Privileged instruction", qw(prot)); +f_("PROT", "Protected mode only"); +f_("SMM", "System management mode only"); +f_("LONG", "Long mode only", qw(x86-64)); +f_("VIRTUAL", "Virtual instructions"); +f_("UNDOC", "Undocumented instructions"); +f_("VENDOR", "Vendor-specific instructions"); +f_("SUPERCEDED", "Instruction opcode has been reused"); +f_("APX", "Advanced Performance Extensions", qw(long)); +f_("APX_F", "Advanced Performance Extensions (APX) base set"); + +f_("FPU", "x87 floating-point instructions"); +f_("MMX", "MMX", qw(FPU)); +f_("MMXEXT", "MMX extension SSE subset", qw(MMX)); +f_("3DNOW", "3DNow!", qw(MMXEXT)); +f_("SSE", "SSE (KNI, MMX2)", qw(MMXEXT)); +f_("SSE2", "SSE2", qw(SSE)); +f_("SSE3", "SSE3 (PNI)", qw(SSE2)); +f_("VMX", "VMX"); +f_("SSSE3", "SSSE3", qw(SSE3)); +f_("SSE4A", "AMD SSE4a", qw(SSE3)); +f_("SSE4.1", "SSE4.1", qw(SSSE3)); +f_("SSE4.2", "SSE4.2", qw(SSE4.1)); +f_("SSE5", "SSE5", qw(SSE4.2)); +f_("LAHF_LM", "LAHF/SAHF in long mode"); +f_("LOCKREX", "LOCK as REX.R in 16/32-bit mode"); +f_("CX16", "CMPXCHG16B"); +f_("AVX", "AVX (256-bit floating point)", qw(VEX SSSE3)); +f_("AVX2", "AVX2 (256-bit integer)", qw(AVX)); +f_("FMA", "Fused multiply-add", qw(AVX)); +f_("BMI1", "Bit manipulation instructions 1"); +f_("BMI2", "Bit manipulation instructions 2", qw(bmi1)); +f_("TBM", ""); +f_("RTM", ""); +f_("AVX512", "AVX-512 instructions"); +f_("AVX512F", "AVX-512F (base architecture)", qw(EVEX)); +f_("AVX512CD", "AVX-512 Conflict Detection", qw(AVX512F)); +f_("AVX512ER", "AVX-512 Exponential and Reciprocal", qw(AVX512F)); +f_("AVX512PF", "AVX-512 Prefetch"); +f_("MPX", "Memory protection extension", qw(obsolete)); +f_("SHA", "SHA instructions"); +f_("AVX512VL", "AVX-512 Vector Length Orthogonality", qw(AVX512F)); +f_("AVX512DQ", "AVX-512 Dword and Qword", qw(AVX512F)); +f_("AVX512BW", "AVX-512 Byte and Word", qw(AVX512F)); +f_("AVX512IFMA", "AVX-512 IFMA instructions"); +f_("AVX512VBMI", "AVX-512 VBMI instructions"); +f_("AES", "AES instructions"); +f_("VAES", "AES AVX instructions", qw(AES AVX)); +f_("VPCLMULQDQ", "AVX Carryless Multiplication"); +f_("GFNI", "Galois Field instructions"); +f_("AVX512VBMI2", "AVX-512 VBMI2 instructions"); +f_("AVX512VNNI", "AVX-512 VNNI instructions"); +f_("AVX512BITALG", "AVX-512 Bit Algorithm instructions"); +f_("AVX512VPOPCNTDQ", "AVX-512 VPOPCNTD/VPOPCNTQ"); +f_("AVX5124FMAPS", "AVX-512 4-iteration multiply-add"); +f_("AVX5124VNNIW", "AVX-512 4-iteration dot product"); +f_("AVX512FP16", "AVX-512 FP16 instructions"); +f_("AVX512FC16", "AVX-512 FC16 instructions"); +f_("SGX", "Intel Software Guard Extensions (SGX)"); +f_("CET", "Intel Control-Flow Enforcement Technology (CET)"); +f_("ENQCMD", "Enqueue command instructions"); +f_("TSXLDTRK", "TSX suspend load address tracking"); +f_("AVX512BF16", "AVX-512 bfloat16"); +f_("AVX512VP2INTERSECT", "AVX-512 VP2INTERSECT instructions"); +f_("AMXTILE", "AMX tile configuration instructions", qw(AMXTILE)); +f_("AMXBF16", "AMX bfloat16 multiplication", qw(AMXTILE)); +f_("AMXINT8", "AMX 8-bit integer multiplication", qw(AMXTILE)); +f_("FRED", "Flexible Return and Exception Delivery (FRED)", qw(lkgs)); +f_("RAOINT", "Remote atomic operations (RAO-INT)"); +f_("UINTR", "User interrupts"); +f_("CMPCCXADD", "CMPccXADD instructions"); +f_("PREFETCHI", "PREFETCHI0 and PREFETCHI1 instructions"); +f_("MSRLIST", "RDMSRLIST and WRMSRLIST instructions"); +f_("AVXNECONVERT", "AVX exceptionless floating-point conversions"); +f_("AVXVNNIINT8", "AVX Vector Neural Network 8-bit integer instructions"); +f_("AVXIFMA", "AVX integer multiply and add"); +f_("LATEAVX", "Instructions first added as EVEX encoded; VEX added later"); +f_("HRESET", "History reset"); +f_("SMAP", "Supervisor Mode Access Prevention (SMAP)"); +f_("SHA512", "SHA512 instructions"); +f_("HSM3", "SM3 hash instructions"); +f_("HSM4", "SM4 hash instructions"); +f_("AVX10.1", "AVX 10.1 instructions", qw(avx2)); +f_("AVX10.2", "AVX 10.2 instructions", qw(avx10.1)); +f_("ADX", "ADCX and ADOX instructions"); +f_("PKU", "Protection key for user mode"); +f_("MONITOR", "MONITOR and MWAIT instructions"); +f_("MONITORX", "MONITORX and MWAITX instructions"); +f_("WAITPKG", "User wait instructions package"); + +# +# Single-instruction flags without special help text +# +map { f_($_) } +qw(cpuid invpcid prefetchwt1 pconfig wbnoinvd serialize + wrmsrns clflushopt clwb rdrand rdseed rdpid + lzcnt ptwrite cldemote movdiri movdir64b clzero + movbe fcomi lkgs jmpe); +d_('rdseed', 'rdrand'); +d_('jmpe', 'virtual'); + +f_("8086only", "8086/8088 only instructions", qw(8086 obsolete)); +f_("286only", "80286 only instructions", qw(286 obsolete)); +f_("386only", "80386 only instructions", qw(386 obsolete)); + +f_("8087", "8087 floating-point instructions", qw(8086 fpu)); +f_("287", "80287 floating-point instructions", qw(286 fpu)); +f_("387", "80387 floating-point instructions", qw(386 fpu)); + +f_("8086", "8086/8088 base features"); +f_("186", "80186/80188 base features", qw(8086)); +f_("286", "80286 base features", qw(186)); +f_("386", "80386 base features", qw(286)); +f_("486", "486 family base instructions", qw(386)); +f_("PENT", "Pentium (P5) instructions", qw(486 fpu cpuid)); +f_("P6", "P6 (Pentium Pro)", qw(pent fcomi)); +f_("KATMAI", "Katmai (Pentium III) instructions", qw(p6 mmx sse)); +f_("WILLAMETTE", "Willamette instructions", qw(katmai)); +f_("PRESCOTT", "Prescott instructions", qw(prescott)); +f_("X86-64", "x86-64 base feature set", qw(prescott fpu syscall)); +f_("NEHALEM", "Nehalem instructions", qw(x64)); +f_("WESTMERE", "Westmere instructions", qw(nehalem)); +f_("SANDYBRIDGE", "Sandy Bridge instructions", qw(westmere)); +f_("FUTURE", "Ivy Bridge or newer instructions", qw(sandybridge)); diff --git a/x86/iflags.ph b/x86/iflags.ph index dc91b97ac..e8ef88b7b 100644 --- a/x86/iflags.ph +++ b/x86/iflags.ph @@ -1,7 +1,8 @@ # -*- perl -*- # -# dword bound, index 0 - specific flags +# dword bound, index 0 - instruction generation flags; +# not part of the instruction feature mask # if_align('IGEN', $NOBREAK); @@ -54,7 +55,6 @@ if_("SIB", "SIB encoding required"); if_("LOCK", "Lockable if operand 0 is memory"); if_("LOCK1", "Lockable if operand 1 is memory"); if_("NOLONG", "Not available in long mode"); -if_("LONG", "Long mode"); if_("NOHLE", "HLE prefixes forbidden"); if_("MIB", "split base/index EA"); if_("BND", "BND (0xF2) prefix available"); @@ -69,143 +69,16 @@ if_("FL", "Instruction modifies the flags"); if_("DFV", "Destination flag values"); # -# dword bound - instruction feature filtering flags +# dword bound - instruction feature filtering flags (from x86features.ph) # if_align('FEATURE'); -# -# Encoding formats that can be set with the CPU directive -# -if_("VEX", "VEX or XOP encoded instruction"); -if_("EVEX", "EVEX encoded instruction"); +require 'x86/x86features.ph'; -# -# Feature filtering flags -# -if_("PRIV", "Privileged instruction"); -if_("SMM", "Only valid in SMM"); -if_("PROT", "Protected mode only"); -if_("UNDOC", "Undocumented"); -if_("FPU", "FPU"); -if_("MMX", "MMX"); -if_("3DNOW", "3DNow!"); -if_("SSE", "SSE (KNI, MMX2)"); -if_("SSE2", "SSE2"); -if_("SSE3", "SSE3 (PNI)"); -if_("VMX", "VMX"); -if_("SSSE3", "SSSE3"); -if_("SSE4A", "AMD SSE4a"); -if_("SSE41", "SSE4.1"); -if_("SSE42", "SSE4.2"); -if_("SSE5", "SSE5"); -if_("AVX", "AVX (256-bit floating point)"); -if_("AVX2", "AVX2 (256-bit integer)"); -if_("FMA", "Fused multiply-add"); -if_("BMI1", "Bit manipulation instructions 1"); -if_("BMI2", "Bit manipulation instructions 2"); -if_("TBM", ""); -if_("RTM", ""); -if_("AVX512", "AVX-512"); -if_("AVX512F", "AVX-512F (base architecture)"); -if_("AVX512CD", "AVX-512 Conflict Detection"); -if_("AVX512ER", "AVX-512 Exponential and Reciprocal"); -if_("AVX512PF", "AVX-512 Prefetch"); -if_("MPX", "MPX"); -if_("SHA", "SHA"); -if_("AVX512VL", "AVX-512 Vector Length Orthogonality"); -if_("AVX512DQ", "AVX-512 Dword and Qword"); -if_("AVX512BW", "AVX-512 Byte and Word"); -if_("AVX512IFMA", "AVX-512 IFMA instructions"); -if_("AVX512VBMI", "AVX-512 VBMI instructions"); -if_("AES", "AES instructions"); -if_("VAES", "AES AVX instructions"); -if_("VPCLMULQDQ", "AVX Carryless Multiplication"); -if_("GFNI", "Galois Field instructions"); -if_("AVX512VBMI2", "AVX-512 VBMI2 instructions"); -if_("AVX512VNNI", "AVX-512 VNNI instructions"); -if_("AVX512BITALG", "AVX-512 Bit Algorithm instructions"); -if_("AVX512VPOPCNTDQ", "AVX-512 VPOPCNTD/VPOPCNTQ"); -if_("AVX5124FMAPS", "AVX-512 4-iteration multiply-add"); -if_("AVX5124VNNIW", "AVX-512 4-iteration dot product"); -if_("AVX512FP16", "AVX-512 FP16 instructions"); -if_("AVX512FC16", "AVX-512 FC16 instructions"); -if_("SGX", "Intel Software Guard Extensions (SGX)"); -if_("CET", "Intel Control-Flow Enforcement Technology (CET)"); -if_("ENQCMD", "Enqueue command instructions"); -if_("TSXLDTRK", "TSX suspend load address tracking"); -if_("AVX512BF16", "AVX-512 bfloat16"); -if_("AVX512VP2INTERSECT", "AVX-512 VP2INTERSECT instructions"); -if_("AMXTILE", "AMX tile configuration instructions"); -if_("AMXBF16", "AMX bfloat16 multiplication"); -if_("AMXINT8", "AMX 8-bit integer multiplication"); -if_("FRED", "Flexible Return and Exception Delivery (FRED)"); -if_("RAOINT", "Remote atomic operations (RAO-INT)"); -if_("UINTR", "User interrupts"); -if_("CMPCCXADD", "CMPccXADD instructions"); -if_("PREFETCHI", "PREFETCHI0 and PREFETCHI1"); -if_("MSRLIST", "RDMSRLIST and WRMSRLIST"); -if_("AVXNECONVERT", "AVX exceptionless floating-point conversions"); -if_("AVXVNNIINT8", "AVX Vector Neural Network 8-bit integer instructions"); -if_("AVXIFMA", "AVX integer multiply and add"); -if_("HRESET", "History reset"); -if_("SMAP", "Supervisor Mode Access Prevention (SMAP)"); -if_("SHA512", "SHA512 instructions"); -if_("HSM3", "SM3 hash instructions"); -if_("HSM4", "SM4 hash instructions"); -if_("APX", "Advanced Performance Extensions (APX)"); -if_("AVX10_1", "AVX 10.1 instructions"); -if_("AVX10_2", "AVX 10.2 instructions"); -if_("ADX", "ADCX and ADOX instructions"); -if_("PKU", "Protection key for user mode"); -if_("MONITOR", "MONITOR and MWAIT"); -if_("MONITORX", "MONITORX and MWAITX"); -if_("WAITPKG", "User wait instruction package"); +our @cpufeatures; -# Single-instruction CPUID bits without additional help text -my @oneins = qw(invpcid prefetchwt1 pconfig wbnoinvd serialize lkgs - wrmsrns clflushopt clwb rdrand rdseed rdpid - lzcnt ptwrite cldemote movdiri movdir64b clzero - movbe); -foreach my $ins (@oneins) { - if_($ins, "\U$ins\E instruction"); +foreach my $feat (@cpufeatures) { + if_($feat->{'cname'}, $feat->{'help'}); } -# Put these last to minimize their relevance -if_("OBSOLETE", "Instruction removed from architecture"); -if_("NEVER", "Instruction never implemented"); -if_("NOP", "Instruction is always a (nonintentional) NOP"); - -# -# dword bound - cpu type flags -# -# The CYRIX and AMD flags should have the highest bit values; the -# disassembler selection algorithm depends on it. -# -if_align('CPU'); - -if_("8086", "8086"); -if_("186", "186+"); -if_("286", "286+"); -if_("386", "386+"); -if_("486", "486+"); -if_("PENT", "Pentium"); -if_("P6", "P6"); -if_("KATMAI", "Katmai"); -if_("WILLAMETTE", "Willamette"); -if_("PRESCOTT", "Prescott"); -if_("IA64", "IA64 (in x86 mode)"); -if_("X86_64", "x86-64 (long or legacy mode)"); -if_("NEHALEM", "Nehalem"); -if_("WESTMERE", "Westmere"); -if_("SANDYBRIDGE", "Sandy Bridge"); -if_("FUTURE", "Ivy Bridge or newer"); - -# Default CPU level -if_("DEFAULT", "Default CPU level"); - -# Must be the last CPU definition -if_("ANY", "Allow any known instruction"); - -# These must come after the CPU definitions proper -if_("CYRIX", "Cyrix-specific"); -if_("AMD", "AMD-specific"); +1; diff --git a/x86/insns-iflags.ph b/x86/insns-iflags.ph index 0aa3e20fe..90ad0e04d 100644 --- a/x86/insns-iflags.ph +++ b/x86/insns-iflags.ph @@ -1,7 +1,7 @@ #!/usr/bin/perl ## -------------------------------------------------------------------------- ## -## Copyright 1996-2024 The NASM Authors - All Rights Reserved +## Copyright 1996-2025 The NASM Authors - All Rights Reserved ## See the file AUTHORS included with the NASM distribution for ## the specific copyright holders. ## @@ -64,6 +64,7 @@ # for a set of flags, so be careful moving bits (and # don't forget to update C code generation then). # + sub dword_align($) { my($n) = @_; @@ -71,7 +72,6 @@ sub dword_align($) { return $n; } - my $n_iflags = 0; my %flag_byname; my @flag_bynum; diff --git a/x86/insns.dat b/x86/insns.dat index c4c7e7858..a6d65d703 100644 --- a/x86/insns.dat +++ b/x86/insns.dat @@ -73,7 +73,7 @@ EQU imm:imm ignore PSEUDO ;# No operation ; In 64-bit mode NOP (90) is technically always 64 bits, but allow "o64 nop" to generate 48 90 NOP void [ osz norexb nof3 90] 8086 -NOP2 void [ norexb nof3 66 90] 386,ND +NOP2 void [ norexb nof3 66 90] 386,ND,UNDOC $wdq NOP rm# [m: o# 0f 1f /0] P6 ;# Integer data move instructions @@ -237,7 +237,7 @@ $wdq MOVZXD reg#,rm32 [rm: nw o# 8b /r] 8086,OPT,ND ;# Atomic operations $bwdq CMPXCHG rm#,reg# [mr: hle 0f b0# /r] PENT,SM,LOCK CMPXCHG8B mem64 [m: hle norexw 0f c7 /1] PENT,LOCK - CMPXCHG16B mem128 [m: o64 0f c7 /1] X86_64,LONG,LOCK + CMPXCHG16B mem128 [m: o64 0f c7 /1] CX16,LONG,LOCK $bwdq XADD rm#,reg# [mr: hle o# 0f c0# /r] 486,SM,LOCK @@ -294,12 +294,6 @@ $wdq LOOPNE imm8|near|short,cx# [i-: a# nw e0 rel8] 8086,NOAPX $wdq LOOPZ imm8|near|short,cx# [i-: a# nw e1 rel8] 8086,NOAPX,ND $wdq LOOPNZ imm8|near|short,cx# [i-: a# nw e0 rel8] 8086,NOAPX,ND -; JMPE is obsolete, but seems to be used by a fair number of virtual environments? -$zwdq JMPE imm##|near [i: nw o# 0f b8 rel] IA64 -; 0f 00 /6 with a prefix has been repurposed in long mode -$wdq JMPE rm#|near [m: nw o# np 0f 00 /6] IA64 -$wd JMPE rm#|near [m: o# 0f 00 /6] IA64,NOLONG - ;# Call and return $wdq CALL imm##|near [i: nw o# e8 rel] 8086,BND,NOAPX,OSIZE $wd CALL imm#|far [i: o# 9a iwd seg] 8086,ND,NOLONG,OSIZE @@ -327,17 +321,22 @@ INT03 void [ cc] 8086,ND BRKPT void [ cc] 8086,ND INTO void [ ce] 8086,NOLONG -SYSCALL void [ 0f 05] P6,AMD -SYSENTER void [ 0f 34] P6,NOAPX - -SYSEXIT void [ 0f 35] P6,PRIV,NOAPX +SYSCALL void [ 0f 05] SYSCALL,AMD SYSRET void [ 0f 07] P6,PRIV,AMD +SYSENTER void [ 0f 34] SYSENTER,NOAPX +SYSEXIT void [ 0f 35] SYSENTER,PRIV,NOAPX $zwdq IRET% void [ o# cf] 8086 ERETS void [ f2 0f 01 ca ] FRED,PRIV,LONG ERETU void [ f3 0f 01 ca ] FRED,PRIV,LONG +;# Jump to Emulator +$zwdq JMPE imm##|near [i: nw o# 0f b8 rel] JMPE +; 0f 00 /6 with a prefix has been repurposed in long mode +$wdq JMPE rm#|near [m: nw o# np 0f 00 /6] JMPE +$wd JMPE rm#|near [m: o# 0f 00 /6] JMPE,NOLONG + ;# Flag register instructions CLC void [ f8] 8086 CLD void [ fc] 8086 @@ -351,9 +350,11 @@ STAC void [ np 0f 01 cb] SMAP,PRIV CMC void [ f5] 8086 -LAHF void [ 9f] 8086 -SAHF void [ 9e] 8086 -SALC void [ d6] 8086,UNDOC +LAHF void [ 9f] 8086,NOLONG +SAHF void [ 9e] 8086,NOLONG +LAHF void [ 9f] LONG,LAHF_LM +SAHF void [ 9e] LONG,LAHF_LM +SALC void [ d6] 8086,NOLONG,UNDOC $zwdq PUSHF% void [ nw o# 9c] 8086 $zwdq POPF% void [ nw o# 9d] 8086 @@ -368,22 +369,22 @@ $bwd INS% void [ o# 6c#] 186 $bwd OUTS% void [ o# 6e#] 186 ;# Synchronization and fencing -LFENCE void [ np 0f ae e8] X86_64,LONG,AMD -MFENCE void [ np 0f ae f0] X86_64,LONG,AMD -SFENCE void [ np 0f ae f8] X86_64,LONG,AMD SERIALIZE void [ np 0f 01 e8] SERIALIZE +LFENCE void [ np 0f ae e8] SSE2 +MFENCE void [ np 0f ae f0] SSE2 +SFENCE void [ np 0f ae f8] KATMAI -;# Memory management and control -CLFLUSH mem [m: np 0f ae /7] WILLAMETTE,SSE2 +;# Cache and TLB management and control +CLFLUSH mem [m: np 0f ae /7] SSE2 CLFLUSHOPT mem [m: 66 0f ae /7] CLFLUSHOPT CLWB mem [m: 66 0f ae /6] CLWB ; This one was killed before it saw the light of day -PCOMMIT void [ 66 0f ae f8] NEVER,NOP +PCOMMIT void [ 66 0f ae f8] NEVER,OBSOLETE,NOP ; AMD Zen v1 -$wdq CLZERO ax# [-: a# 0f 01 fc] AMD,CLZERO - CLZERO void [ 0f 01 fc] AMD,CLZERO,ND +$wdq CLZERO ax# [-: a# 0f 01 fc] CLZERO + CLZERO void [ 0f 01 fc] CLZERO,ND INVD void [ 0f 08] 486,PRIV WBINVD void [ np 0f 09] 486,PRIV @@ -436,7 +437,7 @@ CPU_WRITE void [ m1 3c] PENT,NOLONG,CYRIX,OBSOLETE,ND DMINT void [ m1 39] P6,NOLONG,CYRIX,NOAPX,OBSOLETE,ND RDM void [ 0f 3a] P6,CYRIX,ND,NOLONG,OBSOLETE SMINT void [ m1 38] P6,CYRIX,ND,NOLONG,OBSOLETE -SMINTOLD void [ m1 7e] 486,CYRIX,ND,NOLONG,,OBSOLETE +SMINTOLD void [ m1 7e] 486,CYRIX,ND,NOLONG,OBSOLETE ;# System management mode RSM void [ 0f aa] PENT,SMM @@ -496,7 +497,7 @@ $wdq LSS reg#,mem# [rm: o# 0f b2 /r] 386,SM PUSH reg_gs [-: 0f a8] 386 POP reg_es [-: 07] 8086,NOLONG - POP reg_cs [-: m0 0f] 8086,NOLONG,UNDOC,ND,OBSOLETE + POP reg_cs [-: m0 0f] 8086ONLY,NOLONG,SUPERCEDED POP reg_ss [-: 17] 8086,NOLONG POP reg_ds [-: 1f] 8086,NOLONG POP reg_fs [-: 0f a1] 386 @@ -528,8 +529,8 @@ $dq SIDT mem# [m: nw o# 0f 01 /1] 286 $zwdq SLDT sel# [m: optd# 0f 00 /0] 286,PROT $zwdq STR sel# [m: optd# 0f 00 /1] 286,PROT -LOADALL void [ 0f 07] 386,UNDOC,ND,OBSOLETE -LOADALL286 void [ 0f 05] 286,UNDOC,ND,OBSOLETE +LOADALL void [ 0f 07] 386ONLY,UNDOC,OBSOLETE,SUPERCEDED +LOADALL286 void [ 0f 05] 286ONLY,UNDOC,OBSOLETE,SUPERCEDED ;# x87 floating point F2XM1 void [ d9 f0] 8086,FPU @@ -1048,7 +1049,6 @@ PREFETCHT1 mem8 [m: 0f 18 /2] KATMAI,SB PREFETCHT2 mem8 [m: 0f 18 /3] KATMAI,SB PREFETCHIT0 mem8 [m: 0f 18 /7] PREFETCHI,SB PREFETCHIT1 mem8 [m: 0f 18 /6] PREFETCHI,SB -SFENCE void [ np 0f ae f8] KATMAI ;# New MMX instructions introduced in Katmai MASKMOVQ mmxreg,mmxreg [rm: np 0f f7 /r] KATMAI,MMX @@ -1083,8 +1083,6 @@ MOVNTDQ mem,xmmreg [mr: 66 0f e7 /r] WILLAMETTE,SSE2,SO MOVNTI mem,reg32 [mr: np 0f c3 /r] WILLAMETTE,SD MOVNTI mem,reg64 [mr: o64 np 0f c3 /r] X86_64,LONG,SQ MOVNTPD mem,xmmreg [mr: 66 0f 2b /r] WILLAMETTE,SSE2,SO -LFENCE void [ np 0f ae e8] WILLAMETTE,SSE2 -MFENCE void [ np 0f ae f0] WILLAMETTE,SSE2 ;# Willamette MMX instructions (SSE2 SIMD Integer Instructions) MOVD mem,xmmreg [mr: 66 norexw 0f 7e /r] WILLAMETTE,SSE2,SD @@ -1352,77 +1350,77 @@ MOVNTSS mem32,xmmreg [mr: f3 0f 2b /r] SSE4A,AMD,SD ;# New instructions in Barcelona ;# Penryn New Instructions (SSE4.1) -BLENDPD xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 0d /r ib,u] SSE41 -BLENDPS xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 0c /r ib,u] SSE41 -BLENDVPD xmmreg,xmmrm128,xmm0 [rm-: 66 0f38 15 /r] SSE41 -BLENDVPD xmmreg,xmmrm128 [rm: 66 0f38 15 /r] SSE41 -BLENDVPS xmmreg,xmmrm128,xmm0 [rm-: 66 0f38 14 /r] SSE41 -BLENDVPS xmmreg,xmmrm128 [rm: 66 0f38 14 /r] SSE41 -DPPD xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 41 /r ib,u] SSE41 -DPPS xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 40 /r ib,u] SSE41 -EXTRACTPS rm32,xmmreg,imm8 [mri: 66 0f3a 17 /r ib,u] SSE41 -EXTRACTPS reg64,xmmreg,imm8 [mri: o64 66 0f3a 17 /r ib,u] SSE41,X86_64,LONG -INSERTPS xmmreg,xmmrm32,imm8 [rmi: 66 0f3a 21 /r ib,u] SSE41 -MOVNTDQA xmmreg,mem128 [rm: 66 0f38 2a /r] SSE41 -MPSADBW xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 42 /r ib,u] SSE41 -PACKUSDW xmmreg,xmmrm128 [rm: 66 0f38 2b /r] SSE41 -PBLENDVB xmmreg,xmmrm,xmm0 [rm-: 66 0f38 10 /r] SSE41 -PBLENDVB xmmreg,xmmrm128 [rm: 66 0f38 10 /r] SSE41 -PBLENDW xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 0e /r ib,u] SSE41 -PCMPEQQ xmmreg,xmmrm128 [rm: 66 0f38 29 /r] SSE41 -PEXTRB reg32,xmmreg,imm8 [mri: 66 0f3a 14 /r ib,u] SSE41 -PEXTRB mem8,xmmreg,imm8 [mri: 66 0f3a 14 /r ib,u] SSE41 -PEXTRB reg64,xmmreg,imm8 [mri: o64nw 66 0f3a 14 /r ib,u] SSE41,X86_64,LONG -PEXTRD rm32,xmmreg,imm8 [mri: norexw 66 0f3a 16 /r ib,u] SSE41 -PEXTRQ rm64,xmmreg,imm8 [mri: o64 66 0f3a 16 /r ib,u] SSE41,X86_64,LONG -PEXTRW reg32,xmmreg,imm8 [mri: 66 0f3a 15 /r ib,u] SSE41 -PEXTRW mem16,xmmreg,imm8 [mri: 66 0f3a 15 /r ib,u] SSE41 -PEXTRW reg64,xmmreg,imm8 [mri: o64 66 0f3a 15 /r ib,u] SSE41,X86_64,LONG -PHMINPOSUW xmmreg,xmmrm128 [rm: 66 0f38 41 /r] SSE41 -PINSRB xmmreg,mem,imm8 [rmi: 66 0f3a 20 /r ib,u] SSE41,SB,AR2 -PINSRB xmmreg,rm8,imm8 [rmi: nohi 66 0f3a 20 /r ib,u] SSE41,SB,AR2 -PINSRB xmmreg,reg32,imm8 [rmi: 66 0f3a 20 /r ib,u] SSE41,SB,AR2 -PINSRD xmmreg,rm32,imm8 [rmi: norexw 66 0f3a 22 /r ib,u] SSE41,SB,AR2 -PINSRQ xmmreg,rm64,imm8 [rmi: o64 66 0f3a 22 /r ib,u] SSE41,X86_64,LONG,SB,AR2 -PMAXSB xmmreg,xmmrm128 [rm: 66 0f38 3c /r] SSE41 -PMAXSD xmmreg,xmmrm128 [rm: 66 0f38 3d /r] SSE41 -PMAXUD xmmreg,xmmrm128 [rm: 66 0f38 3f /r] SSE41 -PMAXUW xmmreg,xmmrm128 [rm: 66 0f38 3e /r] SSE41 -PMINSB xmmreg,xmmrm128 [rm: 66 0f38 38 /r] SSE41 -PMINSD xmmreg,xmmrm128 [rm: 66 0f38 39 /r] SSE41 -PMINUD xmmreg,xmmrm128 [rm: 66 0f38 3b /r] SSE41 -PMINUW xmmreg,xmmrm128 [rm: 66 0f38 3a /r] SSE41 -PMOVSXBW xmmreg,xmmrm64 [rm: 66 0f38 20 /r] SSE41,SQ -PMOVSXBD xmmreg,xmmrm32 [rm: 66 0f38 21 /r] SSE41,SD -PMOVSXBQ xmmreg,xmmrm16 [rm: 66 0f38 22 /r] SSE41,SW -PMOVSXWD xmmreg,xmmrm64 [rm: 66 0f38 23 /r] SSE41,SQ -PMOVSXWQ xmmreg,xmmrm32 [rm: 66 0f38 24 /r] SSE41,SD -PMOVSXDQ xmmreg,xmmrm64 [rm: 66 0f38 25 /r] SSE41,SQ -PMOVZXBW xmmreg,xmmrm64 [rm: 66 0f38 30 /r] SSE41,SQ -PMOVZXBD xmmreg,xmmrm32 [rm: 66 0f38 31 /r] SSE41,SD -PMOVZXBQ xmmreg,xmmrm16 [rm: 66 0f38 32 /r] SSE41,SW -PMOVZXWD xmmreg,xmmrm64 [rm: 66 0f38 33 /r] SSE41,SQ -PMOVZXWQ xmmreg,xmmrm32 [rm: 66 0f38 34 /r] SSE41,SD -PMOVZXDQ xmmreg,xmmrm64 [rm: 66 0f38 35 /r] SSE41,SQ -PMULDQ xmmreg,xmmrm128 [rm: 66 0f38 28 /r] SSE41 -PMULLD xmmreg,xmmrm128 [rm: 66 0f38 40 /r] SSE41 -PTEST xmmreg,xmmrm128 [rm: 66 0f38 17 /r] SSE41 -ROUNDPD xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 09 /r ib,u] SSE41 -ROUNDPS xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 08 /r ib,u] SSE41 -ROUNDSD xmmreg,xmmrm64,imm8 [rmi: 66 0f3a 0b /r ib,u] SSE41 -ROUNDSS xmmreg,xmmrm32,imm8 [rmi: 66 0f3a 0a /r ib,u] SSE41 +BLENDPD xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 0d /r ib,u] SSE4_1 +BLENDPS xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 0c /r ib,u] SSE4_1 +BLENDVPD xmmreg,xmmrm128,xmm0 [rm-: 66 0f38 15 /r] SSE4_1 +BLENDVPD xmmreg,xmmrm128 [rm: 66 0f38 15 /r] SSE4_1 +BLENDVPS xmmreg,xmmrm128,xmm0 [rm-: 66 0f38 14 /r] SSE4_1 +BLENDVPS xmmreg,xmmrm128 [rm: 66 0f38 14 /r] SSE4_1 +DPPD xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 41 /r ib,u] SSE4_1 +DPPS xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 40 /r ib,u] SSE4_1 +EXTRACTPS rm32,xmmreg,imm8 [mri: 66 0f3a 17 /r ib,u] SSE4_1 +EXTRACTPS reg64,xmmreg,imm8 [mri: o64 66 0f3a 17 /r ib,u] SSE4_1,X86_64,LONG +INSERTPS xmmreg,xmmrm32,imm8 [rmi: 66 0f3a 21 /r ib,u] SSE4_1 +MOVNTDQA xmmreg,mem128 [rm: 66 0f38 2a /r] SSE4_1 +MPSADBW xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 42 /r ib,u] SSE4_1 +PACKUSDW xmmreg,xmmrm128 [rm: 66 0f38 2b /r] SSE4_1 +PBLENDVB xmmreg,xmmrm,xmm0 [rm-: 66 0f38 10 /r] SSE4_1 +PBLENDVB xmmreg,xmmrm128 [rm: 66 0f38 10 /r] SSE4_1 +PBLENDW xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 0e /r ib,u] SSE4_1 +PCMPEQQ xmmreg,xmmrm128 [rm: 66 0f38 29 /r] SSE4_1 +PEXTRB reg32,xmmreg,imm8 [mri: 66 0f3a 14 /r ib,u] SSE4_1 +PEXTRB mem8,xmmreg,imm8 [mri: 66 0f3a 14 /r ib,u] SSE4_1 +PEXTRB reg64,xmmreg,imm8 [mri: o64nw 66 0f3a 14 /r ib,u] SSE4_1,X86_64,LONG +PEXTRD rm32,xmmreg,imm8 [mri: norexw 66 0f3a 16 /r ib,u] SSE4_1 +PEXTRQ rm64,xmmreg,imm8 [mri: o64 66 0f3a 16 /r ib,u] SSE4_1,X86_64,LONG +PEXTRW reg32,xmmreg,imm8 [mri: 66 0f3a 15 /r ib,u] SSE4_1 +PEXTRW mem16,xmmreg,imm8 [mri: 66 0f3a 15 /r ib,u] SSE4_1 +PEXTRW reg64,xmmreg,imm8 [mri: o64 66 0f3a 15 /r ib,u] SSE4_1,X86_64,LONG +PHMINPOSUW xmmreg,xmmrm128 [rm: 66 0f38 41 /r] SSE4_1 +PINSRB xmmreg,mem,imm8 [rmi: 66 0f3a 20 /r ib,u] SSE4_1,SB,AR2 +PINSRB xmmreg,rm8,imm8 [rmi: nohi 66 0f3a 20 /r ib,u] SSE4_1,SB,AR2 +PINSRB xmmreg,reg32,imm8 [rmi: 66 0f3a 20 /r ib,u] SSE4_1,SB,AR2 +PINSRD xmmreg,rm32,imm8 [rmi: norexw 66 0f3a 22 /r ib,u] SSE4_1,SB,AR2 +PINSRQ xmmreg,rm64,imm8 [rmi: o64 66 0f3a 22 /r ib,u] SSE4_1,X86_64,LONG,SB,AR2 +PMAXSB xmmreg,xmmrm128 [rm: 66 0f38 3c /r] SSE4_1 +PMAXSD xmmreg,xmmrm128 [rm: 66 0f38 3d /r] SSE4_1 +PMAXUD xmmreg,xmmrm128 [rm: 66 0f38 3f /r] SSE4_1 +PMAXUW xmmreg,xmmrm128 [rm: 66 0f38 3e /r] SSE4_1 +PMINSB xmmreg,xmmrm128 [rm: 66 0f38 38 /r] SSE4_1 +PMINSD xmmreg,xmmrm128 [rm: 66 0f38 39 /r] SSE4_1 +PMINUD xmmreg,xmmrm128 [rm: 66 0f38 3b /r] SSE4_1 +PMINUW xmmreg,xmmrm128 [rm: 66 0f38 3a /r] SSE4_1 +PMOVSXBW xmmreg,xmmrm64 [rm: 66 0f38 20 /r] SSE4_1,SQ +PMOVSXBD xmmreg,xmmrm32 [rm: 66 0f38 21 /r] SSE4_1,SD +PMOVSXBQ xmmreg,xmmrm16 [rm: 66 0f38 22 /r] SSE4_1,SW +PMOVSXWD xmmreg,xmmrm64 [rm: 66 0f38 23 /r] SSE4_1,SQ +PMOVSXWQ xmmreg,xmmrm32 [rm: 66 0f38 24 /r] SSE4_1,SD +PMOVSXDQ xmmreg,xmmrm64 [rm: 66 0f38 25 /r] SSE4_1,SQ +PMOVZXBW xmmreg,xmmrm64 [rm: 66 0f38 30 /r] SSE4_1,SQ +PMOVZXBD xmmreg,xmmrm32 [rm: 66 0f38 31 /r] SSE4_1,SD +PMOVZXBQ xmmreg,xmmrm16 [rm: 66 0f38 32 /r] SSE4_1,SW +PMOVZXWD xmmreg,xmmrm64 [rm: 66 0f38 33 /r] SSE4_1,SQ +PMOVZXWQ xmmreg,xmmrm32 [rm: 66 0f38 34 /r] SSE4_1,SD +PMOVZXDQ xmmreg,xmmrm64 [rm: 66 0f38 35 /r] SSE4_1,SQ +PMULDQ xmmreg,xmmrm128 [rm: 66 0f38 28 /r] SSE4_1 +PMULLD xmmreg,xmmrm128 [rm: 66 0f38 40 /r] SSE4_1 +PTEST xmmreg,xmmrm128 [rm: 66 0f38 17 /r] SSE4_1 +ROUNDPD xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 09 /r ib,u] SSE4_1 +ROUNDPS xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 08 /r ib,u] SSE4_1 +ROUNDSD xmmreg,xmmrm64,imm8 [rmi: 66 0f3a 0b /r ib,u] SSE4_1 +ROUNDSS xmmreg,xmmrm32,imm8 [rmi: 66 0f3a 0a /r ib,u] SSE4_1 ;# Nehalem New Instructions (SSE4.2) -CRC32 reg32,rm8 [rm: f2i 0f38 f0 /r] SSE42 -CRC32 reg32,rm16 [rm: o16 f2i 0f38 f1 /r] SSE42 -CRC32 reg32,rm32 [rm: o32 f2i 0f38 f1 /r] SSE42 -CRC32 reg64,rm8 [rm: o64 f2i 0f38 f0 /r] SSE42,X86_64,LONG -CRC32 reg64,rm64 [rm: o64 f2i 0f38 f1 /r] SSE42,X86_64,LONG -PCMPESTRI xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 61 /r ib,u] SSE42 -PCMPESTRM xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 60 /r ib,u] SSE42 -PCMPISTRI xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 63 /r ib,u] SSE42 -PCMPISTRM xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 62 /r ib,u] SSE42 -PCMPGTQ xmmreg,xmmrm128 [rm: 66 0f38 37 /r] SSE42 +CRC32 reg32,rm8 [rm: f2i 0f38 f0 /r] SSE4_2 +CRC32 reg32,rm16 [rm: o16 f2i 0f38 f1 /r] SSE4_2 +CRC32 reg32,rm32 [rm: o32 f2i 0f38 f1 /r] SSE4_2 +CRC32 reg64,rm8 [rm: o64 f2i 0f38 f0 /r] SSE4_2,X86_64,LONG +CRC32 reg64,rm64 [rm: o64 f2i 0f38 f1 /r] SSE4_2,X86_64,LONG +PCMPESTRI xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 61 /r ib,u] SSE4_2 +PCMPESTRM xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 60 /r ib,u] SSE4_2 +PCMPISTRI xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 63 /r ib,u] SSE4_2 +PCMPISTRM xmmreg,xmmrm128,imm8 [rmi: 66 0f3a 62 /r ib,u] SSE4_2 +PCMPGTQ xmmreg,xmmrm128 [rm: 66 0f38 37 /r] SSE4_2 POPCNT reg16,rm16 [rm: o16 f3i 0f b8 /r] NEHALEM,SW POPCNT reg32,rm32 [rm: o32 f3i 0f b8 /r] NEHALEM,SD POPCNT reg64,rm64 [rm: o64 f3i 0f b8 /r] NEHALEM,SQ,LONG diff --git a/x86/rex2.pl b/x86/rex2.pl old mode 100644 new mode 100755 diff --git a/x86/x86features.ph b/x86/x86features.ph new file mode 100644 index 000000000..15c6194e5 --- /dev/null +++ b/x86/x86features.ph @@ -0,0 +1,183 @@ +# -*- perl -*- +# +# CPU feature sets and their dependencies. +# + +use integer; +use strict; + +our %cpufeature; # cpu features by name +our @cpufeatures; # List of cpu features in numeric order +our @cpus; + +# Make a name into a valid upper case C identifier suffix +sub cname($) { + my($n) = @_; + $n = uc($n); + $n =~ s/[^\w]/_/g; + $n =~ s/__+/_/g; + return $n; +} + +# Create a new CPU feature flag +sub f_($;$@) { + my($name, $help, @deplist) = @_; + + $name = lc($name); + if (!defined($help)) { + $help = uc($name)." instruction"; + } + + my $feat = { + 'name' => $name, + 'cname' => cname($name), + 'help' => $help, + '_dep' => {$name => 1}, # All features "depend" on themselves + 'num' => scalar(@cpufeatures), + 'vendor' => 0 + }; + $cpufeature{$name} = $feat; + push(@cpufeatures, $feat); + + d_($name, @deplist); + return $feat; +} + +# Add dependencies to a CPU feature +sub d_($@) { + my($name, @deplist) = @_; + + $name = lc($name); + my $feat = $cpufeature{$name}; + if (!defined($feat)) { + die "$0: tried to add dependencies to nonexistent cpu feature \U$name\E \n"; + } + + my $_dep = $feat->{'_dep'}; + foreach my $d (@deplist) { + $_dep->{lc($d)} = 1; + } +} + +# The actual feature list +require 'x86/features.ph' + +# +# Vendor flags +# +foreach my $v (qw(Cyrix AMD Intel)) { + my $feat = f_($v, "$v-specific instructions", qw(vendor)); + $feat->{'vendor'} = 1; +} + +# +# Some automatically generated dependencies +# +foreach my $fn (keys(%cpufeature)) { + my $feat = $cpufeature{$fn}; + my $name = $feat->{'name'}; + + if ($fn =~ /^avx512.+$/) { + d_('avx512', $name); + } elsif ($fn =~ /^avx.+$/) { + d_($name, 'avx'); + } + + if ($fn =~ /^apx.+$/) { + d_('apx', $name); + } +} + +# +# Compute the transitive closure and produce dependencies as a list of +# references as opposed to names. +# +# Although a circular dependency isn't incorrect as such, it also means +# that all the CPU features in the circle are in fact identical, and so +# they should be merged into one feature bit. +# +sub _cpufeature_closure($;@) { + my($feat, @stack) = @_; + my $name = $feat->{'name'}; + my $dep = $feat->{'_dep'}; + + my @_deps = keys(%$dep); + + if (defined($feat->{'deps'})) { + return @_deps; + } elsif (exists($feat->{'deps'})) { + my $list = join(', ', map { $_->{'name'} } ($feat, @stack)); + die "$0: circular depencency for CPU features $list\n"; + } + + # Mark this as in progress for loop detection + $feat->{'deps'} = undef; + + # For better error messages + push(@stack, $feat); + + foreach my $depname (@_deps) { + next if ($depname eq $name); + my $cfeat = $cpufeature{$depname}; + if (!defined($cfeat)) { + die "$0: feature $name depends on non-existent feature $depname\n"; + } + foreach my $cdep (_cpufeature_closure($cfeat, @stack)) { + $dep->{$cdep}++; + } + } + + @_deps = keys(%$dep); # Update with the closure + + my $deps = [sort { $a->{'num'} <=> $b->{'num'} } + map { $cpufeature{$_} } @_deps]; + + return $feat->{'deps'} = $deps; +} + +foreach my $feat (@cpufeatures) { + _cpufeature_closure($feat); +} + +# +# Create the anti-depency mask, that is, the list of features to be removed +# when a certain feature is removed, too. +# +foreach my $feat (@cpufeatures) { + $feat->{'nuke'} = []; +} +foreach my $feat (@cpufeatures) { + foreach my $dep (@{$feat->{'deps'}}) { + push(@{$dep->{'nuke'}}, $feat); + } +} + +# +# Create bitmasks from lists +# +our $cpufeature_bits = $cpufeatures[-1]->{'num'} + 1; +our $cpufeature_words = ($cpufeature_bits + 31) >> 5; + +sub set_bits(\@@) { + my $words = shift(@_); + + foreach my $n (@_) { + $words->[$n >> 5] |= 1 << ($n & 31); + } + + return $words; +} + +sub makemask($$) { + my($feat, $field) = @_; + my $fw = [(0) x $cpufeature_words]; + set_bits(@$fw, map { $_->{'num'} } @{$feat->{$field}}); + return $fw; +} + +foreach my $feat (@cpufeatures) { + $feat->{'depmask'} = makemask($feat, 'deps'); + $feat->{'badmask'} = makemask($feat, 'nuke'); +} + +1;