Support more intrinsics in mir interpreter
This commit is contained in:
parent
403433a355
commit
5eb4796d3d
@ -14,6 +14,66 @@ fn size_of() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_of_val() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized
|
||||
extern "rust-intrinsic" {
|
||||
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
|
||||
}
|
||||
|
||||
struct X(i32, u8);
|
||||
|
||||
const GOAL: usize = size_of_val(&X(1, 2));
|
||||
"#,
|
||||
8,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized
|
||||
extern "rust-intrinsic" {
|
||||
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
|
||||
}
|
||||
|
||||
const GOAL: usize = {
|
||||
let x: &[i32] = &[1, 2, 3];
|
||||
size_of_val(x)
|
||||
};
|
||||
"#,
|
||||
12,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized, fmt, builtin_impls
|
||||
extern "rust-intrinsic" {
|
||||
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
|
||||
}
|
||||
|
||||
const GOAL: usize = {
|
||||
let x: &i16 = &5;
|
||||
let y: &dyn core::fmt::Debug = x;
|
||||
let z: &dyn core::fmt::Debug = &y;
|
||||
size_of_val(x) + size_of_val(y) * 10 + size_of_val(z) * 100
|
||||
};
|
||||
"#,
|
||||
1622,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: coerce_unsized
|
||||
extern "rust-intrinsic" {
|
||||
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
|
||||
}
|
||||
|
||||
const GOAL: usize = {
|
||||
size_of_val("salam")
|
||||
};
|
||||
"#,
|
||||
5,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transmute() {
|
||||
check_number(
|
||||
@ -69,7 +129,7 @@ fn wrapping_add() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saturating_add() {
|
||||
fn saturating() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
@ -80,6 +140,16 @@ fn saturating_add() {
|
||||
"#,
|
||||
255,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn saturating_sub<T>(a: T, b: T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: bool = saturating_sub(5u8, 7) == 0 && saturating_sub(8u8, 4) == 4;
|
||||
"#,
|
||||
1,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
@ -160,6 +230,24 @@ fn needs_drop() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn discriminant_value() {
|
||||
check_number(
|
||||
r#"
|
||||
//- minicore: discriminant, option
|
||||
use core::marker::DiscriminantKind;
|
||||
extern "rust-intrinsic" {
|
||||
pub fn discriminant_value<T>(v: &T) -> <T as DiscriminantKind>::Discriminant;
|
||||
}
|
||||
const GOAL: bool = {
|
||||
discriminant_value(&Some(2i32)) == discriminant_value(&Some(5i32))
|
||||
&& discriminant_value(&Some(2i32)) != discriminant_value(&None::<i32>)
|
||||
};
|
||||
"#,
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn likely() {
|
||||
check_number(
|
||||
@ -376,3 +464,47 @@ fn cttz() {
|
||||
3,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rotate() {
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn rotate_left<T: Copy>(x: T, y: T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: i64 = rotate_left(0xaa00000000006e1i64, 12);
|
||||
"#,
|
||||
0x6e10aa,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn rotate_right<T: Copy>(x: T, y: T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: i64 = rotate_right(0x6e10aa, 12);
|
||||
"#,
|
||||
0xaa00000000006e1,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn rotate_left<T: Copy>(x: T, y: T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: i8 = rotate_left(129, 2);
|
||||
"#,
|
||||
6,
|
||||
);
|
||||
check_number(
|
||||
r#"
|
||||
extern "rust-intrinsic" {
|
||||
pub fn rotate_right<T: Copy>(x: T, y: T) -> T;
|
||||
}
|
||||
|
||||
const GOAL: i32 = rotate_right(10006016, 1020315);
|
||||
"#,
|
||||
320192512,
|
||||
);
|
||||
}
|
||||
|
@ -1049,58 +1049,9 @@ fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals<'_>) -> Result<Interva
|
||||
Rvalue::Discriminant(p) => {
|
||||
let ty = self.place_ty(p, locals)?;
|
||||
let bytes = self.eval_place(p, locals)?.get(&self)?;
|
||||
let layout = self.layout(&ty)?;
|
||||
let enum_id = 'b: {
|
||||
match ty.kind(Interner) {
|
||||
TyKind::Adt(e, _) => match e.0 {
|
||||
AdtId::EnumId(e) => break 'b e,
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
return Ok(Owned(0u128.to_le_bytes().to_vec()));
|
||||
};
|
||||
match &layout.variants {
|
||||
Variants::Single { index } => {
|
||||
let r = self.const_eval_discriminant(EnumVariantId {
|
||||
parent: enum_id,
|
||||
local_id: index.0,
|
||||
})?;
|
||||
Owned(r.to_le_bytes().to_vec())
|
||||
}
|
||||
Variants::Multiple { tag, tag_encoding, variants, .. } => {
|
||||
let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else {
|
||||
not_supported!("missing target data layout");
|
||||
};
|
||||
let size = tag.size(&*target_data_layout).bytes_usize();
|
||||
let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
|
||||
match tag_encoding {
|
||||
TagEncoding::Direct => {
|
||||
let tag = &bytes[offset..offset + size];
|
||||
Owned(pad16(tag, false).to_vec())
|
||||
}
|
||||
TagEncoding::Niche { untagged_variant, niche_start, .. } => {
|
||||
let tag = &bytes[offset..offset + size];
|
||||
let candidate_tag = i128::from_le_bytes(pad16(tag, false))
|
||||
.wrapping_sub(*niche_start as i128)
|
||||
as usize;
|
||||
let variant = variants
|
||||
.iter_enumerated()
|
||||
.map(|(x, _)| x)
|
||||
.filter(|x| x != untagged_variant)
|
||||
.nth(candidate_tag)
|
||||
.unwrap_or(*untagged_variant)
|
||||
.0;
|
||||
let result = self.const_eval_discriminant(EnumVariantId {
|
||||
parent: enum_id,
|
||||
local_id: variant,
|
||||
})?;
|
||||
let result = self.compute_discriminant(ty, bytes)?;
|
||||
Owned(result.to_le_bytes().to_vec())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Rvalue::Repeat(x, len) => {
|
||||
let len = match try_const_usize(self.db, &len) {
|
||||
Some(x) => x as usize,
|
||||
@ -1229,6 +1180,60 @@ fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals<'_>) -> Result<Interva
|
||||
})
|
||||
}
|
||||
|
||||
fn compute_discriminant(&self, ty: Ty, bytes: &[u8]) -> Result<i128> {
|
||||
let layout = self.layout(&ty)?;
|
||||
let enum_id = 'b: {
|
||||
match ty.kind(Interner) {
|
||||
TyKind::Adt(e, _) => match e.0 {
|
||||
AdtId::EnumId(e) => break 'b e,
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
return Ok(0);
|
||||
};
|
||||
match &layout.variants {
|
||||
Variants::Single { index } => {
|
||||
let r = self.const_eval_discriminant(EnumVariantId {
|
||||
parent: enum_id,
|
||||
local_id: index.0,
|
||||
})?;
|
||||
Ok(r)
|
||||
}
|
||||
Variants::Multiple { tag, tag_encoding, variants, .. } => {
|
||||
let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else {
|
||||
not_supported!("missing target data layout");
|
||||
};
|
||||
let size = tag.size(&*target_data_layout).bytes_usize();
|
||||
let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
|
||||
match tag_encoding {
|
||||
TagEncoding::Direct => {
|
||||
let tag = &bytes[offset..offset + size];
|
||||
Ok(i128::from_le_bytes(pad16(tag, false)))
|
||||
}
|
||||
TagEncoding::Niche { untagged_variant, niche_start, .. } => {
|
||||
let tag = &bytes[offset..offset + size];
|
||||
let candidate_tag = i128::from_le_bytes(pad16(tag, false))
|
||||
.wrapping_sub(*niche_start as i128)
|
||||
as usize;
|
||||
let variant = variants
|
||||
.iter_enumerated()
|
||||
.map(|(x, _)| x)
|
||||
.filter(|x| x != untagged_variant)
|
||||
.nth(candidate_tag)
|
||||
.unwrap_or(*untagged_variant)
|
||||
.0;
|
||||
let result = self.const_eval_discriminant(EnumVariantId {
|
||||
parent: enum_id,
|
||||
local_id: variant,
|
||||
})?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn coerce_unsized_look_through_fields<T>(
|
||||
&self,
|
||||
ty: &Ty,
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
mod simd;
|
||||
|
||||
macro_rules! from_bytes {
|
||||
($ty:tt, $value:expr) => {
|
||||
($ty::from_le_bytes(match ($value).try_into() {
|
||||
@ -53,6 +55,28 @@ pub(super) fn detect_and_exec_special_function(
|
||||
)?;
|
||||
return Ok(true);
|
||||
}
|
||||
let is_platform_intrinsic = match &function_data.abi {
|
||||
Some(abi) => *abi == Interned::new_str("platform-intrinsic"),
|
||||
None => match def.lookup(self.db.upcast()).container {
|
||||
hir_def::ItemContainerId::ExternBlockId(block) => {
|
||||
let id = block.lookup(self.db.upcast()).id;
|
||||
id.item_tree(self.db.upcast())[id.value].abi.as_deref()
|
||||
== Some("platform-intrinsic")
|
||||
}
|
||||
_ => false,
|
||||
},
|
||||
};
|
||||
if is_platform_intrinsic {
|
||||
self.exec_platform_intrinsic(
|
||||
function_data.name.as_text().unwrap_or_default().as_str(),
|
||||
args,
|
||||
generic_args,
|
||||
destination,
|
||||
&locals,
|
||||
span,
|
||||
)?;
|
||||
return Ok(true);
|
||||
}
|
||||
let is_extern_c = match def.lookup(self.db.upcast()).container {
|
||||
hir_def::ItemContainerId::ExternBlockId(block) => {
|
||||
let id = block.lookup(self.db.upcast()).id;
|
||||
@ -330,6 +354,21 @@ fn exec_extern_c(
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_platform_intrinsic(
|
||||
&mut self,
|
||||
name: &str,
|
||||
args: &[IntervalAndTy],
|
||||
generic_args: &Substitution,
|
||||
destination: Interval,
|
||||
locals: &Locals<'_>,
|
||||
span: MirSpan,
|
||||
) -> Result<()> {
|
||||
if let Some(name) = name.strip_prefix("simd_") {
|
||||
return self.exec_simd_intrinsic(name, args, generic_args, destination, locals, span);
|
||||
}
|
||||
not_supported!("unknown platform intrinsic {name}");
|
||||
}
|
||||
|
||||
fn exec_intrinsic(
|
||||
&mut self,
|
||||
name: &str,
|
||||
@ -478,6 +517,33 @@ fn exec_intrinsic(
|
||||
let size = self.size_of_sized(ty, locals, "size_of arg")?;
|
||||
destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size])
|
||||
}
|
||||
"size_of_val" => {
|
||||
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
|
||||
return Err(MirEvalError::TypeError("size_of_val generic arg is not provided"));
|
||||
};
|
||||
let [arg] = args else {
|
||||
return Err(MirEvalError::TypeError("size_of_val args are not provided"));
|
||||
};
|
||||
let metadata = arg.interval.slice(self.ptr_size()..self.ptr_size() * 2);
|
||||
let size = match ty.kind(Interner) {
|
||||
TyKind::Str => return destination.write_from_interval(self, metadata),
|
||||
TyKind::Slice(inner) => {
|
||||
let len = from_bytes!(usize, metadata.get(self)?);
|
||||
len * self.size_of_sized(inner, locals, "slice inner type")?
|
||||
}
|
||||
TyKind::Dyn(_) => self.size_of_sized(
|
||||
self.vtable_map.ty_of_bytes(metadata.get(self)?)?,
|
||||
locals,
|
||||
"dyn concrete type",
|
||||
)?,
|
||||
_ => self.size_of_sized(
|
||||
ty,
|
||||
locals,
|
||||
"unsized type other than str, slice, and dyn",
|
||||
)?,
|
||||
};
|
||||
destination.write_from_bytes(self, &size.to_le_bytes())
|
||||
}
|
||||
"min_align_of" | "pref_align_of" => {
|
||||
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
|
||||
return Err(MirEvalError::TypeError("align_of generic arg is not provided"));
|
||||
@ -501,13 +567,17 @@ fn exec_intrinsic(
|
||||
let ans = lhs.get(self)? == rhs.get(self)?;
|
||||
destination.write_from_bytes(self, &[u8::from(ans)])
|
||||
}
|
||||
"saturating_add" => {
|
||||
"saturating_add" | "saturating_sub" => {
|
||||
let [lhs, rhs] = args else {
|
||||
return Err(MirEvalError::TypeError("saturating_add args are not provided"));
|
||||
};
|
||||
let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
|
||||
let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
|
||||
let ans = lhs.saturating_add(rhs);
|
||||
let ans = match name {
|
||||
"saturating_add" => lhs.saturating_add(rhs),
|
||||
"saturating_sub" => lhs.saturating_sub(rhs),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let bits = destination.size * 8;
|
||||
// FIXME: signed
|
||||
let is_signed = false;
|
||||
@ -544,6 +614,26 @@ fn exec_intrinsic(
|
||||
let ans = lhs.wrapping_mul(rhs);
|
||||
destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
|
||||
}
|
||||
"wrapping_shl" | "unchecked_shl" => {
|
||||
// FIXME: signed
|
||||
let [lhs, rhs] = args else {
|
||||
return Err(MirEvalError::TypeError("unchecked_shl args are not provided"));
|
||||
};
|
||||
let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
|
||||
let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
|
||||
let ans = lhs.wrapping_shl(rhs as u32);
|
||||
destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
|
||||
}
|
||||
"wrapping_shr" | "unchecked_shr" => {
|
||||
// FIXME: signed
|
||||
let [lhs, rhs] = args else {
|
||||
return Err(MirEvalError::TypeError("unchecked_shr args are not provided"));
|
||||
};
|
||||
let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
|
||||
let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
|
||||
let ans = lhs.wrapping_shr(rhs as u32);
|
||||
destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
|
||||
}
|
||||
"unchecked_rem" => {
|
||||
// FIXME: signed
|
||||
let [lhs, rhs] = args else {
|
||||
@ -666,6 +756,79 @@ fn exec_intrinsic(
|
||||
destination
|
||||
.write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
|
||||
}
|
||||
"rotate_left" => {
|
||||
let [lhs, rhs] = args else {
|
||||
return Err(MirEvalError::TypeError("rotate_left args are not provided"));
|
||||
};
|
||||
let lhs = &lhs.get(self)?[0..destination.size];
|
||||
let rhs = rhs.get(self)?[0] as u32;
|
||||
match destination.size {
|
||||
1 => {
|
||||
let r = from_bytes!(u8, lhs).rotate_left(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
2 => {
|
||||
let r = from_bytes!(u16, lhs).rotate_left(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
4 => {
|
||||
let r = from_bytes!(u32, lhs).rotate_left(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
8 => {
|
||||
let r = from_bytes!(u64, lhs).rotate_left(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
16 => {
|
||||
let r = from_bytes!(u128, lhs).rotate_left(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
s => not_supported!("destination with size {s} for rotate_left"),
|
||||
}
|
||||
}
|
||||
"rotate_right" => {
|
||||
let [lhs, rhs] = args else {
|
||||
return Err(MirEvalError::TypeError("rotate_right args are not provided"));
|
||||
};
|
||||
let lhs = &lhs.get(self)?[0..destination.size];
|
||||
let rhs = rhs.get(self)?[0] as u32;
|
||||
match destination.size {
|
||||
1 => {
|
||||
let r = from_bytes!(u8, lhs).rotate_right(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
2 => {
|
||||
let r = from_bytes!(u16, lhs).rotate_right(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
4 => {
|
||||
let r = from_bytes!(u32, lhs).rotate_right(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
8 => {
|
||||
let r = from_bytes!(u64, lhs).rotate_right(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
16 => {
|
||||
let r = from_bytes!(u128, lhs).rotate_right(rhs);
|
||||
destination.write_from_bytes(self, &r.to_le_bytes())
|
||||
}
|
||||
s => not_supported!("destination with size {s} for rotate_right"),
|
||||
}
|
||||
}
|
||||
"discriminant_value" => {
|
||||
let [arg] = args else {
|
||||
return Err(MirEvalError::TypeError("discriminant_value arg is not provided"));
|
||||
};
|
||||
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
|
||||
return Err(MirEvalError::TypeError("discriminant_value generic arg is not provided"));
|
||||
};
|
||||
let addr = Address::from_bytes(arg.get(self)?)?;
|
||||
let size = self.size_of_sized(ty, locals, "discriminant_value ptr type")?;
|
||||
let interval = Interval { addr, size };
|
||||
let r = self.compute_discriminant(ty.clone(), interval.get(self)?)?;
|
||||
destination.write_from_bytes(self, &r.to_le_bytes()[0..destination.size])
|
||||
}
|
||||
"const_eval_select" => {
|
||||
let [tuple, const_fn, _] = args else {
|
||||
return Err(MirEvalError::TypeError("const_eval_select args are not provided"));
|
||||
|
124
crates/hir-ty/src/mir/eval/shim/simd.rs
Normal file
124
crates/hir-ty/src/mir/eval/shim/simd.rs
Normal file
@ -0,0 +1,124 @@
|
||||
//! Shim implementation for simd intrinsics
|
||||
|
||||
use crate::TyKind;
|
||||
|
||||
use super::*;
|
||||
|
||||
macro_rules! from_bytes {
|
||||
($ty:tt, $value:expr) => {
|
||||
($ty::from_le_bytes(match ($value).try_into() {
|
||||
Ok(x) => x,
|
||||
Err(_) => return Err(MirEvalError::TypeError("mismatched size")),
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! not_supported {
|
||||
($x: expr) => {
|
||||
return Err(MirEvalError::NotSupported(format!($x)))
|
||||
};
|
||||
}
|
||||
|
||||
impl Evaluator<'_> {
|
||||
fn detect_simd_ty(&self, ty: &Ty) -> Result<usize> {
|
||||
match ty.kind(Interner) {
|
||||
TyKind::Adt(_, subst) => {
|
||||
let Some(len) = subst.as_slice(Interner).get(1).and_then(|x| x.constant(Interner)) else {
|
||||
return Err(MirEvalError::TypeError("simd type without len param"));
|
||||
};
|
||||
match try_const_usize(self.db, len) {
|
||||
Some(x) => Ok(x as usize),
|
||||
None => Err(MirEvalError::TypeError("simd type with unevaluatable len param")),
|
||||
}
|
||||
}
|
||||
_ => Err(MirEvalError::TypeError("simd type which is not a struct")),
|
||||
}
|
||||
}
|
||||
|
||||
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::TypeError("simd bit op args are not provided"));
|
||||
};
|
||||
let result = left
|
||||
.get(self)?
|
||||
.iter()
|
||||
.zip(right.get(self)?)
|
||||
.map(|(&x, &y)| match name {
|
||||
"and" => x & y,
|
||||
"or" => x | y,
|
||||
"xor" => x ^ y,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
destination.write_from_bytes(self, &result)
|
||||
}
|
||||
"eq" | "ne" => {
|
||||
let [left, right] = args else {
|
||||
return Err(MirEvalError::TypeError("simd_eq args are not provided"));
|
||||
};
|
||||
let result = left.get(self)? == right.get(self)?;
|
||||
let result = result ^ (name == "ne");
|
||||
destination.write_from_bytes(self, &[u8::from(result)])
|
||||
}
|
||||
"bitmask" => {
|
||||
let [op] = args else {
|
||||
return Err(MirEvalError::TypeError("simd_shuffle args are not provided"));
|
||||
};
|
||||
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(|&x| x == 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::TypeError("simd_shuffle args are not provided"));
|
||||
};
|
||||
let TyKind::Array(_, index_len) = index.ty.kind(Interner) else {
|
||||
return Err(MirEvalError::TypeError("simd_shuffle index argument has non-array type"));
|
||||
};
|
||||
let index_len = match try_const_usize(self.db, index_len) {
|
||||
Some(x) => x as usize,
|
||||
None => {
|
||||
return Err(MirEvalError::TypeError(
|
||||
"simd type with unevaluatable len param",
|
||||
))
|
||||
}
|
||||
};
|
||||
let left_len = self.detect_simd_ty(&left.ty)?;
|
||||
let left_count = left.interval.size / left_len;
|
||||
let vector =
|
||||
left.get(self)?.chunks(left_count).chain(right.get(self)?.chunks(left_count));
|
||||
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(x) => x,
|
||||
None => {
|
||||
return Err(MirEvalError::TypeError(
|
||||
"out of bound access in simd shuffle",
|
||||
))
|
||||
}
|
||||
};
|
||||
result.extend(val);
|
||||
}
|
||||
destination.write_from_bytes(self, &result)
|
||||
}
|
||||
_ => not_supported!("unknown simd intrinsic {name}"),
|
||||
}
|
||||
}
|
||||
}
|
@ -474,7 +474,7 @@ fn main() {
|
||||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 9287..9295,
|
||||
range: 9288..9296,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
@ -487,7 +487,7 @@ fn main() {
|
||||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 9319..9323,
|
||||
range: 9320..9324,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
@ -511,7 +511,7 @@ fn main() {
|
||||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 9287..9295,
|
||||
range: 9288..9296,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
@ -524,7 +524,7 @@ fn main() {
|
||||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 9319..9323,
|
||||
range: 9320..9324,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
@ -548,7 +548,7 @@ fn main() {
|
||||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 9287..9295,
|
||||
range: 9288..9296,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
@ -561,7 +561,7 @@ fn main() {
|
||||
file_id: FileId(
|
||||
1,
|
||||
),
|
||||
range: 9319..9323,
|
||||
range: 9320..9324,
|
||||
},
|
||||
),
|
||||
tooltip: "",
|
||||
|
@ -20,6 +20,7 @@
|
||||
//! deref_mut: deref
|
||||
//! deref: sized
|
||||
//! derive:
|
||||
//! discriminant:
|
||||
//! drop:
|
||||
//! eq: sized
|
||||
//! error: fmt
|
||||
@ -129,6 +130,14 @@ pub trait Tuple {}
|
||||
#[lang = "phantom_data"]
|
||||
pub struct PhantomData<T: ?Sized>;
|
||||
// endregion:phantom_data
|
||||
|
||||
// region:discriminant
|
||||
#[lang = "discriminant_kind"]
|
||||
pub trait DiscriminantKind {
|
||||
#[lang = "discriminant_type"]
|
||||
type Discriminant;
|
||||
}
|
||||
// endregion:discriminant
|
||||
}
|
||||
|
||||
// region:default
|
||||
|
Loading…
Reference in New Issue
Block a user