在RISC-V非特权手册中,它写道有一个名为的伪指令li:
li rd, immediate | Myriad sequences | Load immediate
Run Code Online (Sandbox Code Playgroud)
但它只说基本指令是Myriad sequences,在谷歌搜索后,没有给出它是什么的有希望的答案。
有谁知道什么Myriad sequences是以及li扩展到什么?
另外,在玩了一段时间 Godbolt 后,我发现可以实现以下功能:
li rd, immediate | Myriad sequences | Load immediate
Run Code Online (Sandbox Code Playgroud)
给出 asm 输出(带-O2标志):
func:
li a0,73728
addi a0,a0,837
ret
Run Code Online (Sandbox Code Playgroud)
但如果我勾选这个compile to binary选项,godbolt 就会给我
main:
lui a0,0x12
addi a0,a0,837 # 12345 <__BSS_END__+0x30d>
ret
Run Code Online (Sandbox Code Playgroud)
这是否意味着li扩展到addi?或者是链接器做了一些优化?
li如果您正在使用汇编语言进行编程,或者编译器编写器生成汇编代码,您可以根据需要选择使用伪指令或避免使用伪指令。
对于 32 位整数,它将生成lui+的某种组合addi,但根据常量的值,可能会省略其中之一。
如果给定的立即数li很小并且可以容纳有符号的12位字段,addi则单独的值就足够了,尽管汇编器不会疏忽地使用ori它。
如果给定的立即数li是 0x1000 的倍数,那么lui单独也可以处理这项工作。
对于大于 32 位的常量,序列的选择可以是 3 个或更多指令。\xc2\xa0 我们已经看到编译器为某些大常量生成工作但次优的代码序列。
\n这里需要在用于构造大型立即数的寄存器数量和执行此操作所需的指令数量之间进行权衡 \xe2\x80\x94 有时使用额外的寄存器可以缩短代码序列,重用中间值。\xc2\ xa0 其他优化也是可能的,例如重用(如在公共子表达式消除或循环不变代码运动中)用于生成两个单独的较大立即数的中间值。
\n然而,虽然编译器和汇编程序员可以选择使用更多寄存器来使用更少的指令(因为他们知道编译中的其他寄存器使用情况),但汇编器本身无法进行这种权衡,因为他们不跟踪函数内部或函数之间的寄存器用法。\xc2\xa0 因此,汇编器仅限于使用一个暂存寄存器,即目标寄存器li临时构造常量的目标。
许多巧妙的序列都可以构造立即数。\xc2\xa0 Anaddi后跟左移可能适合 12 个非零位或更少但具有零位作为 LSB 的常量(即使在相同指令长度的lui+addi也将起作用)。\xc2\xa0 这些序列变化对于较大(>32 位)常量值变得更加相关。
消费者不需要汇编器使用定义明确的序列来生成立即数,因此 RISC V 规范省略了任何规范并遵循汇编器实现。
\n事实上,RISC V 规范超越了大多数 ISA 规范,纳入(并标准化)了许多有用的伪指令的规范,例如ret和 ,call这些伪指令实际上并不是 ISA 的一部分,但确实有助于汇编编程以及促进通过帮助巩固某些约定来进行硬件优化,例如影子调用堆栈。
lui+序列addi有一些奇怪之处,如下:addi使用 12 位有符号立即数,这意味着为了构造第 12 位设置的 32 位立即数,提供给 的常量lui必须偏置 +1!\ xc2\xa0 这是因为来自 will 符号的 12 位立即数扩展并变为负数,因此需要addi+1 的偏置。lui
有人可能会问为什么 RISC V 设计者要使用addi扩展符号?
作为背景,我们要注意 MIPS 设计者选择了两种形式的符号扩展,有符号addi和无符号/零ori。\xc2\xa0 因此,对于某些序列,MIPS 可以避免 +1/-1 问题。\xc2\xa0 然而,他们也希望能够将立即数的下半部分放入 和 中可用的偏移量中lw,并且这些立即数具有符号扩展立即数(出于其他原因,这是可取的)。 \xc2\xa0 因此,无论如何都需要在+中使用立即数所需sw的所有机制,意味着在某些情况下偏置为+1。luilwlui
RISC V 采用这个 \xe2\x80\x94 甚至完全消除了零扩展立即数以简化解码 \xe2\x80\x94 因此它们必须应对所有lui+ ... 组合的 +1/-1 偏置。