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 {
29 source_info: terminator.source_info,
30 kind: 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 {
40 source_info: terminator.source_info,
41 kind: 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 {
51 source_info: terminator.source_info,
52 kind: 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 {
69 source_info: terminator.source_info,
70 kind: 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 {
88 source_info: terminator.source_info,
89 kind: StatementKind::Intrinsic(Box::new(
90 NonDivergingIntrinsic::Assume(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 {
125 source_info: terminator.source_info,
126 kind: 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 {
145 source_info: terminator.source_info,
146 kind: 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::min_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::min_align_of => NullOp::AlignOf,
159 _ => bug!("unexpected intrinsic"),
160 };
161 block.statements.push(Statement {
162 source_info: terminator.source_info,
163 kind: 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 {
187 source_info: terminator.source_info,
188 kind: 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 {
221 source_info: terminator.source_info,
222 kind: StatementKind::Assign(Box::new((
223 derefed_place,
224 Rvalue::Use(val.node),
225 ))),
226 });
227 terminator.kind = TerminatorKind::Goto { target };
228 }
229 sym::discriminant_value => {
230 let target = target.unwrap();
231 let Ok([arg]) = take_array(args) else {
232 span_bug!(
233 terminator.source_info.span,
234 "Wrong arguments for discriminant_value intrinsic"
235 );
236 };
237 let arg = arg.node.place().unwrap();
238 let arg = tcx.mk_place_deref(arg);
239 block.statements.push(Statement {
240 source_info: terminator.source_info,
241 kind: StatementKind::Assign(Box::new((
242 *destination,
243 Rvalue::Discriminant(arg),
244 ))),
245 });
246 terminator.kind = TerminatorKind::Goto { target };
247 }
248 sym::offset => {
249 let target = target.unwrap();
250 let Ok([ptr, delta]) = take_array(args) else {
251 span_bug!(
252 terminator.source_info.span,
253 "Wrong number of arguments for offset intrinsic",
254 );
255 };
256 block.statements.push(Statement {
257 source_info: terminator.source_info,
258 kind: StatementKind::Assign(Box::new((
259 *destination,
260 Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr.node, delta.node))),
261 ))),
262 });
263 terminator.kind = TerminatorKind::Goto { target };
264 }
265 sym::transmute | sym::transmute_unchecked => {
266 let dst_ty = destination.ty(local_decls, tcx).ty;
267 let Ok([arg]) = take_array(args) else {
268 span_bug!(
269 terminator.source_info.span,
270 "Wrong number of arguments for transmute intrinsic",
271 );
272 };
273
274 block.statements.push(Statement {
278 source_info: terminator.source_info,
279 kind: StatementKind::Assign(Box::new((
280 *destination,
281 Rvalue::Cast(CastKind::Transmute, arg.node, dst_ty),
282 ))),
283 });
284 if let Some(target) = *target {
285 terminator.kind = TerminatorKind::Goto { target };
286 } else {
287 terminator.kind = TerminatorKind::Unreachable;
288 }
289 }
290 sym::aggregate_raw_ptr => {
291 let Ok([data, meta]) = take_array(args) else {
292 span_bug!(
293 terminator.source_info.span,
294 "Wrong number of arguments for aggregate_raw_ptr intrinsic",
295 );
296 };
297 let target = target.unwrap();
298 let pointer_ty = generic_args.type_at(0);
299 let kind = if let ty::RawPtr(pointee_ty, mutability) = pointer_ty.kind() {
300 AggregateKind::RawPtr(*pointee_ty, *mutability)
301 } else {
302 span_bug!(
303 terminator.source_info.span,
304 "Return type of aggregate_raw_ptr intrinsic must be a raw pointer",
305 );
306 };
307 let fields = [data.node, meta.node];
308 block.statements.push(Statement {
309 source_info: terminator.source_info,
310 kind: StatementKind::Assign(Box::new((
311 *destination,
312 Rvalue::Aggregate(Box::new(kind), fields.into()),
313 ))),
314 });
315 terminator.kind = TerminatorKind::Goto { target };
316 }
317 sym::ptr_metadata => {
318 let Ok([ptr]) = take_array(args) else {
319 span_bug!(
320 terminator.source_info.span,
321 "Wrong number of arguments for ptr_metadata intrinsic",
322 );
323 };
324 let target = target.unwrap();
325 block.statements.push(Statement {
326 source_info: terminator.source_info,
327 kind: StatementKind::Assign(Box::new((
328 *destination,
329 Rvalue::UnaryOp(UnOp::PtrMetadata, ptr.node),
330 ))),
331 });
332 terminator.kind = TerminatorKind::Goto { target };
333 }
334 _ => {}
335 }
336 }
337 }
338 }
339
340 fn is_required(&self) -> bool {
341 true
342 }
343}