【问题标题】:What is the definition of JAL in RISC-V and how does one use it?JAL 在 RISC-V 中的定义是什么?如何使用它?
【发布时间】:2019-04-01 20:26:35
【问题描述】:

我不明白 JAL 在 RISC-V 中的工作方式,因为我看到了多个相互冲突的定义。例如,如果我参考这个网站: https://rv8.io/isa.html

它说: JAL rd,offset 将第三个参数作为偏移量,但在某些情况下会显示 JAL rd, imm。有什么区别?

似乎 JAL 应该采用一个函数并在 rd 中返回其输出(我不知道为什么有些消息来源同时称它为 rard)。但如果是这样的话,子程序或函数是什么? rd 似乎被定义为寄存器目标,而imm 似乎只是一个整数..

真的很困惑,请帮忙。

【问题讨论】:

  • risc-v 规范非常清楚该指令是如何工作的,整段都在上面。没有任何含糊或误导性的内容,请在您的问题中发布该段落,以便所有人查看并描述您不理解的部分。
  • rd 是链接寄存器,其中jal 存储返回地址。函数和子程序基本上是可以互换的术语。您可能会争辩说,子例程是没有返回值的函数的子集,例如 void foo(int x)

标签: architecture hardware cpu-architecture riscv


【解决方案1】:

您似乎在混淆机器代码和汇编语言。

从机器代码的角度来看,整个指令以及它的所有字段都是简单的数字:

  • 有些是编码操作码之类的字段,
  • 有些是对寄存器编号等内容进行编码的字段,并且,
  • 有些字段是数字编码的东西,如有符号或无符号整数。

这些编码由指令集架构定义。硬件专门设计用于根据其 ISA 规范解释这些数字位字段。

汇编器、链接器和操作系统加载器共同允许您使用符号值来形成指令,而不是各个字段的数字(甚至整个指令的一个数字):

  • 操作码字段中操作码编号的操作码助记符,
  • 在寄存器字段中为寄存器编号注册名称,以及,
  • 标签——甚至是复杂的表达式——用于数字操作数字段中的各种立即数。

我不会太认真地认为一个文本会引用 rard。它可能表明存在差异,或者只是记录指令机器代码字段的不同方式。

JAL 指令对两个操作数进行编码:寄存器和立即数。

用返回地址的位置更新识别出的寄存器号,也就是jal指令的位置——加上jal指令的长度,这样寄存器就得到下一个sequential em>(由 在 jal 之后的 address) 指令,这是一个正确的调用返回地址。

与指令中的所有位域一样,立即数是一种编码——解码后的值最终会产生分支目标地址。它是通过将立即位字段转换为有符号偏移量来计算的,并添加到 jal 指令的 pc 中。编码允许 18 位(分布在多个位字段中)加上一个符号位,并且不编码偏移量的最后一位(分支目标始终是 16 位对齐的,这意味着无论如何最后一位总是为零,所以它是未存储)。最终 jal 可以从 jal 指令本身达到 -0.5MB 到 +0.5MB。

如前所述,执行硬件将立即(子)字段转换为添加到 pc 以识别最终分支/调用目标的偏移量。我们可以在汇编语言中提供标签和其他复杂的表达式是这些语言的一个特性,其目的是将标签和其他表达式压缩成处理器所需的直接位字段常量,以便到达预期的位置。目标代码中的重定位和/或加载的代码中的修正存在复杂的交互,确保这些直接位字段保持有用的位模式,硬件可以使用该位模式,基于在运行时相对简单的字段提取和添加来获得预期的位置。


为了让函数互相调用而不踩到对方的脚趾,调用者和被调用者的asm代码必须一致:

  • 传递的参数
  • 传递的返回地址
  • 返回的返回值
  • 保留的寄存器
  • 暂存寄存器

这被广泛地称为调用约定。它规定了调用者对被调用者一无所知(反之亦然)如何交互。它对哪个寄存器或堆栈位置保存第一个参数、第二个参数等施加了软件约定要求。哪个寄存器或堆栈位置保存返回地址、返回值的传递方式以及调用者环境的哪些寄存器被保留通过调用与可能被调用擦除(从头开始)。

当正确遵守约定时,调用者(不知道被调用者的实现,只知道参数和返回值的类型,也就是函数签名)可以

  • 在调用过程中将局部变量安全地存储在机器寄存器中,
  • 传递参数
  • 调用被调用者,
  • 返回调用者,
  • 然后,接收返回值并继续

【讨论】:

  • ra 表示返回地址,是x1寄存器的助记符,通常用作操作数。 rd 表示寄存器目标,它是指令写入的寄存器的通用参数名称。
【解决方案2】:

jal指令中imm(或imm20)是一个20位二进制数。

offsetjal指令对imm解释imm的内容左移1位,然后符号扩展为地址大小(目前是 32 位或 64 位),从而生成一个值在 -100 万(大约)到 +100 万之间的整数。

这个offset整数被添加到jal指令本身的地址上,得到你要调用的函数的地址。这个新地址被放入 PC 中,程序继续执行该地址处的任何指令。

同时,jal 之后的指令地址被存入 CPU 寄存器rd。被调用的函数可能稍后会使用它返回,使用 jalr rn 指令。

RISC-V 硬件允许将 32 个整数寄存器中的任何一个指定为 rd。如果将寄存器 0 (x0) 指定为 rd,则返回地址将被丢弃,您实际上拥有的是 +/1 MB goto,而不是函数调用。

标准 RISC-V ABI(软件约定,与硬件无关)指定对于正常功能,rd 应该是寄存器 1 (x1),然后通常称为 ra(返回地址)。寄存器 5 (x5) 也常用于特殊的运行时库函数,例如在函数开始和结束时保存和恢复寄存器的特殊函数。

RISC-V 指令集手册建议 CPU 设计人员可能会选择添加特殊硬件(return address stack)以使严格嵌套的 jal x1/x5,offset 和 `jalr x1/x5' 对运行得比预期更快,因此遵循标准 ABI 可能会有优势。但是,即使使用其他寄存器,程序也能正常工作。

【讨论】:

    猜你喜欢
    • 2020-02-05
    • 2020-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-10
    • 2020-10-18
    • 2020-05-05
    相关资源
    最近更新 更多