我的教授将此作为家庭作业问题的答案之一发布。谁能帮我分解一下?我不明白他在用 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 位编码。这意味着立即数操作数的空间被限制在几位。因此,要将更大的常量放入寄存器(RV32G/RV64G 也是 32 位或 64 位宽),您需要拆分它并使用多条指令移动部分,即 RV32G 为 2 个,RV64G 为 8 个。

使用 32 位 RISC-V (RV32G),可以使用加载上位立即数 (lui) 和添加立即数 (addi) 指令加载更大的常量。 lui 的立即数操作数为 20 位宽,而 addi 允许 12 位的立即数操作数。因此,它们足以加载最多使用 32 位的常量。
lui 对其立即操作数进行符号扩展并将其左移 12 位并将结果加载到目标寄存器中。因此它的名字。 addi 还在添加它之前对其立即操作数进行符号扩展。

因此,对于 RV32G,要使用 lui 后跟 addi 加载更大的常量,必须取高 20 位,将它们逻辑右移 12 位,以便抵消 lui 左移的 12 位。紧接着对低 12 位进行掩码以获取 addi 的操作数。

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

假设我们用 x 表示常量 h 的高部分,用 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

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

在您的汇编示例中,>> 表示右移,<< 表示左移,& 表示逻辑与。它们用于实现所描述的 split 和算术,例如在
 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 汇编中,只需使用立即加载( 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

关于assembly - 理解教授对 assembly 作业的回答,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59869014/

10-11 19:00