mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-05 13:58:04 +02:00
rusticl: add proc macro module for generating API stubs
Most of the API stubs are very very trivial to generate as the sole purpose of those are to deconstruct the returned `Result` object. Sadly we can't use external crates yet, so "syn" and "qoute" can't be used for this :'( The code is kinda hacky, but we also don't expose this to other people, so we can keep this as a big hack until we can use external crates. I wish there was a better solution here. Signed-off-by: Karol Herbst <git@karolherbst.de> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/23413>
This commit is contained in:
parent
b1cd5780d6
commit
5875f2e803
2 changed files with 232 additions and 0 deletions
|
|
@ -44,6 +44,10 @@ libmesa_rust_files = files(
|
|||
'mesa/pipe/transfer.rs',
|
||||
)
|
||||
|
||||
rusticl_proc_macros_files = files(
|
||||
'proc/lib.rs',
|
||||
)
|
||||
|
||||
rusticl_files = files(
|
||||
'lib.rs',
|
||||
'api.rs',
|
||||
|
|
@ -316,6 +320,15 @@ libmesa_rust = static_library(
|
|||
]
|
||||
)
|
||||
|
||||
rusticl_proc_macros = shared_library(
|
||||
'rusticl_proc_macros',
|
||||
[rusticl_proc_macros_files],
|
||||
rust_crate_type : 'proc-macro',
|
||||
rust_args : [
|
||||
rusticl_args,
|
||||
],
|
||||
)
|
||||
|
||||
librusticl = static_library(
|
||||
'rusticl',
|
||||
[rusticl_files],
|
||||
|
|
@ -329,6 +342,7 @@ librusticl = static_library(
|
|||
libmesa_rust_gen,
|
||||
libmesa_rust_util,
|
||||
rusticl_opencl_gen,
|
||||
rusticl_proc_macros,
|
||||
],
|
||||
dependencies : [
|
||||
idep_rusticl_gen,
|
||||
|
|
|
|||
218
src/gallium/frontends/rusticl/proc/lib.rs
Normal file
218
src/gallium/frontends/rusticl/proc/lib.rs
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
extern crate proc_macro;
|
||||
use proc_macro::Delimiter;
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro::TokenTree::Group;
|
||||
use proc_macro::TokenTree::Ident;
|
||||
use proc_macro::TokenTree::Punct;
|
||||
|
||||
/// Macro for generating the C API stubs for normal functions
|
||||
#[proc_macro_attribute]
|
||||
pub fn cl_entrypoint(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let mut name = None;
|
||||
let mut args = None;
|
||||
let mut ret_type = None;
|
||||
|
||||
let mut iter = item.clone().into_iter();
|
||||
while let Some(item) = iter.next() {
|
||||
match item {
|
||||
Ident(ident) => match ident.to_string().as_str() {
|
||||
// extract the function name
|
||||
"fn" => name = Some(iter.next().unwrap().to_string()),
|
||||
|
||||
// extract inner type
|
||||
"CLResult" => {
|
||||
// skip the `<`
|
||||
iter.next();
|
||||
let mut ret_type_tmp = String::new();
|
||||
|
||||
for ident in iter.by_ref() {
|
||||
if ident.to_string() == ">" {
|
||||
break;
|
||||
}
|
||||
|
||||
if ret_type_tmp.ends_with("mut") || ret_type_tmp.ends_with("const") {
|
||||
ret_type_tmp.push(' ');
|
||||
}
|
||||
|
||||
ret_type_tmp.push_str(ident.to_string().as_str());
|
||||
}
|
||||
|
||||
ret_type = Some(ret_type_tmp);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Group(group) => {
|
||||
if args.is_some() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if group.delimiter() != Delimiter::Parenthesis {
|
||||
continue;
|
||||
}
|
||||
|
||||
// the first group are our function args :)
|
||||
args = Some(group.stream());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let name = name.as_ref().expect("no name found!");
|
||||
let args = args.as_ref().expect("no args found!");
|
||||
let ret_type = ret_type.as_ref().expect("no ret_type found!");
|
||||
|
||||
let mut arg_names = Vec::new();
|
||||
let mut collect = true;
|
||||
|
||||
// extract the variable names of our function arguments
|
||||
for item in args.clone() {
|
||||
match item {
|
||||
Ident(ident) => {
|
||||
if collect {
|
||||
arg_names.push(ident);
|
||||
}
|
||||
}
|
||||
|
||||
// we ignore everything between a `:` and a `,` as those are the argument types
|
||||
Punct(punct) => match punct.as_char() {
|
||||
':' => collect = false,
|
||||
',' => collect = true,
|
||||
_ => {}
|
||||
},
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// convert to string and strip `mut` specifiers
|
||||
let arg_names: Vec<_> = arg_names
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|ident| ident.to_string())
|
||||
.filter(|ident| ident != "mut")
|
||||
.collect();
|
||||
|
||||
let arg_names_str = arg_names.join(",");
|
||||
let mut args = args.to_string();
|
||||
if !args.ends_with(',') {
|
||||
args.push(',');
|
||||
}
|
||||
|
||||
// depending on the return type we have to generate a different match case
|
||||
let mut res: TokenStream = if ret_type == "()" {
|
||||
// trivial case: return the `Err(err)` as is
|
||||
format!(
|
||||
"pub extern \"C\" fn cl_{name}(
|
||||
{args}
|
||||
) -> cl_int {{
|
||||
match {name}({arg_names_str}) {{
|
||||
Ok(_) => CL_SUCCESS as cl_int,
|
||||
Err(e) => e,
|
||||
}}
|
||||
}}"
|
||||
)
|
||||
} else {
|
||||
// here we write the error code into the last argument, which we also add. All OpenCL APIs
|
||||
// which return an object do have the `errcode_ret: *mut cl_int` argument last, so we can
|
||||
// just make use of this here.
|
||||
format!(
|
||||
"pub extern \"C\" fn cl_{name}(
|
||||
{args}
|
||||
errcode_ret: *mut cl_int,
|
||||
) -> {ret_type} {{
|
||||
let (ptr, err) = match {name}({arg_names_str}) {{
|
||||
Ok(o) => (o, CL_SUCCESS as cl_int),
|
||||
Err(e) => (std::ptr::null_mut(), e),
|
||||
}};
|
||||
if !errcode_ret.is_null() {{
|
||||
unsafe {{
|
||||
*errcode_ret = err;
|
||||
}}
|
||||
}}
|
||||
ptr
|
||||
}}"
|
||||
)
|
||||
}
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
res.extend(item);
|
||||
res
|
||||
}
|
||||
|
||||
/// Special macro for generating C function stubs to call into our `CLInfo` trait
|
||||
#[proc_macro_attribute]
|
||||
pub fn cl_info_entrypoint(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let mut name = None;
|
||||
let mut args = Vec::new();
|
||||
let mut iter = item.clone().into_iter();
|
||||
|
||||
let mut collect = false;
|
||||
|
||||
// we have to extract the type name we implement the trait for and the type of the input
|
||||
// parameters. The input Parameters are defined as `T` inside `CLInfo<T>` or `CLInfoObj<T, ..>`
|
||||
while let Some(item) = iter.next() {
|
||||
match item {
|
||||
Ident(ident) => {
|
||||
if collect {
|
||||
args.push(ident);
|
||||
} else if ident.to_string() == "for" {
|
||||
name = Some(iter.next().unwrap().to_string());
|
||||
}
|
||||
}
|
||||
Punct(punct) => match punct.as_char() {
|
||||
'<' => collect = true,
|
||||
'>' => collect = false,
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let name = name.as_ref().expect("no name found!");
|
||||
assert!(!args.is_empty());
|
||||
|
||||
// the 1st argument is special as it's the actual property being queried. The remaining
|
||||
// arguments are additional input data being passed before the property.
|
||||
let arg = &args[0];
|
||||
let (args_values, args) = args[1..]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, arg)| (format!("arg{idx},"), format!("arg{idx}: {arg},")))
|
||||
.reduce(|(a1, b1), (a2, b2)| (a1 + &a2, b1 + &b2))
|
||||
.unwrap_or_default();
|
||||
|
||||
// depending on the amount of arguments we have a different trait implementation
|
||||
let method = if args.len() > 1 {
|
||||
"get_info_obj"
|
||||
} else {
|
||||
"get_info"
|
||||
};
|
||||
|
||||
let mut res: TokenStream = format!(
|
||||
"pub extern \"C\" fn {attr}(
|
||||
input: {name},
|
||||
{args}
|
||||
param_name: {arg},
|
||||
param_value_size: usize,
|
||||
param_value: *mut ::std::ffi::c_void,
|
||||
param_value_size_ret: *mut usize,
|
||||
) -> cl_int {{
|
||||
match input.{method}(
|
||||
{args_values}
|
||||
param_name,
|
||||
param_value_size,
|
||||
param_value,
|
||||
param_value_size_ret,
|
||||
) {{
|
||||
Ok(_) => CL_SUCCESS as cl_int,
|
||||
Err(e) => e,
|
||||
}}
|
||||
}}"
|
||||
)
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
res.extend(item);
|
||||
res
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue