从程序员的角度来看,'new' 处理器中的 'new' 是什么
What's 'new' in a 'new' processor when viewed from programmer's point
我最近对了解低级计算很感兴趣。据我所知,当今广泛使用的计算机遵循 x86/x86-64 体系结构。
据我了解,架构,更具体地说,指令集架构 (ISA) 是程序员能够向 CPU 发出的指令集。
第一个问题,ISA是不断发展还是保持不变?
我认为它在不断发展(意味着新指令不断修改 added/previous 指令?)但是旧处理器如何能够执行用新指令编写的代码? (它不知道新指令,但应该能够执行代码,因为它具有 x86 架构)。编译器处理这个东西还是处理器?基本上相同的指令集合如何能够 运行 在所有处理器上,旧的还是新的?
最后,除了不是程序员关心的微体系结构(如果我错了请纠正我),程序员在处理新处理器时会看到哪些变化?由于微体系结构的变化,旧指令可能 运行 由于高效实施而速度很快。但是引入新的指令是为了允许以前不能做的事情吗?或者以前可以用一堆指令完成什么,但现在由于硬件的变化可以用一个指令完成?新寄存器?还有什么吗?
它是否做了类似的事情 - 如果处理器支持这个新的强大指令以加快执行速度,则使用新指令否则回退到较慢的旧指令。如果是,谁来执行这个 if - else 子句?编译器?如果不是,那会发生什么?
与大多数 ISA 一样,x86 也在不断发展。
一些 ISA 通过重新定义现有的操作码来打破向后兼容(例如 MIPS64r6 就是这样做的),但这种情况很少见。例如MIPS32r6 / MIPS64r6 就是一个例子:https://en.wikipedia.org/wiki/MIPS_architecture#MIPS32/MIPS64_Release_6 重新定义了几种编码,并删除了一些指令。
x86 从来没有 向后兼容:Ryzen 或 Skylake-X 仍然可以启动并且 运行 机器代码可以在 8086。这就是成为 x86 CPU 的部分含义:另请参见 The start of x86: Intel 8080 vs Intel 8086?。 (我们只是在谈论机器代码,但如果您以传统 BIOS 模式而不是 UEFI 启动 PC,甚至 I/O 设备也会被模拟,所以很早的 8086 PC OS 比如早期的 DOS 实际上可能 运行 本机。)
英特尔是,像PIC,PIT,A20门。并且放弃对传统 BIOS 启动 (CSM) 的支持,只支持 UEFI,但 CPUs 本身仍将支持切换回实模式。
Intel 和 AMD 将此发挥到了极致,未记录 8086 指令如 SALC(如 sbb al,al
但未更新 FLAGS)在 16 和 32 中仍然受支持-当前 CPUs 上的位模式,用完了有价值的操作码编码 space,可用于新指令的较短编码。
但是使用新insns的软件只适用于新的硬件。新软件将 运行 在当前和未来的硬件上,而旧硬件只要它选择兼容。 (例如,在 32 位代码中,您可能会避免使用 cmov
或 Pentium Pro 新增的其他指令,因此您的代码可以在 P5 (i586) Pentium / PMMX 上 运行。)
x86-64 设置了一个新基线,其中包括 SSE2 和 PPro 指令,如 cmov
。所以幸运的是,64 位代码不必担心与没有这些东西的旧 CPU 兼容,x86-64 需要它们。
包含 AVX2、FMA 和 BMI2(例如 Haswell)的新基线会非常好。 BMI1/BMI2 如果您的编译器可以在您的代码中的任何地方使用它们以获得更高效的变量计数移位指令等,而不只是像 SIMD 指令那样的几个热循环,那么它们尤其有用。但英特尔仍在销售不带 BMI2 的新 CPUs(例如 Pentium/Celeron 版本的 Skylake / Coffee Lake。)
If no, then what happens?
CPU 不支持的指令通常会因 #UD
(未定义) 而出错。在类 Unix OSes 上,您的进程将收到 SIGILL(非法指令信号。
(有趣的事实:original 8086 didn't have a #UD exception;每个字节序列都被解码为某物。)
制作一个二进制文件将利用新指令但不会在旧 CPUs 上触发非法指令错误的唯一方法是进行 运行time CPU 检测和动态调度。一些编译器可以为您完成。
新指令的编码(在旧 CPUs 上)看起来像是不同指令的冗余前缀。例如lzcnt
在不支持它的 CPU 上将解码为 rep bsr
,而 运行 则解码为 bsr
。并给出与 lzcnt
!
不同的结果
(Intel 的文档明确指出,未来的 CPU 不能保证像当前 CPU 那样解码带有无意义前缀的指令。这为他们留下了空间来以这种方式进行 ISA 扩展.)
有时,对旧 CPUs 上无意义的 REP 前缀的静默忽略对 ISA 扩展很有用。例如pause
是 rep nop
。它在旧的 CPUs 上无害地解码是非常有用的,允许它被放置在自旋循环中而无需检查。类似地,硬件锁省略(事务内存)解码为仍然适用于旧 CPU 的代码,实际上执行原子操作而不是开始事务。
另请参阅:Stop the instruction set war,作者:Agner Fog。英特尔通过不发布即将推出的 ISA 扩展的详细信息来搞砸 AMD 的一些历史,因此 AMD 最终开发了他们自己的不兼容的扩展,并花了更多年的时间来添加对他们自己的 CPU 的新扩展的支持。 (例如,SSSE3 在 Bulldozer 之前的 AMD CPUs 上不可用,这意味着即使是需要新计算机的游戏也不能将它作为多年的基准,而 Phenom-II CPUs 是还在。)
But are the new instructions introduced to allow what couldn't be done previously?
8086 是图灵完备的(有限内存除外)所以 "couldn't be done" 最重要的形式是寻址更多内存:386 中的 32 位地址,64 位地址(错误的 48 虚拟/52 物理)在 x86-64 中。但是那些是通过引入全新的模式来实现的;他们还引入的新指令是另一回事。
但是如果你的意思是"couldn't be done efficiently":
是的,SIMD 是最重要的示例之一。 MMX,然后 SSE/SSE2,然后 SSE4.x。然后是两倍宽的向量的 AVX。并行处理 16 或 32 字节数据的整个向量可以大大加快 strlen
或 memcmp
之类的东西,而不是一次一个字节的循环。对很多数组内容也很有帮助。
是新指令集启用新技巧的一个有趣示例。例如AVX512 内置了这个操作,而 AVX2 + BMI2 允许 pdep
/pext
的技巧,这在以前是不可能的。
SSSE3 pshufb
是第一个变量控制洗牌指令,并且从查找 table 加载洗牌控制允许以前不可能有效的事情。例如.
还展示了一些您可以使用 x86 的 pmaddubsw
/ pmaddwd
整数乘法 + 水平加法指令执行的一些漂亮的事情,以乘以小数位值。
本附录的 a bugfixed fork of an appendix of the NASM manual. The current version 中详细记录了在 8086 之后添加新指令的早期历史,删除了每条指令的文本描述以为 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]
当然,对于 32 位扩展,任何 reg32 指令都需要 386,但是 请注意 imul-immediate 在 186 (imul cx, [bx], 123
) 中是新的,而 2-operand imul 在 386 ( imul cx, [bx]
),允许乘法而不破坏 DX:AX,使 AX 更少 "special"。
其他 386 条指令,如 movsx
和 movzx
也大大有助于使寄存器更加正交,让您可以有效地对任何寄存器进行符号扩展。在此之前,您必须将数据导入 AL 并使用 cbw
,或导入 AX 以供 cwd
签名扩展到 DX:AX.
我最近对了解低级计算很感兴趣。据我所知,当今广泛使用的计算机遵循 x86/x86-64 体系结构。
据我了解,架构,更具体地说,指令集架构 (ISA) 是程序员能够向 CPU 发出的指令集。
第一个问题,ISA是不断发展还是保持不变?
我认为它在不断发展(意味着新指令不断修改 added/previous 指令?)但是旧处理器如何能够执行用新指令编写的代码? (它不知道新指令,但应该能够执行代码,因为它具有 x86 架构)。编译器处理这个东西还是处理器?基本上相同的指令集合如何能够 运行 在所有处理器上,旧的还是新的?
最后,除了不是程序员关心的微体系结构(如果我错了请纠正我),程序员在处理新处理器时会看到哪些变化?由于微体系结构的变化,旧指令可能 运行 由于高效实施而速度很快。但是引入新的指令是为了允许以前不能做的事情吗?或者以前可以用一堆指令完成什么,但现在由于硬件的变化可以用一个指令完成?新寄存器?还有什么吗?
它是否做了类似的事情 - 如果处理器支持这个新的强大指令以加快执行速度,则使用新指令否则回退到较慢的旧指令。如果是,谁来执行这个 if - else 子句?编译器?如果不是,那会发生什么?
与大多数 ISA 一样,x86 也在不断发展。
一些 ISA 通过重新定义现有的操作码来打破向后兼容(例如 MIPS64r6 就是这样做的),但这种情况很少见。例如MIPS32r6 / MIPS64r6 就是一个例子:https://en.wikipedia.org/wiki/MIPS_architecture#MIPS32/MIPS64_Release_6 重新定义了几种编码,并删除了一些指令。
x86 从来没有 向后兼容:Ryzen 或 Skylake-X 仍然可以启动并且 运行 机器代码可以在 8086。这就是成为 x86 CPU 的部分含义:另请参见 The start of x86: Intel 8080 vs Intel 8086?。 (我们只是在谈论机器代码,但如果您以传统 BIOS 模式而不是 UEFI 启动 PC,甚至 I/O 设备也会被模拟,所以很早的 8086 PC OS 比如早期的 DOS 实际上可能 运行 本机。)
英特尔是
Intel 和 AMD 将此发挥到了极致,未记录 8086 指令如 SALC(如 sbb al,al
但未更新 FLAGS)在 16 和 32 中仍然受支持-当前 CPUs 上的位模式,用完了有价值的操作码编码 space,可用于新指令的较短编码。
但是使用新insns的软件只适用于新的硬件。新软件将 运行 在当前和未来的硬件上,而旧硬件只要它选择兼容。 (例如,在 32 位代码中,您可能会避免使用 cmov
或 Pentium Pro 新增的其他指令,因此您的代码可以在 P5 (i586) Pentium / PMMX 上 运行。)
x86-64 设置了一个新基线,其中包括 SSE2 和 PPro 指令,如 cmov
。所以幸运的是,64 位代码不必担心与没有这些东西的旧 CPU 兼容,x86-64 需要它们。
包含 AVX2、FMA 和 BMI2(例如 Haswell)的新基线会非常好。 BMI1/BMI2 如果您的编译器可以在您的代码中的任何地方使用它们以获得更高效的变量计数移位指令等,而不只是像 SIMD 指令那样的几个热循环,那么它们尤其有用。但英特尔仍在销售不带 BMI2 的新 CPUs(例如 Pentium/Celeron 版本的 Skylake / Coffee Lake。)
If no, then what happens?
CPU 不支持的指令通常会因 #UD
(未定义) 而出错。在类 Unix OSes 上,您的进程将收到 SIGILL(非法指令信号。
(有趣的事实:original 8086 didn't have a #UD exception;每个字节序列都被解码为某物。)
制作一个二进制文件将利用新指令但不会在旧 CPUs 上触发非法指令错误的唯一方法是进行 运行time CPU 检测和动态调度。一些编译器可以为您完成。
新指令的编码(在旧 CPUs 上)看起来像是不同指令的冗余前缀。例如lzcnt
在不支持它的 CPU 上将解码为 rep bsr
,而 运行 则解码为 bsr
。并给出与 lzcnt
!
(Intel 的文档明确指出,未来的 CPU 不能保证像当前 CPU 那样解码带有无意义前缀的指令。这为他们留下了空间来以这种方式进行 ISA 扩展.)
有时,对旧 CPUs 上无意义的 REP 前缀的静默忽略对 ISA 扩展很有用。例如pause
是 rep nop
。它在旧的 CPUs 上无害地解码是非常有用的,允许它被放置在自旋循环中而无需检查。类似地,硬件锁省略(事务内存)解码为仍然适用于旧 CPU 的代码,实际上执行原子操作而不是开始事务。
另请参阅:Stop the instruction set war,作者:Agner Fog。英特尔通过不发布即将推出的 ISA 扩展的详细信息来搞砸 AMD 的一些历史,因此 AMD 最终开发了他们自己的不兼容的扩展,并花了更多年的时间来添加对他们自己的 CPU 的新扩展的支持。 (例如,SSSE3 在 Bulldozer 之前的 AMD CPUs 上不可用,这意味着即使是需要新计算机的游戏也不能将它作为多年的基准,而 Phenom-II CPUs 是还在。)
But are the new instructions introduced to allow what couldn't be done previously?
8086 是图灵完备的(有限内存除外)所以 "couldn't be done" 最重要的形式是寻址更多内存:386 中的 32 位地址,64 位地址(错误的 48 虚拟/52 物理)在 x86-64 中。但是那些是通过引入全新的模式来实现的;他们还引入的新指令是另一回事。
但是如果你的意思是"couldn't be done efficiently":
是的,SIMD 是最重要的示例之一。 MMX,然后 SSE/SSE2,然后 SSE4.x。然后是两倍宽的向量的 AVX。并行处理 16 或 32 字节数据的整个向量可以大大加快 strlen
或 memcmp
之类的东西,而不是一次一个字节的循环。对很多数组内容也很有帮助。
pdep
/pext
的技巧,这在以前是不可能的。
SSSE3 pshufb
是第一个变量控制洗牌指令,并且从查找 table 加载洗牌控制允许以前不可能有效的事情。例如
pmaddubsw
/ pmaddwd
整数乘法 + 水平加法指令执行的一些漂亮的事情,以乘以小数位值。
本附录的 a bugfixed fork of an appendix of the NASM manual. The current version 中详细记录了在 8086 之后添加新指令的早期历史,删除了每条指令的文本描述以为 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]
当然,对于 32 位扩展,任何 reg32 指令都需要 386,但是 请注意 imul-immediate 在 186 (imul cx, [bx], 123
) 中是新的,而 2-operand imul 在 386 ( imul cx, [bx]
),允许乘法而不破坏 DX:AX,使 AX 更少 "special"。
其他 386 条指令,如 movsx
和 movzx
也大大有助于使寄存器更加正交,让您可以有效地对任何寄存器进行符号扩展。在此之前,您必须将数据导入 AL 并使用 cbw
,或导入 AX 以供 cwd
签名扩展到 DX:AX.