RISC-V build 32-bit constants with LUI and ADDI
risc-v instruction set list
blt in risc v
LUI (load upper immediate) is used to build 32-bit constants and uses the U-type format. LUI places the U-immediate value in the top 20 bits of the destination register rd, filling in the lowest 12 bits with zeros.
I found this in manual, but if I want to move 0xffffffff to a register, all the code I need is:
LUI x2, 0xfffff000 ADDI x2, x2, 0xfff
But a problem occurred, ADDI will extend sign to make a immediate data to a signed number, so
0xfff will be extend to
0xffffefff but not
and what is an good implementation to move a 32bits immediate to register?
The RISC-V assembler supports the pseudo-instruction
li x2, 0xFFFFFFFF.
Nis a signed, 2's complement 32 bit integer.
Common case implementation of
li x2,N is:
# sign extend low 12 bits M=(N << 20) >> 20 # Upper 20 bits K=((N-M) >> 12) <<12 # Load upper 20 bits LUI x2,K # Add lower bits ADDI x2,x2,M
Of course, to load short immediate
li can use
li x2, 0xFFFFFFFF is
riscv - RISC-V build 32-bit constants with LUI and ADDI, LUI (load upper immediate) is used to build 32-bit constants and uses the U-type format. LUI places the U-immediate value in the top 20 bits of the destination In RISC-V instruction set manual, the shift Immediate instructions: SLLI (Shift Left Logical Immediate). SRLI (Shift Right Logical Immediate). SRAI (Shift Right Arithmetic Immediate). It is mentioned in the manual . Shifts by a constant are encoded as a specialization of the I-type format.
I was going to say "use
ORI instead of
ADDI" but then I read the Instruction Set Manual and it turns out that that doesn't work either, because all of the lower-12 Immediate operands get sign-extended, even for logical operations.
AFAICT you have to bias the value you put into the upper 20 bits in a way that anticipates the effect of the instruction you use to set the lower 12 bits. So if you want to end up with a value X in the top 20 bits and you're going to use
ADDI to set the lower 12 bits, and those lower 12 bits have a 1 in the leftmost position, you must do
LUI (X+1) rather than
LUI X. Similarly if you are going to use
XORI to set the lower 12 bits, and those lower 12 bits have a 1 in the leftmost position, you must do
LUI (~X) (that is, the bitwise inverse of X) rather than
But before you do any of that, I'd look to see whether your assembler already has some sort of "load immediate" pseudo-op or macro that will take care of this for you. If it doesn't, then see if you can write one :-)
It's not unusual for RISC processors to need this kind of extra effort from the programmer (or, more usually, from the compiler). The idea is "keep the hardware simple so it can go fast, and it doesn't matter if that makes it harder to construct the software".
[PDF] The RISC-V Instruction Set Manual, ADDI adds the sign-extended 12-bit immediate to register rs1. LUI (load upper immediate) is used to build 32-bit constants and uses the LUI (load upper immediate) is used to build 32-bit constants and uses the U-type format. LUI places the U-immediate value in the top 20 bits of the destination register rd, filling in the lowest 12 bits with zeros. It places the "U-immediate value" in the top 20 bits. What is the "U-immediate value"? In the figure above the quoted text I can see:
TL;DR: The 32-bit constant you want to load into
0xffffffff which corresponds to -1. Since -1 is in the range [-2048, 2047], this constant can be loaded with a single instruction:
addi x2, zero, -1. You can also use the
li, x2, -1 which the assembler, in turn, translates to
addi x2, zero, -1.
Loading a 32-bit constant with a
In general, we need a
addi sequence – two instructions – for loading a 32-bit constant into a register. The
lui instruction encodes a 20-bit immediate, whereas the
addi instruction encodes a 12-bit immediate.
addi can be used to load the upper 20 bits and the lower 12 bits of a 32-bit constant, respectively.
Let N be a 32-bit constant we want to load into a register: N ≡ n31 ... n0. Then, we can split this constant into its upper 20 bits and lower 12 bits, NU and NL, respectively: NU ≡ n31 ... n12 ; NL ≡ n11 ... n0
In principle, we encode NU in the immediate in
lui and NL in the immediate in
addi. Nevertheless, there is a difficulty to handle if the most significant bit of the 12-bit immediate in
addi is 1 because the immediate value encoded in the
addi instruction is sign extended to 32 bits. If this is the case, the
addi instruction adds to the destination register not NL, but NL - 4096 instead — -4096 (or -212) is the resulting number when the upper 20 bits are 1s and the lower 12 bits are 0s.
To compensate for the unwanted term -4096, we can add 1 to
lui's immediate – the LSB of the immediate in
lui corresponds to bit #12 – so, adding 1 to this immediate results in adding 4096 to the destination register which cancels out the -4096 term.
Loading a 32-bit constant with a single
The issue explained above is due to the sign extension that the immediate in
addi undergoes. The decision of sign extending
addi's immediate was probably to allow the loading of small integers – integers between -2048 and 2047, both inclusive – with a single
addi instruction. For example, if the immediate in
addi were zero extended instead of sign extended, it wouldn't be possible to load such a frequent constant like -1 into a register with just a single instruction.
Loading a 32-bit constant with the
In any case, you can always use the
li pseudoinstruction for loading a 32-bit constant without having to care about what the value of the constant to load is. This pseudoinstruction can load any 32-bit number into a register, and it is, therefore, simpler to use and less error-prone than manually writing the
If the number fits in
addi's immediate field ([-2048, 2047]), the assembler will translate the
li pseudoinstruction into just an
addi instruction, otherwise,
li will be translated into a
addi sequence and the complication explained above is handled automatically by the assembler.
[PDF] RISC-V Instruction Formats, Divide the 32 bits of an instruction into. “fields”. – regular By convention, RISCV instructions are each. 1 word = 4 bytes addi, lw, jalr, slli. • S-Format: lui, auipc —upper immediate is 20-bits imm[11:0] can hold values in range [-2. 11. , +2 executable, we can compile them independently and then RISC-V handles 32-bit constants and addresses with instructions that set the upper 20 bits of a 32-bit register. Load upper immediate lui loads 20 bits into bits 31 through 12. Then a second instruction such as addi can set the bottom 12 bits.
48-bit LI-type instruction format (Large Immediate), RISC-V's base 32-bit instruction format and prevalent 12-bit I-Type only used for sign extended 32-bit constant building (LUI+ADDI). LUI is 32-bit constants can be loaded to a register with LUI + ORI/ADDI. For larger constants more registers are needed and the immediate results are shifted and or'd together to form the larger constant.
Load a large immediate constant in ASM : RISCV, 32-bit constants can be loaded to a register with LUI + ORI/ADDI. For larger constants more registers are needed and the immediate results are shifted and or'd 4 Answers 4. active oldest votes. 7. The immediate argument passed to addi is only 16 bits. To load a 32-bit immediate value that is outside the range of a 16-bit value you need to do it in two goes, as in the example from your text book.
RISC-V Assembler Reference, For detailed information on the RISC-V instruction set refer to the RISC-V ISA Specification. .word, 32-bit comma separated words (naturally aligned) lui followed by addi and slli (shift left logical immediate) to construct constants by shifting This 32-bit form of UNIMP is emitted when targeting a system without the C extension, or when the .option norvc directive is used. Pseudo Ops Both the RISC-V-specific and GNU .-prefixed options.