您似乎在混淆机器代码和汇编语言。
从机器代码的角度来看,整个指令以及它的所有字段都是简单的数字:
- 有些是编码操作码之类的字段,
- 有些是对寄存器编号等内容进行编码的字段,并且,
- 有些字段是数字编码的东西,如有符号或无符号整数。
这些编码由指令集架构定义。硬件专门设计用于根据其 ISA 规范解释这些数字位字段。
汇编器、链接器和操作系统加载器共同允许您使用符号值来形成指令,而不是各个字段的数字(甚至整个指令的一个数字):
- 操作码字段中操作码编号的操作码助记符,
- 在寄存器字段中为寄存器编号注册名称,以及,
- 标签——甚至是复杂的表达式——用于数字操作数字段中的各种立即数。
我不会太认真地认为一个文本会引用 ra 与 rd。它可能表明存在差异,或者只是记录指令机器代码字段的不同方式。
JAL 指令对两个操作数进行编码:寄存器和立即数。
用返回地址的位置更新识别出的寄存器号,也就是jal指令的位置——加上jal指令的长度,这样寄存器就得到下一个sequential em>(由
在 jal 之后的 address) 指令,这是一个正确的调用返回地址。
与指令中的所有位域一样,立即数是一种编码——解码后的值最终会产生分支目标地址。它是通过将立即位字段转换为有符号偏移量来计算的,并添加到 jal 指令的 pc 中。编码允许 18 位(分布在多个位字段中)加上一个符号位,并且不编码偏移量的最后一位(分支目标始终是 16 位对齐的,这意味着无论如何最后一位总是为零,所以它是未存储)。最终 jal 可以从 jal 指令本身达到 -0.5MB 到 +0.5MB。
如前所述,执行硬件将立即(子)字段转换为添加到 pc 以识别最终分支/调用目标的偏移量。我们可以在汇编语言中提供标签和其他复杂的表达式是这些语言的一个特性,其目的是将标签和其他表达式压缩成处理器所需的直接位字段常量,以便到达预期的位置。目标代码中的重定位和/或加载的代码中的修正存在复杂的交互,确保这些直接位字段保持有用的位模式,硬件可以使用该位模式,基于在运行时相对简单的字段提取和添加来获得预期的位置。
为了让函数互相调用而不踩到对方的脚趾,调用者和被调用者的asm代码必须一致:
- 传递的参数
- 传递的返回地址
- 返回的返回值
- 保留的寄存器
- 暂存寄存器
这被广泛地称为调用约定。它规定了调用者对被调用者一无所知(反之亦然)如何交互。它对哪个寄存器或堆栈位置保存第一个参数、第二个参数等施加了软件约定要求。哪个寄存器或堆栈位置保存返回地址、返回值的传递方式以及调用者环境的哪些寄存器被保留通过调用与可能被调用擦除(从头开始)。
当正确遵守约定时,调用者(不知道被调用者的实现,只知道参数和返回值的类型,也就是函数签名)可以
- 在调用过程中将局部变量安全地存储在机器寄存器中,
- 传递参数
- 调用被调用者,
- 返回调用者,
- 然后,接收返回值并继续