[PATCH V2 3/8] Support APX GPR32 with extend evex prefix
Cui, Lili
lili.cui@intel.com
Mon Nov 13 05:53:04 GMT 2023
> Subject: Re: [PATCH V2 3/8] Support APX GPR32 with extend evex prefix
>
> On 03.11.2023 17:50, Cui, Lili wrote:
> > --- a/gas/config/tc-i386.c
> > +++ b/gas/config/tc-i386.c
> > @@ -3672,9 +3672,10 @@ install_template (const insn_template *t)
> > /* Dual VEX/EVEX templates need stripping one of the possible variants.
> */
> > if (t->opcode_modifier.vex && t->opcode_modifier.evex)
> > {
> > - if ((maybe_cpu (t, CpuAVX) || maybe_cpu (t, CpuAVX2)
> > - || maybe_cpu (t, CpuFMA))
> > - && (maybe_cpu (t, CpuAVX512F) || maybe_cpu (t, CpuAVX512VL)))
> > + if ((maybe_cpu (t, CpuAVX) || maybe_cpu (t, CpuAVX2)
> > + || maybe_cpu (t, CpuFMA) || maybe_cpu (t, CpuAMX_TILE))
> > + && (maybe_cpu (t, CpuAVX512F) || maybe_cpu (t, CpuAVX512VL)
> > + || maybe_cpu (t, CpuAPX_F)))
>
> Something's odd with formatting (indentation) here. I first thought I had
> screwed up in my patch, but looking back things appear to be correct there.
>
Rebase with master.
> > @@ -3689,12 +3690,11 @@ install_template (const insn_template *t)
> > i.tm.cpu.bitfield.cpuavx = 1;
> > else
> > {
> > - gas_assert (!i.tm.cpu.bitfield.isa);
> > i.tm.cpu.bitfield.isa = i.tm.cpu_any.bitfield.isa;
>
> You may not remove the assertion here, or else the assignment could silently
> overwrite a value. Can you explain why you did the removal?
>
Rebased with master. Sorry I just saw the comments here.
> > @@ -3885,6 +3885,14 @@ is_any_vex_encoding (const insn_template *t)
> > return t->opcode_modifier.vex || is_evex_encoding (t); }
> >
> > +static INLINE bool
> > +is_any_apx_evex_encoding (void)
> > +{
> > + return i.rex2 || i.tm.opcode_space == SPACE_EVEXMAP4
> > + || (i.vex.register_specifier
> > + && i.vex.register_specifier->reg_flags & RegRex2); }
>
> The use of i.rex2 here doesn't fit the name; the sole user has first checked
> that no legacy encoding is going to be used, and that's a prereq here. Such a
> prereq needs spelling out, such that one can be easily aware when possibly
> adding another caller.
>
> Also, what does "any" stand for in the name here ...
>
How about "check_if_any_vex_is_evex_apx_encoding ()" ?
> > static INLINE bool
> > is_any_apx_rex2_encoding (void)
>
> ... and also the one here? In is_any_vex_encoding() it refers to VEX/XOP/EVEX.
>
Changed it to is_apx_rex2_encoding.
> > @@ -4161,6 +4169,27 @@ build_rex2_prefix (void)
> > | (i.rex2 << 4) | i.rex);
> > }
> >
> > +/* Build the EVEX prefix (4-byte) for evex insn
> > + | 62h |
> > + | `R`X`B`R' | B'mmm |
> > + | W | v`v`v`v | `x' | pp |
> > + | z| L'L | b | `v | aaa |
> > +*/
> > +static void
> > +build_apx_evex_prefix (void)
> > +{
> > + build_evex_prefix ();
> > + if (i.rex2 & REX_R)
> > + i.vex.bytes[1] &= 0xef;
>
> I think this would read easier as ~0x10 (similarly below then). I also think ...
>
Done.
> > + if (i.vex.register_specifier
> > + && register_number (i.vex.register_specifier) > 0xf)
> > + i.vex.bytes[3] &= 0xf7;
> > + if (i.rex2 & REX_B)
> > + i.vex.bytes[1] |= 0x08;
>
> ... it would help if the byte 1 updates were kept together (the compiler may
> even produce better code then), and maybe ...
>
> > + if (i.rex2 & REX_X)
> > + i.vex.bytes[2] &= 0xfb;
>
> ... the byte 2 update ahead of the byte 3 one.
>
Done.
> Also why do you use register_number() here, and not the RegRex2 flag?
>
RegRex2 flag is better, changed it.
> > @@ -5624,19 +5653,42 @@ md_assemble (char *line)
> > }
> >
> > /* Check for explicit REX2 prefix. */
> > - if (i.rex2 || i.rex2_encoding)
> > + if (i.rex2_encoding)
>
> This change ...
>
> > {
> > as_bad (_("REX2 prefix invalid with `%s'"), insn_name (&i.tm));
>
> ... further invalidates this message. Yet iirc you said you removed the
> i.rex2 part of the check anyway from patch 1.
>
> > return;
> > }
> >
> > - if (i.tm.opcode_modifier.vex)
> > + if (is_any_apx_evex_encoding ())
> > + {
> > + if (i.tm.opcode_space == SPACE_EVEXMAP4 &&
> (i.prefix[DATA_PREFIX] != 0))
> > + {
> > + i.tm.opcode_modifier.opcodeprefix = PREFIX_0X66;
>
> Perhaps better assert that no other embedded prefix was already recorded
> here?
Added the code as below, I added as_bad instead of assert, I think this is a input error and not a gas internal error, right? Besides REX_PREFIX?
if (check_if_any_vex_is_evex_apx_encoding ())
{
if (i.tm.opcode_space == SPACE_EVEXMAP4 && (i.prefix[DATA_PREFIX] != 0))
{
i.tm.opcode_modifier.opcodeprefix = PREFIX_0X66;
i.prefix[DATA_PREFIX] = 0;
/* Prefixes other than the rex prefix cannot be used with the data prefix. */
const unsigned char *p = i.prefix;
for (j = 0; j < ARRAY_SIZE (i.prefix); ++j, ++p)
{
if (!*p)
continue;
switch (j)
{
case DATA_PREFIX:
case REX_PREFIX:
break;
default:
as_bad (_("unexpecting prefix %x together with DATA "
"prefix in front of evex-promoted apx "
"instruction "), *p);
return;
}
}
}
build_apx_evex_prefix ();
}
>
> > + i.prefix[DATA_PREFIX] = 0;
> > + }
> > +
> > + build_apx_evex_prefix ();
> > +
> > + /* Encode the NDD bit of the instruction promoted from the legacy
> > + space. */
> > + if (i.vex.register_specifier && i.tm.opcode_space ==
> SPACE_EVEXMAP4)
> > + i.vex.bytes[3] |= 0x10;
>
> Why the restriction to map 3? And why is this not part of
> build_apx_evex_prefix()?
It is the ND bit, which is added in advance here. After reading the comments below, I'll move this to an NDD patch.
>
> > + /* Encode the NF bit of the instruction promoted from legacy and
> vex
> > + space. */
> > + if (i.has_nf)
> > + i.vex.bytes[3] |= 0x04;
>
> This wants to move to the patch actually introducing NF handling.
>
Done.
> > @@ -5663,16 +5715,17 @@ md_assemble (char *line)
> > instruction already has a prefix, we need to convert old
> > registers to new ones. */
> >
> > - if ((i.types[0].bitfield.class == Reg && i.types[0].bitfield.byte
> > - && (i.op[0].regs->reg_flags & RegRex64) != 0)
> > - || (i.types[1].bitfield.class == Reg && i.types[1].bitfield.byte
> > - && (i.op[1].regs->reg_flags & RegRex64) != 0)
> > - || (((i.types[0].bitfield.class == Reg && i.types[0].bitfield.byte)
> > - || (i.types[1].bitfield.class == Reg && i.types[1].bitfield.byte))
> > - && (i.rex != 0 || i.rex2 != 0)))
> > + if (!is_any_vex_encoding (&i.tm)
> > + && ((i.types[0].bitfield.class == Reg && i.types[0].bitfield.byte
> > + && (i.op[0].regs->reg_flags & RegRex64) != 0)
> > + || (i.types[1].bitfield.class == Reg && i.types[1].bitfield.byte
> > + && (i.op[1].regs->reg_flags & RegRex64) != 0)
> > + || (((i.types[0].bitfield.class == Reg && i.types[0].bitfield.byte)
> > + || (i.types[1].bitfield.class == Reg && i.types[1].bitfield.byte))
> > + && (i.rex != 0 || i.rex2 != 0))))
>
> I'm a little puzzled by this (hard to read) adjustment:
> is_any_vex_encoding() basically excludes most new templates added here,
> yet for those permitting Reg8 you still need to enforce the EVEX-imposed
> restriction.
>
Moved is_any_vex_encoding() into if body to constrain i.rex |= REX_OPCODE.
if (((i.types[0].bitfield.class == Reg && i.types[0].bitfield.byte
&& (i.op[0].regs->reg_flags & RegRex64) != 0)
|| (i.types[1].bitfield.class == Reg && i.types[1].bitfield.byte
&& (i.op[1].regs->reg_flags & RegRex64) != 0)
|| (((i.types[0].bitfield.class == Reg && i.types[0].bitfield.byte)
|| (i.types[1].bitfield.class == Reg && i.types[1].bitfield.byte))
&& (i.rex != 0 || i.rex2 != 0))))
{
int x;
if (!is_apx_rex2_encoding () && !is_any_vex_encoding(&i.tm))
i.rex |= REX_OPCODE;
> > @@ -7043,7 +7096,7 @@ VEX_check_encoding (const insn_template *t)
> > static int check_EgprOperands (const insn_template *t) {
> > - if (t->opcode_modifier.noegpr)
> > + if (t->opcode_modifier.noegpr && !need_evex_encoding())
> > {
> > for (unsigned int op = 0; op < i.operands; op++)
> > {
>
> What is this change about?
>
After merging vex and evex, evex exits here early and all evex supports egpr.
> > @@ -14252,6 +14306,9 @@ static bool check_register (const reg_entry
> > *r)
> >
> > if (r->reg_flags & RegRex2)
> > {
> > + if (is_evex_encoding (current_templates->start))
> > + i.vec_encoding = vex_encoding_evex;
>
> What if the APX template isn't first in the group?
>
If apx_f is not supported, it will return false, just after this code. Oh, better to move it to the back. Done.
if (r->reg_flags & RegRex2)
{
if (current_templates->start->opcode_modifier.evex)
i.vec_encoding = vex_encoding_evex;
if (!cpu_arch_flags.bitfield.cpuapx_f
|| flag_code != CODE_64BIT)
return false;
}
> > --- a/opcodes/i386-opc.h
> > +++ b/opcodes/i386-opc.h
> > @@ -975,6 +975,7 @@ typedef struct insn_template
> > 1: 0F opcode prefix / space.
> > 2: 0F38 opcode prefix / space.
> > 3: 0F3A opcode prefix / space.
> > + 4: EVEXMAP4 opcode prefix / space.
> > 5: EVEXMAP5 opcode prefix / space.
> > 6: EVEXMAP6 opcode prefix / space.
> > 7: VEXMAP7 opcode prefix / space.
> > @@ -986,6 +987,7 @@ typedef struct insn_template
> > #define SPACE_0F 1
> > #define SPACE_0F38 2
> > #define SPACE_0F3A 3
> > +#define SPACE_EVEXMAP4 4
> > #define SPACE_EVEXMAP5 5
> > #define SPACE_EVEXMAP6 6
> > #define SPACE_VEXMAP7 7
>
> Nit: Please can padding here match surrounding code?
>
Done.
> > @@ -338,6 +342,7 @@ adc, 0x14, 0, W|No_sSuf, {
> > Imm8|Imm16|Imm32|Imm32S, Acc|Byte|Word|Dword|Qword } adc,
> 0x80/2, 0,
> > W|Modrm|No_sSuf|HLEPrefixLock, { Imm8|Imm16|Imm32|Imm32S,
> >
> Reg8|Reg16|Reg32|Reg64|Byte|Word|Dword|Qword|Unspecified|BaseInde
> x }
> >
> > neg, 0xf6/3, 0, W|Modrm|No_sSuf|HLEPrefixLock, {
> >
> Reg8|Reg16|Reg32|Reg64|Byte|Word|Dword|Qword|Unspecified|BaseInde
> x }
> > +
> > not, 0xf6/2, 0, W|Modrm|No_sSuf|HLEPrefixLock, {
> >
> Reg8|Reg16|Reg32|Reg64|Byte|Word|Dword|Qword|Unspecified|BaseInde
> x }
> >
> > aaa, 0x37, No64, NoSuf, {}
>
> Nit: In an already overlarge patch it is extremely helpful if any unrelated
> changed could be omitted.
>
Done.
> > @@ -1316,13 +1321,16 @@ getsec, 0xf37, SMX, NoSuf, {}
> >
> > invept, 0x660f3880, EPT&No64, Modrm|IgnoreSize|NoSuf, {
> > Oword|Unspecified|BaseIndex, Reg32 } invept, 0x660f3880, EPT&x64,
> > Modrm|NoSuf|NoRex64, { Oword|Unspecified|BaseIndex, Reg64 }
> > +invept, 0xf3f0, APX_F_64&EPT, Modrm|NoSuf|EVex128|EVexMap4, {
> > +Oword|Unspecified|BaseIndex, Reg64 }
> > invvpid, 0x660f3881, EPT&No64, Modrm|IgnoreSize|NoSuf, {
> > Oword|Unspecified|BaseIndex, Reg32 } invvpid, 0x660f3881, EPT&x64,
> > Modrm|NoSuf|NoRex64, { Oword|Unspecified|BaseIndex, Reg64 }
> > +invvpid, 0xf3f1, APX_F_64&EPT, Modrm|NoSuf|EVex128|EVexMap4, {
> > +Oword|Unspecified|BaseIndex, Reg64 }
> >
> > // INVPCID instruction
> >
> > invpcid, 0x660f3882, INVPCID&No64, Modrm|IgnoreSize|NoSuf, {
> > Oword|Unspecified|BaseIndex, Reg32 } invpcid, 0x660f3882,
> > INVPCID&x64, Modrm|NoSuf|NoRex64, { Oword|Unspecified|BaseIndex,
> Reg64
> > }
> > +invpcid, 0xf3f2, APX_F_64&INVPCID, Modrm|NoSuf|EVex128|EVexMap4, {
> > +Oword|Unspecified|BaseIndex, Reg64 }
>
> While ordering doesn't matter functionality wise, overall readability imo
> would end up improved if you had EPT&APX_F and INVPCID&APX_f here (and
> the likewise elsewhere, albeit it looks in other cases you already have things
> the other way round).
>
Done, it should be consistent.
> > @@ -3310,6 +3364,7 @@ prefetchit1, 0xf18/6, PREFETCHI&x64,
> > Modrm|Anysize|IgnoreSize|NoSuf, { BaseIndex // CMPCCXADD
> instructions.
> >
> > cmp<cc>xadd, 0x66e<cc:opc>, CMPCCXADD&x64,
> > Modrm|Vex|Space0F38|VexVVVV|SwapSources|CheckOperandSize|NoSuf,
> {
> > Reg32|Reg64, Reg32|Reg64, Dword|Qword|Unspecified|BaseIndex }
> > +cmp<cc>xadd, 0x66e<cc:opc>, CMPCCXADD&x64&APX_F_64,
> >
> +Modrm|EVex128|Space0F38|VexVVVV|SwapSources|CheckOperandSize|N
> oSuf, {
> > +Reg32|Reg64, Reg32|Reg64, Dword|Qword|Unspecified|BaseIndex }
>
> Nit: Redundant x64.
>
Done, thanks!
Lili.
More information about the Binutils
mailing list