]>
Commit | Line | Data |
---|---|---|
136023e0 XL |
1 | //! This pass lowers calls to core::slice::len to just Len op. |
2 | //! It should run before inlining! | |
3 | ||
4 | use crate::transform::MirPass; | |
5 | use rustc_hir::def_id::DefId; | |
6 | use rustc_index::vec::IndexVec; | |
7 | use rustc_middle::mir::*; | |
8 | use rustc_middle::ty::{self, TyCtxt}; | |
9 | ||
10 | pub struct LowerSliceLenCalls; | |
11 | ||
12 | impl<'tcx> MirPass<'tcx> for LowerSliceLenCalls { | |
13 | fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { | |
14 | lower_slice_len_calls(tcx, body) | |
15 | } | |
16 | } | |
17 | ||
18 | pub fn lower_slice_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { | |
19 | let language_items = tcx.lang_items(); | |
20 | let slice_len_fn_item_def_id = if let Some(slice_len_fn_item) = language_items.slice_len_fn() { | |
21 | slice_len_fn_item | |
22 | } else { | |
23 | // there is no language item to compare to :) | |
24 | return; | |
25 | }; | |
26 | ||
27 | let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); | |
28 | ||
29 | for block in basic_blocks { | |
30 | // lower `<[_]>::len` calls | |
31 | lower_slice_len_call(tcx, block, &*local_decls, slice_len_fn_item_def_id); | |
32 | } | |
33 | } | |
34 | ||
35 | struct SliceLenPatchInformation<'tcx> { | |
36 | add_statement: Statement<'tcx>, | |
37 | new_terminator_kind: TerminatorKind<'tcx>, | |
38 | } | |
39 | ||
40 | fn lower_slice_len_call<'tcx>( | |
41 | tcx: TyCtxt<'tcx>, | |
42 | block: &mut BasicBlockData<'tcx>, | |
43 | local_decls: &IndexVec<Local, LocalDecl<'tcx>>, | |
44 | slice_len_fn_item_def_id: DefId, | |
45 | ) { | |
46 | let mut patch_found: Option<SliceLenPatchInformation<'_>> = None; | |
47 | ||
48 | let terminator = block.terminator(); | |
49 | match &terminator.kind { | |
50 | TerminatorKind::Call { | |
51 | func, | |
52 | args, | |
53 | destination: Some((dest, bb)), | |
54 | cleanup: None, | |
55 | from_hir_call: true, | |
56 | .. | |
57 | } => { | |
58 | // some heuristics for fast rejection | |
59 | if args.len() != 1 { | |
60 | return; | |
61 | } | |
62 | let arg = match args[0].place() { | |
63 | Some(arg) => arg, | |
64 | None => return, | |
65 | }; | |
66 | let func_ty = func.ty(local_decls, tcx); | |
67 | match func_ty.kind() { | |
68 | ty::FnDef(fn_def_id, _) if fn_def_id == &slice_len_fn_item_def_id => { | |
69 | // perform modifications | |
70 | // from something like `_5 = core::slice::<impl [u8]>::len(move _6) -> bb1` | |
71 | // into `_5 = Len(*_6) | |
72 | // goto bb1 | |
73 | ||
74 | // make new RValue for Len | |
75 | let deref_arg = tcx.mk_place_deref(arg); | |
76 | let r_value = Rvalue::Len(deref_arg); | |
77 | let len_statement_kind = StatementKind::Assign(Box::new((*dest, r_value))); | |
78 | let add_statement = Statement { | |
79 | kind: len_statement_kind, | |
80 | source_info: terminator.source_info.clone(), | |
81 | }; | |
82 | ||
83 | // modify terminator into simple Goto | |
84 | let new_terminator_kind = TerminatorKind::Goto { target: bb.clone() }; | |
85 | ||
86 | let patch = SliceLenPatchInformation { add_statement, new_terminator_kind }; | |
87 | ||
88 | patch_found = Some(patch); | |
89 | } | |
90 | _ => {} | |
91 | } | |
92 | } | |
93 | _ => {} | |
94 | } | |
95 | ||
96 | if let Some(SliceLenPatchInformation { add_statement, new_terminator_kind }) = patch_found { | |
97 | block.statements.push(add_statement); | |
98 | block.terminator_mut().kind = new_terminator_kind; | |
99 | } | |
100 | } |