lwc1 $f16, const5($gp) 语法

lwc1 $f16, const5($gp) syntax

关于 $gp 语法,我发现它有点混乱,如果 $gp 指向某种地址,然后用 'const5' 偏移它(如果我在这里错了请纠正我,因为我不是当然),那么会发生什么?另外,const5 也不是浮点数,所以我想知道这个 lwc1 和 const5($gp) 在幕后是如何工作的。

练习是将以下 C 指令翻译成 MIPS 汇编语言:

float f2c (float fahr) 
{
     return ((5.0/9.0)*(fahr - 32.0));
}

翻译的MIPS(来自教科书):

f2c: 
    lwc1 $f16, const5($gp);
    lwc1 $f18, const9($gp);
    ...


lwc1 是一个加载指令,其操作与 lw 类似,但加载到浮点寄存器而不是整数寄存器。 lwc1 不检查、解释或验证正在加载的值,它只是将内存中有效地址处的 4 字节又名 32 位值加载到指定的浮点寄存器中,而无需修改。

lwc1 可用于将整数或浮点值加载到浮点寄存器中。加载整数后,您可以使用 cvt.s.w 将浮点寄存器中的整数转换为浮点值(在相同或另一个浮点寄存器中),这样您就可以进行浮点 add/sub 等..

lwsw一样,MIPS上的整数加载和存储,浮点加载和存储指令只有一种寻址模式。该寻址模式是base + offset。有效地址就是基址寄存器的内容+符号扩展immediate/offset。虽然lwc1加载到一个浮点寄存器,但寻址方式仍然以整数寄存器为基数。

$gp 是一个称为全局(数据)指针的寄存器。当程序启动环境正确设置时,它指向加载到内存中的全局数据部分的开头 +32k。这允许通过对立即数使用负值和正值来访问 64k 的全局数据。 $gp 不应在启动后由程序修改,以便可以依赖它继续指向全局数据部分。

如果程序被编译为使用 $gp,则存在程序全局数据多于 64k 的风险,在这种情况下,您应该得到 linker 错误。 $gp 允许机器代码独立于数据的实际位置,并且全局数据的访问顺序只有一条指令长度。 (64k 限制仅适用于全局数据、变量和常量,但不适用于动态分配的堆数据。)

使用 $gp 访问全局数据的替代方法是使用绝对寻址,例如当环境不支持它时,或者程序具有超过 64k 的全局数据时,这在 MIPS 上接受 2 条指令,第一个是 lui,第二个在 addiulwsw.

之间变化

为了使 lwc1 const5($gp) 正常工作,

  • 程序的目标代码(编译器输出)需要将一些浮点常量指定为全局数据,可能带有 __const5 & __const9、
  • 等标签
  • 程序目标代码需要在访问这些标签时指定适当的重定位,
  • linker 需要收集全局数据并为相对于全局数据部分 +32k 的 const5 和 const9 等名称赋值,并且
  • 启动代码需要将正确的值放入$gp

(常量 5 被赋予标签并随后被引用的确切机制转到目标代码中指定的重定位的详细信息,并且可能与这个过于简化的图示有所不同。)


RISC V 有类似的指令,lui(加上另一个 auipc)和各种 lw。它通常使用 2 条指令序列来访问全局数据,但是使用一种称为 relaxation 的机制,link er 可以执行 link 时间优化,将代码序列从 2 条指令减少到 1 条指令,只要访问将触手可及。这种放松机制解决了程序具有超过 64k 数据的问题,同时仍然尽可能允许 1 条指令访问。它相当复杂,因为 linker 实际上会改变代码段的大小(即从某些目标代码的中间删除代码),这意味着编译器现在也必须为内部分支生成重定位。

理论上,链接器松弛可以应用于任何指令集体系结构,这些体系结构具有更短的方式来表达相同的全局访问。