使用 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
我的教授将此发布为家庭作业问题的答案之一。任何人都可以为我打破这个吗?我不明白他对 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