| 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<RValue<'gcc>, ()> { |
| // 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); |
| } |