]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_transform/src/lower_intrinsics.rs
Introduce NullOp::AlignOf
[rust.git] / compiler / rustc_mir_transform / src / lower_intrinsics.rs
1 //! Lowers intrinsic calls
2
3 use crate::MirPass;
4 use rustc_middle::mir::*;
5 use rustc_middle::ty::subst::SubstsRef;
6 use rustc_middle::ty::{self, Ty, TyCtxt};
7 use rustc_span::symbol::{sym, Symbol};
8 use rustc_span::Span;
9 use rustc_target::spec::abi::Abi;
10
11 pub struct LowerIntrinsics;
12
13 impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
14     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
15         let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
16         for block in basic_blocks {
17             let terminator = block.terminator.as_mut().unwrap();
18             if let TerminatorKind::Call { func, args, destination, .. } = &mut terminator.kind {
19                 let func_ty = func.ty(local_decls, tcx);
20                 let (intrinsic_name, substs) = match resolve_rust_intrinsic(tcx, func_ty) {
21                     None => continue,
22                     Some(it) => it,
23                 };
24                 match intrinsic_name {
25                     sym::unreachable => {
26                         terminator.kind = TerminatorKind::Unreachable;
27                     }
28                     sym::forget => {
29                         if let Some((destination, target)) = *destination {
30                             block.statements.push(Statement {
31                                 source_info: terminator.source_info,
32                                 kind: StatementKind::Assign(Box::new((
33                                     destination,
34                                     Rvalue::Use(Operand::Constant(Box::new(Constant {
35                                         span: terminator.source_info.span,
36                                         user_ty: None,
37                                         literal: ty::Const::zero_sized(tcx, tcx.types.unit).into(),
38                                     }))),
39                                 ))),
40                             });
41                             terminator.kind = TerminatorKind::Goto { target };
42                         }
43                     }
44                     sym::copy_nonoverlapping => {
45                         let target = destination.unwrap().1;
46                         let mut args = args.drain(..);
47                         block.statements.push(Statement {
48                             source_info: terminator.source_info,
49                             kind: StatementKind::CopyNonOverlapping(Box::new(
50                                 rustc_middle::mir::CopyNonOverlapping {
51                                     src: args.next().unwrap(),
52                                     dst: args.next().unwrap(),
53                                     count: args.next().unwrap(),
54                                 },
55                             )),
56                         });
57                         assert_eq!(
58                             args.next(),
59                             None,
60                             "Extra argument for copy_non_overlapping intrinsic"
61                         );
62                         drop(args);
63                         terminator.kind = TerminatorKind::Goto { target };
64                     }
65                     sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => {
66                         if let Some((destination, target)) = *destination {
67                             let lhs;
68                             let rhs;
69                             {
70                                 let mut args = args.drain(..);
71                                 lhs = args.next().unwrap();
72                                 rhs = args.next().unwrap();
73                             }
74                             let bin_op = match intrinsic_name {
75                                 sym::wrapping_add => BinOp::Add,
76                                 sym::wrapping_sub => BinOp::Sub,
77                                 sym::wrapping_mul => BinOp::Mul,
78                                 _ => bug!("unexpected intrinsic"),
79                             };
80                             block.statements.push(Statement {
81                                 source_info: terminator.source_info,
82                                 kind: StatementKind::Assign(Box::new((
83                                     destination,
84                                     Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))),
85                                 ))),
86                             });
87                             terminator.kind = TerminatorKind::Goto { target };
88                         }
89                     }
90                     sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
91                         // The checked binary operations are not suitable target for lowering here,
92                         // since their semantics depend on the value of overflow-checks flag used
93                         // during codegen. Issue #35310.
94                     }
95                     sym::size_of | sym::min_align_of => {
96                         if let Some((destination, target)) = *destination {
97                             let tp_ty = substs.type_at(0);
98                             let null_op = match intrinsic_name {
99                                 sym::size_of => NullOp::SizeOf,
100                                 sym::min_align_of => NullOp::AlignOf,
101                                 _ => bug!("unexpected intrinsic"),
102                             };
103                             block.statements.push(Statement {
104                                 source_info: terminator.source_info,
105                                 kind: StatementKind::Assign(Box::new((
106                                     destination,
107                                     Rvalue::NullaryOp(null_op, tp_ty),
108                                 ))),
109                             });
110                             terminator.kind = TerminatorKind::Goto { target };
111                         }
112                     }
113                     sym::discriminant_value => {
114                         if let (Some((destination, target)), Some(arg)) =
115                             (*destination, args[0].place())
116                         {
117                             let arg = tcx.mk_place_deref(arg);
118                             block.statements.push(Statement {
119                                 source_info: terminator.source_info,
120                                 kind: StatementKind::Assign(Box::new((
121                                     destination,
122                                     Rvalue::Discriminant(arg),
123                                 ))),
124                             });
125                             terminator.kind = TerminatorKind::Goto { target };
126                         }
127                     }
128                     _ if intrinsic_name.as_str().starts_with("simd_shuffle") => {
129                         validate_simd_shuffle(tcx, args, terminator.source_info.span);
130                     }
131                     _ => {}
132                 }
133             }
134         }
135     }
136 }
137
138 fn resolve_rust_intrinsic(
139     tcx: TyCtxt<'tcx>,
140     func_ty: Ty<'tcx>,
141 ) -> Option<(Symbol, SubstsRef<'tcx>)> {
142     if let ty::FnDef(def_id, substs) = *func_ty.kind() {
143         let fn_sig = func_ty.fn_sig(tcx);
144         if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() {
145             return Some((tcx.item_name(def_id), substs));
146         }
147     }
148     None
149 }
150
151 fn validate_simd_shuffle(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) {
152     match &args[2] {
153         Operand::Constant(_) => {} // all good
154         _ => {
155             let msg = "last argument of `simd_shuffle` is required to be a `const` item";
156             tcx.sess.span_err(span, msg);
157         }
158     }
159 }