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.
This commit is contained in:
H. Peter Anvin 2025-08-09 15:05:08 -07:00
parent 0ab85c391c
commit 00d7defbca
19 changed files with 923 additions and 421 deletions

2
.gitignore vendored
View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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)
{

View file

@ -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 {+|-}] */

View file

@ -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()) {
/*

View file

@ -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)

View file

@ -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
*

2
x86/cpunames.c Normal file
View file

@ -0,0 +1,2 @@
#define COMPILING_CPUNAMES_C 1
#include "cpunames.h"

98
x86/cpunames.ph Normal file
View file

@ -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;

136
x86/cpunames.pl Normal file
View file

@ -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";

2
x86/featureinfo.c Normal file
View file

@ -0,0 +1,2 @@
#define COMPILING_FEATUREINFO_C 1
#include "featureinfo.h"

51
x86/featureinfo.pl Executable file
View file

@ -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);

137
x86/features.ph Normal file
View file

@ -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));

View file

@ -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;

View file

@ -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;

View file

@ -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

0
x86/rex2.pl Normal file → Executable file
View file

183
x86/x86features.ph Normal file
View file

@ -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;