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