rust-lang/rust · error
cannot allocate registers
Error message
cannot allocate registers
What it means
This fires in Cranelift codegen's inline assembly register allocator. When processing `out`/`inout` operands with register-class constraints (e.g., `{reg}` for a register class like `reg` on x86), the code iterates all available registers in the class and checks for overlap with already-allocated registers. If every register in the class is already taken, `alloc_reg` stays `None` and `.expect("cannot allocate registers")` panics. This means the inline asm has more register-class operands than the target architecture has physical registers.
Solutions
- Reduce the number of register-class operands in the `asm!` block — reuse registers with `inlateout` where possible.
- Use specific named registers (e.g., `out("rax")`) for operands where the specific register is known.
- Move some operands to memory: load/store around the asm block instead of using registers.
- Use the LLVM backend (`-Zcodegen-backend=llvm`) which reports this as a proper error rather than an ICE.
- Split the asm block into multiple smaller blocks to reduce per-block register pressure.
Example fix
// before (too many reg-class operands on x86-32 → register exhaustion → panic)
unsafe {
asm!(
"",
out("reg") a, out("reg") b, out("reg") c,
out("reg") d, out("reg") e, out("reg") f,
out("reg") g, out("reg") h, // too many!
);
}
// after (use inlateout to reuse registers, or spill to memory)
unsafe {
asm!("", inlateout("reg") a, inlateout("reg") b);
asm!("", inlateout("reg") c, inlateout("reg") d);
// fewer regs per block
} Defensive patterns
Strategy: validation
Validate before calling
// Before using asm! with many reg-class operands, count them:
// (Manual check) Count in(reg)/out(reg)/inout(reg) operands.
// On x86-64: keep total reg-class operands under ~12.
// On x86-32: keep under ~5.
// In build.rs, warn if compiling inline asm for register-poor targets:
// if cfg!(target_arch = "x86") {
// println!("cargo:warning=x86 has few GP registers; limit asm! reg operands");
// } Type guard
// No language-level guard for register count, but a documentation comment // and lint can help: // SAFETY: This asm block uses N reg-class operands. Maximum for x86-64 is ~12. // If N exceeds the target limit, cg_clif panics with 'cannot allocate registers'. // Use inlateout to reuse registers, or named registers for specific ones.
Prevention
- Count register-class operands in each asm! block; keep below target limits.
- Use inlateout(reg) to reuse the same register for input and output.
- Use specific named registers (out("rax")) to reduce class-based allocation pressure.
- Split large asm blocks into multiple smaller ones.
- Use the LLVM backend for asm-heavy code — it reports register exhaustion as an error, not an ICE.
When it happens
Trigger: Using `asm!` with more register-class-constrained operands (e.g., `out("reg")`, `in("reg")`) than the target has available registers. For example, on x86-64, requesting more than ~14 general-purpose registers in a single `asm!` block via `in(reg)` / `out(reg)` operands. On register-poor targets like x86 (32-bit) or certain RISC-V configurations, the limit is even lower.
Common situations: Inline assembly blocks with many in/out register operands, especially on 32-bit x86 (only 6-8 general registers) or when many operands use the unconstrained `reg` class. Also triggered by register-pressure-heavy code surrounding the asm block that spills into the allocator. Unlike the LLVM backend which reports a graceful error, cg_clif panics.
Related errors
- Failed to spawn `as`.
- Failed to wait for `as`.
- erroneous constant missed by mono item collection
- expected monomorphic const in codegen
- expected monomorphic const in codegen
AI-assisted analysis of rust-lang/rust@7088e4b63a (2026-08-10).
Data as JSON: /api/errors/dce14c93019b864f.
Report an issue: GitHub.
Appendix: source
Thrown at compiler/rustc_codegen_cranelift/src/inline_asm.rs:321
| CInlineAsmOperand::InOut {
reg: InlineAsmRegOrRegClass::RegClass(class), ..
} => {
let mut alloc_reg = None;
for ® in &map[&class] {
let mut used = false;
reg.overlapping_regs(|r| {
if allocated.contains_key(&r) {
used = true;
}
});
if !used {
alloc_reg = Some(reg);
break;
}
}
let reg = alloc_reg.expect("cannot allocate registers");
regs[i] = Some(reg);
allocated.insert(reg, (true, true));
}
_ => (),
}
}
// Allocate in/lateout.
for (i, operand) in self.operands.iter().enumerate() {
match *operand {
CInlineAsmOperand::In { reg: InlineAsmRegOrRegClass::RegClass(class), .. } => {
let mut alloc_reg = None;
for ® in &map[&class] {
let mut used = false;
reg.overlapping_regs(|r| {
if allocated.get(&r).copied().unwrap_or_default().0 {
used = true;
}View on GitHub (pinned to 7088e4b63a)