1 //! Lowers intrinsic calls
3 use crate::transform::MirPass;
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_target::spec::abi::Abi;
10 pub struct LowerIntrinsics;
12 impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
13 fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
14 let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
15 for block in basic_blocks {
16 let terminator = block.terminator.as_mut().unwrap();
17 if let TerminatorKind::Call { func, args, destination, .. } = &mut terminator.kind {
18 let func_ty = func.ty(local_decls, tcx);
19 let (intrinsic_name, substs) = match resolve_rust_intrinsic(tcx, func_ty) {
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 (
33 Rvalue::Use(Operand::Constant(box Constant {
34 span: terminator.source_info.span,
36 literal: ty::Const::zero_sized(tcx, tcx.types.unit),
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(
49 box 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 (
83 Rvalue::BinaryOp(bin_op, box (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.
95 if let Some((destination, target)) = *destination {
96 let tp_ty = substs.type_at(0);
97 block.statements.push(Statement {
98 source_info: terminator.source_info,
99 kind: StatementKind::Assign(box (
101 Rvalue::NullaryOp(NullOp::SizeOf, tp_ty),
104 terminator.kind = TerminatorKind::Goto { target };
107 sym::discriminant_value => {
108 if let (Some((destination, target)), Some(arg)) =
109 (*destination, args[0].place())
111 let arg = tcx.mk_place_deref(arg);
112 block.statements.push(Statement {
113 source_info: terminator.source_info,
114 kind: StatementKind::Assign(box (
116 Rvalue::Discriminant(arg),
119 terminator.kind = TerminatorKind::Goto { target };
129 fn resolve_rust_intrinsic(
132 ) -> Option<(Symbol, SubstsRef<'tcx>)> {
133 if let ty::FnDef(def_id, substs) = *func_ty.kind() {
134 let fn_sig = func_ty.fn_sig(tcx);
135 if fn_sig.abi() == Abi::RustIntrinsic {
136 return Some((tcx.item_name(def_id), substs));