What Happens When a Function Is Called
Calling a Procedure (a function or subroutine) requires more than just jumping to its code. The processor must remember where to return afterward, pass input arguments, receive a result, and make sure the calling code's register values are not silently overwritten by the called procedure. RISC-V handles this through a defined convention rather than special hardware magic.
The Jump-and-Link Instruction
RISC-V provides a dedicated instruction for calling procedures that automatically saves the return location:
jal ra, ProcedureAddressThis instruction jumps execution to ProcedureAddress while simultaneously storing the address of the next instruction into a designated register, conventionally called ra (return address). When the procedure finishes, it uses this saved address to jump back:
jalr x0, 0(ra)This returns control to exactly the instruction that follows the original call.
Register Conventions for Passing Data
To keep procedure calls predictable across different compilers and programs, RISC-V follows a fixed Register Convention:
- A specific range of registers is reserved for passing
Argument Valuesinto a procedure. - A specific register is reserved for returning the
Result Valueback to the caller. - Some registers are designated as
Saved Registers, which a called procedure must preserve and restore before returning if it intends to use them. - Other registers are
Temporary Registers, which a called procedure is free to overwrite without any obligation to restore their original value.
The Stack: Handling Nested and Recursive Calls
A single set of registers is not enough when procedures call other procedures, or call themselves recursively. To handle this, RISC-V uses a region of memory called the Stack, managed through a dedicated Stack Pointer register.
Before a procedure overwrites a saved register it needs to reuse, it pushes that register's current value onto the stack; before returning, it restores the value by popping it back:
Reserve stack space:
addi sp, sp, -8
Save a register's value:
sd s0, 0(sp)
Restore it later:
ld s0, 0(sp)
Release stack space:
addi sp, sp, 8This mechanism allows an arbitrary depth of nested or recursive calls, since each call gets its own private area of stack memory for anything it needs to preserve.
Representing Text: Characters and Encoding
Beyond numbers, computers also need to represent human-readable text. Each individual character is stored using a numeric encoding standard, most commonly ASCII for basic Latin text or Unicode for a much broader range of scripts and symbols.
A sequence of characters forming text is called a String, and it is typically stored in memory as consecutive bytes, one per character, often ending with a special terminating value that marks where the string ends.
Why These Conventions Matter
Because register usage and stack management follow a strict, agreed-upon convention rather than being decided ad hoc by each program, code compiled by different compilers, or written in different languages, can call procedures written by each other correctly and safely — a property essential for building large software systems out of independently developed components.