167 lines
5.3 KiB
Rust
167 lines
5.3 KiB
Rust
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);
|
|
}
|