为什么 X86 中没有 NAND、NOR 和 XNOR 指令? 操作码编码空间

如何解决为什么 X86 中没有 NAND、NOR 和 XNOR 指令? 操作码编码空间

  • 它们是您可以在计算机上执行的最简单的“指令”之一(它们是我亲自实施的第一个指令)
  • 执行 NOT(AND(x,y)) 使执行时间和依赖链长度和代码大小加倍
  • BMI1 引入了“andnot”,这是一个有意义的添加,是一个独特的操作 - 为什么不是这个问题标题中的那些?
  • 您通常会在“它们占用宝贵的操作码空间”这几行中阅读答案,但随后我查看了 AVX512 引入的所有 kmask 操作,顺便说一句,其中包括 NAND 和 XNOR ...... ....
  • 优化编译器可以生成更好的代码
  • 使用 SIMD 时情况变得更糟 => 没有 NOT 指令,这需要将执行时间、依赖链长度(编辑:
vpcmpeqd  xmm15,xmm15,xmm15
vpor      xmm0,xmm0,xmm1
vpandn    xmm0,xmm15

解决方法

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

请记住,大多数代码并不是在进行无分支的比特黑客。这只是在 8086 之后的几十年中在 SIMD 中变得更加普遍。我怀疑大多数程序员宁愿拥有 nor 而不是 {{1 }} (8086 没有空间留给更多遵循其正常模式的标准 ALU 指令编码1。)很多代码花费了大量时间比较和分支,循环数据结构(并停止记忆),或做“正常”数学。位操作代码当然存在,但很多代码并没有涉及太多。

在整个地方保存一两个指令会有所帮助,但前提是您可以使用这些新指令编译整个应用程序。 (虽然大多数 BMI1 和 BMI2 实际上都是这样,例如 SHLX/SHRX 用于 1-uop copy-and-shift-by-variable,但 Intel 仍然添加它们来修补非常糟糕的 3-uop shift-by-cl。 ) 如果您以特定服务器为目标,那很好(因此您可以使用 or 构建),但是许多 x86 代码是提前编译的,以便在随机消费者机器上使用。像 SSE 这样的扩展可以极大地加速单个循环,因此通常可以将单个函数分派到不同版本以利用它,同时保持较低的基线要求。

但是对于您建议的新添加版本的说明,它不会以这种方式工作,因此添加它们的好处要低得多。他们还没有出现,因为 8086 非常拥挤。

但大多数ISAS都没有这些,ARM没有,甚至PowerPC也没有,它选择在其32位指令字中使用编码空间来拥有大量操作码。 (包括诸如 -march=native 旋转和位范围掩码之类的整洁内容,以及其他位域插入/提取到任意位置的内容。)因此,这不仅仅是 8086 旧版 x86-64 的问题,也是最重要的CPU 架构师认为不值得为这些添加操作码,即使在具有大量空间的 RISC 中也是如此。

虽然MIPS 确实有一个rlwinm,而不是一个nor。 (MIPS not 对立即数进行零扩展,因此它不能用于非完整寄存器。)


SIMD 代码:

请注意,一旦您创建了一次全 1 向量,就可以在循环中重复使用它。大多数 SIMD 代码都在循环中,尽管对单个结构小心使用 SIMD 会很好。

SIMD 不仅为关键路径增加了 1 个周期,而且您的 NOR 实现总共有 2 个周期的延迟。在您的示例中, xori 不在关键路径上,并且几乎所有 CPU 上都不依赖于 reg 的旧值。 (不过仍然需要一个 SIMD 执行单元来编写这些)。它消耗吞吐量而不是延迟。对于给定的代码块,执行时间可能取决于吞吐量或延迟。 (How many CPU cycles are needed for each assembly instruction? (没那么简单) / What considerations go into predicting latency for operations on modern superscalar processors and how can I calculate them by hand?)

顺便说一句,编译器经常使用 pcmpeqd 而不是 vpxor;唯一的优点是使用内存源操作数,您可以在其中使用异或非加载,这与 vpandn 不同,其中可选内存操作数 (src2) 是未反转的操作数。 vpandn


标量代码

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

向 BMI1 或未来扩展中添加更多此类指令所带来的真正加速对于大多数一般工作负载(如 SPECint)来说可能(已经)相当小。

比整数 dst = ~src1 & src2 等更有价值的可能是非破坏性 VEX 版本的 常见 整数指令,例如 xnor,LEA 无法完成。所以很多 sub/mov 序列可能是 sub。也可能是 vsubimul、可能是 or,也可能是 and/shl/shr-立即数。但是确定如果您要添加东西,不妨使用 nand、nor 和 xnor。也许标量 sarabs 以避免愚蠢的 setcc r/m32-zeroing 或 xor 您需要布尔化为 32 位整数。 (当您在使用它时,如果您能为它找到一个一字节的操作码,例如 64 位模式释放的操作码之一,那么 movzx 也有利于代码密度。)

有一整套糟糕的或短视的设计决策,如果可以扭转它会很好(或者如果 AVX 修复会很好),例如mov r/m32,sign_extended_imm8 合并到 XMM0 中,因此它具有错误的依赖关系,导致 GCC 花费额外的 insn 对目标进行异或清零。 AVX 是为 VEX 版本改变这种行为的机会,也许可以通过为现有执行单元提供物理零注册作为合并目标来在内部处理。 (存在于 SnB 系列的物理寄存器文件中,这就是为什么在重命名时可以完全消除异或归零的原因,例如 mov-elimination。)但是不,英特尔尽可能地保留了旧版 SSE 版本的所有内容,保留那种目光短浅的奔腾 III 设计决策。 :((PIII 将 xmm regs 分成两个 64 位一半:只写低半部分对 SSE1 cvtsi2sd xmm0,eax 有好处。我猜英特尔继续在 P4 中合并 SSE2 cvtsi2ss 以保持一致性。 )


在 AVX-512 之前的某些 SIMD 版本中添加否定布尔指令可能是有意义的,例如 SSE4.1(它添加了一堆杂项整数,并使事情更加正交,并且被添加了。并且只在 45nm Core2 中添加,因此晶体管预算比 MMX 或 SSE1/2 天高很多),或 AVX(这为 VEX 开辟了很多编码空间) .

但既然它们没有,那么既然 cvtsi2sd 存在,添加它们就没什么意义了。除非英特尔打算创建新的传统 SSE 或仅 256 位的 VEX 扩展,而 AMD 可能希望实施这些扩展......

(即使在他们的 Silvermont 系列 CPU 和 Pentium/Celeron CPU 中,Legacy-SSE 也可以使用它,它们都不解码 VEX 前缀。这就是为什么不幸的是,即使 Skylake Pentiums 也禁用 BMI1/2 支持以及 AVX1/2/ FMA。这真的很愚蠢,这意味着我们离能够使用 BMI1/2 作为应在“现代桌面”上运行的提前编译的东西的基准还差得很远。)


操作码编码空间

VEX 有很多编码空间,掩码指令使用了它。此外,AVX-512 仅由高端 CPU 实现;英特尔的低功耗 Silvermont 系列 CPU 实施它需要很长时间。因此需要解码所有这些不同的 VEX 编码掩码指令是 AVX-512 CPU 必须处理的事情。

AVX-512(或前身)最初是为 Larrabee 设计的,这是一个变成 Xeon Phi 计算卡的 GPU 项目。因此,AVX-512 ISA 设计选择并不能完全反映您在设计时考虑了通用用途。尽管拥有大量相对较小的内核意味着您需要避免任何会导致解码器芯片面积膨胀或功耗过大的情况,因此这并非不合理。

但没有 VEX,x86 操作码空间非常拥挤(实际上 32 位模式下没有 1 字节的操作码,只剩下很少的 vpternlogdhttp://ref.x86asm.net/coder32.html)。英特尔(与 AMD 不同)仍然出于某种原因喜欢制造一些无法解码 VEX 前缀的 CPU。当然,他们可以改变这一点,并将 VEX 解码添加到 Silvermont,这样他们就可以在不支持 AVX(或所有 BMI2)的情况下使用 VEX 编码的整数指令。 (BMI2 包括 pext/pdep,在专用执行单元中快速实现它们的成本很高。AMD 选择对它们进行微编码,因此它们非常慢,但这让代码可以有效地使用其他 BMI2 指令。)

(不幸的是,CPU 无法(通过 CPUID)宣传它仅支持 128 位向量大小的 AVX 指令,这将允许更窄的 CPU 仍然获得非破坏性指令。OTOH,没有一些前向兼容代码在支持它的 CPU 上使用更广泛指令的方式,制作 128 位 AVX 代码以针对当前 CPU 进行优化可能最终被称为“足够好”,并且没有人费心为可以支持的 CPU 制作 256=位版本它。)

脚注 1:原始 8086 指令的操作码

对 8086 来说,仅仅解码每一个不同的操作码是一个挑战,每条 ALU 指令有大约 8 个不同的操作码:内存目标、内存源、直接源和特殊情况无 modrm AL/AX 形式。对于 8 位和 16 位版本的每个版本,时间为 2。加上0f xx。当然,直接形式可以使用 ModRM 中的 xnor r/m16,sign_extended_imm8 字段作为额外的操作码位,但是 /rxnor r/m8,r 以及 16 位形式需要 4 个单独的操作码字节,因此 { {1}} 和 xnor r,r/m8,所以每条指令有 6 个完整的操作码字节,加上一些重载的操作码 /constant

(半相关:https://codegolf.stackexchange.com/questions/132981/tips-for-golfing-in-x86-x64-machine-code/160739#160739 回复:短格式 AL、imm8 编码。)

您可以在原始 8086 操作码中看到的部分模式是,一位在 xnor al,imm8 目标与 xnor ax,imm16 源之间进行选择,另一位介于 8 和 16 位操作数大小( Is there a pattern to x86 op codes? (other than direction and size bits) / Are x86 opcodes arbitrary?)。因此,对于一些较少见的指令(例如通过省略 memory-dst 或 8 位形式)以不同的方式执行此操作可能会破坏模式,并且如果这样需要比标准模式更多的额外晶体管,以便在加载或寄存器获取后为 ALU 供电,或加载/铝/存储。

事实上,我认为 8086 没有足够的空间容纳更多支持所有标准形式(如 r/mr/m)的 ALU 指令。并且 8086 没有解码任何 add 操作码;后来用于扩展。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)> insert overwrite table dwd_trade_cart_add_inc > select data.id, > data.user_id, > data.course_id, > date_format(
错误1 hive (edu)> insert into huanhuan values(1,'haoge'); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive> show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 <configuration> <property> <name>yarn.nodemanager.res