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 (intrinsic_name, substs) = match resolve_rust_intrinsic(tcx, func_ty) {
24 match intrinsic_name {
26 terminator.kind = TerminatorKind::Unreachable;
29 if let Some((destination, target)) = *destination {
30 block.statements.push(Statement {
31 source_info: terminator.source_info,
32 kind: StatementKind::Assign(Box::new((
34 Rvalue::Use(Operand::Constant(Box::new(Constant {
35 span: terminator.source_info.span,
37 literal: ty::Const::zero_sized(tcx, tcx.types.unit).into(),
41 terminator.kind = TerminatorKind::Goto { target };
44 sym::copy_nonoverlapping => {
45 let target = destination.unwrap().1;
46 let mut args = args.drain(..);
47 block.statements.push(Statement {
48 source_info: terminator.source_info,
49 kind: StatementKind::CopyNonOverlapping(Box::new(
50 rustc_middle::mir::CopyNonOverlapping {
51 src: args.next().unwrap(),
52 dst: args.next().unwrap(),
53 count: args.next().unwrap(),
60 "Extra argument for copy_non_overlapping intrinsic"
63 terminator.kind = TerminatorKind::Goto { target };
65 sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => {
66 if let Some((destination, target)) = *destination {
70 let mut args = args.drain(..);
71 lhs = args.next().unwrap();
72 rhs = args.next().unwrap();
74 let bin_op = match intrinsic_name {
75 sym::wrapping_add => BinOp::Add,
76 sym::wrapping_sub => BinOp::Sub,
77 sym::wrapping_mul => BinOp::Mul,
78 _ => bug!("unexpected intrinsic"),
80 block.statements.push(Statement {
81 source_info: terminator.source_info,
82 kind: StatementKind::Assign(Box::new((
84 Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))),
87 terminator.kind = TerminatorKind::Goto { target };
90 sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
91 // The checked binary operations are not suitable target for lowering here,
92 // since their semantics depend on the value of overflow-checks flag used
93 // during codegen. Issue #35310.
95 sym::size_of | sym::min_align_of => {
96 if let Some((destination, target)) = *destination {
97 let tp_ty = substs.type_at(0);
98 let null_op = match intrinsic_name {
99 sym::size_of => NullOp::SizeOf,
100 sym::min_align_of => NullOp::AlignOf,
101 _ => bug!("unexpected intrinsic"),
103 block.statements.push(Statement {
104 source_info: terminator.source_info,
105 kind: StatementKind::Assign(Box::new((
107 Rvalue::NullaryOp(null_op, tp_ty),
110 terminator.kind = TerminatorKind::Goto { target };
113 sym::discriminant_value => {
114 if let (Some((destination, target)), Some(arg)) =
115 (*destination, args[0].place())
117 let arg = tcx.mk_place_deref(arg);
118 block.statements.push(Statement {
119 source_info: terminator.source_info,
120 kind: StatementKind::Assign(Box::new((
122 Rvalue::Discriminant(arg),
125 terminator.kind = TerminatorKind::Goto { target };
128 _ if intrinsic_name.as_str().starts_with("simd_shuffle") => {
129 validate_simd_shuffle(tcx, args, terminator.source_info.span);
138 fn resolve_rust_intrinsic(
141 ) -> Option<(Symbol, SubstsRef<'tcx>)> {
142 if let ty::FnDef(def_id, substs) = *func_ty.kind() {
143 let fn_sig = func_ty.fn_sig(tcx);
144 if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() {
145 return Some((tcx.item_name(def_id), substs));
151 fn validate_simd_shuffle(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) {
153 Operand::Constant(_) => {} // all good
155 let msg = "last argument of `simd_shuffle` is required to be a `const` item";
156 tcx.sess.span_err(span, msg);