为什么X86中没有NAND、NOR和XNOR指令? 它们是你可以在计算机上执行的最简单的“指令”之一(它们是我个人最先实现的指令) 执行NOT(和(x,y))将执行时间、依赖链长度和代码大小加倍 BMI1引入了“andnot”,这是一个有意义的补充,是一个独特的操作-为什么不在这个问题的标题中 您通常会阅读“它们占用宝贵的操作码空间”这几行中的答案,但接下来我会查看AVX512引入的所有kmask操作,顺便说一句,其中包括NAND和XNOR 优化编译器可以生成更好的代码 SIMD=>没有NOT指令,这就需要将执行时间、依赖链长度增加三倍(编辑:

为什么X86中没有NAND、NOR和XNOR指令? 它们是你可以在计算机上执行的最简单的“指令”之一(它们是我个人最先实现的指令) 执行NOT(和(x,y))将执行时间、依赖链长度和代码大小加倍 BMI1引入了“andnot”,这是一个有意义的补充,是一个独特的操作-为什么不在这个问题的标题中 您通常会阅读“它们占用宝贵的操作码空间”这几行中的答案,但接下来我会查看AVX512引入的所有kmask操作,顺便说一句,其中包括NAND和XNOR 优化编译器可以生成更好的代码 SIMD=>没有NOT指令,这就需要将执行时间、依赖链长度增加三倍(编辑:,x86,x86-64,cpu-architecture,instructions,instruction-set,X86,X86 64,Cpu Architecture,Instructions,Instruction Set,这些指令没有您想象的那么有价值,而且一旦创建了基本ISA,架构师通常不会添加新指令,除非某些重要用例获得了巨大成功。(例如,MMX对于大多数代码来说并不是一个巨大的胜利,但对于作为早期使用案例之一的视频/音频编解码器来说却是一个巨大的加速。) 请记住,大多数代码并不是在进行无分支的比特攻击。这在8086之后的几十年里才在SIMD中变得更加普遍。我怀疑大多数程序员宁愿使用或,也不愿意使用或(8086没有空间容纳更多遵循其正常模式的标准ALU指令编码1)许多代码花费大量时间进行比较和分支,在数据结构

这些指令没有您想象的那么有价值,而且一旦创建了基本ISA,架构师通常不会添加新指令,除非某些重要用例获得了巨大成功。(例如,MMX对于大多数代码来说并不是一个巨大的胜利,但对于作为早期使用案例之一的视频/音频编解码器来说却是一个巨大的加速。)

请记住,大多数代码并不是在进行无分支的比特攻击。这在8086之后的几十年里才在SIMD中变得更加普遍。我怀疑大多数程序员宁愿使用
,也不愿意使用
(8086没有空间容纳更多遵循其正常模式的标准ALU指令编码1)许多代码花费大量时间进行比较和分支,在数据结构上循环(并暂停内存),或进行“正常”数学运算。当然存在位操作代码,但许多代码并不涉及太多

保存一条或两条指令会有所帮助,但前提是你可以用这些新指令编译整个应用程序。(虽然BMI1和BMI2中的大多数实际上都是这样的,例如SHLX/SHRX用于1-uop复制和按变量移位,但Intel仍然添加了它们来修补真正糟糕的3-uop移位。)如果您针对的是特定的服务器,那么这很好(因此您可以使用
-march=native
构建),但许多x86代码都是提前编译的,以便在随机消费机器上使用。像SSE这样的扩展可以大大加快单循环的速度,因此通常可以将单个函数分派到不同版本来利用,同时保持较低的基线要求

但是,对于你建议的新添加的指令版本来说,这种方式是行不通的,所以添加它们的好处要低得多。而且它们还没有出现,因为8086是超级拥挤的

但大多数ISA都没有这些操作码,ARM没有,甚至PowerPC也没有。PowerPC选择在其32位指令字中使用编码空间来拥有大量操作码(包括整洁的东西,如
rlwinm
旋转和位范围屏蔽,以及其他位字段插入/提取到任意位置的东西)因此,这不仅仅是8086旧式处理器再次破坏x86-64的问题,更重要的是,大多数CPU架构师认为为这些处理器添加操作码是不值得的,即使在具有大量空间的RISC中也是如此

虽然MIPS有一个
,而不是
(MIPS
xori
零扩展了立即数,因此不能用于非完整寄存器。)


SIMD代码: 请注意,一旦创建了一次all-One向量,就可以在循环中重用它。大多数SIMD代码都在循环中,尽管对单个结构谨慎使用SIMD可能会很好

SIMD不仅向关键路径添加了1个周期,使NOR实现总共延迟了2个周期。在您的示例中,
pcmpeqd
脱离了关键路径,几乎所有CPU上都不依赖于旧的reg值。(不过,仍然需要SIMD执行单元来写入这些值)。它需要吞吐量,但不需要延迟。对于给定的代码块,执行时间可能取决于吞吐量或延迟。((没那么简单)/)

顺便说一句,编译器通常对所有操作数使用
vpxor
,而不是
vpandn
;唯一的优点是使用了一个内存源操作数,在这个操作数中,您不能使用异或进行加载,而不像可选的内存操作数(src2)是一个没有反转的操作数。
dst=~src1&src2


标量码
您通常可以将代码安排为不需要反转,例如,在OR之后检查相反的标志条件。不总是这样;当然,当您执行一系列按位操作时,它可能会出现,SIMD可能更是如此

对于像SPECint这样的大多数通用工作负载来说,向BMI1或未来的扩展添加更多这样的指令所带来的实际加速可能(已经)非常小

比整数
xnor
等更有价值。可能是像
sub
这样的普通整数指令的非破坏性VEX版本,不能用LEA
完成。因此许多
mov
/
sub
序列可能是
vsub
。也可能是
imul
,也可能是
ode>和shl
/
shr
/
sar
-立即。但如果您添加内容,最好使用nand、nor和xnor。也可以使用标量
abs
,以及
setcc r/m32
,以避免愚蠢的
xor
调零或
movzx
,您需要将布尔值化为32位整数。(当您使用时,
mov r/m32、sign\u extended\u imm8
也有助于提高代码密度,前提是您可以找到一个单字节的操作码,例如64位模式释放的操作码之一。)

有一大堆糟糕的或短视的设计决策,如果能够扭转(或者如果AVX修复了,那就更好了),例如,
cvtsi2sd xmm0,eax
合并到xmm0中,因此它具有错误的依赖性,导致GCC在xor ze上花费额外的insn
vpcmpeqd  xmm15, xmm15, xmm15
vpor      xmm0,  xmm0,  xmm1
vpandn    xmm0,  xmm0,  xmm15