相对跳转指令,在不同CPU架构上的区别

大部分的跳转都是相对地址跳转,即偏移量跳转,然而这个偏移量,在不同的架构上表现是不同的。这篇文章将会对比几种架构来展示其中的细节。

RISC-V

汇编源码(rv.asm):

l1: beq x0, x0, l1
l2: beq x0, x0, l1

编译后进行反汇编看机器码:

riscv64-unknown-elf-as rv.asm -o rv.o && riscv64-unknown-elf-objdump -S rv.o
rv.o:     file format elf64-littleriscv


Disassembly of section .text:

0000000000000000 <l1>:
   0:   00000063                beqz    zero,0 <l1>

0000000000000004 <l2>:
   4:   fe000ee3                beqz    zero,0 <l1>

根据RISC-V指令集手册, beq指令的编码格式为:

32             25    20    15    12             7         0
 +--------------+-----+-----+-----+-------------+---------+
 | imm[12|10:5] | rs2 | rs1 | 000 | imm[4:1|11] | 1100011 |
 +--------------+-----+-----+-----+-------------+---------+

所以上面两条指令的跳转偏移量分别是0和一个有符号数0b1111111_1110_0(值为-4)。

ARM

汇编源码(arm.asm):

l1: beq l1
l2: beq l1

编译后进行反汇编看机器码:

arm-none-eabi-as arm.asm -o arm.o && arm-none-eabi-objdump -S arm.o
arm.o:     file format elf32-littlearm


Disassembly of section .text:

00000000 <l1>:
   0:   0afffffe        beq     0 <l1>

00000004 <l2>:
   4:   0afffffd        beq     0 <l1>

根据ARM架构手册beq(实际上是b)指令的编码格式为:

32     28          24                 0
 +------+-------+---+-----------------+
 | cond | 1 0 1 | 0 | signed_immed_24 |
 +------+-------+---+-----------------+

两条指令的跳转偏移量分别是FFFFFEFFFFFD,经过符号扩展和移位,这两个偏移量将变为FFFFFFF8FFFFFFF4。 它们也是有符号数,所以是-8-12

为什么不是0-4呢?

这是因为一些历史原因:

The original ARM design had a 3-stage pipeline (fetch-decode-execute). To simplify the design they chose to have the PC read as the value currently on the instruction fetch address lines, rather than that of the currently executing instruction from 2 cycles ago. Since most PC-relative addresses are calculated at link time, it's easier to have the assembler/linker compensate for that 2-instruction offset than to design all the logic to 'correct' the PC register.

ARM最初的设计有一个三级流水线(预取-解码-执行)。为了简化设计,他们选择将PC的读入值直接指定为当前执行时的预取线上的地址,而不是2个周期前的那个地址 ……

MCS-51

汇编源码(8051.asm):

l1: jc l1
l2: jc l1

编译后进行反汇编看机器码:

sdas8051 -l 8051.lst 8051.asm && head 8051.lst
ASxxxx Assembler V02.00 + NoICE + SDCC mods  (Intel 8051), page 1.
Hexadecimal [24-Bits]



      000000 40 FE            [24]    1 l1: jc l1
      000002 40 FC            [24]    2 l2: jc l1

根据8051指令集手册JC的编码格式是:

16        8          0
 +--------+----------+
 | OFFSET | 01000000 | 
 +--------+----------+

所以OFFSET分别是FE-2)和FC-4)。

和ARM类似,我们拿到的也是指令当前指令时候的PC值,所以需要减去前一条指令的长度。