MIPS中的跳转/分支指令

本文节选自《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 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中的分支延迟槽和存储延时槽


<think>嗯,用户想了解Excel中的VLOOKUP函数的用法和参数含义。首先,我需要回忆一下VLOOKUP的基本结构。VLOOKUP是垂直查找函数,用于在表格的首列查找指定的值,然后返回该行中指定列的数据。它的四个参数分别是查找值、表格范围、列索引号和匹配模式。 接下来,我得确认每个参数的具体作用。第一个参数是lookup_value,也就是用户要查找的值,比如产品ID或者姓名。第二个参数是table_array,即查找的范围,这里要注意的是,查找值必须位于这个范围的第一列,否则函数无法正确工作。第三个参数是col_index_num,也就是用户想要返回的数据在表格范围中的第几列,这个列数是从表格范围的第一列开始算起的,不是整个工作表的列。第四个参数是range_lookup,决定是精确匹配还是近似匹配,通常用FALSE或0来指定精确匹配,避免错误。 然后,用户可能需要一个具体的例子来理解如何使用。比如,假设有一个产品价格表,用户想根据产品ID查找对应的价格。这时候,VLOOKUP就可以在ID列查找,然后返回价格列的数据。同时,要提醒用户注意表格范围最好使用绝对引用,比如$A$2:$C$100,这样在拖动公式时范围不会变化。 可能遇到的问题也需要考虑。比如,如果查找值不在表格的第一列,或者列索引号超过了表格的列数,函数会返回错误。此外,数据格式不一致也可能导致问题,比如查找值是数字,而表格中是文本格式的数字,这时候需要统一格式。还有,当出现#N/A错误时,可能是查找值不存在,或者匹配模式设置不正确,需要检查参数是否正确。 用户可能还会问到如何避免常见的错误,比如部分数据匹配出错的情况。这时候需要检查是否有多余的空格,或者数据格式不一致,或者表格范围没有锁定导致拖动时偏移。此外,XLOOKUP作为VLOOKUP的升级版,可能在某些情况下更方便,比如可以向左查找或者返回多列数据,这也是可以提及的内容。 最后,整理步骤,从基础语法到参数说明,再到具体案例,逐步引导用户理解如何使用VLOOKUP函数。同时,提供常见错误和解决方法,帮助用户在实际应用中避免问题。如果有必要,还可以比较VLOOKUP和其他查找函数的区别,比如HLOOKUP或XLOOKUP,但根据用户的问题,主要集中在VLOOKUP本身即可。</think>### Excel VLOOKUP函数详解 #### 一、基础语法与参数说明 VLOOKUP函数的语法为: $$=VLOOKUP(lookup\_value, table\_array, col\_index\_num, [range\_lookup])$$ 包含4个参数: 1. **lookup_value**(必填):要查找的值(如单元格引用或具体值) 2. **table_array**(必填):包含数据的表格范围(必须包含查找列和返回列) 3. **col_index_num**(必填):返回值所在列的序号(从table_array第一列开始计数) 4. **range_lookup**(可选):匹配类型 - `TRUE`/`1`:近似匹配(默认值,需数据升序排列) - `FALSE`/`0`:精确匹配(常用选项) [^1][^2] #### 二、使用步骤演示(工资表查询案例) 假设需要根据员工编号查询工资: 1. 建立查询单元格(如`B12`) 2. 输入公式: ```excel =VLOOKUP(A12, $A$2:$D$100, 4, 0) ``` - `A12`:待查询的员工编号 - `$A$2:$D$100`:锁定数据区域(绝对引用) - `4`:返回第4列(工资列) - `0`:精确匹配 [^2][^3] #### 三、常见错误与解决方法 | 错误现象 | 原因 | 解决方案 | |---------|------|---------| | #N/A | 查找值不存在 | 检查数据源或改用`IFERROR`容错 | | #REF! | 列序号超出范围 | 确认col_index_num ≤ 表格列数 | | 部分匹配失败 | 数据格式不一致 | 统一数值/文本格式 | | 结果错位 | 表格未锁定 | 使用`$`符号固定区域引用 | [^3][^4] #### 四、进阶技巧 1. **多条件查询**: 使用辅助列合并多个条件字段 ```excel =VLOOKUP(A2&B2, $D$2:$F$100, 3, 0) ``` 2. **通配符匹配**: `"*"`匹配任意字符,`"?"`匹配单个字符 ```excel =VLOOKUP("张*", $A$2:$C$100, 3, 0) ``` 3. **跨表查询**: 引用其他工作表数据 ```excel =VLOOKUP(A2, Sheet2!$A$2:$D$100, 4, 0) ``` [^1][^4]
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值