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