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(©.destination) {
pending.remove(©.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(©.source) {
// Materialize the copy.
sequentialized.push(RegisterCopy {View on GitHub (pinned to 0e5b888e1c)
Solutions
- Deduplicate by destination before calling: keep exactly one winning copy per destination register.
- Audit allocator move-insertion points (live-range splits, block-argument resolution, restore moves) for double moves into the same register.
- 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
- Deduplicate moves by destination before calling sequentialize_register
- Audit allocator move-insertion points (splits, coalescing, block arguments) for double moves
- Log the copy list on validation failure so the responsible pass is identifiable
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
- Spare register cannot be a source or destination of a copy
- No holder for source register {:?}
- trying to unwrap {:?} into reg
- VReg operand made it past register allocation
- Unexpected source operand during x86_scratch_split: {src:?}
AI-assisted analysis of ruby/ruby@0e5b888e1c (2026-08-21).
Data as JSON: /api/errors/52a5b7e0afe519bf.
Report an issue: GitHub.