blob: dae01e41e5f3d3f466cf7729fabd258d1f537f51 [file] [log] [blame]
Chris Wailesa1538422021-12-02 10:37:12 -08001//! Lowers intrinsic calls
2
Chris Wailescd1aefd2023-07-13 13:36:21 -07003use crate::{errors, MirPass};
Chris Wailesa1538422021-12-02 10:37:12 -08004use rustc_middle::mir::*;
5use rustc_middle::ty::subst::SubstsRef;
6use rustc_middle::ty::{self, Ty, TyCtxt};
7use rustc_span::symbol::{sym, Symbol};
8use rustc_span::Span;
Charisee635618d2023-06-01 20:46:00 +00009use rustc_target::abi::{FieldIdx, VariantIdx};
Chris Wailesa1538422021-12-02 10:37:12 -080010
11pub struct LowerIntrinsics;
12
13impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
14 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
Chariseeb1d32802022-09-22 15:38:41 +000015 let local_decls = &body.local_decls;
16 for block in body.basic_blocks.as_mut() {
Chris Wailesa1538422021-12-02 10:37:12 -080017 let terminator = block.terminator.as_mut().unwrap();
Chris Wailes65720582022-08-11 09:53:28 -070018 if let TerminatorKind::Call { func, args, destination, target, .. } =
19 &mut terminator.kind
20 {
Chris Wailesa1538422021-12-02 10:37:12 -080021 let func_ty = func.ty(local_decls, tcx);
Charisee341341c2022-05-20 05:14:50 +000022 let Some((intrinsic_name, substs)) = resolve_rust_intrinsic(tcx, func_ty) else {
23 continue;
Chris Wailesa1538422021-12-02 10:37:12 -080024 };
25 match intrinsic_name {
26 sym::unreachable => {
27 terminator.kind = TerminatorKind::Unreachable;
28 }
29 sym::forget => {
Chris Wailes65720582022-08-11 09:53:28 -070030 if let Some(target) = *target {
Chris Wailesa1538422021-12-02 10:37:12 -080031 block.statements.push(Statement {
32 source_info: terminator.source_info,
33 kind: StatementKind::Assign(Box::new((
Chris Wailes65720582022-08-11 09:53:28 -070034 *destination,
Chris Wailesa1538422021-12-02 10:37:12 -080035 Rvalue::Use(Operand::Constant(Box::new(Constant {
36 span: terminator.source_info.span,
37 user_ty: None,
Chris Wailes65720582022-08-11 09:53:28 -070038 literal: ConstantKind::zero_sized(tcx.types.unit),
Chris Wailesa1538422021-12-02 10:37:12 -080039 }))),
40 ))),
41 });
42 terminator.kind = TerminatorKind::Goto { target };
43 }
44 }
45 sym::copy_nonoverlapping => {
Chris Wailes65720582022-08-11 09:53:28 -070046 let target = target.unwrap();
Chris Wailesa1538422021-12-02 10:37:12 -080047 let mut args = args.drain(..);
48 block.statements.push(Statement {
49 source_info: terminator.source_info,
Chris Wailes2f380c12022-11-09 13:04:22 -080050 kind: StatementKind::Intrinsic(Box::new(
51 NonDivergingIntrinsic::CopyNonOverlapping(
52 rustc_middle::mir::CopyNonOverlapping {
53 src: args.next().unwrap(),
54 dst: args.next().unwrap(),
55 count: args.next().unwrap(),
56 },
57 ),
58 )),
59 });
60 assert_eq!(
61 args.next(),
62 None,
63 "Extra argument for copy_non_overlapping intrinsic"
64 );
65 drop(args);
66 terminator.kind = TerminatorKind::Goto { target };
67 }
68 sym::assume => {
69 let target = target.unwrap();
70 let mut args = args.drain(..);
71 block.statements.push(Statement {
72 source_info: terminator.source_info,
73 kind: StatementKind::Intrinsic(Box::new(
74 NonDivergingIntrinsic::Assume(args.next().unwrap()),
Chris Wailesa1538422021-12-02 10:37:12 -080075 )),
76 });
77 assert_eq!(
78 args.next(),
79 None,
80 "Extra argument for copy_non_overlapping intrinsic"
81 );
82 drop(args);
83 terminator.kind = TerminatorKind::Goto { target };
84 }
85 sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => {
Chris Wailes65720582022-08-11 09:53:28 -070086 if let Some(target) = *target {
Chris Wailesa1538422021-12-02 10:37:12 -080087 let lhs;
88 let rhs;
89 {
90 let mut args = args.drain(..);
91 lhs = args.next().unwrap();
92 rhs = args.next().unwrap();
93 }
94 let bin_op = match intrinsic_name {
95 sym::wrapping_add => BinOp::Add,
96 sym::wrapping_sub => BinOp::Sub,
97 sym::wrapping_mul => BinOp::Mul,
98 _ => bug!("unexpected intrinsic"),
99 };
100 block.statements.push(Statement {
101 source_info: terminator.source_info,
102 kind: StatementKind::Assign(Box::new((
Chris Wailes65720582022-08-11 09:53:28 -0700103 *destination,
Chris Wailesa1538422021-12-02 10:37:12 -0800104 Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))),
105 ))),
106 });
107 terminator.kind = TerminatorKind::Goto { target };
108 }
109 }
110 sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
Chris Wailes5c0824a2023-04-24 16:30:59 -0700111 if let Some(target) = *target {
112 let lhs;
113 let rhs;
114 {
115 let mut args = args.drain(..);
116 lhs = args.next().unwrap();
117 rhs = args.next().unwrap();
118 }
119 let bin_op = match intrinsic_name {
120 sym::add_with_overflow => BinOp::Add,
121 sym::sub_with_overflow => BinOp::Sub,
122 sym::mul_with_overflow => BinOp::Mul,
123 _ => bug!("unexpected intrinsic"),
124 };
125 block.statements.push(Statement {
126 source_info: terminator.source_info,
127 kind: StatementKind::Assign(Box::new((
128 *destination,
129 Rvalue::CheckedBinaryOp(bin_op, Box::new((lhs, rhs))),
130 ))),
131 });
132 terminator.kind = TerminatorKind::Goto { target };
133 }
Chris Wailesa1538422021-12-02 10:37:12 -0800134 }
135 sym::size_of | sym::min_align_of => {
Chris Wailes65720582022-08-11 09:53:28 -0700136 if let Some(target) = *target {
Chris Wailesa1538422021-12-02 10:37:12 -0800137 let tp_ty = substs.type_at(0);
138 let null_op = match intrinsic_name {
139 sym::size_of => NullOp::SizeOf,
140 sym::min_align_of => NullOp::AlignOf,
141 _ => bug!("unexpected intrinsic"),
142 };
143 block.statements.push(Statement {
144 source_info: terminator.source_info,
145 kind: StatementKind::Assign(Box::new((
Chris Wailes65720582022-08-11 09:53:28 -0700146 *destination,
Chris Wailesa1538422021-12-02 10:37:12 -0800147 Rvalue::NullaryOp(null_op, tp_ty),
148 ))),
149 });
150 terminator.kind = TerminatorKind::Goto { target };
151 }
152 }
Charisee635618d2023-06-01 20:46:00 +0000153 sym::read_via_copy => {
154 let [arg] = args.as_slice() else {
155 span_bug!(terminator.source_info.span, "Wrong number of arguments");
156 };
157 let derefed_place =
158 if let Some(place) = arg.place() && let Some(local) = place.as_local() {
159 tcx.mk_place_deref(local.into())
160 } else {
161 span_bug!(terminator.source_info.span, "Only passing a local is supported");
162 };
163 terminator.kind = match *target {
164 None => {
165 // No target means this read something uninhabited,
166 // so it must be unreachable, and we don't need to
167 // preserve the assignment either.
168 TerminatorKind::Unreachable
169 }
170 Some(target) => {
171 block.statements.push(Statement {
172 source_info: terminator.source_info,
173 kind: StatementKind::Assign(Box::new((
174 *destination,
175 Rvalue::Use(Operand::Copy(derefed_place)),
176 ))),
177 });
178 TerminatorKind::Goto { target }
179 }
180 }
181 }
Chris Wailescd1aefd2023-07-13 13:36:21 -0700182 sym::write_via_move => {
183 let target = target.unwrap();
184 let Ok([ptr, val]) = <[_; 2]>::try_from(std::mem::take(args)) else {
185 span_bug!(
186 terminator.source_info.span,
187 "Wrong number of arguments for write_via_move intrinsic",
188 );
189 };
190 let derefed_place =
191 if let Some(place) = ptr.place() && let Some(local) = place.as_local() {
192 tcx.mk_place_deref(local.into())
193 } else {
194 span_bug!(terminator.source_info.span, "Only passing a local is supported");
195 };
196 block.statements.push(Statement {
197 source_info: terminator.source_info,
198 kind: StatementKind::Assign(Box::new((
199 derefed_place,
200 Rvalue::Use(val),
201 ))),
202 });
203 terminator.kind = TerminatorKind::Goto { target };
204 }
Chris Wailesa1538422021-12-02 10:37:12 -0800205 sym::discriminant_value => {
Chris Wailes65720582022-08-11 09:53:28 -0700206 if let (Some(target), Some(arg)) = (*target, args[0].place()) {
Chris Wailesa1538422021-12-02 10:37:12 -0800207 let arg = tcx.mk_place_deref(arg);
208 block.statements.push(Statement {
209 source_info: terminator.source_info,
210 kind: StatementKind::Assign(Box::new((
Chris Wailes65720582022-08-11 09:53:28 -0700211 *destination,
Chris Wailesa1538422021-12-02 10:37:12 -0800212 Rvalue::Discriminant(arg),
213 ))),
214 });
215 terminator.kind = TerminatorKind::Goto { target };
216 }
217 }
Chris Wailescd1aefd2023-07-13 13:36:21 -0700218 sym::offset => {
219 let target = target.unwrap();
220 let Ok([ptr, delta]) = <[_; 2]>::try_from(std::mem::take(args)) else {
221 span_bug!(
222 terminator.source_info.span,
223 "Wrong number of arguments for offset intrinsic",
224 );
225 };
226 block.statements.push(Statement {
227 source_info: terminator.source_info,
228 kind: StatementKind::Assign(Box::new((
229 *destination,
230 Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr, delta))),
231 ))),
232 });
233 terminator.kind = TerminatorKind::Goto { target };
234 }
Charisee635618d2023-06-01 20:46:00 +0000235 sym::option_payload_ptr => {
236 if let (Some(target), Some(arg)) = (*target, args[0].place()) {
237 let ty::RawPtr(ty::TypeAndMut { ty: dest_ty, .. }) =
238 destination.ty(local_decls, tcx).ty.kind()
239 else { bug!(); };
240
241 block.statements.push(Statement {
242 source_info: terminator.source_info,
243 kind: StatementKind::Assign(Box::new((
244 *destination,
245 Rvalue::AddressOf(
246 Mutability::Not,
247 arg.project_deeper(
248 &[
249 PlaceElem::Deref,
250 PlaceElem::Downcast(
251 Some(sym::Some),
252 VariantIdx::from_u32(1),
253 ),
254 PlaceElem::Field(FieldIdx::from_u32(0), *dest_ty),
255 ],
256 tcx,
257 ),
258 ),
259 ))),
260 });
261 terminator.kind = TerminatorKind::Goto { target };
262 }
263 }
Chris Wailescd1aefd2023-07-13 13:36:21 -0700264 sym::transmute | sym::transmute_unchecked => {
Charisee635618d2023-06-01 20:46:00 +0000265 let dst_ty = destination.ty(local_decls, tcx).ty;
266 let Ok([arg]) = <[_; 1]>::try_from(std::mem::take(args)) else {
267 span_bug!(
268 terminator.source_info.span,
269 "Wrong number of arguments for transmute intrinsic",
270 );
271 };
272
273 // Always emit the cast, even if we transmute to an uninhabited type,
274 // because that lets CTFE and codegen generate better error messages
275 // when such a transmute actually ends up reachable.
276 block.statements.push(Statement {
277 source_info: terminator.source_info,
278 kind: StatementKind::Assign(Box::new((
279 *destination,
280 Rvalue::Cast(CastKind::Transmute, arg, dst_ty),
281 ))),
282 });
283
284 if let Some(target) = *target {
285 terminator.kind = TerminatorKind::Goto { target };
286 } else {
287 terminator.kind = TerminatorKind::Unreachable;
288 }
289 }
Chris Wailesa1538422021-12-02 10:37:12 -0800290 _ if intrinsic_name.as_str().starts_with("simd_shuffle") => {
291 validate_simd_shuffle(tcx, args, terminator.source_info.span);
292 }
293 _ => {}
294 }
295 }
296 }
297 }
298}
299
Charisee7878d542022-02-24 18:21:36 +0000300fn resolve_rust_intrinsic<'tcx>(
Chris Wailesa1538422021-12-02 10:37:12 -0800301 tcx: TyCtxt<'tcx>,
302 func_ty: Ty<'tcx>,
303) -> Option<(Symbol, SubstsRef<'tcx>)> {
304 if let ty::FnDef(def_id, substs) = *func_ty.kind() {
Chris Wailes65720582022-08-11 09:53:28 -0700305 if tcx.is_intrinsic(def_id) {
Chris Wailesa1538422021-12-02 10:37:12 -0800306 return Some((tcx.item_name(def_id), substs));
307 }
308 }
309 None
310}
311
Charisee7878d542022-02-24 18:21:36 +0000312fn validate_simd_shuffle<'tcx>(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) {
Chris Wailescd1aefd2023-07-13 13:36:21 -0700313 if !matches!(args[2], Operand::Constant(_)) {
314 tcx.sess.emit_err(errors::SimdShuffleLastConst { span });
Chris Wailesa1538422021-12-02 10:37:12 -0800315 }
316}