charon_lib/transform/reconstruct_boxes.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
//! # Micro-pass: reconstruct piecewise box allocations using `malloc` and `ShallowInitBox`.
use derive_visitor::visitor_enter_fn;
use derive_visitor::Drive;
use crate::register_error_or_panic;
use crate::transform::TransformCtx;
use crate::ullbc_ast::*;
use super::ctx::UllbcPass;
pub struct Transform;
impl Transform {
/// The special `#[rustc_box] Box::new(x)` construct becomes the following:
///
/// ```text
/// @2 := size_of<i32>
/// @3 := align_of<i32>
/// @4 := alloc::alloc::exchange_malloc(move (@2), move (@3))
/// @5 := shallow_init_box::<i32>(move (@4))
/// // possibly some intermediate statements
/// *(@5) := x
/// ```
///
/// We reconstruct this into a call to `Box::new(x)`.
fn update_statements(
locals: &mut Locals,
seq: &mut [Statement],
) -> Vec<(usize, Vec<Statement>)> {
let seq_len = seq.len();
if let [Statement {
content: RawStatement::Assign(size, Rvalue::NullaryOp(NullOp::SizeOf, _)),
..
}, Statement {
content: RawStatement::Assign(align, Rvalue::NullaryOp(NullOp::AlignOf, _)),
..
}, Statement {
content: RawStatement::Call(call_malloc),
..
}, Statement {
content:
RawStatement::Assign(box_make, Rvalue::ShallowInitBox(Operand::Move(alloc_use), _)),
..
}, rest @ ..] = seq
{
let prefix_len = seq_len - rest.len();
// TODO: once we have a system to recognize intrinsics, check the call is to exchange_malloc.
if let [Operand::Move(arg0), Operand::Move(arg1)] = call_malloc.args.as_slice()
&& arg0 == size
&& arg1 == align
&& call_malloc.dest == *alloc_use
&& box_make.is_local()
&& let var_id = box_make.var_id()
&& let TyKind::Adt(TypeId::Builtin(BuiltinTy::Box), generics) =
locals[var_id].ty.kind()
{
// Find the assignment into the box.
for i in 0..rest.len() {
if let Statement {
content: RawStatement::Assign(box_deref, val),
..
} = &mut rest[i]
&& let Some((sub, ProjectionElem::Deref)) = box_deref.as_projection()
&& sub == box_make
{
let real_i = prefix_len + i;
let mut to_insert = Vec::new();
let dest = box_make.clone();
let val = val.clone();
let generics = generics.clone();
seq[0].content = RawStatement::Nop;
seq[1].content = RawStatement::Nop;
seq[2].content = RawStatement::Nop;
seq[3].content = RawStatement::Nop;
let val = match val {
Rvalue::Use(op) => op,
_ => {
// We need to create a new variable to store the value.
let name = locals[var_id].name.clone();
let ty = generics.types[0].clone();
let var = locals.new_var(name, ty);
let st = Statement {
span: seq[real_i].span,
content: RawStatement::Assign(var.clone(), val),
};
to_insert.push((real_i, vec![st]));
Operand::Move(var)
}
};
seq[real_i].content = RawStatement::Call(Call {
func: FnOperand::Regular(FnPtr {
func: FunIdOrTraitMethodRef::Fun(FunId::Builtin(
BuiltinFunId::BoxNew,
)),
generics,
}),
args: vec![val],
dest,
});
return to_insert;
}
}
}
}
Vec::new()
}
}
impl UllbcPass for Transform {
fn transform_body(&self, ctx: &mut TransformCtx<'_>, b: &mut ExprBody) {
for block in &mut b.body {
block.transform_sequences(&mut |seq| Transform::update_statements(&mut b.locals, seq));
}
// Make sure we got all the `ShallowInitBox`es.
b.body.drive(&mut visitor_enter_fn(|rvalue: &Rvalue| {
if rvalue.is_shallow_init_box() {
register_error_or_panic!(
ctx,
b.span,
"Could not reconstruct `Box` initialization; \
branching during `Box` initialization is not supported."
);
}
}));
}
}