Microcode in Intel's 8087 floating-point chip: the scale instruction
130 points • 2 days agoArticle Link

Intel 8087 于 1980 年问世,通过为浮点运算建立严格的标准而彻底改变了计算领域。 在此之前,业界的浮点实现常常不一致、精度不足。 8087 以高速且数学上准确的计算改变了这种局面,并最终为几乎所有现代计算机采用的 IEEE 754 标准奠定了基础。 芯片性能的核心是一套复杂的微代码引擎,它不仅完成常规算术,还处理大量棘手的边界情况。

要理解 8087 的工作方式,观察其架构很有帮助:指数采用 16 位数据通路,有效数字采用 64 位数据通路。芯片使用内部寄存器存储 80 位数值,其中包含一个符号位、 15 位指数和 64 位有效数字。架构的一个关键部件是微代码 ROM,其中含有 1648 条微指令。对这些指令的逆向工程揭示了芯片如何处理复杂任务,例如通过调整指数按二的幂缩放数值的 FSCALE 指令。

FSCALE 本身就是 8087 隐藏复杂性的典型例子。表面上它很简单,但在实现上需要超过 140 条微指令和多次子程序调用,以应对各种特殊情况。微代码必须处理零、无穷大和 Not-a-Number(NaN)等条目,以及溢出和下溢情形。这个过程包括用桶形移位器对浮点参数进行精确移位以转换为整数、更新指数,并通过指数转换器和加法器等内部电路谨慎管理舍入模式。

芯片的大量智能体现在处理"特殊"参数和非规范结果的子程序中。例如,为了在接近零时保持精度,8087 能处理非规格化数(denormalized numbers),这些数在有效数字中不含前导 1 。 8087 还采用了复杂的舍入机制,跟踪保护位、舍入位和粘滞位,以保证即便必须舍入,结果也尽可能精确,这也是它能成功取代基于软件的浮点仿真器的原因之一。

总的来说,8087 之所以成功,是因为它在保持高可靠性的同时,把大量数学计算从主处理器中卸载出来。通过专用硬件电路与详尽微代码的结合,它有效地收拾了早期浮点标准的混乱。如今,像 Opcode Collective 这样的团体持续进行的逆向工程工作,继续揭示这块开创性硬件如何为我们在现代计算中仍依赖的数值稳定性和性能奠定基础。

50 comments • Comments Link

• 8087 协处理器通过允许浮点运算与整数指令并行执行,为早期 x86 系统中的数学密集型应用带来了显著的性能提升。

• 早期 x86 微架构需要手工、指令级的调度来充分利用超标量能力,这与现代通过乱序执行透明管理流水线的方式形成鲜明对比。

• x87 架构由于基于栈的设计,对编译器而言具有独特挑战,需要复杂的寄存器交换、溢出管理以及对 80 位扩展精度值的不一致处理办法。

• 现代编译器与处理器更倾向于使用 SIMD 单元(SSE/AVX),因为它们提供标准化的单精度和双精度类型,避免了 x87 的 80 位栈所带来的不可控精度偏移。

• 80 位格式是一种设计取舍,旨在为高精度整数算术提供 64 位的有效数字(significand),在有效适配 10 字节块的同时保持尾数按 2 的幂次对齐。

• 虽然有时被称为 64 位处理器,但 8087 严格来说是一个依赖主 CPU 执行内存寻址等任务的协处理器,而像 IBM 7030 Stretch 和 Cray-1 这样的早期系统则奠定了 64 位计算的基础。

• 历史上有些遗留软件(例如旧版 .NET Framework)依赖 x87 进行数学运算,这说明尽管现代 SIMD 单元在性能上占优,业界长期以来仍不愿完全放弃对 x87 的支持。

• 早期计算器通常采用二进制编码十进制(BCD)和专用串行架构,以简化人类可读十进制与机器算术之间的转换。

• 8087 的内部实现采用垂直微码,每周期执行一条微指令来管理复杂算法,这比纯软件例程提供了显著的性能优势。

• Intel 为 8087 提供了一个庞大的软件仿真器,以便在缺乏硬件协处理器的系统上维持功能,不过那是用汇编语言完全重写的实现,而非对内部微码状态机的直接移植。

讨论侧重于浮点算术的发展和 x87 协处理器的架构特性。参与者回顾了 8087 在早期 x86 时代带来的显著性能提升,以及它对编译器设计和数值一致性带来的技术挑战。共识认为,尽管 8087 在实现高精度数学运算方面开创性很强,但其基于栈的 80 位架构长期来看过于特殊化,因此业界最终转向了更符合现代编译器预期和 64 位数据标准的 SSE 与 AVX 等基于 SIMD 的指令集。