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