rust/compiler/rustc_mir_transform/src/lower_intrinsics.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

159 lines
7.4 KiB
Rust
Raw Normal View History

//! Lowers intrinsic calls
use crate::MirPass;
use rustc_middle::mir::*;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
pub struct LowerIntrinsics;
impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
for block in basic_blocks {
let terminator = block.terminator.as_mut().unwrap();
if let TerminatorKind::Call { func, args, destination, .. } = &mut terminator.kind {
let func_ty = func.ty(local_decls, tcx);
2022-02-18 17:48:49 -06:00
let Some((intrinsic_name, substs)) = resolve_rust_intrinsic(tcx, func_ty) else {
continue;
};
match intrinsic_name {
sym::unreachable => {
terminator.kind = TerminatorKind::Unreachable;
}
sym::forget => {
if let Some((destination, target)) = *destination {
block.statements.push(Statement {
source_info: terminator.source_info,
2021-08-04 22:36:38 -05:00
kind: StatementKind::Assign(Box::new((
destination,
2021-08-04 22:36:38 -05:00
Rvalue::Use(Operand::Constant(Box::new(Constant {
span: terminator.source_info.span,
user_ty: None,
literal: ty::Const::zero_sized(tcx, tcx.types.unit).into(),
2021-08-04 22:36:38 -05:00
}))),
))),
});
terminator.kind = TerminatorKind::Goto { target };
}
}
sym::copy_nonoverlapping => {
let target = destination.unwrap().1;
let mut args = args.drain(..);
block.statements.push(Statement {
source_info: terminator.source_info,
2021-08-04 22:36:38 -05:00
kind: StatementKind::CopyNonOverlapping(Box::new(
rustc_middle::mir::CopyNonOverlapping {
src: args.next().unwrap(),
dst: args.next().unwrap(),
count: args.next().unwrap(),
},
2021-08-04 22:36:38 -05:00
)),
});
assert_eq!(
args.next(),
None,
"Extra argument for copy_non_overlapping intrinsic"
);
drop(args);
terminator.kind = TerminatorKind::Goto { target };
}
sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => {
if let Some((destination, target)) = *destination {
let lhs;
let rhs;
{
let mut args = args.drain(..);
lhs = args.next().unwrap();
rhs = args.next().unwrap();
}
let bin_op = match intrinsic_name {
sym::wrapping_add => BinOp::Add,
sym::wrapping_sub => BinOp::Sub,
sym::wrapping_mul => BinOp::Mul,
_ => bug!("unexpected intrinsic"),
};
block.statements.push(Statement {
source_info: terminator.source_info,
2021-08-04 22:36:38 -05:00
kind: StatementKind::Assign(Box::new((
destination,
2021-08-04 22:36:38 -05:00
Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))),
))),
});
terminator.kind = TerminatorKind::Goto { target };
}
}
sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
// The checked binary operations are not suitable target for lowering here,
// since their semantics depend on the value of overflow-checks flag used
// during codegen. Issue #35310.
}
2021-09-07 10:06:07 -05:00
sym::size_of | sym::min_align_of => {
if let Some((destination, target)) = *destination {
let tp_ty = substs.type_at(0);
2021-09-07 10:06:07 -05:00
let null_op = match intrinsic_name {
sym::size_of => NullOp::SizeOf,
sym::min_align_of => NullOp::AlignOf,
_ => bug!("unexpected intrinsic"),
};
block.statements.push(Statement {
source_info: terminator.source_info,
2021-08-04 22:36:38 -05:00
kind: StatementKind::Assign(Box::new((
destination,
2021-09-07 10:06:07 -05:00
Rvalue::NullaryOp(null_op, tp_ty),
2021-08-04 22:36:38 -05:00
))),
});
terminator.kind = TerminatorKind::Goto { target };
}
}
sym::discriminant_value => {
if let (Some((destination, target)), Some(arg)) =
(*destination, args[0].place())
{
let arg = tcx.mk_place_deref(arg);
block.statements.push(Statement {
source_info: terminator.source_info,
2021-08-04 22:36:38 -05:00
kind: StatementKind::Assign(Box::new((
destination,
Rvalue::Discriminant(arg),
2021-08-04 22:36:38 -05:00
))),
});
terminator.kind = TerminatorKind::Goto { target };
}
}
_ if intrinsic_name.as_str().starts_with("simd_shuffle") => {
validate_simd_shuffle(tcx, args, terminator.source_info.span);
}
_ => {}
}
}
}
}
}
fn resolve_rust_intrinsic<'tcx>(
tcx: TyCtxt<'tcx>,
func_ty: Ty<'tcx>,
) -> Option<(Symbol, SubstsRef<'tcx>)> {
if let ty::FnDef(def_id, substs) = *func_ty.kind() {
let fn_sig = func_ty.fn_sig(tcx);
if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() {
return Some((tcx.item_name(def_id), substs));
}
}
None
}
fn validate_simd_shuffle<'tcx>(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) {
match &args[2] {
Operand::Constant(_) => {} // all good
_ => {
2021-07-21 15:43:19 -05:00
let msg = "last argument of `simd_shuffle` is required to be a `const` item";
tcx.sess.span_err(span, msg);
}
}
}