• 欢迎访问开心洋葱网站,在线教程,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站,欢迎加入开心洋葱 QQ群
  • 为方便开心洋葱网用户,开心洋葱官网已经开启复制功能!
  • 欢迎访问开心洋葱网站,手机也能访问哦~欢迎加入开心洋葱多维思维学习平台 QQ群
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏开心洋葱吧~~~~~~~~~~~~~!
  • 由于近期流量激增,小站的ECS没能经的起亲们的访问,本站依然没有盈利,如果各位看如果觉着文字不错,还请看官给小站打个赏~~~~~~~~~~~~~!

MIPS中的跳转/分支指令

嵌入式开发 弦苦 3163次浏览 0个评论

本文节选自《See MIPS run 2rd》/《MIPS 体系结构透视》中的部分章节,结合个人理解,对部分译文有所改动。


1.5.2 Addressing and Memory Accesses

Jump instructions: The limited 32-bit instruction length is a particular problem for branches in an architecture that wants to support very large programs. The smallest opcode field in a MIPS instruction is 6 bits, leaving 26 bits to define the target of a jump. Since all instructions are four-byte aligned in memory, the two least significant address bits need not be stored, allowing an address range of 2^28= 256 MB. Rather than make this branch PC relative, this is interpreted as an absolute address within a 256MB segment. That’s inconvenient for single programs larger than this, although it hasn’t been much of a problem yet!

Branches out of segment can be achieved by using a jump register instruction, which can go to any 32-bit address.

Conditional branches have only a 16-bit displacement field—giving a 2^18 byte range, since instructions are four-byte aligned—which is interpreted as a signed PC-relative displacement. Compilers can only code a simple conditional branch instruction if they know that the target will be within 128 KB of the instruction following the branch.

1.5.2 编址及内存访问

MIPS 指令格式分为 RIJ 三类,如下表:

Type-31-                                 format (bits)                                 -0-
Ropcode (6)rs (5)rt (5)rd (5)shamt (5)funct (6)
Iopcode (6)rs (5)rt (5)immediate (16)
Jopcode (6)address (26)


跳转指令(j):有限的32位指令长度对于大型程序的分支跳转支持确实是个难题。MIPS指令中最小的操作码域占6位,剩下的26位用于跳转目标的编址。由于所有指令在内存中都是4字节对齐的,因此最低的2个比特位是无需存储的,这样实际可供寻址范围为2^28=256MB。分支跳转地址被当做一个256MB的段内绝对地址,而非PC相对寻址。这对于地址范围超过256MB的跳转程序而言是无能为力的,所幸目前也很少遇到这么大的远程跳转需求。

段外分支跳转可以使用寄存器跳转指令实现,它可以跳转到任意(有效的)32位地址。

条件分支跳转指令(b)编码域的后 16 位 broffset 是相对PC的有符号偏移量,由于指令是4字节对齐的,因此可支持的跳转范围实际上是2^18=256KB(相对PC的-128KB~+128KB)。如果确定跳转目标地址在分支指令前后的128KB范围内,编译器就可以编码只生成一条简单的条件分支指令。 


1.5.4 Programmer-Visible Pipeline Effects

Delayed branches: The pipeline structure of the MIPS CPU (Figure 1.3) means that when a jump/branch instruction reaches the execute phase and a new program counter is generated, the instructionafter the jump will already have been started. Rather than discard this potentially useful work, the architecture dictates that the instruction after a branch must always be executed before the instruction at the target of the branch. The instruction position following any branch is called the branch delay slot.

————————————————————–

It is the responsibility of the compiler system or the assembly programming wizard to allow for and even to exploit the branch delay; it turns out that it is usually possible to arrange that the instruction in the branch delay slot does useful work. Quite often, the instruction that would otherwise have been placed before the branch can be moved into the delay slot.

This can be a bit tricky on a conditional branch, where the branch delay instruction must be (at least) harmless on both paths. Where nothing useful can be done, the delay slot is filled with a nop instruction.

————————————————————–

Late Data from load(load delay slot): Another consequence of the pipeline is that a load instruction’s data arrives from the cache/memory system after the next instruction’s ALU phase starts—so it is not possible to use the data from a load in the following instruction. (See Figure 1.4 for how this works.)

The instruction position immediately after the load is called the load delay slot, and an optimizing compiler will try to do something useful with it. The assembler willhide this from you but may end up putting in a nop.

On modern MIPS CPUs the load result is interlocked:If you try to use the result too early, the CPU stops until the data arrives. But on early MIPS CPUs, there were no interlocks, and the attempt to use data in the load delay slot led to unpredictable results.

1.5.4 程序员可见的流水线效果

MIPS中的跳转/分支指令

延迟分支:MIPS CPU 的流水线结构(见图1-3)意味着,当跳转/分支指令到达执行阶段并且新的程序计数器已经产生时,紧随其后的下一条指令已经开始执行了。

MIPS体系架构并没有抛弃这部分潜在的有用工作,而是规定分支之后的指令总是在分支目标指令之前执行。紧随分支指令之后的位置称为分支延迟槽

————————————————————–

编译器或汇编程序可以考虑充分利用分支延迟,比如在延迟槽中填充有用的指令,把原来放在分支指令之前的指令移到延迟槽中执行。

> 例如《VxWorks BSP for AMD’s AU1500(MIPS)》的 V100R001CPE\romMipsInit.s 中定义 ROM 异常入口点时,XVECENT 宏中利用分支延迟槽传参:#define  XVECENT(f,bev)     b f; li k0,bev

对条件分支来说需要很小心,分支延迟指令必须对(至少)两条路径都不会产生不良影响。在没有任何可用操作时,延迟槽将填充空指令(nop)占位

————————————————————–

数据加载延迟(加载延迟槽):流水线的另一后果是数据加载指令的数据在下一条指令的 ALU 阶段启动之后才能从缓存或内存系统中取得,于是下一条指令便不能使用该数据。图1-4显示了此工作原理。

紧跟在加载指令后的指令位置叫做加载延迟槽,优化的编译器可以用延迟槽来做一些有用的工作。这一特性对程序员是透明的,很多时候汇编器向该延迟槽填入一个空操作指令(nop)占位。

在现代MIPS CPU中加载结果会导致互锁:如果你想过早使用该数据,CPU 就会停下来直到该数据到达。但早期的 MIPS CPU 中没有互锁硬件,任何企图提前使用加载延迟槽中的数据的操作都将导致无法预料的结果。

> XVECENT宏中的分支延迟槽执行数据加载,但是由于数据加载延迟,不能在f(romExcHandle)的第一条指令中访问 k0 参数,或者在 li 后添加一条 nop 指令。


 8.7.8 Jumps, Subroutine Calls, and Branches

The MIPS architecture follows Motorola nomenclature for these instructions, as follows:

(1) PC-relative instructions are called “branch” and absolute-addressed instructions “jump”; the operation mnemonics begin with b or j.

(2) A subroutine call is “jump and link” or “branch and link” and the mnemonics end . . .al.

(3) All the branch instructions, even branch and link, areconditional, testing one or two registers.Unconditional versions can be and are readily synthesized—for example,beq $0, $0, label.

j: This instruction transfers control unconditionally to an absolute address. Actually, j doesn’t quite manage a 32-bit address: The top 4 address bits of the target are not defined by the instruction, and thetop 4 bits of the current PC value are used instead. Most of the time this doesn’t matter; 28 bits still gives a maximum code size of 256 MB.

To reach a long way away, you must use the jr(jump to register) instruction, which is also used for computed jumps. You can write the j mnemonic with a register, but it’s quite popular not to do so.

jal , jalr: These implement a direct and indirect subroutine call. As well as jumping to the specified address, they store the return address (the instruction’s own address+8) in register ra, which is the alias for $31. Why add eight to the program counter? Remember that jump instructions, like branches, always execute the immediately following branch delay slot instruction, so the return address needs to be the instruction after the branch delay slot. Subroutine return is done with a jump to register, most often jr ra.

PC-relative subroutine calls can use the balbgezal, and bltzal instructions. The conditional branch-and-link instructions save their return address into ra even when the condition is false, which can be useful when doing a computation using the current instruction’s address.

b: Unconditional PC-relative (though relatively short-range) branch.

bal: PC-relative function call.

8.7.8 跳转, 分支和子程序调用指令

MIPS 体系架构的跳转、分支和子程序调用指令沿用了 Motorola 命名法:

(1)PC 相对地址跳转指令称为“分支”,绝对地址跳转指令称为“跳转”,相应的助记符分别以 和 开头。

(2)子程序调用称为 “跳转并链接” 或 “分支并链接”,相应的助记符以 al 结尾。

(3)所有的分支指令,包括分支并链接指令都是有条件的跳转,需要对某些寄存器进行测试。

无条件的分支指令可以很容易地由其他指令合成,比如“beq $0, $0, label”——$0总是等于它自己,故eq总成立,从而实现了无条件跳转。例如 b label=>beq $zero, $zero, offsbal label=>bgezal $zero, offs

j:该指令无条件跳转到一个绝对地址。实际上,j 指令跳转到的地址并不是直接指定32位的地址(所有 MIPS 指令都是 32 位长,不可能全部用于编址数据域,那样的指令是无效的,也许只有nop):由于目的地址的最高4位无法在指令的编码中给出,32位地址的最高4位取值当前PC的最高4位。对于一般的程序而言,28位地址所支持的256MB跳转空间已经足够大了。

要实现更远程的跳转,必须使用 jr 指令跳转到指定寄存器中,该指令也用于需要计算合成跳转目标地址的情形。你可以使用 j 助记符后面紧跟一个寄存器表示寄存器跳转,不过一般不推荐这么做。

jal、jalr:这两条指令分别实现了直接和间接子程序调用。在跳转到指定地址实现子程序调用的同时,需要将返回地址(当前指令地址+8)保存到 ra($31)寄存器中。为什么是当前指令地址加8呢?这是因为紧随跳转指令之后有一条立即执行的延迟槽指令(例如nop占位指令),加8刚好是延迟槽后面的那条有效指令。从子程序返回是通过寄存器跳转完成,通常调用 jr ra

基于 PC 相对寻址的位置无关子程序调用通过 bal、bgezal 和 bltzal 指令完成。条件分支和链接指令即使在条件为假的情况下,也会将它们的返回地址保存到 ra 中,这在需要基于当前指令地址做计算的场合非常有用。

b:相对当前指令地址(PC)的无条件短距离跳转指令。

bal:基于当前指令地址(PC)的函数调用指令。

说明

1.关于流水线,可参考《大话处理器》中第4章《微架构——处理器的内心世界》,其中“跟着顺溜学流水线”也许有助理解。

2.“jump and link” or “branch and link”中的link(链接),在语义的理解上,我们可以类比联想HTML中的href标签(超链接)。

3.在跳转到指定地址实现子程序调用的同时,需要将返回地址保存到ra寄存器,即通常所说的“函数调用的现场保护”,以便子程序返回时能够继续调用之前的流程。对于跳转/分支指令,MIPS CPU将自动保存ra;若子程序需要嵌套调用其他子程序,则必须先存储ra,通常是压入栈,子程序末尾弹出之前保存的ra,然后jr到ra。

参考:

imgtec:MIPS Architectures / documentation datasheet


MIPS architecture:wikinekochan
Introduction to the MIPS Architecture  
MIPS Instruction Set  


MIPS指令集及汇编

MIPS指令特点

MIPS中的分支延迟槽和存储延时槽


开心洋葱 , 版权所有丨如未注明 , 均为原创丨未经授权请勿修改 , 转载请注明MIPS中的跳转/分支指令
喜欢 (0)

您必须 登录 才能发表评论!

加载中……