Instruction
RET
Returns from a procedure: pops the return address (and CS for a far return) and optionally releases stack bytes of the caller's arguments.
- Extensions
- Forms
- 4 (4 Legacy)
- Data
- Intel XED v2026.08.23
RET pops 8 bytes and jumps there. Nothing checks that a CALL put them on the stack, so a function whose return address was overwritten, by a buffer overflow for example, returns wherever the new bytes point. CET’s shadow stacks close that hole: when they are enabled, RET also pops the copy that CALL wrote to the shadow stack and raises #CP if the two differ. Intel and AMD both have shadow stacks; on Linux the loader turns them on for programs whose ELF notes say they are built for it.
The forms:
- RET (C3) is the return of all ordinary 64-bit code.
- RET imm16 (C2) adds imm16 to RSP after the pop, releasing the
stack arguments that the caller passed. 32-bit conventions in which
the callee cleans up, such as
__stdcall(see the 32-bit Windows conventions), end in it; in the 64-bit ABIs the caller owns its argument area, so compilers emit a plain RET. - Far RET (CB, CA) pops RIP and then CS. Its default operand size
in 64-bit mode is 32 bits, not 64: without REX.W it pops a 4-byte
offset and a 4-byte slot for CS, so a frame built with two 8-byte
PUSHes, as when 64-bit code enters a compatibility-mode segment,
needs the REX.W form (
lretqin AT&T syntax).
Things to know:
- Faults. A non-canonical return address raises #GP(0) at the RET, before RSP moves, as Zen 5 recorded: the bad address is still on top of the stack.
- Prediction. Processors predict a RET from a stack of the addresses that CALLs pushed (52 entries per thread on Zen 5, by AMD’s optimization guide). A RET whose address did not come from its matching CALL, such as a PUSH of an address followed by RET to jump there, is mispredicted; Intel’s optimization manual asks for calls and returns in pairs.
- 66 prefix. Intel’s manual fixes the operand size of near branches at 64 bits in 64-bit mode. AMD’s lets a 66 prefix make a near RET 16 bits: a 2-byte pop, with RIP cut to 16 bits.
- Straight-line speculation. Intel’s manual promises that the bytes after a near RET are not executed speculatively unless they are also a branch target or a predicted one. It makes no such promise after an indirect JMP or CALL.
In the optimization guide
What the optimization guide says about RET, a line per claim. Each links to the claim in the guide, with its sources and where they differ.
Branches
- Calls and returns: Returns are predicted by a return stack, which every call pushes and every return pops.
- Calls and returns: The return stack predicts returns only while calls and returns pair up, so pushing an address and jumping, or returning to a computed address, costs mispredictions.
- Calls and returns: A call that is the last thing a function does can become a jump to the callee, whose return then goes straight to the original caller.
- Calls and returns: Even predicted calls and returns cost time: each leaves the predictor idle for a cycle on Zen 3 and Zen 4, a return for two on Zen 5, and Agner Fog measured one every two cycles.
Bulldozer to Excavator
- Branches: The return address stack has 24 entries, so each
callshould have itsret: a deeper chain loses the oldest and an unpaired call desynchronises it, except a call to the very next instruction, which the core recognises.
Jaguar and Puma
- Branches: A 16-entry return stack that a call to the next instruction puts out of step, and no need for rep ret.
Sandy Bridge and Ivy Bridge
- Branches: The return stack buffer holds 16 near return addresses.
Silvermont to Goldmont Plus
- Branches: The return stack buffer holds 16 entries on Silvermont and Goldmont, twice the in-order Atom’s.
Skylake to Cooper Lake
- The JCC erratum: Under rare conditions a jump that crosses a 64-byte line could make these cores misbehave (erratum SKX102).
Zen and Zen+
- Branches: The return address stack has 32 entries, of which 31 can be used, and 15 per thread with two threads.
Zen 2
- Branches: The return address stack has 32 entries, 31 of them usable, and 15 per thread with two threads.
Zen 3
- Branches: The return address stack has 32 entries per thread.
- Integer execution: PUSH, POP, CALL and RET update RSP without a dependency, and loads, stores,
leaandmovfrom RSP can use the tracked value.
Zen 4
- Branches: The return address stack has 32 entries per thread, against 52 on Zen 5.
- Integer execution: PUSH, POP, CALL and RET update RSP without a dependency, and loads, stores,
leaandmovfrom RSP can use the tracked value.
Zen 5
- Branches: The return address stack has 52 entries per thread.
- Integer execution: PUSH, POP, CALL and RET update RSP without a dependency, and loads, stores,
leaandaddorsubof a small immediate through RSP can use the tracked value.
Vulnerability mitigations
Mitigations of these processor vulnerabilities use RET:
- CVE-2022-23824: Incomplete Branch Prediction Barrier (IBPB and Return Stack Buffer Interactions)
- CVE-2020-0551: Load Value Injection
Forms
| Instruction | Encoding | Requires |
|---|---|---|
RET imm16
RET_NEAR_IMMw | C2 iw | Base instruction set |
RET
RET_NEAR | C3 | Base instruction set |
RET imm16
RET_FAR_IMMw | CA iw | Base instruction set |
RET
RET_FAR | CB | Base instruction set |
Latency and throughput
| Model | imm16 | RET |
|---|---|---|
| Sandy Bridge | 6 / 1p5 p23 | 1 / 1p5 |
| Haswell | 1 / 2p012356 | 7 / 1p012356 |
| Broadwell, Skylake, Skylake-SP, Ice Lake | 6 / 0.5p0236 | 7 / 1p012356 |
| Alder Lake, Sapphire Rapids | 6 / 1p0 p2 p3 p6 p10 | 7 / 0.5p0 p2 p3 p6 p10 |
| Lunar Lake | 6 / 0.67p0 p2 p4 p20 p21 p22 | 7 / 0.33p0 p2 p4 p20 p21 p22 |
| Bonnell | 1 / 1p01 | 79 / 39.5p01 |
| Silvermont | 4 / 1IEC1 MEC | 4 / 1IEC1 MEC |
| Piledriver | 5 / 1.5AGLU01 Branch EX1 Load | 5 / 1.5AGLU01 Branch EX1 Load |
| Zen | 5 / 0.5AGU0 AGU1 ALU0 ALU1 ALU2 ALU3 | 1 / 0.5ALU0 ALU3 |
| Zen 2 | 5 / 0.33AGU0 AGU1 AGU2 ALU0 ALU1 ALU2 ALU3 | 1 / 0.5ALU0 ALU3 |
| Zen 3, Zen 4 | 5 / 0.5AGU0 AGU1 AGU2 ALU0 BRU1 LSU Load | 5 / 0.5AGU0 AGU1 AGU2 ALU0 BRU1 LSU Load |
| Jaguar | 4 / 1ALU0 ALU1 LAGU | 4 / 1ALU0 ALU1 LAGU |
Operation
What the forms do, in pseudocode that our tests run against results recorded on real processors. How to read.
RETw = OSIZE # the size of the return address:
if OSIZE == 16: # with 66, 2 bytes on AMD's (APM,
if INTEL: # Zen 5)
w = 64 # ignored: SDM
t = load(rsp, w) # 8 bytes, or 2 zero-extended
if not canonical(t):
fault(GP, 0) # RSP stays as it was
rsp = rsp + w // 8
rip = tRun on AMD Zen 5 (EPYC 9645): 4 cases. Its branches for Intel's processors were not run: no Intel processor ran these forms.
RET imm16w = OSIZE # as RET's
if OSIZE == 16:
if INTEL:
w = 64 # ignored: SDM
t = load(rsp, w)
if not canonical(t):
fault(GP, 0)
rsp = rsp + w // 8 + size # and size bytes more
rip = tRun on AMD Zen 5 (EPYC 9645): 6 cases. Its branches for Intel's processors were not run: no Intel processor ran these forms.
RETn = OSIZE // 8
off = load(rsp, OSIZE)
sel = load(rsp + n, OSIZE) & 0xFFFF
rsp = rsp + 2 * n # to an outer privilege level, RSP
cs = sel # and SS are popped too; with the
rip = off # checks of the descriptor
Not run on a processor: a far return loads CS from a descriptor table, which the probe does not record.
RET imm16n = OSIZE // 8
off = load(rsp, OSIZE)
sel = load(rsp + n, OSIZE) & 0xFFFF
rsp = rsp + 2 * n + size
cs = sel
rip = offNot run on a processor: a far return loads CS from a descriptor table, which the probe does not record.
Operands
Flags
None: XED records no flag that the forms test or change.
Attributes
What else XED records about the forms. How to read.
CPUID and processors
Each form belongs to an XED ISA set. A form can be used when the processor reports every CPUID bit of one of its ISA set's alternatives.
| ISA set | CPUID | Processors |
|---|---|---|
I86 | No CPUID bit: part of the base instruction set. | Every x86-64 processor in XED’s model and Zen 3 to Zen 6 |
More General-purpose instructions
Sources
- Intel XED v2026.08.23 (commit
0bcb6237345c): forms, encodings, operands, flags, exception classes, attributes, ISA sets, CPUID bits and chip model. - The CPU data of x86-64.net: Zen 3 to Zen 6, from GCC’s
-marchfeature sets and AMD’s Processor Programming References, which the page of each processor cites. - LLVM 23.1.2's x86 scheduling models (commit
85ac56026243): the latency, reciprocal throughput and ports of each form, as llvm-mca reports them. - Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 2 (325383-093, September 2026): Volume 2B, page 4-569 (RET)
- Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 1 (253665-093, September 2026)
- AMD64 Architecture Programmer's Manual, Volume 2: System Programming (24593, rev. 3.46, October 2026)
- AMD64 Architecture Programmer's Manual, Volume 3: General-Purpose and System Instructions (24594, rev. 3.39, October 2026)
- Linux kernel documentation: Control-flow Enforcement Technology (CET) Shadow Stack
- Intel 64 and IA-32 Architectures Optimization Reference Manual, Volume 1 (248966-051, October 2026)
- Software Optimization Guide for the AMD Zen5 Microarchitecture (58455, rev. 1.00, August 2024), Wayback Machine copy of AMD's ZIP
The tables are derived from Intel XED, Copyright Intel Corporation, licensed under the Apache License 2.0; x86-64.net converted and reformatted them. The timings come from LLVM's scheduling models, part of the LLVM Project, licensed under the Apache License 2.0 with LLVM Exceptions. The text is our own.