# RISC-V build 32-bit constants with LUI and ADDI

10

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
``````

But a problem occurred, ADDI will extend sign to make a immediate data to a signed number, so `0xfff` will be extend to `0xffffffff`.

It make `x2` to `0xffffefff` but not `0xffffffff`

and what is an good implementation to move a 32bits immediate to register?

riscv

8

The RISC-V assembler supports the pseudo-instruction `li x2, 0xFFFFFFFF`.

Let `N`is 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

LUI x2,K

``````

Of course, to load short immediate `li` can use

``````   addi x2,x0,imm
``````

So, `li x2, 0xFFFFFFFF` is `addi x2,x0,-1`.

6

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 `LUI X`.

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".

3

TL;DR: The 32-bit constant you want to load into `x2` is `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` pseudoinstruction: `li, x2, -1` which the assembler, in turn, translates to `addi x2, zero, -1`.

### Loading a 32-bit constant with a `lui`+`addi` sequence

In general, we need a `lui`+`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. `lui` and `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 `addi` instruction

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 integersintegers 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 `li`pseudoinstruction

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 `lui`+`addi` sequence.

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 `lui`+`addi` sequence and the complication explained above is handled automatically by the assembler.

User contributions licensed under CC BY-SA 3.0