使用 LUI 和 ADDI 了解 RISC-V 汇编中的 EQU 和 >> 运算符

Understand EQU and >> operators on them in RISC-V assembly, with LUI and ADDI

我的教授将此发布为家庭作业问题的答案之一。任何人都可以为我打破这个吗?我不明白他对 CON1 - CON4 做了什么以及 >> 和 0x0FFF 是什么意思。

CON1:   EQU 6000
CON2:   EQU 6245
CON3:   EQU 10000
CON4:   EQU 10245
A:  DM 4                         ; DM is Define Memory

    addi    t1,  x0, A           ; t1 = &A

    lui t0,  (CON1>>12) + ((CON1 & 0x0800)>>11)
    addi    t0,  t0, CON1&0xFFF
    sd  t0,  0(t1)            // Cut and paste from last question of Quiz1
                                      // Blank line between groups of statements
    lui t0,  (CON2>>12) + ((CON2 & 0x0800)>>11)
    addi    t0,  t0, CON2&0xFFF
    sd  t0,  8(t1)

    lui t0,  (CON3>>12) + ((CON3 & 0x0800)>>11)
    addi    t0,  t0, CON3&0xFFF
    sd  t0,  16(t1)

    lui t0,  (CON4>>12) + ((CON4 & 0x0800)>>11)
    addi    t0,  t0, CON4&0xFFF
    sd  t0,  24(t1)
                                      // We need this to avoid the NO INSTRUCTION error
    ebreak x0, x0, 0              ; Suspend program.

如有任何帮助,我们将不胜感激。我们正在使用 RISC-V

在RISC-V基本指令集中,每条指令都以32位编码。这意味着立即操作数的 space 被限制为几个位。因此,要将更大的常量放入寄存器(RV32G/RV64G 也是 32 位或 64 位宽),您需要将其拆分并使用多条指令移动部分,即 2 个 RV32G 和最多 8 个 RV64G。

使用 32 位 RISC-V (RV32G),可以使用 load upper immediate (lui) 和 add immediate 加载更大的常量 (addi) 指令。 lui 的立即操作数是 20 位宽,而 addi 允许 12 位的立即操作数。因此,它们足以加载最多使用 32 位的常量。

lui 对其直接操作数进行符号扩展并将其左移 12 位并将结果加载到目标寄存器中。因此它的名字。 addi 还在添加它之前对其直接操作数进行符号扩展。

因此,对于 RV32G,要用 lui 后跟 addi 加载更大的常量,必须取高 20 位,将它们逻辑右移 12 位,这样 12 lui 的位左移被抵消了。随后屏蔽低 12 位以获得 addi.

的操作数

如果 addi 不对其直接操作数进行符号扩展,这就足够了。如果是因为最高位设置为 1,我们必须增加 lui 操作数,以便在加法中再次将多余的符号位清零。

假设我们用 h 表示常量 x 的高位部分,用 l 表示低位部分,因为 RISC-V 在寄存器溢出时实现二进制补码和算术回绕,我们可以使用模块化算法看到:

     h + l = x                             # taking register widths into account:
 => (h + l) % 2**32  = x % 2**32           # in case l is sign extended:
 => (h + l + e + c) % 2**32  = x % 2**32   # replace e with the additional sign bits:
<=> (h + l + 4294963200 + c) % 2**32  = x % 2**32     # eliminate c:
<=> (h + l + 4294963200 + 4096) % 2**32  = x % 2**32
<=> (h + l) % 2**32  + (4294963200 + 4096) % 2**32  = x % 2**32
<=> (h + l) % 2**32  + 0  = x % 2**32

因此,当且仅当 addi 的立即操作数是符号时,我们必须将 1 添加到 lui 立即操作数(左移 12 位后等于 4096)扩展。

在您的汇编示例中,>> 表示右移,<< 左移,& 逻辑与。它们用于实现所描述的拆分和算术,例如在

 lui t0,  (CON1>>12) + ((CON1 & 0x0800)>>11)
 addi    t0,  t0, CON1&0xFFF

其中 CON1 & 0x0800 屏蔽了 12 位,即 addi 立即操作数的符号位。如果已设置,则 ((CON1 & 0x0800)>>11) 的计算结果为 1,从而抵消了由以下 addi 指令添加的多余符号位。 CON1&0xFFF 屏蔽最低 12 位。

在标准的 RISC-V 汇编中,所有这些繁琐的位管理都可以通过使用 load immediate (li) 伪指令来避免,例如:

li     t1, 6245

汇编程序自动将其转换为最佳指令序列(例如使用 objdump 检查):

lui    t1, 0x2
addi   t1, t1,-1947

或者,使用 GNU 作为汇编器,也有将操作数拆分为上部和下部的指令:

lui    a1, %hi(6245)
addi   a1, a1, %lo(6245)

可以说,这也比代码段中的混乱更具可读性。

这也适用于 GNU 中的符号,例如:

.set CON2, 6245

li    a1, 6245

lui   a2, %hi(CON2)
addi  a2, a2, %lo(CON2)

li    a3, CON2

# => a1 == a2 == a3