Instruction
IRET
Returns from an interrupt or exception handler, popping the instruction pointer, CS and the flags, and in 64-bit mode RSP and SS too.
- Extensions
- Mnemonics
- IRET, IRETD, IRETQ
- Forms
- 3 (3 Legacy)
- Data
- Intel XED v2026.08.23
IRET returns from an interrupt or exception handler: it pops the return address, CS and RFLAGS that the event pushed, and in 64-bit mode RSP and SS as well, always, even when the privilege level does not change. A handler can thus resume the interrupted code wherever it was, user or kernel, 64-bit or compatibility mode.
The size of the frame
The operand size sets the size of each of the five slots: 8 bytes for
IRETQ (48 CF), 4 for IRETD (CF), 2 with a 66 prefix. A 64-bit handler
needs IRETQ, and the mnemonic decides: in our builds, a bare iret in
Intel syntax became CF with a warning from the GNU assembler, and 66 CF,
the 16-bit form, from Clang 23. Zen 5 ran that 16-bit form in 64-bit
mode and loaded RIP and RSP from 2-byte slots, zero-extended: RIP 1234H
and RSP 40H.
What it refuses
Zen 5 recorded #GP, with nothing changed, for a frame with a null CS, a CS of RPL 0 at CPL 3, a data segment as CS, a null SS or one of RPL 0 for a return to CPL 3, a code segment as SS, a non-canonical RIP, a RIP past the 4 GiB limit of a compatibility-mode segment, and for any IRET while NT was set, since IA-32e mode has no task to return to. It did load RSP = 8000000000000000H, which is not canonical, without a fault: IRET itself does not check it.
Flags it may not change
At CPL 3 with IOPL 0, IRET leaves IF and IOPL as they were whatever the frame says, as Zen 5 showed with a frame holding IOPL 3 and IF 0. The status flags, TF, DF and NT come from the frame, and AC and ID too except in the 16-bit form.
The manuals differ on RSP
Returning to compatibility mode, Intel’s manual loads all of RSP from the frame, AMD’s only ESP, zero-extended. Zen 5 followed AMD’s: a frame RSP of 1234567810000040H gave RSP 10000040H.
Serializing, and NMIs
Both vendors list IRET among the serializing instructions that need no privilege, as CPUID is. IRET also ends the blocking of NMIs that an NMI’s delivery starts. With FRED, events return through ERETU and ERETS, and an IRET that would change CPL raises #GP. AMD’s Zen 5 guide lists IRET as microcoded.
Forms
| Instruction | Encoding | Requires |
|---|---|---|
IRET
IRET | 66 CF | Base instruction set |
IRETD
IRETD | CF | Base instruction set |
IRETQ 64-bit mode only
IRETQ | REX.W CF | LM |
Latency and throughput
| Model | IRETIRETDIRETQ |
|---|---|
| Sandy Bridge | 6 / 1p5 p23 |
| Haswell, Broadwell, Skylake, Skylake-SP, Ice Lake, Alder Lake | 6 / 0.5p0236 |
| Sapphire Rapids | 6 / 0.5p0 p2 p3 p6 p10 |
| Lunar Lake | 6 / 0.33p0 p2 p4 p20 p21 p22 |
| Bonnell | 1 / 1p01 |
| Silvermont | 4 / 1IEC1 MEC |
| Piledriver | 5 / 1.5AGLU01 Branch EX1 Load |
| Zen, Zen 2 | 1 / 0.5ALU0 ALU3 |
| Zen 3, Zen 4 | 5 / 0.5AGU0 AGU1 AGU2 ALU0 BRU1 LSU Load |
| Jaguar | 4 / 1ALU0 ALU1 LAGU |
Operation
What the forms do, in pseudocode that our tests run against results recorded on real processors. How to read.
if nt:
fault(GP, 0) # no task to return to in IA-32e mode
n = OSIZE // 8 # a field: 8 bytes for IRETQ, 4 for
# IRETD, 2 for IRET
t = load(rsp, OSIZE) # the return address
c = load(rsp + n, OSIZE) & 0xFFFF
f = load(rsp + 2 * n, OSIZE) # RFLAGS
new_sp = load(rsp + 3 * n, OSIZE) # 64-bit mode pops RSP and SS
s = load(rsp + 4 * n, OSIZE) & 0xFFFF # also when the CPL stays
pl = c & 3 # the CPL to return to: CS's RPL
if c & ~3 == 0:
fault(GP, 0) # a null CS
if pl < CPL:
fault(GP, c & ~3) # never to a more privileged level
d = descriptor(c)
if d >> 43 & 3 != 3:
fault(GP, c & ~3) # bits 44 and 43: not code
dpl = d >> 45 & 3
if d >> 42 & 1 and dpl > pl:
fault(GP, c & ~3) # conforming: a DPL up to the RPL
if not d >> 42 & 1 and dpl != pl:
fault(GP, c & ~3) # else the DPL is the RPL
if d >> 47 & 1 == 0:
fault(NP, c & ~3)
wide = d >> 53 & 1 # L: 64-bit code; else compatibility
# mode
lim = d & 0xFFFF | d >> 32 & 0xF0000
if d >> 55 & 1:
lim = lim << 12 | 0xFFF # G: in units of 4 KiB
if wide and not canonical(t):
fault(GP, 0)
if not wide and t > lim:
fault(GP, 0)
if s & ~3 == 0:
if pl == 3 or not wide:
fault(GP, 0) # a null SS: only to 64-bit code at
# CPL 0 to 2
else:
if s & 3 != pl:
fault(GP, s & ~3)
e = descriptor(s)
if e >> 41 & 0xD != 9:
fault(GP, s & ~3) # bits 44, 43 and 41: S set, code
# clear, writable set
if e >> 45 & 3 != pl:
fault(GP, s & ~3) # its DPL is the new CPL
if e >> 47 & 1 == 0:
fault(SS, s & ~3)
cf = f & 1 # the status flags, TF, DF and NT at
pf = f >> 2 & 1 # every operand size
af = f >> 4 & 1
zf = f >> 6 & 1
sf = f >> 7 & 1
tf = f >> 8 & 1
df = f >> 10 & 1
of = f >> 11 & 1
nt = f >> 14 & 1
rf = 0 # IRET's image has no RF: cleared
if OSIZE != 16:
rf = f >> 16 & 1 # IRETD and IRETQ load it from the
# image: SDM Volume 3B, 20.3.1.1,
# rev. 093; Zen 5 too (a breakpoint
# at the return address did not
# fire), though APM Volume 3, IRET,
# rev. 3.36, clears it
if OSIZE != 16:
ac = f >> 18 & 1
id = f >> 21 & 1
if CPL <= iopl:
if_ = f >> 9 & 1 # else IF stays
if CPL == 0:
iopl = f >> 12 & 3
if OSIZE != 16:
vif = f >> 19 & 1
vip = f >> 20 & 1
if not wide:
if AMD:
new_sp = low(new_sp, 32) # into compatibility mode, ESP
# alone, zero-extended: APM (RSP.s,
# of the new stack size), Zen 5
rsp = new_sp # all of RSP on Intel's (SDM); IRETD's
# and IRET's zero-extended
ss = s
cs = c # and CPL becomes pl
rip = t
# VM stays 0, the other bits of the image are ignored. To an outer
# level, ES, DS, FS and GS become null where the new CPL may not
# use their segments (data, or code that is not conforming, of a
# lower DPL). With FRED enabled, an IRET that would change CPL, or
# enter compatibility mode at CPL 0, raises #GP(c & ~3). IRET also
# ends the blocking of NMIsRun on AMD Zen 5 (EPYC 9645): 53 cases. Its branches for Intel's processors were not run: no Intel processor ran these forms.
Operands
Flags
| OF | SF | ZF | AF | PF | CF | DF | IF | TF | AC | IOPL | NT | RF | VM | VIF | VIP | ID |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| All forms | ||||||||||||||||
| P | P | P | P | P | P | P | P | P | P | TP | TP | P | TP | P | P | P |
Attributes
What else XED records about the forms. How to read.
- All forms
- may abort a TSX transaction
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 |
I386 | No CPUID bit: part of the base instruction set. | Every x86-64 processor in XED’s model and Zen 3 to Zen 6 |
LONGMODE | LM | 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 2A, page 3-490 (IRET/
IRETD/ IRETQ) - Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 3 (325384-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)
- 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.