1Learning Outcomes¶
Explain why U-Type is needed for building wide immediates.
Translate the pseudoinstruction
li(Load Immediate) into machine code.
🎥 Lecture Video
Recall that I-Type arithmetic instructions encode an sign extension 12-bit immediate as a two’s complement integer, which is then sign-extended to a 32-bit value. While constants are frequently short and fit into the 12-bit I-Type fields, we inevitably need to encode wide immediates to build numberic constants larger than the I-type’s 12-bit imm.
RISC-V achieves wide immediates as follows:
Specify 32-bit constants using two instructions: an I-Type arithmetic instruction for the 12-bit lower immediate, i.e., bits 0 to 11; and and a different instruction for the 20-bit upper immediate, i.e., bits 12 to 31. (Again, reduced instruction set!)
Define an instruction format, U-Type (the “U” is for Upper immediate), that can be used for instructions that specify upper immediates.
There are two U-Type instructions: lui and auipc. These are located in the “Other” section of the RISC-V green card.
2Load Upper Immediate (lui)¶
From the P&H textbook (Chapter 2.10), one such instruction, lui, is Load Upper Immediate:
...load a 20-bit constant into bits 12 through 31 of a register. The rightmost 12 bits are filled with zeros.
In Table 1, we call this upper immediate immu and the 32-bit immediate imm.
Table 1:The lui instruction.
| Instruction | Name | Description |
|---|---|---|
lui rd immu | Load Upper Immediate | imm = immu << 12R[rd] = imm |
As shown in Figure 1, imm = immu << 12; this 32-bit numeric constant imm is then written to register rd.

Figure 1:For U-Type instructions, immu is the top 20 bits of a 32-bit-wide numeric constant imm imm.
2.1Load Immediate Pseudoinstruction¶
With this new instruction, we can now return to translating the li pseudoinstruction, Load Immediate. When we introduced load immediate, you may have noticed a footnote in Table 3:
This description is incomplete given the range of
imminaddi. See the green-card and a later section for the full translation of load immediates.
In other words, when li must use a numeric constant wider than the 12 bits accommodated by the I-Type addi’s imm field it translates to two instructions: lui and addi.
| Pseudoinstruction | Name | Description | Translation |
|---|---|---|---|
li rd imm | Load Immediate | R[rd] = imm | lui (if needed), addi |
In other words, li translates to addi when the immediate is between -2048 and 2047. For anything larger, li translates to two instructions: lui and addi.
In general, the compiler or assembler will break large constants across the lui and addi instructions, as needed. After all, there are some important caveats to this process. Hover over the two footnotes below before continuing.
Show Explanation
luisetsx10to0x87654000. Note0x87654is 20 bits.addisetsx10to0x87654321 = 0x87654000 + 0x00000321, where the latter operand is the sign-extended version of the 12-bit immediate0x321.
Show Explanation
Let’s first consider the strawman[3], which does not work:
lui x10 0xB0BAC
addi x10 x10 0xAFERemember, the addi instruction sign-extends the 12-bit immediate imm. If the sign bit of imm is set, then imm is negative, subtracting 1 from the upper 20 bits set by the previous lui instruction.
luisetsx10to0xB0BAC000.addisetsx10to0xB0BAC000 + 0xFFFFAFE, because the 12-bit immediate0xAFEis a two’s complement negative number.x10is then set to0xB0BABAFE.
Figure 2 “cutely”[4] illustrates this “subtraction by one” using hexadecimal arithmetic and algebraic properties.

Figure 2:When addi’s immediate imm is signed, a naive lui immediate immu will yield an incorrect numeric constant.
To solve this, know that addi will always follow lui when resolving a li pseudoinstruction, and addi will always sign-extend its immediate imm. So if addi’s 12-bit imm field is negative, proactively add 1 to lui’s 20-bit immu field:
lui x10 0xB0BAD
addi x10 x10 0xAFEluisetsx10to0xB0BAD000.addisetsx10to0xB0BAD000 + 0xFFFFAFE, because the 12-bit immediate0xAFEis a two’s complement negative number.x10is then set to0xB0BACAFE.
3Add Upper Immediate to PC¶
Coming soon. Used for J-Type instructions.
Table 2:The auipc instruction.
| Instruction | Name | Description |
|---|---|---|
auipc rd immu | Add Upper Imm to PC | imm = immu << 12R[rd] = PC + imm |
4U-Type: Fields¶
In order to translate lui and auipc to machine code, we need an instruction format that supports 20-bit immediates.
The U-Type instruction format is only used by lui and auipc and is the one of the last rows of the instruction format table of the RISC-V green card.

Figure 3:The U-Type Instruction Format.
U-Type has only three fields (Figure 3):
Register operand: The U-Type field
rdis the destination register to put the immediate.Opcode: The U-Type field
opcodespecifies the operation:auipcorlui(Table 3).Constant operand: The upper immediate field (we’ll call it
immu) specifies a 20-bit immediate value.immuis left-shifted by 12 to form the upper 20 bits of a 32-bit numeric constantimm(Figure 1).
Table 3:RV32I Instructions: U-Type
| Instruction | imm[31:12] (“immu”) | rd | opcode |
|---|---|---|---|
auipc | imm[31:12] | rd | 0010111 |
lui | imm[31:12] | rd | 0110111 |