BR3392392: fix broadcast decorators and improve error messages

Checkin c33d95fde9:
    BR 3392370: {z} decorator allowed on MOVDQ* memory operands

... inadvertently broke broadcast operations, which only apply to
memory operands and therefore were only handled in one of the two
brace-parser implementations.  Fix that.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
This commit is contained in:
H. Peter Anvin 2017-04-02 18:38:58 -07:00
parent e886c0e968
commit 8e37ff4ea1
4 changed files with 85 additions and 49 deletions

View File

@ -198,7 +198,9 @@ enum match_result {
MERR_INVALOP,
MERR_OPSIZEMISSING,
MERR_OPSIZEMISMATCH,
MERR_BRNOTHERE,
MERR_BRNUMMISMATCH,
MERR_MASKNOTHERE,
MERR_BADCPU,
MERR_BADMODE,
MERR_BADHLE,
@ -756,10 +758,18 @@ int64_t assemble(int32_t segment, int64_t start, int bits, insn *instruction)
case MERR_OPSIZEMISMATCH:
nasm_error(ERR_NONFATAL, "mismatch in operand sizes");
break;
case MERR_BRNOTHERE:
nasm_error(ERR_NONFATAL,
"broadcast not permitted on this operand");
break;
case MERR_BRNUMMISMATCH:
nasm_error(ERR_NONFATAL,
"mismatch in the number of broadcasting elements");
break;
case MERR_MASKNOTHERE:
nasm_error(ERR_NONFATAL,
"mask not permitted on this operand");
break;
case MERR_BADCPU:
nasm_error(ERR_NONFATAL, "no instruction for this cpu level");
break;
@ -2278,6 +2288,7 @@ static enum match_result matches(const struct itemplate *itemp,
for (i = 0; i < itemp->operands; i++) {
opflags_t type = instruction->oprs[i].type;
decoflags_t deco = instruction->oprs[i].decoflags;
decoflags_t ideco = itemp->deco[i];
bool is_broadcast = deco & BRDCAST_MASK;
uint8_t brcast_num = 0;
opflags_t template_opsize, insn_opsize;
@ -2289,12 +2300,15 @@ static enum match_result matches(const struct itemplate *itemp,
if (!is_broadcast) {
template_opsize = itemp->opd[i] & SIZE_MASK;
} else {
decoflags_t deco_brsize = itemp->deco[i] & BRSIZE_MASK;
decoflags_t deco_brsize = ideco & BRSIZE_MASK;
if (~ideco & BRDCAST_MASK)
return MERR_BRNOTHERE;
/*
* when broadcasting, the element size depends on
* the instruction type. decorator flag should match.
*/
if (deco_brsize) {
template_opsize = (deco_brsize == BR_BITS32 ? BITS32 : BITS64);
/* calculate the proper number : {1to<brcast_num>} */
@ -2304,8 +2318,10 @@ static enum match_result matches(const struct itemplate *itemp,
}
}
if ((itemp->opd[i] & ~type & ~SIZE_MASK) ||
(deco & ~itemp->deco[i] & ~BRNUM_MASK)) {
if (~ideco & deco & OPMASK_MASK)
return MERR_MASKNOTHERE;
if (itemp->opd[i] & ~type & ~SIZE_MASK) {
return MERR_INVALOP;
} else if (template_opsize) {
if (template_opsize != insn_opsize) {

View File

@ -191,52 +191,56 @@ static void process_size_override(insn *result, operand *op)
}
/*
* when two or more decorators follow a register operand,
* consecutive decorators are parsed here.
* opmask and zeroing decorators can be placed in any order.
* e.g. zmm1 {k2}{z} or zmm2 {z}{k3}
* decorator(s) are placed at the end of an operand.
* Brace decorators are are parsed here. opmask and zeroing
* decorators can be placed in any order. e.g. zmm1 {k2}{z} or zmm2
* {z}{k3} decorator(s) are placed at the end of an operand.
*/
static bool parse_braces(decoflags_t *decoflags)
{
int i;
bool recover = false;
int i, j;
i = tokval.t_type;
do {
if (i == TOKEN_OPMASK) {
while (true) {
switch (i) {
case TOKEN_OPMASK:
if (*decoflags & OPMASK_MASK) {
nasm_error(ERR_NONFATAL, "opmask k%"PRIu64" is already set",
nasm_error(ERR_NONFATAL,
"opmask k%"PRIu64" is already set",
*decoflags & OPMASK_MASK);
*decoflags &= ~OPMASK_MASK;
}
*decoflags |= VAL_OPMASK(nasm_regvals[tokval.t_integer]);
} else if (i == TOKEN_DECORATOR) {
switch (tokval.t_integer) {
break;
case TOKEN_DECORATOR:
j = tokval.t_integer;
switch (j) {
case BRC_Z:
/*
* according to AVX512 spec, only zeroing/merging decorator
* is supported with opmask
*/
*decoflags |= GEN_Z(0);
*decoflags |= Z_MASK;
break;
case BRC_1TO2:
case BRC_1TO4:
case BRC_1TO8:
case BRC_1TO16:
*decoflags |= BRDCAST_MASK | VAL_BRNUM(j - BRC_1TO2);
break;
default:
nasm_error(ERR_NONFATAL, "{%s} is not an expected decorator",
tokval.t_charptr);
nasm_error(ERR_NONFATAL,
"{%s} is not an expected decorator",
tokval.t_charptr);
break;
}
} else if (i == ',' || i == TOKEN_EOS){
break;
} else {
nasm_error(ERR_NONFATAL, "only a series of valid decorators"
" expected");
recover = true;
break;
case ',':
case TOKEN_EOS:
return false;
default:
nasm_error(ERR_NONFATAL,
"only a series of valid decorators expected");
return true;
}
i = stdscan(NULL, &tokval);
} while(1);
return recover;
}
}
static int parse_mref(operand *op, const expr *e)

View File

@ -4,45 +4,46 @@ NASM = ../nasm
NASMOPT = -Ox -I../misc $(OPT)
PERL = perl
TESTS = $(wildcard *.asm)
RM = rm -f
$(NASM):
$(MAKE) -C ..
%.bin: %.asm $(NASM)
$(NASM) $(NASMOPT) -f bin -o $@ -l $*.lst $<
$(RM) $*.lst ; $(NASM) $(NASMOPT) -f bin -o $@ -l $*.lst $<
%.ith: %.asm $(NASM)
$(NASM) $(NASMOPT) -f ith -o $@ -l $*.lst $<
$(RM) $*.lst ; $(NASM) $(NASMOPT) -f ith -o $@ -l $*.lst $<
%.srec: %.asm $(NASM)
$(NASM) $(NASMOPT) -f srec -o $@ -l $*.lst $<
$(RM) $*.lst ; $(NASM) $(NASMOPT) -f srec -o $@ -l $*.lst $<
%.o: %.asm $(NASM)
$(NASM) $(NASMOPT) -f elf32 -o $@ -l $*.lst $<
$(RM) $*.lst ; $(NASM) $(NASMOPT) -f elf32 -o $@ -l $*.lst $<
%.o64: %.asm $(NASM)
$(NASM) $(NASMOPT) -f elf64 -o $@ -l $*.lst $<
$(RM) $*.lst ; $(NASM) $(NASMOPT) -f elf64 -o $@ -l $*.lst $<
%.obj: %.asm $(NASM)
$(NASM) $(NASMOPT) -f obj -o $@ -l $*.lst $<
$(RM) $*.lst ; $(NASM) $(NASMOPT) -f obj -o $@ -l $*.lst $<
%.coff: %.asm $(NASM)
$(NASM) $(NASMOPT) -f coff -o $@ -l $*.lst $<
$(RM) $*.lst ; $(NASM) $(NASMOPT) -f coff -o $@ -l $*.lst $<
%.win32: %.asm $(NASM)
$(NASM) $(NASMOPT) -f win32 -o $@ -l $*.lst $<
$(RM) $*.lst ; $(NASM) $(NASMOPT) -f win32 -o $@ -l $*.lst $<
%.win64: %.asm $(NASM)
$(NASM) $(NASMOPT) -f win64 -o $@ -l $*.lst $<
$(RM) $*.lst ; $(NASM) $(NASMOPT) -f win64 -o $@ -l $*.lst $<
%.mo32: %.asm $(NASM)
$(NASM) $(NASMOPT) -f macho32 -o $@ -l $*.lst $<
$(RM) $*.lst ; $(NASM) $(NASMOPT) -f macho32 -o $@ -l $*.lst $<
%.mo64: %.asm $(NASM)
$(NASM) $(NASMOPT) -f macho64 -o $@ -l $*.lst $<
$(RM) $*.lst ; $(NASM) $(NASMOPT) -f macho64 -o $@ -l $*.lst $<
%.dbg: %.asm $(NASM)
$(NASM) $(NASMOPT) -f dbg -o $@ -l $*.lst $<
$(RM) $*.lst ; $(NASM) $(NASMOPT) -f dbg -o $@ -l $*.lst $<
%.asm: %.pl
$(PERL) $< > $@
@ -62,13 +63,13 @@ diff: performtest.pl $(NASM) $(TESTS)
$(PERL) performtest.pl --diff --nasm='$(NASM)' $(TESTS)
clean:
rm -f *.com *.o *.o64 *.obj *.win32 *.win64 *.exe *.lst *.bin
rm -f *.dbg *.coff *.ith *.srec *.mo32 *.mo64 *.i
rm -rf testresults
rm -f elftest elftest64
$(RM) *.com *.o *.o64 *.obj *.win32 *.win64 *.exe *.lst *.bin
$(RM) *.dbg *.coff *.ith *.srec *.mo32 *.mo64 *.i
$(RM) -r testresults
$(RM) elftest elftest64
spotless: clean
rm -rf golden
$(RM) -r golden
#
# Test for ELF32 shared libraries; assumes an x86 Linux system

15
test/br3392392.asm Normal file
View File

@ -0,0 +1,15 @@
bits 64
vpaddd zmm0, zmm0, [rax]{1to16}
vpaddd zmm2{k3}, zmm0, zmm1
vpaddd zmm2 {k3}, zmm0, zmm1
vpaddd zmm0{k1}, zmm0, [rax]{1to16}
vmovdqa32 [rsi]{k1}, zmm1
vmovdqa32 [rsi]{z}, zmm1
vmovdqa32 [rsi]{k1}{z}, zmm1
vmovdqa32 [rsi]{z}{k1}, zmm1
%ifdef ERROR
vmovdqa32 [rsi]{z}{1to16}, zmm1
vmovdqa32 [rsi]{z}{k1}{1to16}, zmm1
vpaddd zmm0, zmm0, [rax]{k1}
%endif