nak,compiler: Move AsSlice to common code

Reviewed-by: Christian Gmeiner <cgmeiner@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/30443>
This commit is contained in:
Faith Ekstrand 2024-07-31 12:20:26 -05:00 committed by Marge Bot
parent 3ca448a549
commit ab72be6c5e
8 changed files with 282 additions and 246 deletions

View file

@ -0,0 +1,28 @@
// Copyright © 2024 Collabora, Ltd.
// SPDX-License-Identifier: MIT
use std::ops::Index;
pub enum AttrList<T: 'static> {
Array(&'static [T]),
Uniform(T),
}
impl<T: 'static> Index<usize> for AttrList<T> {
type Output = T;
fn index(&self, idx: usize) -> &T {
match self {
AttrList::Array(arr) => &arr[idx],
AttrList::Uniform(typ) => typ,
}
}
}
pub trait AsSlice<T> {
type Attr;
fn as_slice(&self) -> &[T];
fn as_mut_slice(&mut self) -> &mut [T];
fn attrs(&self) -> AttrList<Self::Attr>;
}

View file

@ -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;

View file

@ -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,
)

View file

@ -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<String> {
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::<Ident>(&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<Self::Attr> {
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<Self::Attr> {
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<Self::Attr> {
match self {
#types_cases
}
}
}
}
.into()
}
_ => panic!("Not a struct type"),
}
}

View file

@ -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;

View file

@ -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(

View file

@ -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<T: 'static> {
Array(&'static [T]),
Uniform(T),
}
impl<T: 'static> Index<usize> for AttrList<T> {
type Output = T;
fn index(&self, idx: usize) -> &T {
match self {
AttrList::Array(arr) => &arr[idx],
AttrList::Uniform(typ) => typ,
}
}
}
pub trait AsSlice<T> {
type Attr;
fn as_slice(&self) -> &[T];
fn as_mut_slice(&mut self) -> &mut [T];
fn attrs(&self) -> AttrList<Self::Attr>;
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum SrcType {

View file

@ -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<String> {
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::<Ident>(&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<Self::Attr> {
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<Self::Attr> {
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<Self::Attr> {
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")