diff --git a/library/stdarch/crates/stdarch-gen-arm/src/big_endian.rs b/library/stdarch/crates/stdarch-gen-arm/src/big_endian.rs new file mode 100644 index 000000000000..5bf1a720ea63 --- /dev/null +++ b/library/stdarch/crates/stdarch-gen-arm/src/big_endian.rs @@ -0,0 +1,221 @@ +use crate::expression::LetVariant; +use crate::wildstring::WildStringPart; +use crate::{ + expression::{Expression, IdentifierType}, + typekinds::*, + wildstring::WildString, +}; + +/// Simplifies creating a string that can be used in an Expression, as Expression +/// expects all strings to be `WildString` +fn create_single_wild_string(name: &str) -> WildString { + WildString(vec![WildStringPart::String(name.to_string())]) +} + +/// Creates an Identifier with name `name` with no wildcards. This, for example, +/// can be used to create variables, function names or arbitrary input. Is is +/// extremely flexible. +pub fn create_symbol_identifier(arbitrary_string: &str) -> Expression { + let identifier_name = create_single_wild_string(arbitrary_string); + Expression::Identifier(identifier_name, IdentifierType::Symbol) +} + +/// To compose the simd_shuffle! call we need: +/// - simd_shuffle!(, , ) +/// +/// Here we are creating a string version of the `` that can be used as an +/// Expression Identifier +/// +/// In textual form `a: int32x4_t` which has 4 lanes would generate: +/// ``` +/// [0, 1, 2, 3] +/// ``` +fn create_array(lanes: u32, reverse: bool) -> Option { + if reverse { + match lanes { + 1 => None, /* Makes no sense to shuffle an array of size 1 */ + 2 => Some("[1, 0]".to_string()), + 3 => Some("[2, 1, 0]".to_string()), + 4 => Some("[3, 2, 1, 0]".to_string()), + 8 => Some("[7, 6, 5, 4, 3, 2, 1, 0]".to_string()), + 16 => Some("[15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]".to_string()), + _ => panic!("Incorrect vector number of vector lanes: {}", lanes), + } + } else { + match lanes { + 1 => None, /* Makes no sense to shuffle an array of size 1 */ + 2 => Some("[0, 1]".to_string()), + 3 => Some("[0, 1, 2]".to_string()), + 4 => Some("[0, 1, 2, 3]".to_string()), + 8 => Some("[0, 1, 2, 3, 4, 5, 6, 7]".to_string()), + 16 => Some("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]".to_string()), + _ => panic!("Incorrect vector number of vector lanes: {}", lanes), + } + } +} + +/// Creates: `let : = ` +pub fn create_let_variable( + variable_name: &str, + type_kind: &TypeKind, + expression: Expression, +) -> Expression { + let identifier_name = create_single_wild_string(variable_name); + Expression::Let(LetVariant::WithType( + identifier_name, + type_kind.clone(), + Box::new(expression), + )) +} + +pub fn create_mut_let_variable( + variable_name: &str, + type_kind: &TypeKind, + expression: Expression, +) -> Expression { + let identifier_name = create_single_wild_string(variable_name); + Expression::Let(LetVariant::MutWithType( + identifier_name, + type_kind.clone(), + Box::new(expression), + )) +} + +pub fn type_has_tuple(type_kind: &TypeKind) -> bool { + if let TypeKind::Vector(vector_type) = type_kind { + vector_type.tuple_size().is_some() + } else { + false + } +} + +pub fn make_variable_mutable(variable_name: &str, type_kind: &TypeKind) -> Expression { + let mut_variable = format!( + "let mut {}: {} = {}", + variable_name, + type_kind.to_string(), + variable_name + ); + let identifier_name = create_single_wild_string(&mut_variable); + Expression::Identifier(identifier_name, IdentifierType::Symbol) +} + +/// For creating shuffle calls, accepts function pointers for formatting for tuple +/// types and types without a tuple +/// +/// Example: +/// +/// `a: int32x4_t` with formatting function `create_shuffle_call_fmt` creates: +/// ``` +/// simd_shuffle!(a, a, [0, 1, 2, 3]) +/// ``` +/// +/// `a: int32x4x2_t` creates: +/// ``` +/// a.0 = simd_shuffle!(a.0, a.0, [0, 1, 2, 3]) +/// a.1 = simd_shuffle!(a.1, a.1, [0, 1, 2, 3]) +/// ``` +fn create_shuffle_internal( + variable_name: &String, + type_kind: &TypeKind, + reverse: bool, + fmt_tuple: fn(variable_name: &String, idx: u32, array_lanes: &String) -> String, + fmt: fn(variable_name: &String, type_kind: &TypeKind, array_lanes: &String) -> String, +) -> Option { + let TypeKind::Vector(vector_type) = type_kind else { + return None; + }; + + let lane_count = vector_type.lanes(); + let Some(array_lanes) = create_array(lane_count, reverse) else { + return None; + }; + + let tuple_count = vector_type.tuple_size().map_or_else(|| 0, |t| t.to_int()); + + if tuple_count > 0 { + let capacity_estimate: usize = + tuple_count as usize * (lane_count as usize + ((variable_name.len() + 2) * 3)); + let mut string_builder = String::with_capacity(capacity_estimate); + + /* .idx = simd_shuffle!(.idx, .idx, []) */ + for idx in 0..tuple_count { + let formatted = fmt_tuple(variable_name, idx, &array_lanes); + string_builder += formatted.as_str(); + } + Some(create_symbol_identifier(&string_builder)) + } else { + /* Generate a list of shuffles for each tuple */ + let expression = fmt(variable_name, type_kind, &array_lanes); + Some(create_symbol_identifier(&expression)) + } +} + +fn create_assigned_tuple_shuffle_call_fmt( + variable_name: &String, + idx: u32, + array_lanes: &String, +) -> String { + format!( + "{variable_name}.{idx} = simd_shuffle!({variable_name}.{idx}, {variable_name}.{idx}, {array_lanes});\n", + variable_name = variable_name, + idx = idx, + array_lanes = array_lanes + ) +} + +fn create_assigned_shuffle_call_fmt( + variable_name: &String, + type_kind: &TypeKind, + array_lanes: &String, +) -> String { + format!( + "let {variable_name}: {type_kind} = simd_shuffle!({variable_name}, {variable_name}, {array_lanes})", + type_kind = type_kind.to_string(), + variable_name = variable_name, + array_lanes = array_lanes + ) +} + +fn create_shuffle_call_fmt( + variable_name: &String, + _type_kind: &TypeKind, + array_lanes: &String, +) -> String { + format!( + "simd_shuffle!({variable_name}, {variable_name}, {array_lanes})", + variable_name = variable_name, + array_lanes = array_lanes + ) +} + +/// Create a `simd_shuffle!(<...>, [...])` call, where the output is stored +/// in a variable named `variable_name` +pub fn create_assigned_shuffle_call( + variable_name: &String, + type_kind: &TypeKind, + reverse: bool, +) -> Option { + create_shuffle_internal( + variable_name, + type_kind, + reverse, + create_assigned_tuple_shuffle_call_fmt, + create_assigned_shuffle_call_fmt, + ) +} + +/// Create a `simd_shuffle!(<...>, [...])` call +pub fn create_shuffle_call( + variable_name: &String, + type_kind: &TypeKind, + reverse: bool, +) -> Option { + create_shuffle_internal( + variable_name, + type_kind, + reverse, + create_assigned_tuple_shuffle_call_fmt, + create_shuffle_call_fmt, + ) +} diff --git a/library/stdarch/crates/stdarch-gen-arm/src/context.rs b/library/stdarch/crates/stdarch-gen-arm/src/context.rs index aa29eda820d6..44b5208f3994 100644 --- a/library/stdarch/crates/stdarch-gen-arm/src/context.rs +++ b/library/stdarch/crates/stdarch-gen-arm/src/context.rs @@ -35,6 +35,10 @@ pub struct GlobalContext { pub arch_cfgs: Vec, #[serde(default)] pub uses_neon_types: bool, + + /// Should the yaml file automagically generate big endian shuffling + #[serde(default)] + pub auto_big_endian: Option, } /// Context of an intrinsic group diff --git a/library/stdarch/crates/stdarch-gen-arm/src/expression.rs b/library/stdarch/crates/stdarch-gen-arm/src/expression.rs index 4a572db3e8f6..b796bf675c18 100644 --- a/library/stdarch/crates/stdarch-gen-arm/src/expression.rs +++ b/library/stdarch/crates/stdarch-gen-arm/src/expression.rs @@ -9,6 +9,7 @@ use std::fmt; use std::str::FromStr; use crate::intrinsic::Intrinsic; +use crate::wildstring::WildStringPart; use crate::{ context::{self, Context, VariableType}, intrinsic::{Argument, LLVMLink, StaticDefinition}, @@ -29,6 +30,7 @@ pub enum IdentifierType { pub enum LetVariant { Basic(WildString, Box), WithType(WildString, TypeKind, Box), + MutWithType(WildString, TypeKind, Box), } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -155,9 +157,11 @@ impl Expression { cl_ptr_ex.pre_build(ctx)?; arg_exs.iter_mut().try_for_each(|ex| ex.pre_build(ctx)) } - Self::Let(LetVariant::Basic(_, ex) | LetVariant::WithType(_, _, ex)) => { - ex.pre_build(ctx) - } + Self::Let( + LetVariant::Basic(_, ex) + | LetVariant::WithType(_, _, ex) + | LetVariant::MutWithType(_, _, ex), + ) => ex.pre_build(ctx), Self::CastAs(ex, _) => ex.pre_build(ctx), Self::Multiply(lhs, rhs) | Self::Xor(lhs, rhs) => { lhs.pre_build(ctx)?; @@ -214,7 +218,8 @@ impl Expression { Self::Let(variant) => { let (var_name, ex, ty) = match variant { LetVariant::Basic(var_name, ex) => (var_name, ex, None), - LetVariant::WithType(var_name, ty, ex) => { + LetVariant::WithType(var_name, ty, ex) + | LetVariant::MutWithType(var_name, ty, ex) => { if let Some(w) = ty.wildcard() { ty.populate_wildcard(ctx.local.provide_type_wildcard(w)?)?; } @@ -285,9 +290,11 @@ impl Expression { // Nested structures that aren't inherently unsafe, but could contain other expressions // that might be. Self::Assign(_var, exp) => exp.requires_unsafe_wrapper(ctx_fn), - Self::Let(LetVariant::Basic(_, exp) | LetVariant::WithType(_, _, exp)) => { - exp.requires_unsafe_wrapper(ctx_fn) - } + Self::Let( + LetVariant::Basic(_, exp) + | LetVariant::WithType(_, _, exp) + | LetVariant::MutWithType(_, _, exp), + ) => exp.requires_unsafe_wrapper(ctx_fn), Self::Array(exps) => exps.iter().any(|exp| exp.requires_unsafe_wrapper(ctx_fn)), Self::Multiply(lhs, rhs) | Self::Xor(lhs, rhs) => { lhs.requires_unsafe_wrapper(ctx_fn) || rhs.requires_unsafe_wrapper(ctx_fn) @@ -330,6 +337,32 @@ impl Expression { } } } + + /// Determine if an expression is a `static_assert<...>` function call. + pub fn is_static_assert(&self) -> bool { + match self { + Expression::FnCall(fn_call) => match fn_call.0.as_ref() { + Expression::Identifier(wild_string, _) => { + if let WildStringPart::String(function_name) = &wild_string.0[0] { + function_name.starts_with("static_assert") + } else { + false + } + } + _ => panic!("Badly defined function call: {:?}", fn_call), + }, + _ => false, + } + } + + /// Determine if an espression is a LLVM binding + pub fn is_llvm_link(&self) -> bool { + if let Expression::LLVMLink(_) = self { + true + } else { + false + } + } } impl FromStr for Expression { @@ -422,6 +455,10 @@ impl ToTokens for Expression { let var_ident = format_ident!("{}", var_name.to_string()); tokens.append_all(quote! { let #var_ident: #ty = #exp }) } + Self::Let(LetVariant::MutWithType(var_name, ty, exp)) => { + let var_ident = format_ident!("{}", var_name.to_string()); + tokens.append_all(quote! { let mut #var_ident: #ty = #exp }) + } Self::Assign(var_name, exp) => { /* If we are dereferencing a variable to assign a value \ * the 'format_ident!' macro does not like the asterix */ diff --git a/library/stdarch/crates/stdarch-gen-arm/src/intrinsic.rs b/library/stdarch/crates/stdarch-gen-arm/src/intrinsic.rs index cabe58f9d608..0101423f1a1c 100644 --- a/library/stdarch/crates/stdarch-gen-arm/src/intrinsic.rs +++ b/library/stdarch/crates/stdarch-gen-arm/src/intrinsic.rs @@ -10,6 +10,10 @@ use std::ops::RangeInclusive; use std::str::FromStr; use crate::assert_instr::InstructionAssertionsForBaseType; +use crate::big_endian::{ + create_assigned_shuffle_call, create_let_variable, create_mut_let_variable, + create_shuffle_call, create_symbol_identifier, make_variable_mutable, type_has_tuple, +}; use crate::context::{GlobalContext, GroupContext}; use crate::input::{InputSet, InputSetEntry}; use crate::predicate_forms::{DontCareMethod, PredicateForm, PredicationMask, ZeroingMethod}; @@ -284,6 +288,7 @@ pub struct Signature { pub name: WildString, /// List of function arguments, leave unset or empty for no arguments pub arguments: Vec, + /// Function return type, leave unset for void pub return_type: Option, @@ -493,12 +498,14 @@ impl LLVMLink { let mut sig_name = ctx.local.signature.name.clone(); sig_name.prepend_str("_"); + let argv = self + .arguments + .clone() + .unwrap_or_else(|| ctx.local.signature.arguments.clone()); + let mut sig = Signature { name: sig_name, - arguments: self - .arguments - .clone() - .unwrap_or_else(|| ctx.local.signature.arguments.clone()), + arguments: argv, return_type: self .return_type .clone() @@ -905,6 +912,13 @@ pub struct Intrinsic { pub base_type: Option, /// Attributes for the function pub attr: Option>, + /// Big endian variant for composing, this gets populated internally + #[serde(skip)] + pub big_endian_compose: Vec, + /// Big endian sometimes needs the bits inverted from the default reverse + /// to work correctly + #[serde(default)] + pub big_endian_inverse: Option, } impl Intrinsic { @@ -1014,6 +1028,12 @@ impl Intrinsic { variant.post_build(&mut ctx)?; + /* If we should generate big endian we shall do so. It's possible + * we may not want to in some instances */ + if ctx.global.auto_big_endian.unwrap_or(false) { + self.generate_big_endian(&mut variant); + } + if let Some(n_variant_op) = ctx.local.n_variant_op().cloned() { variant.generate_n_variant(n_variant_op, &mut ctx) } else { @@ -1021,6 +1041,146 @@ impl Intrinsic { } } + /// Add a big endian implementation + fn generate_big_endian(&self, variant: &mut Intrinsic) { + /* We can't always blindly reverse the bits we sometimes need a + * different order - thus this allows us to have the ability to do so + * without having to play codegolf witht the yaml AST */ + let should_reverse = { + if let Some(should_reverse) = variant.big_endian_inverse { + should_reverse + } else if variant.compose.len() == 1 { + match &variant.compose[0] { + Expression::FnCall(fn_call) => fn_call.0.to_string() == "transmute", + _ => false, + } + } else { + false + } + }; + + let mut big_endian_expressions: Vec = Vec::new(); + + /* We cannot assign `a.0 = ` directly to a function parameter so + * need to make them mutable */ + for function_parameter in &variant.signature.arguments { + if type_has_tuple(&function_parameter.kind) { + /* We do not want to be creating a `mut` variant if the type + * has one lane. If it has one lane that means it does not need + * shuffling */ + if let TypeKind::Vector(vector_type) = &function_parameter.kind { + if vector_type.lanes() == 1 { + continue; + } + } + + let mutable_variable = make_variable_mutable( + &function_parameter.name.to_string(), + &function_parameter.kind, + ); + big_endian_expressions.push(mutable_variable); + } + } + + /* Possibly shuffle the vectors */ + for function_parameter in &variant.signature.arguments { + if let Some(shuffle_call) = create_assigned_shuffle_call( + &function_parameter.name.to_string(), + &function_parameter.kind, + should_reverse, + ) { + big_endian_expressions.push(shuffle_call); + } + } + + if !big_endian_expressions.is_empty() { + Vec::reserve( + &mut variant.big_endian_compose, + big_endian_expressions.len() + variant.compose.len(), + ); + let mut expression = &variant.compose[0]; + let needs_reordering = expression.is_static_assert() || expression.is_llvm_link(); + + /* We want to keep the asserts and llvm links at the start of + * the new big_endian_compose vector that we are creating */ + if needs_reordering { + let mut expression_idx = 0; + while expression.is_static_assert() || expression.is_llvm_link() { + /* Add static asserts and llvm links to the start of the + * vector */ + variant.big_endian_compose.push(expression.clone()); + expression_idx += 1; + expression = &variant.compose[expression_idx]; + } + + /* Add the big endian specific expressions */ + variant.big_endian_compose.extend(big_endian_expressions); + + /* Add the rest of the expressions */ + for i in expression_idx..variant.compose.len() { + variant.big_endian_compose.push(variant.compose[i].clone()); + } + } else { + /* If we do not need to reorder anything then immediately add + * the expressions from the big_endian_expressions and + * concatinate the compose vector */ + variant.big_endian_compose.extend(big_endian_expressions); + variant + .big_endian_compose + .extend(variant.compose.iter().cloned()); + } + } + + /* If we have a return type, there is a possibility we want to generate + * a shuffle call */ + if let Some(return_type) = &variant.signature.return_type { + let return_value = variant + .compose + .last() + .expect("Cannot define a return type with an empty function body"); + + /* If we do not create a shuffle call we do not need modify the + * return value and append to the big endian ast array. A bit confusing + * as in code we are making the final call before caputuring the return + * value of the intrinsic that has been called.*/ + let ret_val_name = "ret_val".to_string(); + if let Some(simd_shuffle_call) = + create_shuffle_call(&ret_val_name, return_type, should_reverse) + { + /* There is a possibility that the funcion arguments did not + * require big endian treatment, thus we need to now add the + * original function body before appending the return value.*/ + if variant.big_endian_compose.is_empty() { + variant + .big_endian_compose + .extend(variant.compose.iter().cloned()); + } + + /* Now we shuffle the return value - we are creating a new + * return value for the intrinsic. */ + let return_value_variable = if type_has_tuple(&return_type) { + create_mut_let_variable(&ret_val_name, return_type, return_value.clone()) + } else { + create_let_variable(&ret_val_name, return_type, return_value.clone()) + }; + + /* Remove the last item which will be the return value */ + variant.big_endian_compose.pop(); + variant.big_endian_compose.push(return_value_variable); + variant.big_endian_compose.push(simd_shuffle_call); + if type_has_tuple(return_type) { + /* We generated `tuple_count` number of calls to shuffle + * re-assigning each tuple however those generated calls do + * not make the parent function return. So we add the return + * value here */ + variant + .big_endian_compose + .push(create_symbol_identifier(&ret_val_name)); + } + } + } + } + /// Implement a "zeroing" (_z) method by calling an existing "merging" (_m) method, as required. fn generate_zeroing_pass_through( &mut self, @@ -1505,120 +1665,157 @@ impl Intrinsic { } } -impl ToTokens for Intrinsic { - fn to_tokens(&self, tokens: &mut TokenStream) { - let signature = &self.signature; - let fn_name = signature.fn_name().to_string(); - let target_feature = self.target_features.join(","); - let safety = self - .safety - .as_ref() - .expect("safety should be determined during `pre_build`"); +/// Some intrinsics require a little endian and big endian implementation, others +/// do not +enum Endianness { + Little, + Big, + NA, +} - if let Some(doc) = &self.doc { - let mut doc = vec![doc.to_string()]; +/// Based on the endianess will create the appropriate intrinsic, or simply +/// create the desired intrinsic without any endianess +fn create_tokens(intrinsic: &Intrinsic, endianness: Endianness, tokens: &mut TokenStream) { + let signature = &intrinsic.signature; + let fn_name = signature.fn_name().to_string(); + let target_feature = intrinsic.target_features.join(","); + let safety = intrinsic + .safety + .as_ref() + .expect("safety should be determined during `pre_build`"); - doc.push(format!("[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/{})", &signature.doc_name())); + if let Some(doc) = &intrinsic.doc { + let mut doc = vec![doc.to_string()]; - if safety.has_doc_comments() { - doc.push("## Safety".to_string()); - for comment in safety.doc_comments() { - doc.push(format!(" * {comment}")); - } - } else { - assert!( - safety.is_safe(), - "{fn_name} is both public and unsafe, and so needs safety documentation" - ); + doc.push(format!("[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/{})", &signature.doc_name())); + + if safety.has_doc_comments() { + doc.push("## Safety".to_string()); + for comment in safety.doc_comments() { + doc.push(format!(" * {comment}")); } - - tokens.append_all(quote! { #(#[doc = #doc])* }); } else { assert!( - matches!(self.visibility, FunctionVisibility::Private), - "{fn_name} needs to be private, or to have documentation." - ); - assert!( - !safety.has_doc_comments(), - "{fn_name} needs a documentation section for its safety comments." + safety.is_safe(), + "{fn_name} is both public and unsafe, and so needs safety documentation" ); } - tokens.append_all(quote! { #[inline] }); + tokens.append_all(quote! { #(#[doc = #doc])* }); + } else { + assert!( + matches!(intrinsic.visibility, FunctionVisibility::Private), + "{fn_name} needs to be private, or to have documentation." + ); + assert!( + !safety.has_doc_comments(), + "{fn_name} needs a documentation section for its safety comments." + ); + } - /* If we have manually defined attributes on the block of yaml with - * 'attr:' we want to add them */ - if let Some(attr) = &self.attr { - /* Scan to see if we have defined `FnCall: [target_feature, ['']]`*/ - if !has_target_feature_attr(attr) { - /* If not add the default one that is defined at the top of - * the yaml file. This does mean we scan the attributes vector - * twice, once to see if the `target_feature` exists and again - * to actually append the tokens. We could impose that the - * `target_feature` call has to be the first argument of the - * `attr` block */ - tokens.append_all(quote! { - #[target_feature(enable = #target_feature)] - }); - } + tokens.append_all(quote! { #[inline] }); - /* Target feature will get added here */ - let attr_expressions = &mut attr.iter().peekable(); - while let Some(ex) = attr_expressions.next() { - let mut inner = TokenStream::new(); - ex.to_tokens(&mut inner); - tokens.append(Punct::new('#', Spacing::Alone)); - tokens.append(Group::new(Delimiter::Bracket, inner)); - } - } else { + match endianness { + Endianness::Little => tokens.append_all(quote! { #[cfg(target_endian = "little")] }), + Endianness::Big => tokens.append_all(quote! { #[cfg(target_endian = "big")] }), + Endianness::NA => {} + }; + + /* If we have manually defined attributes on the block of yaml with + * 'attr:' we want to add them */ + if let Some(attr) = &intrinsic.attr { + /* Scan to see if we have defined `FnCall: [target_feature, ['']]`*/ + if !has_target_feature_attr(attr) { + /* If not add the default one that is defined at the top of + * the yaml file. This does mean we scan the attributes vector + * twice, once to see if the `target_feature` exists and again + * to actually append the tokens. We could impose that the + * `target_feature` call has to be the first argument of the + * `attr` block */ tokens.append_all(quote! { #[target_feature(enable = #target_feature)] }); } - if let Some(assert_instr) = &self.assert_instr { - if !assert_instr.is_empty() { - InstructionAssertionsForBaseType(&assert_instr, &self.base_type.as_ref()) - .to_tokens(tokens) - } + /* Target feature will get added here */ + let attr_expressions = &mut attr.iter().peekable(); + while let Some(ex) = attr_expressions.next() { + let mut inner = TokenStream::new(); + ex.to_tokens(&mut inner); + tokens.append(Punct::new('#', Spacing::Alone)); + tokens.append(Group::new(Delimiter::Bracket, inner)); } + } else { + tokens.append_all(quote! { + #[target_feature(enable = #target_feature)] + }); + } - match &self.visibility { - FunctionVisibility::Public => tokens.append_all(quote! { pub }), - FunctionVisibility::Private => {} + if let Some(assert_instr) = &intrinsic.assert_instr { + if !assert_instr.is_empty() { + InstructionAssertionsForBaseType(&assert_instr, &intrinsic.base_type.as_ref()) + .to_tokens(tokens) } - if safety.is_unsafe() { - tokens.append_all(quote! { unsafe }); - } - tokens.append_all(quote! { #signature }); + } - // If the intrinsic function is explicitly unsafe, we populate `body_default_safety` with - // the implementation. No explicit unsafe blocks are required. - // - // If the intrinsic is safe, we fill `body_default_safety` until we encounter an expression - // that requires an unsafe wrapper, then switch to `body_unsafe`. Since the unsafe - // operation (e.g. memory access) is typically the last step, this tends to minimises the - // amount of unsafe code required. - let mut body_default_safety = TokenStream::new(); - let mut body_unsafe = TokenStream::new(); - let mut body_current = &mut body_default_safety; - for (pos, ex) in self.compose.iter().with_position() { - if safety.is_safe() && ex.requires_unsafe_wrapper(&fn_name) { - body_current = &mut body_unsafe; - } - ex.to_tokens(body_current); - let is_last = matches!(pos, itertools::Position::Last | itertools::Position::Only); - let is_llvm_link = matches!(ex, Expression::LLVMLink(_)); - if !is_last && !is_llvm_link { - body_current.append(Punct::new(';', Spacing::Alone)); - } - } - let mut body = body_default_safety; - if !body_unsafe.is_empty() { - body.append_all(quote! { unsafe { #body_unsafe } }); - } + match &intrinsic.visibility { + FunctionVisibility::Public => tokens.append_all(quote! { pub }), + FunctionVisibility::Private => {} + } + if safety.is_unsafe() { + tokens.append_all(quote! { unsafe }); + } + tokens.append_all(quote! { #signature }); - tokens.append(Group::new(Delimiter::Brace, body)); + let expressions = match endianness { + Endianness::Little | Endianness::NA => &intrinsic.compose, + Endianness::Big => &intrinsic.big_endian_compose, + }; + + tokens.append_all(quote! { #signature }); + + // If the intrinsic function is explicitly unsafe, we populate `body_default_safety` with + // the implementation. No explicit unsafe blocks are required. + // + // If the intrinsic is safe, we fill `body_default_safety` until we encounter an expression + // that requires an unsafe wrapper, then switch to `body_unsafe`. Since the unsafe + // operation (e.g. memory access) is typically the last step, this tends to minimises the + // amount of unsafe code required. + let mut body_default_safety = TokenStream::new(); + let mut body_unsafe = TokenStream::new(); + let mut body_current = &mut body_default_safety; + for (pos, ex) in expressions.iter().with_position() { + if safety.is_safe() && ex.requires_unsafe_wrapper(&fn_name) { + body_current = &mut body_unsafe; + } + ex.to_tokens(body_current); + let is_last = matches!(pos, itertools::Position::Last | itertools::Position::Only); + let is_llvm_link = matches!(ex, Expression::LLVMLink(_)); + if !is_last && !is_llvm_link { + body_current.append(Punct::new(';', Spacing::Alone)); + } + } + let mut body = body_default_safety; + if !body_unsafe.is_empty() { + body.append_all(quote! { unsafe { #body_unsafe } }); + } + + tokens.append(Group::new(Delimiter::Brace, body)); +} + +impl ToTokens for Intrinsic { + fn to_tokens(&self, tokens: &mut TokenStream) { + if self.big_endian_compose.len() >= 1 { + for i in 0..2 { + match i { + 0 => create_tokens(self, Endianness::Little, tokens), + 1 => create_tokens(self, Endianness::Big, tokens), + _ => panic!("Currently only little and big endian exist"), + } + } + } else { + create_tokens(self, Endianness::NA, tokens); + } } } diff --git a/library/stdarch/crates/stdarch-gen-arm/src/main.rs b/library/stdarch/crates/stdarch-gen-arm/src/main.rs index c78e5dc4e430..9ea1917c14f1 100644 --- a/library/stdarch/crates/stdarch-gen-arm/src/main.rs +++ b/library/stdarch/crates/stdarch-gen-arm/src/main.rs @@ -1,6 +1,7 @@ #![feature(pattern)] mod assert_instr; +mod big_endian; mod context; mod expression; mod fn_suffix;