rust-lang/rust · error
save_register for {:?}
Error message
save_register for {:?} What it means
Thrown by rustc_codegen_cranelift when saving a clobbered register to a stack slot before a user asm! block. save_register() emits the architecture-specific store instruction for the base-pointer-relative slot (e.g. mov [rbx+off], reg). Only X86_64, AArch64, and RiscV64 are implemented; other arches hit `_ => unimplemented!("save_register for {:?}", arch)`.
Source
Thrown at compiler/rustc_codegen_cranelift/src/inline_asm.rs:773
generated_asm.push('\n');
}
InlineAsmArch::AArch64 => {
generated_asm.push_str(" str ");
match reg {
InlineAsmReg::AArch64(reg) if reg.vreg_index().is_some() => {
// rustc emits v0 rather than q0
reg.emit(generated_asm, InlineAsmArch::AArch64, Some('q')).unwrap()
}
_ => reg.emit(generated_asm, InlineAsmArch::AArch64, None).unwrap(),
}
writeln!(generated_asm, ", [x19, 0x{:x}]", offset.bytes()).unwrap();
}
InlineAsmArch::RiscV64 => {
generated_asm.push_str(" sd ");
reg.emit(generated_asm, InlineAsmArch::RiscV64, None).unwrap();
writeln!(generated_asm, ", 0x{:x}(s1)", offset.bytes()).unwrap();
}
_ => unimplemented!("save_register for {:?}", arch),
}
}
fn restore_register(
generated_asm: &mut String,
arch: InlineAsmArch,
reg: InlineAsmReg,
offset: Size,
) {
match arch {
InlineAsmArch::X86_64 => {
match reg {
InlineAsmReg::X86(reg)
if matches!(
reg.reg_class(),
X86InlineAsmRegClass::xmm_reg
| X86InlineAsmRegClass::ymm_reg
| X86InlineAsmRegClass::zmm_regView on GitHub (pinned to 22057b88b0)
Solutions
- Switch to the LLVM backend for that target.
- Reduce the asm to arches Cranelift supports (x86_64/aarch64/riscv64) via cfg.
- Avoid explicit register clobbers in asm operands where a plain Rust intrinsic or builtin is available.
- Implement save_register for the arch (emit the store to the base-pointer-relative offset, mirroring the existing arms) upstream.
Example fix
// before
let r: u64;
asm!("mov {}, 1", out("rax") r, options(nostack));
// after - avoid register-clobber asm, use a plain expression
let r: u64 = 1; Defensive patterns
Strategy: validation
Validate before calling
// Detect asm! blocks that explicitly list registers in `out("reg")` /
// `inout("reg")` — save_register path triggers on named-register operands.
let re = regex::Regex::new(r#"asm!\([^)]*\b(out|inout|lateout)\("(?:rax|rbx|rcx|rdx|rsi|rdi|rbp|r[0-9]+)""#).unwrap();
if re.is_match(&src) {
eprintln!("cg_clif cannot save_named_register for asm; use LLVM.");
} Try / catch
// Compiler-process panic; not catchable. Use LLVM backend for any crate // that performs explicit register save/restore via asm!.
Prevention
- Avoid named-register operands in asm! under cg_clif; prefer `in("reg")` class operands.
- If you must save/restore a specific register, wrap the asm in a Rust function compiled by LLVM.
- Audit clobbers: every explicit register clobber can hit the unimplemented save_register path.
When it happens
Trigger: Compiling asm! with out("reg") / inout("reg") operands that clobber registers, on a target other than X86_64/AArch64/RiscV64 using the Cranelift backend. The backend must spill each clobbered register to a stack slot before the asm and reload it after.
Common situations: Inline asm with multiple register operands (e.g. SIMD/DSP intrinsics) targeting a tier-3 arch under cg_clif; porting a crate that relies on per-register asm operands to a new architecture before the cranelift backend has the save/restore sequences.
Related errors
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/c257c86d76ac6b56.json.
Report an issue: GitHub.