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
//! This pass eliminates casting of arrays into slices when their length
//! is taken using `.len()` method. Handy to preserve information in MIR for const prop

use crate::ssa::SsaLocals;
use crate::MirPass;
use rustc_index::IndexVec;
use rustc_middle::mir::visit::*;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, TyCtxt};

pub struct NormalizeArrayLen;

impl<'tcx> MirPass<'tcx> for NormalizeArrayLen {
    fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
        sess.mir_opt_level() >= 3
    }

    #[instrument(level = "trace", skip(self, tcx, body))]
    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
        debug!(def_id = ?body.source.def_id());
        normalize_array_len_calls(tcx, body)
    }
}

fn normalize_array_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
    let ssa = SsaLocals::new(body);

    let slice_lengths = compute_slice_length(tcx, &ssa, body);
    debug!(?slice_lengths);

    Replacer { tcx, slice_lengths }.visit_body_preserves_cfg(body);
}

fn compute_slice_length<'tcx>(
    tcx: TyCtxt<'tcx>,
    ssa: &SsaLocals,
    body: &Body<'tcx>,
) -> IndexVec<Local, Option<ty::Const<'tcx>>> {
    let mut slice_lengths = IndexVec::from_elem(None, &body.local_decls);

    for (local, rvalue, _) in ssa.assignments(body) {
        match rvalue {
            Rvalue::Cast(
                CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize),
                operand,
                cast_ty,
            ) => {
                let operand_ty = operand.ty(body, tcx);
                debug!(?operand_ty);
                if let Some(operand_ty) = operand_ty.builtin_deref(true)
                    && let ty::Array(_, len) = operand_ty.ty.kind()
                    && let Some(cast_ty) = cast_ty.builtin_deref(true)
                    && let ty::Slice(..) = cast_ty.ty.kind()
                {
                    slice_lengths[local] = Some(*len);
                }
            }
            // The length information is stored in the fat pointer, so we treat `operand` as a value.
            Rvalue::Use(operand) => {
                if let Some(rhs) = operand.place() && let Some(rhs) = rhs.as_local() {
                    slice_lengths[local] = slice_lengths[rhs];
                }
            }
            // The length information is stored in the fat pointer.
            // Reborrowing copies length information from one pointer to the other.
            Rvalue::Ref(_, _, rhs) | Rvalue::AddressOf(_, rhs) => {
                if let [PlaceElem::Deref] = rhs.projection[..] {
                    slice_lengths[local] = slice_lengths[rhs.local];
                }
            }
            _ => {}
        }
    }

    slice_lengths
}

struct Replacer<'tcx> {
    tcx: TyCtxt<'tcx>,
    slice_lengths: IndexVec<Local, Option<ty::Const<'tcx>>>,
}

impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
    fn tcx(&self) -> TyCtxt<'tcx> {
        self.tcx
    }

    fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, loc: Location) {
        if let Rvalue::Len(place) = rvalue
            && let [PlaceElem::Deref] = &place.projection[..]
            && let Some(len) = self.slice_lengths[place.local]
        {
            *rvalue = Rvalue::Use(Operand::Constant(Box::new(ConstOperand {
                span: rustc_span::DUMMY_SP,
                user_ty: None,
                const_: Const::from_ty_const(len, self.tcx),
            })));
        }
        self.super_rvalue(rvalue, loc);
    }
}