Subroutine linkage is the mechanism a processor uses to handle the call-and-return cycle — saving the return address on call, jumping to the subroutine, and resuming the caller on return. The simplest implementation uses a Link register; more complete schemes also handle argument passing, register preservation, and the stack.

The basic mechanism

The call instruction is a special branch with two effects:

  1. Save the return address (PC + 4) somewhere safe.
  2. Branch to the target address.

The ret instruction is a special branch with one effect:

  1. Branch to the saved return address.

The “somewhere safe” is what differs between linkage methods.

Used by Nios II, MIPS, ARM, RISC-V. The return address is saved in a dedicated Link register (r31 in Nios II).

Pros:

  • Fast — register write is cheaper than a memory store.
  • Leaf functions need no further bookkeeping.

Cons:

  • Single register holds only one return address. Nested calls overwrite it unless the caller saves it first.

Stack-based linkage

Used by x86. The return address is pushed onto the stack by call, popped by ret.

Pros:

  • Nested calls automatic — each push adds a new return address.

Cons:

  • Every call costs a memory write.

Putting it all together

A complete linkage scheme covers more than just return addresses. The full discipline (informally, the calling convention) addresses:

  1. Arguments — how the caller passes values to the callee (registers, stack, or both).
  2. Return value — which register holds the result.
  3. Caller-saved registers — registers the caller must save before calling, since the callee is allowed to clobber them freely.
  4. Callee-saved registers — registers the callee must preserve (save on entry, restore before return) so the caller can rely on them surviving the call.
  5. Stack frame layout — how locals, saved registers, and parameters are organized within the stack frame.

For Nios II:

ConventionRegisters
Argument registersr4, r5, r6, r7
Return valuer2 (and r3 for 64-bit)
Caller-savedr2..r15
Callee-savedr16..r23
Special / reservedr24..r31 (et, bt, gp, sp, fp, ea, ba, ra)
Stack pointerr27 (sp)
Frame pointerr28 (fp)
Link registerr31 (ra)

A subroutine following this convention:

MySub:
    # save callee-saved registers we'll use
    subi sp, sp, 12
    stw  ra,  8(sp)     # save return address (since we'll call others)
    stw  r16, 4(sp)     # save callee-saved register
    stw  r17, 0(sp)

    # ... do work ...

    # restore and return
    ldw  r17, 0(sp)
    ldw  r16, 4(sp)
    ldw  ra,  8(sp)
    addi sp, sp, 12
    ret

Why the convention matters

Subroutines compiled by different tools or written by different programmers have to inter-operate. If everyone follows the same calling convention, they can — your library function works whether called from C, hand-written assembly, or another library.

Violating the convention (clobbering a callee-saved register without restoring it, putting an argument in the wrong place) results in subtle, hard-to-debug failures: data appears to corrupt mysteriously, returns go to the wrong address, the stack pointer gets misaligned. The convention is invisible when followed and devastating when broken.

For the saving discipline used to enable nested calls without losing return addresses, see Subroutine nesting.