charon_lib/transform/simplify_constants.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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
//! The MIR constant expressions lead to a lot of duplication: there are
//! for instance constant ADTs which duplicate the "regular" aggregated
//! ADTs in the operands, constant references, etc. This reduces the number
//! of cases to handle and eases the function translation in Aeneas.
//!
//! This pass removes all those occurrences so that only the
//! [ConstantExpression::Literal]. It does so by introducing intermediate statements.
//!
//! A small remark about the intermediate statements we introduce for the globals:
//! we do so because, when evaluating the code in "concrete" mode, it allows to
//! handle the globals like function calls.
use crate::transform::TransformCtx;
use crate::ullbc_ast::*;
use super::ctx::UllbcPass;
/// If the constant value is a constant ADT, push `Assign::Aggregate` statements
/// to the vector of statements, that bind new variables to the ADT parts and
/// the variable assigned to the complete ADT.
///
/// Goes fom e.g. `f(T::A(x, y))` to `let a = T::A(x, y); f(a)`.
/// The function is recursively called on the aggregate fields (e.g. here x and y).
fn transform_constant_expr<F: FnMut(Ty) -> VarId>(
span: &Span,
nst: &mut Vec<Statement>,
val: ConstantExpr,
make_new_var: &mut F,
) -> Operand {
match val.value {
RawConstantExpr::Literal(_)
| RawConstantExpr::Var(_)
| RawConstantExpr::TraitConst(..)
| RawConstantExpr::FnPtr(..) => {
// Nothing to do
// TODO: for trait const: might come from a top-level impl, so we might
// want to introduce an intermediate statement to be able to evaluate
// it as a function call, like for globals.
Operand::Const(val)
}
RawConstantExpr::Global(global_ref) => {
// Introduce an intermediate statement
let var_id = make_new_var(val.ty.clone());
nst.push(Statement::new(
*span,
RawStatement::Assign(Place::new(var_id), Rvalue::Global(global_ref)),
));
Operand::Move(Place::new(var_id))
}
RawConstantExpr::Ref(box bval) => {
match bval.value {
RawConstantExpr::Global(global_ref) => {
// Introduce an intermediate statement to borrow the static.
let ref_var_id = make_new_var(val.ty);
nst.push(Statement::new(
*span,
RawStatement::Assign(
Place::new(ref_var_id),
Rvalue::GlobalRef(global_ref, RefKind::Shared),
),
));
// Return the new operand
Operand::Move(Place::new(ref_var_id))
}
_ => {
// Recurse on the borrowed value
let bval_ty = bval.ty.clone();
let bval = transform_constant_expr(span, nst, bval, make_new_var);
// Introduce an intermediate statement to evaluate the referenced value
let bvar_id = make_new_var(bval_ty);
nst.push(Statement::new(
*span,
RawStatement::Assign(Place::new(bvar_id), Rvalue::Use(bval)),
));
// Introduce an intermediate statement to borrow the value
let ref_var_id = make_new_var(val.ty);
let rvalue = Rvalue::Ref(Place::new(bvar_id), BorrowKind::Shared);
nst.push(Statement::new(
*span,
RawStatement::Assign(Place::new(ref_var_id), rvalue),
));
// Return the new operand
Operand::Move(Place::new(ref_var_id))
}
}
}
RawConstantExpr::MutPtr(box bval) => {
match bval.value {
RawConstantExpr::Global(global_ref) => {
// Introduce an intermediate statement to borrow the static.
let ref_var_id = make_new_var(val.ty);
nst.push(Statement::new(
*span,
RawStatement::Assign(
Place::new(ref_var_id),
Rvalue::GlobalRef(global_ref, RefKind::Mut),
),
));
// Return the new operand
Operand::Move(Place::new(ref_var_id))
}
_ => {
// Recurse on the borrowed value
let bval_ty = bval.ty.clone();
let bval = transform_constant_expr(span, nst, bval, make_new_var);
// Introduce an intermediate statement to evaluate the referenced value
let bvar_id = make_new_var(bval_ty);
nst.push(Statement::new(
*span,
RawStatement::Assign(Place::new(bvar_id), Rvalue::Use(bval)),
));
// Introduce an intermediate statement to borrow the value
let ref_var_id = make_new_var(val.ty);
let rvalue = Rvalue::RawPtr(Place::new(bvar_id), RefKind::Mut);
nst.push(Statement::new(
*span,
RawStatement::Assign(Place::new(ref_var_id), rvalue),
));
// Return the new operand
Operand::Move(Place::new(ref_var_id))
}
}
}
RawConstantExpr::Adt(variant, fields) => {
// Recurse on the fields
let fields = fields
.into_iter()
.map(|f| transform_constant_expr(span, nst, f, make_new_var))
.collect();
// Introduce an intermediate assignment for the aggregated ADT
let rval = {
let (adt_kind, generics) = val.ty.kind().as_adt().unwrap();
let aggregate_kind = AggregateKind::Adt(*adt_kind, variant, None, generics.clone());
Rvalue::Aggregate(aggregate_kind, fields)
};
let var_id = make_new_var(val.ty);
nst.push(Statement::new(
*span,
RawStatement::Assign(Place::new(var_id), rval),
));
// Return the new operand
Operand::Move(Place::new(var_id))
}
}
}
fn transform_operand<F: FnMut(Ty) -> VarId>(
span: &Span,
nst: &mut Vec<Statement>,
op: &mut Operand,
f: &mut F,
) {
// Transform the constant operands (otherwise do nothing)
take_mut::take(op, |op| {
if let Operand::Const(val) = op {
transform_constant_expr(span, nst, val, f)
} else {
op
}
})
}
pub struct Transform;
impl UllbcPass for Transform {
fn transform_body(&self, _ctx: &mut TransformCtx<'_>, b: &mut ExprBody) {
let mut f = make_locals_generator(&mut b.locals);
body_transform_operands(&mut b.body, &mut |span, nst, op| {
transform_operand(span, nst, op, &mut f)
});
}
}