Skip to main content

charon_lib/transform/simplify_output/
simplify_constants.rs

1//! The MIR constant expressions lead to a lot of duplication: there are
2//! for instance constant ADTs which duplicate the "regular" aggregated
3//! ADTs in the operands, constant references, etc. This reduces the number
4//! of cases to handle and eases the function translation in Aeneas.
5//!
6//! This pass removes all those occurrences, leaving only primitive constant expressions. It does
7//! so by introducing intermediate statements.
8//!
9//! A small remark about the intermediate statements we introduce for the globals:
10//! we do so because, when evaluating the code in "concrete" mode, it allows to
11//! handle the globals like function calls.
12
13use itertools::Itertools;
14
15use crate::transform::TransformCtx;
16use crate::transform::ctx::{BodyTransformCtx, UllbcPass, UllbcStatementTransformCtx};
17use crate::ullbc_ast::*;
18
19/// If the constant value is a constant ADT, push `Assign::Aggregate` statements
20/// to the vector of statements, that bind new variables to the ADT parts and
21/// the variable assigned to the complete ADT.
22///
23/// Goes fom e.g. `f(T::A(x, y))` to `let a = T::A(x, y); f(a)`.
24/// The function is recursively called on the aggregate fields (e.g. here x and y).
25fn transform_constant_expr(
26    ctx: &mut UllbcStatementTransformCtx<'_>,
27    mut val: ConstantExpr,
28) -> Operand {
29    let rval = match val.kind() {
30        // Here we use a copy, rather than a move -- moving a global would leave it uninitialized.
31        ConstantExprKind::Global(global_ref) => {
32            return Operand::Copy(Place::new_global(global_ref.clone(), val.ty().clone()));
33        }
34        ConstantExprKind::PtrNoProvenance(ptr) => {
35            let usize_ty = Ty::mk_usize();
36            let ptr_usize = ConstantExprKind::Integer(IntegerValue::Unsigned(UIntTy::Usize, *ptr));
37            let cast = UnOp::Cast(CastKind::RawPtr(usize_ty.clone(), val.ty().clone()));
38            Rvalue::UnaryOp(cast, Operand::Const(ConstantExpr::new(ptr_usize, usize_ty)))
39        }
40        cexpr @ (ConstantExprKind::Ref(..) | ConstantExprKind::Ptr(..)) => {
41            let (rk, bval, metadata) = match cexpr {
42                ConstantExprKind::Ref(bval, metadata) => (None, bval.clone(), metadata.clone()),
43                ConstantExprKind::Ptr(rk, bval, metadata) => {
44                    (Some(*rk), bval.clone(), metadata.clone())
45                }
46                _ => unreachable!(),
47            };
48
49            // As the value is originally an argument, it must be Sized, hence no metadata
50            let place = match bval.kind() {
51                ConstantExprKind::Global(global_ref) => {
52                    Place::new_global(global_ref.clone(), bval.ty().clone())
53                }
54                _ => {
55                    // Recurse on the borrowed value
56                    let bval = transform_constant_expr(ctx, bval);
57
58                    // Evaluate the referenced value
59                    let bval_ty = bval.ty().clone();
60                    ctx.rval_to_place(Rvalue::Use(bval, WithRetag::No), bval_ty)
61                }
62            };
63            match (rk, metadata) {
64                // Borrow the place.
65                (None, None) => ctx.borrow(place, BorrowKind::Shared),
66                (Some(rk), None) => ctx.raw_borrow(place, rk),
67                // Unsizing borrow.
68                (None, Some(metadata)) => {
69                    let sized_ref = ctx.borrow_to_new_var(place, BorrowKind::Shared, None);
70                    Rvalue::UnaryOp(
71                        UnOp::Cast(CastKind::Unsize(
72                            sized_ref.ty.clone(),
73                            val.ty().clone(),
74                            metadata,
75                        )),
76                        Operand::Move(sized_ref),
77                    )
78                }
79                (Some(rk), Some(metadata)) => {
80                    let sized_raw_ref = ctx.raw_borrow_to_new_var(place, rk, None);
81                    Rvalue::UnaryOp(
82                        UnOp::Cast(CastKind::Unsize(
83                            sized_raw_ref.ty.clone(),
84                            val.ty().clone(),
85                            metadata,
86                        )),
87                        Operand::Move(sized_raw_ref),
88                    )
89                }
90            }
91        }
92        ConstantExprKind::Adt(..) if val.ty().is_unit() => {
93            // Keep unit constants to avoid adding countless unit locals.
94            return Operand::Const(val);
95        }
96        ConstantExprKind::Adt(variant, fields) => {
97            let fields = fields
98                .iter()
99                .cloned()
100                .map(|x| transform_constant_expr(ctx, x))
101                .collect();
102
103            // Build an `Aggregate` rvalue.
104            let tref = val.ty().kind().as_adt().unwrap();
105            let aggregate_kind = AggregateKind::Adt(tref.clone(), *variant, None);
106            Rvalue::Aggregate(aggregate_kind, fields)
107        }
108        ConstantExprKind::Array(fields)
109            if let TyKind::Array(ty, _, ty_is_sized) = val.ty().kind() =>
110        {
111            let fields = fields
112                .iter()
113                .cloned()
114                .map(|x| transform_constant_expr(ctx, x))
115                .collect_vec();
116            let len = ConstantExpr::mk_usize(fields.len() as u128);
117            Rvalue::Aggregate(
118                AggregateKind::Array(ty.clone(), len, ty_is_sized.clone()),
119                fields,
120            )
121        }
122        ConstantExprKind::FnPtr(fptr) if let TyKind::FnPtr(sig) = val.ty().kind() => {
123            let from_ty =
124                TyKind::FnDef(sig.clone().map(|_| fptr.clone().move_under_binder())).into_ty();
125            let to_ty = TyKind::FnPtr(sig.clone()).into_ty();
126            Rvalue::UnaryOp(
127                UnOp::Cast(CastKind::FnPtr(from_ty.clone(), to_ty)),
128                Operand::Const(ConstantExpr::new(
129                    ConstantExprKind::FnDef(fptr.clone()),
130                    from_ty,
131                )),
132            )
133        }
134        ConstantExprKind::VTableRef(tref)
135            if let Some(vtable_ref) = tref.vtable_ref(&ctx.ctx.translated)
136                && let TyKind::Ref(_, vtable_ty, _) = val.ty().kind() =>
137        {
138            let inner = ConstantExpr::new(
139                ConstantExprKind::Global(vtable_ref.clone()),
140                vtable_ty.clone(),
141            );
142            val.with_contents_mut(|kind, _| *kind = ConstantExprKind::Ref(inner, None));
143            // Normalize further into a place access.
144            return transform_constant_expr(ctx, val);
145        }
146        _ => return Operand::Const(val),
147    };
148    Operand::Move(ctx.rval_to_place(rval, val.ty().clone()))
149}
150
151fn transform_operand(ctx: &mut UllbcStatementTransformCtx<'_>, op: &mut Operand) {
152    // Transform the constant operands (otherwise do nothing)
153    take_mut::take(op, |op| {
154        if let Operand::Const(val) = op {
155            transform_constant_expr(ctx, val)
156        } else {
157            op
158        }
159    })
160}
161
162pub struct Transform;
163impl UllbcPass for Transform {
164    fn should_run(&self, options: &crate::options::TranslateOptions) -> bool {
165        !options.raw_consts
166    }
167
168    fn transform_function(&self, ctx: &mut TransformCtx, fun_decl: &mut FunDecl) {
169        fun_decl.transform_ullbc_operands(ctx, transform_operand);
170        if let Some(body) = fun_decl.body.as_unstructured_mut() {
171            for block in body.body.iter_mut() {
172                // Normalize unit constants into unit aggregates.
173                block.dyn_visit_in_body_mut(|rvalue: &mut Rvalue| {
174                    take_mut::take(rvalue, |rvalue| match rvalue {
175                        Rvalue::Use(Operand::Const(e), _)
176                            if e.kind().is_adt() && e.ty().is_unit() =>
177                        {
178                            Rvalue::unit_value()
179                        }
180                        _ => rvalue,
181                    });
182                });
183            }
184        }
185    }
186}