diff --git a/c2rust-ast-builder/src/builder.rs b/c2rust-ast-builder/src/builder.rs index 252339c988..40131c384b 100644 --- a/c2rust-ast-builder/src/builder.rs +++ b/c2rust-ast-builder/src/builder.rs @@ -1,6 +1,9 @@ //! Helpers for building AST nodes. Normally used by calling `mk().some_node(args...)`. -use std::str; +use std::{ + iter::{once, repeat}, + str, +}; use proc_macro2::{Span, TokenStream, TokenTree}; use std::default::Default; @@ -1693,6 +1696,62 @@ impl Builder { })) } + // `use ;` item + pub fn use_simple_item_rename(self, path: Pa, rename: Option) -> Box + where + Pa: Make, + I: Make, + Ip: Into<(I, RIt)>, + RIt: Iterator, + { + let path = path.make(&self); + + fn split_path(mut p: Path) -> (Path, Option) { + if let Some(punct) = p.segments.pop() { + (p, Some(punct.into_value().ident)) + } else { + (p, None) + } + } + let leading_colon = path.leading_colon; + let (prefix, ident) = split_path(path); + let ident = ident.expect("use_simple_item called with path `::`"); + + let inner_trees = if let Some(rename) = rename { + let (rename_ident, renames) = rename.into(); + let rename_ident = rename_ident.make(&self); + + once(UseTree::Name(UseName { ident })) + .chain(repeat(rename_ident).zip(renames).map(|(ident, rename)| { + UseTree::Rename(UseRename { + ident, + as_token: Token![as](self.span), + rename: rename.make(&self), + }) + })) + .collect() + } else { + once(UseTree::Name(UseName { ident })).collect() + }; + + let tree = use_tree_with_prefix( + prefix, + UseTree::Group(UseGroup { + brace_token: token::Brace(self.span), + items: inner_trees, + }), + ); + + Box::new(Item::Use(ItemUse { + attrs: self.attrs, + vis: self.vis, + use_token: Token![use](self.span), + leading_colon, + semi_token: Token![;](self.span), + tree, + })) + } + // `use ;` item pub fn use_simple_item(self, path: Pa, rename: Option) -> Box where @@ -1700,7 +1759,6 @@ impl Builder { I: Make, { let path = path.make(&self); - let rename = rename.map(|n| n.make(&self)); fn split_path(mut p: Path) -> (Path, Option) { if let Some(punct) = p.segments.pop() { @@ -1712,18 +1770,20 @@ impl Builder { let leading_colon = path.leading_colon; let (prefix, ident) = split_path(path); let ident = ident.expect("use_simple_item called with path `::`"); + let tree = if let Some(rename) = rename { use_tree_with_prefix( prefix, UseTree::Rename(UseRename { ident, as_token: Token![as](self.span), - rename, + rename: rename.make(&self), }), ) } else { use_tree_with_prefix(prefix, UseTree::Name(UseName { ident })) }; + Box::new(Item::Use(ItemUse { attrs: self.attrs, vis: self.vis, @@ -1766,6 +1826,56 @@ impl Builder { })) } + pub fn use_multiple_item_rename( + self, + path: Pa, + inner: It, + rename: RIt, + ) -> Box + where + Pa: Make, + I: Make, + Ip: Into<(I, It)>, + It: Iterator, + RIt: Iterator, + { + let path = path.make(&self); + let inner_trees = inner + .map(|i| { + UseTree::Name(UseName { + ident: i.make(&self), + }) + }) + .chain(rename.flat_map(|ip| { + let (ident, renames) = ip.into(); + let ident = ident.make(&self); + repeat(ident).zip(renames).map(|(ident, rename)| { + UseTree::Rename(UseRename { + ident: ident.make(&self), + as_token: Token![as](self.span), + rename: rename.make(&self), + }) + }) + })) + .collect(); + let leading_colon = path.leading_colon; + let tree = use_tree_with_prefix( + path, + UseTree::Group(UseGroup { + brace_token: token::Brace(self.span), + items: inner_trees, + }), + ); + Box::new(Item::Use(ItemUse { + attrs: self.attrs, + vis: self.vis, + use_token: Token![use](self.span), + leading_colon, + semi_token: Token![;](self.span), + tree, + })) + } + pub fn use_glob_item(self, path: Pa) -> Box where Pa: Make, @@ -1916,6 +2026,16 @@ impl Builder { } } + pub fn struct_field_anon(self, ty: Box) -> Field { + Field { + ident: None, + vis: self.vis, + attrs: self.attrs, + ty: *ty, + colon_token: None, + } + } + pub fn enum_field(self, ty: Box) -> Field { Field { ident: None, diff --git a/c2rust-transpile/src/rust_ast/item_store.rs b/c2rust-transpile/src/rust_ast/item_store.rs index c6a5399467..1a12b01cc8 100644 --- a/c2rust-transpile/src/rust_ast/item_store.rs +++ b/c2rust-transpile/src/rust_ast/item_store.rs @@ -1,6 +1,6 @@ use c2rust_ast_builder::{mk, Builder}; use indexmap::{IndexMap, IndexSet}; -use syn::{ForeignItem, Ident, Item}; +use syn::{ForeignItem, Item}; use std::borrow::Cow; use std::mem::swap; @@ -9,6 +9,7 @@ use std::mem::swap; pub struct MultiImport { attrs: Option, leaves: IndexSet, + renames: IndexMap>, } impl MultiImport { @@ -16,6 +17,7 @@ impl MultiImport { MultiImport { attrs: None, leaves: IndexSet::new(), + renames: IndexMap::new(), } } @@ -33,6 +35,35 @@ impl MultiImport { self.insert(leaf); self.attrs = Some(attrs); } + + pub fn insert_with_rename<'a, S>(&mut self, leaf: S, rename: S) + where + S: Into>, + { + let leaf: String = leaf.into().into_owned(); + let rename: String = rename.into().into_owned(); + if let Some(renames) = self.renames.get_mut(&leaf) { + renames.insert(rename); + } else { + let mut set = IndexSet::new(); + set.insert(rename); + self.renames.insert(leaf.clone(), set); + }; + + self.insert(leaf); + } + + pub fn insert_with_attr_rename<'a, S>(&mut self, leaf: S, attrs: Builder, rename: S) + where + S: Into>, + { + self.insert_with_rename(leaf, rename); + self.attrs = Some(attrs); + } + + pub fn insert_attrs(&mut self, attrs: Builder) { + self.attrs = Some(attrs); + } } #[derive(Debug, Default)] @@ -53,11 +84,25 @@ impl PathedMultiImports { let attrs = imports.attrs.unwrap_or_else(mk); if leaves.len() == 1 { - path.push(leaves.pop().unwrap()); + let leaf = leaves.pop().unwrap(); + path.push(leaf.clone()); - attrs.use_simple_item(path, None as Option) + let renames = imports + .renames + .get(&leaf) + .map(|r| Some((leaf.clone(), r.clone().into_iter()))) + .unwrap_or(None); + + attrs.use_simple_item_rename(path, renames) } else { - attrs.use_multiple_item(path, leaves.into_iter()) + attrs.use_multiple_item_rename( + path, + leaves.into_iter(), + imports + .renames + .iter() + .map(|(leaf, renames)| (leaf.clone(), renames.clone().into_iter())), + ) } } @@ -95,6 +140,22 @@ impl ItemStore { self.uses.get_mut(path).insert_with_attr(ident, attrs) } + pub fn add_use_with_rename(&mut self, path: Vec, ident: &str, rename: &str) { + self.uses.get_mut(path).insert_with_rename(ident, rename) + } + + pub fn add_use_with_attr_rename( + &mut self, + path: Vec, + ident: &str, + attrs: Builder, + rename: &str, + ) { + self.uses + .get_mut(path) + .insert_with_attr_rename(ident, attrs, rename) + } + pub fn drain(&mut self) -> (Vec>, Vec, PathedMultiImports) { let mut items = Vec::new(); let mut foreign_items = Vec::new(); diff --git a/c2rust-transpile/src/translator/builtins.rs b/c2rust-transpile/src/translator/builtins.rs index 60275f9867..5d0a4b84ef 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -441,6 +441,19 @@ impl<'c> Translation<'c> { "__builtin_ia32_pcmpestris128" => self.convert_simd_builtin(ctx, "_mm_cmpestrs", args), "__builtin_ia32_pcmpestriz128" => self.convert_simd_builtin(ctx, "_mm_cmpestrz", args), + "__builtin_ia32_vcvtph2ps" => self.convert_simd_builtin(ctx, "_mm_cvtph_ps", args), + "__builtin_ia32_vcvtps2ph256" => { + self.convert_simd_builtin(ctx, "_mm256_cvtps_ph", args) + } + "__builtin_ia32_vextractf128_ps256" => { + self.convert_simd_builtin(ctx, "_mm256_extractf128_ps", args) + } + "__builtin_ia32_vextractf128_si256" => { + self.convert_simd_builtin(ctx, "_mm256_extractf128_ps", args) + } + "__builtin_ia32_roundps256" => self.convert_simd_builtin(ctx, "_mm256_round_ps", args), + "__builtin_ia32_vcvtps2ph" => self.convert_simd_builtin(ctx, "_mm_cvtps_ph", args), + "__sync_val_compare_and_swap_1" | "__sync_val_compare_and_swap_2" | "__sync_val_compare_and_swap_4" diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index d269e023f1..b971bd271f 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -3595,22 +3595,6 @@ impl<'c> Translation<'c> { (rhs, lhs, rhs_node) }; - let lhs_node_type = lhs_node - .get_type() - .ok_or_else(|| format_err!("lhs node bad type"))?; - if self - .ast_context - .resolve_type(lhs_node_type) - .kind - .is_vector() - { - return Err(TranslationError::new( - self.ast_context.display_loc(src_loc), - err_msg("Attempting to index a vector type") - .context(TranslationErrorKind::OldLLVMSimd), - )); - } - let rhs = self.convert_expr(ctx.used(), *rhs)?; rhs.and_then(|rhs| { let simple_index_array = if ctx.needs_address() { @@ -3680,6 +3664,40 @@ impl<'c> Translation<'c> { mk().index_expr(lhs, cast_int(rhs, "usize", false)) } })) + } else if lhs_node_kind.is_vector() { + // LHS is a vector type, we just need to do a transmute to an array and + // take the type + match lhs_node_kind { + CTypeKind::Vector(vkind, _vsize) => { + let vector_kind_size_of = + self.compute_size_of_type(ctx, vkind.ctype)?; + let vector_ty = self.convert_type(vkind.ctype)?; + + let lhs = self.convert_expr(ctx.used(), *lhs)?; + let lhs_type = lhs_node + .get_type() + .ok_or_else(|| format_err!("bad lhs type"))?; + let lhs_type_size_of = self.compute_size_of_type(ctx, lhs_type)?; + + Ok(lhs.map(|lhs| { + // Array size is vector_kind_size (e.g. size_of::<__mm256>()) / element size (e.g. size_of::()) + let array_ty = mk().array_ty( + vector_ty, + mk().binary_expr( + BinOp::Div(Default::default()), + // mk().lit_expr(mk().int_lit(*size as u128, "usize")), + lhs_type_size_of.to_expr(), + vector_kind_size_of.to_expr(), + ), + ); + mk().unsafe_().index_expr( + transmute_expr(mk().infer_ty(), array_ty, lhs), + cast_int(rhs, "usize", false), + ) + })) + } + _ => unreachable!(), + } } else { // LHS must be ref decayed for the offset method call's self param let lhs = self.convert_expr(ctx.used().decay_ref(), *lhs)?; @@ -3720,6 +3738,7 @@ impl<'c> Translation<'c> { })?, ) .map(|ty| &self.ast_context.resolve_type(ty.ctype).kind); + let is_variadic = match fn_ty { Some(CTypeKind::Function(_, _, is_variadic, _, _)) => *is_variadic, _ => false, @@ -3739,6 +3758,7 @@ impl<'c> Translation<'c> { return self.convert_builtin(ctx, fexp, args); } + // Function pointer call _ => { let callee = self.convert_expr(ctx.used(), func)?; diff --git a/c2rust-transpile/src/translator/simd.rs b/c2rust-transpile/src/translator/simd.rs index 0363b0ff66..a49dfe6fc5 100644 --- a/c2rust-transpile/src/translator/simd.rs +++ b/c2rust-transpile/src/translator/simd.rs @@ -11,20 +11,18 @@ use crate::c_ast::CastKind::{BitCast, IntegralCast}; /// As of rustc 1.29, rust is known to be missing some SIMD functions. /// See -static MISSING_SIMD_FUNCTIONS: [&str; 36] = [ +static MISSING_SIMD_FUNCTIONS: &[&str] = &[ "_mm_and_si64", "_mm_andnot_si64", "_mm_cmpeq_pi16", "_mm_cmpeq_pi32", "_mm_cmpeq_pi8", "_mm_cvtm64_si64", - "_mm_cvtph_ps", "_mm_cvtsi32_si64", "_mm_cvtsi64_m64", "_mm_cvtsi64_si32", "_mm_empty", "_mm_free", - "_mm_loadu_si64", "_mm_madd_pi16", "_mm_malloc", "_mm_mulhi_pi16", @@ -51,20 +49,54 @@ static MISSING_SIMD_FUNCTIONS: [&str; 36] = [ ]; static SIMD_X86_64_ONLY: &[&str] = &[ + "_mm_crc32_u64", + "_mm_cvti64_sd", + "_mm_cvti64_ss", + "_mm_cvt_roundi64_sd", + "_mm_cvt_roundi64_ss", + "_mm_cvt_roundsd_i64", + "_mm_cvt_roundsd_si64", + "_mm_cvt_roundsd_u64", + "_mm_cvt_roundsi64_sd", + "_mm_cvt_roundsi64_ss", + "_mm_cvt_roundss_i64", + "_mm_cvt_roundss_si64", + "_mm_cvt_roundss_u64", + "_mm_cvt_roundu64_sd", + "_mm_cvt_roundu64_ss", + "_mm_cvtsd_i64", "_mm_cvtsd_si64", + "_mm_cvtsd_si64x", + "_mm_cvtsd_u64", "_mm_cvtsi128_si64", "_mm_cvtsi128_si64x", "_mm_cvtsi64_sd", "_mm_cvtsi64_si128", "_mm_cvtsi64_ss", + "_mm_cvtsi64x_sd", + "_mm_cvtsi64x_si128", + "_mm_cvtss_i64", "_mm_cvtss_si64", + "_mm_cvtss_u64", + "_mm_cvtt_roundsd_i64", + "_mm_cvtt_roundsd_si64", + "_mm_cvtt_roundsd_u64", + "_mm_cvtt_roundss_i64", + "_mm_cvtt_roundss_si64", + "_mm_cvtt_roundss_u64", + "_mm_cvttsd_i64", "_mm_cvttsd_si64", "_mm_cvttsd_si64x", + "_mm_cvttsd_u64", + "_mm_cvttss_i64", "_mm_cvttss_si64", - "_mm_stream_si64", + "_mm_cvttss_u64", + "_mm_cvtu64_sd", + "_mm_cvtu64_ss", "_mm_extract_epi64", "_mm_insert_epi64", - "_mm_crc32_u64", + "_mm_stream_si64", + "_mm_tzcnt_64", ]; fn add_arch_use(store: &mut ItemStore, arch_name: &str, item_name: &str) { @@ -84,6 +116,24 @@ fn add_arch_use(store: &mut ItemStore, arch_name: &str, item_name: &str) { ); } +fn add_arch_use_rename(store: &mut ItemStore, arch_name: &str, item_name: &str, rename: &str) { + store.add_use_with_attr_rename( + vec!["core".into(), "arch".into(), arch_name.into()], + item_name, + mk().meta_item_attr( + AttrStyle::Outer, + mk().meta_list( + "cfg", + vec![NestedMeta::Meta( + mk().meta_namevalue("target_arch", arch_name), + )], + ), + ) + .pub_(), + rename, + ); +} + impl<'c> Translation<'c> { /// Given the name of a typedef check if its one of the SIMD types. /// This function returns `true` when the name of the type is one that @@ -107,6 +157,22 @@ impl<'c> Translation<'c> { true } + "__m128_u" | "__m128i_u" | "__m128d_u" | "__m256_u" | "__m256i_u" | "__m256d_u" => { + // Rust doesn't have unaligned SIMD types, but it's not incorrect to use an unaligned + // type instead, it's just slightly less efficient. We'll just use the aligned type + // and rename it to the unaligned type. + self.with_cur_file_item_store(|item_store| { + add_arch_use_rename(item_store, "x86", &name.replace("_u", ""), name); + add_arch_use_rename(item_store, "x86_64", &name.replace("_u", ""), name); + }); + + self.with_cur_file_item_store(|item_store| { + add_arch_use(item_store, "x86", &name.replace("_u", "")); + add_arch_use(item_store, "x86_64", &name.replace("_u", "")); + }); + + true + } // These seem to be C internal types only, and shouldn't need any explicit support. // See https://internals.rust-lang.org/t/getting-explicit-simd-on-stable-rust/4380/115 "__v1di" @@ -142,11 +208,57 @@ impl<'c> Translation<'c> { | "__v8su" | "__v16hu" | "__mm_loadh_pi_v2f32" - | "__mm_loadl_pi_v2f32" => true, + | "__mm_loadl_pi_v2f32" => self.generate_simd_type(name)?, _ => false, }) } + /// Given the name of a SIMD typedef that is valid but not a built in core Rust type, attempt + /// to generate a Rust type for it. + /// https://internals.rust-lang.org/t/getting-explicit-simd-on-stable-rust/4380?page=6 + pub fn generate_simd_type(&self, name: &str) -> TranslationResult { + let prefix = name + .chars() + .take_while(|c| !c.is_numeric()) + .collect::(); + let width = name + .split_at(prefix.len()) + .1 + .chars() + .take_while(|c| c.is_numeric()) + .collect::() + .parse::() + .unwrap(); + let elem_ty = name.split_at(prefix.len() + width.to_string().len()).1; + // Prefixes: q (8), h (16), s (32), d (64) + // Signedness: i (signed), u (unsigned), f (float) + let elem_width = match elem_ty { + "qi" | "qu" => 8, + "hi" | "hu" => 16, + "si" | "su" | "sf" => 32, + "di" | "du" | "df" => 64, + _ => return Err(format_err!("Unknown SIMD type: {}", name).into()), + }; + + // Suffix is either 'd' (for 64-bit fp), 'i' (for integral types) or '' (for 32-bit fp) + let suffix = match elem_ty { + "df" => "d", + "sf" => "", + _ => "i", + }; + + let conversion_ty_name = format!("__m{}{}", width * elem_width, suffix,); + + self.with_cur_file_item_store(|item_store| { + add_arch_use_rename(item_store, "x86", &conversion_ty_name, name); + add_arch_use_rename(item_store, "x86_64", &conversion_ty_name, name); + add_arch_use(item_store, "x86", &conversion_ty_name); + add_arch_use(item_store, "x86_64", &conversion_ty_name); + }); + + Ok(true) + } + /// Determine if a particular function name is an SIMD primitive. If so an appropriate /// use statement is generated, `true` is returned, and no further processing will need to be done. pub fn import_simd_function(&self, name: &str) -> TranslationResult { @@ -185,7 +297,7 @@ impl<'c> Translation<'c> { match self.ast_context[expr_id].kind { // For some reason there seems to be an incorrect implicit cast here to char // it's possible the builtin takes a char even though the function takes an int - ImplicitCast(_, expr_id, IntegralCast, _, _) => expr_id, + ImplicitCast(_, _, IntegralCast, _, _) => expr_id, // (internal)(external)(vector input) ExplicitCast(qty, _, BitCast, _, _) => { if let CTypeKind::Vector(..) = self.ast_context.resolve_type(qty.ctype).kind { @@ -224,6 +336,7 @@ impl<'c> Translation<'c> { let call = mk().call_expr(mk().ident_expr(fn_name), call_params); if ctx.is_used() { + // Get the ty of the return value of the call Ok(WithStmts::new_val(call)) } else { Ok(WithStmts::new( @@ -249,9 +362,9 @@ impl<'c> Translation<'c> { (Float, 8) => ("_mm256_setzero_ps", 32), (Double, 2) => ("_mm_setzero_pd", 16), (Double, 4) => ("_mm256_setzero_pd", 32), - (Char, 16) | (Int, 4) | (LongLong, 2) => ("_mm_setzero_si128", 16), - (Char, 32) | (Int, 8) | (LongLong, 4) => ("_mm256_setzero_si256", 32), - (Char, 8) | (Int, 2) | (LongLong, 1) => { + (Char, 16) | (Short, 8) | (Int, 4) | (LongLong, 2) => ("_mm_setzero_si128", 16), + (Char, 32) | (Short, 16) | (Int, 8) | (LongLong, 4) => ("_mm256_setzero_si256", 32), + (Char, 8) | (Short, 4) | (Int, 2) | (LongLong, 1) => { // __m64 is still unstable as of rust 1.29 self.use_feature("stdsimd");