1

如何使用 AVX2 中的等效指令替换丢失的VPERMIL2PS指令?

VPERMIL2PS ymm1, ymm2, ymm3, ymm4/m256, imz2

使用来自 ymm4/mem 的控件置换 ymm2 和 ymm3 中的单精度浮点值,结果存储在具有选择性零匹配控件的 ymm1 中。

VPERMIL2PS (VEX.256 encoded version)
DEST[31:0]  sel_and_condzerosp(SRC1[127:0], SRC2[127:0], SRC3[3:0])
DEST[63:32]  sel_and_condzerosp(SRC1[127:0], SRC2[127:0], SRC3[35:32])
DEST[95:64]  sel_and_condzerosp(SRC1[127:0], SRC2[127:0], SRC3[67:64])
DEST[127:96]  sel_and_condzerosp(SRC1[127:0], SRC2[127:0], SRC3[99:96])
DEST[159:128]  sel_and_condzerosp(SRC1[255:128], SRC2[255:128], SRC3[131:128])
DEST[191:160]  sel_and_condzerosp(SRC1[255:128], SRC2[255:128], SRC3[163:160])
DEST[223:192]  sel_and_condzerosp(SRC1[255:128], SRC2[255:128], SRC3[195:192])
DEST[255:224]  sel_and_condzerosp(SRC1[255:128], SRC2[255:128], SRC3[227:224])

英特尔 C/C++ 编译器内在等效项

VPERMIL2PS __m128 _mm_permute2_ps (__m128 a, __m128 b, __m128i ctrl, int imm)
VPERMIL2PS __m256 _mm256_permute2_ps (__m256 a, __m256 b, __m256i ctrl, int imm)

VPERMIL2PS ymm1, ymm2, ymm3,ymm4/m256, imz2 描述 - 使用来自 ymm4/mem 的控件置换 ymm2 和 ymm3 中的单精度浮点值,结果存储在 ymm1 中,并带有选择性的零匹配控件。imz2:is4 立即字节的一部分,提供适用于双源置换指令的控制功能。

最接近的指令是 VPERMILPS .. 这个指令仍然有效

VPERMILPS (256-bit immediate version)
DEST[31:0]  Select4(SRC1[127:0], imm8[1:0]);
DEST[63:32]  Select4(SRC1[127:0], imm8[3:2]);
DEST[95:64]  Select4(SRC1[127:0], imm8[5:4]);
DEST[127:96]  Select4(SRC1[127:0], imm8[7:6]);
DEST[159:128]  Select4(SRC1[255:128], imm8[1:0]);
DEST[191:160]  Select4(SRC1[255:128], imm8[3:2]);
DEST[223:192]  Select4(SRC1[255:128], imm8[5:4]);
DEST[255:224]  Select4(SRC1[255:128], imm8[7:6]);

VPERMILPS ymm1、ymm2、ymm3/m256 描述 - RVM V/V AVX 使用来自 ymm3/mem 的控件在 ymm2 中置换单精度浮点值并将结果存储在 ymm1 中。

我很难说它如何正确,因为为了可靠性,您需要模拟指令VPERMIL2PS,因此我呼吁当地专家!

最近的英特尔(R) AVX 架构更改 2009 年 1 月 29 日 删除:VPERMIL2PS 和 VPERMIL2PD

所有 PERMIL2 指令都消失了——包括 128 位和 256 位版本。像下面的 FMA 一样,他们使用 VEX.W 位来选择来自内存的源——我们不再朝着使用 VEX.W 的方向发展。

英特尔编译器不理解此 VPERMIL2PS 指令。

AVX-512 指令需要最新的处理器,这不是一个通用的解决方案.. Visual Studio成功组装了该指令,但该指令无法在处理器上执行,抛出异常。

反汇编代码

align 20h;
Yperm_msk ymmword 000000000100000006000000070000000C0000000D0000000A0000000B000000h

                vmovups ymm0, [rbp+920h+var_8C0]
                vmovdqu ymm1, Yperm_msk
                vpermil2ps ymm0, ymm0, [rbp+920h+var_880], ymm1, 920h+var_920
                vmovups [rbp+920h+var_1A0], ymm0

指令的完整描述

手术

select2sp(src1, src2, sel) // This macro is used by another macro “sel_and_condzerosp“ below
{
if (sel[2:0]=0) then TMP  src1[31:0]
if (sel[2:0]=1) then TMP  src1[63:32]
if (sel[2:0]=2) then TMP  src1[95:64]
if (sel[2:0]=3) then TMP  src1[127:96]
if (sel[2:0]=4) then TMP  src2[31:0]
if (sel[2:0]=5) then TMP  src2[63:32]
if (sel[2:0]=6) then TMP  src2[95:64]
if (sel[2:0]=7) then TMP  src2[127:96]
return TMP
}
sel_and_condzerosp(src1, src2, sel) // This macro is used by VPERMIL2PS
{
TMP[31:0]  select2sp(src1[127:0], src2[127:0], sel[2:0])
IF (imm8[1:0] = 2) AND (sel[3]=1) THEN TMP[31:0]  0
IF (imm8[1:0] = 3) AND (sel[3]=0) THEN TMP[31:0]  0
return TMP
}

VPERMIL2PS(VEX.256 编码版本)

DEST[31:0]  sel_and_condzerosp(SRC1[127:0], SRC2[127:0], SRC3[3:0])
DEST[63:32]  sel_and_condzerosp(SRC1[127:0], SRC2[127:0], SRC3[35:32])
DEST[95:64]  sel_and_condzerosp(SRC1[127:0], SRC2[127:0], SRC3[67:64])
DEST[127:96]  sel_and_condzerosp(SRC1[127:0], SRC2[127:0], SRC3[99:96])
DEST[159:128]  sel_and_condzerosp(SRC1[255:128], SRC2[255:128], SRC3[131:128])
DEST[191:160]  sel_and_condzerosp(SRC1[255:128], SRC2[255:128], SRC3[163:160])
DEST[223:192]  sel_and_condzerosp(SRC1[255:128], SRC2[255:128], SRC3[195:192])
DEST[255:224]  sel_and_condzerosp(SRC1[255:128], SRC2[255:128], SRC3[227:224])

Bochs 模拟此指令的方式

class bxInstruction_c;

void BX_CPP_AttrRegparmN(1) BX_CPU_C::VPERMIL2PS_VdqHdqWdqIbR(bxInstruction_c *i)
{
  BxPackedYmmRegister op1 = BX_READ_YMM_REG(i->src1());
  BxPackedYmmRegister op2 = BX_READ_YMM_REG(i->src2());
  BxPackedYmmRegister op3 = BX_READ_YMM_REG(i->src3()), result;
  unsigned len = i->getVL();

  result.clear();

  for (unsigned n=0; n < len; n++) {
    xmm_permil2ps(&result.ymm128(n), &op1.ymm128(n), &op2.ymm128(n), &op3.ymm128(n), i->Ib() & 3);
  }

  BX_WRITE_YMM_REGZ_VLEN(i->dst(), result, len);

  BX_NEXT_INSTR(i);
}

BX_CPP_INLINE void xmm_permil2ps(BxPackedXmmRegister *r, const BxPackedXmmRegister *op1, const BxPackedXmmRegister *op2, const BxPackedXmmRegister *op3, unsigned m2z)
{
  for(unsigned n=0; n < 4; n++) {
    Bit32u ctrl = op3->xmm32u(n);
    if ((m2z ^ ((ctrl >> 3) & 0x1)) == 0x3)
      r->xmm32u(n) = 0;
    else
      r->xmm32u(n) = (ctrl & 0x4) ? op1->xmm32u(ctrl & 0x3) : op2->xmm32u(ctrl & 0x3);
  }
}
4

1 回答 1

3

它们并没有“消失”,它们一开始就从未存在于任何真正的 CPU 中。2009 年是第一个带有 AVX1 的 CPU 发布之前,而 AVX 仍处于规划阶段。IDK 你在看什么,甚至提到了他们。

ISA ref 手册的当前版本或它的 HTML 摘录没有提及它。英特尔的内在函数也没有指导. 也许是 Sandybridge 发布之前 10 年前的“未来扩展”手册?

因为为了可靠性,你需要模拟指令 VPERMIL2PS

不,你没有,它从一开始就不存在,所以没有使用它的代码。 (或者很少;可能一些基于早期预发布 AVX 文档的预期编写)。您只需要准确实现任何给定问题所需的功能。

您标记了此 (AMD) XOP,但您仅引用了 Intel 文档;我认为 XOP 确实有一些 2-input shuffle,但我没有去查看文档。当然只适用于 128 位向量。


AVX1 确实有一些 2 输入随机播放,但没有可变控制。有vshufps/pd可以立即控制,vunpckl/hps并且...pd可以执行相应 128 位随机播放的两个单独的通道内版本。

vshufps最坏的情况是,您可以从 2x +构建任何固定的 2 输入通道内随机播放vblendps 最好的情况是 1 vshufps,或者中间是vshufps+vblendps或 2x vshufps(例如,将您想要的元素收集到一个向量中,然后按正确的顺序放置它们)。这些vshufps洗牌中的任何一个都可以是vunpcklpshps。请记住,即时vblendps很便宜,但在 Intel 上,shuffle 仅具有 1/clock 吞吐量(端口 5 直到 Ice Lake)。

您甚至可以使用 variable-control 2xvpermilps和 compare 或 shift +vblendvps来 emulate vpermil2ps,因为vpermilps忽略索引中的高位。因此,这将是 BOCHS 实现(ctrl & 0x4) ? op2[ctrl & 0x3] : op2[ctrl & 0x3];,您将两个输入都 ctrl打乱vpermilps(隐含地只查看低 2 位),然后ctrl & 4通过整数移位将该位移到顶部来混合。

(可选地,还可以vandps通过使用vpslld将第 3 个索引位放在顶部进行混合来模拟条件归零,或者vpsrad使用与零比较的结果来为vpand.vblendvps可以只使用它来混合零而不是移位/和或cmp/and)。

但是,如果您关心编译时常量 shuffle control 的性能,请不要天真地放弃它。而是从可用的 2 输入操作中构建等效的 shuffle。这就是为什么我不费心用 C 写出完整的实现。


AVX2 仅添加了一些新的 2 输入 shuffle,它们可能在这里有用:256 位vpalignr,就像 2 条通道内palignr指令。它还添加了整数vpunpckl/h b/w/d/q,但我们已经vunpckl/hps从 AVX1 中获得了。


直到AVX512Fvpermt2psvpermi2ps/pd才出现真正的可变控制 2 输入随机播放。

但它不支持基于高位索引元素的条件归零,例如pshufb或建议的vpermil2ps;而是使用掩码寄存器进行零掩码。例如

  vmovd2m    k1, ymm0                              ; extract top bit of dword elements
  knotw      k1, k1                                ; cleared for elements to be zeroed
  vpermi2ps  ymm0{k1}{z}, ymm0, ymm1, ymm2         ; ymm0=indices   ymm1,ymm2 = table
  ; indices overwritten with result
  ; use vpermt2ps instead to overwrite one of the "table" inputs instead of the index vector.

或者可能更好地用于vpfclassps k1, ymm0, some_constant设置k1非负值,避免需要knot. 在 Skylake-X 上,它是一个微指令。

或者vptestnmdset1(1UL<<31)掩码一起使用来设置向量的掩码寄存器 = !signbit


它也不是“在车道上”,因此您可能需要调整索引,我认为为索引 > 4 添加 8。 vpermi/t2ps索引到两个向量的串联中,因此一个源内的交叉车道发生在选择另一个输入之前。

于 2019-08-18T01:00:41.703 回答