Skip to main content

charon_lib/transform/simplify_output/
ops_to_function_calls.rs

1//! Desugar some unary/binary operations and the array repeats to function calls.
2//! For instance, we desugar ArrayToSlice from an unop to a function call.
3//! This allows a more uniform treatment later on.
4//! TODO: actually transform all the unops and binops to function calls?
5use crate::llbc_ast::*;
6use crate::transform::TransformCtx;
7
8use crate::transform::ctx::LlbcPass;
9
10fn transform_st(s: &mut Statement) {
11    match &s.kind {
12        // Transform the ArrayToSlice unop
13        StatementKind::Assign(
14            p,
15            Rvalue::UnaryOp(
16                UnOp::Cast(CastKind::Unsize(src_ty, tgt_ty, UnsizingMetadata::Length(_))),
17                op,
18            ),
19        ) => {
20            if let (TyKind::Ref(_, ty1, kind1), TyKind::Ref(_, ty2, kind2)) =
21                (src_ty.kind(), tgt_ty.kind())
22                && let TyKind::Array(arr_ty, len) = ty1.kind()
23                && let TyKind::Slice(..) = ty2.kind()
24            {
25                // In MIR terminology, we go from &[T; l] to &[T] which means we
26                // effectively "unsize" the type, as `l` no longer appears in the
27                // destination type. At runtime, the converse happens: the length
28                // materializes into the fat pointer.
29                assert!(kind1 == kind2);
30                // We could avoid the clone operations below if we take the content of
31                // the statement. In practice, this shouldn't have much impact.
32                let id = match *kind1 {
33                    RefKind::Mut => BuiltinFunId::ArrayToSliceMut,
34                    RefKind::Shared => BuiltinFunId::ArrayToSliceShared,
35                };
36                let func = FnPtrKind::mk_builtin(id);
37                let generics = GenericArgs::new(
38                    [Region::Erased].into(),
39                    [arr_ty.clone()].into(),
40                    [*len.clone()].into(),
41                    [].into(),
42                );
43                s.kind = StatementKind::Call {
44                    call: Call {
45                        func: FnOperand::Regular(FnPtr::new(func, generics)),
46                        args: vec![op.clone()],
47                        dest: p.clone(),
48                    },
49                    on_unwind: Block::new_unreachable(s.span),
50                };
51            }
52        }
53        // Transform the array aggregates to function calls
54        StatementKind::Assign(p, Rvalue::Repeat(op, ty, cg)) => {
55            // We could avoid the clone operations below if we take the content of
56            // the statement. In practice, this shouldn't have much impact.
57            let id = BuiltinFunId::ArrayRepeat;
58            let func = FnPtrKind::mk_builtin(id);
59            let generics = GenericArgs::new(
60                [].into(),
61                [ty.clone()].into(),
62                [*cg.clone()].into(),
63                [].into(),
64            );
65            s.kind = StatementKind::Call {
66                call: Call {
67                    func: FnOperand::Regular(FnPtr::new(func, generics)),
68                    args: vec![op.clone()],
69                    dest: p.clone(),
70                },
71                on_unwind: Block::new_unreachable(s.span),
72            };
73        }
74        // Transform the raw pointer aggregate to a function call
75        StatementKind::Assign(p, Rvalue::Aggregate(AggregateKind::RawPtr(ty, is_mut), ops)) => {
76            let id = BuiltinFunId::PtrFromParts(*is_mut);
77            let func = FnPtrKind::mk_builtin(id);
78            let generics = GenericArgs::new(
79                [Region::Erased].into(),
80                [ty.clone()].into(),
81                [].into(),
82                [].into(),
83            );
84
85            s.kind = StatementKind::Call {
86                call: Call {
87                    func: FnOperand::Regular(FnPtr::new(func, generics)),
88                    args: ops.clone(),
89                    dest: p.clone(),
90                },
91                on_unwind: Block::new_unreachable(s.span),
92            };
93        }
94        _ => {}
95    }
96}
97
98pub struct Transform;
99impl LlbcPass for Transform {
100    fn should_run(&self, options: &crate::options::TranslateOptions) -> bool {
101        options.ops_to_function_calls
102    }
103
104    fn transform_body(&self, _ctx: &mut TransformCtx, b: &mut ExprBody) {
105        b.body.visit_statements(&mut transform_st);
106    }
107}