立即|Myriad序列|在RISC-V非特权手册中,它写道有一个伪指令叫做li
或Load immediate
,但它只说基本指令是Myriad sequences
,在谷歌搜索后,没有给出它是什么的有希望的答案。
有人知道Myriad sequences
是什么吗?li
扩展到了什么?
同样在玩了一段时间的godbolt,我看到,使以下功能:
int func(void) {
return 0x12345LL;
}
给出asm输出(带-O2
标志):
func:
li a0,73728
addi a0,a0,837
ret
但如果我选中compile to binary
选项,godbolt会给我
main:
lui a0,0x12
addi a0,a0,837 # 12345 <__BSS_END__+0x30d>
ret
这是否意味着li
被扩展到addi
?或者是链接器做了一些优化?
2条答案
按热度按时间qni6mghb1#
如果您使用汇编语言编程,或者编译器编写器生成汇编代码,您可以选择使用
li
伪指令,也可以选择避免使用它。对于32位整数,它将生成
lui
+addi
的某种组合,尽管根据常量的值可以省略其中的任何一个。li
的立即数很小,可以放入有符号的12位字段,那么addi
就足够了,尽管汇编程序也可以使用ori
。li
的立即数是0x 1000的倍数,那么lui
也可以单独处理该作业。对于大于32位的常量,序列的选择可以是3条或更多的指令。我们已经看到编译器为某些大常量生成了工作但次优的代码序列。
在构造长立即数所用的寄存器数量和所需的指令数量之间存在一个权衡--有时使用额外的寄存器可以缩短代码序列,从而重用中间值。也可以进行其他优化。例如再利用(如在公共子表达式消除或循环不变代码移动中)用于生成两个单独的较大立即数的中间值。
然而,尽管编译器和汇编程序员可以选择使用更多寄存器以使用更少指令(因为它们知道编译中的其它寄存器使用)汇编程序本身不能进行这种折衷,因为它们不跟踪函数内或函数之间的寄存器使用。汇编程序因此限于使用一个临时寄存器,即X1 M8 N1 X的目标作为临时来构造常数。
许多聪明的序列都可以构造立即数。
addi
后左移可能适合于12位或更少非零位但LSB为零的常量(即使相同指令长度的lui
+addi
也可以工作)。这些序列变化对于较大(〉32位)的常量值更为相关。消费者不需要汇编器使用定义良好的序列来生成立即数,因此RISC V规范省略了任何规范,并遵从汇编器的实现。
事实上,RISC V规范超越了大多数伊萨规范,纳入(和标准化)了许多有用的伪指令(如
ret
和call
)的规范,这些伪指令实际上并不是ISA的一部分,但通过帮助巩固某些约定,确实有助于汇编编程和硬件优化(如影子调用堆栈)。lui
+addi
序列有一些奇怪之处,如下所示:addi
使用12位有符号立即数,这意味着要构造一个设置了第12位的32位立即数,提供给lui
的常量必须偏置+1!这是因为来自addi
的12位立即数将符号扩展并变为负值,因此lui
中需要偏置+1。有人可能会问,为什么RISC V设计师使用符号扩展的
addi
?作为背景,我们注意到MIPS设计者选择了两种形式的符号扩展,有符号扩展用于
addi
,无符号扩展用于ori
。因此对于某些序列,MIPS可以避免+1/-1问题。然而,他们也希望能够将立即数的较低部分放入lw
和sw
中可用的偏移量中。而且这些都有符号扩展立即数(这是出于其他原因而需要的),所以在lui
+lw
中使用立即数所需的所有机制都是需要的,这意味着在某些情况下lui
的偏差为+1。RISC V接受了这一点--甚至完全消除了零扩展立即数以简化解码--因此无论如何,它们必须与所有
lui
+...组合的+1/-1偏差竞争。nukf8bse2#
“无数序列”不是一个专门的术语。
这是一个英语短语,意思是汇编器可以选择许多不同的方法来扩展
li
(取决于他们选择的常量和策略),只要得到相同的结果,所有这些方法都是允许的。搜索“无数序列”不会找到任何有用的东西;在这种情况下,正确方法是,如果您不熟悉“minimary”作为一个英语单词的含义,可以在字典中查找:google for
define myriad
从牛津英语词典中提取一个定义:形容词
无数的或者数量极大的。
例如“城市的万家灯火”
在您的示例中,
li a0,73728
显然扩展为lui a0, 0x12
,因为常量73728
是0x12000
。addi
已经存在于asm源代码中,因此在机器码中是按字面意思出现的。更常见的情况是,人们编写
li a0, 0x12345
,然后让汇编程序将其扩展为lui
+addi
,但您选择的编译器似乎只将li
用于实现常量的lui
部分。