rust-lang/rust · error
epilogue for {:?}
Error message
epilogue for {:?} What it means
Thrown by rustc_codegen_cranelift when emitting the inline-asm epilogue that restores the base pointer and callee-saved register after a user asm! block returns. As with the prologue, only X86_64, AArch64, and RiscV64 have restore sequences; any other InlineAsmArch hits `_ => unimplemented!("epilogue for {:?}", arch)`. It fires only for asm! blocks that fall through to a return (non-noreturn).
Source
Thrown at compiler/rustc_codegen_cranelift/src/inline_asm.rs:708
fn epilogue(generated_asm: &mut String, arch: InlineAsmArch) {
match arch {
InlineAsmArch::X86_64 => {
generated_asm.push_str(" pop rbx\n");
generated_asm.push_str(" pop rbp\n");
generated_asm.push_str(" ret\n");
}
InlineAsmArch::AArch64 => {
generated_asm.push_str(" ldr x19, [sp, #24]\n");
generated_asm.push_str(" ldp fp, lr, [sp], #32\n");
generated_asm.push_str(" ret\n");
}
InlineAsmArch::RiscV64 => {
generated_asm.push_str(" ld s1, 0(sp)\n");
generated_asm.push_str(" ld ra, 8(sp)\n");
generated_asm.push_str(" addi sp, sp, 16\n");
generated_asm.push_str(" ret\n");
}
_ => unimplemented!("epilogue for {:?}", arch),
}
}
fn epilogue_noreturn(generated_asm: &mut String, arch: InlineAsmArch) {
match arch {
InlineAsmArch::X86_64 => {
generated_asm.push_str(" ud2\n");
}
InlineAsmArch::AArch64 => {
generated_asm.push_str(" brk #0x1\n");
}
InlineAsmArch::RiscV64 => {
generated_asm.push_str(" ebreak\n");
}
_ => unimplemented!("epilogue_noreturn for {:?}", arch),
}
}
View on GitHub (pinned to 22057b88b0)
Solutions
- Switch the codegen backend to LLVM (default) for this build target.
- Constrain the crate/arch to x86_64, aarch64, or riscv64 when using Cranelift.
- Add #[cfg(not(target_arch = "..."))] gates around the offending asm! so it is skipped on unsupported arches.
- Implement the arch arm in epilogue() (mirror the corresponding prologue() restore order) and patch upstream.
Example fix
// before
unsafe fn pause() { asm!("nop", out("eax") _, options(nostack)); }
// after - only build the asm on backends/arches Cranelift supports
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "riscv64"))]
unsafe fn pause() { asm!("nop", out("eax") _, options(nostack)); } Defensive patterns
Strategy: validation
Validate before calling
// Reject asm! macros that contain prologue/epilogue-style register clobbers
// before invoking the cranelift backend.
let src = std::fs::read_to_string("src/lib.rs")?;
if src.contains("asm!") && (src.contains("in(") || src.contains("out(") || src.contains("clobber_abi")) {
eprintln!("cg_clif epilogue path for inline asm is unimplemented; switch to LLVM.");
} Try / catch
// Unreachable at runtime: panics inside rustc_codegen_cranelift abort the
// compiler process. Guard at build time instead.
// build.rs:
fn main() {
let backend = std::env::var("RUSTC_CODEGEN").unwrap_or_default();
if backend == "cranelift" { println!("cargo:rustc-cfg=needs_llvm_codegen"); }
} Prevention
- Inline-asm epilogues (return-path clobber restore) are unimplemented in cg_clif; keep asm! out of hot paths compiled with cranelift.
- Prefer intrinsics (`core::arch::x86_64::_...`) over hand-written asm where available.
- When asm is unavoidable, build only that crate with the LLVM backend via `.cargo/config` per-target triple.
When it happens
Trigger: Compiling asm! with register operands that need an epilogue restore, on a target other than X86_64/AArch64/RiscV64, using the Cranelift backend. Specifically asm! blocks that are not marked noreturn and therefore require restoring the saved base register (e.g. s1 on RiscV, rbx on x86_64) before ret.
Common situations: Using cg_clif on a tier-3 target (LoongArch64, PowerPC, S390x, Arm32) where the crate uses inline asm for perf counters, spinlocks, or CPUID-like probes; nightly toolchain with the cranelift codegen backend forced on.
Related errors
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/b5a1a57bbb0475f9.json.
Report an issue: GitHub.