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};
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 local_decls = &body.local_decls;
15 for block in body.basic_blocks.as_mut() {
16 let terminator = block.terminator.as_mut().unwrap();
17 if let TerminatorKind::Call { func, args, destination, target, .. } =
20 let func_ty = func.ty(local_decls, tcx);
21 let Some((intrinsic_name, substs)) = resolve_rust_intrinsic(tcx, func_ty) else {
24 match intrinsic_name {
26 terminator.kind = TerminatorKind::Unreachable;
29 if let Some(target) = *target {
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: ConstantKind::zero_sized(tcx.types.unit),
41 terminator.kind = TerminatorKind::Goto { target };
44 sym::copy_nonoverlapping => {
45 let target = target.unwrap();
46 let mut args = args.drain(..);
47 block.statements.push(Statement {
48 source_info: terminator.source_info,
49 kind: StatementKind::Intrinsic(Box::new(
50 NonDivergingIntrinsic::CopyNonOverlapping(
51 rustc_middle::mir::CopyNonOverlapping {
52 src: args.next().unwrap(),
53 dst: args.next().unwrap(),
54 count: args.next().unwrap(),
62 "Extra argument for copy_non_overlapping intrinsic"
65 terminator.kind = TerminatorKind::Goto { target };
68 let target = target.unwrap();
69 let mut args = args.drain(..);
70 block.statements.push(Statement {
71 source_info: terminator.source_info,
72 kind: StatementKind::Intrinsic(Box::new(
73 NonDivergingIntrinsic::Assume(args.next().unwrap()),
79 "Extra argument for copy_non_overlapping intrinsic"
82 terminator.kind = TerminatorKind::Goto { target };
84 sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => {
85 if let Some(target) = *target {
89 let mut args = args.drain(..);
90 lhs = args.next().unwrap();
91 rhs = args.next().unwrap();
93 let bin_op = match intrinsic_name {
94 sym::wrapping_add => BinOp::Add,
95 sym::wrapping_sub => BinOp::Sub,
96 sym::wrapping_mul => BinOp::Mul,
97 _ => bug!("unexpected intrinsic"),
99 block.statements.push(Statement {
100 source_info: terminator.source_info,
101 kind: StatementKind::Assign(Box::new((
103 Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))),
106 terminator.kind = TerminatorKind::Goto { target };
109 sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
110 // The checked binary operations are not suitable target for lowering here,
111 // since their semantics depend on the value of overflow-checks flag used
112 // during codegen. Issue #35310.
114 sym::size_of | sym::min_align_of => {
115 if let Some(target) = *target {
116 let tp_ty = substs.type_at(0);
117 let null_op = match intrinsic_name {
118 sym::size_of => NullOp::SizeOf,
119 sym::min_align_of => NullOp::AlignOf,
120 _ => bug!("unexpected intrinsic"),
122 block.statements.push(Statement {
123 source_info: terminator.source_info,
124 kind: StatementKind::Assign(Box::new((
126 Rvalue::NullaryOp(null_op, tp_ty),
129 terminator.kind = TerminatorKind::Goto { target };
132 sym::discriminant_value => {
133 if let (Some(target), Some(arg)) = (*target, args[0].place()) {
134 let arg = tcx.mk_place_deref(arg);
135 block.statements.push(Statement {
136 source_info: terminator.source_info,
137 kind: StatementKind::Assign(Box::new((
139 Rvalue::Discriminant(arg),
142 terminator.kind = TerminatorKind::Goto { target };
145 _ if intrinsic_name.as_str().starts_with("simd_shuffle") => {
146 validate_simd_shuffle(tcx, args, terminator.source_info.span);
155 fn resolve_rust_intrinsic<'tcx>(
158 ) -> Option<(Symbol, SubstsRef<'tcx>)> {
159 if let ty::FnDef(def_id, substs) = *func_ty.kind() {
160 if tcx.is_intrinsic(def_id) {
161 return Some((tcx.item_name(def_id), substs));
167 fn validate_simd_shuffle<'tcx>(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) {
169 Operand::Constant(_) => {} // all good
171 let msg = "last argument of `simd_shuffle` is required to be a `const` item";
172 tcx.sess.span_err(span, msg);