Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 1 | //! Lowers intrinsic calls |
| 2 | |
Chris Wailes | cd1aefd | 2023-07-13 13:36:21 -0700 | [diff] [blame^] | 3 | use crate::{errors, MirPass}; |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 4 | use rustc_middle::mir::*; |
| 5 | use rustc_middle::ty::subst::SubstsRef; |
| 6 | use rustc_middle::ty::{self, Ty, TyCtxt}; |
| 7 | use rustc_span::symbol::{sym, Symbol}; |
| 8 | use rustc_span::Span; |
Charisee | 635618d | 2023-06-01 20:46:00 +0000 | [diff] [blame] | 9 | use rustc_target::abi::{FieldIdx, VariantIdx}; |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 10 | |
| 11 | pub struct LowerIntrinsics; |
| 12 | |
| 13 | impl<'tcx> MirPass<'tcx> for LowerIntrinsics { |
| 14 | fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |
Charisee | b1d3280 | 2022-09-22 15:38:41 +0000 | [diff] [blame] | 15 | let local_decls = &body.local_decls; |
| 16 | for block in body.basic_blocks.as_mut() { |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 17 | let terminator = block.terminator.as_mut().unwrap(); |
Chris Wailes | 6572058 | 2022-08-11 09:53:28 -0700 | [diff] [blame] | 18 | if let TerminatorKind::Call { func, args, destination, target, .. } = |
| 19 | &mut terminator.kind |
| 20 | { |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 21 | let func_ty = func.ty(local_decls, tcx); |
Charisee | 341341c | 2022-05-20 05:14:50 +0000 | [diff] [blame] | 22 | let Some((intrinsic_name, substs)) = resolve_rust_intrinsic(tcx, func_ty) else { |
| 23 | continue; |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 24 | }; |
| 25 | match intrinsic_name { |
| 26 | sym::unreachable => { |
| 27 | terminator.kind = TerminatorKind::Unreachable; |
| 28 | } |
| 29 | sym::forget => { |
Chris Wailes | 6572058 | 2022-08-11 09:53:28 -0700 | [diff] [blame] | 30 | if let Some(target) = *target { |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 31 | block.statements.push(Statement { |
| 32 | source_info: terminator.source_info, |
| 33 | kind: StatementKind::Assign(Box::new(( |
Chris Wailes | 6572058 | 2022-08-11 09:53:28 -0700 | [diff] [blame] | 34 | *destination, |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 35 | Rvalue::Use(Operand::Constant(Box::new(Constant { |
| 36 | span: terminator.source_info.span, |
| 37 | user_ty: None, |
Chris Wailes | 6572058 | 2022-08-11 09:53:28 -0700 | [diff] [blame] | 38 | literal: ConstantKind::zero_sized(tcx.types.unit), |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 39 | }))), |
| 40 | ))), |
| 41 | }); |
| 42 | terminator.kind = TerminatorKind::Goto { target }; |
| 43 | } |
| 44 | } |
| 45 | sym::copy_nonoverlapping => { |
Chris Wailes | 6572058 | 2022-08-11 09:53:28 -0700 | [diff] [blame] | 46 | let target = target.unwrap(); |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 47 | let mut args = args.drain(..); |
| 48 | block.statements.push(Statement { |
| 49 | source_info: terminator.source_info, |
Chris Wailes | 2f380c1 | 2022-11-09 13:04:22 -0800 | [diff] [blame] | 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()), |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 75 | )), |
| 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 | } |
| 85 | sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => { |
Chris Wailes | 6572058 | 2022-08-11 09:53:28 -0700 | [diff] [blame] | 86 | if let Some(target) = *target { |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 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, |
| 102 | kind: StatementKind::Assign(Box::new(( |
Chris Wailes | 6572058 | 2022-08-11 09:53:28 -0700 | [diff] [blame] | 103 | *destination, |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 104 | Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))), |
| 105 | ))), |
| 106 | }); |
| 107 | terminator.kind = TerminatorKind::Goto { target }; |
| 108 | } |
| 109 | } |
| 110 | sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { |
Chris Wailes | 5c0824a | 2023-04-24 16:30:59 -0700 | [diff] [blame] | 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 | } |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 134 | } |
| 135 | sym::size_of | sym::min_align_of => { |
Chris Wailes | 6572058 | 2022-08-11 09:53:28 -0700 | [diff] [blame] | 136 | if let Some(target) = *target { |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 137 | let tp_ty = substs.type_at(0); |
| 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 | }; |
| 143 | block.statements.push(Statement { |
| 144 | source_info: terminator.source_info, |
| 145 | kind: StatementKind::Assign(Box::new(( |
Chris Wailes | 6572058 | 2022-08-11 09:53:28 -0700 | [diff] [blame] | 146 | *destination, |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 147 | Rvalue::NullaryOp(null_op, tp_ty), |
| 148 | ))), |
| 149 | }); |
| 150 | terminator.kind = TerminatorKind::Goto { target }; |
| 151 | } |
| 152 | } |
Charisee | 635618d | 2023-06-01 20:46:00 +0000 | [diff] [blame] | 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 | } |
Chris Wailes | cd1aefd | 2023-07-13 13:36:21 -0700 | [diff] [blame^] | 182 | sym::write_via_move => { |
| 183 | let target = target.unwrap(); |
| 184 | let Ok([ptr, val]) = <[_; 2]>::try_from(std::mem::take(args)) else { |
| 185 | span_bug!( |
| 186 | terminator.source_info.span, |
| 187 | "Wrong number of arguments for write_via_move intrinsic", |
| 188 | ); |
| 189 | }; |
| 190 | let derefed_place = |
| 191 | if let Some(place) = ptr.place() && let Some(local) = place.as_local() { |
| 192 | tcx.mk_place_deref(local.into()) |
| 193 | } else { |
| 194 | span_bug!(terminator.source_info.span, "Only passing a local is supported"); |
| 195 | }; |
| 196 | block.statements.push(Statement { |
| 197 | source_info: terminator.source_info, |
| 198 | kind: StatementKind::Assign(Box::new(( |
| 199 | derefed_place, |
| 200 | Rvalue::Use(val), |
| 201 | ))), |
| 202 | }); |
| 203 | terminator.kind = TerminatorKind::Goto { target }; |
| 204 | } |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 205 | sym::discriminant_value => { |
Chris Wailes | 6572058 | 2022-08-11 09:53:28 -0700 | [diff] [blame] | 206 | if let (Some(target), Some(arg)) = (*target, args[0].place()) { |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 207 | let arg = tcx.mk_place_deref(arg); |
| 208 | block.statements.push(Statement { |
| 209 | source_info: terminator.source_info, |
| 210 | kind: StatementKind::Assign(Box::new(( |
Chris Wailes | 6572058 | 2022-08-11 09:53:28 -0700 | [diff] [blame] | 211 | *destination, |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 212 | Rvalue::Discriminant(arg), |
| 213 | ))), |
| 214 | }); |
| 215 | terminator.kind = TerminatorKind::Goto { target }; |
| 216 | } |
| 217 | } |
Chris Wailes | cd1aefd | 2023-07-13 13:36:21 -0700 | [diff] [blame^] | 218 | sym::offset => { |
| 219 | let target = target.unwrap(); |
| 220 | let Ok([ptr, delta]) = <[_; 2]>::try_from(std::mem::take(args)) else { |
| 221 | span_bug!( |
| 222 | terminator.source_info.span, |
| 223 | "Wrong number of arguments for offset intrinsic", |
| 224 | ); |
| 225 | }; |
| 226 | block.statements.push(Statement { |
| 227 | source_info: terminator.source_info, |
| 228 | kind: StatementKind::Assign(Box::new(( |
| 229 | *destination, |
| 230 | Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr, delta))), |
| 231 | ))), |
| 232 | }); |
| 233 | terminator.kind = TerminatorKind::Goto { target }; |
| 234 | } |
Charisee | 635618d | 2023-06-01 20:46:00 +0000 | [diff] [blame] | 235 | sym::option_payload_ptr => { |
| 236 | if let (Some(target), Some(arg)) = (*target, args[0].place()) { |
| 237 | let ty::RawPtr(ty::TypeAndMut { ty: dest_ty, .. }) = |
| 238 | destination.ty(local_decls, tcx).ty.kind() |
| 239 | else { bug!(); }; |
| 240 | |
| 241 | block.statements.push(Statement { |
| 242 | source_info: terminator.source_info, |
| 243 | kind: StatementKind::Assign(Box::new(( |
| 244 | *destination, |
| 245 | Rvalue::AddressOf( |
| 246 | Mutability::Not, |
| 247 | arg.project_deeper( |
| 248 | &[ |
| 249 | PlaceElem::Deref, |
| 250 | PlaceElem::Downcast( |
| 251 | Some(sym::Some), |
| 252 | VariantIdx::from_u32(1), |
| 253 | ), |
| 254 | PlaceElem::Field(FieldIdx::from_u32(0), *dest_ty), |
| 255 | ], |
| 256 | tcx, |
| 257 | ), |
| 258 | ), |
| 259 | ))), |
| 260 | }); |
| 261 | terminator.kind = TerminatorKind::Goto { target }; |
| 262 | } |
| 263 | } |
Chris Wailes | cd1aefd | 2023-07-13 13:36:21 -0700 | [diff] [blame^] | 264 | sym::transmute | sym::transmute_unchecked => { |
Charisee | 635618d | 2023-06-01 20:46:00 +0000 | [diff] [blame] | 265 | let dst_ty = destination.ty(local_decls, tcx).ty; |
| 266 | let Ok([arg]) = <[_; 1]>::try_from(std::mem::take(args)) else { |
| 267 | span_bug!( |
| 268 | terminator.source_info.span, |
| 269 | "Wrong number of arguments for transmute intrinsic", |
| 270 | ); |
| 271 | }; |
| 272 | |
| 273 | // Always emit the cast, even if we transmute to an uninhabited type, |
| 274 | // because that lets CTFE and codegen generate better error messages |
| 275 | // when such a transmute actually ends up reachable. |
| 276 | block.statements.push(Statement { |
| 277 | source_info: terminator.source_info, |
| 278 | kind: StatementKind::Assign(Box::new(( |
| 279 | *destination, |
| 280 | Rvalue::Cast(CastKind::Transmute, arg, dst_ty), |
| 281 | ))), |
| 282 | }); |
| 283 | |
| 284 | if let Some(target) = *target { |
| 285 | terminator.kind = TerminatorKind::Goto { target }; |
| 286 | } else { |
| 287 | terminator.kind = TerminatorKind::Unreachable; |
| 288 | } |
| 289 | } |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 290 | _ if intrinsic_name.as_str().starts_with("simd_shuffle") => { |
| 291 | validate_simd_shuffle(tcx, args, terminator.source_info.span); |
| 292 | } |
| 293 | _ => {} |
| 294 | } |
| 295 | } |
| 296 | } |
| 297 | } |
| 298 | } |
| 299 | |
Charisee | 7878d54 | 2022-02-24 18:21:36 +0000 | [diff] [blame] | 300 | fn resolve_rust_intrinsic<'tcx>( |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 301 | tcx: TyCtxt<'tcx>, |
| 302 | func_ty: Ty<'tcx>, |
| 303 | ) -> Option<(Symbol, SubstsRef<'tcx>)> { |
| 304 | if let ty::FnDef(def_id, substs) = *func_ty.kind() { |
Chris Wailes | 6572058 | 2022-08-11 09:53:28 -0700 | [diff] [blame] | 305 | if tcx.is_intrinsic(def_id) { |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 306 | return Some((tcx.item_name(def_id), substs)); |
| 307 | } |
| 308 | } |
| 309 | None |
| 310 | } |
| 311 | |
Charisee | 7878d54 | 2022-02-24 18:21:36 +0000 | [diff] [blame] | 312 | fn validate_simd_shuffle<'tcx>(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) { |
Chris Wailes | cd1aefd | 2023-07-13 13:36:21 -0700 | [diff] [blame^] | 313 | if !matches!(args[2], Operand::Constant(_)) { |
| 314 | tcx.sess.emit_err(errors::SimdShuffleLastConst { span }); |
Chris Wailes | a153842 | 2021-12-02 10:37:12 -0800 | [diff] [blame] | 315 | } |
| 316 | } |