在RISCV汇编中,“li”是伪指令。 我有这个说明:
li t2, 0x1800
csrc mstatus, t2
“li”被组装成以下2条指令。
lui x7 2
addi x7 x7 -2048
我的问题,为什么是 2 和 -2048?为什么“li”会组合成lui和addi? 有针对这种行为的文档吗?
我使用“riscv64-unknown-elf-as”作为汇编器。
有针对这种行为的文档吗?
这并不是真正经过考虑的行为,而是一个聪明但众所周知的代码序列,用于缩短汇编器和编译器使用的立即数的组合。
处理器的唯一行为是所有 I-Type 指令中 12 位立即数的符号扩展。
设计师这样做的原因是两件事的结合:
他们希望允许像
addi
这样的指令使用负立即数,以及lw
和sw
认为负偏移量足够有用,因为它们可以用于帧指针相对算术来访问局部变量,或者到达紧邻该块之前的块的标头等等。
更进一步,他们希望硬件只有一种12位扩展,即签名扩展。
这两点放在一起意味着
lui
和以下之一:addi
、lw
、sw
,可以完成完整的 32 位地址/值,所有工作原理相同:第二条指令的符号扩展可能需要增加用于 lui
的常量。
他们不必以这种方式构建它;他们可以这样做。例如,他们可以提供另一条指令
addui
,在相加之前清除高 20 位;或者,他们可以提供具有相同功能的 lw
和 sw
版本,或者定义 lw
和 sw
以仅支持 12 位无符号立即数。
但他们选择的是一种折衷方案,既允许一般的负立即数,又采用更简单的硬件替代方案。
设计人员竭尽全力简化硬件,同时考虑嵌入式和其他功率/尺寸有限的处理器
为什么是 2 和 -2048?
为了避免 addi 具有负 12 位数字的符号扩展功能,您必须将立即数限制为 11 位无符号,这将使第 12 位(符号位)为零,因此在 12 位中不会为负,所以永远不会扩展负号。例如,0x400 适合 11 位,因此我们可以这样做:
lui x7, 1
addi x7, x7, 0x400
addi x7, x7, 0x400
实现 0x1000 + 0x400 + 0x400 = 0x1800。
但是,正如您所看到的,这涉及三个指令!
为了缩短代码序列,我们必须利用额外的第 12 位(符号),即使它将被设置/开启/真/1/负,并且会导致该代码的高 20 位为 -1 值。使用前立即通过
addi
。
-1(由 12 位立即数的符号扩展引起的高 20 位)需要偏移 +1(高 20 位)才能获得所需的数字,并且 +1 偏移是在
中完成的lui
指令,因此用 lui x7, 2
代替 1,并用 addi x7, x7, 0x800
来完成 2 个指令序列。 0x800作为带符号的12位数字是-2048,所以:2和-2048:0x2000=8192; 8192+-2048=6144; 6144=0x1800.