关于$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
将浮点寄存器中的整数转换为浮点值(在同一个或另一个浮点寄存器中),这样您就可以执行浮点加/减等操作。
与lw
和sw
一样,整型加载&在MIPS上,浮点加载和存储指令只有一种寻址模式。该寻址模式是基数+偏移量。有效地址就是基寄存器的内容+扩展的immediate/offset符号。虽然lwc1
加载到浮点寄存器,寻址模式仍然使用整数寄存器作为基数。
$gp
是一个称为全局(数据)指针的寄存器。当程序启动环境正确设置时,它指向从加载到内存中的全局数据段开始的+32k。这允许访问64k的全局数据,通过使用负和正的值为立即。在启动之后,程序不应该修改$gp
,这样它就可以被依赖于继续指向全局数据段。
如果程序被编译为使用$gp
,那么有一个风险,程序有超过64k的全局数据,在这种情况下,你应该得到一个链接器错误。$gp
允许机器代码独立于数据的实际位置,并且全局数据的访问序列只有一条指令的长度。(64k限制仅适用于全局数据,变量&常量,但不包括动态分配的堆数据)
使用$gp
访问全局数据的替代方法,例如当环境不支持它时,或者程序拥有超过64k的全局数据时,是使用绝对寻址,在MIPS上需要2条指令,其中第一条是lui
,第二条在addiu
,lw
或sw
之间变化。
为了使lwc1 const5($gp)
工作,
- 程序的目标代码(编译器输出)需要指定一些浮点常量作为全局数据,可能带有__const5 &__const9,
- 程序目标代码需要在访问这些标签时指定适当的重定位,
- 链接器需要收集全局数据,并给const5 &const9相对于全局数据段+32k,和
- 启动代码需要将正确的值放入
$gp
。
(给常量5一个标签并在后面引用它的确切机制将参考目标代码中指定的重定位细节,可能与这个过于简化的示例有所不同。)
RISC V有类似的指令,lui
(加上另一个auipc
)和各种lw
。它通常使用2条指令序列来访问全局数据,但是使用一种称为松弛的机制,链接器可以执行链接时间优化,将代码序列从2条指令减少到1条指令,只要访问是触手可及的。这种放松机制解决了程序拥有超过64k数据的问题,同时仍然允许在任何可能的情况下访问1条指令。这是相当复杂的,因为链接器实际上会改变代码段的大小(即从一些目标代码的中间删除代码),这意味着编译器现在必须为内部分支生成重定位。
从理论上讲,链接器松弛可以应用于任何指令集体系结构,只要它有更短的方式来表达相同的全局访问。