PTEST 能否用于测试两个寄存器是否均为零或其他情况?

Can PTEST be used to test if two registers are both zero or some other condition?

除了测试单个寄存器是否全为零之外,SSE4.1 ptest 还能做什么?

您能否结合使用 SF 和 CF 来测试关于两个未知输入寄存器的有用信息?

PTEST 有什么用?您可能认为检查打包比较的结果(如 PCMPEQD 或 CMPPS)会很好,但至少在 Intel CPU 上,

另见 Checking if TWO SSE registers are not both zero without destroying them

不,除非我遗漏了一些聪明的东西,否则带有两个未知寄存器的 ptest 通常无法用于检查关于它们的一些 属性。 (除了明显的东西,你已经想要一个按位与,比如两个位图之间的交集)。

测试两个寄存器是否全为零,或将它们放在一起并针对自身进行 PTEST。


ptest xmm0, xmm1 产生两个结果:

  • ZF = 是 xmm0 & xmm1 全零吗?
  • CF = 是 (~xmm0) & xmm1 全零吗?

如果第二个向量全为零,则标志完全不依赖于第一个向量中的位。

将 "is-all-zero" 检查视为 AND 和 ANDNOT 结果的 NOT(bitwise horizontal-OR()) 可能很有用。但可能不会,因为这对我的大脑来说太多了,无法轻松思考。垂直与然后水平或的序列可能会让你更容易理解为什么 PTEST 没有告诉你太多关于两个未知寄存器的组合,就像整数 TEST 指令一样。

这是 2 位 ptest a,mask 的真相 table。希望这有助于考虑 128b 输入的 0 和 1 的混合。

注意 CF(a,mask) == ZF(~a,mask).

a    mask     ZF    CF
00   00       1     1
01   00       1     1
10   00       1     1
11   00       1     1

00   01       1     0
01   01       0     1
10   01       1     0
11   01       0     1

00   10       1     0
01   10       1     0
10   10       0     1
11   10       0     1

00   11       1     0
01   11       0     0
10   11       0     0
11   11       0     1

Intel's intrinsics guide lists 2 interesting intrinsics for it。请注意 args 的命名:amask 是一个线索,它们告诉您有关 a 的部分由已知的 AND 掩码选择的信息。

  • _mm_test_mix_ones_zeros (__m128i a, __m128i mask): returns (ZF == 0 && CF == 0)
  • _mm_test_all_zeros (__m128i a, __m128i mask): returns ZF

还有更简单命名的版本:

  • int _mm_testc_si128 (__m128i a, __m128i b): returns CF
  • int _mm_testnzc_si128 (__m128i a, __m128i b): returns (ZF == 0 && CF == 0)
  • int _mm_testz_si128 (__m128i a, __m128i b): returns ZF

这些内在函数有 AVX2 __m256i 版本,但指南仅列出了 __m128i 操作数的 all_zeros 和 mix_ones_zeros 备用名称版本。

如果你想从 C 或 C++ 测试一些其他条件,你应该使用具有相同操作数的 testctestz,并希望你的编译器意识到它只需要做一个PTEST,甚至希望使用单个 JCC、SETCC 或 CMOVCC 来实现您的逻辑。 (我建议检查 asm,至少对于你最关心的编译器。)


请注意 _mm_testz_si128(v, set1(0xff)) 始终与 _mm_testz_si128(v,v) 相同,因为这就是 AND 的工作原理。但 CF 结果并非如此。

您可以使用

检查向量是否为全一
bool is_all_ones = _mm_testc_si128(v, _mm_set1_epi8(0xff));

这可能并不比针对全一向量的 PCMPEQB 更快,但代码量更小,然后是通常的 movemask + cmp。它不会避免需要矢量常数。

PTEST 的优点是它不会破坏任何一个输入操作数,即使没有 AVX。