mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
193 lines
7.9 KiB
Rust
193 lines
7.9 KiB
Rust
//! Shim implementation for simd intrinsics
|
|
|
|
use std::cmp::Ordering;
|
|
|
|
use crate::consteval::try_const_usize;
|
|
use crate::TyKind;
|
|
|
|
use super::*;
|
|
|
|
macro_rules! from_bytes {
|
|
($ty:tt, $value:expr) => {
|
|
($ty::from_le_bytes(match ($value).try_into() {
|
|
Ok(it) => it,
|
|
Err(_) => return Err(MirEvalError::InternalError("mismatched size".into())),
|
|
}))
|
|
};
|
|
}
|
|
|
|
macro_rules! not_supported {
|
|
($it: expr) => {
|
|
return Err(MirEvalError::NotSupported(format!($it)))
|
|
};
|
|
}
|
|
|
|
impl Evaluator<'_> {
|
|
fn detect_simd_ty(&self, ty: &Ty) -> Result<(usize, Ty)> {
|
|
match ty.kind(Interner) {
|
|
TyKind::Adt(id, subst) => {
|
|
let len = match subst.as_slice(Interner).get(1).and_then(|it| it.constant(Interner))
|
|
{
|
|
Some(len) => len,
|
|
_ => {
|
|
if let AdtId::StructId(id) = id.0 {
|
|
let struct_data = self.db.struct_data(id);
|
|
let fields = struct_data.variant_data.fields();
|
|
let Some((first_field, _)) = fields.iter().next() else {
|
|
not_supported!("simd type with no field");
|
|
};
|
|
let field_ty = self.db.field_types(id.into())[first_field]
|
|
.clone()
|
|
.substitute(Interner, subst);
|
|
return Ok((fields.len(), field_ty));
|
|
}
|
|
return Err(MirEvalError::InternalError(
|
|
"simd type with no len param".into(),
|
|
));
|
|
}
|
|
};
|
|
match try_const_usize(self.db, len) {
|
|
Some(len) => {
|
|
let Some(ty) =
|
|
subst.as_slice(Interner).first().and_then(|it| it.ty(Interner))
|
|
else {
|
|
return Err(MirEvalError::InternalError(
|
|
"simd type with no ty param".into(),
|
|
));
|
|
};
|
|
Ok((len as usize, ty.clone()))
|
|
}
|
|
None => Err(MirEvalError::InternalError(
|
|
"simd type with unevaluatable len param".into(),
|
|
)),
|
|
}
|
|
}
|
|
_ => Err(MirEvalError::InternalError("simd type which is not a struct".into())),
|
|
}
|
|
}
|
|
|
|
pub(super) fn exec_simd_intrinsic(
|
|
&mut self,
|
|
name: &str,
|
|
args: &[IntervalAndTy],
|
|
_generic_args: &Substitution,
|
|
destination: Interval,
|
|
_locals: &Locals,
|
|
_span: MirSpan,
|
|
) -> Result<()> {
|
|
match name {
|
|
"and" | "or" | "xor" => {
|
|
let [left, right] = args else {
|
|
return Err(MirEvalError::InternalError(
|
|
"simd bit op args are not provided".into(),
|
|
));
|
|
};
|
|
let result = left
|
|
.get(self)?
|
|
.iter()
|
|
.zip(right.get(self)?)
|
|
.map(|(&it, &y)| match name {
|
|
"and" => it & y,
|
|
"or" => it | y,
|
|
"xor" => it ^ y,
|
|
_ => unreachable!(),
|
|
})
|
|
.collect::<Vec<_>>();
|
|
destination.write_from_bytes(self, &result)
|
|
}
|
|
"eq" | "ne" | "lt" | "le" | "gt" | "ge" => {
|
|
let [left, right] = args else {
|
|
return Err(MirEvalError::InternalError("simd args are not provided".into()));
|
|
};
|
|
let (len, ty) = self.detect_simd_ty(&left.ty)?;
|
|
let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_)));
|
|
let size = left.interval.size / len;
|
|
let dest_size = destination.size / len;
|
|
let mut destination_bytes = vec![];
|
|
let vector = left.get(self)?.chunks(size).zip(right.get(self)?.chunks(size));
|
|
for (l, r) in vector {
|
|
let mut result = Ordering::Equal;
|
|
for (l, r) in l.iter().zip(r).rev() {
|
|
let it = l.cmp(r);
|
|
if it != Ordering::Equal {
|
|
result = it;
|
|
break;
|
|
}
|
|
}
|
|
if is_signed {
|
|
if let Some((&l, &r)) = l.iter().zip(r).next_back() {
|
|
if l != r {
|
|
result = (l as i8).cmp(&(r as i8));
|
|
}
|
|
}
|
|
}
|
|
let result = match result {
|
|
Ordering::Less => ["lt", "le", "ne"].contains(&name),
|
|
Ordering::Equal => ["ge", "le", "eq"].contains(&name),
|
|
Ordering::Greater => ["ge", "gt", "ne"].contains(&name),
|
|
};
|
|
let result = if result { 255 } else { 0 };
|
|
destination_bytes.extend(std::iter::repeat(result).take(dest_size));
|
|
}
|
|
|
|
destination.write_from_bytes(self, &destination_bytes)
|
|
}
|
|
"bitmask" => {
|
|
let [op] = args else {
|
|
return Err(MirEvalError::InternalError(
|
|
"simd_bitmask args are not provided".into(),
|
|
));
|
|
};
|
|
let (op_len, _) = self.detect_simd_ty(&op.ty)?;
|
|
let op_count = op.interval.size / op_len;
|
|
let mut result: u64 = 0;
|
|
for (i, val) in op.get(self)?.chunks(op_count).enumerate() {
|
|
if !val.iter().all(|&it| it == 0) {
|
|
result |= 1 << i;
|
|
}
|
|
}
|
|
destination.write_from_bytes(self, &result.to_le_bytes()[0..destination.size])
|
|
}
|
|
"shuffle" => {
|
|
let [left, right, index] = args else {
|
|
return Err(MirEvalError::InternalError(
|
|
"simd_shuffle args are not provided".into(),
|
|
));
|
|
};
|
|
let TyKind::Array(_, index_len) = index.ty.kind(Interner) else {
|
|
return Err(MirEvalError::InternalError(
|
|
"simd_shuffle index argument has non-array type".into(),
|
|
));
|
|
};
|
|
let index_len = match try_const_usize(self.db, index_len) {
|
|
Some(it) => it as usize,
|
|
None => {
|
|
return Err(MirEvalError::InternalError(
|
|
"simd type with unevaluatable len param".into(),
|
|
))
|
|
}
|
|
};
|
|
let (left_len, _) = self.detect_simd_ty(&left.ty)?;
|
|
let left_size = left.interval.size / left_len;
|
|
let vector =
|
|
left.get(self)?.chunks(left_size).chain(right.get(self)?.chunks(left_size));
|
|
let mut result = vec![];
|
|
for index in index.get(self)?.chunks(index.interval.size / index_len) {
|
|
let index = from_bytes!(u32, index) as usize;
|
|
let val = match vector.clone().nth(index) {
|
|
Some(it) => it,
|
|
None => {
|
|
return Err(MirEvalError::InternalError(
|
|
"out of bound access in simd shuffle".into(),
|
|
))
|
|
}
|
|
};
|
|
result.extend(val);
|
|
}
|
|
destination.write_from_bytes(self, &result)
|
|
}
|
|
_ => not_supported!("unknown simd intrinsic {name}"),
|
|
}
|
|
}
|
|
}
|