与大多数 ISA 一样,x86 也在不断发展。
一些 ISA 通过重新定义现有的操作码(例如 MIPS64r6 这样做)来破坏向后兼容,但这种情况很少见。例如 MIPS32r6 / MIPS64r6 就是一个例子:https ://en.wikipedia.org/wiki/MIPS_architecture#MIPS32/MIPS64_Release_6重新定义了几种编码,以及删除了一些指令。
x86从未破坏向后兼容:Ryzen 或 Skylake-X 仍然可以启动和运行在 8086 上工作的机器代码。这就是 x86 CPU 的一部分含义:另请参阅x86 的开始:英特尔 8080 与英特尔 8086?. (我们只是在谈论机器代码,但如果您以传统 BIOS 模式而不是 UEFI 启动 PC,那么即使是 I/O 设备也会被模拟,因此像早期 DOS 这样的非常早期的 8086 PC 操作系统实际上可能会在本地运行。)
英特尔计划从其芯片组中删除一些传统的 IBM-PC 硬件仿真支持,如 PIC、PIT、A20 门。并且放弃对传统 BIOS 启动 (CSM) 的支持,只支持 UEFI,但 CPU 本身仍将支持切换回实模式。
英特尔和 AMD 将这一点发挥到了极致,以至于当前 CPU 上的 16 位和 32 位模式仍然支持像 SALC(类似但没有更新 FLAGS)这样的未记录的 8086 指令,sbb al,al
这会占用宝贵的操作码编码空间,这些空间可用于较短的编码新的指令。
但是使用新 insn 的 SW 仅适用于新 HW。新软件将在当前和未来的硬件上运行,而旧硬件则可以选择与之兼容。(例如,在 32 位代码中,您可能会避免使用cmov
Pentium Pro 的新指令或其他指令,因此您的代码可以在 P5 (i586) Pentium / PMMX 上运行。)
x86-64 设置了一个新基线,其中包括 SSE2 和 PPro 指令,如cmov
. 幸运的是,64 位代码不必担心与没有这些东西的旧 CPU 兼容,x86-64 需要它们。
包含 AVX2、FMA 和 BMI2(例如 Haswell)的新基线会非常好。如果您的编译器可以在整个代码中的任何地方使用 BMI1/BMI2 来实现更有效的变量计数移位指令等,而不仅仅是像 SIMD 指令那样的几个热循环,那么 BMI1/BMI2 尤其有用。但英特尔仍在销售不带 BMI2 的新 CPU(例如 Pentium/Celeron 版本的 Skylake/Coffee Lake。)
如果没有,那会发生什么?
CPU 不支持的指令通常会出现#UD
(UnDefined)错误。在类 Unix 操作系统上,您的进程将收到 SIGILL(非法指令信号。
(有趣的事实:原始 8086 没有 #UD 异常;每个字节序列都被解码为某种东西。)
制作一个能够利用新指令但不会在旧 CPU 上触发非法指令错误的二进制文件的唯一方法是执行运行时 CPU 检测和动态调度。一些编译器可以为您做到这一点。
新指令的编码可能(在旧 CPU 上)看起来像是不同指令的冗余前缀。例如lzcnt
,在不支持它的 CPU 上将解码为rep bsr
,它运行为bsr
. 并给出不同的结果lzcnt
!
(英特尔的文档明确指出,未来的 CPU 不能保证像当前 CPU 那样解码带有无意义前缀的指令。这为他们留下了以这种方式进行 ISA 扩展的空间。)
有时,对旧 CPU 上无意义的 REP 前缀的静默忽略对 ISA 扩展很有用。例如pause
是rep nop
。它在旧 CPU 上无害地解码非常有用,允许将其放置在自旋循环中而无需检查。类似地,硬件锁省略(事务性内存)解码为仍然可以在旧 CPU 上运行的代码,实际上是在执行原子操作而不是开始事务。
另见:停止指令集战争,由 Agner Fog 撰写。英特尔通过不发布即将推出的 ISA 扩展的详细信息来搞砸 AMD 的一些历史,因此 AMD 最终开发了自己的不兼容的产品,并花费了更多年时间来为自己的 CPU 添加对新扩展的支持。(例如,SSSE3 在 Bulldozer 之前的 AMD CPU 上不可用,这意味着即使是需要新计算机的游戏也不能将其作为基准多年,而 Phenom-II CPU 仍然存在。)
但是引入的新指令是否允许以前无法完成的事情?
8086 是图灵完备的(有限内存除外),因此“无法完成”的最重要形式是寻址更多内存:386 中的 32 位地址,x86 中的 64 位地址(错误 48 虚拟/52 物理)- 64. 但是这些都是通过引入全新的模式来实现的。他们还引入的新指令是另一回事。
但是,如果您的意思是“无法有效完成”:
是的,SIMD 是最重要的例子之一。MMX,然后是 SSE/SSE2,然后是 SSE4.x。然后 AVX 是两倍宽的向量。并行处理 16 或 32 字节数据的整个向量可以极大地加速strlen
或memcmp
与一次一个字节的循环相比。对于很多数组的东西也很有帮助。
AVX2 基于面具的最有效打包方式是什么?是新指令集启用的新技巧的一个有趣示例。例如,AVX512 内置了这个操作,而 AVX2 + BMI2 允许使用pdep
/的技巧,pext
这是以前不可能的。
SSSE3pshufb
是第一个可变控制 shuffle 指令,从查找表中加载 shuffle-control 可以实现以前无法有效实现的事情。例如从字符串获取 IPv4 地址的最快方法。
如何使用 SIMD 实现 atoi?还展示了您可以使用 x86 的pmaddubsw
/pmaddwd
整数乘法 + 水平加法指令执行的一些漂亮操作,以乘以小数位值。
在 8086 之后添加的新指令的早期历史很好地记录在NASM 手册附录的错误修复分支中。本附录的当前版本删除了每条指令的文本描述,以便为 SIMD 指令腾出空间。(有很多。)
A.5.118 IMUL: Signed Integer Multiply
IMUL r/m8 ; F6 /5 [8086]
IMUL r/m16 ; o16 F7 /5 [8086]
IMUL r/m32 ; o32 F7 /5 [386]
IMUL reg16,r/m16 ; o16 0F AF /r [386]
IMUL reg32,r/m32 ; o32 0F AF /r [386]
IMUL reg16,imm8 ; o16 6B /r ib [186]
IMUL reg16,imm16 ; o16 69 /r iw [186]
IMUL reg32,imm8 ; o32 6B /r ib [386]
IMUL reg32,imm32 ; o32 69 /r id [386]
IMUL reg16,r/m16,imm8 ; o16 6B /r ib [186]
IMUL reg16,r/m16,imm16 ; o16 69 /r iw [186]
IMUL reg32,r/m32,imm8 ; o32 6B /r ib [386]
IMUL reg32,r/m32,imm32 ; o32 69 /r id [386]
当然,任何 reg32 指令都需要 386 来进行 32 位扩展,但请注意 imul-immediate 在 186 ( imul cx, [bx], 123
) 中是新的,而 2 操作数 imul 在 386 ( imul cx, [bx]
)中是新的,允许在不破坏 DX:AX 的情况下进行乘法运算,从而使 AX 不那么“特殊” ”。
其他 386 条指令在使寄存器更加正交方面也大有帮助,让您可以有效地对任何寄存器进行符号扩展movsx
。movzx
在此之前,您必须将数据输入 AL 并使用cbw
,或输入 AXcwd
以签名扩展为 DX:AX。