]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_transform/src/lower_intrinsics.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / compiler / rustc_mir_transform / src / lower_intrinsics.rs
CommitLineData
fc512014
XL
1//! Lowers intrinsic calls
2
c295e0f8 3use crate::MirPass;
fc512014
XL
4use rustc_middle::mir::*;
5use rustc_middle::ty::subst::SubstsRef;
6use rustc_middle::ty::{self, Ty, TyCtxt};
7use rustc_span::symbol::{sym, Symbol};
17df50a5 8use rustc_span::Span;
353b0b11 9use rustc_target::abi::{FieldIdx, VariantIdx};
fc512014
XL
10
11pub struct LowerIntrinsics;
12
13impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
14 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
064997fb
FG
15 let local_decls = &body.local_decls;
16 for block in body.basic_blocks.as_mut() {
fc512014 17 let terminator = block.terminator.as_mut().unwrap();
923072b8
FG
18 if let TerminatorKind::Call { func, args, destination, target, .. } =
19 &mut terminator.kind
20 {
fc512014 21 let func_ty = func.ty(local_decls, tcx);
5e7ed085
FG
22 let Some((intrinsic_name, substs)) = resolve_rust_intrinsic(tcx, func_ty) else {
23 continue;
fc512014
XL
24 };
25 match intrinsic_name {
26 sym::unreachable => {
27 terminator.kind = TerminatorKind::Unreachable;
28 }
29 sym::forget => {
923072b8 30 if let Some(target) = *target {
fc512014
XL
31 block.statements.push(Statement {
32 source_info: terminator.source_info,
94222f64 33 kind: StatementKind::Assign(Box::new((
923072b8 34 *destination,
94222f64 35 Rvalue::Use(Operand::Constant(Box::new(Constant {
fc512014
XL
36 span: terminator.source_info.span,
37 user_ty: None,
923072b8 38 literal: ConstantKind::zero_sized(tcx.types.unit),
94222f64
XL
39 }))),
40 ))),
fc512014
XL
41 });
42 terminator.kind = TerminatorKind::Goto { target };
43 }
44 }
6a06907d 45 sym::copy_nonoverlapping => {
923072b8 46 let target = target.unwrap();
6a06907d
XL
47 let mut args = args.drain(..);
48 block.statements.push(Statement {
49 source_info: terminator.source_info,
f2b60f7d
FG
50 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()),
94222f64 75 )),
6a06907d
XL
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 }
fc512014 85 sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => {
923072b8 86 if let Some(target) = *target {
fc512014
XL
87 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,
94222f64 102 kind: StatementKind::Assign(Box::new((
923072b8 103 *destination,
94222f64
XL
104 Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))),
105 ))),
fc512014
XL
106 });
107 terminator.kind = TerminatorKind::Goto { target };
108 }
109 }
110 sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
9ffffee4
FG
111 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 }
fc512014 134 }
c295e0f8 135 sym::size_of | sym::min_align_of => {
923072b8 136 if let Some(target) = *target {
fc512014 137 let tp_ty = substs.type_at(0);
c295e0f8
XL
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 };
fc512014
XL
143 block.statements.push(Statement {
144 source_info: terminator.source_info,
94222f64 145 kind: StatementKind::Assign(Box::new((
923072b8 146 *destination,
c295e0f8 147 Rvalue::NullaryOp(null_op, tp_ty),
94222f64 148 ))),
fc512014
XL
149 });
150 terminator.kind = TerminatorKind::Goto { target };
151 }
152 }
353b0b11
FG
153 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 }
fc512014 182 sym::discriminant_value => {
923072b8 183 if let (Some(target), Some(arg)) = (*target, args[0].place()) {
fc512014
XL
184 let arg = tcx.mk_place_deref(arg);
185 block.statements.push(Statement {
186 source_info: terminator.source_info,
94222f64 187 kind: StatementKind::Assign(Box::new((
923072b8 188 *destination,
fc512014 189 Rvalue::Discriminant(arg),
94222f64 190 ))),
fc512014
XL
191 });
192 terminator.kind = TerminatorKind::Goto { target };
193 }
194 }
353b0b11
FG
195 sym::option_payload_ptr => {
196 if let (Some(target), Some(arg)) = (*target, args[0].place()) {
197 let ty::RawPtr(ty::TypeAndMut { ty: dest_ty, .. }) =
198 destination.ty(local_decls, tcx).ty.kind()
199 else { bug!(); };
200
201 block.statements.push(Statement {
202 source_info: terminator.source_info,
203 kind: StatementKind::Assign(Box::new((
204 *destination,
205 Rvalue::AddressOf(
206 Mutability::Not,
207 arg.project_deeper(
208 &[
209 PlaceElem::Deref,
210 PlaceElem::Downcast(
211 Some(sym::Some),
212 VariantIdx::from_u32(1),
213 ),
214 PlaceElem::Field(FieldIdx::from_u32(0), *dest_ty),
215 ],
216 tcx,
217 ),
218 ),
219 ))),
220 });
221 terminator.kind = TerminatorKind::Goto { target };
222 }
223 }
224 sym::transmute => {
225 let dst_ty = destination.ty(local_decls, tcx).ty;
226 let Ok([arg]) = <[_; 1]>::try_from(std::mem::take(args)) else {
227 span_bug!(
228 terminator.source_info.span,
229 "Wrong number of arguments for transmute intrinsic",
230 );
231 };
232
233 // Always emit the cast, even if we transmute to an uninhabited type,
234 // because that lets CTFE and codegen generate better error messages
235 // when such a transmute actually ends up reachable.
236 block.statements.push(Statement {
237 source_info: terminator.source_info,
238 kind: StatementKind::Assign(Box::new((
239 *destination,
240 Rvalue::Cast(CastKind::Transmute, arg, dst_ty),
241 ))),
242 });
243
244 if let Some(target) = *target {
245 terminator.kind = TerminatorKind::Goto { target };
246 } else {
247 terminator.kind = TerminatorKind::Unreachable;
248 }
249 }
17df50a5
XL
250 _ if intrinsic_name.as_str().starts_with("simd_shuffle") => {
251 validate_simd_shuffle(tcx, args, terminator.source_info.span);
252 }
fc512014
XL
253 _ => {}
254 }
255 }
256 }
257 }
258}
259
a2a8927a 260fn resolve_rust_intrinsic<'tcx>(
fc512014
XL
261 tcx: TyCtxt<'tcx>,
262 func_ty: Ty<'tcx>,
263) -> Option<(Symbol, SubstsRef<'tcx>)> {
264 if let ty::FnDef(def_id, substs) = *func_ty.kind() {
923072b8 265 if tcx.is_intrinsic(def_id) {
fc512014
XL
266 return Some((tcx.item_name(def_id), substs));
267 }
268 }
269 None
270}
17df50a5 271
a2a8927a 272fn validate_simd_shuffle<'tcx>(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) {
17df50a5
XL
273 match &args[2] {
274 Operand::Constant(_) => {} // all good
275 _ => {
94222f64
XL
276 let msg = "last argument of `simd_shuffle` is required to be a `const` item";
277 tcx.sess.span_err(span, msg);
17df50a5
XL
278 }
279 }
280}