charon_lib/transform/simplify_output/
index_to_function_calls.rs1use crate::llbc_ast::*;
4use crate::transform::TransformCtx;
5use crate::transform::ctx::{BodyTransformCtx, LlbcStatementTransformCtx};
6use derive_generic_visitor::*;
7
8use crate::transform::ctx::LlbcPass;
9
10#[derive(Visitor)]
16struct IndexVisitor<'a, 'b> {
17 ctx: &'b mut LlbcStatementTransformCtx<'a>,
18 place_mutability_stack: Vec<bool>,
23}
24
25impl<'a, 'b> IndexVisitor<'a, 'b> {
26 fn transform_place(&mut self, mut_access: bool, place: &mut Place) {
28 use ProjectionElem::*;
29 let Some((subplace, pe @ (Index { .. } | Subslice { .. }))) = place.as_projection() else {
32 return;
33 };
34
35 let (ty, len) = match subplace.ty.kind() {
36 TyKind::Array(ty, len) => (ty.clone(), Some(len.clone())),
37 TyKind::Slice(ty) => (ty.clone(), None),
38 _ => unreachable!("Indexing can only be done on arrays or slices"),
39 };
40
41 let indexing_function = {
43 let builtin_fun = BuiltinFunId::Index(BuiltinIndexOp {
44 is_array: subplace.ty.kind().is_array(),
45 mutability: RefKind::mutable(mut_access),
46 is_range: pe.is_subslice(),
47 });
48 let generics = GenericArgs {
50 types: [ty.clone()].into(),
51 const_generics: len.map(|l| [l].into()).unwrap_or_default(),
52 regions: [Region::Erased].into(),
53 trait_refs: [].into(),
54 };
55 FnOperand::Regular(FnPtr::new(FnPtrKind::mk_builtin(builtin_fun), generics))
56 };
57
58 let output_inner_ty = if matches!(pe, Index { .. }) {
59 ty
60 } else {
61 TyKind::Slice(ty).into_ty()
62 };
63 let output_ty = {
64 TyKind::Ref(
65 Region::Erased,
66 output_inner_ty.clone(),
67 RefKind::mutable(mut_access),
68 )
69 .into_ty()
70 };
71
72 let input_var =
76 self.ctx
77 .borrow_to_new_var(subplace.clone(), BorrowKind::mutable(mut_access), None);
78
79 let mut args = vec![Operand::Move(input_var)];
81 if let Subslice { from, .. } = &pe {
82 args.push(from.as_ref().clone());
83 }
84 let (last_arg, from_end) = match &pe {
85 Index {
86 offset: x,
87 from_end,
88 ..
89 }
90 | Subslice {
91 to: x, from_end, ..
92 } => (x.as_ref().clone(), *from_end),
93 _ => unreachable!(),
94 };
95 let to_idx = self
96 .ctx
97 .compute_subslice_end_idx(subplace, last_arg, from_end);
98 args.push(to_idx);
99
100 let output_var = {
104 let output_var = self.ctx.fresh_var(None, output_ty);
105 let index_call = Call {
106 func: indexing_function,
107 args,
108 dest: output_var.clone(),
109 };
110 let kind = StatementKind::Call(index_call);
111 self.ctx
112 .statements
113 .push(Statement::new(self.ctx.span, kind));
114 output_var
115 };
116
117 *place = output_var.project(ProjectionElem::Deref, output_inner_ty);
119 }
120
121 fn visit_inner_with_mutability<T>(
123 &mut self,
124 x: &mut T,
125 mutability: bool,
126 ) -> ControlFlow<Infallible>
127 where
128 T: for<'s> DriveMut<'s, BodyVisitableWrapper<Self>> + BodyVisitable,
129 {
130 self.place_mutability_stack.push(mutability);
131 self.visit_inner(x)?;
132 self.place_mutability_stack.pop();
133 Continue(())
134 }
135}
136
137impl VisitBodyMut for IndexVisitor<'_, '_> {
139 fn exit_place(&mut self, place: &mut Place) {
141 let mut_access = *self.place_mutability_stack.last().unwrap();
144 self.transform_place(mut_access, place);
145 }
146
147 fn visit_operand(&mut self, x: &mut Operand) -> ControlFlow<Infallible> {
148 match x {
149 Operand::Move(_) => self.visit_inner_with_mutability(x, true),
150 Operand::Copy(_) => self.visit_inner_with_mutability(x, false),
151 Operand::Const(..) => self.visit_inner(x),
152 }
153 }
154
155 fn visit_call(&mut self, x: &mut Call) -> ControlFlow<Infallible> {
156 self.visit_inner_with_mutability(x, true)
157 }
158
159 fn visit_fn_operand(&mut self, x: &mut FnOperand) -> ControlFlow<Infallible> {
160 match x {
161 FnOperand::Regular(_) => self.visit_inner(x),
162 FnOperand::Dynamic(_) => self.visit_inner_with_mutability(x, true),
163 }
164 }
165
166 fn visit_rvalue(&mut self, x: &mut Rvalue) -> ControlFlow<Infallible> {
167 use Rvalue::*;
168 match x {
169 RawPtr {
172 kind: RefKind::Mut, ..
173 }
174 | Ref {
175 kind: BorrowKind::Mut | BorrowKind::TwoPhaseMut | BorrowKind::UniqueImmutable,
176 ..
177 } => self.visit_inner_with_mutability(x, true),
178 RawPtr {
179 kind: RefKind::Shared,
180 ..
181 }
182 | Ref {
183 kind: BorrowKind::Shared | BorrowKind::Shallow,
184 ..
185 }
186 | Discriminant(..)
187 | Len(..) => self.visit_inner_with_mutability(x, false),
188
189 Use(_) | NullaryOp(..) | UnaryOp(..) | BinaryOp(..) | Aggregate(..) | Repeat(..)
190 | ShallowInitBox(..) => self.visit_inner(x),
191 }
192 }
193
194 fn visit_llbc_block(&mut self, _: &mut llbc_ast::Block) -> ControlFlow<Infallible> {
195 ControlFlow::Continue(())
196 }
197}
198
199pub struct Transform;
258impl LlbcPass for Transform {
259 fn should_run(&self, options: &crate::options::TranslateOptions) -> bool {
260 options.index_to_function_calls
261 }
262
263 fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
264 decl.transform_llbc_statements(ctx, |ctx, st: &mut Statement| {
265 let mut visitor = IndexVisitor {
266 ctx,
267 place_mutability_stack: Vec::new(),
268 };
269 use StatementKind::*;
270 match &mut st.kind {
271 Assign(..)
272 | SetDiscriminant(..)
273 | CopyNonOverlapping(_)
274 | Drop(..)
275 | Deinit(..)
276 | Call(..) => {
277 let _ = visitor.visit_inner_with_mutability(st, true);
278 }
279 Switch(..) => {
280 let _ = visitor.visit_inner_with_mutability(st, false);
281 }
282 Nop | Error(..) | Assert(..) | Abort(..) | StorageDead(..) | StorageLive(..)
283 | Return | Break(..) | Continue(..) | Loop(..) => {
284 let _ = st.drive_body_mut(&mut visitor);
285 }
286 }
287 })
288 }
289}