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

  1. Reduce the number of register-class operands in the `asm!` block — reuse registers with `inlateout` where possible.
  2. Use specific named registers (e.g., `out("rax")`) for operands where the specific register is known.
  3. Move some operands to memory: load/store around the asm block instead of using registers.
  4. Use the LLVM backend (`-Zcodegen-backend=llvm`) which reports this as a proper error rather than an ICE.
  5. 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

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


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 &reg 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 &reg 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)