1use rustc_middle::mir::*;
4use rustc_middle::ty::{self, TyCtxt};
5use rustc_middle::{bug, span_bug};
6use rustc_span::sym;
7
8use crate::take_array;
9
10pub(super) struct LowerIntrinsics;
11
12impl<'tcx> crate::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 && let ty::FnDef(def_id, generic_args) = *func.ty(local_decls, tcx).kind()
20 && let Some(intrinsic) = tcx.intrinsic(def_id)
21 {
22 match intrinsic.name {
23 sym::unreachable => {
24 terminator.kind = TerminatorKind::Unreachable;
25 }
26 sym::ub_checks => {
27 let target = target.unwrap();
28 block.statements.push(Statement::new(
29 terminator.source_info,
30 StatementKind::Assign(Box::new((
31 *destination,
32 Rvalue::NullaryOp(NullOp::UbChecks, tcx.types.bool),
33 ))),
34 ));
35 terminator.kind = TerminatorKind::Goto { target };
36 }
37 sym::contract_checks => {
38 let target = target.unwrap();
39 block.statements.push(Statement::new(
40 terminator.source_info,
41 StatementKind::Assign(Box::new((
42 *destination,
43 Rvalue::NullaryOp(NullOp::ContractChecks, tcx.types.bool),
44 ))),
45 ));
46 terminator.kind = TerminatorKind::Goto { target };
47 }
48 sym::forget => {
49 let target = target.unwrap();
50 block.statements.push(Statement::new(
51 terminator.source_info,
52 StatementKind::Assign(Box::new((
53 *destination,
54 Rvalue::Use(Operand::Constant(Box::new(ConstOperand {
55 span: terminator.source_info.span,
56 user_ty: None,
57 const_: Const::zero_sized(tcx.types.unit),
58 }))),
59 ))),
60 ));
61 terminator.kind = TerminatorKind::Goto { target };
62 }
63 sym::copy_nonoverlapping => {
64 let target = target.unwrap();
65 let Ok([src, dst, count]) = take_array(args) else {
66 bug!("Wrong arguments for copy_non_overlapping intrinsic");
67 };
68 block.statements.push(Statement::new(
69 terminator.source_info,
70 StatementKind::Intrinsic(Box::new(
71 NonDivergingIntrinsic::CopyNonOverlapping(
72 rustc_middle::mir::CopyNonOverlapping {
73 src: src.node,
74 dst: dst.node,
75 count: count.node,
76 },
77 ),
78 )),
79 ));
80 terminator.kind = TerminatorKind::Goto { target };
81 }
82 sym::assume => {
83 let target = target.unwrap();
84 let Ok([arg]) = take_array(args) else {
85 bug!("Wrong arguments for assume intrinsic");
86 };
87 block.statements.push(Statement::new(
88 terminator.source_info,
89 StatementKind::Intrinsic(Box::new(NonDivergingIntrinsic::Assume(
90 arg.node,
91 ))),
92 ));
93 terminator.kind = TerminatorKind::Goto { target };
94 }
95 sym::wrapping_add
96 | sym::wrapping_sub
97 | sym::wrapping_mul
98 | sym::three_way_compare
99 | sym::unchecked_add
100 | sym::unchecked_sub
101 | sym::unchecked_mul
102 | sym::unchecked_div
103 | sym::unchecked_rem
104 | sym::unchecked_shl
105 | sym::unchecked_shr => {
106 let target = target.unwrap();
107 let Ok([lhs, rhs]) = take_array(args) else {
108 bug!("Wrong arguments for {} intrinsic", intrinsic.name);
109 };
110 let bin_op = match intrinsic.name {
111 sym::wrapping_add => BinOp::Add,
112 sym::wrapping_sub => BinOp::Sub,
113 sym::wrapping_mul => BinOp::Mul,
114 sym::three_way_compare => BinOp::Cmp,
115 sym::unchecked_add => BinOp::AddUnchecked,
116 sym::unchecked_sub => BinOp::SubUnchecked,
117 sym::unchecked_mul => BinOp::MulUnchecked,
118 sym::unchecked_div => BinOp::Div,
119 sym::unchecked_rem => BinOp::Rem,
120 sym::unchecked_shl => BinOp::ShlUnchecked,
121 sym::unchecked_shr => BinOp::ShrUnchecked,
122 _ => bug!("unexpected intrinsic"),
123 };
124 block.statements.push(Statement::new(
125 terminator.source_info,
126 StatementKind::Assign(Box::new((
127 *destination,
128 Rvalue::BinaryOp(bin_op, Box::new((lhs.node, rhs.node))),
129 ))),
130 ));
131 terminator.kind = TerminatorKind::Goto { target };
132 }
133 sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
134 let target = target.unwrap();
135 let Ok([lhs, rhs]) = take_array(args) else {
136 bug!("Wrong arguments for {} intrinsic", intrinsic.name);
137 };
138 let bin_op = match intrinsic.name {
139 sym::add_with_overflow => BinOp::AddWithOverflow,
140 sym::sub_with_overflow => BinOp::SubWithOverflow,
141 sym::mul_with_overflow => BinOp::MulWithOverflow,
142 _ => bug!("unexpected intrinsic"),
143 };
144 block.statements.push(Statement::new(
145 terminator.source_info,
146 StatementKind::Assign(Box::new((
147 *destination,
148 Rvalue::BinaryOp(bin_op, Box::new((lhs.node, rhs.node))),
149 ))),
150 ));
151 terminator.kind = TerminatorKind::Goto { target };
152 }
153 sym::size_of | sym::align_of => {
154 let target = target.unwrap();
155 let tp_ty = generic_args.type_at(0);
156 let null_op = match intrinsic.name {
157 sym::size_of => NullOp::SizeOf,
158 sym::align_of => NullOp::AlignOf,
159 _ => bug!("unexpected intrinsic"),
160 };
161 block.statements.push(Statement::new(
162 terminator.source_info,
163 StatementKind::Assign(Box::new((
164 *destination,
165 Rvalue::NullaryOp(null_op, tp_ty),
166 ))),
167 ));
168 terminator.kind = TerminatorKind::Goto { target };
169 }
170 sym::read_via_copy => {
171 let Ok([arg]) = take_array(args) else {
172 span_bug!(terminator.source_info.span, "Wrong number of arguments");
173 };
174 let derefed_place = if let Some(place) = arg.node.place()
175 && let Some(local) = place.as_local()
176 {
177 tcx.mk_place_deref(local.into())
178 } else {
179 span_bug!(
180 terminator.source_info.span,
181 "Only passing a local is supported"
182 );
183 };
184 block.statements.push(Statement::new(
187 terminator.source_info,
188 StatementKind::Assign(Box::new((
189 *destination,
190 Rvalue::Use(Operand::Copy(derefed_place)),
191 ))),
192 ));
193 terminator.kind = match *target {
194 None => {
195 TerminatorKind::Unreachable
198 }
199 Some(target) => TerminatorKind::Goto { target },
200 }
201 }
202 sym::write_via_move => {
203 let target = target.unwrap();
204 let Ok([ptr, val]) = take_array(args) else {
205 span_bug!(
206 terminator.source_info.span,
207 "Wrong number of arguments for write_via_move intrinsic",
208 );
209 };
210 let derefed_place = if let Some(place) = ptr.node.place()
211 && let Some(local) = place.as_local()
212 {
213 tcx.mk_place_deref(local.into())
214 } else {
215 span_bug!(
216 terminator.source_info.span,
217 "Only passing a local is supported"
218 );
219 };
220 block.statements.push(Statement::new(
221 terminator.source_info,
222 StatementKind::Assign(Box::new((derefed_place, Rvalue::Use(val.node)))),
223 ));
224 terminator.kind = TerminatorKind::Goto { target };
225 }
226 sym::discriminant_value => {
227 let target = target.unwrap();
228 let Ok([arg]) = take_array(args) else {
229 span_bug!(
230 terminator.source_info.span,
231 "Wrong arguments for discriminant_value intrinsic"
232 );
233 };
234 let arg = arg.node.place().unwrap();
235 let arg = tcx.mk_place_deref(arg);
236 block.statements.push(Statement::new(
237 terminator.source_info,
238 StatementKind::Assign(Box::new((
239 *destination,
240 Rvalue::Discriminant(arg),
241 ))),
242 ));
243 terminator.kind = TerminatorKind::Goto { target };
244 }
245 sym::offset => {
246 let target = target.unwrap();
247 let Ok([ptr, delta]) = take_array(args) else {
248 span_bug!(
249 terminator.source_info.span,
250 "Wrong number of arguments for offset intrinsic",
251 );
252 };
253 block.statements.push(Statement::new(
254 terminator.source_info,
255 StatementKind::Assign(Box::new((
256 *destination,
257 Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr.node, delta.node))),
258 ))),
259 ));
260 terminator.kind = TerminatorKind::Goto { target };
261 }
262 sym::slice_get_unchecked => {
263 let target = target.unwrap();
264 let Ok([ptrish, index]) = take_array(args) else {
265 span_bug!(
266 terminator.source_info.span,
267 "Wrong number of arguments for {intrinsic:?}",
268 );
269 };
270
271 let place = ptrish.node.place().unwrap();
272 assert!(!place.is_indirect());
273 let updated_place = place.project_deeper(
274 &[
275 ProjectionElem::Deref,
276 ProjectionElem::Index(
277 index.node.place().unwrap().as_local().unwrap(),
278 ),
279 ],
280 tcx,
281 );
282
283 let ret_ty = generic_args.type_at(0);
284 let rvalue = match *ret_ty.kind() {
285 ty::RawPtr(_, Mutability::Not) => {
286 Rvalue::RawPtr(RawPtrKind::Const, updated_place)
287 }
288 ty::RawPtr(_, Mutability::Mut) => {
289 Rvalue::RawPtr(RawPtrKind::Mut, updated_place)
290 }
291 ty::Ref(region, _, Mutability::Not) => {
292 Rvalue::Ref(region, BorrowKind::Shared, updated_place)
293 }
294 ty::Ref(region, _, Mutability::Mut) => Rvalue::Ref(
295 region,
296 BorrowKind::Mut { kind: MutBorrowKind::Default },
297 updated_place,
298 ),
299 _ => bug!("Unknown return type {ret_ty:?}"),
300 };
301
302 block.statements.push(Statement::new(
303 terminator.source_info,
304 StatementKind::Assign(Box::new((*destination, rvalue))),
305 ));
306 terminator.kind = TerminatorKind::Goto { target };
307 }
308 sym::transmute | sym::transmute_unchecked => {
309 let dst_ty = destination.ty(local_decls, tcx).ty;
310 let Ok([arg]) = take_array(args) else {
311 span_bug!(
312 terminator.source_info.span,
313 "Wrong number of arguments for transmute intrinsic",
314 );
315 };
316
317 block.statements.push(Statement::new(
321 terminator.source_info,
322 StatementKind::Assign(Box::new((
323 *destination,
324 Rvalue::Cast(CastKind::Transmute, arg.node, dst_ty),
325 ))),
326 ));
327 if let Some(target) = *target {
328 terminator.kind = TerminatorKind::Goto { target };
329 } else {
330 terminator.kind = TerminatorKind::Unreachable;
331 }
332 }
333 sym::aggregate_raw_ptr => {
334 let Ok([data, meta]) = take_array(args) else {
335 span_bug!(
336 terminator.source_info.span,
337 "Wrong number of arguments for aggregate_raw_ptr intrinsic",
338 );
339 };
340 let target = target.unwrap();
341 let pointer_ty = generic_args.type_at(0);
342 let kind = if let ty::RawPtr(pointee_ty, mutability) = pointer_ty.kind() {
343 AggregateKind::RawPtr(*pointee_ty, *mutability)
344 } else {
345 span_bug!(
346 terminator.source_info.span,
347 "Return type of aggregate_raw_ptr intrinsic must be a raw pointer",
348 );
349 };
350 let fields = [data.node, meta.node];
351 block.statements.push(Statement::new(
352 terminator.source_info,
353 StatementKind::Assign(Box::new((
354 *destination,
355 Rvalue::Aggregate(Box::new(kind), fields.into()),
356 ))),
357 ));
358 terminator.kind = TerminatorKind::Goto { target };
359 }
360 sym::ptr_metadata => {
361 let Ok([ptr]) = take_array(args) else {
362 span_bug!(
363 terminator.source_info.span,
364 "Wrong number of arguments for ptr_metadata intrinsic",
365 );
366 };
367 let target = target.unwrap();
368 block.statements.push(Statement::new(
369 terminator.source_info,
370 StatementKind::Assign(Box::new((
371 *destination,
372 Rvalue::UnaryOp(UnOp::PtrMetadata, ptr.node),
373 ))),
374 ));
375 terminator.kind = TerminatorKind::Goto { target };
376 }
377 _ => {}
378 }
379 }
380 }
381 }
382
383 fn is_required(&self) -> bool {
384 true
385 }
386}