tail

RISC-V tail Pseudo-Instruction Details

Assembler pseudo-instruction

Tail call pseudo-instruction, expands to AUIPC t1 + JALR x0 (no return address saved). It is used when the final action of a function jumps to another function, whose return goes directly to the current function's caller.

What You Write
tail symbol
Typical Real Expansion
auipc t1, offset[31:12] jalr x0, offset[11:0](t1) # t2 may replace t1 when Zicfilp is enabled

What This Pseudo Instruction Is Saving You From Writing

Enables tail-call optimization — when the last action of a function is calling another, jump directly to the target without saving a new return address. This assumes the current function no longer needs its local state and has satisfied calling-convention requirements before the tail jump.

tail primarily means "Tail call, does not update ra". It is assembler-level shorthand; when debugging, auditing, or reading machine code, reason from the real expansion and relocation semantics listed on this page.

Official Semantics Checklist

The official assembly manual treats tail as an assembler-level pseudo-instruction or alias; hardware executes the expanded real instruction sequence.
The real semantics come from the ISA definitions of AUIPC and the other expanded instructions, not from a separate tail hardware opcode.
For symbolic targets, final machine code can be affected by R_RISCV_CALL/R_RISCV_CALL_PLT relocations and linker relaxation.

Toolchain And Linker Boundaries

tail can involve symbolic relocations and linker relaxation like call, but rd=x0 means no new return address is produced.
The official assembly manual notes that the scratch register can change under extension conditions; do not hard-code t1 as the only visible form.

How To Read The Expansion

Step 1
AUIPC t1, offset[31:12]: adds the upper PC-relative offset to the current PC and stores the intermediate address in t1 (or t2 under Zicfilp).
Step 2
JALR x0, offset[11:0](t1): completes the jump via t1 + low 12-bit offset; rd=x0 means no return address is saved.
Step 3
Key difference from call: call writes ra (saving the return address), tail writes x0 (discarding it).

What You May See In objdump / Disassembly

The AUIPC t1 + JALR x0 pair is shown as tail symbol in disassembly. rd=x0 means no return address is saved, distinguishing it from call (rd=ra).

Official References And Reading Order

This page treats pseudo-instructions as assembler-level aliases or macros: first read what real instructions they expand to, then use the official ISA manual for the behavior of those real instructions. ABI, relocation, and linker-relaxation details follow the psABI document.

When To Think Of It First

Tail-recursion optimization (recursive function whose last action calls itself)
Wrapper/forwarder functions at function end
Continuation-passing style (CPS) in functional programming
Reduce unnecessary stack frame nesting depth

Pitfalls / Common Confusions

tail does not write ra — the target returns directly to the current function's caller; if the current function created a stack frame, restore the stack and saved registers before the tail jump
Do not assume the temporary register is always t1 — toolchains may use t2 when Zicfilp is enabled
Tail-call optimization requires the compiler or programmer to ensure no local variables of the current function are accessed after the call

FAQ

Is tail a real RISC-V instruction?

tail is an assembler pseudo-instruction or alias, not a separate hardware opcode. The “Typical Real Expansion” section lists the official expansion, and behavior is defined by the expanded ISA instructions.

What is the main trap when using tail?

tail does not write ra — the target returns directly to the current function's caller; if the current function created a stack frame, restore the stack and saved registers before the tail jump