Skip to content

Commit 673b448

Browse files
authored
Cranelift DFG: make inst clone deep-clone varargs lists. (#5727)
When investigating #5716, I found that rematerialization of a `call`, in addition to blowing up for other reasons, caused aliasing of the varargs list (the `EntityList` in the `ListPool`), such that editing the args of the second copy of the call instruction inadvertently updated the first as well. This PR modifies `DataFlowGraph::clone_inst` so that it always clones the varargs list if present. This shouldn't have any functional impact on Cranelift today, because we don't rematerialize any instructions with varargs; but it's important to get it right to avoid a bug later!
1 parent c8a6adf commit 673b448

File tree

3 files changed

+111
-0
lines changed

3 files changed

+111
-0
lines changed

cranelift/codegen/meta/src/gen_inst.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,83 @@ fn gen_instruction_data_impl(formats: &[&InstructionFormat], fmt: &mut Formatter
383383
});
384384
fmt.line("}");
385385
});
386+
fmt.line("}");
387+
388+
fmt.empty_line();
389+
390+
fmt.doc_comment(r#"
391+
Deep-clone an `InstructionData`, including any referenced lists.
392+
393+
This operation requires a reference to a `ValueListPool` to
394+
clone the `ValueLists`.
395+
"#);
396+
fmt.line("pub fn deep_clone(&self, pool: &mut ir::ValueListPool) -> Self {");
397+
fmt.indent(|fmt| {
398+
fmt.line("match *self {");
399+
fmt.indent(|fmt| {
400+
for format in formats {
401+
let name = format!("Self::{}", format.name);
402+
let mut members = vec!["opcode"];
403+
404+
if format.has_value_list {
405+
members.push("ref args");
406+
} else if format.num_value_operands == 1 {
407+
members.push("arg");
408+
} else if format.num_value_operands > 0 {
409+
members.push("args");
410+
}
411+
412+
match format.num_block_operands {
413+
0 => {}
414+
1 => {
415+
members.push("destination");
416+
}
417+
_ => {
418+
members.push("blocks");
419+
}
420+
};
421+
422+
for field in &format.imm_fields {
423+
members.push(field.member);
424+
}
425+
let members = members.join(", ");
426+
427+
fmtln!(fmt, "{}{{{}}} => {{", name, members ); // beware the moustaches
428+
fmt.indent(|fmt| {
429+
fmtln!(fmt, "Self::{} {{", format.name);
430+
fmt.indent(|fmt| {
431+
fmtln!(fmt, "opcode,");
432+
433+
if format.has_value_list {
434+
fmtln!(fmt, "args: args.deep_clone(pool),");
435+
} else if format.num_value_operands == 1 {
436+
fmtln!(fmt, "arg,");
437+
} else if format.num_value_operands > 0 {
438+
fmtln!(fmt, "args,");
439+
}
440+
441+
match format.num_block_operands {
442+
0 => {}
443+
1 => {
444+
fmtln!(fmt, "destination: destination.deep_clone(pool),");
445+
}
446+
2 => {
447+
fmtln!(fmt, "blocks: [blocks[0].deep_clone(pool), blocks[1].deep_clone(pool)],");
448+
}
449+
_ => panic!("Too many block targets in instruction"),
450+
}
451+
452+
for field in &format.imm_fields {
453+
fmtln!(fmt, "{},", field.member);
454+
}
455+
});
456+
fmtln!(fmt, "}");
457+
});
458+
fmtln!(fmt, "}");
459+
}
460+
});
461+
fmt.line("}");
462+
});
386463
fmt.line("}");
387464
});
388465
fmt.line("}");

cranelift/codegen/src/ir/dfg.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,10 @@ impl DataFlowGraph {
907907
pub fn clone_inst(&mut self, inst: Inst) -> Inst {
908908
// First, add a clone of the InstructionData.
909909
let inst_data = self.insts[inst].clone();
910+
// If the `inst_data` has a reference to a ValueList, clone it
911+
// as well, because we can't share these (otherwise mutating
912+
// one would affect the other).
913+
let inst_data = inst_data.deep_clone(&mut self.value_lists);
910914
let new_inst = self.make_inst(inst_data);
911915
// Get the controlling type variable.
912916
let ctrl_typevar = self.ctrl_typevar(inst);
@@ -1615,4 +1619,25 @@ mod tests {
16151619
assert_eq!(pos.func.dfg.resolve_aliases(c2), c2);
16161620
assert_eq!(pos.func.dfg.resolve_aliases(c), c2);
16171621
}
1622+
1623+
#[test]
1624+
fn cloning() {
1625+
use crate::ir::InstBuilder;
1626+
1627+
let mut func = Function::new();
1628+
let mut sig = Signature::new(crate::isa::CallConv::SystemV);
1629+
sig.params.push(ir::AbiParam::new(types::I32));
1630+
let sig = func.import_signature(sig);
1631+
let block0 = func.dfg.make_block();
1632+
let mut pos = FuncCursor::new(&mut func);
1633+
pos.insert_block(block0);
1634+
let v1 = pos.ins().iconst(types::I32, 0);
1635+
let v2 = pos.ins().iconst(types::I32, 1);
1636+
let call_inst = pos.ins().call_indirect(sig, v1, &[v1]);
1637+
let func = pos.func;
1638+
1639+
let call_inst_dup = func.dfg.clone_inst(call_inst);
1640+
func.dfg.inst_args_mut(call_inst)[0] = v2;
1641+
assert_eq!(v1, func.dfg.inst_args(call_inst_dup)[0]);
1642+
}
16181643
}

cranelift/codegen/src/ir/instructions.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,15 @@ impl BlockCall {
123123
pub fn display<'a>(&self, pool: &'a ValueListPool) -> DisplayBlockCall<'a> {
124124
DisplayBlockCall { block: *self, pool }
125125
}
126+
127+
/// Deep-clone the underlying list in the same pool. The returned
128+
/// list will have identical contents but changes to this list
129+
/// will not change its contents or vice-versa.
130+
pub fn deep_clone(&self, pool: &mut ValueListPool) -> Self {
131+
Self {
132+
values: self.values.deep_clone(pool),
133+
}
134+
}
126135
}
127136

128137
/// Wrapper for the context needed to display a [BlockCall] value.

0 commit comments

Comments
 (0)