ruby/ruby · critical

Destination register {:?} has multiple copies.

Error message

Destination register {:?} has multiple copies.

What it means

In a parallel copy set every destination register must be written exactly once; two copies into the same destination are ambiguous (which source wins?) and cannot be sequentialized. sequentialize_register() detects this when inserting into the pending map keyed by destination and the insert returns the previous entry, panicking with 'Destination register has multiple copies'.

Source

Thrown at zjit/src/backend/parcopy.rs:64

pub fn sequentialize_register<T: PartialEq + Eq + Hash + Ord + std::fmt::Debug + Clone + Copy>(parallel_copies: &[RegisterCopy<T>], spare: T) -> Vec<RegisterCopy<T>> {
    let mut sequentialized = Vec::new();
    // `resource` in the original code, this point to the current register
    // holding a particular initial value.
    // If a given Register is no longer needed, the value might be inaccurate.
    let mut current_holder = std::collections::HashMap::new();
    // Copies that are pending, indexed by destination register.
    // Use btree map to stay deterministic.
    let mut pending = std::collections::BTreeMap::new();
    // If a copy can be materialized (nothing depends on the destination), we
    // move it from pending into available.
    let mut available = Vec::new();

    for copy in parallel_copies {
        if copy.source == spare || copy.destination == spare {
            panic!("Spare register cannot be a source or destination of a copy");
        }
        if let Some(_old_value) = pending.insert(copy.destination, copy) {
            panic!(
                "Destination register {:?} has multiple copies.",
                copy.destination
            );
        }
        current_holder.insert(copy.source, copy.source);
    }
    for copy in parallel_copies {
        // If we didn't record it, this means nothing depends on that register.
        if !current_holder.contains_key(&copy.destination) {
            pending.remove(&copy.destination);
            available.push(copy);
        }
    }
    while !pending.is_empty() || !available.is_empty() {
        while let Some(copy) = available.pop() {
            if let Some(source) = current_holder.get_mut(&copy.source) {
                // Materialize the copy.
                sequentialized.push(RegisterCopy {

View on GitHub (pinned to 0e5b888e1c)

Solutions

  1. Deduplicate by destination before calling: keep exactly one winning copy per destination register.
  2. Audit allocator move-insertion points (live-range splits, block-argument resolution, restore moves) for double moves into the same register.
  3. Log the copy list when the pre-check finds a duplicate so the offending pass can be identified from the register numbers.

Example fix

// before - two moves target r10 in one parallel set
let copies = vec![copy(r1, r10), copy(r2, r10)];
let seq = sequentialize_register(&copies, spare); // panics: multiple copies to r10

// after - one copy per destination; the loser is rewritten first
let copies = vec![copy(r1, r9), copy(r2, r10)];
let seq = sequentialize_register(&copies, spare);
Defensive patterns

Strategy: validation

Validate before calling

fn unique_destinations(copies: &[RegisterCopy<Reg>]) -> bool {
    let mut seen = std::collections::HashSet::new();
    copies.iter().all(|c| seen.insert(c.destination))
}
assert!(unique_destinations(&copies),
    "each destination must be written exactly once");

Prevention

When it happens

Trigger: The register allocator emitting two moves into the same physical register at one block boundary (a live-range split or coalescing bug); hand-built copy lists containing a duplicated destination; block-argument moves duplicated with restore moves.

Common situations: Coalescing changes that merge live ranges but leave both moves behind; insertion of an extra restore move for a register that also receives a block-argument move; copy-paste errors when assembling move lists manually.

Related errors


AI-assisted analysis of ruby/ruby@0e5b888e1c (2026-08-21). Data as JSON: /api/errors/52a5b7e0afe519bf. Report an issue: GitHub.