1 //! Lowers intrinsic calls
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};
9 use rustc_target::spec::abi::Abi;
11 pub struct LowerIntrinsics;
13 impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
14 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
15 let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
16 for block in basic_blocks {
17 let terminator = block.terminator.as_mut().unwrap();
18 if let TerminatorKind::Call { func, args, destination, .. } = &mut terminator.kind {
19 let func_ty = func.ty(local_decls, tcx);
20 let Some((intrinsic_name, substs)) = resolve_rust_intrinsic(tcx, func_ty) else {
23 match intrinsic_name {
25 terminator.kind = TerminatorKind::Unreachable;
28 if let Some((destination, target)) = *destination {
29 block.statements.push(Statement {
30 source_info: terminator.source_info,
31 kind: StatementKind::Assign(Box::new((
33 Rvalue::Use(Operand::Constant(Box::new(Constant {
34 span: terminator.source_info.span,
36 literal: ty::Const::zero_sized(tcx, tcx.types.unit).into(),
40 terminator.kind = TerminatorKind::Goto { target };
43 sym::copy_nonoverlapping => {
44 let target = destination.unwrap().1;
45 let mut args = args.drain(..);
46 block.statements.push(Statement {
47 source_info: terminator.source_info,
48 kind: StatementKind::CopyNonOverlapping(Box::new(
49 rustc_middle::mir::CopyNonOverlapping {
50 src: args.next().unwrap(),
51 dst: args.next().unwrap(),
52 count: args.next().unwrap(),
59 "Extra argument for copy_non_overlapping intrinsic"
62 terminator.kind = TerminatorKind::Goto { target };
64 sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => {
65 if let Some((destination, target)) = *destination {
69 let mut args = args.drain(..);
70 lhs = args.next().unwrap();
71 rhs = args.next().unwrap();
73 let bin_op = match intrinsic_name {
74 sym::wrapping_add => BinOp::Add,
75 sym::wrapping_sub => BinOp::Sub,
76 sym::wrapping_mul => BinOp::Mul,
77 _ => bug!("unexpected intrinsic"),
79 block.statements.push(Statement {
80 source_info: terminator.source_info,
81 kind: StatementKind::Assign(Box::new((
83 Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))),
86 terminator.kind = TerminatorKind::Goto { target };
89 sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
90 // The checked binary operations are not suitable target for lowering here,
91 // since their semantics depend on the value of overflow-checks flag used
92 // during codegen. Issue #35310.
94 sym::size_of | sym::min_align_of => {
95 if let Some((destination, target)) = *destination {
96 let tp_ty = substs.type_at(0);
97 let null_op = match intrinsic_name {
98 sym::size_of => NullOp::SizeOf,
99 sym::min_align_of => NullOp::AlignOf,
100 _ => bug!("unexpected intrinsic"),
102 block.statements.push(Statement {
103 source_info: terminator.source_info,
104 kind: StatementKind::Assign(Box::new((
106 Rvalue::NullaryOp(null_op, tp_ty),
109 terminator.kind = TerminatorKind::Goto { target };
112 sym::discriminant_value => {
113 if let (Some((destination, target)), Some(arg)) =
114 (*destination, args[0].place())
116 let arg = tcx.mk_place_deref(arg);
117 block.statements.push(Statement {
118 source_info: terminator.source_info,
119 kind: StatementKind::Assign(Box::new((
121 Rvalue::Discriminant(arg),
124 terminator.kind = TerminatorKind::Goto { target };
127 _ if intrinsic_name.as_str().starts_with("simd_shuffle") => {
128 validate_simd_shuffle(tcx, args, terminator.source_info.span);
137 fn resolve_rust_intrinsic<'tcx>(
140 ) -> Option<(Symbol, SubstsRef<'tcx>)> {
141 if let ty::FnDef(def_id, substs) = *func_ty.kind() {
142 let fn_sig = func_ty.fn_sig(tcx);
143 if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() {
144 return Some((tcx.item_name(def_id), substs));
150 fn validate_simd_shuffle<'tcx>(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) {
152 Operand::Constant(_) => {} // all good
154 let msg = "last argument of `simd_shuffle` is required to be a `const` item";
155 tcx.sess.span_err(span, msg);