use gccjit::{RValue, Type}; use rustc_codegen_ssa::base::compare_simd_types; use rustc_codegen_ssa::common::{TypeKind, span_invalid_monomorphization_error}; use rustc_codegen_ssa::mir::operand::OperandRef; use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods}; use rustc_hir as hir; use rustc_middle::span_bug; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, Symbol, sym}; use crate::builder::Builder; pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, name: Symbol, callee_ty: Ty<'tcx>, args: &[OperandRef<'tcx, RValue<'gcc>>], ret_ty: Ty<'tcx>, llret_ty: Type<'gcc>, span: Span) -> Result, ()> { // macros for error handling: macro_rules! emit_error { ($msg: tt) => { emit_error!($msg, ) }; ($msg: tt, $($fmt: tt)*) => { span_invalid_monomorphization_error( bx.sess(), span, &format!(concat!("invalid monomorphization of `{}` intrinsic: ", $msg), name, $($fmt)*)); } } macro_rules! return_error { ($($fmt: tt)*) => { { emit_error!($($fmt)*); return Err(()); } } } macro_rules! require { ($cond: expr, $($fmt: tt)*) => { if !$cond { return_error!($($fmt)*); } }; } macro_rules! require_simd { ($ty: expr, $position: expr) => { require!($ty.is_simd(), "expected SIMD {} type, found non-SIMD `{}`", $position, $ty) }; } let tcx = bx.tcx(); let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), callee_ty.fn_sig(tcx)); let arg_tys = sig.inputs(); let name_str = &*name.as_str(); // every intrinsic below takes a SIMD vector as its first argument require_simd!(arg_tys[0], "input"); let in_ty = arg_tys[0]; let comparison = match name { sym::simd_eq => Some(hir::BinOpKind::Eq), sym::simd_ne => Some(hir::BinOpKind::Ne), sym::simd_lt => Some(hir::BinOpKind::Lt), sym::simd_le => Some(hir::BinOpKind::Le), sym::simd_gt => Some(hir::BinOpKind::Gt), sym::simd_ge => Some(hir::BinOpKind::Ge), _ => None, }; let (in_len, in_elem) = arg_tys[0].simd_size_and_type(bx.tcx()); if let Some(cmp_op) = comparison { require_simd!(ret_ty, "return"); let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx()); require!( in_len == out_len, "expected return type with length {} (same as input type `{}`), \ found `{}` with length {}", in_len, in_ty, ret_ty, out_len ); require!( bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer, "expected return type with integer elements, found `{}` with non-integer `{}`", ret_ty, out_ty ); return Ok(compare_simd_types( bx, args[0].immediate(), args[1].immediate(), in_elem, llret_ty, cmp_op, )); } if let Some(stripped) = name_str.strip_prefix("simd_shuffle") { let n: u64 = stripped.parse().unwrap_or_else(|_| { span_bug!(span, "bad `simd_shuffle` instruction only caught in codegen?") }); require_simd!(ret_ty, "return"); let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx()); require!( out_len == n, "expected return type of length {}, found `{}` with length {}", n, ret_ty, out_len ); require!( in_elem == out_ty, "expected return element type `{}` (element of input `{}`), \ found `{}` with element type `{}`", in_elem, in_ty, ret_ty, out_ty ); let vector = args[2].immediate(); return Ok(bx.shuffle_vector( args[0].immediate(), args[1].immediate(), vector, )); } macro_rules! arith_binary { ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { $(if name == sym::$name { match in_elem.kind() { $($(ty::$p(_))|* => { return Ok(bx.$call(args[0].immediate(), args[1].immediate())) })* _ => {}, } require!(false, "unsupported operation on `{}` with element `{}`", in_ty, in_elem) })* } } arith_binary! { simd_add: Uint, Int => add, Float => fadd; simd_sub: Uint, Int => sub, Float => fsub; simd_mul: Uint, Int => mul, Float => fmul; simd_div: Uint => udiv, Int => sdiv, Float => fdiv; simd_rem: Uint => urem, Int => srem, Float => frem; simd_shl: Uint, Int => shl; simd_shr: Uint => lshr, Int => ashr; simd_and: Uint, Int => and; simd_or: Uint, Int => or; // FIXME(antoyo): calling `or` might not work on vectors. simd_xor: Uint, Int => xor; } unimplemented!("simd {}", name); }