为什么x86跳转/调用指令使用相对位移而不是绝对目的地? [英] Why do x86 jump/call instructions use relative displacements instead of absolute destinations?

查看:298
本文介绍了为什么x86跳转/调用指令使用相对位移而不是绝对目的地?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am learning 8086 and there is one particular question which is bothering me and I have not been able to find any satisfactory answer yet.

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

I understand that CPU executes the code sequentially and if want to change the code flow we would like the IP to point to the new/old address where the code of our interest is sitting.

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

Now, my question is why we(I mean CPU) don't just go and update the IP with the address corresponding to the label when we encounter jump instruction?

遇到跳转指令时,需要将位移添加到IP上是什么?

What is the need to have a displacement which is added to IP when we encounter jump instruction?

我认为

  1. 计算位移(即从跳转标签到跳转后下一条指令的距离)和
  2. 然后以位移2的恭维
  3. 最终将其添加到IP中,以便IP指向标签
  4. 所指向的地址/指令
  1. calculating the displacement(i.e the distance from the jump label to the next instruction after the jump) and
  2. then taking that displacements 2's compliment,
  3. which finally gets added to the IP so that IP points to the address/instruction pointed by label

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

To me this sounds like more work then just updating the IP with the address corresponding to label. But, I am sure there must be a reason for the way things are done, its just that I am not aware.

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

What was the reason for this design choice in 8086?

推荐答案

您大大高估了解码相对跳转所消耗的CPU复杂性.

You are vastly over-estimating the cost in CPU complexity of decoding a relative jump.

  1. 计算位移(即从跳转标签到跳转后下一条指令的距离)
  2. 然后考虑位移2的赞美

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

The machine code has to contain the result of step 2 (a signed integer relative displacement), so all of that is done at assemble time. And in the assembler, subtracting two integer addresses already gives you the signed 2's complement displacement you need.

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

There are real advantages to using relative displacements, so making the ISA worse just to simplify writing an assembler would not have made any sense. You only need to write the assembler once, but everything that runs on the machine benefits from more compact code, and position independence.

相对分支位移是完全正常的,并且也在大多数其他体系结构中使用(例如,ARM:

Relative branch displacements are completely normal, and used in most other architectures, too (e.g. ARM: https://community.arm.com/processors/b/blog/posts/branch-and-call-sequences-explained, where fixed-width instructions makes a direct absolute branch encoding impossible anyway). It would have made 8086 the odd one out to not use relative branch encoding.

更新:也许不是完全奇怪. MIPS对beq/bne使用rel16 << 2(MIPS指令固定为32位宽,并且始终对齐).但是对于无条件的j(跳转)说明,它有趣的是它使用伪直接编码.它保留PC的高4位,并用指令中编码的值直接替换PC[27:2]位. (同样,程序计数器的低2位始终为0.)因此,在相同地址空间的1/16内,j指令是直接跳转,并且不会提供与位置无关的代码.这适用于jal(跳转和链接= call),从PIC进行函数调用代码效率不高:((以前需要PIC二进制文件的Linux-MIPS,

update: Maybe not totally the odd one out. MIPS uses rel16 << 2 for beq / bne (MIPS instructions are fixed at 32-bits wide and always aligned). But for unconditional j (jump) instructions, it interestingly it uses a pseudo-direct encoding. It keeps the high 4 bits of PC, and directly replaces the PC[27:2] bits with the value encoded in the instruction. (Again, low 2 bits of the program counter are always 0.) So within the same 1/16th of address space, j instructions are direct jumps, and don't give you position-independent code. This applies to jal (jump-and-link = call), making function calls from PIC code less efficient :( Linux-MIPS used to require PIC binaries, but apparently now it doesn't (but shared libs still have to be PIC).

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

When the CPU runs eb fe, all it has to do is add the displacement to IP instead of replacing IP. Since non-jump instructions already update IP by adding the instruction length, the adder hardware already exists.

请注意,符号扩展 8位位移到16位(或32或64) -bit)在硬件上是微不足道的:2的补码符号扩展名只是复制符号位,不需要任何逻辑门,只需将一位连接到其余位的导线即可. (例如0xfe变为0xfffe,而0x05变为0x0005.)

Note that sign-extending 8-bit displacements to 16-bit (or 32 or 64-bit) is trivial in hardware: 2's complement sign-extension is just copying the sign bit, which doesn't require any logic gates, just wires to connect one bit to the rest. (e.g. 0xfe becomes 0xfffe, while 0x05 becomes 0x0005.)

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

8086 put a big emphasis on code density, providing short forms of many common instructions. This makes sense, because code-fetch was one of the most important bottlenecks on 8086, so smaller code usually was faster code.

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

For example, two forms of relative jmp existed, one with rel8 (short) and one with rel16 (near). (In 32 and 64-bit mode introduced in later CPUs, the E9 opcode is a jmp rel32 instead of rel16, but EB is still jmp rel8 because jumps within a function are often within -128/+127).

但是call并没有特别的缩写,因为在大多数情况下它并没有太多用处.那么,为什么它仍然会因为相对位移而不是绝对位移而困扰呢?

But there's no special short for for call, because it wouldn't be much use most of the time. So why does it still bother with a relative displacement instead of absolute?

x86确实具有绝对跳转,但仅适用于间接跳转或跳转. (到另一个代码段).例如,EA操作码是 jmp ptr16:16:跳转到绝对值,操作数中给出的地址".

Well x86 does have absolute jumps, but only for indirect or far jumps. (To a different code segment). For example, the EA opcode is jmp ptr16:16: "Jump far, absolute, address given in operand".

要进行绝对的近跳,只需mov ax, target_label/jmp ax. (或使用MASM语法,mov ax, OFFSET target_label).

To do an absolute near jump, simply mov ax, target_label / jmp ax. (Or in MASM syntax, mov ax, OFFSET target_label).

相对位移与位置无关

关于这个问题的评论提出了这一点.

Comments on the question brought this up.

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

Consider a block of machine code (already assembled), with some jumps inside the block. If you copy that whole block to a different start address (or change the CS base address so the same block is accessible at a different offset with the segment), then only relative jumps will keep working.

对于标签+绝对地址以解决相同的问题,必须使用不同的ORG指令重新组装代码.显然,当您以远距离的跳频更改CS时,这不可能即时发生!

For labels + absolute addresses to solve the same problem, the code would have to be re-assembled with a different ORG directive. Obviously that can't happen on the fly when you change CS with a far jmp!

这篇关于为什么x86跳转/调用指令使用相对位移而不是绝对目的地?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆