diff --git a/cilly/src/bin/linker/main.rs b/cilly/src/bin/linker/main.rs index 81b57aee..6de3b072 100644 --- a/cilly/src/bin/linker/main.rs +++ b/cilly/src/bin/linker/main.rs @@ -374,6 +374,7 @@ fn main() { cilly::v2::builtins::atomics::generate_all_atomics(&mut final_assembly, &mut overrides); cilly::v2::builtins::instert_threading(&mut final_assembly, &mut overrides); cilly::v2::builtins::math::math(&mut final_assembly, &mut overrides); + cilly::v2::builtins::simd::simd(&mut final_assembly, &mut overrides); } // Ensure the cctor and tcctor exist! let _ = final_assembly.tcctor(); diff --git a/cilly/src/call_site.rs b/cilly/src/call_site.rs deleted file mode 100644 index 8b137891..00000000 --- a/cilly/src/call_site.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/cilly/src/dotnet_type.rs b/cilly/src/dotnet_type.rs deleted file mode 100644 index 2b85216a..00000000 --- a/cilly/src/dotnet_type.rs +++ /dev/null @@ -1,262 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use crate::{IString, Type}; - -#[derive(Serialize, Deserialize, PartialEq, Clone, Eq, Hash, Debug)] -pub struct ClassRef { - assembly: Option, - name_path: IString, - generics: Vec, - // In cause of `System.BadImageFormatException: Expected value type but got type kind 14` check if `is_valuetype` is always correct! - is_valuetype: bool, -} -impl ClassRef { - #[must_use] - pub fn bit_operations() -> Self { - ClassRef::new("System.Runtime".into(), "System.Numerics.BitOperations") - .with_valuetype(false) - } - #[must_use] - pub fn marshal() -> Self { - Self::new( - Some("System.Runtime.InteropServices"), - "System.Runtime.InteropServices.Marshal", - ) - .with_valuetype(false) - } - #[must_use] - pub fn gc_handle() -> Self { - Self::new( - Some("System.Runtime"), - "System.Runtime.InteropServices.GCHandle", - ) - .with_valuetype(true) - } - #[must_use] - pub fn thread() -> Self { - Self::new(Some("System.Threading.Thread"), "System.Threading.Thread").with_valuetype(false) - } - #[must_use] - pub fn thread_start() -> Self { - Self::new( - Some("System.Threading.Thread"), - "System.Threading.ThreadStart", - ) - .with_valuetype(false) - } - #[must_use] - pub fn half() -> Self { - Self::new(Some("System.Runtime"), "System.Half").with_valuetype(true) - } - #[must_use] - pub fn single() -> Self { - Self::new(Some("System.Runtime"), "System.Single").with_valuetype(true) - } - #[must_use] - pub fn byte() -> Self { - Self::new(Some("System.Runtime"), "System.Byte") - } - #[must_use] - pub fn sbyte() -> Self { - Self::new(Some("System.Runtime"), "System.SByte") - } - #[must_use] - pub fn uint16() -> Self { - Self::new(Some("System.Runtime"), "System.UInt16") - } - #[must_use] - pub fn int16() -> Self { - Self::new(Some("System.Runtime"), "System.Int16") - } - #[must_use] - pub fn double() -> Self { - Self::new(Some("System.Runtime"), "System.Double").with_valuetype(true) - } - #[must_use] - pub fn console() -> Self { - Self::new(Some("System.Console"), "System.Console").with_valuetype(false) - } - #[must_use] - pub fn enviroment() -> Self { - Self::new(Some("System.Runtime"), "System.Environment").with_valuetype(false) - } - #[must_use] - pub fn math() -> Self { - Self::new(Some("System.Runtime"), "System.Math").with_valuetype(false) - } - #[must_use] - pub fn mathf() -> Self { - Self::new(Some("System.Runtime"), "System.MathF").with_valuetype(false) - } - #[must_use] - pub fn int_128() -> Self { - Self::new(Some("System.Runtime"), "System.Int128") - } - #[must_use] - pub fn binary_primitives() -> Self { - Self::new( - Some("System.Memory"), - "System.Buffers.Binary.BinaryPrimitives", - ) - .with_valuetype(false) - } - #[must_use] - pub fn uint_128() -> Self { - Self::new(Some("System.Runtime"), "System.UInt128") - } - #[must_use] - pub fn usize_type() -> Self { - Self::new(Some("System.Runtime"), "System.UIntPtr") - } - #[must_use] - pub fn isize_type() -> Self { - Self::new(Some("System.Runtime"), "System.IntPtr") - } - #[must_use] - pub fn type_handle_type() -> Self { - Self::new(Some("System.Runtime"), "System.RuntimeTypeHandle") - } - #[must_use] - pub fn type_type() -> Self { - Self::new(Some("System.Runtime"), "System.Type").with_valuetype(false) - } - #[must_use] - pub fn object_type() -> Self { - Self::new(Some("System.Runtime"), "System.Object").with_valuetype(false) - } - #[must_use] - pub fn string_type() -> Self { - Self::new(Some("System.Runtime"), "System.String").with_valuetype(false) - } - #[must_use] - pub fn managed_array() -> Self { - Self::new(Some("System.Runtime"), "System.Array").with_valuetype(false) - } - #[must_use] - pub fn with_valuetype(mut self, valuetype: bool) -> Self { - self.set_valuetype(valuetype); - self - } - #[must_use] - pub fn compiler_services_unsafe() -> Self { - Self::new( - Some("System.Runtime"), - "System.Runtime.CompilerServices.Unsafe", - ) - .with_valuetype(false) - } - pub fn new, S2: Into + std::borrow::Borrow>( - assembly: Option, - name_path: S2, - ) -> Self { - assert!(!name_path.borrow().contains('/')); - Self { - assembly: assembly.map(std::convert::Into::into), - name_path: name_path.into(), - generics: Vec::new(), - is_valuetype: true, - } - } - #[must_use] - pub const fn is_valuetype(&self) -> bool { - self.is_valuetype - } - #[must_use] - pub const fn tpe_prefix(&self) -> &'static str { - if self.is_valuetype() { - "valuetype" - } else { - "class" - } - } - pub fn set_valuetype(&mut self, set_valuetype: bool) { - self.is_valuetype = set_valuetype - } - #[must_use] - pub fn array(element: &Type, length: usize) -> Self { - let name = crate::arr_name(length, element); - Self::new::(None, name) - } - - pub fn asm(&self) -> Option<&str> { - self.assembly.as_ref().map(std::convert::AsRef::as_ref) - } - #[must_use] - pub fn name_path<'a, 'b: 'a>(&'a self) -> &'a str { - &self.name_path - } - - #[must_use] - pub fn generics(&self) -> &[Type] { - &self.generics - } - pub fn set_generics(&mut self, set_generics: impl Into>) { - self.generics = set_generics.into() - } - - #[must_use] - pub fn interlocked() -> Self { - Self::new(Some("System.Threading"), "System.Threading.Interlocked").with_valuetype(false) - } - #[must_use] - pub fn monitor() -> Self { - Self::new(Some("System.Threading"), "System.Threading.Monitor").with_valuetype(false) - } - #[must_use] - pub fn exception() -> Self { - Self::new(Some("System.Runtime"), "System.Exception").with_valuetype(false) - } - #[must_use] - pub fn assembly() -> Self { - Self::new(Some("System.Runtime"), "System.Reflection.Assembly").with_valuetype(false) - } - #[must_use] - pub fn i_dictionary() -> Self { - Self::new(Some("System.Runtime"), "System.Collections.IDictionary").with_valuetype(false) - } - #[must_use] - pub fn dictionary(key: Type, value: Type) -> Self { - let mut res = Self::new( - Some("System.Collections.Concurrent"), - "System.Collections.Concurrent.ConcurrentDictionary", - ) - .with_valuetype(false); - res.set_generics(&[key, value]); - res - } - #[must_use] - pub fn collection() -> Self { - Self::new(Some("System.Runtime"), "System.Collections.ICollection").with_valuetype(false) - } - #[must_use] - pub fn dictionary_iterator() -> Self { - Self::new( - Some("System.Runtime"), - "System.Collections.IDictionaryEnumerator", - ) - .with_valuetype(false) - } - #[must_use] - pub fn collection_iterator() -> Self { - Self::new(Some("System.Runtime"), "System.Collections.IEnumerator").with_valuetype(false) - } - - #[must_use] - pub fn native_mem() -> Self { - Self::new( - Some("System.Runtime.InteropServices"), - "System.Runtime.InteropServices.NativeMemory", - ) - .with_valuetype(false) - } - - pub fn dotnet_tuple(elements: &[Type]) -> Self { - let mut res = Self::new(Some("System.Runtime"), "System.ValueTuple"); - res.set_generics(elements); - res - } - - pub fn dictionary_entry() -> Self { - Self::new(Some("System.Runtime"), "System.Collections.DictionaryEntry") - } -} diff --git a/cilly/src/fn_sig.rs b/cilly/src/fn_sig.rs deleted file mode 100644 index 8b137891..00000000 --- a/cilly/src/fn_sig.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/cilly/src/ilasm_exporter.rs b/cilly/src/ilasm_exporter.rs deleted file mode 100644 index 8b137891..00000000 --- a/cilly/src/ilasm_exporter.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/cilly/src/lib.rs b/cilly/src/lib.rs index 49c95b4d..10d0b767 100644 --- a/cilly/src/lib.rs +++ b/cilly/src/lib.rs @@ -47,7 +47,6 @@ pub mod asm; pub mod basic_block; -pub mod call_site; pub mod cil_iter; pub mod cil_iter_mut; pub mod cil_node; diff --git a/cilly/src/static_field_descr.rs b/cilly/src/static_field_descr.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/cilly/src/type.rs b/cilly/src/type.rs deleted file mode 100644 index b9c9a720..00000000 --- a/cilly/src/type.rs +++ /dev/null @@ -1,132 +0,0 @@ -use serde::{Deserialize, Serialize}; - -use crate::{utilis::MemoryUsage, ClassRef, FnSig}; -#[derive(Serialize, Deserialize, PartialEq, Clone, Eq, Hash, Debug)] -pub enum Type { - /// Void type - Void, - /// Boolean type - Bool, - // Floating-point types - F16, - F32, - F64, - F128, - // Unsigned intiegers - U8, - U16, - U32, - U64, - U128, - USize, - // Signed intiegers - I8, - I16, - I32, - I64, - I128, - ISize, - /// A refernece to a .NET type - DotnetType(Box), - // Pointer to a type - Ptr(Box), - /// A managed reference `&`. IS NOT EQUIVALENT TO RUST `&`! - ManagedReference(Box), - /// Foregin type. Will never be interacted with directly - Foreign, - /// Generic argument - GenericArg(u32), - CallGenericArg(u32), - DotnetChar, - /// Rust `FnDefs` - DelegatePtr(Box), - /// Generic argument of a method - MethodGenericArg(i32), - ManagedArray { - element: Box, - dims: std::num::NonZeroU8, - }, -} - -impl MemoryUsage for Type { - fn memory_usage(&self, counter: &mut impl crate::utilis::MemoryUsageCounter) -> usize { - let total_size = std::mem::size_of::(); - let name = std::any::type_name::(); - let inner_size = match self { - Self::Ptr(inner) | Self::ManagedReference(inner) => inner.memory_usage(counter), - Self::DelegatePtr(inner) => inner.memory_usage(counter), - _ => 0, - }; - counter.add_type(name, total_size + inner_size); - total_size + inner_size - } -} -impl Type { - /// If this is a reference to a dotnet type, return that type. Will not work with pointers/references. - #[must_use] - pub fn as_class_ref(&self) -> Option { - match self { - Self::DotnetType(inner) => Some(inner.as_ref().clone()), - _ => None, - } - } - /// If this is a reference to a dotnet type, return that type. Works with pointers/references. - #[must_use] - pub fn dotnet_refs(&self) -> Option { - match self { - Self::DotnetType(inner) => Some(inner.as_ref().clone()), - Self::Ptr(inner) | Self::ManagedReference(inner) => inner.dotnet_refs(), - _ => None, - } - } - - #[must_use] - /// If this type is a pointer to a unction type, return its signature. - pub const fn as_delegate_ptr(&self) -> Option<&crate::fn_sig::FnSig> { - if let Self::DelegatePtr(v) = self { - Some(v) - } else { - None - } - } - #[must_use] - /// Checks if a type can be operated on by CIL numeric instructions. - pub const fn is_primitive_numeric(&self) -> bool { - // match_same_arms disabled, since we want to document the variants explicitly. - #[allow(clippy::match_same_arms)] - match self { - Self::I8 - | Self::I16 - | Self::I32 - | Self::I64 - | Self::ISize - | Self::U8 - | Self::U16 - | Self::U32 - | Self::U64 - | Self::USize => true, - Self::Bool => true, - Self::F32 | Self::F64 => true, - Self::Ptr(_) | Self::DelegatePtr(_) => true, - // 128 bit ints are NOT primitve CIL types! - Self::I128 | Self::U128 => true, - _ => false, - } - } -} -impl From for Type { - fn from(value: ClassRef) -> Self { - Self::DotnetType(Box::new(value)) - } -} -#[test] -fn type_repr_size_ok() { - // Type should not grow without a good reason. - assert!(std::mem::size_of::() <= 16); -} -#[macro_export] -macro_rules! ptr { - ($tpe:expr) => { - Type::Ptr(Box::new($tpe)) - }; -} diff --git a/cilly/src/type_def.rs b/cilly/src/type_def.rs deleted file mode 100644 index 7f08b7e7..00000000 --- a/cilly/src/type_def.rs +++ /dev/null @@ -1,173 +0,0 @@ -use std::num::NonZeroU32; - -use serde::{Deserialize, Serialize}; - -use crate::{access_modifier::AccessModifer, method::Method, ClassRef, IString, Type}; - -#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, Clone, Debug)] -pub struct ClassDef { - access: AccessModifer, - name: IString, - inner_types: Vec, - fields: Vec<(IString, Type)>, - functions: Vec, - explicit_offsets: Option>, - gargc: u32, - extends: Option, - explict_size: Option, - //requires_aligement_adjustements:bool, -} -impl ClassDef { - pub fn set_generic_count(&mut self, generic_count: u32) { - self.gargc = generic_count; - } - - fn field_types(&self) -> impl Iterator { - self.fields().iter().map(|(_, tpe)| tpe) - } - pub fn all_types(&self) -> impl Iterator { - //TODO: this breaks if a type contains more than one layer of nested types! - self.field_types() - .chain(self.inner_types().iter().flat_map(ClassDef::field_types)) - } - #[must_use] - pub fn gargc(&self) -> u32 { - self.gargc - } - #[must_use] - pub fn name(&self) -> &str { - &self.name - } - #[must_use] - pub fn access_modifier(&self) -> AccessModifer { - self.access - } - #[must_use] - pub fn extends(&self) -> Option<&ClassRef> { - self.extends.as_ref() - } - #[must_use] - pub fn fields(&self) -> &[(IString, Type)] { - &self.fields - } - pub fn add_field(&mut self, name: IString, tpe: Type) { - self.fields.push((name, tpe)); - } - #[must_use] - pub fn inner_types(&self) -> &[Self] { - &self.inner_types - } - #[must_use] - pub fn explicit_offsets(&self) -> Option<&Vec> { - self.explicit_offsets.as_ref() - } - pub fn add_method(&mut self, method: Method) { - self.functions.push(method); - } - pub fn methods(&self) -> impl Iterator { - self.functions.iter() - } - pub fn methods_mut(&mut self) -> impl Iterator { - self.functions.iter_mut() - } - #[must_use] - pub fn nameonly(name: &str) -> Self { - Self { - access: AccessModifer::Public, - name: name.into(), - inner_types: vec![], - fields: vec![], - functions: vec![], - gargc: 0, - extends: None, - explicit_offsets: None, - explict_size: Some(NonZeroU32::new(1).unwrap()), - } - } - #[must_use] - #[allow(clippy::too_many_arguments)] - pub fn new( - access: AccessModifer, - name: IString, - inner_types: Vec, - fields: Vec<(IString, Type)>, - functions: Vec, - explicit_offsets: Option>, - gargc: u32, - extends: Option, - explict_size: Option, - ) -> Self { - let res = Self { - access, - name, - inner_types, - fields, - functions, - explicit_offsets, - gargc, - extends, - explict_size, - }; - //TODO:consider having this enabled only for debug - res.sanity_check(); - res - } - - #[must_use] - pub const fn explict_size(&self) -> Option { - self.explict_size - } - - fn sanity_check(&self) { - if let Some(size) = self.explict_size() { - self.explicit_offsets().iter().flat_map(|vec|*vec).for_each(|offset|assert!(*offset <= size.get(), "Sanity check failed! The size of type {name} is {size}, yet it has a filed at offset {offset}",name = self.name)); - } - if let Some(offsets) = self.explicit_offsets() { - assert_eq!( - offsets.len(), - self.fields().len(), - "Sanity check failed! Type {name} has a field decl / field offset mismatch.", - name = self.name() - ); - let max_offset = offsets.iter().max().unwrap_or(&0); - let explict_size = self - .explict_size() - .unwrap_or_else(|| { - panic!( - "Explict offsets provided without explicit size. Type: {}", - self.name() - ) - }) - .get(); - assert!( - (*max_offset) < explict_size, - "name:{:?} max_offset:{max_offset} explict_size:{explict_size} offsets:{:?} fields:{:?}", - self.name(), - self.explicit_offsets().unwrap(), - self.fields() - ); - } - self.field_types() - .for_each(|tpe| assert_ne!(*tpe, Type::Void)); - } -} -impl From for Type { - fn from(val: ClassDef) -> Type { - Type::ClassRef(ClassRef::new::<&str, _>(None, val.name()).into()) - } -} -impl From<&ClassDef> for Type { - fn from(val: &ClassDef) -> Type { - Type::ClassRef(ClassRef::new::<&str, _>(None, val.name()).into()) - } -} -impl From for ClassRef { - fn from(val: ClassDef) -> ClassRef { - ClassRef::new::<&str, _>(None, val.name()) - } -} -impl From<&ClassDef> for ClassRef { - fn from(val: &ClassDef) -> ClassRef { - ClassRef::new::<&str, _>(None, val.name()) - } -} diff --git a/cilly/src/v2/asm_link.rs b/cilly/src/v2/asm_link.rs index fbfd0576..e0fb4033 100644 --- a/cilly/src/v2/asm_link.rs +++ b/cilly/src/v2/asm_link.rs @@ -21,7 +21,8 @@ impl Assembly { | Type::Bool | Type::Void | Type::PlatformObject - | Type::PlatformGeneric(_, _) => tpe, + | Type::PlatformGeneric(_, _) + | Type::SMIDVector(_) => tpe, Type::ClassRef(class_ref) => { Type::ClassRef(self.translate_class_ref(source, class_ref)) } diff --git a/cilly/src/v2/builtins/mod.rs b/cilly/src/v2/builtins/mod.rs index 161665fd..ec82a834 100644 --- a/cilly/src/v2/builtins/mod.rs +++ b/cilly/src/v2/builtins/mod.rs @@ -12,6 +12,8 @@ pub mod thread; pub use thread::*; pub mod int128; pub use int128::*; +pub mod simd; + pub fn insert_swap_at_generic(asm: &mut Assembly, patcher: &mut MissingMethodPatcher) { let name = asm.alloc_string("swap_at_generic"); let generator = move |_, asm: &mut Assembly| { diff --git a/cilly/src/v2/builtins/simd/binop.rs b/cilly/src/v2/builtins/simd/binop.rs new file mode 100644 index 00000000..324db361 --- /dev/null +++ b/cilly/src/v2/builtins/simd/binop.rs @@ -0,0 +1,52 @@ +use crate::{ + v2::asm::MissingMethodPatcher, Assembly, BasicBlock, CILNode, CILRoot, MethodImpl, + MethodRefIdx, Type, +}; +macro_rules! binop { + ($op_name:ident,$op_dotnet:literal) => { + pub fn $op_name(asm: &mut Assembly, patcher: &mut MissingMethodPatcher) { + let name = asm.alloc_string(stringify!($op_name)); + let generator = move |mref: MethodRefIdx, asm: &mut Assembly| { + let sig = asm[asm[mref].sig()].clone(); + + let Some(comparands) = sig.inputs()[0].as_simdvector() else { + let name = stringify!($op_name); + todo!("Can't {name} {comparands:?} ", comparands = sig.inputs()[0]) + }; + let elem: Type = comparands.elem().into(); + + let extension_class = comparands.extension_class(asm); + let extension_class = asm[extension_class].clone(); + let equals = asm.alloc_string($op_dotnet); + // Generic vec + let generic_class = comparands.class(asm); + let mut generic_class = asm[generic_class].clone(); + generic_class.set_generics(vec![Type::PlatformGeneric( + 0, + crate::tpe::GenericKind::CallGeneric, + )]); + let generic_class = asm.alloc_class_ref(generic_class); + let equals = extension_class.static_mref_generic( + &[Type::ClassRef(generic_class), Type::ClassRef(generic_class)], + Type::ClassRef(generic_class), + equals, + asm, + [elem].into(), + ); + let lhs = asm.alloc_node(CILNode::LdArg(0)); + let rhs = asm.alloc_node(CILNode::LdArg(1)); + let res = asm.alloc_node(CILNode::Call(Box::new((equals, [lhs, rhs].into())))); + + let ret = asm.alloc_root(CILRoot::Ret(res)); + MethodImpl::MethodBody { + blocks: vec![BasicBlock::new(vec![ret], 0, None)], + locals: vec![], + } + }; + patcher.insert(name, Box::new(generator)); + } + }; +} +binop!(simd_or, "BitwiseOr"); +binop!(simd_add, "Add"); +binop!(simd_sub, "Subtract"); diff --git a/cilly/src/v2/builtins/simd/eq.rs b/cilly/src/v2/builtins/simd/eq.rs new file mode 100644 index 00000000..54506c78 --- /dev/null +++ b/cilly/src/v2/builtins/simd/eq.rs @@ -0,0 +1,137 @@ +use crate::{ + v2::asm::MissingMethodPatcher, Assembly, BasicBlock, CILNode, CILRoot, MethodImpl, + MethodRefIdx, Type, +}; + +use super::dotnet_vec_cast; +pub(super) fn simd_eq(asm: &mut Assembly, patcher: &mut MissingMethodPatcher) { + let name = asm.alloc_string("simd_eq"); + let generator = move |mref: MethodRefIdx, asm: &mut Assembly| { + let sig = asm[asm[mref].sig()].clone(); + let result = sig.output(); + let Some(comparands) = sig.inputs()[0].as_simdvector() else { + todo!( + "Can't simd compare {comparands:?} and get {result:?}", + comparands = sig.inputs()[0] + ) + }; + let elem: Type = comparands.elem().into(); + let Some(result) = result.as_simdvector() else { + todo!("Can't simd compare {comparands:?} and get {result:?}",) + }; + let extension_class = comparands.extension_class(asm); + let extension_class = asm[extension_class].clone(); + let equals = asm.alloc_string("Equals"); + // Generic vec + let generic_class = comparands.class(asm); + let mut generic_class = asm[generic_class].clone(); + generic_class.set_generics(vec![Type::PlatformGeneric( + 0, + crate::tpe::GenericKind::CallGeneric, + )]); + let generic_class = asm.alloc_class_ref(generic_class); + let equals = extension_class.static_mref_generic( + &[Type::ClassRef(generic_class), Type::ClassRef(generic_class)], + Type::ClassRef(generic_class), + equals, + asm, + [elem].into(), + ); + let lhs = asm.alloc_node(CILNode::LdArg(0)); + let rhs = asm.alloc_node(CILNode::LdArg(1)); + let equals = asm.alloc_node(CILNode::Call(Box::new((equals, [lhs, rhs].into())))); + let cast = dotnet_vec_cast(equals, *comparands, *result, asm); + let ret = asm.alloc_root(CILRoot::Ret(cast)); + MethodImpl::MethodBody { + blocks: vec![BasicBlock::new(vec![ret], 0, None)], + locals: vec![], + } + }; + patcher.insert(name, Box::new(generator)); +} +pub(super) fn simd_eq_all(asm: &mut Assembly, patcher: &mut MissingMethodPatcher) { + let name = asm.alloc_string("simd_eq_all"); + let generator = move |mref: MethodRefIdx, asm: &mut Assembly| { + let sig = asm[asm[mref].sig()].clone(); + let result = sig.output(); + let Some(comparands) = sig.inputs()[0].as_simdvector() else { + todo!( + "Can't simd compare {comparands:?} and get {result:?}", + comparands = sig.inputs()[0] + ) + }; + let elem: Type = comparands.elem().into(); + + let extension_class = comparands.extension_class(asm); + let extension_class = asm[extension_class].clone(); + let equals = asm.alloc_string("EqualsAll"); + // Generic vec + let generic_class = comparands.class(asm); + let mut generic_class = asm[generic_class].clone(); + generic_class.set_generics(vec![Type::PlatformGeneric( + 0, + crate::tpe::GenericKind::CallGeneric, + )]); + let generic_class = asm.alloc_class_ref(generic_class); + let equals = extension_class.static_mref_generic( + &[Type::ClassRef(generic_class), Type::ClassRef(generic_class)], + Type::Bool, + equals, + asm, + [elem].into(), + ); + let lhs = asm.alloc_node(CILNode::LdArg(0)); + let rhs = asm.alloc_node(CILNode::LdArg(1)); + let equals = asm.alloc_node(CILNode::Call(Box::new((equals, [lhs, rhs].into())))); + + let ret = asm.alloc_root(CILRoot::Ret(equals)); + MethodImpl::MethodBody { + blocks: vec![BasicBlock::new(vec![ret], 0, None)], + locals: vec![], + } + }; + patcher.insert(name, Box::new(generator)); +} +pub(super) fn simd_eq_any(asm: &mut Assembly, patcher: &mut MissingMethodPatcher) { + let name = asm.alloc_string("simd_eq_any"); + let generator = move |mref: MethodRefIdx, asm: &mut Assembly| { + let sig = asm[asm[mref].sig()].clone(); + let result = sig.output(); + let Some(comparands) = sig.inputs()[0].as_simdvector() else { + todo!( + "Can't simd compare {comparands:?} and get {result:?}", + comparands = sig.inputs()[0] + ) + }; + let elem: Type = comparands.elem().into(); + + let extension_class = comparands.extension_class(asm); + let extension_class = asm[extension_class].clone(); + let equals = asm.alloc_string("EqualsAny"); + // Generic vec + let generic_class = comparands.class(asm); + let mut generic_class = asm[generic_class].clone(); + generic_class.set_generics(vec![Type::PlatformGeneric( + 0, + crate::tpe::GenericKind::CallGeneric, + )]); + let generic_class = asm.alloc_class_ref(generic_class); + let equals = extension_class.static_mref_generic( + &[Type::ClassRef(generic_class), Type::ClassRef(generic_class)], + Type::Bool, + equals, + asm, + [elem].into(), + ); + let lhs = asm.alloc_node(CILNode::LdArg(0)); + let rhs = asm.alloc_node(CILNode::LdArg(1)); + let equals = asm.alloc_node(CILNode::Call(Box::new((equals, [lhs, rhs].into())))); + + let ret = asm.alloc_root(CILRoot::Ret(equals)); + MethodImpl::MethodBody { + blocks: vec![BasicBlock::new(vec![ret], 0, None)], + locals: vec![], + } + }; + patcher.insert(name, Box::new(generator)); +} diff --git a/cilly/src/v2/builtins/simd/mod.rs b/cilly/src/v2/builtins/simd/mod.rs new file mode 100644 index 00000000..8946cabc --- /dev/null +++ b/cilly/src/v2/builtins/simd/mod.rs @@ -0,0 +1,135 @@ +use crate::{ + tpe::simd::SIMDVector, v2::asm::MissingMethodPatcher, Assembly, BasicBlock, BranchCond, + CILNode, CILRoot, MethodImpl, MethodRef, MethodRefIdx, NodeIdx, Type, +}; +mod eq; +use eq::*; +mod binop; +use binop::*; +fn dotnet_vec_cast( + src: NodeIdx, + src_type: SIMDVector, + target_type: SIMDVector, + asm: &mut Assembly, +) -> NodeIdx { + if src_type == target_type { + return src; + } + todo!(); +} + +fn simd_ones_compliment(asm: &mut Assembly, patcher: &mut MissingMethodPatcher) { + let name: crate::StringIdx = asm.alloc_string("simd_ones_compliment"); + let generator = move |mref: MethodRefIdx, asm: &mut Assembly| { + let sig = asm[asm[mref].sig()].clone(); + + let Some(vec_type) = sig.inputs()[0].as_simdvector() else { + todo!( + "Can't calc the ones compliment of {vec_type:?}", + vec_type = sig.inputs()[0] + ) + }; + let elem: Type = vec_type.elem().into(); + let extension_class = vec_type.extension_class(asm); + let extension_class = asm[extension_class].clone(); + let ones_compliment = asm.alloc_string("OnesComplement"); + // Generic vec + let generic_class = vec_type.class(asm); + let mut generic_class = asm[generic_class].clone(); + generic_class.set_generics(vec![Type::PlatformGeneric( + 0, + crate::tpe::GenericKind::CallGeneric, + )]); + let generic_class = asm.alloc_class_ref(generic_class); + let ones_compliment = extension_class.static_mref_generic( + &[Type::ClassRef(generic_class)], + Type::ClassRef(generic_class), + ones_compliment, + asm, + [elem].into(), + ); + let val = asm.alloc_node(CILNode::LdArg(0)); + let res = asm.alloc_node(CILNode::Call(Box::new((ones_compliment, [val].into())))); + let ret = asm.alloc_root(CILRoot::Ret(res)); + MethodImpl::MethodBody { + blocks: vec![BasicBlock::new(vec![ret], 0, None)], + locals: vec![], + } + }; + patcher.insert(name, Box::new(generator)); +} +fn simd_shuffle(asm: &mut Assembly, patcher: &mut MissingMethodPatcher) { + let name: crate::StringIdx = asm.alloc_string("simd_shuffle"); + let generator = move |mref: MethodRefIdx, asm: &mut Assembly| { + /* */ + todo!("simd_shuffle not supported yet!"); + MethodImpl::MethodBody { + blocks: vec![BasicBlock::new(vec![], 0, None)], + locals: vec![], + } + }; + patcher.insert(name, Box::new(generator)); +} +fn simd_vec_from_val(asm: &mut Assembly, patcher: &mut MissingMethodPatcher) { + let name: crate::StringIdx = asm.alloc_string("simd_vec_from_val"); + let generator = move |mref: MethodRefIdx, asm: &mut Assembly| { + let sig = asm[asm[mref].sig()].clone(); + let Some(vec_type) = sig.output().as_simdvector() else { + todo!( + "Can't simd_vec_from_val {vec_type:?}", + vec_type = sig.output() + ) + }; + let extension_class = vec_type.extension_class(asm); + let extension_class = asm[extension_class].clone(); + let create = asm.alloc_string("Create"); + let create = extension_class.static_mref(&[sig.inputs()[0]], *sig.output(), create, asm); + let val = asm.alloc_node(CILNode::LdArg(0)); + let res = asm.alloc_node(CILNode::Call(Box::new((create, [val].into())))); + let ret = asm.alloc_root(CILRoot::Ret(res)); + MethodImpl::MethodBody { + blocks: vec![BasicBlock::new(vec![ret], 0, None)], + locals: vec![], + } + }; + patcher.insert(name, Box::new(generator)); +} +fn simd_allset(asm: &mut Assembly, patcher: &mut MissingMethodPatcher) { + let name: crate::StringIdx = asm.alloc_string("simd_allset"); + let generator = move |mref: MethodRefIdx, asm: &mut Assembly| { + let sig = asm[asm[mref].sig()].clone(); + let Some(vec_type) = sig.output().as_simdvector() else { + todo!("Can't simd_allset {vec_type:?}", vec_type = sig.output()) + }; + let class = vec_type.class(asm); + let class = asm[class].clone(); + let generic_class = vec_type.class(asm); + let mut generic_class = asm[generic_class].clone(); + generic_class.set_generics(vec![Type::PlatformGeneric( + 0, + crate::tpe::GenericKind::TypeGeneric, + )]); + let generic_class = asm.alloc_class_ref(generic_class); + let create = asm.alloc_string("get_AllBitsSet"); + let create = class.static_mref(&[], Type::ClassRef(generic_class), create, asm); + let res = asm.alloc_node(CILNode::Call(Box::new((create, [].into())))); + let ret = asm.alloc_root(CILRoot::Ret(res)); + MethodImpl::MethodBody { + blocks: vec![BasicBlock::new(vec![ret], 0, None)], + locals: vec![], + } + }; + patcher.insert(name, Box::new(generator)); +} + +pub fn simd(asm: &mut Assembly, patcher: &mut MissingMethodPatcher) { + simd_eq(asm, patcher); + simd_ones_compliment(asm, patcher); + simd_vec_from_val(asm, patcher); + simd_or(asm, patcher); + simd_add(asm, patcher); + simd_sub(asm, patcher); + simd_allset(asm, patcher); + simd_eq_all(asm, patcher); + simd_eq_any(asm, patcher); +} diff --git a/cilly/src/v2/c_exporter/mod.rs b/cilly/src/v2/c_exporter/mod.rs index fa3fb319..96293454 100644 --- a/cilly/src/v2/c_exporter/mod.rs +++ b/cilly/src/v2/c_exporter/mod.rs @@ -14,7 +14,6 @@ use super::{ bimap::IntoBiMapIndex, cilnode::{ExtendKind, PtrCastRes}, cilroot::BranchCond, - int, method::LocalDef, Assembly, BinOp, CILIter, CILIterElem, CILNode, CILRoot, ClassDefIdx, ClassRef, ClassRefIdx, Const, Exporter, Int, MethodDef, MethodRef, NodeIdx, RootIdx, SigIdx, Type, @@ -88,6 +87,7 @@ fn c_tpe(field_tpe: Type, asm: &Assembly) -> String { dims = "*".repeat(dims.get() as usize) ), Type::FnPtr(_) => "void*".into(), + Type::SMIDVector(_) => panic!("SMID is not supported in C"), } } fn mref_to_name(mref: &MethodRef, asm: &Assembly) -> String { diff --git a/cilly/src/v2/cillyir_exporter/mod.rs b/cilly/src/v2/cillyir_exporter/mod.rs index 02c49268..dbe40898 100644 --- a/cilly/src/v2/cillyir_exporter/mod.rs +++ b/cilly/src/v2/cillyir_exporter/mod.rs @@ -94,6 +94,7 @@ fn tpe_to(tpe: &Type, asm: &Assembly) -> String { | Type::Void => format!("{tpe:?}"), Type::PlatformArray { .. } => todo!(), Type::FnPtr(sig) => format!("Type::FnPtr({sig})", sig = sig_to(asm[*sig].clone(), asm)), + Type::SMIDVector(_) => panic!("SMID is not supported when dumping cilly IR"), } } fn sig_to(sig: FnSig, asm: &Assembly) -> String { diff --git a/cilly/src/v2/class.rs b/cilly/src/v2/class.rs index 6e2bffc6..e345af57 100644 --- a/cilly/src/v2/class.rs +++ b/cilly/src/v2/class.rs @@ -399,6 +399,25 @@ impl ClassRef { [].into(), )) } + /// Returns a reference to an static method of this class, with a given name. + pub fn static_mref_generic( + &self, + inputs: &[Type], + output: Type, + fn_name: StringIdx, + asm: &mut Assembly, + generics: Box<[Type]>, + ) -> MethodRefIdx { + let this = asm.alloc_class_ref(self.clone()); + let sig = asm.sig(inputs, output); + asm.alloc_methodref(MethodRef::new( + this, + fn_name, + sig, + super::cilnode::MethodKind::Static, + generics, + )) + } // Returns a `System.Collections.Concurrent.ConcurrentDictionary` of key,value pub fn concurent_dictionary(key: Type, value: Type, asm: &mut Assembly) -> ClassRefIdx { let name: StringIdx = @@ -412,6 +431,10 @@ impl ClassRef { let asm_name = Some(asm.alloc_string("System.Collections")); asm.alloc_class_ref(ClassRef::new(name, asm_name, false, [key, value].into())) } + + pub fn set_generics(&mut self, generics: Vec) { + self.generics = generics.into(); + } } #[derive(Hash, PartialEq, Eq, Clone, Debug, Serialize, Deserialize)] pub struct ClassDef { diff --git a/cilly/src/v2/il_exporter/mod.rs b/cilly/src/v2/il_exporter/mod.rs index 44e52c48..d724a634 100644 --- a/cilly/src/v2/il_exporter/mod.rs +++ b/cilly/src/v2/il_exporter/mod.rs @@ -1,4 +1,4 @@ -use crate::v2::MethodImpl; +use crate::{v2::MethodImpl, ClassRef}; use lazy_static::lazy_static; use std::{io::Write, path::Path}; @@ -6,8 +6,8 @@ use super::{ asm::{IlasmFlavour, ILASM_FLAVOUR, ILASM_PATH}, cilnode::{ExtendKind, UnOp}, cilroot::BranchCond, - int, method::LocalDef, + tpe::simd::{SIMDElem, SIMDVector}, Assembly, BinOp, CILIter, CILIterElem, CILNode, ClassRefIdx, Exporter, Int, NodeIdx, RootIdx, SigIdx, Type, }; @@ -427,9 +427,23 @@ impl ILExporter { .map(|tpe| non_void_type_il(tpe, asm)) .intersperse(",".to_owned()) .collect(); + let generic = if mref.generics().is_empty() { + "".to_string() + } else { + let generic_list: String = mref + .generics() + .iter() + .map(|tpe| type_il(tpe, asm)) + .intersperse(",".to_owned()) + .collect(); + format!("<{generic_list}>") + }; let name = &asm[mref.name()]; let class = class_ref(mref.class(), asm); - writeln!(out, "{call_op} {output} {class}::'{name}'({inputs})") + writeln!( + out, + "{call_op} {output} {class}::'{name}'{generic}({inputs})" + ) } CILNode::IntCast { input, @@ -527,43 +541,43 @@ impl ILExporter { (Type::Ref(_), true) => todo!(), (Type::Ref(_), false) => todo!(), (Type::Int(int), volitale) => match (int, volitale) { - (int::Int::U8, true) => writeln!(out, "volatile. ldind.u1"), - (int::Int::U8, false) => writeln!(out, "ldind.u1"), - (int::Int::U16, true) => writeln!(out, "volatile. ldind.u2"), - (int::Int::U16, false) => writeln!(out, "ldind.u2"), - (int::Int::U32, true) => writeln!(out, "volatile. ldind.u4"), - (int::Int::U32, false) => writeln!(out, "ldind.u4"), - (int::Int::U64, true) => writeln!(out, "volatile. ldind.u8"), - (int::Int::U64, false) => writeln!(out, "ldind.u8"), - (int::Int::U128, true) => writeln!( + (Int::U8, true) => writeln!(out, "volatile. ldind.u1"), + (Int::U8, false) => writeln!(out, "ldind.u1"), + (Int::U16, true) => writeln!(out, "volatile. ldind.u2"), + (Int::U16, false) => writeln!(out, "ldind.u2"), + (Int::U32, true) => writeln!(out, "volatile. ldind.u4"), + (Int::U32, false) => writeln!(out, "ldind.u4"), + (Int::U64, true) => writeln!(out, "volatile. ldind.u8"), + (Int::U64, false) => writeln!(out, "ldind.u8"), + (Int::U128, true) => writeln!( out, "volatile. ldobj valuetype [System.Runtime]System.UInt128" ), - (int::Int::U128, false) => { + (Int::U128, false) => { writeln!(out, "ldobj valuetype [System.Runtime]System.UInt128") } - (int::Int::USize, true) => writeln!(out, "volatile. ldind.i"), - (int::Int::USize, false) => writeln!(out, "ldind.i"), - (int::Int::I8, true) => writeln!(out, "volatile. ldind.i1"), - (int::Int::I8, false) => writeln!(out, "ldind.i1"), - (int::Int::I16, true) => writeln!(out, "volatile. ldind.i2"), - (int::Int::I16, false) => writeln!(out, "ldind.i2"), - (int::Int::I32, true) => writeln!(out, "volatile. ldind.i4"), - (int::Int::I32, false) => writeln!(out, "ldind.i4"), - (int::Int::I64, true) => writeln!(out, "volatile. ldind.i8"), - (int::Int::I64, false) => writeln!(out, "ldind.i8"), - (int::Int::I128, true) => writeln!( + (Int::USize, true) => writeln!(out, "volatile. ldind.i"), + (Int::USize, false) => writeln!(out, "ldind.i"), + (Int::I8, true) => writeln!(out, "volatile. ldind.i1"), + (Int::I8, false) => writeln!(out, "ldind.i1"), + (Int::I16, true) => writeln!(out, "volatile. ldind.i2"), + (Int::I16, false) => writeln!(out, "ldind.i2"), + (Int::I32, true) => writeln!(out, "volatile. ldind.i4"), + (Int::I32, false) => writeln!(out, "ldind.i4"), + (Int::I64, true) => writeln!(out, "volatile. ldind.i8"), + (Int::I64, false) => writeln!(out, "ldind.i8"), + (Int::I128, true) => writeln!( out, "volatile. ldobj valuetype [System.Runtime]System.Int128" ), - (int::Int::I128, false) => { + (Int::I128, false) => { writeln!( out, "volatile. ldobj valuetype [System.Runtime]System.Int128" ) } - (int::Int::ISize, true) => writeln!(out, "volatile. ldind.i"), - (int::Int::ISize, false) => writeln!(out, "ldind.i"), + (Int::ISize, true) => writeln!(out, "volatile. ldind.i"), + (Int::ISize, false) => writeln!(out, "ldind.i"), }, (Type::ClassRef(cref), true) => { writeln!(out, "volatile. ldobj {cref}", cref = class_ref(cref, asm)) @@ -608,6 +622,12 @@ impl ILExporter { (Type::PlatformArray { .. }, false) => writeln!(out, "ldind.ref"), (Type::FnPtr(_), true) => writeln!(out, "volatile. ldind.i"), (Type::FnPtr(_), false) => writeln!(out, "ldind.i"), + (Type::SMIDVector(_), true) => { + writeln!(out, "volatile. ldobj {}", type_il(&tpe, asm)) + } + (Type::SMIDVector(_), false) => { + writeln!(out, "ldobj {}", type_il(&tpe, asm)) + } } } CILNode::SizeOf(tpe) => { @@ -1014,6 +1034,7 @@ impl ILExporter { .collect(); let name = &asm[mref.name()]; let class = class_ref(mref.class(), asm); + writeln!( out, "{call_op} {output} {class}::'{name}'({inputs}) //mref:{:?}", @@ -1080,6 +1101,7 @@ impl ILExporter { Type::Void => writeln!(out, "pop pop ldstr \"Attempted to wrtie to a zero-sized type(void).\" newobj void [System.Runtime]System.Exception::.ctor(string) throw"), // TODO: forbid this, since this is NEVER valid. Type::PlatformArray { .. } => writeln!(out, "{is_volitale} stind.ref"), Type::FnPtr(_) => writeln!(out, "{is_volitale} stind.i"), + Type::SMIDVector(_)=>writeln!(out, "stobj {}", type_il(&tpe, asm)), } } super::CILRoot::InitBlk(blk) => { @@ -1297,6 +1319,18 @@ fn non_void_type_il(tpe: &Type, asm: &Assembly) -> String { } fn type_il(tpe: &Type, asm: &Assembly) -> String { match tpe { + Type::SMIDVector(simdvec) => { + let vec_bits = simdvec.bits(); + assert!( + vec_bits == 64 || vec_bits == 128 || vec_bits == 256 || vec_bits == 512, + "Unusported SIMD vector size" + ); + let elem = match simdvec.elem() { + SIMDElem::Int(int) => type_il(&Type::Int(int), asm), + SIMDElem::Float(float) => type_il(&Type::Float(float), asm), + }; + format!("valuetype [System.Runtime.Intrinsics]System.Runtime.Intrinsics.Vector{vec_bits}`1<{elem}>") + } Type::Ptr(inner) => format!("{}*", type_il(&asm[*inner], asm)), Type::Ref(inner) => format!("{}&", type_il(&asm[*inner], asm)), Type::Int(int) => match int { diff --git a/cilly/src/v2/java_exporter/mod.rs b/cilly/src/v2/java_exporter/mod.rs index a0ef751c..14dcd31a 100644 --- a/cilly/src/v2/java_exporter/mod.rs +++ b/cilly/src/v2/java_exporter/mod.rs @@ -5,8 +5,8 @@ use std::io::Write; use crate::v2::MethodImpl; use super::{ - cilroot::BranchCond, int, Assembly, CILIter, CILIterElem, CILNode, ClassRefIdx, Exporter, - NodeIdx, RootIdx, Type, + cilroot::BranchCond, Assembly, CILIter, CILIterElem, CILNode, ClassRefIdx, Exporter, NodeIdx, + RootIdx, Type, }; use lazy_static::lazy_static; lazy_static! { @@ -131,7 +131,7 @@ pub(crate) fn class_ref(cref: ClassRefIdx, asm: &Assembly) -> String { generics = cref .generics() .iter() - .map(|tpe| type_il(tpe, asm)) + .map(|tpe| type_string(*tpe, asm)) .intersperse(",".to_string()) .collect::() ) @@ -148,67 +148,6 @@ pub(crate) fn class_ref(cref: ClassRefIdx, asm: &Assembly) -> String { format!("{prefix} '{name}{generic_postfix}'{generic_list}") } } -fn non_void_type_il(tpe: &Type, asm: &Assembly) -> String { - match tpe { - Type::Void => "valuetype RustVoid".into(), - _ => type_il(tpe, asm), - } -} -fn type_il(tpe: &Type, asm: &Assembly) -> String { - match tpe { - Type::Ptr(inner) => format!("{}*", type_il(&asm[*inner], asm)), - Type::Ref(inner) => format!("{}&", type_il(&asm[*inner], asm)), - Type::Int(int) => match int { - super::Int::U8 => "uint8".into(), - super::Int::U16 => "uint16".into(), - super::Int::U32 => "uint32".into(), - super::Int::U64 => "uint64".into(), - super::Int::U128 => "valuetype [System.Runtime]System.UInt128".into(), - super::Int::USize => "native uint".into(), - super::Int::I8 => "int8".into(), - super::Int::I16 => "int16".into(), - super::Int::I32 => "int32".into(), - super::Int::I64 => "int64".into(), - super::Int::I128 => "valuetype [System.Runtime]System.Int128".into(), - super::Int::ISize => "native int".into(), - }, - Type::ClassRef(cref) => class_ref(*cref, asm), - Type::Float(float) => match float { - super::Float::F16 => todo!(), - super::Float::F32 => "float32".into(), - super::Float::F64 => "float64".into(), - super::Float::F128 => todo!(), - }, - Type::PlatformChar => "char".into(), - Type::PlatformGeneric(arg, generic) => match generic { - super::tpe::GenericKind::MethodGeneric => todo!(), - super::tpe::GenericKind::CallGeneric => format!("!!{arg}"), - super::tpe::GenericKind::TypeGeneric => format!("!{arg}"), - }, - Type::Bool => "bool".into(), - Type::Void => "void".into(), - Type::PlatformArray { elem, dims } => format!( - "{elem}[{dims}]", - elem = type_il(&asm[*elem], asm), - dims = (1..(dims.get())).map(|_| ',').collect::() - ), - Type::FnPtr(sig) => { - let sig = &asm[*sig]; - format!( - "method {output}*({inputs})", - output = type_il(sig.output(), asm), - inputs = sig - .inputs() - .iter() - .map(|tpe| non_void_type_il(tpe, asm)) - .intersperse(",".to_string()) - .collect::(), - ) - } - Type::PlatformString => "string".into(), - Type::PlatformObject => "object".into(), - } -} fn type_string(tpe: Type, asm: &Assembly) -> String { match tpe { @@ -235,6 +174,7 @@ fn type_string(tpe: Type, asm: &Assembly) -> String { elem = type_string(asm[elem], asm) ), Type::FnPtr(_) => "J".into(), + Type::SMIDVector(_) => panic!("SMID is not supported in Java"), } } /* diff --git a/cilly/src/v2/mod.rs b/cilly/src/v2/mod.rs index 21b3ae65..535b0298 100644 --- a/cilly/src/v2/mod.rs +++ b/cilly/src/v2/mod.rs @@ -13,12 +13,12 @@ pub use cilroot::{BranchCond, CILRoot, RootIdx}; pub use class::{ClassDef, ClassDefIdx, ClassRef, ClassRefIdx}; pub use cst::Const; pub use field::{FieldDesc, FieldIdx, StaticFieldDesc, StaticFieldIdx}; -pub use float::Float; pub use fnsig::{FnSig, SigIdx}; -pub use int::Int; pub use iter::{CILIter, CILIterElem}; pub use method::{MethodDef, MethodDefIdx, MethodImpl, MethodRef, MethodRefIdx}; pub use strings::StringIdx; +pub use tpe::float::Float; +pub use tpe::int::Int; pub use tpe::{Type, TypeIdx}; pub mod access; @@ -35,12 +35,10 @@ pub mod cilroot; pub mod class; pub mod cst; pub mod field; -pub mod float; pub mod fnsig; /// Defines hashable and equable floating point types. All NaNs are compared by bits, and -0.0 != 0.0. pub mod hashable; pub mod il_exporter; -pub mod int; pub mod iter; pub mod java_exporter; pub mod method; diff --git a/cilly/src/v2/float.rs b/cilly/src/v2/tpe/float.rs similarity index 95% rename from cilly/src/v2/float.rs rename to cilly/src/v2/tpe/float.rs index 11ee4499..b67d9a5d 100644 --- a/cilly/src/v2/float.rs +++ b/cilly/src/v2/tpe/float.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use super::{ +use super::super::{ cilnode::MethodKind, hashable::{HashableF32, HashableF64}, Assembly, CILNode, ClassRef, ClassRefIdx, Const, MethodRef, NodeIdx, Type, @@ -115,6 +115,15 @@ impl Float { )); asm.alloc_node(CILNode::Call(Box::new((mref, Box::new([base, exp]))))) } + + pub fn bits(&self) -> u8 { + match self { + Float::F16 => 16, + Float::F32 => 32, + Float::F64 => 64, + Float::F128 => 128, + } + } } impl From for Type { fn from(value: Float) -> Self { diff --git a/cilly/src/v2/int.rs b/cilly/src/v2/tpe/int.rs similarity index 99% rename from cilly/src/v2/int.rs rename to cilly/src/v2/tpe/int.rs index 083bf2ec..f6a180bc 100644 --- a/cilly/src/v2/int.rs +++ b/cilly/src/v2/tpe/int.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use super::{ +use super::super::{ cilnode::MethodKind, Assembly, CILNode, ClassRef, ClassRefIdx, Const, MethodRef, Type, }; @@ -258,6 +258,10 @@ impl Int { Int::USize | Int::ISize => None, } } + + pub fn bits(&self) -> u8 { + self.size().unwrap() * 8 + } } #[test] fn is_signed() { diff --git a/cilly/src/v2/tpe.rs b/cilly/src/v2/tpe/mod.rs similarity index 93% rename from cilly/src/v2/tpe.rs rename to cilly/src/v2/tpe/mod.rs index 46bc32b9..575482c3 100644 --- a/cilly/src/v2/tpe.rs +++ b/cilly/src/v2/tpe/mod.rs @@ -1,12 +1,16 @@ use std::num::NonZeroU8; use serde::{Deserialize, Serialize}; +use simd::{SIMDElem, SIMDVector}; use super::{ bimap::{BiMapIndex, IntoBiMapIndex}, Assembly, ClassRefIdx, Float, Int, SigIdx, }; +pub mod float; +pub mod int; +pub mod simd; #[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] pub struct TypeIdx(BiMapIndex); impl IntoBiMapIndex for TypeIdx { @@ -33,6 +37,7 @@ pub enum Type { Void, PlatformArray { elem: TypeIdx, dims: NonZeroU8 }, FnPtr(SigIdx), + SMIDVector(SIMDVector), } #[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] pub enum GenericKind { @@ -56,7 +61,8 @@ impl Type { | Type::PlatformGeneric(_, _) | Type::PlatformObject | Type::Bool - | Type::Void => Box::new(std::iter::empty()), + | Type::Void + | Type::SMIDVector(_) => Box::new(std::iter::empty()), Type::FnPtr(sig) => Box::new( asm[*sig] .iter_types() @@ -78,12 +84,13 @@ impl Type { /// # use cilly::*; /// # use cilly::v2::Int; /// # let asm = cilly::v2::Assembly::default(); - /// assert_eq!(Type::PlatformString.mangle(&asm),"s"); + /// assert_eq!(Type::PlatformString.mangle(&asm),"st"); /// assert_eq!(Type::Int(Int::I128).mangle(&asm),"i16"); /// ``` #[must_use] pub fn mangle(&self, asm: &Assembly) -> String { match self { + Type::SMIDVector(val) => val.name(), Type::Ptr(inner) => format!("p{}", asm[*inner].mangle(asm)), Type::Ref(inner) => format!("r{}", asm[*inner].mangle(asm)), Type::Int(int) => match int { @@ -119,7 +126,7 @@ impl Type { Float::F64 => "f8".into(), Float::F128 => "f16".into(), }, - Type::PlatformString => "s".into(), + Type::PlatformString => "st".into(), Type::PlatformChar => "c".into(), Type::PlatformGeneric(_, _) => todo!(), Type::PlatformObject => "o".into(), @@ -230,4 +237,12 @@ impl Type { None } } + + pub fn as_simdvector(&self) -> Option<&SIMDVector> { + if let Self::SMIDVector(v) = self { + Some(v) + } else { + None + } + } } diff --git a/cilly/src/v2/tpe/simd.rs b/cilly/src/v2/tpe/simd.rs new file mode 100644 index 00000000..88c42d1d --- /dev/null +++ b/cilly/src/v2/tpe/simd.rs @@ -0,0 +1,88 @@ +use serde::{Deserialize, Serialize}; + +use crate::{ClassRef, ClassRefIdx, Float, Int}; + +use super::Type; +#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] +pub enum SIMDElem { + Int(Int), + Float(Float), +} +impl SIMDElem { + fn bits(&self) -> u8 { + match self { + SIMDElem::Int(int) => int.bits(), + SIMDElem::Float(float) => float.bits(), + } + } +} +impl Into for SIMDElem { + fn into(self) -> Type { + match self { + SIMDElem::Int(int) => Type::Int(int), + SIMDElem::Float(float) => Type::Float(float), + } + } +} +#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)] +pub struct SIMDVector { + elem: SIMDElem, + count: u8, +} +impl SIMDVector { + pub fn new(elem: SIMDElem, count: u8) -> Self { + let res = Self { elem, count }; + let bits = res.bits(); + assert!( + bits == 64 || bits == 128 || bits == 256 || bits == 512, + "A vec with {count} {elem:?} has the size of {bits}, which is not supported." + ); + res + } + + pub fn name(&self) -> String { + match self.elem { + SIMDElem::Int(elem) => { + format!("simd{elem}_{count}", elem = elem.name(), count = self.count) + } + SIMDElem::Float(elem) => { + format!("simd{elem}_{count}", elem = elem.name(), count = self.count) + } + } + } + + pub fn bits(&self) -> u16 { + self.elem.bits() as u16 * self.count as u16 + } + + pub fn elem(&self) -> SIMDElem { + self.elem + } + + pub fn class(&self, asm: &mut crate::Assembly) -> ClassRefIdx { + let elem = self.elem().into(); + let asm_name = asm.alloc_string("System.Runtime.Intrinsics"); + let name = asm.alloc_string(format!("System.Runtime.Intrinsics.Vector{}", self.bits())); + asm.alloc_class_ref(ClassRef::new(name, Some(asm_name), true, vec![elem].into())) + } + pub fn extension_class(&self, asm: &mut crate::Assembly) -> ClassRefIdx { + let asm_name = asm.alloc_string("System.Runtime.Intrinsics"); + let name = asm.alloc_string(format!("System.Runtime.Intrinsics.Vector{}", self.bits())); + asm.alloc_class_ref(ClassRef::new(name, Some(asm_name), false, vec![].into())) + } + + pub fn count(&self) -> u8 { + self.count + } +} +impl TryInto for Type { + type Error = (); + + fn try_into(self) -> Result { + match self { + Type::Int(int) => Ok(SIMDElem::Int(int)), + Type::Float(float) => Ok(SIMDElem::Float(float)), + _ => Err(()), + } + } +} diff --git a/src/compile_test.rs b/src/compile_test.rs index c297be9c..c2e4ebfb 100644 --- a/src/compile_test.rs +++ b/src/compile_test.rs @@ -767,6 +767,7 @@ run_test! {intrinsics,pow_sqrt,stable} run_test! {intrinsics,printf,stable} run_test! {intrinsics,ptr_offset_from_unsigned,stable} run_test! {intrinsics,round,stable} +run_test! {intrinsics,simd,stable} run_test! {intrinsics,size_of_val,stable} run_test! {intrinsics,transmute,stable} run_test! {intrinsics,trigonometry,stable} diff --git a/src/terminator/intrinsics/mod.rs b/src/terminator/intrinsics/mod.rs index ae3f9cbe..a9d041d2 100644 --- a/src/terminator/intrinsics/mod.rs +++ b/src/terminator/intrinsics/mod.rs @@ -1072,15 +1072,14 @@ pub fn handle_intrinsic<'tcx>( MethodKind::Static, vec![].into(), ); - let place_set = place_set( + place_set( destination, call!( ctx.alloc_methodref(log), [handle_operand(&args[0].node, ctx),] ), ctx, - ); - place_set + ) } "log2f32" => { let log = MethodRef::new( @@ -1643,6 +1642,163 @@ pub fn handle_intrinsic<'tcx>( ctx, ) } + "simd_eq" => { + let comparands = ctx.type_from_cache( + call_instance.args[0] + .as_type() + .expect("simd_eq works only on types!"), + ); + let result = ctx.type_from_cache( + call_instance.args[1] + .as_type() + .expect("simd_eq works only on types!"), + ); + let lhs = handle_operand(&args[0].node, ctx); + let rhs = handle_operand(&args[1].node, ctx); + let name = ctx.alloc_string("simd_eq"); + let main_module = ctx.main_module(); + let main_module = ctx[*main_module].clone(); + let eq = main_module.static_mref(&[comparands, comparands], result, name, ctx); + place_set(destination, call!(eq, [lhs, rhs]), ctx) + } + "simd_or" => { + let vec = ctx.type_from_cache( + call_instance.args[0] + .as_type() + .expect("simd_or works only on types!"), + ); + + let lhs = handle_operand(&args[0].node, ctx); + let rhs = handle_operand(&args[1].node, ctx); + let name = ctx.alloc_string("simd_or"); + let main_module = ctx.main_module(); + let main_module = ctx[*main_module].clone(); + let eq = main_module.static_mref(&[vec, vec], vec, name, ctx); + place_set(destination, call!(eq, [lhs, rhs]), ctx) + } + "simd_add" => { + let vec = ctx.type_from_cache( + call_instance.args[0] + .as_type() + .expect("simd_add works only on types!"), + ); + + let lhs = handle_operand(&args[0].node, ctx); + let rhs = handle_operand(&args[1].node, ctx); + let name = ctx.alloc_string("simd_add"); + let main_module = ctx.main_module(); + let main_module = ctx[*main_module].clone(); + let eq = main_module.static_mref(&[vec, vec], vec, name, ctx); + place_set(destination, call!(eq, [lhs, rhs]), ctx) + } + "simd_sub" => { + let vec = ctx.type_from_cache( + call_instance.args[0] + .as_type() + .expect("simd_sub works only on types!"), + ); + + let lhs = handle_operand(&args[0].node, ctx); + let rhs = handle_operand(&args[1].node, ctx); + let name = ctx.alloc_string("simd_sub"); + let main_module = ctx.main_module(); + let main_module = ctx[*main_module].clone(); + let eq = main_module.static_mref(&[vec, vec], vec, name, ctx); + place_set(destination, call!(eq, [lhs, rhs]), ctx) + } + "simd_shuffle" => { + let t_type = ctx.type_from_cache( + call_instance.args[0] + .as_type() + .expect("simd_eq works only on types!"), + ); + let u_type = ctx.type_from_cache( + call_instance.args[1] + .as_type() + .expect("simd_eq works only on types!"), + ); + let v_type = ctx.type_from_cache( + call_instance.args[2] + .as_type() + .expect("simd_eq works only on types!"), + ); + let x = handle_operand(&args[0].node, ctx); + let y = handle_operand(&args[1].node, ctx); + // When the two vectors provided to simd shuffles are always the same, and have a length of 1(are scalar), the shuffle is equivalent to creating a vector [scalar,scalar]. + if x == y && matches!(t_type, Type::Int(_) | Type::Float(_)) { + let name = ctx.alloc_string("simd_vec_from_val"); + let main_module = ctx.main_module(); + let main_module = ctx[*main_module].clone(); + let shuffle = main_module.static_mref(&[t_type], v_type, name, ctx); + // SANITY: for this optimzation to work, the u(index vector) and v(result vector) both have to have be vectors. + let (_u_type, _v_type) = ( + u_type.as_simdvector().unwrap(), + v_type.as_simdvector().unwrap(), + ); + return place_set(destination, call!(shuffle, [x]), ctx); + } + let idx = handle_operand(&args[2].node, ctx); + let name = ctx.alloc_string("simd_shuffle"); + let main_module = ctx.main_module(); + let main_module = ctx[*main_module].clone(); + let shuffle = main_module.static_mref(&[t_type, t_type, u_type], v_type, name, ctx); + place_set(destination, call!(shuffle, [x, y, idx]), ctx) + } + "simd_ne" => { + let comparands = ctx.type_from_cache( + call_instance.args[0] + .as_type() + .expect("simd_eq works only on types!"), + ); + let result = ctx.type_from_cache( + call_instance.args[1] + .as_type() + .expect("simd_eq works only on types!"), + ); + let lhs = handle_operand(&args[0].node, ctx); + let rhs = handle_operand(&args[1].node, ctx); + let eq = ctx.alloc_string("simd_eq"); + let ones_compliment = ctx.alloc_string("simd_ones_compliment"); + let main_module = ctx.main_module(); + let main_module = ctx[*main_module].clone(); + let eq = main_module.static_mref(&[comparands, comparands], result, eq, ctx); + let eq = call!(eq, [lhs, rhs]); + let ones_compliment = main_module.static_mref(&[result], result, ones_compliment, ctx); + let ne = call!(ones_compliment, [eq]); + place_set(destination, ne, ctx) + } + "simd_reduce_any" => { + let vec = ctx.type_from_cache( + call_instance.args[0] + .as_type() + .expect("simd_eq works only on types!"), + ); + let x = handle_operand(&args[0].node, ctx); + let simd_eq = ctx.alloc_string("simd_eq_any"); + let allset = ctx.alloc_string("simd_allset"); + let main_module = ctx.main_module(); + let main_module = ctx[*main_module].clone(); + let eq = main_module.static_mref(&[vec, vec], Type::Bool, simd_eq, ctx); + let allset = main_module.static_mref(&[], vec, allset, ctx); + let allset = call!(allset, []); + place_set(destination, call!(eq, [x, allset]), ctx) + } + "simd_reduce_all" => { + let vec = ctx.type_from_cache( + call_instance.args[0] + .as_type() + .expect("simd_eq works only on types!"), + ); + let x = handle_operand(&args[0].node, ctx); + let simd_eq = ctx.alloc_string("simd_eq_all"); + let allset = ctx.alloc_string("simd_allset"); + let main_module = ctx.main_module(); + let main_module = ctx[*main_module].clone(); + let eq = main_module.static_mref(&[vec, vec], Type::Bool, simd_eq, ctx); + let allset = main_module.static_mref(&[], vec, allset, ctx); + let allset = call!(allset, []); + place_set(destination, call!(eq, [x, allset]), ctx) + } _ => intrinsic_slow(fn_name, args, destination, ctx, call_instance, span), } } diff --git a/src/type/mod.rs b/src/type/mod.rs index d972d8b5..89505834 100644 --- a/src/type/mod.rs +++ b/src/type/mod.rs @@ -7,9 +7,12 @@ use crate::{ fn_ctx::MethodCompileCtx, utilis::{adt::FieldOffsetIterator, garg_to_string}, }; -use cilly::v2::{ - cilnode::MethodKind, Access, BasicBlock, BinOp, CILNode, CILRoot, ClassDef, ClassDefIdx, - ClassRef, ClassRefIdx, Float, Int, MethodDef, MethodImpl, StringIdx, Type, +use cilly::{ + tpe::simd::SIMDVector, + v2::{ + cilnode::MethodKind, Access, BasicBlock, BinOp, CILNode, CILRoot, ClassDef, ClassDefIdx, + ClassRef, ClassRefIdx, Float, Int, MethodDef, MethodImpl, StringIdx, Type, + }, }; pub use r#type::*; use rustc_middle::ty::{AdtDef, AdtKind, FloatTy, IntTy, List, ParamEnv, Ty, TyKind, UintTy}; @@ -57,7 +60,6 @@ pub fn from_float(float: &FloatTy) -> cilly::Type { fn get_adt<'tcx>( adt_ty: Ty<'tcx>, def: AdtDef<'tcx>, - subst: &'tcx List>, name: StringIdx, ctx: &mut MethodCompileCtx<'tcx, '_>, @@ -195,6 +197,18 @@ pub fn get_type<'tcx>(ty: Ty<'tcx>, ctx: &mut MethodCompileCtx<'tcx, '_>) -> Typ } TyKind::Adt(def, subst) => { let name = crate::utilis::adt_name(*def, ctx.tcx(), subst); + if def.repr().simd() { + let (count, elem) = ty.simd_size_and_type(ctx.tcx()); + let elem = ctx.type_from_cache(elem); + // if count == 1, then this is just a single type. + if count == 1 { + return elem; + } + return Type::SMIDVector(SIMDVector::new( + elem.try_into().unwrap(), + count.try_into().unwrap(), + )); + } if is_name_magic(name.as_ref()) { if name.contains(INTEROP_CLASS_TPE_NAME) { assert!(