2020-09-23 15:13:49 +02:00
|
|
|
//! Codegen `extern "platform-intrinsic"` intrinsics.
|
|
|
|
|
|
2022-02-23 11:49:34 +01:00
|
|
|
use rustc_middle::ty::subst::SubstsRef;
|
|
|
|
|
use rustc_span::Symbol;
|
|
|
|
|
|
2019-11-18 20:58:33 +01:00
|
|
|
use super::*;
|
2020-08-28 12:10:48 +02:00
|
|
|
use crate::prelude::*;
|
2019-11-18 20:58:33 +01:00
|
|
|
|
2022-02-23 11:49:34 +01:00
|
|
|
fn report_simd_type_validation_error(
|
|
|
|
|
fx: &mut FunctionCx<'_, '_, '_>,
|
|
|
|
|
intrinsic: Symbol,
|
|
|
|
|
span: Span,
|
|
|
|
|
ty: Ty<'_>,
|
|
|
|
|
) {
|
|
|
|
|
fx.tcx.sess.span_err(span, &format!("invalid monomorphization of `{}` intrinsic: expected SIMD input type, found non-SIMD `{}`", intrinsic, ty));
|
|
|
|
|
// Prevent verifier error
|
|
|
|
|
crate::trap::trap_unreachable(fx, "compilation should not have succeeded");
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-14 17:11:06 +01:00
|
|
|
pub(super) fn codegen_simd_intrinsic_call<'tcx>(
|
2021-03-05 19:12:59 +01:00
|
|
|
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
2022-02-23 11:49:34 +01:00
|
|
|
intrinsic: Symbol,
|
|
|
|
|
_substs: SubstsRef<'tcx>,
|
2019-11-18 20:58:33 +01:00
|
|
|
args: &[mir::Operand<'tcx>],
|
|
|
|
|
ret: CPlace<'tcx>,
|
|
|
|
|
span: Span,
|
|
|
|
|
) {
|
|
|
|
|
intrinsic_match! {
|
2022-02-23 11:49:34 +01:00
|
|
|
fx, intrinsic, args,
|
2019-11-18 20:58:33 +01:00
|
|
|
_ => {
|
2020-02-22 15:17:30 +01:00
|
|
|
fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic));
|
2019-11-18 20:58:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
simd_cast, (c a) {
|
2022-02-23 11:49:34 +01:00
|
|
|
if !a.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
simd_for_each_lane(fx, a, ret, &|fx, lane_ty, ret_lane_ty, lane| {
|
|
|
|
|
let ret_lane_clif_ty = fx.clif_type(ret_lane_ty).unwrap();
|
2019-11-18 20:58:33 +01:00
|
|
|
|
2022-02-23 11:49:34 +01:00
|
|
|
let from_signed = type_sign(lane_ty);
|
|
|
|
|
let to_signed = type_sign(ret_lane_ty);
|
2019-11-18 20:58:33 +01:00
|
|
|
|
2022-02-23 11:49:34 +01:00
|
|
|
clif_int_or_float_cast(fx, lane, from_signed, ret_lane_clif_ty, to_signed)
|
2019-11-18 21:29:34 +01:00
|
|
|
});
|
2019-11-18 20:58:33 +01:00
|
|
|
};
|
|
|
|
|
|
2022-02-23 11:49:34 +01:00
|
|
|
simd_eq | simd_ne | simd_lt | simd_le | simd_gt | simd_ge, (c x, c y) {
|
|
|
|
|
if !x.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FIXME use vector instructions when possible
|
|
|
|
|
simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, res_lane_ty, x_lane, y_lane| {
|
|
|
|
|
let res_lane = match (lane_ty.kind(), intrinsic) {
|
|
|
|
|
(ty::Uint(_), sym::simd_eq) => fx.bcx.ins().icmp(IntCC::Equal, x_lane, y_lane),
|
|
|
|
|
(ty::Uint(_), sym::simd_ne) => fx.bcx.ins().icmp(IntCC::NotEqual, x_lane, y_lane),
|
|
|
|
|
(ty::Uint(_), sym::simd_lt) => {
|
|
|
|
|
fx.bcx.ins().icmp(IntCC::UnsignedLessThan, x_lane, y_lane)
|
|
|
|
|
}
|
|
|
|
|
(ty::Uint(_), sym::simd_le) => {
|
|
|
|
|
fx.bcx.ins().icmp(IntCC::UnsignedLessThanOrEqual, x_lane, y_lane)
|
|
|
|
|
}
|
|
|
|
|
(ty::Uint(_), sym::simd_gt) => {
|
|
|
|
|
fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, x_lane, y_lane)
|
|
|
|
|
}
|
|
|
|
|
(ty::Uint(_), sym::simd_ge) => {
|
|
|
|
|
fx.bcx.ins().icmp(IntCC::UnsignedGreaterThanOrEqual, x_lane, y_lane)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(ty::Int(_), sym::simd_eq) => fx.bcx.ins().icmp(IntCC::Equal, x_lane, y_lane),
|
|
|
|
|
(ty::Int(_), sym::simd_ne) => fx.bcx.ins().icmp(IntCC::NotEqual, x_lane, y_lane),
|
|
|
|
|
(ty::Int(_), sym::simd_lt) => fx.bcx.ins().icmp(IntCC::SignedLessThan, x_lane, y_lane),
|
|
|
|
|
(ty::Int(_), sym::simd_le) => {
|
|
|
|
|
fx.bcx.ins().icmp(IntCC::SignedLessThanOrEqual, x_lane, y_lane)
|
|
|
|
|
}
|
|
|
|
|
(ty::Int(_), sym::simd_gt) => {
|
|
|
|
|
fx.bcx.ins().icmp(IntCC::SignedGreaterThan, x_lane, y_lane)
|
|
|
|
|
}
|
|
|
|
|
(ty::Int(_), sym::simd_ge) => {
|
|
|
|
|
fx.bcx.ins().icmp(IntCC::SignedGreaterThanOrEqual, x_lane, y_lane)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(ty::Float(_), sym::simd_eq) => fx.bcx.ins().fcmp(FloatCC::Equal, x_lane, y_lane),
|
|
|
|
|
(ty::Float(_), sym::simd_ne) => fx.bcx.ins().fcmp(FloatCC::NotEqual, x_lane, y_lane),
|
|
|
|
|
(ty::Float(_), sym::simd_lt) => fx.bcx.ins().fcmp(FloatCC::LessThan, x_lane, y_lane),
|
|
|
|
|
(ty::Float(_), sym::simd_le) => {
|
|
|
|
|
fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, x_lane, y_lane)
|
|
|
|
|
}
|
|
|
|
|
(ty::Float(_), sym::simd_gt) => fx.bcx.ins().fcmp(FloatCC::GreaterThan, x_lane, y_lane),
|
|
|
|
|
(ty::Float(_), sym::simd_ge) => {
|
|
|
|
|
fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, x_lane, y_lane)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_ => unreachable!(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let ty = fx.clif_type(res_lane_ty).unwrap();
|
|
|
|
|
|
|
|
|
|
let res_lane = fx.bcx.ins().bint(ty, res_lane);
|
|
|
|
|
fx.bcx.ins().ineg(res_lane)
|
|
|
|
|
});
|
2019-11-18 20:58:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// simd_shuffle32<T, U>(x: T, y: T, idx: [u32; 32]) -> U
|
2021-04-30 14:49:58 +02:00
|
|
|
_ if intrinsic.as_str().starts_with("simd_shuffle"), (c x, c y, o idx) {
|
2022-02-23 11:49:34 +01:00
|
|
|
if !x.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-07-03 16:52:37 +02:00
|
|
|
|
2021-12-20 18:56:35 +01:00
|
|
|
// If this intrinsic is the older "simd_shuffleN" form, simply parse the integer.
|
|
|
|
|
// If there is no suffix, use the index array length.
|
|
|
|
|
let n: u16 = if intrinsic == sym::simd_shuffle {
|
|
|
|
|
// Make sure this is actually an array, since typeck only checks the length-suffixed
|
|
|
|
|
// version of this intrinsic.
|
|
|
|
|
let idx_ty = fx.monomorphize(idx.ty(fx.mir, fx.tcx));
|
|
|
|
|
match idx_ty.kind() {
|
|
|
|
|
ty::Array(ty, len) if matches!(ty.kind(), ty::Uint(ty::UintTy::U32)) => {
|
|
|
|
|
len.try_eval_usize(fx.tcx, ty::ParamEnv::reveal_all()).unwrap_or_else(|| {
|
|
|
|
|
span_bug!(span, "could not evaluate shuffle index array length")
|
|
|
|
|
}).try_into().unwrap()
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
fx.tcx.sess.span_err(
|
|
|
|
|
span,
|
|
|
|
|
&format!(
|
|
|
|
|
"simd_shuffle index must be an array of `u32`, got `{}`",
|
|
|
|
|
idx_ty,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
// Prevent verifier error
|
|
|
|
|
crate::trap::trap_unreachable(fx, "compilation should not have succeeded");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
intrinsic.as_str()["simd_shuffle".len()..].parse().unwrap()
|
|
|
|
|
};
|
2019-11-18 20:58:33 +01:00
|
|
|
|
|
|
|
|
assert_eq!(x.layout(), y.layout());
|
|
|
|
|
let layout = x.layout();
|
|
|
|
|
|
2020-12-27 10:30:38 +01:00
|
|
|
let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
|
|
|
|
|
let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
|
2019-11-18 20:58:33 +01:00
|
|
|
|
2020-12-27 10:30:38 +01:00
|
|
|
assert_eq!(lane_ty, ret_lane_ty);
|
|
|
|
|
assert_eq!(u64::from(n), ret_lane_count);
|
2019-11-18 20:58:33 +01:00
|
|
|
|
|
|
|
|
let total_len = lane_count * 2;
|
|
|
|
|
|
|
|
|
|
let indexes = {
|
2020-03-31 13:20:19 +02:00
|
|
|
use rustc_middle::mir::interpret::*;
|
2019-11-18 20:58:33 +01:00
|
|
|
let idx_const = crate::constant::mir_operand_get_const_val(fx, idx).expect("simd_shuffle* idx not const");
|
|
|
|
|
|
2021-03-08 16:18:03 +00:00
|
|
|
let idx_bytes = match idx_const {
|
|
|
|
|
ConstValue::ByRef { alloc, offset } => {
|
2021-03-29 10:45:09 +02:00
|
|
|
let size = Size::from_bytes(4 * ret_lane_count /* size_of([u32; ret_lane_count]) */);
|
Introduce `ConstAllocation`.
Currently some `Allocation`s are interned, some are not, and it's very
hard to tell at a use point which is which.
This commit introduces `ConstAllocation` for the known-interned ones,
which makes the division much clearer. `ConstAllocation::inner()` is
used to get the underlying `Allocation`.
In some places it's natural to use an `Allocation`, in some it's natural
to use a `ConstAllocation`, and in some places there's no clear choice.
I've tried to make things look as nice as possible, while generally
favouring `ConstAllocation`, which is the type that embodies more
information. This does require quite a few calls to `inner()`.
The commit also tweaks how `PartialOrd` works for `Interned`. The
previous code was too clever by half, building on `T: Ord` to make the
code shorter. That caused problems with deriving `PartialOrd` and `Ord`
for `ConstAllocation`, so I changed it to build on `T: PartialOrd`,
which is slightly more verbose but much more standard and avoided the
problems.
2022-03-02 07:15:04 +11:00
|
|
|
alloc.inner().get_bytes(fx, alloc_range(offset, size)).unwrap()
|
2019-11-18 20:58:33 +01:00
|
|
|
}
|
|
|
|
|
_ => unreachable!("{:?}", idx_const),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
(0..ret_lane_count).map(|i| {
|
|
|
|
|
let i = usize::try_from(i).unwrap();
|
2020-03-31 13:20:19 +02:00
|
|
|
let idx = rustc_middle::mir::interpret::read_target_uint(
|
2019-11-18 20:58:33 +01:00
|
|
|
fx.tcx.data_layout.endian,
|
|
|
|
|
&idx_bytes[4*i.. 4*i + 4],
|
|
|
|
|
).expect("read_target_uint");
|
2020-03-27 16:42:28 +01:00
|
|
|
u16::try_from(idx).expect("try_from u32")
|
|
|
|
|
}).collect::<Vec<u16>>()
|
2019-11-18 20:58:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for &idx in &indexes {
|
2020-12-27 10:30:38 +01:00
|
|
|
assert!(u64::from(idx) < total_len, "idx {} out of range 0..{}", idx, total_len);
|
2019-11-18 20:58:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (out_idx, in_idx) in indexes.into_iter().enumerate() {
|
2020-12-27 10:30:38 +01:00
|
|
|
let in_lane = if u64::from(in_idx) < lane_count {
|
2021-08-06 16:26:56 +02:00
|
|
|
x.value_lane(fx, in_idx.into())
|
2019-11-18 20:58:33 +01:00
|
|
|
} else {
|
2021-08-06 16:26:56 +02:00
|
|
|
y.value_lane(fx, u64::from(in_idx) - lane_count)
|
2019-11-18 20:58:33 +01:00
|
|
|
};
|
2021-08-06 16:26:56 +02:00
|
|
|
let out_lane = ret.place_lane(fx, u64::try_from(out_idx).unwrap());
|
2019-11-18 20:58:33 +01:00
|
|
|
out_lane.write_cvalue(fx, in_lane);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-07-19 14:54:18 +02:00
|
|
|
simd_insert, (c base, o idx, c val) {
|
2020-07-03 16:52:37 +02:00
|
|
|
// FIXME validate
|
2020-01-04 18:20:00 +01:00
|
|
|
let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
|
|
|
|
|
idx_const
|
|
|
|
|
} else {
|
2020-10-16 14:03:29 +02:00
|
|
|
fx.tcx.sess.span_fatal(
|
|
|
|
|
span,
|
|
|
|
|
"Index argument for `simd_insert` is not a constant",
|
2020-01-04 18:20:00 +01:00
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2021-03-08 16:18:03 +00:00
|
|
|
let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
|
2020-12-27 10:30:38 +01:00
|
|
|
let (lane_count, _lane_ty) = base.layout().ty.simd_size_and_type(fx.tcx);
|
2020-01-04 18:20:00 +01:00
|
|
|
if idx >= lane_count.into() {
|
|
|
|
|
fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count));
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-19 14:54:18 +02:00
|
|
|
ret.write_cvalue(fx, base);
|
|
|
|
|
let ret_lane = ret.place_field(fx, mir::Field::new(idx.try_into().unwrap()));
|
|
|
|
|
ret_lane.write_cvalue(fx, val);
|
2020-01-04 18:20:00 +01:00
|
|
|
};
|
|
|
|
|
|
2019-11-18 20:58:33 +01:00
|
|
|
simd_extract, (c v, o idx) {
|
2022-02-23 11:49:34 +01:00
|
|
|
if !v.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-18 20:58:33 +01:00
|
|
|
let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) {
|
|
|
|
|
idx_const
|
|
|
|
|
} else {
|
2020-11-27 20:48:53 +01:00
|
|
|
fx.tcx.sess.span_warn(
|
2020-10-16 14:03:29 +02:00
|
|
|
span,
|
|
|
|
|
"Index argument for `simd_extract` is not a constant",
|
2019-11-18 20:58:33 +01:00
|
|
|
);
|
2020-11-27 20:48:53 +01:00
|
|
|
let res = crate::trap::trap_unimplemented_ret_value(
|
|
|
|
|
fx,
|
|
|
|
|
ret.layout(),
|
|
|
|
|
"Index argument for `simd_extract` is not a constant",
|
|
|
|
|
);
|
|
|
|
|
ret.write_cvalue(fx, res);
|
|
|
|
|
return;
|
2019-11-18 20:58:33 +01:00
|
|
|
};
|
|
|
|
|
|
2021-03-08 16:18:03 +00:00
|
|
|
let idx = idx_const.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const));
|
2020-12-27 10:30:38 +01:00
|
|
|
let (lane_count, _lane_ty) = v.layout().ty.simd_size_and_type(fx.tcx);
|
2019-11-18 20:58:33 +01:00
|
|
|
if idx >= lane_count.into() {
|
|
|
|
|
fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count));
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-06 16:26:56 +02:00
|
|
|
let ret_lane = v.value_lane(fx, idx.try_into().unwrap());
|
2019-11-18 20:58:33 +01:00
|
|
|
ret.write_cvalue(fx, ret_lane);
|
|
|
|
|
};
|
|
|
|
|
|
2021-08-06 16:26:56 +02:00
|
|
|
simd_neg, (c a) {
|
2022-02-23 11:49:34 +01:00
|
|
|
if !a.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
simd_for_each_lane(fx, a, ret, &|fx, lane_ty, _ret_lane_ty, lane| {
|
|
|
|
|
match lane_ty.kind() {
|
2021-08-06 16:26:56 +02:00
|
|
|
ty::Int(_) => fx.bcx.ins().ineg(lane),
|
|
|
|
|
ty::Float(_) => fx.bcx.ins().fneg(lane),
|
|
|
|
|
_ => unreachable!(),
|
2022-02-23 11:49:34 +01:00
|
|
|
}
|
2021-08-06 16:26:56 +02:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2022-02-23 11:49:34 +01:00
|
|
|
simd_add | simd_sub | simd_mul | simd_div | simd_rem
|
|
|
|
|
| simd_shl | simd_shr | simd_and | simd_or | simd_xor, (c x, c y) {
|
|
|
|
|
if !x.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-08-06 16:26:56 +02:00
|
|
|
|
2022-02-23 11:49:34 +01:00
|
|
|
// FIXME use vector instructions when possible
|
|
|
|
|
simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, _ret_lane_ty, x_lane, y_lane| match (
|
|
|
|
|
lane_ty.kind(),
|
|
|
|
|
intrinsic,
|
|
|
|
|
) {
|
|
|
|
|
(ty::Uint(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane),
|
|
|
|
|
(ty::Uint(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane),
|
|
|
|
|
(ty::Uint(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane),
|
|
|
|
|
(ty::Uint(_), sym::simd_div) => fx.bcx.ins().udiv(x_lane, y_lane),
|
|
|
|
|
(ty::Uint(_), sym::simd_rem) => fx.bcx.ins().urem(x_lane, y_lane),
|
|
|
|
|
|
|
|
|
|
(ty::Int(_), sym::simd_add) => fx.bcx.ins().iadd(x_lane, y_lane),
|
|
|
|
|
(ty::Int(_), sym::simd_sub) => fx.bcx.ins().isub(x_lane, y_lane),
|
|
|
|
|
(ty::Int(_), sym::simd_mul) => fx.bcx.ins().imul(x_lane, y_lane),
|
|
|
|
|
(ty::Int(_), sym::simd_div) => fx.bcx.ins().sdiv(x_lane, y_lane),
|
|
|
|
|
(ty::Int(_), sym::simd_rem) => fx.bcx.ins().srem(x_lane, y_lane),
|
|
|
|
|
|
|
|
|
|
(ty::Float(_), sym::simd_add) => fx.bcx.ins().fadd(x_lane, y_lane),
|
|
|
|
|
(ty::Float(_), sym::simd_sub) => fx.bcx.ins().fsub(x_lane, y_lane),
|
|
|
|
|
(ty::Float(_), sym::simd_mul) => fx.bcx.ins().fmul(x_lane, y_lane),
|
|
|
|
|
(ty::Float(_), sym::simd_div) => fx.bcx.ins().fdiv(x_lane, y_lane),
|
|
|
|
|
(ty::Float(FloatTy::F32), sym::simd_rem) => fx.lib_call(
|
|
|
|
|
"fmodf",
|
|
|
|
|
vec![AbiParam::new(types::F32), AbiParam::new(types::F32)],
|
|
|
|
|
vec![AbiParam::new(types::F32)],
|
|
|
|
|
&[x_lane, y_lane],
|
|
|
|
|
)[0],
|
|
|
|
|
(ty::Float(FloatTy::F64), sym::simd_rem) => fx.lib_call(
|
|
|
|
|
"fmod",
|
|
|
|
|
vec![AbiParam::new(types::F64), AbiParam::new(types::F64)],
|
|
|
|
|
vec![AbiParam::new(types::F64)],
|
|
|
|
|
&[x_lane, y_lane],
|
|
|
|
|
)[0],
|
|
|
|
|
|
|
|
|
|
(ty::Uint(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane),
|
|
|
|
|
(ty::Uint(_), sym::simd_shr) => fx.bcx.ins().ushr(x_lane, y_lane),
|
|
|
|
|
(ty::Uint(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane),
|
|
|
|
|
(ty::Uint(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane),
|
|
|
|
|
(ty::Uint(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane),
|
|
|
|
|
|
|
|
|
|
(ty::Int(_), sym::simd_shl) => fx.bcx.ins().ishl(x_lane, y_lane),
|
|
|
|
|
(ty::Int(_), sym::simd_shr) => fx.bcx.ins().sshr(x_lane, y_lane),
|
|
|
|
|
(ty::Int(_), sym::simd_and) => fx.bcx.ins().band(x_lane, y_lane),
|
|
|
|
|
(ty::Int(_), sym::simd_or) => fx.bcx.ins().bor(x_lane, y_lane),
|
|
|
|
|
(ty::Int(_), sym::simd_xor) => fx.bcx.ins().bxor(x_lane, y_lane),
|
|
|
|
|
|
|
|
|
|
_ => unreachable!(),
|
2021-08-06 16:26:56 +02:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2020-02-26 16:35:13 +01:00
|
|
|
simd_fma, (c a, c b, c c) {
|
2022-02-23 11:49:34 +01:00
|
|
|
if !a.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-02-26 16:35:13 +01:00
|
|
|
assert_eq!(a.layout(), b.layout());
|
|
|
|
|
assert_eq!(a.layout(), c.layout());
|
|
|
|
|
let layout = a.layout();
|
|
|
|
|
|
2020-12-27 10:30:38 +01:00
|
|
|
let (lane_count, _lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
|
|
|
|
|
let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
|
2020-02-26 16:35:13 +01:00
|
|
|
assert_eq!(lane_count, ret_lane_count);
|
2020-12-27 10:30:38 +01:00
|
|
|
let ret_lane_layout = fx.layout_of(ret_lane_ty);
|
2020-02-26 16:35:13 +01:00
|
|
|
|
|
|
|
|
for lane in 0..lane_count {
|
2021-08-06 16:26:56 +02:00
|
|
|
let a_lane = a.value_lane(fx, lane).load_scalar(fx);
|
|
|
|
|
let b_lane = b.value_lane(fx, lane).load_scalar(fx);
|
|
|
|
|
let c_lane = c.value_lane(fx, lane).load_scalar(fx);
|
2020-02-26 16:35:13 +01:00
|
|
|
|
|
|
|
|
let mul_lane = fx.bcx.ins().fmul(a_lane, b_lane);
|
|
|
|
|
let res_lane = CValue::by_val(fx.bcx.ins().fadd(mul_lane, c_lane), ret_lane_layout);
|
|
|
|
|
|
2021-08-06 16:26:56 +02:00
|
|
|
ret.place_lane(fx, lane).write_cvalue(fx, res_lane);
|
2020-02-26 16:35:13 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-02-23 11:49:34 +01:00
|
|
|
simd_fmin | simd_fmax, (c x, c y) {
|
|
|
|
|
if !x.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, x.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FIXME use vector instructions when possible
|
|
|
|
|
simd_pair_for_each_lane(fx, x, y, ret, &|fx, lane_ty, _ret_lane_ty, x_lane, y_lane| {
|
|
|
|
|
match lane_ty.kind() {
|
|
|
|
|
ty::Float(_) => {},
|
|
|
|
|
_ => unreachable!("{:?}", lane_ty),
|
|
|
|
|
}
|
|
|
|
|
match intrinsic {
|
|
|
|
|
sym::simd_fmin => fx.bcx.ins().fmin(x_lane, y_lane),
|
|
|
|
|
sym::simd_fmax => fx.bcx.ins().fmax(x_lane, y_lane),
|
|
|
|
|
_ => unreachable!(),
|
|
|
|
|
}
|
|
|
|
|
});
|
2019-11-18 20:58:33 +01:00
|
|
|
};
|
2020-07-19 14:54:18 +02:00
|
|
|
|
2021-08-06 16:26:56 +02:00
|
|
|
simd_round, (c a) {
|
2022-02-23 11:49:34 +01:00
|
|
|
if !a.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
simd_for_each_lane(fx, a, ret, &|fx, lane_ty, _ret_lane_ty, lane| {
|
|
|
|
|
match lane_ty.kind() {
|
2021-08-06 16:26:56 +02:00
|
|
|
ty::Float(FloatTy::F32) => fx.lib_call(
|
|
|
|
|
"roundf",
|
|
|
|
|
vec![AbiParam::new(types::F32)],
|
|
|
|
|
vec![AbiParam::new(types::F32)],
|
|
|
|
|
&[lane],
|
|
|
|
|
)[0],
|
|
|
|
|
ty::Float(FloatTy::F64) => fx.lib_call(
|
|
|
|
|
"round",
|
|
|
|
|
vec![AbiParam::new(types::F64)],
|
|
|
|
|
vec![AbiParam::new(types::F64)],
|
|
|
|
|
&[lane],
|
|
|
|
|
)[0],
|
2022-02-23 11:49:34 +01:00
|
|
|
_ => unreachable!("{:?}", lane_ty),
|
|
|
|
|
}
|
2021-08-06 16:26:56 +02:00
|
|
|
});
|
|
|
|
|
};
|
2022-02-23 11:49:34 +01:00
|
|
|
|
|
|
|
|
simd_fabs | simd_fsqrt | simd_ceil | simd_floor | simd_trunc, (c a) {
|
|
|
|
|
if !a.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
simd_for_each_lane(fx, a, ret, &|fx, lane_ty, _ret_lane_ty, lane| {
|
|
|
|
|
match lane_ty.kind() {
|
|
|
|
|
ty::Float(_) => {},
|
|
|
|
|
_ => unreachable!("{:?}", lane_ty),
|
|
|
|
|
}
|
|
|
|
|
match intrinsic {
|
|
|
|
|
sym::simd_fabs => fx.bcx.ins().fabs(lane),
|
|
|
|
|
sym::simd_fsqrt => fx.bcx.ins().sqrt(lane),
|
|
|
|
|
sym::simd_ceil => fx.bcx.ins().ceil(lane),
|
|
|
|
|
sym::simd_floor => fx.bcx.ins().floor(lane),
|
|
|
|
|
sym::simd_trunc => fx.bcx.ins().trunc(lane),
|
|
|
|
|
_ => unreachable!(),
|
|
|
|
|
}
|
2021-08-06 16:26:56 +02:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
simd_reduce_add_ordered | simd_reduce_add_unordered, (c v, v acc) {
|
2022-03-20 16:55:21 +01:00
|
|
|
// FIXME there must be no acc param for integer vectors
|
2022-02-23 11:49:34 +01:00
|
|
|
if !v.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
simd_reduce(fx, v, Some(acc), ret, &|fx, lane_ty, a, b| {
|
|
|
|
|
if lane_ty.is_floating_point() {
|
2020-11-27 20:48:53 +01:00
|
|
|
fx.bcx.ins().fadd(a, b)
|
|
|
|
|
} else {
|
|
|
|
|
fx.bcx.ins().iadd(a, b)
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2021-08-06 16:26:56 +02:00
|
|
|
simd_reduce_mul_ordered | simd_reduce_mul_unordered, (c v, v acc) {
|
2022-03-20 16:55:21 +01:00
|
|
|
// FIXME there must be no acc param for integer vectors
|
2022-02-23 11:49:34 +01:00
|
|
|
if !v.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
simd_reduce(fx, v, Some(acc), ret, &|fx, lane_ty, a, b| {
|
|
|
|
|
if lane_ty.is_floating_point() {
|
2020-11-27 20:48:53 +01:00
|
|
|
fx.bcx.ins().fmul(a, b)
|
|
|
|
|
} else {
|
|
|
|
|
fx.bcx.ins().imul(a, b)
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
simd_reduce_all, (c v) {
|
2022-02-23 11:49:34 +01:00
|
|
|
if !v.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
simd_reduce_bool(fx, v, ret, &|fx, a, b| fx.bcx.ins().band(a, b));
|
2020-11-27 20:48:53 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
simd_reduce_any, (c v) {
|
2022-02-23 11:49:34 +01:00
|
|
|
if !v.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
simd_reduce_bool(fx, v, ret, &|fx, a, b| fx.bcx.ins().bor(a, b));
|
2020-11-27 20:48:53 +01:00
|
|
|
};
|
|
|
|
|
|
2021-08-06 16:26:56 +02:00
|
|
|
simd_reduce_and, (c v) {
|
2022-02-23 11:49:34 +01:00
|
|
|
if !v.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
simd_reduce(fx, v, None, ret, &|fx, _ty, a, b| fx.bcx.ins().band(a, b));
|
2021-08-06 16:26:56 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
simd_reduce_or, (c v) {
|
2022-02-23 11:49:34 +01:00
|
|
|
if !v.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
simd_reduce(fx, v, None, ret, &|fx, _ty, a, b| fx.bcx.ins().bor(a, b));
|
2021-08-06 16:26:56 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
simd_reduce_xor, (c v) {
|
2022-02-23 11:49:34 +01:00
|
|
|
if !v.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
simd_reduce(fx, v, None, ret, &|fx, _ty, a, b| fx.bcx.ins().bxor(a, b));
|
2021-08-06 16:26:56 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
simd_reduce_min, (c v) {
|
2022-02-23 11:49:34 +01:00
|
|
|
if !v.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
simd_reduce(fx, v, None, ret, &|fx, ty, a, b| {
|
|
|
|
|
let lt = match ty.kind() {
|
2021-12-20 18:56:35 +01:00
|
|
|
ty::Int(_) => fx.bcx.ins().icmp(IntCC::SignedLessThan, a, b),
|
|
|
|
|
ty::Uint(_) => fx.bcx.ins().icmp(IntCC::UnsignedLessThan, a, b),
|
|
|
|
|
ty::Float(_) => fx.bcx.ins().fcmp(FloatCC::LessThan, a, b),
|
|
|
|
|
_ => unreachable!(),
|
|
|
|
|
};
|
2021-08-06 16:26:56 +02:00
|
|
|
fx.bcx.ins().select(lt, a, b)
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
simd_reduce_max, (c v) {
|
2022-02-23 11:49:34 +01:00
|
|
|
if !v.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
simd_reduce(fx, v, None, ret, &|fx, ty, a, b| {
|
|
|
|
|
let gt = match ty.kind() {
|
2021-12-20 18:56:35 +01:00
|
|
|
ty::Int(_) => fx.bcx.ins().icmp(IntCC::SignedGreaterThan, a, b),
|
|
|
|
|
ty::Uint(_) => fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, a, b),
|
|
|
|
|
ty::Float(_) => fx.bcx.ins().fcmp(FloatCC::GreaterThan, a, b),
|
|
|
|
|
_ => unreachable!(),
|
|
|
|
|
};
|
2021-08-06 16:26:56 +02:00
|
|
|
fx.bcx.ins().select(gt, a, b)
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
simd_select, (c m, c a, c b) {
|
2022-02-23 11:49:34 +01:00
|
|
|
if !m.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, m.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if !a.layout().ty.is_simd() {
|
|
|
|
|
report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-08-06 16:26:56 +02:00
|
|
|
assert_eq!(a.layout(), b.layout());
|
|
|
|
|
|
|
|
|
|
let (lane_count, lane_ty) = a.layout().ty.simd_size_and_type(fx.tcx);
|
|
|
|
|
let lane_layout = fx.layout_of(lane_ty);
|
|
|
|
|
|
|
|
|
|
for lane in 0..lane_count {
|
|
|
|
|
let m_lane = m.value_lane(fx, lane).load_scalar(fx);
|
|
|
|
|
let a_lane = a.value_lane(fx, lane).load_scalar(fx);
|
|
|
|
|
let b_lane = b.value_lane(fx, lane).load_scalar(fx);
|
|
|
|
|
|
|
|
|
|
let m_lane = fx.bcx.ins().icmp_imm(IntCC::Equal, m_lane, 0);
|
|
|
|
|
let res_lane = CValue::by_val(fx.bcx.ins().select(m_lane, b_lane, a_lane), lane_layout);
|
|
|
|
|
|
|
|
|
|
ret.place_lane(fx, lane).write_cvalue(fx, res_lane);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// simd_saturating_*
|
2020-07-19 14:54:18 +02:00
|
|
|
// simd_bitmask
|
2021-08-06 16:26:56 +02:00
|
|
|
// simd_scatter
|
|
|
|
|
// simd_gather
|
2019-11-18 20:58:33 +01:00
|
|
|
}
|
|
|
|
|
}
|