Caller-saved registers (also known as volatile registers, or call-clobbered) are used to hold temporary quantities that need not be preserved across calls. For that reason, it is the caller's responsibility to push these registers onto the stack or copy them somewhere else if it wants to restore this value after a procedure call.
Callee-saved registers (also known as non-volatile registers, or call-preserved) are used to hold long-lived values that should be preserved across calls. When the caller makes a procedure call, it can expect that those registers will hold the same value after the callee returns, making it the responsibility of the callee to save them and restore them before returning to the caller. Or to not touch them.
What are callee and caller saved registers?
x0 | zero | Hard-wired zero | — |
x1 | ra | Return address | Caller |
x2 | sp | Stack pointer | Callee |
x3 | gp | Global pointer | — |
x4 | tp | Thread pointer | — |
x5-7 | t0-2 | Temporaries | Caller |
x8 | s0, fp | Saved register, frame pointer | Callee |
x9 | s1 | Saved register | Callee |
x10-11 | a0-1 | Function arguments, return values | Caller |
x12-17 | a2-7 | Function arguments | Caller |
x18-27 | s2-11 | Saved registers | Callee |
x28-31 | t3-6 | Temporaries | Caller |
f0-7 | ft0-7 | Floating point temporaries | Caller |
f8-9 | fs0-1 | Floating point saved registers | Callee |
f10-11 | fa0-1 | Floating point arguments, return values | Caller |
f12-17 | fa2-7 | Floating point arguments | Caller |
f18-27 | fs2-11 | Floating point saved registers | Callee |
f28-31 | ft8-11 | Floating point temporaries | Caller |
Level | Name | Abbrev. |
---|---|---|
Debug | D | |
00 | User | U |
01 | Supervisor | S |
10 | Reserved | |
11 | Machine | M |
Debug is a special processor mode used for external debugging. It offers full control over the hardware, access to debug registers, and access to reserved memory regions.
0x7B0 | DRW | dcsr | Debug control and status register |
0x7B1 | DRW | dpc | Debug program counter |
0x7B2 | DRW | dscratch0 | Debug scratch register 0 |
0x7B3 | DRW | dscratch1 | Debug scratch register 1 |
sret | Return from a trap |
wfi | Wait for interrupt |
sfence.vma | TLB flush |
la rd, sym | auipc rd, sym[31:12] addi rd, rd, sym[11:0] |
Load address (.option nopic) |
la rd, sym | auipc rd, sym@GOT[31:12] l[wd] rd, sym@GOT[11:0](rd) |
Load address (.option pic) |
lla rd, sym | auipc rd, sym[31:12] addi rd, rd, sym[11:0] |
Load local address |
lga rd, sym | auipc rd, sym@GOT[31:12] l[wd] rd, sym@GOT[11:0](rd) |
Load local address |
l[bhwd] rd, sym | auipc rd, sym[31:12] l[bhwd] rd, sym[11:0](rd) |
Load global |
s[bhwd] rd, sym, rt | auipc rt, sym[31:12] s[bhwd] rd, sym[11:0](rt) |
Store global |
fl[wd] | auipc rd, sym[31:12] fl[wd] rd, sym[11:0](rt) |
Floating-point load global |
fs[wd] | auipc rd, sym[31:12] fs[wd] rd, sym[11:0](rt) |
Floating-point store global |
nop | addi x0, x0, 0 | No operation |
li rd, imm | Myriad sequences | Load immediate |
mv rd, rs | addi rd, rs, 0 | Copy register |
not rd, rs | xori rd, rs, -1 | One's complement |
neg rd, rs | sub rd, x0, rs | Two’s complement |
negw rd, rs | subw rd, x0, rs | Two’s complement word |
sext.b rd, rs | slli rd, rs, XLEN - 8 srai rd, rd, XLEN - 8 |
Sign extend byte |
sext.h rd, rs | slli rd, rs, XLEN - 16 srai rd, rd, XLEN - 16 |
Sign extend half word |
sext.w rd, rs | addiw rd, rs, 0 | Sign extend word |
zext.h rd, rs | slli rd, rs, XLEN - 16 srli rd, rd, XLEN - 16 |
Zero extend half word |
zext.w rd, rs | slli rd, rs, XLEN - 32 srli rd, rd, XLEN - 32 |
Zero extend word |
seqz rd, rs | sltiu rd, rs, 1 | Set if equal |
snez rd, rs | sltu rd, x0, rs | Set if not equal zero |
sltz rd, rs | slt rd, rs, x0 | Set if less than zero |
sgtz rd, rs | sltiu rd, x0, rs | Set if greater than zero |
fmv.s rd, rs | fsgnj.s rd, rs, rs | Copy single-precision register |
fabs.s rd, rs | fsgnjx.s rd, rs, rs | Single-precision absolute value |
fneg.s rd, rs | fsgnjn.s rd, rs, rs | Single-precision negate |
fmv.d rd, rs | fsgnj.d rd, rs, rs | Copy double-precision register |
fabs.d rd, rs | fsgnjx.d rd, rs, rs | Double-precision absolute value |
fneg.d rd, rs | fsgnjn.d rd, rs, rs | Double-precision negate |
beqz rs, off | beq rs, x0, off | Branch of equal zero |
bnez rs, off | bne rs, x0, off | Branch if not equal zero |
blez rs, off | bge x0, rs, off | Branch if less than or equal zero |
bgez rs, off | bge rs, x0, off | Branch if greater than or equal zero |
bltz rs, off | blt rs, x0, off | Branch if less than zero |
bgtz rs, off | blt x0, rs, off | Branch if greater than zero |
bgt rs, rt, off | blt rt, rs, off | Branch if greater than |
ble rs, rt, off | bge rt, rs, off | Branch if less than or equal |
bgtu rs, rt, off | bltu rt, rs, off | Branch if greater than, unsigned |
bleu rs, rt, off | bgeu rt, rs, off | Branch if greater than or equal, unsigned |
j off | jal x0, off | Jump |
jal off | jal x1, off | Jump and link |
jr rs | jalr x0, rs, 0 | Jump register |
jalr rs | jalr x1, rs, 0 | Jump and link register |
ret | jalr x0, x1, 0 | Return from subroutine |
call off | auipc x6, off[31:12] jalr x1, x6, off[11:0] |
Call far-away subroutine |
tail off | auipc x6, off[31:13] jalr x0, x6, off[11:0] |
Tail call far-away subroutine |
fence | fence iorw, iorw | Fence on all memory and I/O |
rdinstret[h] rd | csrrs rd, instret[h], x0 | Read instructions-retired counter |
rdcycle[h] rd | csrrs rd, cycle[h], x0 | Read cycle counter |
rdtime[h] rd | csrrs rd, time[h], x0 | Read real-time clock |
csrr rd, csr | csrrs rd, csr, x0 | Read CSR |
csrw csr, rs | csrrw x0, csr, rs | Write CSR |
csrs csr, rs | csrrs x0, csr, rs | Set bits in CSR |
csrc csr, rs | csrrc x0, csr, rs | Clear bits in CSR |
csrwi csr, imm | csrrwi x0, csr, imm | Write CSR, immediate |
csrsi csr, imm | csrrsi x0, csr, imm | Set bits in CSR, immediate |
csrci csr, imm | csrrci x0, csr, imm | Clear bits in CSR, immediate |
frcsr rd | csrrs rd, fcsr, x0 | Read FP control/status register |
fscsr rd, rs | csrrw rd, fcsr, rs | Swap FP control/status register |
fscsr rs | csrrw x0, fcsr, rs | Write FP control/status register |
frrm rd | csrrs rd, frm, x0 | Read FP rounding mode |
fsrm rd, rs | csrrw rd, frm, rs | Swap FP rounding mode |
fsrm rs | csrrw x0, frm, rs | Write FP rounding mode |
fsrmi rd, imm | csrrwi rd, frm, imm | Swap FP rounding mode, immediate |
fsrmi imm | csrrwi x0, frm, imm | Write FP rounding mode, immediate |
frflags rd | csrrs rd, fflags, x0 | Read FP exception flags |
fsflags rd, rs | csrrw rd, fflags, rs | Swap FP exception flags |
fsflags rs | csrrw x0, fflags, rs | Write FP exception flags |
fsflagsi rd, imm | csrrwi rd, fflags, imm | Swap FP exception flags, immediate |
fsflagsi imm | csrrwi x0, fflags, imm | Write FP exception flags, immediate |
OpenSBI loads firmware at the address 0x80000000
. It then
performs initial setup, does hart lottery which selects a single
hart that will continue the boot process. Other harts are put into a
loop and continuosly wait for interrupts. It then transfers control to
the supervisor, at address 0x80200000
.
.option push .option norelax la gp, _global_pointer .option pop la sp, _stack_end add fp, sp, zero j main
Licensed under CC BY-SA 4.0.