auipc
auipc
rd, imm
rd = pc + (imm << 12)
R
31–25funct7
24–20rs2
19–15rs1
14–12funct3
11–7rd
6–0opcode
I
31–20imm[11:0]
19–15rs1
14–12funct3
11–7rd
6–0opcode
S
31–25imm[11:5]
24–20rs2
19–15rs1
14–12funct3
11–7imm[4:0]
6–0opcode
B
31–25imm[12,10:5]
24–20rs2
19–15rs1
14–12funct3
11–7imm[4:1,11]
6–0opcode
U
31–12imm[31:12]
11–7rd
6–0opcode
J
31–12imm[20,10:1,11,19:12]
11–7rd
6–0opcode

auipc is the sibling of lui, and its name spells out the difference: add upper immediate to PC. Like lui, it takes a 20-bit constant and treats it as the upper portion of a value. But instead of producing that value on its own, auipc adds it to the program counter — the address of the instruction currently running (introduced on the beq page). The result is an address measured *relative to where this instruction sits in memory*.

That relative quality is the whole purpose, and it solves a real problem. A program does not always know in advance where in memory it will be loaded. If code referred to its data by fixed absolute addresses, it would break whenever it was loaded somewhere else. By computing addresses relative to the current instruction instead, the code keeps working no matter where it lands — a property called position independence.

auipc supplies the coarse part of such an address, and a following instruction adds the fine 12-bit remainder. This pairing is what the la instruction (load the address of a label) is built from, and it is also how calls to distant functions are made.

You will rarely write auipc by hand — la and call exist precisely so you do not have to — but you will see it constantly in the simulator's trace and in disassembled code. Recognizing the pattern of an auipc followed by an instruction with a small offset, the two together forming one address, is a useful reading skill.