assembly - 为什么 x86 跳转/调用指令使用相对位移而不是绝对目的地?

标签 assembly x86 cpu-architecture x86-16

我正在学习 8086,有一个特别的问题困扰着我,我还没有找到任何令人满意的答案。

我知道 CPU 按顺序执行代码,如果要更改代码流,我们希望 IP 指向我们感兴趣的代码所在的新/旧地址。

现在,我的问题是,当我们遇到跳转指令时,为什么我们(我的意思是 CPU)不只是去更新与标签对应的地址的 IP?

当我们遇到跳转指令时,需要在IP上添加一个位移吗?

在我看来

  • 计算位移(即跳转标签到跳转后下一条指令的距离)和
  • 然后采用位移 2 的恭维,
  • 最后被添加到 IP 中,以便 IP 指向标签
  • 所指向的地址/指令

    对我来说,这听起来像是更多的工作,然后只是使用与标签对应的地址更新 IP。但是,我确信事情的完成方式一定是有原因的,只是我不知道。

    在 8086 中选择这种设计的原因是什么?

    最佳答案

    您大大高估了解码相对跳跃的 CPU 复杂性成本。

    1. calculating the displacement(i.e the distance from the jump label to the next instruction after the jump)
    2. then taking that displacements 2's compliment,


    机器代码必须包含步骤 2 的结果(有符号整数相对位移),因此所有这些都在汇编时完成。并且在汇编程序中,减去两个整数地址已经为您提供了您需要的有符号 2 的补码位移。

    使用相对位移有真正的优势,因此仅仅为了简化汇编程序的编写而使 ISA 变得更糟是没有任何意义的。您只需要编写一次汇编程序,但在机器上运行的所有内容都受益于更紧凑的代码和位置独立性。

    相对分支位移是完全正常的,并且在大多数其他架构中也使用(例如 ARM:https://community.arm.com/processors/b/blog/posts/branch-and-call-sequences-explained,其中固定宽度指令使得直接绝对分支编码无论如何都不可能)。 不使用相对分支编码会使 8086 成为奇数。

    更新 : 也许不完全是奇怪的。 MIPS 使用 rel16 << 2对于 beq/bne (MIPS 指令固定为 32 位宽并始终对齐)。但是对于无条件的j (跳转)指令,它interestingly it uses a pseudo-direct encoding .保留PC的高4位,直接替换PC[27:2]位与指令中编码的值。 (同样,程序计数器的低 2 位总是 0。)所以在地址空间的相同 1/16 内,j指令是直接跳转,不给你位置无关的代码。这适用于 jal (跳转链接 = call),making function calls from PIC code less efficient :( Linux-MIPS 过去需要 PIC 二进制文件,but apparently now it doesn't(但共享库仍然必须是 PIC)。

    CPU运行时eb fe , 它所要做的就是将位移添加到 IP而不是替换IP .由于非跳转指令已经更新IP通过添加指令长度,加法器硬件已经存在。

    请注意 sign-extending 8 位到 16 位(或 32 或 64 位)的位移在硬件中是微不足道的:2 的补码符号扩展只是复制符号位,不需要任何逻辑门,只需将一位连接到休息。 (例如 0xfe 变为 0xfffe ,而 0x05 变为 0x0005 。)

    8086 非常强调代码密度,提供许多常用指令的简短形式。这是有道理的,因为代码获取是 8086 上最重要的瓶颈之一,因此更小的代码通常是更快的代码。

    例如,两种形式的相对 jmp 存在,一个与 rel8(短)和一个与 rel16(近)。 (在后来的 CPU 中引入的 32 位和 64 位模式中,E9 操作码是 jmp rel32 而不是 rel16,但 EB 仍然是 jmp rel8,因为函数内的跳转通常在 -128/+ 127)。

    但是 call 没有特殊的缩写。 ,因为它在大多数时候用处不大。那么为什么它仍然为相对位移而不是绝对位移而烦恼呢?

    那么 x86 确实有绝对跳转,但仅限于间接或远跳转。 (到不同的代码段)。例如,EA操作码是 jmp ptr16:16 : "Jump far, absolute, address given in operand".

    要进行绝对近跳,只需 mov ax, target_label/jmp ax . (或在 MASM 语法中,mov ax, OFFSET target_label)。

    相对位移与位置无关

    对这个问题的评论提出了这一点。

    考虑一个机器代码块(已经组装),在 block 内有一些跳转。如果将整个 block 复制到不同的起始地址(或更改CS 基地址,以便可以在段的不同偏移量处访问同一 block ),那么只有相对跳转将继续工作。

    对于labels + absolute addresses要解决同样的问题,必须用不同的 ORG 重新组合代码指示。显然,当您使用远 jmp 更改 CS 时,这不会即时发生!

    关于assembly - 为什么 x86 跳转/调用指令使用相对位移而不是绝对目的地?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46184755/

    相关文章:

    c - SpMV 算法的大小与时间图中的不规则时间跳跃

    memory - 内存地址通常以位为单位多长时间

    c - 如何将函数从反汇编复制到 Visual Studio 并能够从 C 级别调用它?

    gcc - 臂组件: '#define' risk

    linux - 了解 x2APIC 的虚拟 APIC 页面

    c - 胜过 _intel_fast_memcpy 的矢量化 memcpy?

    linux - itoa 汇编实现,div 操作导致segfault?

    arrays - 组装多个数组内存分配

    assembly - x86 BSWAP 指令 REX 不遵循 Intel 规范?

    x86 - ARM 架构与 x86 有何不同?