charon_lib/transform/simplify_output/
simplify_constants.rs1use itertools::Itertools;
14use std::assert_matches::assert_matches;
15
16use crate::transform::TransformCtx;
17use crate::ullbc_ast::*;
18
19use crate::transform::ctx::UllbcPass;
20
21fn transform_constant_expr(
28 span: &Span,
29 val: Box<ConstantExpr>,
30 new_var: &mut impl FnMut(Rvalue, Ty) -> Place,
31) -> Operand {
32 match val.kind {
33 ConstantExprKind::Literal(_)
34 | ConstantExprKind::Var(_)
35 | ConstantExprKind::RawMemory(..)
36 | ConstantExprKind::TraitConst(..)
37 | ConstantExprKind::FnPtr(..)
38 | ConstantExprKind::Opaque(_) => {
39 Operand::Const(val)
44 }
45 ConstantExprKind::Global(global_ref) => {
51 Operand::Copy(Place::new_global(global_ref, val.ty))
52 }
53 ConstantExprKind::PtrNoProvenance(ptr) => {
54 let usize_ty = TyKind::Literal(LiteralTy::UInt(UIntTy::Usize)).into_ty();
55 let ptr_usize = ConstantExprKind::Literal(Literal::Scalar(ScalarValue::Unsigned(
56 UIntTy::Usize,
57 ptr,
58 )));
59 let cast = UnOp::Cast(CastKind::RawPtr(usize_ty.clone(), val.ty.clone()));
60 let uvar = new_var(
61 Rvalue::UnaryOp(
62 cast,
63 Operand::Const(Box::new(ConstantExpr {
64 kind: ptr_usize,
65 ty: usize_ty,
66 })),
67 ),
68 val.ty,
69 );
70 Operand::Move(uvar)
71 }
72 ConstantExprKind::Ref(bval) => {
73 match bval.kind {
74 ConstantExprKind::Global(global_ref) => {
75 let unit_metadata = new_var(Rvalue::unit_value(), Ty::mk_unit());
76 Operand::Move(new_var(
77 Rvalue::Ref {
79 place: Place::new_global(global_ref, bval.ty),
80 kind: BorrowKind::Shared,
81 ptr_metadata: Operand::Move(unit_metadata),
82 },
83 val.ty,
84 ))
85 }
86 _ => {
87 let bval_ty = bval.ty.clone();
89 let bval = transform_constant_expr(span, bval, new_var);
90
91 let bvar = new_var(Rvalue::Use(bval), bval_ty);
93
94 let unit_metadata = new_var(Rvalue::unit_value(), Ty::mk_unit());
95
96 let ref_var = new_var(
99 Rvalue::Ref {
100 place: bvar,
101 kind: BorrowKind::Shared,
102 ptr_metadata: Operand::Move(unit_metadata),
103 },
104 val.ty,
105 );
106
107 Operand::Move(ref_var)
108 }
109 }
110 }
111 ConstantExprKind::Ptr(rk, bval) => {
112 match bval.kind {
113 ConstantExprKind::Global(global_ref) => {
114 let unit_metadata = new_var(Rvalue::unit_value(), Ty::mk_unit());
115 Operand::Move(new_var(
116 Rvalue::RawPtr {
118 place: Place::new_global(global_ref, bval.ty),
119 kind: rk,
120 ptr_metadata: Operand::Move(unit_metadata),
121 },
122 val.ty,
123 ))
124 }
125 _ => {
126 let bval_ty = bval.ty.clone();
128 let bval = transform_constant_expr(span, bval, new_var);
129
130 let bvar = new_var(Rvalue::Use(bval), bval_ty);
132
133 let unit_metadata = new_var(Rvalue::unit_value(), Ty::mk_unit());
134
135 let ref_var = new_var(
138 Rvalue::RawPtr {
139 place: bvar,
140 kind: rk,
141 ptr_metadata: Operand::Move(unit_metadata),
142 },
143 val.ty,
144 );
145
146 Operand::Move(ref_var)
147 }
148 }
149 }
150 ConstantExprKind::Adt(variant, fields) => {
151 let fields = fields
152 .into_iter()
153 .map(|x| transform_constant_expr(span, Box::new(x), new_var))
154 .collect();
155
156 let rval = {
158 let tref = val.ty.kind().as_adt().unwrap();
159 let aggregate_kind = AggregateKind::Adt(tref.clone(), variant, None);
160 Rvalue::Aggregate(aggregate_kind, fields)
161 };
162 let var = new_var(rval, val.ty);
163
164 Operand::Move(var)
165 }
166 ConstantExprKind::Array(fields) => {
167 let fields = fields
168 .into_iter()
169 .map(|x| transform_constant_expr(span, Box::new(x), new_var))
170 .collect_vec();
171
172 let len = ConstGeneric::Value(Literal::Scalar(ScalarValue::Unsigned(
173 UIntTy::Usize,
174 fields.len() as u128,
175 )));
176 let tref = val.ty.kind().as_adt().unwrap();
177 assert_matches!(
178 *tref.id.as_builtin().unwrap(),
179 BuiltinTy::Array | BuiltinTy::Slice
180 );
181 let ty = tref.generics.types[0].clone();
182 let rval = Rvalue::Aggregate(AggregateKind::Array(ty, len), fields);
183 let var = new_var(rval, val.ty);
184
185 Operand::Move(var)
186 }
187 }
188}
189
190fn transform_operand(span: &Span, locals: &mut Locals, nst: &mut Vec<Statement>, op: &mut Operand) {
191 take_mut::take(op, |op| {
193 if let Operand::Const(val) = op {
194 let mut new_var = |rvalue, ty| {
195 if let Rvalue::Use(Operand::Move(place)) = rvalue {
196 place
197 } else {
198 let var = locals.new_var(None, ty);
199 nst.push(Statement::new(
200 *span,
201 StatementKind::StorageLive(var.as_local().unwrap()),
202 ));
203 nst.push(Statement::new(
204 *span,
205 StatementKind::Assign(var.clone(), rvalue),
206 ));
207 var
208 }
209 };
210 transform_constant_expr(span, val, &mut new_var)
211 } else {
212 op
213 }
214 })
215}
216
217pub struct Transform;
218impl UllbcPass for Transform {
219 fn transform_body(&self, _ctx: &mut TransformCtx, body: &mut ExprBody) {
220 for block in body.body.iter_mut() {
221 block.transform_operands(|span, nst, op| {
223 transform_operand(span, &mut body.locals, nst, op)
224 });
225
226 block.dyn_visit_in_body_mut(|rvalue: &mut Rvalue| {
228 take_mut::take(rvalue, |rvalue| match rvalue {
229 Rvalue::Aggregate(AggregateKind::Array(ty, len), ref fields)
230 if fields.len() >= 2
231 && fields.iter().all(|x| x.is_const())
232 && let Ok(op) = fields.iter().dedup().exactly_one() =>
233 {
234 Rvalue::Repeat(op.clone(), ty.clone(), len)
235 }
236 _ => rvalue,
237 });
238 });
239 }
240 }
241}