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

时间:2017-09-12 20:22:00

标签: assembly x86 cpu-architecture x86-16

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

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

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

当我们遇到跳转指令时,需要有一个添加到IP的位移是什么?

在我看来

  1. 计算位移(即跳转后跳转标签到下一条指令的距离)和
  2. 然后采取这种位移2的赞美,
  3. 最终被添加到IP,以便IP指向标签
  4. 指向的地址/指令

    对我而言,这听起来更像是工作,只需使用与标签对应的地址更新IP。但是,我确信必须有一个理由来完成事情,只是我不知道。

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

1 个答案:

答案 0 :(得分:3)

你过分估计解码相对跳跃的CPU复杂性成本。

  
      
  1. 计算位移(即跳转后从跳转标签到下一条指令的距离)
  2.   
  3. 然后采取这种位移2的赞美,
  4.   

机器代码必须包含步骤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(jump-and-link = 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)。

相对位移与位置无关

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

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

要使labels + absolute addresses解决同样的问题,必须使用不同的ORG指令重新组装代码。显然,当你用远jmp更改CS时,不会发生这种情况!