From 6429ebf8ee30595d2b814f8a74a980c5b4b76b02 Mon Sep 17 00:00:00 2001 From: Luis Ruisinger Date: Fri, 3 Apr 2026 15:36:35 +0200 Subject: [PATCH 1/2] [Rewrite] syscall generation during build * removed redundancy in syscall collection * rust expression parsing rather than handwritten expressions, allows also better type handling --- Cargo.lock | 2 + Cargo.toml | 2 + build.rs | 243 ++++++++++++++++++++--------------------------------- 3 files changed, 94 insertions(+), 153 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 933791c..07b2490 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1068,6 +1068,8 @@ dependencies = [ "hal-testing", "interface", "macros", + "prettyplease", + "proc-macro2", "quote", "rand", "syn", diff --git a/Cargo.toml b/Cargo.toml index cb3fff1..9233124 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,9 @@ walkdir = "2.5.0" syn = "2.0.100" quote = "1.0.40" rand = "0.8.5" +prettyplease = "0.2" cfg_aliases = "0.2.1" +proc-macro2 = "1" dtgen = { path = "xtasks/crates/dtgen" } [lints.rust] diff --git a/build.rs b/build.rs index eb314c7..c7db266 100644 --- a/build.rs +++ b/build.rs @@ -1,13 +1,12 @@ use std::process::Command; -use std::{collections::HashMap, fs, fs::File, path::Path, path::PathBuf}; +use std::{collections::HashMap, fs, fs::File, io::Write, path::Path, path::PathBuf}; extern crate rand; extern crate syn; extern crate walkdir; use cfg_aliases::cfg_aliases; -use quote::ToTokens; -use std::io::Write; +use quote::{format_ident, quote}; use syn::{Attribute, FnArg, LitInt, punctuated::Punctuated, token::Comma}; use walkdir::WalkDir; @@ -165,76 +164,82 @@ fn sparse_clone( // Syscalls --------------------------------------------------------------------------------------- +type SyscallData = u16; +type SyscallDataExport = (u16, Punctuated); + fn generate_syscalls_export>(root: P) -> Result<(), std::io::Error> { - let syscalls = collect_syscalls_export(root); + let syscalls = collect_syscalls(root); + let functions = syscalls.iter().map(|(name, (number, inputs))| { + let fn_name = format_ident!("{}", name); + let arg_names: Vec<_> = inputs + .iter() + .filter_map(|arg| match arg { + FnArg::Typed(pat_type) => match &*pat_type.pat { + syn::Pat::Ident(pat_ident) => Some(pat_ident.ident.clone()), + _ => None, + }, + _ => None, + }) + .collect(); + + quote! { + pub fn #fn_name(#inputs) { + hal::asm::syscall!(#number #(, #arg_names)*); + } + } + }); + + let tokens = quote! { #(#functions)* }; + + // publish let out_dir = std::env::var("OUT_DIR").unwrap(); let out_path = Path::new(&out_dir).join("syscalls_export.rs"); let mut file = File::create(out_path)?; - writeln!(file, "// This file is @generated by build.rs. Do not edit!")?; - - for (name, (number, inputs)) in &syscalls { - let mut args = &inputs.iter().fold("".to_owned(), |acc, arg| { - acc + "," + &arg.into_token_stream().to_string() - })[..]; - if !args.is_empty() { - args = &args[1..]; - } - let names = get_arg_names(args); - writeln!(file)?; - writeln!(file, "pub fn {name}({args}) {{")?; - writeln!(file, " hal::asm::syscall!({number}{names});")?; - writeln!(file, "}}")?; - } - + write!( + file, + "// This file is @generated by build.rs. Do not edit!\n\n{}", + prettyplease::unparse(&syn::parse2(tokens).unwrap()) + )?; Ok(()) } -fn get_arg_names(args: &str) -> String { - if args.is_empty() { - return "".to_string(); - } - let mut in_arg_name = true; - - ", ".to_owned() - + &args.chars().fold("".to_owned(), |mut acc, char| { - if char.eq(&' ') { - in_arg_name = false; - return acc; - } - if char.eq(&',') { - in_arg_name = true; - return acc + ", "; - } - if in_arg_name { - acc.push(char); - } - acc - }) -} - fn generate_syscall_map>(root: P) -> Result<(), std::io::Error> { - let syscalls = collect_syscalls(root); + let syscalls = collect_syscalls(root) + .into_iter() + // map from SyscallDataExport to SyscallData + .map(|(name, (num, _))| (name, num)) + .collect::>(); + + let arms = syscalls.iter().map(|(name, &number)| { + let entry = format_ident!("entry_{}", name); + + // the wrapper context defines what type the literal correspondences to + let literal = proc_macro2::Literal::u16_unsuffixed(number); + quote! { + #literal => #entry(args), + } + }); + + let tokens = quote! { + match number { + #(#arms)* + _ => panic!("Unknown syscall number: {}", number), + } + }; + // publish let out_dir = std::env::var("OUT_DIR").unwrap(); let out_path = Path::new(&out_dir).join("syscall_dispatcher.in"); let mut file = File::create(out_path)?; - writeln!(file, "// This file is @generated by build.rs. Do not edit!")?; - writeln!(file)?; - writeln!(file, "match number {{")?; - - for (name, number) in &syscalls { - writeln!(file, " {number} => entry_{name}(args),")?; - } - - writeln!( + // the token stream cannot be parsed since prettyplease cannot parse raw expressions + write!( file, - " _ => panic!(\"Unknown syscall number: {{}}\", number)," + "// This file is @generated by build.rs. Do not edit!\n\n{}", + tokens )?; - writeln!(file, "}}")?; - Ok(()) } @@ -274,112 +279,44 @@ fn is_syscall(attrs: &[Attribute], name: &str) -> Option { None } -type SyscallData = u16; +fn collect_syscalls>(root: P) -> HashMap { + let mut syscalls: HashMap = HashMap::new(); + let mut numbers: HashMap = HashMap::new(); -fn collect_syscalls>(root: P) -> HashMap { - let mut syscalls = HashMap::new(); - let mut numbers = HashMap::new(); + let entries = WalkDir::new(&root) + .into_iter() + .filter_map(|e| e.ok()) + .filter(|e| e.file_type().is_file()); - for entry in WalkDir::new(&root) { - let entry = match entry { - Ok(entry) => entry, - Err(_) => continue, + for entry in entries { + let Ok(contents) = std::fs::read_to_string(entry.path()) else { + continue; }; - - if entry.file_type().is_file() { - let path = entry.path(); - - println!("Processing file: {}", path.display()); - - let contents = match std::fs::read_to_string(path) { - Ok(contents) => contents, - Err(_) => continue, - }; - - let file = match syn::parse_file(&contents) { - Ok(file) => file, - Err(_) => continue, - }; - - for item in file.items { - let item = match item { - syn::Item::Fn(item) => item, - _ => continue, - }; - - let name = item.sig.ident.to_string(); - - if let Some(num) = is_syscall(&item.attrs, &name) { - if syscalls.contains_key(&name) { - println!("cargo::warning=Duplicate syscall handler: {name}"); - continue; - } - - if numbers.contains_key(&num) { - println!("cargo::warning=Duplicate syscall number: {num} for {name}"); - continue; - } - - syscalls.insert(name.clone(), num); - numbers.insert(num, name); - } - } - } - } - - syscalls -} - -type SyscallDataExport = (u16, Punctuated); - -fn collect_syscalls_export>(root: P) -> HashMap { - let mut syscalls = HashMap::new(); - let mut numbers = HashMap::new(); - - for entry in WalkDir::new(&root) { - let entry = match entry { - Ok(entry) => entry, - Err(_) => continue, + let Ok(file) = syn::parse_file(&contents) else { + continue; }; - if entry.file_type().is_file() { - let path = entry.path(); - - println!("Processing file: {}", path.display()); - - let contents = match std::fs::read_to_string(path) { - Ok(contents) => contents, - Err(_) => continue, - }; - - let file = match syn::parse_file(&contents) { - Ok(file) => file, - Err(_) => continue, + for item in file.items.into_iter().filter_map(|i| match i { + syn::Item::Fn(f) => Some(f), + _ => None, + }) { + let name = item.sig.ident.to_string(); + let Some(num) = is_syscall(&item.attrs, &name) else { + continue; }; - for item in file.items { - let item = match item { - syn::Item::Fn(item) => item, - _ => continue, - }; - - let name = item.sig.ident.to_string(); - - if let Some(num) = is_syscall(&item.attrs, &name) { - if syscalls.contains_key(&name) { - println!("cargo:warning=Duplicate syscall handler: {name}"); - continue; - } - - if numbers.contains_key(&num) { - println!("cargo:warning=Duplicate syscall number: {num} for {name}"); - continue; - } + if syscalls.contains_key(&name) { + println!("cargo::warning=Duplicate syscall handler: {name}"); + continue; + } - syscalls.insert(name.clone(), (num, item.sig.inputs)); - numbers.insert(num, name); - } + if numbers.contains_key(&num) { + println!("cargo::warning=Duplicate syscall number: {num} for {name}"); + continue; } + + numbers.insert(num, name.clone()); + syscalls.insert(name, (num, item.sig.inputs)); } } From 59968fde406089c8eb2fafc242eded1c2aebfc27 Mon Sep 17 00:00:00 2001 From: Luis Ruisinger Date: Fri, 3 Apr 2026 16:56:26 +0200 Subject: [PATCH 2/2] info print --- build.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.rs b/build.rs index c7db266..72665a8 100644 --- a/build.rs +++ b/build.rs @@ -289,9 +289,13 @@ fn collect_syscalls>(root: P) -> HashMap