|  | //! Lowers intrinsic calls | 
|  |  | 
|  | use rustc_middle::mir::*; | 
|  | use rustc_middle::ty::{self, TyCtxt}; | 
|  | use rustc_middle::{bug, span_bug}; | 
|  | use rustc_span::sym; | 
|  |  | 
|  | use crate::take_array; | 
|  |  | 
|  | pub(super) struct LowerIntrinsics; | 
|  |  | 
|  | impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { | 
|  | fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { | 
|  | let local_decls = &body.local_decls; | 
|  | for block in body.basic_blocks.as_mut() { | 
|  | let terminator = block.terminator.as_mut().unwrap(); | 
|  | if let TerminatorKind::Call { func, args, destination, target, .. } = | 
|  | &mut terminator.kind | 
|  | && let ty::FnDef(def_id, generic_args) = *func.ty(local_decls, tcx).kind() | 
|  | && let Some(intrinsic) = tcx.intrinsic(def_id) | 
|  | { | 
|  | match intrinsic.name { | 
|  | sym::unreachable => { | 
|  | terminator.kind = TerminatorKind::Unreachable; | 
|  | } | 
|  | sym::ub_checks => { | 
|  | let target = target.unwrap(); | 
|  | block.statements.push(Statement::new( | 
|  | terminator.source_info, | 
|  | StatementKind::Assign(Box::new(( | 
|  | *destination, | 
|  | Rvalue::NullaryOp(NullOp::UbChecks, tcx.types.bool), | 
|  | ))), | 
|  | )); | 
|  | terminator.kind = TerminatorKind::Goto { target }; | 
|  | } | 
|  | sym::contract_checks => { | 
|  | let target = target.unwrap(); | 
|  | block.statements.push(Statement::new( | 
|  | terminator.source_info, | 
|  | StatementKind::Assign(Box::new(( | 
|  | *destination, | 
|  | Rvalue::NullaryOp(NullOp::ContractChecks, tcx.types.bool), | 
|  | ))), | 
|  | )); | 
|  | terminator.kind = TerminatorKind::Goto { target }; | 
|  | } | 
|  | sym::forget => { | 
|  | let target = target.unwrap(); | 
|  | block.statements.push(Statement::new( | 
|  | terminator.source_info, | 
|  | StatementKind::Assign(Box::new(( | 
|  | *destination, | 
|  | Rvalue::Use(Operand::Constant(Box::new(ConstOperand { | 
|  | span: terminator.source_info.span, | 
|  | user_ty: None, | 
|  | const_: Const::zero_sized(tcx.types.unit), | 
|  | }))), | 
|  | ))), | 
|  | )); | 
|  | terminator.kind = TerminatorKind::Goto { target }; | 
|  | } | 
|  | sym::copy_nonoverlapping => { | 
|  | let target = target.unwrap(); | 
|  | let Ok([src, dst, count]) = take_array(args) else { | 
|  | bug!("Wrong arguments for copy_non_overlapping intrinsic"); | 
|  | }; | 
|  | block.statements.push(Statement::new( | 
|  | terminator.source_info, | 
|  | StatementKind::Intrinsic(Box::new( | 
|  | NonDivergingIntrinsic::CopyNonOverlapping( | 
|  | rustc_middle::mir::CopyNonOverlapping { | 
|  | src: src.node, | 
|  | dst: dst.node, | 
|  | count: count.node, | 
|  | }, | 
|  | ), | 
|  | )), | 
|  | )); | 
|  | terminator.kind = TerminatorKind::Goto { target }; | 
|  | } | 
|  | sym::assume => { | 
|  | let target = target.unwrap(); | 
|  | let Ok([arg]) = take_array(args) else { | 
|  | bug!("Wrong arguments for assume intrinsic"); | 
|  | }; | 
|  | block.statements.push(Statement::new( | 
|  | terminator.source_info, | 
|  | StatementKind::Intrinsic(Box::new(NonDivergingIntrinsic::Assume( | 
|  | arg.node, | 
|  | ))), | 
|  | )); | 
|  | terminator.kind = TerminatorKind::Goto { target }; | 
|  | } | 
|  | sym::wrapping_add | 
|  | | sym::wrapping_sub | 
|  | | sym::wrapping_mul | 
|  | | sym::three_way_compare | 
|  | | sym::unchecked_add | 
|  | | sym::unchecked_sub | 
|  | | sym::unchecked_mul | 
|  | | sym::unchecked_div | 
|  | | sym::unchecked_rem | 
|  | | sym::unchecked_shl | 
|  | | sym::unchecked_shr => { | 
|  | let target = target.unwrap(); | 
|  | let Ok([lhs, rhs]) = take_array(args) else { | 
|  | bug!("Wrong arguments for {} intrinsic", intrinsic.name); | 
|  | }; | 
|  | let bin_op = match intrinsic.name { | 
|  | sym::wrapping_add => BinOp::Add, | 
|  | sym::wrapping_sub => BinOp::Sub, | 
|  | sym::wrapping_mul => BinOp::Mul, | 
|  | sym::three_way_compare => BinOp::Cmp, | 
|  | sym::unchecked_add => BinOp::AddUnchecked, | 
|  | sym::unchecked_sub => BinOp::SubUnchecked, | 
|  | sym::unchecked_mul => BinOp::MulUnchecked, | 
|  | sym::unchecked_div => BinOp::Div, | 
|  | sym::unchecked_rem => BinOp::Rem, | 
|  | sym::unchecked_shl => BinOp::ShlUnchecked, | 
|  | sym::unchecked_shr => BinOp::ShrUnchecked, | 
|  | _ => bug!("unexpected intrinsic"), | 
|  | }; | 
|  | block.statements.push(Statement::new( | 
|  | terminator.source_info, | 
|  | StatementKind::Assign(Box::new(( | 
|  | *destination, | 
|  | Rvalue::BinaryOp(bin_op, Box::new((lhs.node, rhs.node))), | 
|  | ))), | 
|  | )); | 
|  | terminator.kind = TerminatorKind::Goto { target }; | 
|  | } | 
|  | sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { | 
|  | let target = target.unwrap(); | 
|  | let Ok([lhs, rhs]) = take_array(args) else { | 
|  | bug!("Wrong arguments for {} intrinsic", intrinsic.name); | 
|  | }; | 
|  | let bin_op = match intrinsic.name { | 
|  | sym::add_with_overflow => BinOp::AddWithOverflow, | 
|  | sym::sub_with_overflow => BinOp::SubWithOverflow, | 
|  | sym::mul_with_overflow => BinOp::MulWithOverflow, | 
|  | _ => bug!("unexpected intrinsic"), | 
|  | }; | 
|  | block.statements.push(Statement::new( | 
|  | terminator.source_info, | 
|  | StatementKind::Assign(Box::new(( | 
|  | *destination, | 
|  | Rvalue::BinaryOp(bin_op, Box::new((lhs.node, rhs.node))), | 
|  | ))), | 
|  | )); | 
|  | terminator.kind = TerminatorKind::Goto { target }; | 
|  | } | 
|  | sym::size_of | sym::align_of => { | 
|  | let target = target.unwrap(); | 
|  | let tp_ty = generic_args.type_at(0); | 
|  | let null_op = match intrinsic.name { | 
|  | sym::size_of => NullOp::SizeOf, | 
|  | sym::align_of => NullOp::AlignOf, | 
|  | _ => bug!("unexpected intrinsic"), | 
|  | }; | 
|  | block.statements.push(Statement::new( | 
|  | terminator.source_info, | 
|  | StatementKind::Assign(Box::new(( | 
|  | *destination, | 
|  | Rvalue::NullaryOp(null_op, tp_ty), | 
|  | ))), | 
|  | )); | 
|  | terminator.kind = TerminatorKind::Goto { target }; | 
|  | } | 
|  | sym::read_via_copy => { | 
|  | let Ok([arg]) = take_array(args) else { | 
|  | span_bug!(terminator.source_info.span, "Wrong number of arguments"); | 
|  | }; | 
|  | let derefed_place = if let Some(place) = arg.node.place() | 
|  | && let Some(local) = place.as_local() | 
|  | { | 
|  | tcx.mk_place_deref(local.into()) | 
|  | } else { | 
|  | span_bug!( | 
|  | terminator.source_info.span, | 
|  | "Only passing a local is supported" | 
|  | ); | 
|  | }; | 
|  | // Add new statement at the end of the block that does the read, and patch | 
|  | // up the terminator. | 
|  | block.statements.push(Statement::new( | 
|  | terminator.source_info, | 
|  | StatementKind::Assign(Box::new(( | 
|  | *destination, | 
|  | Rvalue::Use(Operand::Copy(derefed_place)), | 
|  | ))), | 
|  | )); | 
|  | terminator.kind = match *target { | 
|  | None => { | 
|  | // No target means this read something uninhabited, | 
|  | // so it must be unreachable. | 
|  | TerminatorKind::Unreachable | 
|  | } | 
|  | Some(target) => TerminatorKind::Goto { target }, | 
|  | } | 
|  | } | 
|  | sym::write_via_move => { | 
|  | let target = target.unwrap(); | 
|  | let Ok([ptr, val]) = take_array(args) else { | 
|  | span_bug!( | 
|  | terminator.source_info.span, | 
|  | "Wrong number of arguments for write_via_move intrinsic", | 
|  | ); | 
|  | }; | 
|  | let derefed_place = if let Some(place) = ptr.node.place() | 
|  | && let Some(local) = place.as_local() | 
|  | { | 
|  | tcx.mk_place_deref(local.into()) | 
|  | } else { | 
|  | span_bug!( | 
|  | terminator.source_info.span, | 
|  | "Only passing a local is supported" | 
|  | ); | 
|  | }; | 
|  | block.statements.push(Statement::new( | 
|  | terminator.source_info, | 
|  | StatementKind::Assign(Box::new((derefed_place, Rvalue::Use(val.node)))), | 
|  | )); | 
|  | terminator.kind = TerminatorKind::Goto { target }; | 
|  | } | 
|  | sym::discriminant_value => { | 
|  | let target = target.unwrap(); | 
|  | let Ok([arg]) = take_array(args) else { | 
|  | span_bug!( | 
|  | terminator.source_info.span, | 
|  | "Wrong arguments for discriminant_value intrinsic" | 
|  | ); | 
|  | }; | 
|  | let arg = arg.node.place().unwrap(); | 
|  | let arg = tcx.mk_place_deref(arg); | 
|  | block.statements.push(Statement::new( | 
|  | terminator.source_info, | 
|  | StatementKind::Assign(Box::new(( | 
|  | *destination, | 
|  | Rvalue::Discriminant(arg), | 
|  | ))), | 
|  | )); | 
|  | terminator.kind = TerminatorKind::Goto { target }; | 
|  | } | 
|  | sym::offset => { | 
|  | let target = target.unwrap(); | 
|  | let Ok([ptr, delta]) = take_array(args) else { | 
|  | span_bug!( | 
|  | terminator.source_info.span, | 
|  | "Wrong number of arguments for offset intrinsic", | 
|  | ); | 
|  | }; | 
|  | block.statements.push(Statement::new( | 
|  | terminator.source_info, | 
|  | StatementKind::Assign(Box::new(( | 
|  | *destination, | 
|  | Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr.node, delta.node))), | 
|  | ))), | 
|  | )); | 
|  | terminator.kind = TerminatorKind::Goto { target }; | 
|  | } | 
|  | sym::slice_get_unchecked => { | 
|  | let target = target.unwrap(); | 
|  | let Ok([ptrish, index]) = take_array(args) else { | 
|  | span_bug!( | 
|  | terminator.source_info.span, | 
|  | "Wrong number of arguments for {intrinsic:?}", | 
|  | ); | 
|  | }; | 
|  |  | 
|  | let place = ptrish.node.place().unwrap(); | 
|  | assert!(!place.is_indirect()); | 
|  | let updated_place = place.project_deeper( | 
|  | &[ | 
|  | ProjectionElem::Deref, | 
|  | ProjectionElem::Index( | 
|  | index.node.place().unwrap().as_local().unwrap(), | 
|  | ), | 
|  | ], | 
|  | tcx, | 
|  | ); | 
|  |  | 
|  | let ret_ty = generic_args.type_at(0); | 
|  | let rvalue = match *ret_ty.kind() { | 
|  | ty::RawPtr(_, Mutability::Not) => { | 
|  | Rvalue::RawPtr(RawPtrKind::Const, updated_place) | 
|  | } | 
|  | ty::RawPtr(_, Mutability::Mut) => { | 
|  | Rvalue::RawPtr(RawPtrKind::Mut, updated_place) | 
|  | } | 
|  | ty::Ref(region, _, Mutability::Not) => { | 
|  | Rvalue::Ref(region, BorrowKind::Shared, updated_place) | 
|  | } | 
|  | ty::Ref(region, _, Mutability::Mut) => Rvalue::Ref( | 
|  | region, | 
|  | BorrowKind::Mut { kind: MutBorrowKind::Default }, | 
|  | updated_place, | 
|  | ), | 
|  | _ => bug!("Unknown return type {ret_ty:?}"), | 
|  | }; | 
|  |  | 
|  | block.statements.push(Statement::new( | 
|  | terminator.source_info, | 
|  | StatementKind::Assign(Box::new((*destination, rvalue))), | 
|  | )); | 
|  | terminator.kind = TerminatorKind::Goto { target }; | 
|  | } | 
|  | sym::transmute | sym::transmute_unchecked => { | 
|  | let dst_ty = destination.ty(local_decls, tcx).ty; | 
|  | let Ok([arg]) = take_array(args) else { | 
|  | span_bug!( | 
|  | terminator.source_info.span, | 
|  | "Wrong number of arguments for transmute intrinsic", | 
|  | ); | 
|  | }; | 
|  |  | 
|  | // Always emit the cast, even if we transmute to an uninhabited type, | 
|  | // because that lets CTFE and codegen generate better error messages | 
|  | // when such a transmute actually ends up reachable. | 
|  | block.statements.push(Statement::new( | 
|  | terminator.source_info, | 
|  | StatementKind::Assign(Box::new(( | 
|  | *destination, | 
|  | Rvalue::Cast(CastKind::Transmute, arg.node, dst_ty), | 
|  | ))), | 
|  | )); | 
|  | if let Some(target) = *target { | 
|  | terminator.kind = TerminatorKind::Goto { target }; | 
|  | } else { | 
|  | terminator.kind = TerminatorKind::Unreachable; | 
|  | } | 
|  | } | 
|  | sym::aggregate_raw_ptr => { | 
|  | let Ok([data, meta]) = take_array(args) else { | 
|  | span_bug!( | 
|  | terminator.source_info.span, | 
|  | "Wrong number of arguments for aggregate_raw_ptr intrinsic", | 
|  | ); | 
|  | }; | 
|  | let target = target.unwrap(); | 
|  | let pointer_ty = generic_args.type_at(0); | 
|  | let kind = if let ty::RawPtr(pointee_ty, mutability) = pointer_ty.kind() { | 
|  | AggregateKind::RawPtr(*pointee_ty, *mutability) | 
|  | } else { | 
|  | span_bug!( | 
|  | terminator.source_info.span, | 
|  | "Return type of aggregate_raw_ptr intrinsic must be a raw pointer", | 
|  | ); | 
|  | }; | 
|  | let fields = [data.node, meta.node]; | 
|  | block.statements.push(Statement::new( | 
|  | terminator.source_info, | 
|  | StatementKind::Assign(Box::new(( | 
|  | *destination, | 
|  | Rvalue::Aggregate(Box::new(kind), fields.into()), | 
|  | ))), | 
|  | )); | 
|  | terminator.kind = TerminatorKind::Goto { target }; | 
|  | } | 
|  | sym::ptr_metadata => { | 
|  | let Ok([ptr]) = take_array(args) else { | 
|  | span_bug!( | 
|  | terminator.source_info.span, | 
|  | "Wrong number of arguments for ptr_metadata intrinsic", | 
|  | ); | 
|  | }; | 
|  | let target = target.unwrap(); | 
|  | block.statements.push(Statement::new( | 
|  | terminator.source_info, | 
|  | StatementKind::Assign(Box::new(( | 
|  | *destination, | 
|  | Rvalue::UnaryOp(UnOp::PtrMetadata, ptr.node), | 
|  | ))), | 
|  | )); | 
|  | terminator.kind = TerminatorKind::Goto { target }; | 
|  | } | 
|  | _ => {} | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | fn is_required(&self) -> bool { | 
|  | true | 
|  | } | 
|  | } |