mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-31 23:38:45 +00:00
Auto merge of #15119 - HKalbasi:mir, r=HKalbasi
Support more intrinsics in mir interpreter Increases passed tests on self from 49 to 52
This commit is contained in:
commit
06b99d46d0
6 changed files with 493 additions and 60 deletions
|
@ -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]
|
#[test]
|
||||||
fn transmute() {
|
fn transmute() {
|
||||||
check_number(
|
check_number(
|
||||||
|
@ -69,7 +129,7 @@ fn wrapping_add() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn saturating_add() {
|
fn saturating() {
|
||||||
check_number(
|
check_number(
|
||||||
r#"
|
r#"
|
||||||
extern "rust-intrinsic" {
|
extern "rust-intrinsic" {
|
||||||
|
@ -80,6 +140,16 @@ fn saturating_add() {
|
||||||
"#,
|
"#,
|
||||||
255,
|
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(
|
check_number(
|
||||||
r#"
|
r#"
|
||||||
extern "rust-intrinsic" {
|
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]
|
#[test]
|
||||||
fn likely() {
|
fn likely() {
|
||||||
check_number(
|
check_number(
|
||||||
|
@ -376,3 +464,47 @@ fn cttz() {
|
||||||
3,
|
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,57 +1049,8 @@ impl Evaluator<'_> {
|
||||||
Rvalue::Discriminant(p) => {
|
Rvalue::Discriminant(p) => {
|
||||||
let ty = self.place_ty(p, locals)?;
|
let ty = self.place_ty(p, locals)?;
|
||||||
let bytes = self.eval_place(p, locals)?.get(&self)?;
|
let bytes = self.eval_place(p, locals)?.get(&self)?;
|
||||||
let layout = self.layout(&ty)?;
|
let result = self.compute_discriminant(ty, bytes)?;
|
||||||
let enum_id = 'b: {
|
Owned(result.to_le_bytes().to_vec())
|
||||||
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,
|
|
||||||
})?;
|
|
||||||
Owned(result.to_le_bytes().to_vec())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Rvalue::Repeat(x, len) => {
|
Rvalue::Repeat(x, len) => {
|
||||||
let len = match try_const_usize(self.db, &len) {
|
let len = match try_const_usize(self.db, &len) {
|
||||||
|
@ -1229,6 +1180,60 @@ impl Evaluator<'_> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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>(
|
fn coerce_unsized_look_through_fields<T>(
|
||||||
&self,
|
&self,
|
||||||
ty: &Ty,
|
ty: &Ty,
|
||||||
|
|
|
@ -5,6 +5,8 @@ use std::cmp;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
mod simd;
|
||||||
|
|
||||||
macro_rules! from_bytes {
|
macro_rules! from_bytes {
|
||||||
($ty:tt, $value:expr) => {
|
($ty:tt, $value:expr) => {
|
||||||
($ty::from_le_bytes(match ($value).try_into() {
|
($ty::from_le_bytes(match ($value).try_into() {
|
||||||
|
@ -53,6 +55,28 @@ impl Evaluator<'_> {
|
||||||
)?;
|
)?;
|
||||||
return Ok(true);
|
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 {
|
let is_extern_c = match def.lookup(self.db.upcast()).container {
|
||||||
hir_def::ItemContainerId::ExternBlockId(block) => {
|
hir_def::ItemContainerId::ExternBlockId(block) => {
|
||||||
let id = block.lookup(self.db.upcast()).id;
|
let id = block.lookup(self.db.upcast()).id;
|
||||||
|
@ -330,6 +354,21 @@ impl Evaluator<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
fn exec_intrinsic(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
@ -478,6 +517,33 @@ impl Evaluator<'_> {
|
||||||
let size = self.size_of_sized(ty, locals, "size_of arg")?;
|
let size = self.size_of_sized(ty, locals, "size_of arg")?;
|
||||||
destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size])
|
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" => {
|
"min_align_of" | "pref_align_of" => {
|
||||||
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
|
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"));
|
return Err(MirEvalError::TypeError("align_of generic arg is not provided"));
|
||||||
|
@ -501,13 +567,17 @@ impl Evaluator<'_> {
|
||||||
let ans = lhs.get(self)? == rhs.get(self)?;
|
let ans = lhs.get(self)? == rhs.get(self)?;
|
||||||
destination.write_from_bytes(self, &[u8::from(ans)])
|
destination.write_from_bytes(self, &[u8::from(ans)])
|
||||||
}
|
}
|
||||||
"saturating_add" => {
|
"saturating_add" | "saturating_sub" => {
|
||||||
let [lhs, rhs] = args else {
|
let [lhs, rhs] = args else {
|
||||||
return Err(MirEvalError::TypeError("saturating_add args are not provided"));
|
return Err(MirEvalError::TypeError("saturating_add args are not provided"));
|
||||||
};
|
};
|
||||||
let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
|
let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
|
||||||
let rhs = u128::from_le_bytes(pad16(rhs.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;
|
let bits = destination.size * 8;
|
||||||
// FIXME: signed
|
// FIXME: signed
|
||||||
let is_signed = false;
|
let is_signed = false;
|
||||||
|
@ -544,6 +614,26 @@ impl Evaluator<'_> {
|
||||||
let ans = lhs.wrapping_mul(rhs);
|
let ans = lhs.wrapping_mul(rhs);
|
||||||
destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
|
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" => {
|
"unchecked_rem" => {
|
||||||
// FIXME: signed
|
// FIXME: signed
|
||||||
let [lhs, rhs] = args else {
|
let [lhs, rhs] = args else {
|
||||||
|
@ -666,6 +756,79 @@ impl Evaluator<'_> {
|
||||||
destination
|
destination
|
||||||
.write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
|
.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" => {
|
"const_eval_select" => {
|
||||||
let [tuple, const_fn, _] = args else {
|
let [tuple, const_fn, _] = args else {
|
||||||
return Err(MirEvalError::TypeError("const_eval_select args are not provided"));
|
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(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 9287..9295,
|
range: 9288..9296,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
|
@ -487,7 +487,7 @@ fn main() {
|
||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 9319..9323,
|
range: 9320..9324,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
|
@ -511,7 +511,7 @@ fn main() {
|
||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 9287..9295,
|
range: 9288..9296,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
|
@ -524,7 +524,7 @@ fn main() {
|
||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 9319..9323,
|
range: 9320..9324,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
|
@ -548,7 +548,7 @@ fn main() {
|
||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 9287..9295,
|
range: 9288..9296,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
|
@ -561,7 +561,7 @@ fn main() {
|
||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
range: 9319..9323,
|
range: 9320..9324,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
tooltip: "",
|
tooltip: "",
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
//! deref_mut: deref
|
//! deref_mut: deref
|
||||||
//! deref: sized
|
//! deref: sized
|
||||||
//! derive:
|
//! derive:
|
||||||
|
//! discriminant:
|
||||||
//! drop:
|
//! drop:
|
||||||
//! eq: sized
|
//! eq: sized
|
||||||
//! error: fmt
|
//! error: fmt
|
||||||
|
@ -129,6 +130,14 @@ pub mod marker {
|
||||||
#[lang = "phantom_data"]
|
#[lang = "phantom_data"]
|
||||||
pub struct PhantomData<T: ?Sized>;
|
pub struct PhantomData<T: ?Sized>;
|
||||||
// endregion:phantom_data
|
// endregion:phantom_data
|
||||||
|
|
||||||
|
// region:discriminant
|
||||||
|
#[lang = "discriminant_kind"]
|
||||||
|
pub trait DiscriminantKind {
|
||||||
|
#[lang = "discriminant_type"]
|
||||||
|
type Discriminant;
|
||||||
|
}
|
||||||
|
// endregion:discriminant
|
||||||
}
|
}
|
||||||
|
|
||||||
// region:default
|
// region:default
|
||||||
|
|
Loading…
Reference in a new issue