ChatGPT解决这个技术问题 Extra ChatGPT

为什么x86难看?为什么与其他人相比,它被认为是劣等的? [关闭]

就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、参考资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您认为此问题可以改进并可能重新打开,请访问帮助中心获取指导。 10年前关闭。

我一直在阅读一些 SO 档案,并遇到了针对 x86 架构的声明。

为什么我们需要不同的 CPU 架构用于服务器、小型/大型机和混合内核?说“PC 架构是一团糟,任何操作系统开发人员都会告诉你这一点。”

学习汇编语言值得努力吗? (存档)说“意识到 x86 架构充其量是可怕的”

学习 x86 汇编程序的任何简单方法?说“大多数大学都教授 MIPS 之类的汇编,因为它更容易理解,x86 汇编真的很丑”

还有更多评论,例如

“与大多数架构相比,X86 非常糟糕。”

“X86 不如 MIPS、SPARC 和 PowerPC 绝对是传统观念”

“x86 很丑”

我尝试搜索但没有找到任何原因。我不觉得 x86 不好,可能是因为这是我熟悉的唯一架构。

有人可以给我考虑 x86 与其他人相比丑/差/劣的理由吗?

到目前为止,我将根据答案选择 S&A,但我会顺便指出,对于 m68k 指令集而言,CISC 不是问题。 x86 就是这样,你可以保留它。
什么是“S&A”? “ CISC 对 m68k 指令集来说不是问题。” - 为什么不?
motorala 68000 系列芯片具有高度 CISC 架构,但它们具有统一、相当正交且非常简单的指令集。为什么与 x86 不同?我不知道。但请注意,芯片的复杂性和指令集的复杂性(即汇编程序员看到的接口)之间存在很大差异。
+1 一个非常有趣的问题。
最近对不同处理器能效的研究发现在这里,并很好地讨论了推动 CISC 和RISC 设计。 extremetech.com/extreme/…

P
Peter Cordes

几个可能的原因:

x86 是一个相对较旧的 ISA(毕竟它的祖先是 8086) x86 已经经历了数次显着发展,但需要硬件来保持与旧二进制文件的向后兼容性。例如,现代 x86 硬件仍然包含对本机运行 16 位代码的支持。此外,存在几种内存寻址模型以允许旧代码在同一处理器上互操作,例如实模式、保护模式、虚拟 8086 模式和 (amd64) 长模式。这可能会让一些人感到困惑。 x86 是 CISC 机器。长期以来,这意味着它比 MIPS 或 ARM 等 RISC 机器慢,因为指令具有数据相互依赖性和标志,使得大多数形式的指令级并行性难以实现。现代实现将 x86 指令转换为类似于 RISC 的指令,称为“微操作”,以使这些优化在硬件中实现实用。在某些方面,x86 并不逊色,只是不同而已。例如,输入/输出在绝大多数架构上被处理为内存映射,但在 x86 上却没有。 (注意:现代 x86 机器通常具有某种形式的 DMA 支持,并通过内存映射与其他硬件进行通信;但 ISA 仍然具有 IN 和 OUT 等 I/O 指令) x86 ISA 具有很少的架构寄存器,这可以强制程序在内存中往返的频率高于其他情况所需的频率。执行此操作所需的额外指令占用了可用于有用工作的执行资源,尽管有效的存储转发可保持较低的延迟。将寄存器重命名到大型物理寄存器文件的现代实现可以使许多指令保持运行,但缺乏架构寄存器仍然是 32 位 x86 的一个重大弱点。 x86-64 将整数和向量寄存器从 8 个增加到 16 个,这是 64 位代码比 32 位更快(以及更有效的寄存器调用 ABI)的最大因素之一,而不是每个寄存器的宽度增加。将整数寄存器从 16 个进一步增加到 32 个会有所帮助,但作用不大。 (不过,AVX512 确实增加到 32 个向量寄存器,因为浮点代码具有更高的延迟并且通常需要更多的常量。)(见注释)x86 汇编代码很复杂,因为 x86 是一个具有许多特性的复杂架构。典型 MIPS 机器的指令列表适合单字母大小的纸张。 x86 的等效列表占据了好几页,并且说明只是做更多的事情,因此您通常需要对它们的作用进行更大的解释,而不是列表所能提供的。例如,MOVSB 指令需要一个相对较大的 C 代码块来描述它的作用: if (DF==0) *(byte*)DI++ = *(byte*)SI++;否则 *(byte*)DI-- = *(byte*)SI--;这是一个执行加载、存储和两个加法或减法(由标志输入控制)的指令,每一条指令都是 RISC 机器上的单独指令。虽然 MIPS(和类似的体系结构)的简单性并不一定会让它们变得更好,但对于教授汇编程序类的介绍来说,从更简单的 ISA 开始是有意义的。一些汇编课程教授 x86 的超简化子集,称为 y86,它被简化到对实际使用无用的程度(例如,没有移位指令),或者有些只教授基本的 x86 指令。 x86 使用可变长度操作码,这增加了指令解析方面的硬件复杂性。在现代,随着 CPU 越来越受到内存带宽的限制而不是原始计算的限制,这个成本变得越来越小,但是许多“抨击 x86”的文章和态度来自这个成本相对更大的时代。 2016 年更新:Anandtech 发布了关于 x64 和 AArch64 下操作码大小的讨论。

编辑:这不应该是 x86 的 bash!派对。鉴于问题的措辞,我别无选择,只能进行一些抨击。但是除了(1)之外,所有这些事情都是有充分理由的(见评论)。英特尔设计师并不愚蠢——他们想通过他们的架构实现一些目标,而这些是他们为使这些目标成为现实而必须支付的一些税款。


这是一个权衡。它的优点在于二进制大小可能更小,但缺点在于您需要非常复杂的硬件来实现这些指令的解析器。无论如何,绝大多数指令的大小都是相同的——x86 上可变长度操作码的大部分原因是当他们决定添加功能并发现他们无法用必须使用的位数表示他们想要的内容时.绝大多数人对二进制大小的关注程度不及硬件复杂性或功耗。
@Joey Adams:将 x86 的可变长度指令与 ARM 的拇指模式( en.wikipedia.org/wiki/ARM_architecture#Thumb )进行对比。拇指模式导致 ARM 的目标代码明显更小,因为较短的指令直接映射到普通指令。但由于较大的指令和较小的指令之间存在 1:1 的映射关系,因此解析硬件很容易实现。 x86 的可变长度指令没有这些好处,因为它们一开始就不是这样设计的。
(6) 不是每个程序都需要使用每个操作码,但是该死的,当我需要 SSE3 时,我很高兴我拥有它。
@Chris Kaminski:这对硬件有什么影响?当然,在现代全尺寸计算机上没有人会关心,但如果我正在制造手机之类的东西,我更关心功耗比其他任何事情都重要。可变长度操作码不会增加执行时间,但解码硬件仍然需要电源才能运行。
这是使 x86 指令集如此丑陋的原因之一,因为它无法确定它是累加器还是基于寄存器文件的体系结构(尽管这主要由 386 修复,这使得指令集更加正交,不管 68k 粉丝告诉你什么)。
d
dthorpe

在我看来,对 x86 的主要打击是它的 CISC 起源——指令集包含许多隐含的相互依赖关系。这些相互依赖使得在芯片上执行指令重新排序等事情变得困难,因为必须为每条指令保留这些相互依赖的工件和语义。

例如,大多数 x86 整数加减指令会修改标志寄存器。在执行加法或减法之后,下一个操作通常是查看标志寄存器以检查溢出、符号位等。如果之后还有另一个加法,很难判断开始执行第二次加法是否安全在第一次添加的结果已知之前。

在 RISC 体系结构上,add 指令将指定输入操作数和输出寄存器,并且有关操作的所有内容都将仅使用这些寄存器进行。这使得解耦彼此靠近的添加操作变得更加容易,因为没有bloomin'标志寄存器强制所有内容排队并执行单个文件。

DEC Alpha AXP 芯片是 MIPS 风格的 RISC 设计,在可用指令方面非常简陋,但指令集旨在避免指令间隐式寄存器依赖性。没有硬件定义的堆栈寄存器。没有硬件定义的标志寄存器。甚至指令指针也是操作系统定义的——如果你想返回给调用者,你必须弄清楚调用者将如何让你知道要返回的地址。这通常由操作系统调用约定定义。但是,在 x86 上,它是由芯片硬件定义的。

无论如何,经过 3 或 4 代 Alpha AXP 芯片设计,硬件从具有 32 个 int 寄存器和 32 个浮点寄存器的 spartan 指令集的文字实现变成了具有 80 个内部寄存器的大规模无序执行引擎,寄存器重命名,结果转发(前一条指令的结果被转发到取决于值的后一条指令)和各种疯狂和疯狂的性能提升器。加上所有这些花里胡哨的东西,AXP 芯片芯片仍然比当时可比的 Pentium 芯片芯片小得多,而且 AXP 快得多。

在 x86 家族树中,你看不到那种性能提升的爆发,主要是因为 x86 指令集的复杂性使得许多类型的执行优化即使不是不可能也非常昂贵。英特尔的天才之处在于放弃在硬件中实现 x86 指令集——所有现代 x86 芯片实际上都是 RISC 内核,在一定程度上解释 x86 指令,将它们翻译成内部微码,保留原始 x86 的所有语义指令,但允许对微码进行一些 RISC 乱序和其他优化。

我已经编写了很多 x86 汇编程序,并且可以充分体会到它的 CISC 根源的便利性。但是直到我花了一些时间编写 Alpha AXP 汇编程序之后,我才完全意识到 x86 有多么复杂。 AXP 的简单性和统一性让我大吃一惊。差异是巨大的,深刻的。


除非你能解释 m68k,否则我不会听对 CISC 本身的抨击。
我对m68k不熟悉,所以我不能批评它。
我认为这个答案还不够糟糕,无法投票,但我确实认为整个“RISC 比 CISC 更小更快”的论点在现代并不真正相关。当然,AXP 在当时可能要快得多,但事实是现代 RISC 和现代 CISC 在性能方面大致相同。正如我在回答中所说,x86 解码的轻微功率损失是不将 x86 用于手机等设备的原因,但对于全尺寸台式机或笔记本电脑来说,这没什么好争论的。
@Billy:大小不仅仅是代码大小或指令大小。英特尔在芯片表面积上付出了相当大的代价来实现所有这些特殊指令的硬件逻辑,无论是否在引擎盖下的 RISC 微码核心。模具的尺寸直接影响制造成本,因此它仍然是现代系统设计的一个有效关注点。
arstechnica 的 Jon Stokes 有一篇文章说,用于 x86-RISC 转换的晶体管数量基本保持不变,这意味着它的相对尺寸与芯片中的晶体管总数相比已经缩小:arstechnica.com/old/content/2004/07/pentium-1.ars/2
s
staticsan

x86 体系结构可以追溯到 8008 微处理器及其相关产品的设计。这些 CPU 是在内存很慢的时候设计的,如果你可以在 CPU 芯片上完成它,它通常会快很多。但是,CPU 芯片空间也很昂贵。这两个原因是为什么只有少数寄存器往往具有特殊用途,以及具有各种陷阱和限制的复杂指令集。

同一时代的其他处理器(例如 6502 系列)也有类似的限制和怪癖。有趣的是,8008 系列和 6502 系列都旨在用作嵌入式控制器。即使在那个时候,嵌入式控制器也被期望用汇编程序进行编程,并且在许多方面迎合汇编程序员而不是编译器编写者。 (看看 VAX 芯片,当你迎合编译器编写时会发生什么。)设计者没想到它们会成为通用计算平台。这就是 POWER 架构的前身之类的东西。当然,家用电脑革命改变了这一点。


+1 是来自实际上似乎对该问题具有历史背景的人的唯一答案。
记忆一直很慢。今天可能(相对而言)比我在 1982 年开始使用 Z80s 和 CP/M 时要慢。灭绝并不是唯一的进化路径,因为随着灭绝,特定的进化方向就会停止。我会说 x86 在其 28 年(迄今为止存在)中适应得很好。
在 8086 时代,内存速度一度接近与 CPU 相当。德州仪器 (TI) 的 9900 的设计只有在这种情况下才有效。但随后 CPU 再次领先并一直保持在那里。只是现在,有缓存来帮助管理这个。
@Olof Forshell:它与汇编程序兼容,因为 8080 汇编代码可以转换为 8086 代码。从这个角度来看,它是 8080 plus 扩展,就像您可以将 8080 视为 8008 plus 扩展一样。
@Olof Forshell:除了 8086 是为此而设计的。它是 8080 的扩展,大多数(可能所有)8080 指令都是一对一映射的,具有明显相似的语义。 IBM 360 架构并非如此,无论您想以哪种方式推动它。
O
Olof Forshell

我这里有几个额外的方面:

考虑操作“a=b/c”x86 将其实现为

  mov eax,b
  xor edx,edx
  div dword ptr c
  mov a,eax

作为 div 指令的额外奖励,edx 将包含其余部分。

RISC 处理器需要首先加载 b 和 c 的地址,将 b 和 c 从内存加载到寄存器,进行除法并加载 a 的地址,然后存储结果。 dst,src 语法:

  mov r5,addr b
  mov r5,[r5]
  mov r6,addr c
  mov r6,[r6]
  div r7,r5,r6
  mov r5,addr a
  mov [r5],r7

这里通常不会有余数。

如果要通过指针加载任何变量,则两个序列可能会变得更长,尽管这对于 RISC 来说不太可能,因为它可能已经在另一个寄存器中加载了一个或多个指针。 x86 的寄存器较少,因此指针位于其中之一的可能性较小。

优点和缺点:

RISC 指令可以与周围的代码混合以改进指令调度,这在 x86 中不太可能,而是在 CPU 本身内部完成这项工作(或多或少取决于序列)。在 32 位架构上,上述 RISC 序列通常为 28 字节长(7 条 32 位/4 字节宽的指令)。这将导致片外存储器在获取指令(七次获取)时工作得更多。更密集的 x86 序列包含更少的指令,尽管它们的宽度各不相同,但您可能也在其中查看平均 4 个字节/指令。即使您有指令高速缓存来加快七次取指的速度,也意味着与 x86 相比,您将在其他地方有 3 次的不足来弥补。

x86 架构具有更少的寄存器来保存/恢复意味着它可能会比 RISC 更快地进行线程切换和处理中断。更多的寄存器来保存和恢复需要更多的临时 RAM 堆栈空间来执行中断和更多的永久堆栈空间来存储线程状态。这些方面应该使 x86 成为运行纯 RTOS 的更好候选者。

就个人而言,我发现编写 RISC 程序集比 x86 更难。我通过用 C 语言编写 RISC 例程、编译和修改生成的代码来解决这个问题。从代码生产的角度来看,这更有效,而从执行的角度来看,这可能效率较低。所有需要跟踪的 32 个寄存器。对于 x86,情况正好相反:6-8 个具有“真实”名称的寄存器使问题更易于管理,并且更加确信生成的代码将按预期工作。

丑陋的?那是在旁观者的眼中。我更喜欢“不同”。


我的示例中的 a、b 和 c 应被视为基于内存的变量,而不是立即值。
...“dword ptr”用于指定大小未知的变量的大小,例如,它是否被简单地声明为外部或如果你一直很懒惰。
这不是我第一次听到建议先用 C 语言编写它,然后再将它提炼成汇编程序。这绝对有帮助
在早期,所有处理器都是 RISC。 CISC 的出现是针对非常慢的铁芯内存系统的一种缓解策略,因此 CISC 具有更少、更强大的指令,对内存子系统施加的压力更小,并更好地利用了带宽。同样,寄存器最初被认为是用于进行累加的片上 CPU 内存位置。我最后一次认真地对 RISC 机器进行基准测试是 1993 年——SPARC 和 HP Prisim。 SPARC 整体上都很糟糕。 Prisim 在 add/sub/mul 上的速度是 486 的 20 倍,但在先验方面却很糟糕。 CISC更好。
@OlofForshell 你说 there typically won't be a reminder 但 wiki 说 mips 有它:en.wikipedia.org/wiki/MIPS_instruction_set#Integer
R
R.. GitHub STOP HELPING ICE

我认为这个问题有一个错误的假设。主要只是痴迷于 RISC 的学者称 x86 丑陋。实际上,x86 ISA 可以在单指令操作中完成,这在 RISC ISA 上需要 5-6 条指令。 RISC 粉丝可能会反驳说,现代 x86 CPU 将这些“复杂”指令分解为微操作;然而:

在许多情况下,这只是部分正确或根本不正确。 x86 中最有用的“复杂”指令是诸如 mov %eax, 0x1c(%esp,%edi,4) 之类的东西,即寻址模式,这些都没有分解。在现代机器上,通常更重要的不是花费的周期数(因为大多数任务都不受 CPU 限制),而是代码的指令缓存影响。 5-6 条固定大小(通常为 32 位)指令对缓存的影响远大于一条很少超过 5 字节的复杂指令。

大约 10 到 15 年前,x86 真正吸收了 RISC 的所有优点,而 RISC 的其余特性(实际上是定义的特性 - 最小指令集)是有害且不可取的。

除了制造 CPU 的成本和复杂性以及它们的能源需求之外,x86 是最好的 ISA。任何告诉你其他情况的人都会让意识形态或议程妨碍他们的推理。

另一方面,如果您针对的是 CPU 成本很重要的嵌入式设备,或者是能耗是首要问题的嵌入式/移动设备,那么 ARM 或 MIPS 可能更有意义。请记住,尽管您仍然需要处理处理轻松 3-4 倍大的代码所需的额外内存和二进制大小,并且您将无法接近性能。这是否重要在很大程度上取决于您将在其上运行什么。


在能耗是头等大事的情况下,ARM 或 MIPS 可能更有意义……所以,如果至少有一个方面 ARM 或 MIPS 更有意义,难道 x86 不一定是最好的 ISA?
这就是为什么我用“除了成本......以及他们的能源需求”来限定“最好的”。
我认为英特尔降低 CPU 速度和更小的芯片尺寸在很大程度上消除了功率差异。具有 64k L1 和 1MB L2 高速缓存的新型 Celeron 双 64 位 CPU 是一个 7.5 瓦的芯片。这是我的“星巴克”聚会机器,电池寿命长得离谱,可以在 P6 机器周围运行。作为一个主要从事浮点计算的人,我很久以前就放弃了 RISC。它只是爬行。尤其是 SPARC 的冰川非常严重。 RISC 糟糕的一个完美例子是英特尔 i860 CPU。英特尔再也没有去过那里。
@RocketRoy:7.5 瓦对于 24/7 供电(并且始终不执行有用计算)或使用 3.7v/2000mAh 电池的设备来说是不可接受的。
@RocketRoy“英特尔 i860 CPU。英特尔再也没有去过那里。”经过一番研究,i860 听起来很像安腾:VLIW,编译器顺序指令并行......
P
Peter Cordes

x86 汇编语言还不错。当你得到机器代码时,它开始变得非常难看。指令编码、寻址模式等比大多数 RISC CPU 的要复杂得多。并且为了向后兼容的目的,内置了额外的乐趣——只有在处理器处于特定状态时才会启动。

例如,在 16 位模式下,寻址看起来非常奇怪。 [BX+SI] 有一种寻址模式,但 [AX+BX] 没有。这样的事情往往会使寄存器的使用复杂化,因为您需要确保您的值在您可以根据需要使用的寄存器中。

(幸运的是,32 位模式更加理智(尽管有时本身仍然有点奇怪——例如分段),并且 16 位 x86 代码在引导加载程序和一些嵌入式环境之外基本上不再相关。)

还有过去的遗留物,当时英特尔试图让 x86 成为终极处理器。几个字节长的指令执行了实际上没有人再做的任务,因为坦率地说它们太慢或太复杂了。 ENTER 和 LOOP instructions,举两个例子——注意 C 堆栈帧代码对于大多数编译器来说就像“push ebp; mov ebp, esp”而不是“enter”。


我相信“进入”与“推送/移动”问题的出现是因为在某些处理器上,“推送/移动”更快。在某些处理器上,“输入”更快。这就是生活。
当我被迫使用基于 x86 的机器并开始查看它(具有 m68k 背景)时,我开始感到 asm 编程令人沮丧,......就像我已经学会了用像 C 这样的语言编程,然后被迫与 asm 取得联系……你“感觉”你失去了表达能力、轻松、清晰、“连贯性”、“直觉性”。我敢肯定,如果我用 x86 开始 asm 编程,我会想还不错……也许……我也做过 MMIX 和 MIPS,它们的“asm lang”比 x86 好得多(如果这是 Q 的正确 PoV,但也许不是)
寻址方式问题在 80386 中得到纠正。只有 16 位代码具有有限的寻址方式,32 位代码要好得多。您可以使用特殊前缀在 16 位代码中获得 32 位寻址模式,反之亦然。
@FUZxxl:是的......我可能应该提到丑陋主要限于 16 位代码。固定(我认为)。 :)
感觉不优雅主要来自于错误地认为 8086 的寄存器是通用寄存器。这是不正确的。他们每个人都有一个特殊的目的,如果你不坚持他们的目的,你将会度过一段糟糕的时光。
g
gatoatigrado

我不是专家,但似乎人们不喜欢它的许多功能可能是它表现良好的原因。几年前,拥有寄存器(而不是堆栈)、寄存器帧等被视为使架构对人类更简单的很好的解决方案。然而,现在最重要的是缓存性能,x86 的可变长度字允许它在缓存中存储更多指令。我相信反对者指出的“指令解码”曾经占据了一半的芯片,现在几乎不再是这样了。

我认为并行性是当今最重要的因素之一——至少对于已经运行得足够快可以使用的算法而言。在软件中表达高并行性允许硬件摊销(或通常完全隐藏)内存延迟。当然,更远的架构未来可能是在量子计算之类的领域。

我从 nVidia 听说英特尔的错误之一是他们将二进制格式保持在硬件附近。 CUDA 的 PTX 会进行一些快速的寄存器使用计算(图形着色),因此 nVidia 可以使用寄存器机而不是堆栈机,但仍有升级路径不会破坏所有旧软件。


RISC 在设计时并未考虑到人类开发人员。 RISC 背后的想法之一是将芯片的一些复杂性转移给编写程序集的人,最好是编译器。更多的寄存器意味着更少的内存使用和指令之间的更少依赖,允许更深的管道和更高的性能。请注意,x86-64 的通用寄存器数量是 x86 的两倍,仅此一项就可以显着提高性能。大多数 x86 芯片上的指令在缓存之前被解码,而不是之后(所以大小在这里无关紧要)。
@Dietrich Epp:这并不完全正确。 x86-64 在 ISA 中确实有更多可见的寄存器,但现代 x86 实现通常具有 RISC 样式的寄存器文件,该文件根据需要映射到 ISA 的寄存器以加快执行速度。
“我从 nVidia 听说英特尔的错误之一是他们将二进制格式保持在硬件附近。” -- 我没有得到这个和 CUDA 的 PTX 部分。
@Dietrech Epp:“大多数x86芯片上的指令在缓存之前被解码,而不是之后”这不是真的。它们在解码之前被缓存。我相信 Pentium 4 有一个额外的跟踪缓存,它在解码后缓存,但是已经停止了。
这不是真的,最新的“沙桥”处理器使用一种跟踪缓存(就像奔腾 4 的那样,哦,那个老男孩 :D ),所以技术消失了又回来了......
d
dan04

除了人们已经提到的原因之外:

x86-16 有一个相当奇怪的内存寻址方案,它允许以多达 4096 种不同的方式寻址单个内存位置,将 RAM 限制为 1 MB,并迫使程序员处理两种不同大小的指针。幸运的是,向 32 位的迁移使这个特性变得不必要了,但是 x86 芯片仍然带有段寄存器的残渣。

虽然不是 x86 本身的错,但 x86 调用约定并没有像 MIPS 那样标准化(主要是因为 MS-DOS 没有附带任何编译器),给我们留下了 __cdecl、__stdcall、__fastcall 等的混乱。


嗯..当我想到 x86 的竞争对手时,我不会想到 MIPS。也许是 ARM 或 PowerPC....
@Billy:x86 一直存在。曾经,MIPS 是 x86 的竞争对手。我记得 x86 已经完成了工作以达到与 MIPS 竞争的水平。 (当 MIPS 和 SPARC 在工作站领域展开激烈竞争时。)
@Shannon Severance:仅仅因为曾经存在的东西并不意味着现在的东西。
@supercat:平面 x86-32 内存模型时代的人们往往忘记的是 16 位意味着 64k 内存(任何费心做数学的人都会明白魔法是不可能的,8086 不是对毫无戒心的程序员的严厉惩罚)。获得大约 64k 的方法很少,但 8086 解决方案是一个很好的折衷方案。
@OlofForshell:我认为很多人都在哀叹 8086 不如 68000(它有 16MB 线性寻址空间和通向 4 gigs 的清晰路径)。当然,使用 32 位处理器会更容易访问超过 64K,但 8086 是 16 位架构,旨在比 8 位 8080 更上一层楼。我认为英特尔没有理由飞跃直接从 8 位到 32 位。
B
Bernd Jendrissek

如果您尝试编写针对 x86 的编译器,或者编写 x86 机器仿真器,或者即使您尝试在硬件设计中实现 ISA,我认为您将获得部分答案。

虽然我理解“x86 很丑!”论点,我仍然认为编写 x86 程序集比 MIPS 更有趣(例如)——后者简直太乏味了。它总是对编译器而不是对人类好。我不确定如果芯片尝试过,它会对编译器编写者更具敌意......

对我来说最难看的部分是(实模式)分段的工作方式——任何物理地址都有 4096 个段:偏移量别名。你上次是什么时候需要的?如果段部分是 32 位地址的严格高位,事情会简单得多。


如果正确的 PoV 是人类可以在这些程序集中编写代码的方式,那么 m68k 比 x86 更有趣,并且对人类的友好程度远远超过 x86(对于许多 m68k 程序员来说,这似乎不是那么“人性化”)。
段:偏移地址是在某种程度上与 CP/M 世界保持兼容的尝试。有史以来最糟糕的决定之一。
@Turing Complete:segment:offset 主要不是为了与 CP/M 世界保持兼容。这是一次非常成功的尝试,通过将代码、数据、堆栈和其他内存区域放置在不同的段中,允许 16 位处理器寻址超过 64 KB。
实际上,将数据和堆栈放在不同的段中对 C 来说是完全没用的;它仅适用于 asm。在 C 中,指针可以指向具有静态、自动或动态分配的存储持续时间的数据,因此无法省略段。也许它对 Pascal 或 Fortran 之类的东西有用,但对当时已经是主流语言的 C 没有用......
@Bernd:选择 fs/gs 用于线程本地存储的原因并不是段寄存器对此有好处。只是 x86 严重缺乏寄存器,而段寄存器未被使用。指向线程结构的通用寄存器也可以正常工作,事实上,许多具有更多寄存器的 RISC 系统使用一个作为线程指针。
T
Turing Complete

x86 有一组非常非常有限的通用寄存器,它在最低级别(CISC 地狱)促进了一种非常低效的开发风格,而不是一种有效的加载/存储方法英特尔做出了引入明显愚蠢的段/偏移量的可怕决定 -内存寻址模型与(此时已经!)过时的技术保持兼容在每个人都在使用 32 位的时候,x86 以微不足道的 16 位(其中大多数 - 8088 - 甚至只有具有 8 位外部数据路径,这更可怕!) CPU

对我来说(我是一名 DOS 资深人士,从开发人员的角度来看每一代 PC!)第 3 点是最糟糕的。

想象一下我们在 90 年代初(主流!)的情况:

a) 由于遗留原因而受到严重限制的操作系统(640kB 易于访问的 RAM) - DOS

b)一个操作系统扩展(Windows),它可以在 RAM 方面做更多的事情,但在游戏等方面受到限制......并且不是地球上最稳定的东西(幸运的是后来改变了,但我'我在这里谈论90年代初)

c) 大多数软件仍然是 DOS,我们必须经常为特殊软件创建启动盘,因为有这个 EMM386.exe,一些程序喜欢,另一些则讨厌(尤其是游戏玩家 - 当时我是 AVID 游戏玩家 - 知道我在做什么)在这里谈论)

d) 我们仅限于 MCGA 320x200x8 位(好吧,还有更多的特殊技巧,360x480x8 是可能的,但只有没有运行时库支持),其他一切都是混乱和可怕的(“VESA” - 哈哈)

e) 但在硬件方面,我们有 32 位机器,有相当多兆字节的 RAM 和 VGA 卡,支持高达 1024x768

造成这种糟糕情况的原因是什么?

英特尔的一个简单设计决策。机器指令级别(不是二进制级别!)与已经死去的东西的兼容性,我认为是 8085。其他看似无关的问题(图形模式等)是相关的,因为技术原因和非常狭窄x86 平台自带的思想架构。

今天,情况有所不同,但请询问任何汇编程序开发人员或为 x86 构建编译器后端的人。通用寄存器的数量非常少,不过是一个可怕的性能杀手。


8086 分段体系结构的唯一主要问题是只有一个非专用段寄存器 (ES),并且编程语言没有设计为有效地使用它。它使用的缩放寻址风格在面向对象的语言中可以很好地工作,该语言不期望对象能够从任意地址开始(如果在段落边界上对齐对象,对象引用将只需要两个字节而不是四)。如果将早期的 Macintosh 代码与 PC 代码进行比较,8086 与 68000 相比实际上看起来相当不错。
@supercat:实际上,es 寄存器专用于某些东西,即那些需要存储(movs,stos)或扫描(cmps 和 scas)的字符串指令。给定来自每个段寄存器 es 的 64KiB 寻址还提供了到除代码、数据和堆栈内存(cs、ds、ss)之外的内存的“缺失链接”。段寄存器提供了一种内存保护方案,您无法在寄存器的 64Kib 内存块之外寻址。考虑到 x86 是 16 位架构和当时的光刻限制,您有什么更好的解决方案?
@OlofForshell:ES 用于字符串指令,但可以用作未使用它们的代码的未提交寄存器。在不需要太多操作码空间的情况下缓解 seg-reg 瓶颈的一种方法是使用“rseg”前缀,该前缀将指定对于以下 r/m 格式指令,“r”字段将从 CS/SS/DS 中选择/ES/FS/GS/??/??而不是 AX/BX/CX/DX/SI/DI/SP/BP,并且有 FS/GS 的前缀和 LFS 和 LGS 的指令(如 LDS 和 LES)。我不知道 8086 的微架构是如何布局的,但我认为这样的事情可能会奏效。
@supercat:正如我所写,“寄存器 es 还提供了指向内存的缺失链接......”我记得 Fs 和 gs 直到 386 才到达。
@OlofForshell:他们没有,这使得 80286 架构在大多数方面比 8086 架构更糟糕。我的观点是,添加更多的段寄存器(或者甚至一个,就此而言)会使 8086 架构更加有用,如果段寄存器可以像访问其他的。