diff --git a/src/compiler/rust/as_slice.rs b/src/compiler/rust/as_slice.rs new file mode 100644 index 00000000000..fc4eba16bb4 --- /dev/null +++ b/src/compiler/rust/as_slice.rs @@ -0,0 +1,28 @@ +// Copyright © 2024 Collabora, Ltd. +// SPDX-License-Identifier: MIT + +use std::ops::Index; + +pub enum AttrList { + Array(&'static [T]), + Uniform(T), +} + +impl Index for AttrList { + type Output = T; + + fn index(&self, idx: usize) -> &T { + match self { + AttrList::Array(arr) => &arr[idx], + AttrList::Uniform(typ) => typ, + } + } +} + +pub trait AsSlice { + type Attr; + + fn as_slice(&self) -> &[T]; + fn as_mut_slice(&mut self) -> &mut [T]; + fn attrs(&self) -> AttrList; +} diff --git a/src/compiler/rust/lib.rs b/src/compiler/rust/lib.rs index 80b1ee65a9c..567db097ac6 100644 --- a/src/compiler/rust/lib.rs +++ b/src/compiler/rust/lib.rs @@ -1,6 +1,7 @@ // Copyright © 2024 Igalia S.L. // SPDX-License-Identifier: MIT +pub mod as_slice; pub mod bindings; pub mod bitset; pub mod cfg; diff --git a/src/compiler/rust/meson.build b/src/compiler/rust/meson.build index 3726720eea2..2316f6a87cf 100644 --- a/src/compiler/rust/meson.build +++ b/src/compiler/rust/meson.build @@ -2,6 +2,7 @@ # SPDX-License-Identifier: MIT _compiler_rs_sources = [ + 'as_slice.rs', 'bitset.rs', 'cfg.rs', 'nir.rs', @@ -94,3 +95,21 @@ _libcompiler_rs = static_library( idep_compiler_rs = declare_dependency( link_with : _libcompiler_rs, ) + +dep_syn = dependency('syn', + version : '>= 2.0.15', + fallback : ['syn', 'dep_syn'], + required : true, +) + +_libcompiler_proc_rs = static_library( + 'compiler_proc', + 'proc/lib.rs', + gnu_symbol_visibility : 'hidden', + dependencies : [dep_syn], + rust_abi : 'rust', +) + +idep_compiler_proc_rs = declare_dependency( + link_with : _libcompiler_proc_rs, +) diff --git a/src/compiler/rust/proc/as_slice.rs b/src/compiler/rust/proc/as_slice.rs new file mode 100644 index 00000000000..20a338923a6 --- /dev/null +++ b/src/compiler/rust/proc/as_slice.rs @@ -0,0 +1,220 @@ +// Copyright © 2023 Collabora, Ltd. +// SPDX-License-Identifier: MIT + +use proc_macro::TokenStream; +use proc_macro2::{Span, TokenStream as TokenStream2}; +use syn::*; + +fn expr_as_usize(expr: &syn::Expr) -> usize { + let lit = match expr { + syn::Expr::Lit(lit) => lit, + _ => panic!("Expected a literal, found an expression"), + }; + let lit_int = match &lit.lit { + syn::Lit::Int(i) => i, + _ => panic!("Expected a literal integer"), + }; + assert!(lit.attrs.is_empty()); + lit_int + .base10_parse() + .expect("Failed to parse integer literal") +} + +fn count_type(ty: &Type, slice_type: &str) -> usize { + match ty { + syn::Type::Array(a) => { + let elems = count_type(a.elem.as_ref(), slice_type); + if elems > 0 { + elems * expr_as_usize(&a.len) + } else { + 0 + } + } + syn::Type::Path(p) => { + if p.qself.is_none() && p.path.is_ident(slice_type) { + 1 + } else { + 0 + } + } + _ => 0, + } +} + +fn get_attr(field: &Field, attr_name: &str) -> Option { + for attr in &field.attrs { + if let Meta::List(ml) = &attr.meta { + if ml.path.is_ident(attr_name) { + return Some(format!("{}", ml.tokens)); + } + } + } + None +} + +pub fn derive_as_slice( + input: TokenStream, + slice_type: &str, + attr_name: &str, + attr_type: &str, +) -> TokenStream { + let DeriveInput { + attrs, ident, data, .. + } = parse_macro_input!(input); + + match data { + Data::Struct(s) => { + let mut has_repr_c = false; + for attr in attrs { + match attr.meta { + Meta::List(ml) => { + if ml.path.is_ident("repr") + && format!("{}", ml.tokens) == "C" + { + has_repr_c = true; + } + } + _ => (), + } + } + assert!(has_repr_c, "Struct must be declared #[repr(C)]"); + + let mut first = None; + let mut count = 0_usize; + let mut found_last = false; + let mut attrs = TokenStream2::new(); + + if let Fields::Named(named) = s.fields { + for f in named.named { + let f_count = count_type(&f.ty, slice_type); + let f_attr = get_attr(&f, &attr_name); + + if f_count > 0 { + assert!( + !found_last, + "All fields of type {slice_type} must be consecutive", + ); + + let attr_type = + Ident::new(attr_type, Span::call_site()); + let f_attr = if let Some(s) = f_attr { + let s = syn::parse_str::(&s).unwrap(); + quote! { #attr_type::#s, } + } else { + quote! { #attr_type::DEFAULT, } + }; + + first.get_or_insert(f.ident); + for _ in 0..f_count { + attrs.extend(f_attr.clone()); + } + count += f_count; + } else { + assert!( + f_attr.is_none(), + "{attr_name} attribute is only allowed on {slice_type}" + ); + if !first.is_none() { + found_last = true; + } + } + } + } else { + panic!("Fields are not named"); + } + + let slice_type = Ident::new(slice_type, Span::call_site()); + let attr_type = Ident::new(attr_type, Span::call_site()); + if let Some(first) = first { + quote! { + impl compiler::as_slice::AsSlice<#slice_type> for #ident { + type Attr = #attr_type; + + fn as_slice(&self) -> &[#slice_type] { + unsafe { + let first = &self.#first as *const #slice_type; + std::slice::from_raw_parts(first, #count) + } + } + + fn as_mut_slice(&mut self) -> &mut [#slice_type] { + unsafe { + let first = + &mut self.#first as *mut #slice_type; + std::slice::from_raw_parts_mut(first, #count) + } + } + + fn attrs(&self) -> AttrList { + static ATTRS: [#attr_type; #count] = [#attrs]; + AttrList::Array(&ATTRS) + } + } + } + } else { + quote! { + impl compiler::as_slice::AsSlice<#slice_type> for #ident { + type Attr = #attr_type; + + fn as_slice(&self) -> &[#slice_type] { + &[] + } + + fn as_mut_slice(&mut self) -> &mut [#slice_type] { + &mut [] + } + + fn attrs(&self) -> AttrList { + AttrList::Uniform(#attr_type::DEFAULT) + } + } + } + } + .into() + } + Data::Enum(e) => { + let mut as_slice_cases = TokenStream2::new(); + let mut as_mut_slice_cases = TokenStream2::new(); + let mut types_cases = TokenStream2::new(); + let slice_type = Ident::new(slice_type, Span::call_site()); + let attr_type = Ident::new(attr_type, Span::call_site()); + for v in e.variants { + let case = v.ident; + as_slice_cases.extend(quote! { + #ident::#case(x) => compiler::as_slice::AsSlice::<#slice_type>::as_slice(x), + }); + as_mut_slice_cases.extend(quote! { + #ident::#case(x) => compiler::as_slice::AsSlice::<#slice_type>::as_mut_slice(x), + }); + types_cases.extend(quote! { + #ident::#case(x) => compiler::as_slice::AsSlice::<#slice_type>::attrs(x), + }); + } + quote! { + impl compiler::as_slice::AsSlice<#slice_type> for #ident { + type Attr = #attr_type; + + fn as_slice(&self) -> &[#slice_type] { + match self { + #as_slice_cases + } + } + + fn as_mut_slice(&mut self) -> &mut [#slice_type] { + match self { + #as_mut_slice_cases + } + } + + fn attrs(&self) -> AttrList { + match self { + #types_cases + } + } + } + } + .into() + } + _ => panic!("Not a struct type"), + } +} diff --git a/src/compiler/rust/proc/lib.rs b/src/compiler/rust/proc/lib.rs new file mode 100644 index 00000000000..144658e1b0b --- /dev/null +++ b/src/compiler/rust/proc/lib.rs @@ -0,0 +1,10 @@ +// Copyright © 2024 Collabora, Ltd. +// SPDX-License-Identifier: MIT + +extern crate proc_macro; +extern crate proc_macro2; +#[macro_use] +extern crate quote; +extern crate syn; + +pub mod as_slice; diff --git a/src/nouveau/compiler/meson.build b/src/nouveau/compiler/meson.build index f75f1ab105b..2755e7914eb 100644 --- a/src/nouveau/compiler/meson.build +++ b/src/nouveau/compiler/meson.build @@ -16,12 +16,6 @@ nak_rust_args = [ '-Anon_snake_case', ] -dep_syn = dependency('syn', - version : '>= 2.0.15', - fallback : ['syn', 'dep_syn'], - required : true, -) - dep_paste = dependency('paste', version : '>= 1.0.14', fallback : ['paste', 'dep_paste'], @@ -114,7 +108,7 @@ _libnak_bindings_rs = static_library( _libnak_ir_proc_rs = rust.proc_macro( 'nak_ir_proc', files('nak/ir_proc.rs'), - dependencies : [dep_syn], + dependencies : [idep_compiler_proc_rs], ) _libnak_rs = static_library( diff --git a/src/nouveau/compiler/nak/ir.rs b/src/nouveau/compiler/nak/ir.rs index 04857c00133..92ba1761d4e 100644 --- a/src/nouveau/compiler/nak/ir.rs +++ b/src/nouveau/compiler/nak/ir.rs @@ -10,6 +10,7 @@ use nak_bindings::*; pub use crate::builder::{Builder, InstrBuilder, SSABuilder, SSAInstrBuilder}; use crate::legalize::LegalizeBuilder; use crate::sph::{OutputTopology, PixelImap}; +use compiler::as_slice::*; use compiler::cfg::CFG; use nak_ir_proc::*; use std::cmp::{max, min}; @@ -1398,30 +1399,6 @@ impl fmt::Display for Src { } } -pub enum AttrList { - Array(&'static [T]), - Uniform(T), -} - -impl Index for AttrList { - type Output = T; - - fn index(&self, idx: usize) -> &T { - match self { - AttrList::Array(arr) => &arr[idx], - AttrList::Uniform(typ) => typ, - } - } -} - -pub trait AsSlice { - type Attr; - - fn as_slice(&self) -> &[T]; - fn as_mut_slice(&mut self) -> &mut [T]; - fn attrs(&self) -> AttrList; -} - #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum SrcType { diff --git a/src/nouveau/compiler/nak/ir_proc.rs b/src/nouveau/compiler/nak/ir_proc.rs index 3835d0a4498..c4ee89ec8e3 100644 --- a/src/nouveau/compiler/nak/ir_proc.rs +++ b/src/nouveau/compiler/nak/ir_proc.rs @@ -7,224 +7,11 @@ extern crate proc_macro2; extern crate quote; extern crate syn; +use compiler_proc::as_slice::*; use proc_macro::TokenStream; -use proc_macro2::{Span, TokenStream as TokenStream2}; +use proc_macro2::{TokenStream as TokenStream2}; use syn::*; -fn expr_as_usize(expr: &syn::Expr) -> usize { - let lit = match expr { - syn::Expr::Lit(lit) => lit, - _ => panic!("Expected a literal, found an expression"), - }; - let lit_int = match &lit.lit { - syn::Lit::Int(i) => i, - _ => panic!("Expected a literal integer"), - }; - assert!(lit.attrs.is_empty()); - lit_int - .base10_parse() - .expect("Failed to parse integer literal") -} - -fn count_type(ty: &Type, slice_type: &str) -> usize { - match ty { - syn::Type::Array(a) => { - let elems = count_type(a.elem.as_ref(), slice_type); - if elems > 0 { - elems * expr_as_usize(&a.len) - } else { - 0 - } - } - syn::Type::Path(p) => { - if p.qself.is_none() && p.path.is_ident(slice_type) { - 1 - } else { - 0 - } - } - _ => 0, - } -} - -fn get_attr(field: &Field, attr_name: &str) -> Option { - for attr in &field.attrs { - if let Meta::List(ml) = &attr.meta { - if ml.path.is_ident(attr_name) { - return Some(format!("{}", ml.tokens)); - } - } - } - None -} - -fn derive_as_slice( - input: TokenStream, - slice_type: &str, - attr_name: &str, - attr_type: &str, -) -> TokenStream { - let DeriveInput { - attrs, ident, data, .. - } = parse_macro_input!(input); - - match data { - Data::Struct(s) => { - let mut has_repr_c = false; - for attr in attrs { - match attr.meta { - Meta::List(ml) => { - if ml.path.is_ident("repr") - && format!("{}", ml.tokens) == "C" - { - has_repr_c = true; - } - } - _ => (), - } - } - assert!(has_repr_c, "Struct must be declared #[repr(C)]"); - - let mut first = None; - let mut count = 0_usize; - let mut found_last = false; - let mut attrs = TokenStream2::new(); - - if let Fields::Named(named) = s.fields { - for f in named.named { - let f_count = count_type(&f.ty, slice_type); - let f_attr = get_attr(&f, &attr_name); - - if f_count > 0 { - assert!( - !found_last, - "All fields of type {slice_type} must be consecutive", - ); - - let attr_type = - Ident::new(attr_type, Span::call_site()); - let f_attr = if let Some(s) = f_attr { - let s = syn::parse_str::(&s).unwrap(); - quote! { #attr_type::#s, } - } else { - quote! { #attr_type::DEFAULT, } - }; - - first.get_or_insert(f.ident); - for _ in 0..f_count { - attrs.extend(f_attr.clone()); - } - count += f_count; - } else { - assert!( - f_attr.is_none(), - "{attr_name} attribute is only allowed on {slice_type}" - ); - if !first.is_none() { - found_last = true; - } - } - } - } else { - panic!("Fields are not named"); - } - - let slice_type = Ident::new(slice_type, Span::call_site()); - let attr_type = Ident::new(attr_type, Span::call_site()); - if let Some(first) = first { - quote! { - impl AsSlice<#slice_type> for #ident { - type Attr = #attr_type; - - fn as_slice(&self) -> &[#slice_type] { - unsafe { - let first = &self.#first as *const #slice_type; - std::slice::from_raw_parts(first, #count) - } - } - - fn as_mut_slice(&mut self) -> &mut [#slice_type] { - unsafe { - let first = - &mut self.#first as *mut #slice_type; - std::slice::from_raw_parts_mut(first, #count) - } - } - - fn attrs(&self) -> AttrList { - static ATTRS: [#attr_type; #count] = [#attrs]; - AttrList::Array(&ATTRS) - } - } - } - } else { - quote! { - impl AsSlice<#slice_type> for #ident { - type Attr = #attr_type; - - fn as_slice(&self) -> &[#slice_type] { - &[] - } - - fn as_mut_slice(&mut self) -> &mut [#slice_type] { - &mut [] - } - - fn attrs(&self) -> AttrList { - AttrList::Uniform(#attr_type::DEFAULT) - } - } - } - } - .into() - } - Data::Enum(e) => { - let mut as_slice_cases = TokenStream2::new(); - let mut as_mut_slice_cases = TokenStream2::new(); - let mut types_cases = TokenStream2::new(); - let slice_type = Ident::new(slice_type, Span::call_site()); - let attr_type = Ident::new(attr_type, Span::call_site()); - for v in e.variants { - let case = v.ident; - as_slice_cases.extend(quote! { - #ident::#case(x) => AsSlice::<#slice_type>::as_slice(x), - }); - as_mut_slice_cases.extend(quote! { - #ident::#case(x) => AsSlice::<#slice_type>::as_mut_slice(x), - }); - types_cases.extend(quote! { - #ident::#case(x) => AsSlice::<#slice_type>::attrs(x), - }); - } - quote! { - impl AsSlice<#slice_type> for #ident { - type Attr = #attr_type; - - fn as_slice(&self) -> &[#slice_type] { - match self { - #as_slice_cases - } - } - - fn as_mut_slice(&mut self) -> &mut [#slice_type] { - match self { - #as_mut_slice_cases - } - } - - fn attrs(&self) -> AttrList { - match self { - #types_cases - } - } - } - } - .into() - } - _ => panic!("Not a struct type"), - } -} - #[proc_macro_derive(SrcsAsSlice, attributes(src_type))] pub fn derive_srcs_as_slice(input: TokenStream) -> TokenStream { derive_as_slice(input, "Src", "src_type", "SrcType")