WIP: APX: can now generate code for tested APX instructions

a
Support generating code for APX instruction and add support for the
{nf} prefix.

No disassembler support yet, and only a handful instructions encoded.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
This commit is contained in:
H. Peter Anvin 2024-07-29 20:58:04 -07:00
parent f10f278edd
commit 2e4df506e0
13 changed files with 543 additions and 307 deletions

View file

@ -113,8 +113,8 @@ MANIFEST = @MANIFEST@
NASM = asm/nasm.$(O)
NDISASM = disasm/ndisasm.$(O)
PROGOBJ = $(NASM) $(NDISASM)
PROGS = nasm$(X) ndisasm$(X)
PROGOBJ = $(NASM) # $(NDISASM)
PROGS = nasm$(X) # ndisasm$(X)
LIBOBJ_NW = stdlib/snprintf.$(O) stdlib/vsnprintf.$(O) stdlib/strlcpy.$(O) \
stdlib/strnlen.$(O) stdlib/strrchrnul.$(O) \
@ -160,8 +160,9 @@ LIBOBJ_NW = stdlib/snprintf.$(O) stdlib/vsnprintf.$(O) stdlib/strlcpy.$(O) \
output/outobj.$(O) output/outas86.$(O) \
output/outdbg.$(O) output/outieee.$(O) output/outmacho.$(O) \
output/codeview.$(O) \
\
disasm/disasm.$(O) disasm/sync.$(O)
# \
# disasm/disasm.$(O) disasm/sync.$(O)
# Warnings depend on all source files, so handle them separately
WARNOBJ = asm/warnings.$(O)

View file

@ -102,9 +102,9 @@ static enum match_result find_match(const struct itemplate **tempp,
static enum match_result matches(const struct itemplate *, insn *, int bits);
static opflags_t regflag(const operand *);
static int32_t regval(const operand *);
static int rexflags(int, opflags_t, int);
static int op_rexflags(const operand *, int);
static int op_evexflags(const operand *, int, uint8_t);
static uint32_t rexflags(int, opflags_t, uint32_t);
static uint32_t op_rexflags(const operand *, uint32_t);
static uint32_t op_evexflags(const operand *, uint32_t);
static void add_asp(insn *, int);
static int process_ea(operand *, ea *, int, int, opflags_t,
@ -151,31 +151,37 @@ static const char *size_name(int size)
}
}
static void warn_overflow(int size)
static void warn_overflow(int size, const char *prefix)
{
nasm_warn(ERR_PASS2 | WARN_NUMBER_OVERFLOW, "%s data exceeds bounds",
size_name(size));
nasm_warn(ERR_PASS2 | WARN_NUMBER_OVERFLOW,
"%s%s data exceeds bounds",
prefix, size_name(size));
}
static void warn_overflow_const(int64_t data, int size)
{
if (overflow_general(data, size))
warn_overflow(size);
warn_overflow(size, "");
}
static void warn_overflow_out(int64_t data, int size, enum out_flags flags)
{
bool err;
const char *prefix;
if (flags & OUT_SIGNED)
if (flags & OUT_SIGNED) {
prefix = "signed ";
err = overflow_signed(data, size);
else if (flags & OUT_UNSIGNED)
} else if (flags & OUT_UNSIGNED) {
prefix = "unsigned ";
err = overflow_unsigned(data, size);
else
} else {
prefix = "";
err = overflow_general(data, size);
}
if (err)
warn_overflow(size);
warn_overflow(size, prefix);
}
/*
@ -462,6 +468,24 @@ static void out_rawbyte(struct out_data *data, uint8_t byte)
out(data);
}
static void out_rawword(struct out_data *data, uint16_t value)
{
uint16_t buf = cpu_to_le16(value);
data->type = OUT_RAWDATA;
data->data = &buf;
data->size = 2;
out(data);
}
static void out_rawdword(struct out_data *data, uint32_t value)
{
uint32_t buf = cpu_to_le32(value);
data->type = OUT_RAWDATA;
data->data = &buf;
data->size = 4;
out(data);
}
static inline void out_reserve(struct out_data *data, uint64_t size)
{
data->type = OUT_RESERVE;
@ -1213,7 +1237,6 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
const uint8_t *codes = temp->code;
int64_t length = 0;
uint8_t c;
int rex_mask = ~0;
int op1, op2;
struct operand *opx;
uint8_t opex = 0;
@ -1223,12 +1246,14 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
enum reg_enum mib_index = R_none; /* For a separate index reg form */
const char *errmsg;
ins->rex = 0; /* Ensure REX is reset */
ins->rex = 0; /* Ensure REX is reset */
ins->evex = 0; /* Ensure EVEX is reset */
ins->vexreg = 0; /* No V register */
ins->vex_cm = 0; /* No implicit map */
eat = EA_SCALAR; /* Expect a scalar EA */
memset(ins->evex_p, 0, 3); /* Ensure EVEX is reset */
if (ins->prefixes[PPS_OSIZE] == P_O64)
ins->rex |= REX_W;
/* Default operand size */
ins->op_size = bits == 16 ? 32 : 16;
(void)segment; /* Don't warn that this parameter is unused */
(void)offset; /* Don't warn that this parameter is unused */
@ -1250,8 +1275,7 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
break;
case4(010):
ins->rex |=
op_rexflags(opx, REX_B|REX_H|REX_P|REX_W);
ins->rex |= op_rexflags(opx, REX_rB);
codes++, length++;
break;
@ -1315,7 +1339,7 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
c = *codes++;
op2 = (op2 & ~3) | ((c >> 3) & 3);
opx = &ins->oprs[op2];
ins->rex |= op_rexflags(opx, REX_R|REX_H|REX_P|REX_W);
ins->rex |= op_rexflags(opx, REX_rR);
length++;
break;
@ -1330,19 +1354,19 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
break;
case4(0240):
ins->rex |= REX_EV;
ins->vexreg = regval(opx);
ins->evex_p[2] |= op_evexflags(opx, EVEX_P2VP, 2); /* High-16 NDS */
ins->vex_cm = *codes++;
ins->vex_wlp = *codes++;
ins->evex_tuple = (*codes++ - 0300);
break;
goto evex_common;
case 0250:
ins->rex |= REX_EV;
ins->vexreg = 0;
ins->vex_cm = *codes++;
ins->vex_wlp = *codes++;
goto evex_common;
evex_common:
ins->rex |= REX_EV;
ins->evex = 0x62;
ins->evex += *codes++ << 8;
ins->evex += *codes++ << 16;
ins->evex += *codes++ << 24;
ins->evex_tuple = (*codes++ - 0300);
break;
@ -1351,16 +1375,16 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
break;
case4(0260):
ins->rex |= REX_V;
ins->vexreg = regval(opx);
ins->vex_cm = *codes++;
ins->vex_wlp = *codes++;
break;
goto vex_common;
case 0270:
ins->rex |= REX_V;
ins->vexreg = 0;
ins->vex_cm = *codes++;
goto vex_common;
vex_common:
ins->rex |= REX_V;
ins->vex_cm = *codes++;
ins->vex_wlp = *codes++;
break;
@ -1405,39 +1429,54 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
*! The operand prefix will be ignored by the assembler.
*/
enum prefixes pfx = ins->prefixes[PPS_OSIZE];
if (pfx == P_O16)
break;
if (pfx != P_none)
ins->op_size = 16;
if (bits != 16 && pfx == P_OSP) {
/* Allow osp prefix as is */
} else if (pfx != P_none && pfx != P_O16) {
nasm_warn(WARN_PREFIX_OPSIZE|ERR_PASS2,
"invalid operand size prefix, must be o16");
else
} else {
ins->prefixes[PPS_OSIZE] = P_O16;
}
break;
}
case 0321:
{
enum prefixes pfx = ins->prefixes[PPS_OSIZE];
if (pfx == P_O32)
break;
if (pfx != P_none)
ins->op_size = 32;
if (bits == 16 && pfx == P_OSP) {
/* Allow osp prefix as is */
} else if (pfx != P_none && pfx != P_O32) {
nasm_warn(WARN_PREFIX_OPSIZE|ERR_PASS2,
"invalid operand size prefix, must be o32");
else
} else {
ins->prefixes[PPS_OSIZE] = P_O32;
}
break;
}
case 0322:
break;
case 0323:
rex_mask &= ~REX_W;
break;
case 0324:
ins->rex |= REX_W;
/* fall through */
case 0323:
{
enum prefixes pfx = ins->prefixes[PPS_OSIZE];
ins->op_size = 64;
if (pfx == P_OSP) {
/* Ignore operand size prefix */
} else if (pfx != P_none && pfx != P_O64) {
nasm_warn(WARN_PREFIX_OPSIZE|ERR_PASS2,
"invalid operand size prefix, must be o64");
} else {
ins->prefixes[PPS_OSIZE] = P_none;
}
break;
}
case 0325:
ins->rex |= REX_NH;
@ -1498,6 +1537,19 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
ins->prefixes[PPS_WAIT] = P_WAIT;
break;
case4(0350):
case4(0354):
ins->rex |= REX_2 | ((c & 4) << (13-2)); /* X1 bit */
goto rexx_common;
case4(0344):
goto rexx_common;
rexx_common:
ins->rex |= (c & 2) << (3-1); /* W bit */
ins->vex_cm = c & 1;
break;
case 0360:
break;
@ -1570,20 +1622,15 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
if (op_er_sae && (op_er_sae->decoflags & (ER | SAE))) {
/* set EVEX.b */
ins->evex_p[2] |= EVEX_P2B;
ins->evex ^= EVEX_P2B;
if (op_er_sae->decoflags & ER) {
/* set EVEX.RC (rounding control) */
ins->evex_p[2] |= ((ins->evex_rm - BRC_RN) << 5)
& EVEX_P2RC;
}
} else {
/* set EVEX.L'L (vector length) */
ins->evex_p[2] |= ((ins->vex_wlp << (5 - 2)) & EVEX_P2LL);
ins->evex_p[1] |= ((ins->vex_wlp << (7 - 4)) & EVEX_P1W);
if (opy->decoflags & BRDCAST_MASK) {
/* set EVEX.b */
ins->evex_p[2] |= EVEX_P2B;
ins->evex ^= ((ins->evex_rm - BRC_RN) << 29)
& EVEX_P2RC;
}
} else if (opy->decoflags & BRDCAST_MASK) {
/* set EVEX.b */
ins->evex ^= EVEX_P2B;
}
if (itemp_has(temp, IF_MIB)) {
@ -1622,8 +1669,6 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
}
}
ins->rex &= rex_mask;
if (ins->rex & REX_NH) {
if (ins->rex & REX_H) {
nasm_nonfatal("instruction cannot use high registers");
@ -1644,70 +1689,93 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
return -1;
break;
case P_REX:
if (ins->rex & (REX_V|REX_EV))
if (ins->rex & (REX_V|REX_EV|REX_2))
return -1;
ins->rex |= REX_P; /* Force REX prefix */
break;
case P_REX2:
if (ins->rex & (REX_V|REX_EV))
return -1;
ins->rex |= REX_P | REX_2; /* Force REX2 prefix */
break;
default:
break;
}
if (ins->rex & (REX_V | REX_EV)) {
int bad32 = REX_R|REX_W|REX_X|REX_B;
uint32_t bad32 = REX_BXR;
if (ins->rex & REX_H) {
nasm_nonfatal("cannot use high register in AVX instruction");
nasm_nonfatal("cannot use high byte register in this instruction");
return -1;
}
switch (ins->vex_wlp & 060) {
case 000:
case 040:
ins->rex &= ~REX_W;
break;
case 020:
ins->rex |= REX_W;
bad32 &= ~REX_W;
break;
case 060:
/* Follow REX_W */
break;
if (itemp_has(temp, IF_WW)) {
bad32 |= REX_W;
} else {
ins->rex = (ins->rex & ~REX_W) | ((ins->vex_wlp >> (7-3)) & REX_W);
}
if (bits != 64 && ((ins->rex & bad32) || ins->vexreg > 7)) {
nasm_nonfatal("invalid operands in non-64-bit mode");
return -1;
} else if (!(ins->rex & REX_EV) &&
((ins->vexreg > 15) || (ins->evex_p[0] & 0xf0))) {
nasm_nonfatal("invalid high-16 register in non-AVX-512");
}
if (ins->rex & REX_EV) {
/* EVEX */
length += 4;
} else {
/* VEX */
if (ins->vexreg > 15 || (ins->rex & REX_BXR1))
nasm_nonfatal("invalid high-16 register in non-AVX-512");
return -1;
if (ins->vex_cm != 1 || (ins->rex & REX_BXR0) ||
ins->prefixes[PPS_REX] == P_VEX3) {
/* VEX3 required */
if (ins->prefixes[PPS_REX] == P_VEX2)
nasm_nonfatal("instruction not encodable with {vex2} prefix");
length += 3;
} else {
/* VEX2 available */
length += 2;
}
}
} else if (ins->rex & (REX_BXR1 | REX_2)) {
/* REX2 prefix needed */
if (ins->rex & REX_H) {
nasm_nonfatal("cannot use high byte register in rex2 instruction");
return -1;
}
if (ins->rex & REX_EV) {
length += 4;
} else if (ins->vex_cm != 1 || (ins->rex & (REX_W|REX_X|REX_B)) ||
ins->prefixes[PPS_REX] == P_VEX3) {
if (ins->prefixes[PPS_REX] == P_VEX2)
nasm_nonfatal("instruction not encodable with {vex2} prefix");
length += 3;
} else {
length += 2;
if (bits != 64) {
nasm_nonfatal("invalid operands in non-64-bit mode");
return -1;
}
if (!itemp_has(temp, IF_REX2) || !iflag_test(&cpu, IF_APX)) {
nasm_nonfatal("invalid operands in non-APX mode");
return -1;
}
ins->rex |= REX_2 | REX_P;
length += 2;
} else if (ins->rex & REX_MASK) {
if (ins->rex & REX_H) {
nasm_nonfatal("cannot use high byte register in rex instruction");
return -1;
} else if (bits == 64) {
length++;
} else if ((ins->rex & REX_L) &&
!(ins->rex & (REX_P|REX_W|REX_X|REX_B)) &&
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)) {
/* LOCK-as-REX.R */
assert_no_prefix(ins, PPS_LOCK);
lockcheck = false; /* Already errored, no need for warning */
length++;
ins->rex &= ~REX_P;
} else {
nasm_nonfatal("invalid operands in non-64-bit mode");
return -1;
}
/* Implicitly encoded legacy prefixes */
length += 1 + (ins->vex_cm > 0) + (ins->vex_cm > 1);
}
if (lockcheck && has_prefix(ins, PPS_LOCK, P_LOCK)) {
@ -1754,13 +1822,30 @@ static int64_t calcsize(int32_t segment, int64_t offset, int bits,
static inline void emit_rex(struct out_data *data, insn *ins)
{
if (data->bits == 64) {
if ((ins->rex & REX_MASK) &&
!(ins->rex & (REX_V | REX_EV)) &&
!ins->rex_done) {
uint8_t rex = (ins->rex & REX_MASK) | REX_P;
if (data->bits != 64 || ins->rex_done)
return;
ins->rex_done = true;
if (ins->rex & (REX_V | REX_EV))
return; /* Handled elsewhere */
if (ins->rex & REX_2) {
uint16_t rex2 = 0x00d5;
rex2 |= (ins->rex & (REX_BXR0|REX_W)) << 8;
rex2 |= (ins->rex & REX_BXR1);
rex2 |= (ins->vex_cm & 1) << 15;
out_rawword(data, rex2);
} else {
uint8_t rex = ins->rex & REX_MASK;
if (rex & REX_P)
out_rawbyte(data, rex);
ins->rex_done = true;
if (ins->vex_cm) {
out_rawbyte(data, 0x0f);
if (ins->vex_cm > 1) {
/* Map 2 = 0F 38, map 3 = 0F 3A */
out_rawbyte(data, 0x36 + (ins->vex_cm << 1));
}
}
}
}
@ -1853,25 +1938,30 @@ static int emit_prefix(struct out_data *data, const int bits, insn *ins)
c = 0x67;
break;
case P_O16:
if (bits != 16)
if (bits != 16 && !(ins->rex & (REX_W|REX_V|REX_EV))) {
c = 0x66;
}
break;
case P_O32:
if (bits == 16)
if (bits == 16 && !(ins->rex & (REX_W|REX_V|REX_EV))) {
c = 0x66;
}
break;
case P_O64:
/* REX.W */
/* Handled via REX.W */
break;
case P_OSP:
c = 0x66;
if (!(ins->rex & (REX_2|REX_V|REX_EV)))
c = 0x66;
break;
case P_REX:
case P_VEX:
case P_EVEX:
case P_VEX3:
case P_VEX2:
case P_REX2:
case P_NOBND:
case P_NF:
case P_none:
break;
default:
@ -2054,35 +2144,36 @@ static void gencode(struct out_data *data, insn *ins)
case4(0240):
case 0250:
codes += 3;
ins->evex_p[2] |= op_evexflags(&ins->oprs[0],
EVEX_P2Z | EVEX_P2AAA, 2);
ins->evex_p[2] ^= EVEX_P2VP; /* 1's complement */
bytes[0] = 0x62;
/* EVEX.X can be set by either REX or EVEX for different reasons */
bytes[1] = ((((ins->rex & 7) << 5) |
(ins->evex_p[0] & (EVEX_P0X | EVEX_P0RP))) ^ 0xf0) |
(ins->vex_cm & EVEX_P0MM);
bytes[2] = ((ins->rex & REX_W) << (7 - 3)) |
((~ins->vexreg & 15) << 3) |
(1 << 2) | (ins->vex_wlp & 3);
bytes[3] = ins->evex_p[2];
out_rawdata(data, bytes, 4);
ins->evex ^= op_evexflags(&ins->oprs[0], EVEX_P2Z|EVEX_P2AAA);
ins->evex ^= (ins->rex & REX_BXR0) << 12; /* R0 B0 X0 */
ins->evex ^= (ins->rex & REX_B1) >> (12-11);
ins->evex ^= (ins->rex & REX_X1) << (18-13);
ins->evex ^= (ins->rex & REX_R1) << (15-14);
ins->evex ^= (ins->rex & REX_W) << (23-3);
ins->evex ^= (ins->vexreg & 15) << 19;
ins->evex ^= (ins->vexreg & 16) << (27 - 4);
if (ins->prefixes[PPS_NF] == P_NF)
ins->evex ^= EVEX_P2NF;
out_rawdword(data, ins->evex);
break;
case4(0260):
case 0270:
codes += 2;
if (ins->vex_cm != 1 || (ins->rex & (REX_W|REX_X|REX_B)) ||
if (ins->vex_cm != 1 ||
(ins->rex & (REX_W|REX_X|REX_B)) ||
ins->prefixes[PPS_REX] == P_VEX3) {
bytes[0] = (ins->vex_cm >> 6) ? 0x8f : 0xc4;
bytes[1] = (ins->vex_cm & 31) | ((~ins->rex & 7) << 5);
bytes[1] = (ins->vex_cm & 31) |
((~ins->rex & REX_BXR0) << 5);
bytes[2] = ((ins->rex & REX_W) << (7-3)) |
((~ins->vexreg & 15)<< 3) | (ins->vex_wlp & 07);
((~ins->vexreg & 15) << 3);
out_rawdata(data, bytes, 3);
} else {
bytes[0] = 0xc5;
bytes[1] = ((~ins->rex & REX_R) << (7-2)) |
((~ins->vexreg & 15) << 3) | (ins->vex_wlp & 07);
((~ins->vexreg & 15) << 3) |
(ins->vex_wlp & 07);
out_rawdata(data, bytes, 2);
}
break;
@ -2098,14 +2189,7 @@ static void gencode(struct out_data *data, insn *ins)
int s;
if (absolute_op(opx)) {
if (ins->rex & REX_W)
s = 64;
else if (ins->prefixes[PPS_OSIZE] == P_O16)
s = 16;
else if (ins->prefixes[PPS_OSIZE] == P_O32)
s = 32;
else
s = bits;
s = ins->op_size;
um = (uint64_t)2 << (s-1);
uv = opx->offset;
@ -2159,10 +2243,7 @@ static void gencode(struct out_data *data, insn *ins)
case 0322:
case 0323:
break;
case 0324:
ins->rex |= REX_W;
break;
case 0325:
@ -2202,6 +2283,11 @@ static void gencode(struct out_data *data, insn *ins)
case 0341:
break;
case4(0344):
case4(0350):
case4(0354):
break;
case 0360:
break;
@ -2293,7 +2379,7 @@ static void gencode(struct out_data *data, insn *ins)
if (overflow_general(opy->offset, asize) ||
sext(opy->offset, ins->addr_size) !=
sext(opy->offset, ea_data.bytes << 3))
warn_overflow(ea_data.bytes);
warn_overflow(ea_data.bytes, "displacement ");
out_imm(data, opy, ea_data.bytes,
(asize > ea_data.bytes)
@ -2320,12 +2406,18 @@ static opflags_t regflag(const operand * o)
static int32_t regval(const operand * o)
{
/*
* Certain instruction patterns allow an immediate to be put
* into a register field
*/
if (o->type & IMMEDIATE)
return o->offset;
if (!is_register(o->basereg))
nasm_panic("invalid operand passed to regval()");
return nasm_regvals[o->basereg];
}
static int op_rexflags(const operand * o, int mask)
static uint32_t op_rexflags(const operand * o, uint32_t mask)
{
opflags_t flags;
int val;
@ -2339,12 +2431,16 @@ static int op_rexflags(const operand * o, int mask)
return rexflags(val, flags, mask);
}
static int rexflags(int val, opflags_t flags, int mask)
static uint32_t rexflags(int val, opflags_t flags, uint32_t mask)
{
int rex = 0;
uint32_t rex = 0;
if (val >= 0 && (val & 8))
rex |= REX_B|REX_X|REX_R;
if (val >= 0) {
if (val & 8)
rex |= REX_B|REX_X|REX_R;
if (val & 16)
rex |= REX_B1|REX_X1|REX_R1;
}
if (flags & BITS64)
rex |= REX_W;
if (!(REG_HIGH & ~flags)) /* AH, CH, DH, BH */
@ -2355,35 +2451,21 @@ static int rexflags(int val, opflags_t flags, int mask)
return rex & mask;
}
static int evexflags(int val, decoflags_t deco,
int mask, uint8_t byte)
static uint32_t evexflags(decoflags_t deco, uint32_t mask)
{
int evex = 0;
uint32_t evex = 0;
if (deco & Z)
evex |= EVEX_P2Z;
if (deco & OPMASK_MASK)
evex |= (deco << 24) & EVEX_P2AAA;
switch (byte) {
case 0:
if (val >= 0 && (val & 16))
evex |= (EVEX_P0RP | EVEX_P0X);
break;
case 2:
if (val >= 0 && (val & 16))
evex |= EVEX_P2VP;
if (deco & Z)
evex |= EVEX_P2Z;
if (deco & OPMASK_MASK)
evex |= deco & EVEX_P2AAA;
break;
}
return evex & mask;
}
static int op_evexflags(const operand * o, int mask, uint8_t byte)
static uint32_t op_evexflags(const operand * o, uint32_t mask)
{
int val;
val = nasm_regvals[o->basereg];
return evexflags(val, o->decoflags, mask, byte);
return evexflags(o->decoflags, mask);
}
static enum match_result find_match(const struct itemplate **tempp,
@ -2501,7 +2583,7 @@ static enum match_result matches(const struct itemplate *itemp,
{
opflags_t size[MAX_OPERANDS], asize;
bool opsizemissing = false;
int i, oprs;
int i, oprs, matchop;
/*
* Check the opcode
@ -2540,6 +2622,10 @@ static enum match_result matches(const struct itemplate *itemp,
bits != 64)
return MERR_ENCMISMATCH;
break;
case P_REX2:
if (!itemp_has(itemp, IF_REX2) || bits != 64)
return MERR_ENCMISMATCH;
break;
default:
if (itemp_has(itemp, IF_EVEX)) {
if (!iflag_test(&cpu, IF_EVEX))
@ -2727,18 +2813,25 @@ static enum match_result matches(const struct itemplate *itemp,
/*
* Check operand sizes
*/
if (itemp_has(itemp, IF_SM) || itemp_has(itemp, IF_SM2)) {
oprs = (itemp_has(itemp, IF_SM2) ? 2 : itemp->operands);
for (i = 0; i < oprs; i++) {
asize = itemp->opd[i] & SIZE_MASK;
if (asize) {
for (i = 0; i < oprs; i++)
size[i] = asize;
break;
}
}
} else {
matchop = oprs = 0;
if (itemp_has(itemp, IF_SM)) {
oprs = itemp->operands;
matchop = 0;
} else if (itemp_has(itemp, IF_SM2)) {
oprs = 2;
matchop = 0;
} else if (itemp_has(itemp, IF_SM23)) {
oprs = 3;
matchop = 1;
}
for (i = matchop; i < oprs; i++) {
asize = itemp->opd[i] & SIZE_MASK;
if (asize) {
for (i = matchop; i < oprs; i++)
size[i] = asize;
break;
}
}
for (i = 0; i < itemp->operands; i++) {
@ -2759,6 +2852,13 @@ static enum match_result matches(const struct itemplate *itemp,
if (itemp_has(itemp, (bits == 64 ? IF_NOLONG : IF_LONG)))
return MERR_BADMODE;
/*
* {nf} prefix used? Must be permitted.
*/
if (has_prefix(instruction, PPS_NF, P_NF) &&
!itemp_has(itemp, IF_NF))
return MERR_ENCMISMATCH;
/*
* If we have a HLE prefix, look for the NOHLE flag
*/
@ -2817,9 +2917,7 @@ static int process_ea(operand *input, ea *output, int bits,
output->disp8 = 0;
/* REX flags for the rfield operand */
output->rex |= rexflags(rfield, rflags, REX_R | REX_P | REX_W | REX_H);
/* EVEX.R' flag for the REG operand */
ins->evex_p[0] |= evexflags(rfield, 0, EVEX_P0RP, 0);
output->rex |= rexflags(rfield, rflags, REX_rR);
if (is_class(REGISTER, input->type)) {
/*
@ -2837,8 +2935,7 @@ static int process_ea(operand *input, ea *output, int bits,
goto err;
}
output->rex |= op_rexflags(input, REX_B | REX_P | REX_W | REX_H);
ins->evex_p[0] |= op_evexflags(input, EVEX_P0X, 0);
output->rex |= op_rexflags(input, REX_rB);
output->sib_present = false; /* no SIB necessary */
output->bytes = 0; /* no offset necessary either */
output->modrm = GEN_MODRM(3, rfield, nasm_regvals[input->basereg]);
@ -2988,9 +3085,8 @@ static int process_ea(operand *input, ea *output, int bits,
: ((ix & YMMREG & ~REG_EA)
? EA_YMMVSIB : EA_XMMVSIB));
output->rex |= rexflags(it, ix, REX_X);
output->rex |= rexflags(bt, bx, REX_B);
ins->evex_p[2] |= evexflags(it, 0, EVEX_P2VP, 2);
output->rex |= rexflags(it, ix, REX_rX);
output->rex |= rexflags(bt, bx, REX_rB);
index = it & 7; /* it is known to be != -1 */
@ -3117,8 +3213,8 @@ static int process_ea(operand *input, ea *output, int bits,
(s != 1 && s != 2 && s != 4 && s != 8 && it != -1))
goto err; /* wrong, for various reasons */
output->rex |= rexflags(it, ix, REX_X);
output->rex |= rexflags(bt, bx, REX_B);
output->rex |= rexflags(it, ix, REX_rX);
output->rex |= rexflags(bt, bx, REX_rB);
if (it == -1 && (bt & 7) != REG_NUM_ESP && !(eaflags & EAF_SIB)) {
/* no SIB needed */
@ -3377,7 +3473,7 @@ static void add_asp(insn *ins, int addrbits)
ins->addr_size = addrbits;
} else if (valid & ((addrbits == 32) ? 16 : 32)) {
/* Add an address size prefix */
ins->prefixes[PPS_ASIZE] = (addrbits == 32) ? P_A16 : P_A32;;
ins->prefixes[PPS_ASIZE] = (addrbits == 32) ? P_A16 : P_A32;
ins->addr_size = (addrbits == 32) ? 16 : 32;
} else {
/* Impossible... */

View file

@ -1,6 +1,6 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 1996-2022 The NASM Authors - All Rights Reserved
* Copyright 1996-2024 The NASM Authors - All Rights Reserved
* See the file AUTHORS included with the NASM distribution for
* the specific copyright holders.
*
@ -111,6 +111,7 @@ void set_cpu(const char *value)
{ "any", IF_ANY },
{ "all", IF_ANY },
{ "latevex", IF_LATEVEX },
{ "apx", IF_APX },
{ "evex", IF_EVEX },
{ "vex", IF_VEX },
{ NULL, 0 }

View file

@ -655,7 +655,9 @@ static opflags_t imm_flags(int64_t n, opflags_t flags)
{
if (n == 1)
flags |= UNITY;
if ((uint64_t)n <= 15)
/* Allow FOURBITS matching for negative values, so things like ~0 work */
if (n >= -16 && n <= 15)
flags |= FOURBITS;
if (optimizing.level < 0 || (flags & STRICT))
@ -1048,26 +1050,36 @@ restart_parse:
goto mref_more;
}
if (i == ':' && (mref || !far_jmp_ok)) {
/* segment override? */
mref = true;
if (i == ':') {
bool ok_reg = is_register(value->type) &&
value->value == 1 && !value[1].type;
/*
* Process the segment override.
*/
if (!IS_SREG(value->type) || value->value != 1 ||
value[1].type != 0) {
nasm_nonfatal("invalid segment override");
} else if (result->prefixes[PPS_SEG]) {
nasm_nonfatal("instruction has conflicting segment overrides");
} else {
result->prefixes[PPS_SEG] = value->type;
if (IS_FSGS(value->type))
op->eaflags |= EAF_FSGS;
if (!mref && ok_reg && !IS_SREG(value->type)) {
/*
* Register pair syntax; this terminates the expression
* as if it had ended in a comma, but sets the COLON flag
* on the operand further down.
*/
} else if (mref || !far_jmp_ok) {
/* segment override? */
mref = true;
/*
* Process the segment override.
*/
if (!ok_reg || !IS_SREG(value->type)) {
nasm_nonfatal("invalid segment override");
} else if (result->prefixes[PPS_SEG]) {
nasm_nonfatal("instruction has conflicting segment overrides");
} else {
result->prefixes[PPS_SEG] = value->type;
if (IS_FSGS(value->type))
op->eaflags |= EAF_FSGS;
}
i = stdscan(NULL, &tokval); /* then skip the colon */
goto mref_more;
}
i = stdscan(NULL, &tokval); /* then skip the colon */
goto mref_more;
}
mib = false;
@ -1264,8 +1276,11 @@ restart_parse:
regset_size = 0;
}
/* clear overrides, except TO which applies to FPU regs */
if (op->type & ~TO) {
/*
* Clear overrides, except TO which applies to FPU regs
* and colon which is used in register pair syntax
*/
if (op->type & ~(TO | COLON)) {
/*
* we want to produce a warning iff the specified size
* is different from the register size
@ -1292,7 +1307,7 @@ restart_parse:
goto fail;
}
op->type &= TO;
op->type &= TO | COLON;
op->type |= REGISTER;
op->type |= nasm_reg_flags[value->type];
op->type |= (regset_size >> 1) << REGSET_SHIFT;

View file

@ -77,11 +77,15 @@ wait
% TOKEN_PREFIX, PPS_REX, TFLAG_BRC, P_*
rex
rex2
evex
vex
vex3
vex2
% TOKEN_PREFIX, PPS_NF, TFLAG_BRC, P_*
nf
% TOKEN_SIZE, SIZE_*, 0, S_*
byte
word

View file

@ -535,12 +535,6 @@ enum {
* -----------------------------------------------------------
*/
/* Verify value to be a valid register */
static inline bool is_register(int reg)
{
return reg >= EXPR_REG_START && reg < REG_ENUM_LIMIT;
}
/*
* REX flags
*/
@ -555,22 +549,36 @@ static inline bool is_register(int reg)
#define REX_V 0x0100 /* Instruction uses VEX/XOP instead of REX */
#define REX_NH 0x0200 /* Instruction which doesn't use high regs */
#define REX_EV 0x0400 /* Instruction uses EVEX instead of REX */
#define REX_2 0x0800 /* Instruction requires REX2 */
#define REX_B1 0x1000 /* REX2/EVEX B second bit */
#define REX_X1 0x2000 /* REX2/EVEX X second bit */
#define REX_R1 0x4000 /* REX2/EVEX R second bit */
#define REX_BXR0 0x0007
#define REX_BXR1 0x7000
#define REX_BXR 0x7007
#define REX_rB (REX_B | REX_B1 | REX_H | REX_P)
#define REX_rX (REX_X | REX_X1 | REX_H | REX_P)
#define REX_rR (REX_R | REX_R1 | REX_H | REX_P)
/*
* EVEX bit field
*/
#define EVEX_P0MM 0x0f /* EVEX P[3:0] : Opcode map */
#define EVEX_P0RP 0x10 /* EVEX P[4] : High-16 reg */
#define EVEX_P0X 0x40 /* EVEX P[6] : High-16 rm */
#define EVEX_P1PP 0x03 /* EVEX P[9:8] : Legacy prefix */
#define EVEX_P1VVVV 0x78 /* EVEX P[14:11] : NDS register */
#define EVEX_P1W 0x80 /* EVEX P[15] : Osize extension */
#define EVEX_P2AAA 0x07 /* EVEX P[18:16] : Embedded opmask */
#define EVEX_P2VP 0x08 /* EVEX P[19] : High-16 NDS reg */
#define EVEX_P2B 0x10 /* EVEX P[20] : Broadcast / RC / SAE */
#define EVEX_P2LL 0x60 /* EVEX P[22:21] : Vector length */
#define EVEX_P0MM 0x0f00 /* EVEX P[3:0] : Opcode map */
#define EVEX_P0RP 0x1000 /* EVEX P[4] : High-16 reg */
#define EVEX_P0X 0x4000 /* EVEX P[6] : High-16 rm */
#define EVEX_P1PP 0x030000 /* EVEX P[9:8] : Legacy prefix */
#define EVEX_P1VVVV 0x780000 /* EVEX P[14:11] : NDS register */
#define EVEX_P1W 0x800000 /* EVEX P[15] : Osize extension */
#define EVEX_P2AAA 0x07000000 /* EVEX P[18:16] : Embedded opmask */
#define EVEX_P2NF 0x04000000 /* EVEX P[18]: No flags bit */
#define EVEX_P2VP 0x08000000 /* EVEX P[19] : High-16 NDS reg */
#define EVEX_P2B 0x10000000 /* EVEX P[20] : Broadcast / RC / SAE */
#define EVEX_P2ND EVEX_P2B /* EVEX P[20] : New destination */
#define EVEX_P2LL 0x60000000 /* EVEX P[22:21] : Vector length */
#define EVEX_P2RC EVEX_P2LL /* EVEX P[22:21] : Rounding control */
#define EVEX_P2Z 0x80 /* EVEX P[23] : Zeroing/Merging */
#define EVEX_P2Z 0x80000000 /* EVEX P[23] : Zeroing/Merging */
/*
* REX_V "classes" (prefixes which behave like VEX)
@ -610,10 +618,12 @@ enum prefixes { /* instruction prefixes */
P_BND,
P_NOBND,
P_REX,
P_REX2,
P_EVEX,
P_VEX,
P_VEX3,
P_VEX2,
P_NF,
PREFIX_ENUM_LIMIT
};
@ -718,6 +728,7 @@ enum prefix_pos {
PPS_OSIZE, /* Operand size prefix */
PPS_ASIZE, /* Address size prefix */
PPS_REX, /* REX/VEX type */
PPS_NF, /* No flags */
MAXPREFIX /* Total number of prefix slots */
};
@ -761,6 +772,7 @@ typedef struct insn { /* an instruction itself */
enum opcode opcode; /* the opcode - not just the string */
int operands; /* how many operands? 0-7 (more if db et al) */
int addr_size; /* address size */
int op_size; /* operand size */
operand oprs[MAX_OPERANDS]; /* the operands, defined as above */
extop *eops; /* extended operands */
int eops_float; /* true if DD and floating */
@ -768,11 +780,10 @@ typedef struct insn { /* an instruction itself */
bool forw_ref; /* is there a forward reference? */
bool rex_done; /* REX prefix emitted? */
int rex; /* Special REX Prefix */
int vexreg; /* Register encoded in VEX prefix */
int vexreg; /* Register encoded in VEX.V */
int vex_cm; /* Class and M field for VEX prefix */
int vex_wlp; /* W, P and L information for VEX prefix */
uint8_t evex_p[3]; /* EVEX.P0: [RXB,R',00,mm], P1: [W,vvvv,1,pp] */
/* EVEX.P2: [z,L'L,b,V',aaa] */
uint32_t evex; /* EVEX prefix under construction */
enum ttypes evex_tuple; /* Tuple type for compressed Disp8*N */
int evex_rm; /* static rounding mode for AVX512 (EVEX) */
int8_t evex_brerop; /* BR/ER/SAE operand position */

View file

@ -456,7 +456,7 @@ static inline int64_t const_func sext(int64_t value, unsigned int bits)
/* sext(foo,0) == sext(foo,1) */
bits = bits ? 64-bits : 63;
return value >> bits << bits;
return value << bits >> bits;
}
/* Zero-extend a value to an arbitrary number of bits */
@ -482,20 +482,20 @@ static inline uint64_t const_func zext(uint64_t value, unsigned int bits)
return 0;
bits = 64-bits;
return value >> bits << bits;
return value << bits >> bits;
}
static inline bool const_func overflow_signed(int64_t value, int bytes)
static inline bool const_func overflow_signed(int64_t value, unsigned int bytes)
{
return sext(value, bytes << 3) != value;
}
static inline bool const_func overflow_unsigned(uint64_t value, int bytes)
static inline bool const_func overflow_unsigned(uint64_t value, unsigned int bytes)
{
return zext(value, bytes << 3) != value;
}
static inline bool const_func overflow_general(int64_t value, int bytes)
static inline bool const_func overflow_general(int64_t value, unsigned int bytes)
{
return overflow_unsigned(value, bytes) && overflow_signed(value, bytes);
}

View file

@ -178,16 +178,23 @@
#define REG_CLASS_BND GEN_REG_CLASS(9)
#define REG_CLASS_RM_TMM GEN_REG_CLASS(10)
/* Verify value to be a valid register */
static inline bool is_register(opflags_t reg)
{
return reg >= EXPR_REG_START && reg < REG_ENUM_LIMIT;
}
static inline bool is_class(opflags_t class, opflags_t op)
{
return !(class & ~op);
return !(class & ~op);
}
static inline bool is_reg_class(opflags_t class, opflags_t reg)
{
if (reg >= EXPR_REG_START && reg <= EXPR_REG_END)
return is_class(class, nasm_reg_flags[reg]);
return false;
if (!is_register(reg))
return false;
return is_class(class, nasm_reg_flags[reg]);
}
#define IS_SREG(reg) is_reg_class(REG_SREG, (reg))

33
test/apx.asm Normal file
View file

@ -0,0 +1,33 @@
bits 64
ccmpnz {dfv=} rax,rbx
ccmpnz {dfv=cf} rax,rbx
ccmpnz {dfv=zf} rax,rbx
ccmpnz {dfv=sf} rax,rbx
ccmpnz {dfv=of} rax,rbx
ccmpnz {dfv=of,cf} rax,rbx
ccmpnz {dfv=of,cf} [rax],rbx
ccmpnz {dfv=of,cf} dword [rax],3
ccmpnz {dfv=of,cf} dword [r31],3
ccmpnz 15, dword [r31], 3
ccmpnz {dfv=of,cf} dword [r31], byte 3
ccmpnz {dfv=of,cf} dword [r31], 3
ccmpnz {dfv=of,cf} dword [r31], dword 3
ccmpnz {dfv=of,cf} dword [r31], strict dword 3
ccmpnz {dfv=of,cf} dword [r31], 0xaabbccdd
ccmpnz {dfv=of,cf} dword [r31], dword 0xaabbccdd
ccmpnz {dfv=of,cf} qword [r31], 0xaabbccdd
push rax
pushp rax
push rax, rbx
push rax:rbx
pop rax:rbx
pop rbx, rax
add al,[rdx],cl
add {nf} al,[rdx],cl
add [rdx],cl
add {evex} [rdx],cl
add {nf} [rdx],cl

View file

@ -47,8 +47,8 @@ Codes Mnemonic Explanation
an arbitrary value in bits 3..0 (assembled as zero.)
\2ab /b a ModRM, calculated on EA in operand a, with the reg
field equal to digit b.
\240..\247 this instruction uses EVEX rather than REX or VEX/XOP, with the
V register number taken from operand 0..7 (which may
\240..\243 this instruction uses EVEX rather than REX or VEX/XOP, with the
V register number taken from operand "b" (0..3) (which may
be an immediate, as is used for DFV.)
\250 this instruction uses EVEX rather than REX or VEX/XOP, with the
V register number set to 0 (subject to the XOR as defined
@ -74,10 +74,10 @@ EVEX prefixes are followed by the sequence:
(compressed displacement encoding)
\254..\257 id,s a signed 32-bit operand to be extended to 64 bits.
\260..\267 this instruction uses VEX/XOP rather than REX, with the
V field taken from operand 0..7.
\260..\263 this instruction uses VEX/XOP rather than REX, with the
V register taken from operand "b" 0..3.
\270 this instruction uses VEX/XOP rather than REX, with the
V field set to 1111b.
V register set to 0.
VEX/XOP prefixes are followed by the sequence:
\tmm\wlp tmm format: tt 0mm mmm
[vex] tt = 0
@ -85,15 +85,15 @@ VEX/XOP prefixes are followed by the sequence:
mmmmm = M field
wlp format: 00 0w0 lpp
wlp format: w0 00l lpp
[l0] ll = 0 for L = 0 (.128, .lz)
[l1] ll = 1 for L = 1 (.256)
[lig] ll = 0 for L don't care (always assembled as 0) with IF_LIG
[w0] ww = 0 for W = 0
[w1 ] ww = 1 for W = 1
[wig] ww = 0 for W don't care (always assembled as 0) with IF_WIG
[ww] ww = 0 for W used as REX.W with IF_WW
[w0] w = 0 for W = 0
[w1 ] w = 1 for W = 1
[wig] w = 0 for W don't care (always assembled as 0) with IF_WIG
[ww] w = 0 for W used as REX.W with IF_WW
t = 0 for VEX (C4/C5), t = 1 for XOP (8F).
@ -130,10 +130,11 @@ t = 0 for VEX (C4/C5), t = 1 for XOP (8F).
\340 resb reserve <operand 0> bytes of uninitialized storage.
Operand 0 had better be a segmentless constant.
\341 wait this instruction needs a WAIT "prefix"
\344..\347 rexx[.0f|m1][.w] REX or REX2 prefix, bits [1:0] set the M or W bits (promotable)
\344..\347 rexx[.0f|m1][.w] REX or REX2 prefix, bits [1:0] set the W or M bits (promotable)
rexx.m1 -> rex 0f if REX2 is not generated.
The W bit will also be set if o64 is specified.
35x rex2[.0f|m1][.x1][.w] obligatory REX2 prefix, bits [2:0] set the X1, M, and W bits
The REX prefix can be omitted using normal REX omission rules.
\35x rex2[.0f|m1][.x1][.w] obligatory REX2 prefix, bits [2:0] set the X1, W, and M bits
The W bit will also be set if o64 is specified.
\360 np no SSE prefix (== \364\331)
\361 66 SSE prefix (== \366\331)

View file

@ -47,10 +47,10 @@ uint8_t get_disp8N(insn *ins)
static const uint8_t hv_n[2][VLMAX] = {{8, 16, 32}, {4, 4, 4}};
static const uint8_t dup_n[VLMAX] = {8, 32, 64};
bool evex_b = (ins->evex_p[2] & EVEX_P2B) >> 4;
bool evex_b = !!(ins->evex & EVEX_P2B);
enum ttypes tuple = ins->evex_tuple;
enum vectlens vectlen = (ins->evex_p[2] & EVEX_P2LL) >> 5;
bool evex_w = (ins->evex_p[1] & EVEX_P1W) >> 7;
enum vectlens vectlen = (ins->evex & EVEX_P2LL) >> 29;
bool evex_w = !!(ins->evex & EVEX_P1W);
uint8_t n = 0;
switch(tuple) {

View file

@ -5988,7 +5988,7 @@ VPSHRDD zmmreg|mask|z,zmmreg*,zmmrm512|b32,imm8 [rvmi:fv: evex.nds.512.66.0f3a
VPSHRDQ xmmreg|mask|z,xmmreg*,xmmrm128|b64,imm8 [rvmi:fv: evex.nds.128.66.0f3a.w1 73 /r ib] AVX512VBMI2,AVX512VL,FUTURE
VPSHRDQ ymmreg|mask|z,ymmreg*,ymmrm256|b64,imm8 [rvmi:fv: evex.nds.256.66.0f3a.w1 73 /r ib] AVX512VBMI2,AVX512VL,FUTURE
VPSHRDQ zmmreg|mask|z,zmmreg*,zmmrm512|b64,imm8 [rvmi:fv: evex.nds.512.66.0f3a.w1 73 /r ib] AVX512VBMI2,FUTURE
VPSHRDVW xmmreg|mask|z,xmmreg*,xmmrm128 [rvmi:fvm: evex.dds.128.66.0f38.w1 72 /r] AVX512VBMI2,AVX512VL,FUTURE
VPSHRDVW xmmreg|mask|z,xmmreg*,xmmrm128 [rvmi:fvm: evex.dds.128.66.0f38.w1 72 /r] AVX512VBMI2,AVX512VL,FUTURE
VPSHRDVW ymmreg|mask|z,ymmreg*,ymmrm256 [rvmi:fvm: evex.dds.256.66.0f38.w1 72 /r] AVX512VBMI2,AVX512VL,FUTURE
VPSHRDVW zmmreg|mask|z,zmmreg*,zmmrm512 [rvmi:fvm: evex.dds.512.66.0f38.w1 72 /r] AVX512VBMI2,FUTURE
VPSHRDVD xmmreg|mask|z,xmmreg*,xmmrm128|b32 [rvmi:fv: evex.dds.128.66.0f38.w0 73 /r] AVX512VBMI2,AVX512VL,FUTURE
@ -6394,43 +6394,48 @@ HRESET imm,reg_eax [i-: f3 0f 3a f0 c0 ib ] HRESET,FUTURE,PRIV,SB
HRESET imm [i: f3 0f 3a f0 c0 ib ] HRESET,FUTURE,PRIV,SB,ND
;# APX test
CCMPscc spec4,rm8,reg8 [vmr: evex.scc.dfv.l0.np.m4.wig 38 /r ] SM23,APX
CCMPscc spec4,rm16,reg16 [vmr: evex.scc.dfv.l0.66.m4.w0 39 /r ] SM23,APX
CCMPscc spec4,rm32,reg32 [vmr: evex.scc.dfv.l0.np.m4.w0 39 /r ] SM23,APX
CCMPscc spec4,rm64,reg64 [vmr: evex.scc.dfv.l0.np.m4.w1 39 /r ] SM23,APX
CCMPscc spec4,rm8,reg8 [vmr: evex.scc.dfv.l0.np.m4.wig 38 /r ] APX,SM23
CCMPscc spec4,rm16,reg16 [vmr: o16 evex.scc.dfv.l0.66.m4.w0 39 /r ] APX,SM23
CCMPscc spec4,rm32,reg32 [vmr: o32 evex.scc.dfv.l0.np.m4.w0 39 /r ] APX,SM23
CCMPscc spec4,rm64,reg64 [vmr: o64 evex.scc.dfv.l0.np.m4.w1 39 /r ] APX,SM23
CCMPscc spec4,reg8,rm8 [vrm: evex.scc.dfv.l0.np.m4.wig 3a /r ] SM23,APX
CCMPscc spec4,reg16,rm16 [vrm: evex.scc.dfv.l0.66.m4.w0 3b /r ] SM23,APX
CCMPscc spec4,reg32,rm32 [vrm: evex.scc.dfv.l0.np.m4.w0 3b /r ] SM23,APX
CCMPscc spec4,reg64,rm64 [vrm: evex.scc.dfv.l0.np.m4.w1 3b /r ] SM23,APX
CCMPscc spec4,reg8,rm8 [vrm: evex.scc.dfv.l0.np.m4.wig 3a /r ] APX,SM23
CCMPscc spec4,reg16,rm16 [vrm: o16 evex.scc.dfv.l0.66.m4.w0 3b /r ] APX,SM23
CCMPscc spec4,reg32,rm32 [vrm: o32 evex.scc.dfv.l0.np.m4.w0 3b /r ] APX,SM23
CCMPscc spec4,reg64,rm64 [vrm: o64 evex.scc.dfv.l0.np.m4.w1 3b /r ] APX,SM23
CCMPscc spec4,rm8,imm [vmi: evex.scc.dfv.l0.np.m4.wig 80 /7 ] SM23,APX
CCMPscc spec4,rm16,sbyteword [vmi: evex.scc.dfv.l0.66.m4.w0 83 /7 ] SM23,APX
CCMPscc spec4,rm32,sbytedword [vmi: evex.scc.dfv.l0.np.m4.w0 83 /7 ] SM23,APX
CCMPscc spec4,rm64,sbytedword [vmi: evex.scc.dfv.l0.np.m4.w1 83 /7 ] SM23,APX
CCMPscc spec4,rm16,imm [vmi: evex.scc.dfv.l0.66.m4.w0 81 /7 ] SM23,APX
CCMPscc spec4,rm32,imm [vmi: evex.scc.dfv.l0.np.m4.w0 81 /7 ] SM23,APX
CCMPscc spec4,rm64,imm [vmi: evex.scc.dfv.l0.np.m4.w1 81 /7 ] SM23,APX
CCMPscc spec4,rm8,imm [vmi: evex.scc.dfv.l0.np.m4.wig 80 /7 ib ] APX,SM23
CCMPscc spec4,rm16,imm8 [vmi: o16 evex.scc.dfv.l0.66.m4.w0 83 /7 ib,s ] APX
CCMPscc spec4,rm16,sbyteword [vmi: o16 evex.scc.dfv.l0.66.m4.w0 83 /7 ib,s ] APX,SM23,ND
CCMPscc spec4,rm16,imm [vmi: o16 evex.scc.dfv.l0.66.m4.w0 81 /7 iw ] APX,SM23
CCMPscc spec4,rm32,imm8 [vmi: o32 evex.scc.dfv.l0.np.m4.w0 83 /7 ib,s ] APX
CCMPscc spec4,rm32,sbytedword [vmi: o32 evex.scc.dfv.l0.np.m4.w0 83 /7 ib,s ] APX,SM23,ND
CCMPscc spec4,rm32,imm [vmi: o32 evex.scc.dfv.l0.np.m4.w0 81 /7 id ] APX,SM23
CCMPscc spec4,rm64,imm8 [vmi: o64 evex.scc.dfv.l0.np.m4.w1 83 /7 ib,s ] APX
CCMPscc spec4,rm64,sbytedword [vmi: o64 evex.scc.dfv.l0.np.m4.w1 83 /7 ib,s ] APX,SM23,ND
CCMPscc spec4,rm64,imm [vmi: o64 evex.scc.dfv.l0.np.m4.w1 81 /7 id,s ] APX,SM23
PUSH2 reg64,reg64 [vm: evex.nd.l0.np.m4.w0 ff /6 ] APX
PUSH reg64,reg64 [vm: evex.nd.l0.np.m4.w0 ff /6 ] APX,ND
PUSH2 reg64:reg64 [vm: evex.nd.l0.np.m4.w0 ff /6 ] APX,ND
PUSH reg64:reg64 [vm: evex.nd.l0.np.m4.w0 ff /6 ] APX,ND
PUSH2P reg64,reg64 [vm: evex.nd.l0.np.m4.w1 ff /6 ] APX
PUSHP reg64,reg64 [vm: evex.nd.l0.np.m4.w1 ff /6 ] APX,ND
PUSH2P reg64:reg64 [vm: evex.nd.l0.np.m4.w1 ff /6 ] APX,ND
PUSHP reg64:reg64 [vm: evex.nd.l0.np.m4.w1 ff /6 ] APX,ND
POP2 reg64,reg64 [vm: evex.nd.l0.np.m4.w0 8f /0 ] APX
POP reg64,reg64 [vm: evex.nd.l0.np.m4.w0 8f /0 ] APX,ND
POP2 reg64:reg64 [mv: evex.nd.l0.np.m4.w0 8f /0 ] APX,ND
POP reg64:reg64 [mv: evex.nd.l0.np.m4.w0 8f /0 ] APX,ND
POP2P reg64,reg64 [vm: evex.nd.l0.np.m4.w1 8f /0 ] APX
POPP reg64,reg64 [vm: evex.nd.l0.np.m4.w1 8f /0 ] APX,ND
POP2P reg64:reg64 [mv: evex.nd.l0.np.m4.w1 8f /0 ] APX,ND
POPP reg64:reg64 [mv: evex.nd.l0.np.m4.w1 8f /0 ] APX,ND
PUSH2 reg64,reg64 [vm: o64nw evex.nd1.l0.np.m4.w0 ff /6 ] APX
PUSH reg64,reg64 [vm: o64nw evex.nd1.l0.np.m4.w0 ff /6 ] APX,ND
PUSH2 reg64:reg64 [vm: o64nw evex.nd1.l0.np.m4.w0 ff /6 ] APX,ND
PUSH reg64:reg64 [vm: o64nw evex.nd1.l0.np.m4.w0 ff /6 ] APX,ND
PUSH2P reg64,reg64 [vm: o64nw evex.nd1.l0.np.m4.w1 ff /6 ] APX
PUSHP reg64,reg64 [vm: o64nw evex.nd1.l0.np.m4.w1 ff /6 ] APX,ND
PUSH2P reg64:reg64 [vm: o64nw evex.nd1.l0.np.m4.w1 ff /6 ] APX,ND
PUSHP reg64:reg64 [vm: o64nw evex.nd1.l0.np.m4.w1 ff /6 ] APX,ND
POP2 reg64,reg64 [vm: o64nw evex.nd1.l0.np.m4.w0 8f /0 ] APX
POP reg64,reg64 [vm: o64nw evex.nd1.l0.np.m4.w0 8f /0 ] APX,ND
POP2 reg64:reg64 [mv: o64nw evex.nd1.l0.np.m4.w0 8f /0 ] APX,ND
POP reg64:reg64 [mv: o64nw evex.nd1.l0.np.m4.w0 8f /0 ] APX,ND
POP2P reg64,reg64 [vm: o64nw evex.nd1.l0.np.m4.w1 8f /0 ] APX
POPP reg64,reg64 [vm: o64nw evex.nd1.l0.np.m4.w1 8f /0 ] APX,ND
POP2P reg64:reg64 [mv: o64nw evex.nd1.l0.np.m4.w1 8f /0 ] APX,ND
POPP reg64:reg64 [mv: o64nw evex.nd1.l0.np.m4.w1 8f /0 ] APX,ND
PUSHP reg64 [r: rex2.m0.w1 50+r ] APX
POPP reg64 [r: rex2.m0.w1 58+r ] APX
PUSHP reg64 [r: o64nw rex2.m0.w1 50+r ] APX
POPP reg64 [r: o64nw rex2.m0.w1 58+r ] APX
ADD reg8?,rm8,reg8 [vmr: evex.ndx.l0.np.m4.wig 00 /r ] APX,NF
;# Systematic names for the hinting nop instructions
; These should be last in the file

View file

@ -77,19 +77,26 @@ sub xpush($@) {
}
# Generate relaxed form patterns if applicable
# * is used for an optional source operand, duplicating the previous one
# in the encoding if it is missing.
# ? is used for an optional destination operand which is not encoded if
# missing; if combined evex.ndx set the nd bit if present.
sub relaxed_forms(@) {
my @field_list = @_;
foreach my $fields (@_) {
next unless ($fields->[1] =~ /\*/);
next unless ($fields->[1] =~ /[\*\?]/);
# This instruction has relaxed form(s)
if ($fields->[2] !~ /^\[/) {
if ($fields->[2] !~ /^(\[\s*)(\S+)(\s*:.*\])$/) {
warn "$fname:$line: has an * operand but uses raw bytecodes\n";
next;
}
my @f2o = ($1, $2, $3);
my $opmask = 0;
my $ndmask = 0;
my $ndflag = 0;
my @ops = split(/,/, $fields->[1]);
for (my $oi = 0; $oi < scalar @ops; $oi++) {
if ($ops[$oi] =~ /\*$/) {
@ -98,22 +105,46 @@ sub relaxed_forms(@) {
next;
}
$opmask |= 1 << $oi;
} elsif ($ops[$oi] =~ /\?$/) {
$opmask |= 1 << $oi;
$ndmask |= 1 << $oi;
$ndflag = 1;
}
}
# If .ndx is present, then change it to .nd0 or .nd1
# Set to .nd0 if no ndmask fields are present, otherwise 1
$fields->[2] =~ s/(\.nd)x\b/$1$ndflag/;
for (my $oi = 1; $oi < (1 << scalar @ops); $oi++) {
if (($oi & ~$opmask) == 0) {
my @xops = ();
my $omask = ~$oi;
for ($oj = 0; $oj < scalar(@ops); $oj++) {
if ($omask & 1) {
push(@xops, $ops[$oj]);
my $ndflag = 0;
my $relax = 0;
my $rbit = 1;
my $ondflag = $ndflag;
my $f2 = $f2o[0];
for (my $oj = 0; $oj < scalar(@ops); $oj++) {
my $ob = 1 << $oj;
if ($ob & $ndmask & $oi) {
# Make it disappear completely
$ondflag = 0;
} else {
if ($ob & ~$oi) {
push(@xops, $ops[$oj]);
} else {
$relax |= $rbit;
}
$rbit <<= 1;
$f2 .= substr($f2o[1], $oj, 1);
}
$omask >>= 1;
}
$f2 .= $f2o[2];
my @ff = @$fields;
$ff[1] = join(',', @xops);
$ff[4] = $oi;
$f2 =~ s/(\.nd)x\b/$1$ondflag/;
$ff[2] = $f2;
$ff[4] = $relax;
push(@field_list, [@ff]);
}
}
@ -528,7 +559,7 @@ sub format_insn($$$$$) {
return (undef, undef) if $operands eq "ignore";
# format the operands
$operands =~ s/\*//g;
$operands =~ s/[\*\?]//g;
$operands =~ s/:/|colon,/g;
@ops = ();
@opsize = ();
@ -615,6 +646,11 @@ sub format_insn($$$$$) {
}
}
# Flags that imply long mode only
if ($flags{'APX'}) {
$flags->{'LONG'}++;
}
# Look for SM flags clearly inconsistent with operand bitsizes
if ($flags{'SM'} || $flags{'SM2'} || $flags{'SM23'}) {
my $ssize = 0;
@ -700,10 +736,10 @@ sub show_bytecodes($) {
if ($c <= 4) {
$literals = $c;
$hexlit = 1;
} elsif ($c >= 0240 && $c <= 0250) {
} elsif (($c & 3) == 0240 || $c == 0250) {
$literals = 3;
$hexlit = 1;
} elsif ($c >= 0260 && $c <= 0270) {
} elsif (($c & 3) == 0260 || $c == 0270) {
$literals = 2;
$hexlit = 0;
} elsif ($c == 0171) {
@ -835,17 +871,24 @@ sub startseq($$) {
return addprefix($prefix, $c1, $c1|2);
} elsif ($c0 == 0 || $c0 == 0340) {
return $prefix;
} elsif (($c0 & ~3) == 0260 || $c0 == 0270 ||
($c0 & ~3) == 0240 || $c0 == 0250) {
} elsif (($c0 & ~3) == 0260 || $c0 == 0270) {
# VEX/XOP
my($c,$m,$wlp);
$m = shift(@codes);
$wlp = shift(@codes);
$c = ($m >> 6);
$m = $m & 31;
$prefix .= sprintf('%s%02X%01X', $vex_class[$c], $m, $wlp & 3);
if ($c0 < 0260) {
my $tuple = shift(@codes);
}
} elsif (($c0 & ~3) == 0260 || $c0 == 0270) {
# EVEX
my @p;
push(@p, shift(@codes));
push(@p, shift(@codes));
push(@p, shift(@codes));
my $tuple = shift(@codes);
my $m = $p[0] & 7;
my $p = $p[1] & 3;
$prefix .= sprintf('%s%02X%01X', 'evex', $m, $p);
} elsif ($c0 >= 0172 && $c0 <= 173) {
shift(@codes); # Skip is4 control byte
} else {
@ -918,6 +961,8 @@ sub byte_code_compile($$$) {
my ($op, $oq);
my $opex;
printf STDERR "%s (%x) %s\n", $str, $relax, join(',', sort(keys(%$flags)));
my %imm_codes = (
'ib' => 020, # imm8
'ib,u' => 024, # Unsigned imm8
@ -1063,7 +1108,7 @@ sub byte_code_compile($$$) {
} elsif ($op =~ /^(vex|xop)(|\..*)$/) {
my $vexname = $1;
my $c = $vexmap{$vexname};
my ($m,$w,$l,$p) = (undef,2,undef,0);
my ($m,$w,$l,$p) = (undef,undef,undef,0);
my $has_nds = 0;
my @subops = split(/\./, $2);
foreach $oq (@subops) {
@ -1111,15 +1156,22 @@ sub byte_code_compile($$$) {
die "$fname:$line: undefined modifier: $vexname.$oq\n";
}
}
if (!defined($m) || !defined($w) || !defined($l) || !defined($p)) {
if (!defined($m) || !defined($l) || !defined($p)) {
die "$fname:$line: missing fields in \U$vexname\E specification\n";
}
if (!defined($w)) {
$w = 0;
$flags->{'WIG'}++;
}
my $minmap = ($c == 1) ? 8 : 0; # 0-31 for VEX, 8-31 for XOP
if ($m < $minmap || $m > 31) {
die "$fname:$line: Only maps ${minmap}-31 are valid for \U${vexname}\n";
}
push(@codes, 05) if ($oppos{'v'} > 3);
push(@codes, defined($oppos{'v'}) ? 0260+$oppos{'v'} : 0270,
($c << 6)+$m, ($w << 4)+($l << 2)+$p);
($c << 6)+$m, ($w << 7)+($l << 2)+$p);
$flags->{'VEX'}++;
$prefix_ok = 0;
@ -1138,6 +1190,9 @@ sub byte_code_compile($$$) {
$l = 1;
} elsif ($oq eq '512' || $oq eq 'l2') {
$l = 2;
} elsif ($oq eq '1024' || $oq eq '1k' || $oq eq 'l3') {
# Not actually defined, but...
$l = 3;
} elsif ($oq eq 'w0') {
$w = 0;
} elsif ($oq eq 'w1') {
@ -1181,12 +1236,14 @@ sub byte_code_compile($$$) {
$flags->{'DFV'}++;
$dfv = 1;
push(@bad_op, ['v', $oq]);
} elsif ($oq =~ /^nd([01])$/) {
$nd = $1 + 0;
} elsif ($oq =~ /^(nds|ndd|nd|dds)$/) {
if (!defined($oppos{'v'})) {
die "$fname:$line: evex.$oq without 'v' operand\n";
}
$nds = 1;
$ndd = $oq eq 'nd';
$nd = $oq eq 'nd';
} else {
die "$fname:$line: undefined modifier: evex.$oq\n";
}
@ -1216,7 +1273,9 @@ sub byte_code_compile($$$) {
$p[2] |= 0x04 if ($nf);
$p[2] |= 0x10 if ($nd);
push(@codes, 05) if ($oppos{'v'} > 3);
push(@codes, defined($oppos{'v'}) ? 0240+$oppos{'v'} : 0250, @p);
push(@codes, $tup);
$flags->{'EVEX'}++;
$prefix_ok = 0;
@ -1242,8 +1301,11 @@ sub byte_code_compile($$$) {
}
push(@codes, $oq);
$flags->{'REX2'}++ if ($rex2);
$flags->{'REXX'}++;
if ($rex2) {
$flags->{'APX'}++;
$flags->{'LONG'}++;
}
$flags->{'REX2'}++;
$prefix_ok = 0;
} elsif (defined $imm_codes{$op}) {
if ($op eq 'seg') {