From 2fd9cf11283e5f6eec151d0a68b7aaba27512406 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Fri, 8 Mar 2024 22:16:50 +0200 Subject: [PATCH 1/9] Initial commit --- .gitignore | 1 + Cargo.lock | 86 ++++++ Cargo.toml | 14 + example-abi/Cargo.toml | 7 + example-abi/build.rs | 25 ++ example-abi/src/main.rs | 118 ++++++++ example-abi/syscall.abi | 141 +++++++++ src/abi/mod.rs | 305 ++++++++++++++++++++ src/abi/ty/complex.rs | 109 +++++++ src/abi/ty/mod.rs | 161 +++++++++++ src/abi/ty/primitive.rs | 83 ++++++ src/abi/ty/simple.rs | 104 +++++++ src/error.rs | 22 ++ src/lib.rs | 141 +++++++++ src/parse.rs | 622 ++++++++++++++++++++++++++++++++++++++++ 15 files changed, 1939 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 example-abi/Cargo.toml create mode 100644 example-abi/build.rs create mode 100644 example-abi/src/main.rs create mode 100644 example-abi/syscall.abi create mode 100644 src/abi/mod.rs create mode 100644 src/abi/ty/complex.rs create mode 100644 src/abi/ty/mod.rs create mode 100644 src/abi/ty/primitive.rs create mode 100644 src/abi/ty/simple.rs create mode 100644 src/error.rs create mode 100644 src/lib.rs create mode 100644 src/parse.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..cb89790b --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,86 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "example-abi" +version = "0.1.0" +dependencies = [ + "syscall-generator", +] + +[[package]] +name = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syscall-generator" +version = "0.1.0" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "syn", + "thiserror", +] + +[[package]] +name = "thiserror" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..bc98dd3a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "syscall-generator" +version = "0.1.0" +edition = "2021" + +[dependencies] +prettyplease = "0.2.16" +proc-macro2 = "1.0.78" +quote = "1.0.35" +syn = { version = "2.0.52", features = ["full"] } +thiserror = "1.0.57" + +[workspace] +members = ["example-abi"] diff --git a/example-abi/Cargo.toml b/example-abi/Cargo.toml new file mode 100644 index 00000000..4495e912 --- /dev/null +++ b/example-abi/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "example-abi" +version = "0.1.0" +edition = "2021" + +[build-dependencies] +syscall-generator = { path = ".." } diff --git a/example-abi/build.rs b/example-abi/build.rs new file mode 100644 index 00000000..8ee074ad --- /dev/null +++ b/example-abi/build.rs @@ -0,0 +1,25 @@ +use std::{env, path::Path}; + +use syscall_generator::{ + abi::{ty::TypeWidth, AbiBuilder}, + parse::UnwrapFancy, + TargetEnv, +}; + +fn main() { + println!("cargo:rerun-if-changed={}", "syscall.abi"); + let output_dir = env::var("OUT_DIR").unwrap(); + let output = Path::new(&output_dir); + + let abi = AbiBuilder::from_file( + "syscall.abi", + TargetEnv { + thin_pointer_width: TypeWidth::U64, + fat_pointer_width: TypeWidth::U128, + }, + ) + .unwrap_fancy("syscall.abi"); + + abi.write(output.join("generated_abi.rs")) + .expect("Could not write the generated ABI file"); +} diff --git a/example-abi/src/main.rs b/example-abi/src/main.rs new file mode 100644 index 00000000..339efe1f --- /dev/null +++ b/example-abi/src/main.rs @@ -0,0 +1,118 @@ +#![feature(exposed_provenance)] + +use std::{num::NonZeroUsize, sync::Mutex}; + +#[derive(Debug)] +pub enum Error {} + +pub enum MappingSource {} +pub struct MountOptions; +pub struct UnmountOptions; +pub struct DirectoryEntry; +pub struct FileAttr; +pub struct SpawnOptions; +pub struct ThreadSpawnOptions; +pub enum MutexOperation {} +pub struct SignalEntryData; +pub struct DeviceRequest; +pub enum SentMessage {} +pub enum ReceivedMessageMetadata {} +pub struct TerminalOptions; +pub struct TerminalSize; +pub struct ExecveOptions; +pub struct FileMetadataUpdate; +pub enum ProcessInfoElement {} +pub enum SocketOption {} +pub enum SystemInfo {} + +impl FromSyscallResult for Result<(), Error> { + fn from_syscall_result(value: usize) -> Self { + if (value as isize) < 0 { + todo!() + } else { + Ok(()) + } + } +} + +impl FromSyscallResult for Result { + fn from_syscall_result(value: usize) -> Self { + if (value as isize) < 0 { + todo!() + } else { + Ok(value as _) + } + } +} + +impl IntoSyscallArgument for Option { + fn into_syscall_argument(self) -> usize { + match self { + Some(value) => value.into(), + None => 0, + } + } +} + +macro_rules! syscall { + ($f:expr $(,)?) => { + syscall0($f) + }; + ($f:expr, $a0:expr $(,)?) => { + syscall1($f, $a0) + }; + ($f:expr, $a0:expr, $a1:expr $(,)?) => { + syscall2($f, $a0, $a1) + }; + ($f:expr, $a0:expr, $a1:expr, $a2:expr $(,)?) => { + syscall3($f, $a0, $a1, $a2) + }; + ($f:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr $(,)?) => { + syscall4($f, $a0, $a1, $a2, $a3) + }; + ($f:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr $(,)?) => { + syscall5($f, $a0, $a1, $a2, $a3, $a4) + }; +} + +static SYSCALL_TRACE: Mutex)>> = Mutex::new(Vec::new()); + +fn syscall0(f: SyscallFunction) -> usize { + SYSCALL_TRACE.lock().unwrap().push((f, vec![])); + 0 +} + +fn syscall1(f: SyscallFunction, a0: usize) -> usize { + SYSCALL_TRACE.lock().unwrap().push((f, vec![a0])); + 0 +} + +fn syscall2(f: SyscallFunction, a0: usize, a1: usize) -> usize { + SYSCALL_TRACE.lock().unwrap().push((f, vec![a0, a1])); + 0 +} + +fn syscall3(f: SyscallFunction, a0: usize, a1: usize, a2: usize) -> usize { + SYSCALL_TRACE.lock().unwrap().push((f, vec![a0, a1, a2])); + 0 +} + +fn syscall4(f: SyscallFunction, a0: usize, a1: usize, a2: usize, a3: usize) -> usize { + SYSCALL_TRACE + .lock() + .unwrap() + .push((f, vec![a0, a1, a2, a3])); + 0 +} + +fn syscall5(f: SyscallFunction, a0: usize, a1: usize, a2: usize, a3: usize, a4: usize) -> usize { + SYSCALL_TRACE + .lock() + .unwrap() + .push((f, vec![a0, a1, a2, a3, a4])); + 0 +} + +include!(concat!(env!("OUT_DIR"), "/generated_abi.rs")); + +fn main() {} diff --git a/example-abi/syscall.abi b/example-abi/syscall.abi new file mode 100644 index 00000000..7352af28 --- /dev/null +++ b/example-abi/syscall.abi @@ -0,0 +1,141 @@ +// vi:syntax=yggdrasil_abi: + +extern { + type Duration = core::time::Duration; + type Mutex = core::sync::atomic::AtomicU32; + type SocketAddr = core::net::SocketAddr; + + type SystemInfo; + type MappingSource; + type SpawnOptions; + type ThreadSpawnOptions; + type MutexOperation; + type SignalEntryData; + type ProcessInfoElement; + type ExecveOptions; + type MountOptions; + type UnmountOptions; + type DirectoryEntry; + type FileAttr; + type DeviceRequest; + type SentMessage; + type ReceivedMessageMetadata; + type TerminalOptions; + type TerminalSize; + type FileMetadataUpdate; + type SocketOption; +}; + +newtype Fd(u32); +newtype FileMode(u32); +newtype ProcessId(u32); +newtype ThreadId(u32); +newtype ProcessGroupId(u32); + +// TODO define these as enums +newtype OpenOptions(u32); +newtype FileMode(u32); +newtype ExitCode(i32); +newtype SeekFrom(u64); +newtype Signal(u32); +newtype PollControl(u32); +newtype MessageDestination(u32); +newtype SocketType(u32); + +// Misc + +syscall debug_trace(msg: &str); +syscall get_random(buffer: &mut [u8]); +syscall get_system_info(info: &mut SystemInfo) -> Result; + +// Memory management + +syscall map_memory(hint: Option, len: usize, source: &MappingSource) -> Result; +syscall unmap_memory(virt: usize, len: usize) -> Result<()>; + +// Process/thread management + +syscall spawn_process(options: &SpawnOptions) -> Result; +syscall spawn_thread(options: &ThreadSpawnOptions) -> Result; + +syscall exit_thread(code: ExitCode) -> !; +syscall exit_process(code: ExitCode) -> !; +syscall wait_process(pid: ProcessId, status: &mut ExitCode) -> Result<()>; +syscall send_signal(pid: ProcessId, signal: Signal) -> Result<()>; +syscall nanosleep(duration: &Duration); + +syscall mutex(mutex: &Mutex, op: &MutexOperation) -> Result<()>; + +syscall set_signal_entry(entry: usize, stack: usize); +syscall exit_signal(frame: &SignalEntryData) -> !; + +syscall get_pid() -> ProcessId; + +syscall start_session() -> Result<()>; +syscall set_process_group_id(pid: ProcessId, pgid: Option) -> Result; + +syscall get_process_info(what: &mut ProcessInfoElement) -> Result<()>; +syscall set_process_info(what: &ProcessInfoElement) -> Result<()>; + +// C compat + +syscall fork() -> Result; +syscall execve(opts: &ExecveOptions) -> Result<()>; + +// I/O + +syscall write(fd: Fd, data: &[u8]) -> Result; +syscall read(fd: Fd, data: &mut [u8]) -> Result; +syscall open(at: Option, path: &str, opts: OpenOptions, mode: FileMode) -> Result; +syscall close(fd: Fd) -> Result<()>; +syscall mount(options: &MountOptions) -> Result<()>; +syscall unmount(options: &UnmountOptions) -> Result<()>; +syscall open_directory(at: Option, path: &str) -> Result; +syscall read_directory_entries(fd: Fd, buffer: &mut [MaybeUninit]) -> Result; +syscall create_directory(at: Option, path: &str, mode: FileMode) -> Result<()>; +syscall remove(at: Option, path: &str) -> Result<()>; +syscall remove_directory(at: Option, path: &str) -> Result<()>; +syscall get_metadata(at: Option, path: &str, metadata: &mut FileAttr, follow: bool) -> Result<()>; +// TODO this one is broken +syscall seek(fd: Fd, pos: SeekFrom) -> Result; + +syscall device_request(fd: Fd, req: &mut DeviceRequest) -> Result<()>; + +syscall create_pipe(ends: &mut [MaybeUninit; 2]) -> Result<()>; + +syscall create_poll_channel() -> Result; +syscall poll_channel_control(poll_fd: Fd, ctl: PollControl, fd: Fd) -> Result<()>; +syscall poll_channel_wait(poll_fd: Fd, timeout: &Option, out: &mut Option) -> Result<()>; + +syscall open_channel(name: &str, subscribe: bool) -> Result; +syscall send_message(fd: Fd, message: &SentMessage, destination: MessageDestination) -> Result<()>; +syscall receive_message( + fd: Fd, + metadata: &mut MaybeUninit, + buffer: &mut [u8], + from: &mut MaybeUninit +) -> Result; + +syscall create_shared_memory(size: usize) -> Result; +syscall create_pty(options: &TerminalOptions, size: &TerminalSize, fds: &mut [MaybeUninit; 2]) -> Result<()>; + +syscall clone_fd(source: Fd, target: Option) -> Result; + +syscall update_metadata(at: Option, path: &str, update: &FileMetadataUpdate) -> Result<()>; + +syscall create_timer(repeat: bool) -> Result; + +// Network + +syscall bind_socket(listen_address: &SocketAddr, ty: SocketType) -> Result; +syscall connect_socket( + socket_fd: Option, + remote_address: &SocketAddr, + ty: SocketType, + local_address: &mut MaybeUninit +) -> Result; +syscall send_to(socket_fd: Fd, data: &[u8], recepient: &Option) -> Result; +syscall receive_from(socket_fd: Fd, buffer: &mut [u8], sender: &mut MaybeUninit) -> Result; +syscall get_socket_option(socket_fd: Fd, option: &mut SocketOption) -> Result<()>; +syscall set_socket_option(socket_fd: Fd, option: &SocketOption) -> Result<()>; +syscall accept(listener: Fd, address: &mut MaybeUninit) -> Result; diff --git a/src/abi/mod.rs b/src/abi/mod.rs new file mode 100644 index 00000000..164da5c8 --- /dev/null +++ b/src/abi/mod.rs @@ -0,0 +1,305 @@ +use std::{collections::HashMap, fs::File, io::Write, path::Path, rc::Rc}; + +use proc_macro2::TokenStream; +use quote::{quote, TokenStreamExt}; +use syn::{punctuated::Punctuated, spanned::Spanned, token}; + +pub mod ty; + +use crate::{ + abi::ty::{ComplexType, SimpleType, Type, TypeWidth}, + error::Error, + parse::{parse_abi, ParseError}, + Syscall, TargetEnv, +}; + +#[derive(Debug)] +pub struct Abi { + pub types: HashMap>, + // Contains syscalls and their corresponding SyscallFunction enum variants + pub syscalls: Vec<(syn::Ident, Syscall)>, +} + +pub struct AbiBuilder { + abi: Abi, + target_env: TargetEnv, + emit_syscall_traits: bool, +} + +pub fn syscall_enum_variant_identifier(name: &syn::Ident) -> syn::Ident { + let name = name.to_string(); + let string = name.split('_').fold(String::new(), |mut acc, word| { + word.chars().enumerate().for_each(|(i, c)| { + if i == 0 { + acc.extend(c.to_uppercase()); + } else { + acc.push(c); + } + }); + acc + }); + syn::Ident::new(string.as_str(), name.span()) +} + +impl AbiBuilder { + pub fn from_file>(path: P, target_env: TargetEnv) -> Result { + let abi = Abi::read(path)?; + Ok(Self { + abi, + target_env, + emit_syscall_traits: true, + }) + } + + pub fn write>(self, path: P) -> Result<(), Error> { + // Generate preamble + let mut stream = TokenStream::new(); + + if self.emit_syscall_traits { + stream.append_all(quote! { + pub trait IntoSyscallArgument { + fn into_syscall_argument(self) -> usize; + } + + pub trait FromSyscallArgument { + fn from_syscall_argument(value: usize) -> Self; + } + + pub trait FromSyscallResult { + fn from_syscall_result(value: usize) -> Self; + } + }); + } + + let syscall_discriminants = self + .abi + .syscalls + .iter() + .enumerate() + .map(|(index, (variant, _))| { + quote! { #variant = #index } + }) + .collect::>(); + + stream.append_all(quote! { + #[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] + #[repr(usize)] + pub enum SyscallFunction { + #syscall_discriminants + } + }); + + stream.append_all(self.abi.generate_user_side(&self.target_env)); + + let file = syn::parse_file(&stream.to_string())?; + let out = prettyplease::unparse(&file); + + let mut output = File::create(path)?; + output.write_all(out.as_bytes())?; + + Ok(()) + } +} + +impl Abi { + pub fn read>(path: P) -> Result { + let data = std::fs::read_to_string(path)?; + parse_abi(&data) + } + + pub fn write>(&self, path: P) -> Result<(), Error> { + // TODO read TargetEnv + let target = TargetEnv { + thin_pointer_width: TypeWidth::U64, + fat_pointer_width: TypeWidth::U128, + }; + let mut output = File::create(path)?; + let stream = self.generate_user_side(&target); + let file = syn::parse_file(&stream.to_string())?; + let out = prettyplease::unparse(&file); + output.write_all(out.as_bytes())?; + Ok(()) + } + + pub fn generate_user_side(&self, env: &TargetEnv) -> TokenStream { + let mut stream = TokenStream::new(); + + for (_, ty) in &self.types { + let definition = + if let ComplexType::Simple(SimpleType::Transparent { name, inner }) = ty.as_ref() { + let ty = inner.as_rust_type(); + let mut res = quote! { + #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + #[repr(transparent)] + pub struct #name(pub(crate) #ty); + }; + + if inner.width(env) < env.thin_pointer_width { + res.append_all(quote! { + impl IntoSyscallArgument for Option<#name> { + fn into_syscall_argument(self) -> usize { + match self { + Some(value) => value.0 as usize, + None => usize::MAX + } + } + } + + impl FromSyscallArgument for Option<#name> { + fn from_syscall_argument(value: usize) -> Self { + match value { + usize::MAX => None, + _ => Some(#name(value as _)) + } + } + } + + impl FromSyscallResult for Result<#name, Error> { + fn from_syscall_result(value: usize) -> Self { + if (value as isize) < 0 { + todo!() + } else { + Ok(#name(value as _)) + } + } + } + + impl FromSyscallResult for #name { + fn from_syscall_result(value: usize) -> Self { + Self(value as _) + } + } + }); + }; + + res + } else if matches!(ty.as_ref(), ComplexType::Extern(_)) { + continue; + } else { + todo!() + }; + + stream.append_all(definition); + } + + for (variant, syscall) in &self.syscalls { + stream.append_all(syscall.emit_stub(variant, env)); + } + + stream + } +} + +#[cfg(test)] +mod tests { + use std::{collections::HashMap, rc::Rc}; + + use proc_macro2::Span; + use quote::quote; + use syn::{Ident, Visibility}; + + use crate::{ + abi::ty::{AbiPrimitive, ComplexType, SimpleType, TypeWidth}, + Syscall, TargetEnv, + }; + + use super::Abi; + + #[test] + fn generate_full_simple_abi() { + let target = TargetEnv { + thin_pointer_width: TypeWidth::U64, + fat_pointer_width: TypeWidth::U128, + }; + let fd = ComplexType::transparent(Ident::new("Fd", Span::call_site()), AbiPrimitive::U32); + let read_buf_ty = ComplexType::Simple(SimpleType::Slice { + mutable: true, + element: Rc::new(ComplexType::primitive(AbiPrimitive::U8)), + }); + let write_buf_ty = ComplexType::Simple(SimpleType::Slice { + mutable: false, + element: Rc::new(ComplexType::primitive(AbiPrimitive::U8)), + }); + + let read_syscall = Syscall { + name: Ident::new("read", Span::call_site()), + visibility: Visibility::Inherited, + args: vec![ + (Ident::new("fd", Span::call_site()), fd.clone()), + (Ident::new("buf", Span::call_site()), read_buf_ty), + ], + return_type: Some(ComplexType::SizeResult), + }; + let write_syscall = Syscall { + name: Ident::new("write", Span::call_site()), + visibility: Visibility::Inherited, + args: vec![ + (Ident::new("fd", Span::call_site()), fd.clone()), + (Ident::new("buf", Span::call_site()), write_buf_ty), + ], + return_type: Some(ComplexType::SizeResult), + }; + + let abi = Abi { + types: HashMap::from_iter([("Fd".to_owned(), fd)]), + syscalls: vec![read_syscall, write_syscall], + }; + + let result = abi.generate_user_side(&target); + assert_eq!( + result.to_string(), + quote! { + #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + #[repr(transparent)] + pub struct Fd(pub(crate) u32); + + impl IntoSyscallArgument for Option { + fn into_syscall_argument(self) -> usize { + match self { + Some(value) => value.0 as usize, + None => usize::MAX + } + } + } + + impl FromSyscallArgument for Option { + fn from_syscall_argument(value: usize) -> Self { + match value { + usize::MAX => None, + _ => Some(Fd(value as _)) + } + } + } + + impl FromSyscallResult for Result { + fn from_syscall_result(value: usize) -> Self { + if (value as isize) < 0 { + todo!() + } else { + Ok(Fd(value as _)) + } + } + } + + unsafe fn read(fd: Fd, buf: &mut [u8],) -> Result { + >::from_syscall_result(syscall!( + SyscallFunction::read, + fd.0 as usize, + buf.as_mut_ptr().expose_addr(), + buf.len(), + )) + } + + unsafe fn write(fd: Fd, buf: &[u8],) -> Result { + >::from_syscall_result(syscall!( + SyscallFunction::write, + fd.0 as usize, + buf.as_ptr().expose_addr(), + buf.len(), + )) + } + } + .to_string() + ); + } +} diff --git a/src/abi/ty/complex.rs b/src/abi/ty/complex.rs new file mode 100644 index 00000000..958b92c6 --- /dev/null +++ b/src/abi/ty/complex.rs @@ -0,0 +1,109 @@ +use std::{fmt, rc::Rc}; + +use proc_macro2::TokenStream; +use quote::quote; +use syn::Ident; + +use crate::{error::Error, TargetEnv}; + +use super::{AbiPrimitive, SimpleType, Type, TypeKind, TypeWidth}; + +#[derive(Clone)] +pub enum ComplexType { + Simple(SimpleType), + MaybeUninit(Rc), + Result(Rc), + Option(Rc), + Extern(syn::Type), +} + +impl ComplexType { + pub fn primitive(val: AbiPrimitive) -> Self { + Self::Simple(SimpleType::Primitive(val)) + } + + pub fn transparent(name: Ident, val: AbiPrimitive) -> Self { + Self::Simple(SimpleType::Transparent { name, inner: val }) + } + + pub fn to_simple_type(&self) -> Result { + todo!() + // if let Self::Simple(simple) = self { + // Ok(simple) + // } else { + // todo!("{:?}", self) + // } + } +} + +impl Type for ComplexType { + fn width(&self, env: &TargetEnv) -> TypeWidth { + match self { + Self::Simple(ty) => ty.width(env), + Self::Option(ty) | Self::Result(ty) => match ty.kind() { + TypeKind::Other => ty.width(env).double(), + // Some(&[1, 2, 3]) -> (ptr, len), where ptr and len are non-zero + // None -> (null, 0) + TypeKind::FatPointer => env.fat_pointer_width, + // Some(&x) -> ptr, where ptr is non-zero + // None -> null + TypeKind::ThinPointer => env.thin_pointer_width, + }, + Self::Extern(_) => TypeWidth::Unknown, + // MaybeUninit has the same layout as its underlying data + Self::MaybeUninit(ty) => ty.width(env), + } + } + + fn kind(&self) -> TypeKind { + match self { + Self::Simple(ty) => ty.kind(), + Self::MaybeUninit(ty) => ty.kind(), + _ => TypeKind::Other, + } + } + + fn as_rust_type(&self) -> TokenStream { + match self { + Self::Simple(ty) => ty.as_rust_type(), + Self::Option(ty) => { + let ty = ty.as_rust_type(); + quote!(Option<#ty>) + } + Self::Result(ty) => { + let ty = ty.as_rust_type(); + // TODO take from TargetEnv + quote!(Result<#ty, Error>) + } + Self::Extern(ty) => quote!(#ty), + Self::MaybeUninit(ty) => { + let ty = ty.as_rust_type(); + quote!(core::mem::MaybeUninit<#ty>) + } + } + } + + fn emit_to_syscall_arguments(&self, env: &TargetEnv, value: &Ident) -> TokenStream { + match self { + Self::Simple(ty) => ty.emit_to_syscall_arguments(env, value), + Self::Option(ty) + if ty.width(env) < env.thin_pointer_width || ty.kind() == TypeKind::ThinPointer => + { + quote!(#value.into_syscall_argument()) + } + _ => todo!(), + } + } +} + +impl fmt::Debug for ComplexType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Simple(ty) => f.debug_tuple("Simple").field(&ty).finish(), + Self::Result(ty) => f.debug_tuple("Result").field(&ty).finish(), + Self::Option(ty) => f.debug_tuple("Option").field(&ty).finish(), + Self::Extern(ty) => f.debug_tuple("Extern").field("e!(#ty)).finish(), + Self::MaybeUninit(ty) => f.debug_tuple("MaybeUninit").field(&ty).finish(), + } + } +} diff --git a/src/abi/ty/mod.rs b/src/abi/ty/mod.rs new file mode 100644 index 00000000..04a76bce --- /dev/null +++ b/src/abi/ty/mod.rs @@ -0,0 +1,161 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::Ident; + +use crate::TargetEnv; + +pub mod complex; +pub mod primitive; +pub mod simple; + +pub use complex::ComplexType; +pub use primitive::AbiPrimitive; +pub use simple::SimpleType; + +#[derive(PartialEq, Eq)] +pub enum TypeKind { + ThinPointer, + FatPointer, + Other, +} + +pub trait Type { + fn width(&self, env: &TargetEnv) -> TypeWidth; + fn kind(&self) -> TypeKind; + fn as_rust_type(&self) -> TokenStream; + + fn emit_to_syscall_arguments(&self, env: &TargetEnv, value: &Ident) -> TokenStream; + + fn as_rust_ref_type(&self, mutable: bool) -> TokenStream { + let inner = self.as_rust_type(); + match mutable { + true => quote!(&mut #inner), + false => quote!(& #inner), + } + } + + fn as_rust_slice_type(&self, mutable: bool) -> TokenStream { + let inner = self.as_rust_type(); + match mutable { + true => quote!(&mut [#inner]), + false => quote!(&[#inner]), + } + } + + fn as_rust_array_type(&self, mutable: bool, length: usize) -> TokenStream { + let inner = self.as_rust_type(); + match mutable { + true => quote!(&mut [#inner; #length]), + false => quote!(&[#inner; #length]), + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] +pub enum TypeWidth { + Zero, + U8, + U16, + U32, + U64, + U128, + Unknown, +} + +impl TypeWidth { + pub fn double(&self) -> Self { + match self { + Self::Zero => todo!(), + Self::U8 => Self::U16, + Self::U16 => Self::U32, + Self::U32 => Self::U64, + Self::U64 => Self::U128, + Self::U128 => todo!(), + Self::Unknown => Self::Unknown, + } + } +} + +#[cfg(test)] +mod tests { + use std::rc::Rc; + + use proc_macro2::Span; + use quote::quote; + use syn::Ident; + + use crate::abi::ty::Type; + + use super::{AbiPrimitive, ComplexType, SimpleType, TargetEnv, TypeWidth}; + + const TARGET_64: TargetEnv = TargetEnv { + thin_pointer_width: TypeWidth::U64, + fat_pointer_width: TypeWidth::U128, + }; + + #[test] + fn type_width() { + let ty = SimpleType::Reference { + mutable: false, + pointee: Rc::new(ComplexType::Simple(SimpleType::Primitive( + AbiPrimitive::U32, + ))), + }; + assert_eq!(ty.width(&TARGET_64), TypeWidth::U64); + + let ty = SimpleType::Primitive(AbiPrimitive::U64); + assert_eq!(ty.width(&TARGET_64), TypeWidth::U64); + } + + #[test] + fn option_width() { + let ty = ComplexType::Option(Rc::new(ComplexType::primitive(AbiPrimitive::U64))); + assert_eq!(ty.width(&TARGET_64), TypeWidth::U128); + + let ty = ComplexType::Option(Rc::new(ComplexType::Simple(SimpleType::Reference { + mutable: true, + pointee: Rc::new(ComplexType::Simple(SimpleType::Primitive( + AbiPrimitive::U64, + ))), + }))); + assert_eq!(ty.width(&TARGET_64), TypeWidth::U64); + } + + #[test] + fn simple_rust_types() { + let ty = ComplexType::Option(Rc::new(ComplexType::primitive(AbiPrimitive::U32))); + assert_eq!( + quote!(Option).to_string(), + ty.as_rust_type().to_string() + ); + + let ty = SimpleType::Reference { + mutable: true, + pointee: Rc::new(ComplexType::Simple(SimpleType::Primitive( + AbiPrimitive::U64, + ))), + }; + assert_eq!(quote!(&mut u64).to_string(), ty.as_rust_type().to_string()); + + let ty = SimpleType::Slice { + mutable: false, + element: Rc::new(ComplexType::Simple(SimpleType::Primitive(AbiPrimitive::U8))), + }; + assert_eq!(quote!(&[u8]).to_string(), ty.as_rust_type().to_string()); + } + + #[test] + fn emit_to_syscall_arguments() { + let ty = SimpleType::Reference { + mutable: false, + pointee: Rc::new(ComplexType::Simple(SimpleType::Primitive( + AbiPrimitive::U64, + ))), + }; + let val = Ident::new("val0", Span::call_site()); + assert_eq!( + ty.emit_to_syscall_arguments(&TARGET_64, &val).to_string(), + quote!((val0 as *const _).expose_addr()).to_string() + ); + } +} diff --git a/src/abi/ty/primitive.rs b/src/abi/ty/primitive.rs new file mode 100644 index 00000000..804d8c68 --- /dev/null +++ b/src/abi/ty/primitive.rs @@ -0,0 +1,83 @@ +use std::str::FromStr; + +use proc_macro2::TokenStream; +use quote::quote; +use syn::Ident; + +use crate::TargetEnv; + +use super::{Type, TypeKind, TypeWidth}; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum AbiPrimitive { + Unit, + Bool, + U8, + I8, + U16, + I16, + U32, + I32, + U64, + I64, + USize, +} + +impl Type for AbiPrimitive { + fn width(&self, env: &TargetEnv) -> TypeWidth { + match self { + Self::Unit => TypeWidth::Zero, + Self::U8 | Self::I8 => TypeWidth::U8, + Self::U16 | Self::I16 => TypeWidth::U16, + Self::U32 | Self::I32 => TypeWidth::U32, + Self::U64 | Self::I64 => TypeWidth::U64, + Self::USize => env.thin_pointer_width, + // ??? + Self::Bool => TypeWidth::U32, + } + } + + fn kind(&self) -> TypeKind { + TypeKind::Other + } + + fn as_rust_type(&self) -> TokenStream { + match self { + Self::Unit => quote!(()), + Self::U8 => quote!(u8), + Self::I8 => quote!(i8), + Self::U16 => quote!(u16), + Self::I16 => quote!(i16), + Self::U32 => quote!(u32), + Self::I32 => quote!(i32), + Self::U64 => quote!(u64), + Self::I64 => quote!(i64), + Self::USize => quote!(usize), + Self::Bool => quote!(bool), + } + } + + fn emit_to_syscall_arguments(&self, _env: &TargetEnv, value: &Ident) -> TokenStream { + quote!(#value as usize) + } +} + +impl FromStr for AbiPrimitive { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "i8" => Ok(Self::I8), + "u8" => Ok(Self::U8), + "i16" => Ok(Self::I16), + "u16" => Ok(Self::U16), + "i32" => Ok(Self::I32), + "u32" => Ok(Self::U32), + "i64" => Ok(Self::I64), + "u64" => Ok(Self::U64), + "bool" => Ok(Self::Bool), + "usize" => Ok(Self::USize), + _ => Err(()), + } + } +} diff --git a/src/abi/ty/simple.rs b/src/abi/ty/simple.rs new file mode 100644 index 00000000..c664802e --- /dev/null +++ b/src/abi/ty/simple.rs @@ -0,0 +1,104 @@ +use std::rc::Rc; + +use proc_macro2::TokenStream; +use quote::quote; +use syn::Ident; + +use crate::TargetEnv; + +use super::{AbiPrimitive, ComplexType, Type, TypeKind, TypeWidth}; + +#[derive(Debug, Clone)] +pub enum SimpleType { + Primitive(AbiPrimitive), + Str, + NonZeroUsize, + Never, + Transparent { + name: Ident, + inner: AbiPrimitive, + }, + Reference { + mutable: bool, + pointee: Rc, + }, + Slice { + mutable: bool, + element: Rc, + }, + Array { + mutable: bool, + element: Rc, + length: usize, + }, +} + +impl Type for SimpleType { + fn width(&self, env: &TargetEnv) -> TypeWidth { + match self { + Self::Str => env.fat_pointer_width, + Self::NonZeroUsize => env.thin_pointer_width, + Self::Reference { .. } => env.thin_pointer_width, + Self::Array { .. } => env.thin_pointer_width, + Self::Slice { .. } => env.fat_pointer_width, + Self::Primitive(ty) => ty.width(env), + Self::Transparent { inner, .. } => inner.width(env), + Self::Never => TypeWidth::Zero, + } + } + + fn kind(&self) -> TypeKind { + match self { + Self::Str => TypeKind::FatPointer, + Self::NonZeroUsize => TypeKind::ThinPointer, + Self::Primitive(ty) => ty.kind(), + Self::Transparent { inner, .. } => inner.kind(), + Self::Reference { .. } => TypeKind::ThinPointer, + Self::Array { .. } => TypeKind::ThinPointer, + Self::Slice { .. } => TypeKind::FatPointer, + Self::Never => TypeKind::Other, + } + } + + fn as_rust_type(&self) -> TokenStream { + match self { + Self::Str => quote!(&str), + Self::NonZeroUsize => quote!(core::num::NonZeroUsize), + Self::Reference { mutable, pointee } => pointee.as_rust_ref_type(*mutable), + Self::Array { + mutable, + element, + length, + } => element.as_rust_array_type(*mutable, *length), + Self::Slice { mutable, element } => element.as_rust_slice_type(*mutable), + Self::Primitive(ty) => ty.as_rust_type(), + Self::Transparent { name, .. } => quote!(#name), + Self::Never => quote!(!), + } + } + + fn emit_to_syscall_arguments(&self, env: &TargetEnv, value: &Ident) -> TokenStream { + match self { + Self::NonZeroUsize => quote!(#value.into()), + Self::Primitive(ty) => ty.emit_to_syscall_arguments(env, value), + Self::Transparent { .. } => quote!(#value.0 as usize), + Self::Str => quote!(#value.as_ptr().expose_addr(), #value.len()), + Self::Never => todo!("Prevent Never type from being a syscall argument"), + Self::Reference { mutable, pointee } => { + let pointee = pointee.as_rust_type(); + match mutable { + true => quote!((#value as *mut #pointee).expose_addr()), + false => quote!((#value as *const #pointee).expose_addr()), + } + } + Self::Array { mutable, .. } => match mutable { + true => quote!(#value.as_mut_ptr().expose_addr()), + false => quote!(#value.as_ptr().expose_addr()), + }, + Self::Slice { mutable, .. } => match mutable { + true => quote!(#value.as_mut_ptr().expose_addr(), #value.len()), + false => quote!(#value.as_ptr().expose_addr(), #value.len()), + }, + } + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 00000000..cf615516 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,22 @@ +use std::io; + +use proc_macro2::Span; +use syn::Ident; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0}")] + IoError(#[from] io::Error), + + #[error("{1:?}: Incorrectly constructed type: {0}")] + TypeError(String, Span), + #[error("{0:?}: Unhandled type name")] + UnhandledTypeNameError(Ident), + #[error("{0:?}: Only primitive types are allowed inside newtype X(Y) constructs")] + UnhandledNonPrimitive(Span), + + #[error("{0}")] + LexError(#[from] proc_macro2::LexError), + #[error("{0}")] + ParseError(#[from] syn::parse::Error), +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..ac45a39a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,141 @@ +#![feature(if_let_guard, extend_one, proc_macro_span)] + +use std::{fmt, rc::Rc}; + +use proc_macro2::TokenStream; +use quote::quote; +use syn::Ident; + +use crate::abi::ty::{ComplexType, SimpleType, Type, TypeWidth}; + +pub mod abi; +pub mod error; +pub mod parse; + +pub struct Syscall { + pub name: Ident, + pub args: Vec<(Ident, Rc)>, + pub return_type: Option>, +} + +impl fmt::Debug for Syscall { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Syscall") + .field("name", &self.name) + .field("args", &self.args) + .field("return_type", &self.return_type) + .finish_non_exhaustive() + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct TargetEnv { + pub thin_pointer_width: TypeWidth, + pub fat_pointer_width: TypeWidth, +} + +impl Syscall { + pub fn emit_stub(&self, enum_variant: &syn::Ident, env: &TargetEnv) -> TokenStream { + let Self { + name, + args, + return_type, + } = self; + + let syscall_args = args + .iter() + .map(|(name, ty)| { + let arg = ty.emit_to_syscall_arguments(env, name); + quote!(#arg,) + }) + .collect::(); + let args = args + .iter() + .map(|(name, ty)| { + let ty = ty.as_rust_type(); + quote!(#name: #ty,) + }) + .collect::(); + let return_type_arrow = return_type.as_ref().map(|ty| { + let ty = ty.as_rust_type(); + quote!(-> #ty) + }); + + let sys_call = quote!(syscall!(SyscallFunction::#enum_variant, #syscall_args)); + + let wrapped = match return_type.as_deref() { + Some(ComplexType::Simple(SimpleType::Never)) => { + quote! { + #sys_call; + unreachable!() + } + } + Some(ty) => { + let ty = ty.as_rust_type(); + quote!(<#ty>::from_syscall_result(#sys_call)) + } + None => quote!(#sys_call;), + }; + + let v = quote! { + pub unsafe fn #name(#args) #return_type_arrow { + #wrapped + } + }; + println!("{}", v); + v + } +} + +#[cfg(test)] +mod tests { + use std::rc::Rc; + + use proc_macro2::Span; + use quote::quote; + use syn::Ident; + + use crate::{ + abi::ty::{AbiPrimitive, ComplexType, SimpleType, TypeWidth}, + Syscall, TargetEnv, + }; + + #[test] + fn emit_syscall_stub() { + let target = TargetEnv { + thin_pointer_width: TypeWidth::U64, + fat_pointer_width: TypeWidth::U128, + }; + let syscall = Syscall { + name: Ident::new("read", Span::call_site()), + args: vec![ + ( + Ident::new("fd", Span::call_site()), + ComplexType::Simple(SimpleType::Primitive(AbiPrimitive::U32)), + ), + ( + Ident::new("buffer", Span::call_site()), + ComplexType::Simple(SimpleType::Slice { + mutable: true, + element: Rc::new(ComplexType::Simple(SimpleType::Primitive( + AbiPrimitive::U8, + ))), + }), + ), + ], + return_type: Some(ComplexType::Result(SimpleType::Primitive( + AbiPrimitive::USize, + ))), + }; + let res = syscall.emit_stub(&target); + assert_eq!( + res.to_string(), + quote! { + unsafe fn read(fd: u32, buffer: &mut [u8],) -> Result { + >::from_syscall_result(syscall!(SyscallFunction::read, fd as usize, buffer.as_mut_ptr().expose_addr(), buffer.len(),)) + } + } + .to_string() + ); + } +} diff --git a/src/parse.rs b/src/parse.rs new file mode 100644 index 00000000..734ff60f --- /dev/null +++ b/src/parse.rs @@ -0,0 +1,622 @@ +use std::{collections::HashMap, fmt, path::Path, rc::Rc, str::FromStr}; + +use proc_macro2::Span; +use quote::quote; +use syn::{ + braced, parenthesized, parse::ParseStream, punctuated::Punctuated, spanned::Spanned, token, + Expr, ExprLit, Ident, Lit, PathSegment, Token, +}; + +use crate::{ + abi::{ + syscall_enum_variant_identifier, + ty::{AbiPrimitive, ComplexType, SimpleType}, + Abi, + }, + Syscall, +}; + +/* +Document syntax: + +newtype SomeType = u32; +newtype Fd(u32); + +syscall read(fd: Fd, buf: &mut [u8]) -> SizeResult; +syscall write(fd: Fd, buf: &[u8]) -> SizeResult; + + */ + +pub enum SyntaxError { + UnhandledType(syn::Type), + UndefinedIdentifier(syn::Ident), +} + +pub enum ParseError { + HardError(syn::parse::Error), + IoError(std::io::Error), + SyntaxError(Vec), +} + +pub trait UnwrapFancy { + fn unwrap_fancy>(self, context: P) -> T; +} + +impl From for ParseError { + fn from(value: std::io::Error) -> Self { + Self::IoError(value) + } +} + +impl From for ParseError { + fn from(value: syn::parse::Error) -> Self { + Self::HardError(value) + } +} + +impl From for ParseError { + fn from(value: SyntaxError) -> Self { + Self::SyntaxError(vec![value]) + } +} + +impl SyntaxError { + pub fn span(&self) -> Span { + match self { + Self::UndefinedIdentifier(id) => id.span(), + Self::UnhandledType(ty) => ty.span(), + } + } + + pub fn describe(&self) -> String { + match self { + Self::UnhandledType(_ty) => "Unhandled type".into(), + Self::UndefinedIdentifier(id) => format!("Undefined identifier '{}'", id), + } + } +} + +impl UnwrapFancy for Result { + fn unwrap_fancy>(self, path: P) -> T { + match self { + Self::Ok(value) => value, + Self::Err(err) => { + match err { + ParseError::IoError(err) => { + eprintln!("{}: {}", path.as_ref().display(), err); + } + ParseError::HardError(err) => { + eprintln!("{}: {}", path.as_ref().display(), err); + } + ParseError::SyntaxError(errs) => { + eprintln!("{}:", path.as_ref().display()); + for err in errs { + eprintln!("* ...: {}", err.describe()); + } + } + } + panic!("Compilation aborted"); + } + } + } +} + +impl ParseError { + pub fn syntax1(e: SyntaxError) -> Self { + Self::SyntaxError(vec![e]) + } + + pub fn fold>, Q: Extend>( + mut acc: Q, + it: I, + ) -> Result { + let mut errors = vec![]; + for item in it { + match item { + Ok(val) => acc.extend_one(val), + Err(ParseError::SyntaxError(err)) => errors.extend(err), + Err(err) => return Err(err), + } + } + if errors.is_empty() { + Ok(acc) + } else { + Err(ParseError::SyntaxError(errors)) + } + } +} + +#[derive(Debug)] +pub struct Document { + newtypes: Vec, + syscalls: Vec, +} + +enum NewTypeDefinition { + Alias(syn::Type), + Transparent { + repr: syn::Type, + #[allow(dead_code)] + sentinel: Option, + }, + Extern(syn::Type), +} + +struct SyscallDefinition { + name: Ident, + arguments: Punctuated, + return_type: Option, +} + +struct Argument { + name: Ident, + ty: syn::Type, +} + +#[derive(Debug)] +struct NewType { + name: Ident, + definition: NewTypeDefinition, +} + +struct ExternTypeItem { + name: Ident, + ty: syn::Type, +} + +enum TopLevel { + NewType(NewType), + ExternBlock(Punctuated), + Syscall(SyscallDefinition), +} + +impl syn::parse::Parse for SyscallDefinition { + fn parse(input: ParseStream) -> syn::Result { + let arguments; + + let name = input.parse()?; + parenthesized!(arguments in input); + let arguments = arguments.parse_terminated(Argument::parse, Token![,])?; + + let return_type = if input.peek(Token![->]) { + input.parse::]>()?; + let ty = input.parse()?; + Some(ty) + } else { + None + }; + + Ok(Self { + name, + arguments, + return_type, + }) + } +} + +impl syn::parse::Parse for Argument { + fn parse(input: ParseStream) -> syn::Result { + let name = input.parse()?; + input.parse::()?; + let ty = input.parse()?; + + Ok(Self { name, ty }) + } +} + +impl syn::parse::Parse for NewType { + fn parse(input: ParseStream) -> syn::Result { + let name = input.parse()?; + if input.peek(Token![=]) { + let _token: Token![=] = input.parse()?; + let ty = input.parse()?; + + Ok(Self { + name, + definition: NewTypeDefinition::Alias(ty), + }) + } else { + // Parenthesized definition + let content; + parenthesized!(content in input); + + // First value has to be a type + let ty = content.parse()?; + + Ok(Self { + name, + definition: NewTypeDefinition::Transparent { + repr: ty, + sentinel: None, + }, + }) + } + } +} + +impl syn::parse::Parse for ExternTypeItem { + fn parse(input: ParseStream) -> syn::Result { + input.parse::()?; + let name: syn::Ident = input.parse()?; + + let ty = if input.peek(Token![=]) { + input.parse::()?; + input.parse()? + } else { + syn::Type::Path(syn::TypePath { + qself: None, + path: syn::Path { + leading_colon: None, + segments: Punctuated::from_iter([syn::PathSegment { + arguments: syn::PathArguments::None, + ident: name.clone(), + }]), + }, + }) + }; + + Ok(Self { name, ty }) + } +} + +impl syn::parse::Parse for TopLevel { + fn parse(input: ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + if lookahead.peek(Ident) { + let ident: Ident = input.parse()?; + match ident.to_string().as_str() { + "newtype" => return NewType::parse(input).map(Self::NewType), + "syscall" => return SyscallDefinition::parse(input).map(Self::Syscall), + _ => (), + } + } else if lookahead.peek(token::Extern) { + input.parse::()?; + let lookahead = input.lookahead1(); + if lookahead.peek(Token![type]) { + input.parse::()?; + let name = input.parse()?; + input.parse::()?; + let ty = input.parse()?; + return Ok(Self::NewType(NewType { + name, + definition: NewTypeDefinition::Extern(ty), + })); + } else if lookahead.peek(token::Brace) { + let content; + braced!(content in input); + let items = content.parse_terminated(ExternTypeItem::parse, Token![;])?; + return Ok(Self::ExternBlock(items)); + } else { + return Err(input.error(&format!( + "Expected 'extern type' or 'extern {{ ... }}': {}", + lookahead.error() + ))); + } + } + + Err(input.error(&format!( + "Expected 'extern', 'newtype' or 'syscall', got '{}'", + lookahead.error() + ))) + } +} + +impl syn::parse::Parse for Document { + fn parse(input: ParseStream) -> syn::Result { + let mut newtypes = vec![]; + let mut syscalls = vec![]; + + loop { + if input.is_empty() { + break; + } + + let toplevel = TopLevel::parse(input)?; + input.parse::()?; + + match toplevel { + TopLevel::NewType(ty) => newtypes.push(ty), + TopLevel::ExternBlock(extys) => { + for exty in extys { + newtypes.push(NewType { + name: exty.name, + definition: NewTypeDefinition::Extern(exty.ty), + }); + } + } + TopLevel::Syscall(syscall) => syscalls.push(syscall), + } + } + + Ok(Self { newtypes, syscalls }) + } +} + +impl fmt::Debug for Argument { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let ty = &self.ty; + f.debug_tuple("Argument") + .field(&self.name) + .field("e!(#ty)) + .finish() + } +} + +impl fmt::Debug for SyscallDefinition { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let args: Vec<_> = self.arguments.iter().collect(); + let return_type = self.return_type.as_ref().map(|ty| quote!(#ty)); + + f.debug_struct("SyscallDefinition") + .field("name", &self.name) + .field("args", &args) + .field("return_type", &return_type) + .finish() + } +} + +impl fmt::Debug for NewTypeDefinition { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Transparent { repr, .. } => f + .debug_struct("NewTypeDefinition::Transparent") + .field("repr", "e!(#repr)) + .finish(), + Self::Alias(ty) => f + .debug_tuple("NewTypeDefinition::Alias") + .field("e!(#ty)) + .finish(), + Self::Extern(ty) => f + .debug_tuple("NewTypeDefinition::Extern") + .field("e!(#ty)) + .finish(), + } + } +} + +pub fn parse_abi_document(input: &str) -> Result { + let document: Document = syn::parse_str(input)?; + Ok(document) +} + +fn as_single_segment_path(path: &syn::TypePath) -> Option<&PathSegment> { + if path.qself.is_some() || path.path.leading_colon.is_some() || path.path.segments.len() != 1 { + return None; + } + + Some(&path.path.segments[0]) +} + +fn as_single_type_argument(args: &syn::PathArguments) -> Option<&syn::Type> { + let syn::PathArguments::AngleBracketed(args) = args else { + return None; + }; + if args.args.len() != 1 { + return None; + } + let syn::GenericArgument::Type(ty) = &args.args[0] else { + return None; + }; + + Some(ty) +} + +fn as_const_array_len(len: &Expr) -> Option { + let Expr::Lit(ExprLit { + attrs, + lit: Lit::Int(lit), + }) = len + else { + return None; + }; + + if !attrs.is_empty() { + todo!() + } + + lit.base10_parse().ok() +} + +fn process_type( + t: &syn::Type, + known_types: &HashMap>, +) -> Result, ParseError> { + match t { + syn::Type::Path(path) => { + let segment = as_single_segment_path(path).expect("TODO: more complex paths"); + + match segment.ident.to_string().as_str() { + seg if let Ok(ty) = AbiPrimitive::from_str(seg) => { + Ok(Rc::new(ComplexType::primitive(ty))) + } + "NonZeroUsize" => Ok(Rc::new(ComplexType::Simple(SimpleType::NonZeroUsize))), + "Result" if let Some(ty) = as_single_type_argument(&segment.arguments) => { + let ty = process_type(ty, known_types)?; + Ok(Rc::new(ComplexType::Result(ty))) + } + "Option" if let Some(ty) = as_single_type_argument(&segment.arguments) => { + let ty = process_type(ty, known_types)?; + Ok(Rc::new(ComplexType::Option(ty))) + } + "MaybeUninit" if let Some(ty) = as_single_type_argument(&segment.arguments) => { + let ty = process_type(ty, known_types)?; + Ok(Rc::new(ComplexType::MaybeUninit(ty))) + } + name if let Some(ty) = known_types.get(name) => Ok(ty.clone()), + _ => Err(ParseError::syntax1(SyntaxError::UndefinedIdentifier( + segment.ident.clone(), + ))), + } + } + syn::Type::Never(_) => Ok(Rc::new(ComplexType::Simple(SimpleType::Never))), + syn::Type::Reference(r) => { + // TODO lifetimes + if r.lifetime.is_some() { + todo!() + } + + let mutable = r.mutability.is_some(); + + match r.elem.as_ref() { + syn::Type::Slice(ty) => { + let element = process_type(&ty.elem, known_types)?; + Ok(Rc::new(ComplexType::Simple(SimpleType::Slice { + mutable, + element, + }))) + } + syn::Type::Path(path) + if !mutable + && as_single_segment_path(path) + .map(|seg| seg.ident.to_string() == "str" && seg.arguments.is_empty()) + .unwrap_or(false) => + { + Ok(Rc::new(ComplexType::Simple(SimpleType::Str))) + } + syn::Type::Array(array) if let Some(length) = as_const_array_len(&array.len) => { + let element = process_type(&array.elem, known_types)?; + Ok(Rc::new(ComplexType::Simple(SimpleType::Array { + mutable, + element, + length, + }))) + } + _ => { + let pointee = process_type(&r.elem, known_types)?; + Ok(Rc::new(ComplexType::Simple(SimpleType::Reference { + mutable, + pointee, + }))) + } + } + } + syn::Type::Tuple(t) if t.elems.is_empty() => { + Ok(Rc::new(ComplexType::primitive(AbiPrimitive::Unit))) + } + _ => todo!(), + } +} + +fn process_newtype( + t: &NewType, + known_types: &HashMap>, +) -> Result<(Ident, Rc), ParseError> { + let ty = match &t.definition { + NewTypeDefinition::Transparent { repr, .. } => { + let ty = process_type(repr, known_types)?; + if let ComplexType::Simple(SimpleType::Primitive(ty)) = ty.as_ref() { + Rc::new(ComplexType::Simple(SimpleType::Transparent { + name: t.name.clone(), + inner: *ty, + })) + } else { + todo!() + } + } + NewTypeDefinition::Extern(ty) => Rc::new(ComplexType::Extern(ty.clone())), + NewTypeDefinition::Alias(ty) => process_type(ty, known_types)?, + }; + Ok((t.name.clone(), ty)) +} + +pub fn parse_abi(input: &str) -> Result { + let doc = parse_abi_document(input)?; + let mut abi = Abi { + types: HashMap::new(), + syscalls: Vec::new(), + }; + + ParseError::fold( + (), + doc.newtypes.iter().map(|nt| { + let (name, value) = process_newtype(nt, &abi.types)?; + abi.types.insert(name.to_string(), value); + Ok(()) + }), + )?; + + ParseError::fold( + (), + doc.syscalls.iter().map(|syscall| { + let args = syscall + .arguments + .iter() + .map(|arg| { + let ty = process_type(&arg.ty, &abi.types)?; + Ok((arg.name.clone(), ty)) + }) + .collect::, ParseError>>()?; + let return_type = syscall + .return_type + .as_ref() + .map(|ty| process_type(ty, &abi.types)) + .transpose()?; + + let variant = syscall_enum_variant_identifier(&syscall.name); + let def = Syscall { + name: syscall.name.clone(), + args, + return_type, + }; + + abi.syscalls.push((variant, def)); + + Ok(()) + }), + )?; + + Ok(abi) +} + +#[cfg(test)] +mod tests { + use quote::quote; + + use crate::{abi::ty::TypeWidth, parse::UnwrapFancy, TargetEnv}; + + use super::parse_abi; + + const TARGET_64: TargetEnv = TargetEnv { + thin_pointer_width: TypeWidth::U64, + fat_pointer_width: TypeWidth::U128, + }; + + #[test] + fn emit_basic_syscall() { + let abi = parse_abi( + r#" + extern type MyStruct = some_lib::MyStruct; + newtype Fd(u32); + + syscall read(fd: Fd, buf: &mut [u8]) -> SizeResult; + syscall get_file_info(data: &mut MyStruct); + "#, + ) + .unwrap_fancy(""); + + assert_eq!( + abi.syscalls[0].emit_stub(&TARGET_64).to_string(), + quote! { + unsafe fn read(fd: Fd, buf: &mut [u8],) -> Result { + >::from_syscall_result(syscall!( + SyscallFunction::read, + fd.0 as usize, + buf.as_mut_ptr().expose_addr(), + buf.len(), + )) + } + } + .to_string() + ); + + assert_eq!( + abi.syscalls[1].emit_stub(&TARGET_64).to_string(), + quote! { + unsafe fn get_file_info(data: &mut some_lib::MyStruct,) { + syscall!(SyscallFunction::get_file_info, (data as *mut _).expose_addr(),); + } + } + .to_string() + ); + } +} From a31f566ea930c872dc3b7e14cb364a375005bfaa Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Sat, 9 Mar 2024 12:24:08 +0200 Subject: [PATCH 2/9] Clean up syntax parsing --- example-abi/build.rs | 2 +- example-abi/src/main.rs | 29 +- example-abi/syscall.abi | 63 +++- src/abi/mod.rs | 378 ++++++++++++---------- src/abi/ty/complex.rs | 17 +- src/abi/ty/simple.rs | 2 +- src/lib.rs | 4 +- src/parse.rs | 622 ------------------------------------ src/syntax/bitfield_type.rs | 120 +++++++ src/syntax/common.rs | 54 ++++ src/syntax/document.rs | 135 ++++++++ src/syntax/enum_type.rs | 97 ++++++ src/syntax/error.rs | 103 ++++++ src/syntax/extern_block.rs | 55 ++++ src/syntax/mod.rs | 287 +++++++++++++++++ src/syntax/newtype.rs | 50 +++ src/syntax/syscall.rs | 82 +++++ 17 files changed, 1285 insertions(+), 815 deletions(-) delete mode 100644 src/parse.rs create mode 100644 src/syntax/bitfield_type.rs create mode 100644 src/syntax/common.rs create mode 100644 src/syntax/document.rs create mode 100644 src/syntax/enum_type.rs create mode 100644 src/syntax/error.rs create mode 100644 src/syntax/extern_block.rs create mode 100644 src/syntax/mod.rs create mode 100644 src/syntax/newtype.rs create mode 100644 src/syntax/syscall.rs diff --git a/example-abi/build.rs b/example-abi/build.rs index 8ee074ad..f264e139 100644 --- a/example-abi/build.rs +++ b/example-abi/build.rs @@ -2,7 +2,7 @@ use std::{env, path::Path}; use syscall_generator::{ abi::{ty::TypeWidth, AbiBuilder}, - parse::UnwrapFancy, + syntax::UnwrapFancy, TargetEnv, }; diff --git a/example-abi/src/main.rs b/example-abi/src/main.rs index 339efe1f..5df8612f 100644 --- a/example-abi/src/main.rs +++ b/example-abi/src/main.rs @@ -25,8 +25,12 @@ pub enum ProcessInfoElement {} pub enum SocketOption {} pub enum SystemInfo {} -impl FromSyscallResult for Result<(), Error> { - fn from_syscall_result(value: usize) -> Self { +impl SyscallRegister for Result<(), Error> { + fn into_syscall_register(self) -> usize { + todo!() + } + + fn from_syscall_register(value: usize) -> Self { if (value as isize) < 0 { todo!() } else { @@ -35,8 +39,12 @@ impl FromSyscallResult for Result<(), Error> { } } -impl FromSyscallResult for Result { - fn from_syscall_result(value: usize) -> Self { +impl SyscallRegister for Result { + fn into_syscall_register(self) -> usize { + todo!() + } + + fn from_syscall_register(value: usize) -> Self { if (value as isize) < 0 { todo!() } else { @@ -45,12 +53,13 @@ impl FromSyscallResult for Result { } } -impl IntoSyscallArgument for Option { - fn into_syscall_argument(self) -> usize { - match self { - Some(value) => value.into(), - None => 0, - } +impl SyscallRegister for Option { + fn into_syscall_register(self) -> usize { + todo!() + } + + fn from_syscall_register(_value: usize) -> Self { + todo!() } } diff --git a/example-abi/syscall.abi b/example-abi/syscall.abi index 7352af28..7ad9e26f 100644 --- a/example-abi/syscall.abi +++ b/example-abi/syscall.abi @@ -24,23 +24,72 @@ extern { type TerminalSize; type FileMetadataUpdate; type SocketOption; -}; +} newtype Fd(u32); -newtype FileMode(u32); newtype ProcessId(u32); newtype ThreadId(u32); newtype ProcessGroupId(u32); // TODO define these as enums -newtype OpenOptions(u32); -newtype FileMode(u32); newtype ExitCode(i32); newtype SeekFrom(u64); -newtype Signal(u32); -newtype PollControl(u32); newtype MessageDestination(u32); -newtype SocketType(u32); + +bitfield OpenOptions(u32) { + /// Open the file with read capability + READ: 0, + /// Open the file with write capability + WRITE: 1, + + /// Position at the end of the opened file + APPEND: 2, + /// Truncate the file to zero bytes when opening + TRUNCATE: 3, + + /// Create a new file if it does not exist + CREATE: 4, +} + +bitfield FileMode(u32) { + /// Other user execute access + OTHER_EXEC: 0, + /// Other user write access + OTHER_WRITE: 1, + /// Other user read access + OTHER_READ: 2, + + /// Group execute access + GROUP_EXEC: 0, + /// Group write access + GROUP_WRITE: 1, + /// Group read access + GROUP_READ: 2, + + /// Owner execute access + USER_EXEC: 0, + /// Owner write access + USER_WRITE: 1, + /// Owner read access + USER_READ: 2, +} + +enum Signal(u32) { + MemoryAccessViolation, + Aborted, + Killed, + Interrupted +} + +enum PollControl(u32) { + AddFd +} + +enum SocketType(u32) { + RawPacket, + TcpStream, + UdpPacket +} // Misc diff --git a/src/abi/mod.rs b/src/abi/mod.rs index 164da5c8..b0fd70e3 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -7,15 +7,23 @@ use syn::{punctuated::Punctuated, spanned::Spanned, token}; pub mod ty; use crate::{ - abi::ty::{ComplexType, SimpleType, Type, TypeWidth}, + abi::ty::{ComplexType, TypeWidth}, error::Error, - parse::{parse_abi, ParseError}, + syntax::{ + parse_abi, Attributes, BitfieldRange, BitfieldType, BitfieldTypeField, + DocumentTypeDefinition, EnumType, NewType, ParseError, TypeRepr, + }, Syscall, TargetEnv, }; -#[derive(Debug)] +use self::ty::Type; + +pub trait GenerateTypeDefinition { + fn generate_type_definition(&self, abi_type: &ComplexType, env: &TargetEnv) -> TokenStream; +} + pub struct Abi { - pub types: HashMap>, + pub types: HashMap, DocumentTypeDefinition)>, // Contains syscalls and their corresponding SyscallFunction enum variants pub syscalls: Vec<(syn::Ident, Syscall)>, } @@ -57,16 +65,9 @@ impl AbiBuilder { if self.emit_syscall_traits { stream.append_all(quote! { - pub trait IntoSyscallArgument { - fn into_syscall_argument(self) -> usize; - } - - pub trait FromSyscallArgument { - fn from_syscall_argument(value: usize) -> Self; - } - - pub trait FromSyscallResult { - fn from_syscall_result(value: usize) -> Self; + pub trait SyscallRegister { + fn into_syscall_register(self) -> usize; + fn from_syscall_register(value: usize) -> Self; } }); } @@ -124,62 +125,8 @@ impl Abi { pub fn generate_user_side(&self, env: &TargetEnv) -> TokenStream { let mut stream = TokenStream::new(); - for (_, ty) in &self.types { - let definition = - if let ComplexType::Simple(SimpleType::Transparent { name, inner }) = ty.as_ref() { - let ty = inner.as_rust_type(); - let mut res = quote! { - #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] - #[repr(transparent)] - pub struct #name(pub(crate) #ty); - }; - - if inner.width(env) < env.thin_pointer_width { - res.append_all(quote! { - impl IntoSyscallArgument for Option<#name> { - fn into_syscall_argument(self) -> usize { - match self { - Some(value) => value.0 as usize, - None => usize::MAX - } - } - } - - impl FromSyscallArgument for Option<#name> { - fn from_syscall_argument(value: usize) -> Self { - match value { - usize::MAX => None, - _ => Some(#name(value as _)) - } - } - } - - impl FromSyscallResult for Result<#name, Error> { - fn from_syscall_result(value: usize) -> Self { - if (value as isize) < 0 { - todo!() - } else { - Ok(#name(value as _)) - } - } - } - - impl FromSyscallResult for #name { - fn from_syscall_result(value: usize) -> Self { - Self(value as _) - } - } - }); - }; - - res - } else if matches!(ty.as_ref(), ComplexType::Extern(_)) { - continue; - } else { - todo!() - }; - - stream.append_all(definition); + for (_, (ty, def)) in &self.types { + stream.extend(def.generate_type_definition(ty, env)); } for (variant, syscall) in &self.syscalls { @@ -190,116 +137,221 @@ impl Abi { } } -#[cfg(test)] -mod tests { - use std::{collections::HashMap, rc::Rc}; +impl GenerateTypeDefinition for EnumType { + fn generate_type_definition(&self, abi_type: &ComplexType, env: &TargetEnv) -> TokenStream { + let Self { + attributes, + name, + repr, + variants, + } = self; + let TypeRepr(repr) = &repr; - use proc_macro2::Span; - use quote::quote; - use syn::{Ident, Visibility}; + let direct = quote! { + #attributes + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] + #[repr(#repr)] + pub enum #name { + #variants + } - use crate::{ - abi::ty::{AbiPrimitive, ComplexType, SimpleType, TypeWidth}, - Syscall, TargetEnv, - }; + impl SyscallRegister for #name { + fn into_syscall_register(self) -> usize { + self as #repr as usize + } - use super::Abi; - - #[test] - fn generate_full_simple_abi() { - let target = TargetEnv { - thin_pointer_width: TypeWidth::U64, - fat_pointer_width: TypeWidth::U128, - }; - let fd = ComplexType::transparent(Ident::new("Fd", Span::call_site()), AbiPrimitive::U32); - let read_buf_ty = ComplexType::Simple(SimpleType::Slice { - mutable: true, - element: Rc::new(ComplexType::primitive(AbiPrimitive::U8)), - }); - let write_buf_ty = ComplexType::Simple(SimpleType::Slice { - mutable: false, - element: Rc::new(ComplexType::primitive(AbiPrimitive::U8)), - }); - - let read_syscall = Syscall { - name: Ident::new("read", Span::call_site()), - visibility: Visibility::Inherited, - args: vec![ - (Ident::new("fd", Span::call_site()), fd.clone()), - (Ident::new("buf", Span::call_site()), read_buf_ty), - ], - return_type: Some(ComplexType::SizeResult), - }; - let write_syscall = Syscall { - name: Ident::new("write", Span::call_site()), - visibility: Visibility::Inherited, - args: vec![ - (Ident::new("fd", Span::call_site()), fd.clone()), - (Ident::new("buf", Span::call_site()), write_buf_ty), - ], - return_type: Some(ComplexType::SizeResult), + fn from_syscall_register(value: usize) -> Self { + unsafe { + core::mem::transmute(value as #repr) + } + } + } }; - let abi = Abi { - types: HashMap::from_iter([("Fd".to_owned(), fd)]), - syscalls: vec![read_syscall, write_syscall], - }; - - let result = abi.generate_user_side(&target); - assert_eq!( - result.to_string(), + let wrapped = if abi_type.width(env) < env.fat_pointer_width { quote! { - #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] - #[repr(transparent)] - pub struct Fd(pub(crate) u32); + #direct - impl IntoSyscallArgument for Option { - fn into_syscall_argument(self) -> usize { + impl SyscallRegister for Result<#name, Error> { + fn into_syscall_register(self) -> usize { match self { - Some(value) => value.0 as usize, + Ok(value) => value as #repr as usize, + Err(_err) => todo!(), + } + } + + fn from_syscall_register(value: usize) -> Self { + if (value as isize) >= 0 { + Ok(#name::from_syscall_register(value)) + } else { + todo!() + } + } + } + + impl SyscallRegister for Option<#name> { + fn into_syscall_register(self) -> usize { + match self { + Some(value) => value as #repr as usize, None => usize::MAX } } - } - impl FromSyscallArgument for Option { - fn from_syscall_argument(value: usize) -> Self { + fn from_syscall_register(value: usize) -> Self { match value { usize::MAX => None, - _ => Some(Fd(value as _)) + _ => Some(#name::from_syscall_register(value)) } } } - - impl FromSyscallResult for Result { - fn from_syscall_result(value: usize) -> Self { - if (value as isize) < 0 { - todo!() - } else { - Ok(Fd(value as _)) - } - } - } - - unsafe fn read(fd: Fd, buf: &mut [u8],) -> Result { - >::from_syscall_result(syscall!( - SyscallFunction::read, - fd.0 as usize, - buf.as_mut_ptr().expose_addr(), - buf.len(), - )) - } - - unsafe fn write(fd: Fd, buf: &[u8],) -> Result { - >::from_syscall_result(syscall!( - SyscallFunction::write, - fd.0 as usize, - buf.as_ptr().expose_addr(), - buf.len(), - )) - } } - .to_string() - ); + } else { + direct + }; + + wrapped + } +} + +fn generate_transparent_struct( + attributes: &Attributes, + name: &syn::Ident, + repr: &TypeRepr, + is_thin: bool, +) -> TokenStream { + let TypeRepr(repr) = &repr; + + let direct = quote! { + #attributes + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] + pub struct #name(pub(crate) #repr); + + impl SyscallRegister for #name { + fn into_syscall_register(self) -> usize { + self.0 as _ + } + + fn from_syscall_register(value: usize) -> Self { + Self(value as _) + } + } + }; + + let wrapped = if is_thin { + quote! { + #direct + + impl SyscallRegister for Result<#name, Error> { + fn into_syscall_register(self) -> usize { + match self { + Ok(value) => value.0 as _, + Err(_err) => todo!(), + } + } + + fn from_syscall_register(value: usize) -> Self { + if (value as isize) >= 0 { + Ok(#name(value as _)) + } else { + todo!() + } + } + } + + impl SyscallRegister for Option<#name> { + fn into_syscall_register(self) -> usize { + match self { + Some(value) => value.0 as _, + None => usize::MAX + } + } + + fn from_syscall_register(value: usize) -> Self { + match value { + usize::MAX => None, + _ => Some(#name(value as _)) + } + } + } + } + } else { + direct + }; + + wrapped +} + +impl GenerateTypeDefinition for NewType { + fn generate_type_definition(&self, abi_type: &ComplexType, env: &TargetEnv) -> TokenStream { + let Self { + attributes, + name, + repr, + } = self; + + generate_transparent_struct( + attributes, + name, + repr, + abi_type.width(env) < env.fat_pointer_width, + ) + } +} + +impl GenerateTypeDefinition for BitfieldType { + fn generate_type_definition(&self, abi_type: &ComplexType, env: &TargetEnv) -> TokenStream { + let Self { + attributes, + name, + repr, + fields, + } = self; + + let base = generate_transparent_struct( + attributes, + name, + repr, + abi_type.width(env) < env.fat_pointer_width, + ); + + let mut field_impls = TokenStream::new(); + for BitfieldTypeField { + attributes, + name, + range, + } in fields + { + match range { + BitfieldRange::Single(offset) => { + field_impls.extend(quote! { + #attributes + pub const #name: Self = Self(1 << #offset); + }); + } + BitfieldRange::Range(_start, _end) => todo!(), + } + } + + let base = quote! { + #base + + impl #name { + #field_impls + } + }; + + base + } +} + +impl GenerateTypeDefinition for DocumentTypeDefinition { + fn generate_type_definition(&self, abi_type: &ComplexType, env: &TargetEnv) -> TokenStream { + match self { + Self::Enum(inner) => inner.generate_type_definition(abi_type, env), + Self::Bitfield(inner) => inner.generate_type_definition(abi_type, env), + Self::NewType(inner) => inner.generate_type_definition(abi_type, env), + // Extern types require no definition + Self::ExternType(_) => TokenStream::new(), + } } } diff --git a/src/abi/ty/complex.rs b/src/abi/ty/complex.rs index 958b92c6..f483ca0d 100644 --- a/src/abi/ty/complex.rs +++ b/src/abi/ty/complex.rs @@ -4,7 +4,7 @@ use proc_macro2::TokenStream; use quote::quote; use syn::Ident; -use crate::{error::Error, TargetEnv}; +use crate::TargetEnv; use super::{AbiPrimitive, SimpleType, Type, TypeKind, TypeWidth}; @@ -26,13 +26,12 @@ impl ComplexType { Self::Simple(SimpleType::Transparent { name, inner: val }) } - pub fn to_simple_type(&self) -> Result { - todo!() - // if let Self::Simple(simple) = self { - // Ok(simple) - // } else { - // todo!("{:?}", self) - // } + pub fn as_primitive(&self) -> Option { + if let Self::Simple(SimpleType::Primitive(ty)) = self { + Some(*ty) + } else { + None + } } } @@ -89,7 +88,7 @@ impl Type for ComplexType { Self::Option(ty) if ty.width(env) < env.thin_pointer_width || ty.kind() == TypeKind::ThinPointer => { - quote!(#value.into_syscall_argument()) + quote!(#value.into_syscall_register()) } _ => todo!(), } diff --git a/src/abi/ty/simple.rs b/src/abi/ty/simple.rs index c664802e..9a3c43aa 100644 --- a/src/abi/ty/simple.rs +++ b/src/abi/ty/simple.rs @@ -81,7 +81,7 @@ impl Type for SimpleType { match self { Self::NonZeroUsize => quote!(#value.into()), Self::Primitive(ty) => ty.emit_to_syscall_arguments(env, value), - Self::Transparent { .. } => quote!(#value.0 as usize), + Self::Transparent { .. } => quote!(#value.into_syscall_register()), Self::Str => quote!(#value.as_ptr().expose_addr(), #value.len()), Self::Never => todo!("Prevent Never type from being a syscall argument"), Self::Reference { mutable, pointee } => { diff --git a/src/lib.rs b/src/lib.rs index ac45a39a..bf0665b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ use crate::abi::ty::{ComplexType, SimpleType, Type, TypeWidth}; pub mod abi; pub mod error; -pub mod parse; +pub mod syntax; pub struct Syscall { pub name: Ident, @@ -72,7 +72,7 @@ impl Syscall { } Some(ty) => { let ty = ty.as_rust_type(); - quote!(<#ty>::from_syscall_result(#sys_call)) + quote!(<#ty>::from_syscall_register(#sys_call)) } None => quote!(#sys_call;), }; diff --git a/src/parse.rs b/src/parse.rs deleted file mode 100644 index 734ff60f..00000000 --- a/src/parse.rs +++ /dev/null @@ -1,622 +0,0 @@ -use std::{collections::HashMap, fmt, path::Path, rc::Rc, str::FromStr}; - -use proc_macro2::Span; -use quote::quote; -use syn::{ - braced, parenthesized, parse::ParseStream, punctuated::Punctuated, spanned::Spanned, token, - Expr, ExprLit, Ident, Lit, PathSegment, Token, -}; - -use crate::{ - abi::{ - syscall_enum_variant_identifier, - ty::{AbiPrimitive, ComplexType, SimpleType}, - Abi, - }, - Syscall, -}; - -/* -Document syntax: - -newtype SomeType = u32; -newtype Fd(u32); - -syscall read(fd: Fd, buf: &mut [u8]) -> SizeResult; -syscall write(fd: Fd, buf: &[u8]) -> SizeResult; - - */ - -pub enum SyntaxError { - UnhandledType(syn::Type), - UndefinedIdentifier(syn::Ident), -} - -pub enum ParseError { - HardError(syn::parse::Error), - IoError(std::io::Error), - SyntaxError(Vec), -} - -pub trait UnwrapFancy { - fn unwrap_fancy>(self, context: P) -> T; -} - -impl From for ParseError { - fn from(value: std::io::Error) -> Self { - Self::IoError(value) - } -} - -impl From for ParseError { - fn from(value: syn::parse::Error) -> Self { - Self::HardError(value) - } -} - -impl From for ParseError { - fn from(value: SyntaxError) -> Self { - Self::SyntaxError(vec![value]) - } -} - -impl SyntaxError { - pub fn span(&self) -> Span { - match self { - Self::UndefinedIdentifier(id) => id.span(), - Self::UnhandledType(ty) => ty.span(), - } - } - - pub fn describe(&self) -> String { - match self { - Self::UnhandledType(_ty) => "Unhandled type".into(), - Self::UndefinedIdentifier(id) => format!("Undefined identifier '{}'", id), - } - } -} - -impl UnwrapFancy for Result { - fn unwrap_fancy>(self, path: P) -> T { - match self { - Self::Ok(value) => value, - Self::Err(err) => { - match err { - ParseError::IoError(err) => { - eprintln!("{}: {}", path.as_ref().display(), err); - } - ParseError::HardError(err) => { - eprintln!("{}: {}", path.as_ref().display(), err); - } - ParseError::SyntaxError(errs) => { - eprintln!("{}:", path.as_ref().display()); - for err in errs { - eprintln!("* ...: {}", err.describe()); - } - } - } - panic!("Compilation aborted"); - } - } - } -} - -impl ParseError { - pub fn syntax1(e: SyntaxError) -> Self { - Self::SyntaxError(vec![e]) - } - - pub fn fold>, Q: Extend>( - mut acc: Q, - it: I, - ) -> Result { - let mut errors = vec![]; - for item in it { - match item { - Ok(val) => acc.extend_one(val), - Err(ParseError::SyntaxError(err)) => errors.extend(err), - Err(err) => return Err(err), - } - } - if errors.is_empty() { - Ok(acc) - } else { - Err(ParseError::SyntaxError(errors)) - } - } -} - -#[derive(Debug)] -pub struct Document { - newtypes: Vec, - syscalls: Vec, -} - -enum NewTypeDefinition { - Alias(syn::Type), - Transparent { - repr: syn::Type, - #[allow(dead_code)] - sentinel: Option, - }, - Extern(syn::Type), -} - -struct SyscallDefinition { - name: Ident, - arguments: Punctuated, - return_type: Option, -} - -struct Argument { - name: Ident, - ty: syn::Type, -} - -#[derive(Debug)] -struct NewType { - name: Ident, - definition: NewTypeDefinition, -} - -struct ExternTypeItem { - name: Ident, - ty: syn::Type, -} - -enum TopLevel { - NewType(NewType), - ExternBlock(Punctuated), - Syscall(SyscallDefinition), -} - -impl syn::parse::Parse for SyscallDefinition { - fn parse(input: ParseStream) -> syn::Result { - let arguments; - - let name = input.parse()?; - parenthesized!(arguments in input); - let arguments = arguments.parse_terminated(Argument::parse, Token![,])?; - - let return_type = if input.peek(Token![->]) { - input.parse::]>()?; - let ty = input.parse()?; - Some(ty) - } else { - None - }; - - Ok(Self { - name, - arguments, - return_type, - }) - } -} - -impl syn::parse::Parse for Argument { - fn parse(input: ParseStream) -> syn::Result { - let name = input.parse()?; - input.parse::()?; - let ty = input.parse()?; - - Ok(Self { name, ty }) - } -} - -impl syn::parse::Parse for NewType { - fn parse(input: ParseStream) -> syn::Result { - let name = input.parse()?; - if input.peek(Token![=]) { - let _token: Token![=] = input.parse()?; - let ty = input.parse()?; - - Ok(Self { - name, - definition: NewTypeDefinition::Alias(ty), - }) - } else { - // Parenthesized definition - let content; - parenthesized!(content in input); - - // First value has to be a type - let ty = content.parse()?; - - Ok(Self { - name, - definition: NewTypeDefinition::Transparent { - repr: ty, - sentinel: None, - }, - }) - } - } -} - -impl syn::parse::Parse for ExternTypeItem { - fn parse(input: ParseStream) -> syn::Result { - input.parse::()?; - let name: syn::Ident = input.parse()?; - - let ty = if input.peek(Token![=]) { - input.parse::()?; - input.parse()? - } else { - syn::Type::Path(syn::TypePath { - qself: None, - path: syn::Path { - leading_colon: None, - segments: Punctuated::from_iter([syn::PathSegment { - arguments: syn::PathArguments::None, - ident: name.clone(), - }]), - }, - }) - }; - - Ok(Self { name, ty }) - } -} - -impl syn::parse::Parse for TopLevel { - fn parse(input: ParseStream) -> syn::Result { - let lookahead = input.lookahead1(); - if lookahead.peek(Ident) { - let ident: Ident = input.parse()?; - match ident.to_string().as_str() { - "newtype" => return NewType::parse(input).map(Self::NewType), - "syscall" => return SyscallDefinition::parse(input).map(Self::Syscall), - _ => (), - } - } else if lookahead.peek(token::Extern) { - input.parse::()?; - let lookahead = input.lookahead1(); - if lookahead.peek(Token![type]) { - input.parse::()?; - let name = input.parse()?; - input.parse::()?; - let ty = input.parse()?; - return Ok(Self::NewType(NewType { - name, - definition: NewTypeDefinition::Extern(ty), - })); - } else if lookahead.peek(token::Brace) { - let content; - braced!(content in input); - let items = content.parse_terminated(ExternTypeItem::parse, Token![;])?; - return Ok(Self::ExternBlock(items)); - } else { - return Err(input.error(&format!( - "Expected 'extern type' or 'extern {{ ... }}': {}", - lookahead.error() - ))); - } - } - - Err(input.error(&format!( - "Expected 'extern', 'newtype' or 'syscall', got '{}'", - lookahead.error() - ))) - } -} - -impl syn::parse::Parse for Document { - fn parse(input: ParseStream) -> syn::Result { - let mut newtypes = vec![]; - let mut syscalls = vec![]; - - loop { - if input.is_empty() { - break; - } - - let toplevel = TopLevel::parse(input)?; - input.parse::()?; - - match toplevel { - TopLevel::NewType(ty) => newtypes.push(ty), - TopLevel::ExternBlock(extys) => { - for exty in extys { - newtypes.push(NewType { - name: exty.name, - definition: NewTypeDefinition::Extern(exty.ty), - }); - } - } - TopLevel::Syscall(syscall) => syscalls.push(syscall), - } - } - - Ok(Self { newtypes, syscalls }) - } -} - -impl fmt::Debug for Argument { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let ty = &self.ty; - f.debug_tuple("Argument") - .field(&self.name) - .field("e!(#ty)) - .finish() - } -} - -impl fmt::Debug for SyscallDefinition { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let args: Vec<_> = self.arguments.iter().collect(); - let return_type = self.return_type.as_ref().map(|ty| quote!(#ty)); - - f.debug_struct("SyscallDefinition") - .field("name", &self.name) - .field("args", &args) - .field("return_type", &return_type) - .finish() - } -} - -impl fmt::Debug for NewTypeDefinition { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Transparent { repr, .. } => f - .debug_struct("NewTypeDefinition::Transparent") - .field("repr", "e!(#repr)) - .finish(), - Self::Alias(ty) => f - .debug_tuple("NewTypeDefinition::Alias") - .field("e!(#ty)) - .finish(), - Self::Extern(ty) => f - .debug_tuple("NewTypeDefinition::Extern") - .field("e!(#ty)) - .finish(), - } - } -} - -pub fn parse_abi_document(input: &str) -> Result { - let document: Document = syn::parse_str(input)?; - Ok(document) -} - -fn as_single_segment_path(path: &syn::TypePath) -> Option<&PathSegment> { - if path.qself.is_some() || path.path.leading_colon.is_some() || path.path.segments.len() != 1 { - return None; - } - - Some(&path.path.segments[0]) -} - -fn as_single_type_argument(args: &syn::PathArguments) -> Option<&syn::Type> { - let syn::PathArguments::AngleBracketed(args) = args else { - return None; - }; - if args.args.len() != 1 { - return None; - } - let syn::GenericArgument::Type(ty) = &args.args[0] else { - return None; - }; - - Some(ty) -} - -fn as_const_array_len(len: &Expr) -> Option { - let Expr::Lit(ExprLit { - attrs, - lit: Lit::Int(lit), - }) = len - else { - return None; - }; - - if !attrs.is_empty() { - todo!() - } - - lit.base10_parse().ok() -} - -fn process_type( - t: &syn::Type, - known_types: &HashMap>, -) -> Result, ParseError> { - match t { - syn::Type::Path(path) => { - let segment = as_single_segment_path(path).expect("TODO: more complex paths"); - - match segment.ident.to_string().as_str() { - seg if let Ok(ty) = AbiPrimitive::from_str(seg) => { - Ok(Rc::new(ComplexType::primitive(ty))) - } - "NonZeroUsize" => Ok(Rc::new(ComplexType::Simple(SimpleType::NonZeroUsize))), - "Result" if let Some(ty) = as_single_type_argument(&segment.arguments) => { - let ty = process_type(ty, known_types)?; - Ok(Rc::new(ComplexType::Result(ty))) - } - "Option" if let Some(ty) = as_single_type_argument(&segment.arguments) => { - let ty = process_type(ty, known_types)?; - Ok(Rc::new(ComplexType::Option(ty))) - } - "MaybeUninit" if let Some(ty) = as_single_type_argument(&segment.arguments) => { - let ty = process_type(ty, known_types)?; - Ok(Rc::new(ComplexType::MaybeUninit(ty))) - } - name if let Some(ty) = known_types.get(name) => Ok(ty.clone()), - _ => Err(ParseError::syntax1(SyntaxError::UndefinedIdentifier( - segment.ident.clone(), - ))), - } - } - syn::Type::Never(_) => Ok(Rc::new(ComplexType::Simple(SimpleType::Never))), - syn::Type::Reference(r) => { - // TODO lifetimes - if r.lifetime.is_some() { - todo!() - } - - let mutable = r.mutability.is_some(); - - match r.elem.as_ref() { - syn::Type::Slice(ty) => { - let element = process_type(&ty.elem, known_types)?; - Ok(Rc::new(ComplexType::Simple(SimpleType::Slice { - mutable, - element, - }))) - } - syn::Type::Path(path) - if !mutable - && as_single_segment_path(path) - .map(|seg| seg.ident.to_string() == "str" && seg.arguments.is_empty()) - .unwrap_or(false) => - { - Ok(Rc::new(ComplexType::Simple(SimpleType::Str))) - } - syn::Type::Array(array) if let Some(length) = as_const_array_len(&array.len) => { - let element = process_type(&array.elem, known_types)?; - Ok(Rc::new(ComplexType::Simple(SimpleType::Array { - mutable, - element, - length, - }))) - } - _ => { - let pointee = process_type(&r.elem, known_types)?; - Ok(Rc::new(ComplexType::Simple(SimpleType::Reference { - mutable, - pointee, - }))) - } - } - } - syn::Type::Tuple(t) if t.elems.is_empty() => { - Ok(Rc::new(ComplexType::primitive(AbiPrimitive::Unit))) - } - _ => todo!(), - } -} - -fn process_newtype( - t: &NewType, - known_types: &HashMap>, -) -> Result<(Ident, Rc), ParseError> { - let ty = match &t.definition { - NewTypeDefinition::Transparent { repr, .. } => { - let ty = process_type(repr, known_types)?; - if let ComplexType::Simple(SimpleType::Primitive(ty)) = ty.as_ref() { - Rc::new(ComplexType::Simple(SimpleType::Transparent { - name: t.name.clone(), - inner: *ty, - })) - } else { - todo!() - } - } - NewTypeDefinition::Extern(ty) => Rc::new(ComplexType::Extern(ty.clone())), - NewTypeDefinition::Alias(ty) => process_type(ty, known_types)?, - }; - Ok((t.name.clone(), ty)) -} - -pub fn parse_abi(input: &str) -> Result { - let doc = parse_abi_document(input)?; - let mut abi = Abi { - types: HashMap::new(), - syscalls: Vec::new(), - }; - - ParseError::fold( - (), - doc.newtypes.iter().map(|nt| { - let (name, value) = process_newtype(nt, &abi.types)?; - abi.types.insert(name.to_string(), value); - Ok(()) - }), - )?; - - ParseError::fold( - (), - doc.syscalls.iter().map(|syscall| { - let args = syscall - .arguments - .iter() - .map(|arg| { - let ty = process_type(&arg.ty, &abi.types)?; - Ok((arg.name.clone(), ty)) - }) - .collect::, ParseError>>()?; - let return_type = syscall - .return_type - .as_ref() - .map(|ty| process_type(ty, &abi.types)) - .transpose()?; - - let variant = syscall_enum_variant_identifier(&syscall.name); - let def = Syscall { - name: syscall.name.clone(), - args, - return_type, - }; - - abi.syscalls.push((variant, def)); - - Ok(()) - }), - )?; - - Ok(abi) -} - -#[cfg(test)] -mod tests { - use quote::quote; - - use crate::{abi::ty::TypeWidth, parse::UnwrapFancy, TargetEnv}; - - use super::parse_abi; - - const TARGET_64: TargetEnv = TargetEnv { - thin_pointer_width: TypeWidth::U64, - fat_pointer_width: TypeWidth::U128, - }; - - #[test] - fn emit_basic_syscall() { - let abi = parse_abi( - r#" - extern type MyStruct = some_lib::MyStruct; - newtype Fd(u32); - - syscall read(fd: Fd, buf: &mut [u8]) -> SizeResult; - syscall get_file_info(data: &mut MyStruct); - "#, - ) - .unwrap_fancy(""); - - assert_eq!( - abi.syscalls[0].emit_stub(&TARGET_64).to_string(), - quote! { - unsafe fn read(fd: Fd, buf: &mut [u8],) -> Result { - >::from_syscall_result(syscall!( - SyscallFunction::read, - fd.0 as usize, - buf.as_mut_ptr().expose_addr(), - buf.len(), - )) - } - } - .to_string() - ); - - assert_eq!( - abi.syscalls[1].emit_stub(&TARGET_64).to_string(), - quote! { - unsafe fn get_file_info(data: &mut some_lib::MyStruct,) { - syscall!(SyscallFunction::get_file_info, (data as *mut _).expose_addr(),); - } - } - .to_string() - ); - } -} diff --git a/src/syntax/bitfield_type.rs b/src/syntax/bitfield_type.rs new file mode 100644 index 00000000..9b0c318d --- /dev/null +++ b/src/syntax/bitfield_type.rs @@ -0,0 +1,120 @@ +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use syn::{braced, punctuated::Punctuated, Token}; + +use super::{ + common::{Attributes, TypeRepr}, + document::DocumentItemAttributes, +}; + +#[derive(Clone)] +pub enum BitfieldRange { + Single(syn::LitInt), + Range(syn::LitInt, syn::LitInt), +} + +#[derive(Clone)] +pub struct BitfieldTypeField { + pub attributes: Attributes, + pub name: syn::Ident, + pub range: BitfieldRange, +} + +#[derive(Clone)] +pub struct BitfieldType { + pub attributes: Attributes, + pub name: syn::Ident, + pub repr: TypeRepr, + pub fields: Punctuated, +} + +impl DocumentItemAttributes for BitfieldType { + fn extend_attributes>(&mut self, attrs: I) { + self.attributes.extend(attrs); + } +} + +impl syn::parse::Parse for BitfieldRange { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let start = input.parse()?; + if input.peek(Token![..]) { + input.parse::()?; + let end = input.parse()?; + Ok(Self::Range(start, end)) + } else { + Ok(Self::Single(start)) + } + } +} + +impl syn::parse::Parse for BitfieldTypeField { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let attributes = input.parse()?; + let name = input.parse()?; + input.parse::()?; + let range = input.parse()?; + Ok(Self { + attributes, + name, + range, + }) + } +} + +impl syn::parse::Parse for BitfieldType { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let fields; + let attributes = input.parse()?; + let name = input.parse()?; + let repr = input.parse()?; + braced!(fields in input); + let fields = fields.parse_terminated(BitfieldTypeField::parse, Token![,])?; + + Ok(Self { + attributes, + name, + repr, + fields, + }) + } +} + +impl ToTokens for BitfieldRange { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::Range(start, end) => tokens.extend(quote!(#start..#end)), + Self::Single(offset) => tokens.extend(quote!(#offset)), + } + } +} + +impl ToTokens for BitfieldType { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + attributes, + name, + repr, + fields, + } = self; + tokens.extend(quote! { + #attributes + bitfield #name #repr { + #fields + } + }) + } +} + +impl ToTokens for BitfieldTypeField { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + attributes, + name, + range, + } = self; + tokens.extend(quote! { + #attributes + #name: #range + }) + } +} diff --git a/src/syntax/common.rs b/src/syntax/common.rs new file mode 100644 index 00000000..edabe009 --- /dev/null +++ b/src/syntax/common.rs @@ -0,0 +1,54 @@ +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use syn::{parenthesized, Token}; + +#[derive(Clone)] +pub struct TypeRepr(pub syn::Ident); +#[derive(Clone)] +pub struct Attributes(pub Vec); + +impl Attributes { + pub fn new() -> Self { + Self(vec![]) + } + + pub fn extend>(&mut self, attrs: I) { + self.0.extend(attrs); + } +} + +impl syn::parse::Parse for Attributes { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + syn::Attribute::parse_outer(input).map(Self) + } +} + +impl syn::parse::Parse for TypeRepr { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let content; + parenthesized!(content in input); + let content = content.parse_terminated(syn::Ident::parse, Token![,])?; + let mut content = content.into_iter(); + let ty = content.next().expect("TODO insert error here"); + if content.len() != 0 { + todo!("TODO insert error here"); + } + Ok(Self(ty)) + } +} + +impl ToTokens for TypeRepr { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self(repr) = self; + tokens.extend(quote!((#repr))) + } +} + +impl ToTokens for Attributes { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self(attrs) = self; + for attr in attrs { + attr.to_tokens(tokens); + } + } +} diff --git a/src/syntax/document.rs b/src/syntax/document.rs new file mode 100644 index 00000000..be1c2b3b --- /dev/null +++ b/src/syntax/document.rs @@ -0,0 +1,135 @@ +use quote::ToTokens; +use syn::{parse::ParseStream, Token}; + +use super::{ + bitfield_type::BitfieldType, + enum_type::EnumType, + extern_block::{ExternType, ExternTypeBlock}, + newtype::NewType, + syscall::SyscallDefinition, +}; + +#[derive(Clone)] +pub enum DocumentTypeDefinition { + NewType(NewType), + Bitfield(BitfieldType), + Enum(EnumType), + ExternType(ExternType), +} + +pub struct Document { + pub types: Vec, + pub syscalls: Vec, +} + +pub trait DocumentItemAttributes { + fn extend_attributes>(&mut self, attrs: I); +} + +enum DocumentItem { + ExternTypeBlock(ExternTypeBlock), + + // Type definitions + NewType(NewType), + Bitfield(BitfieldType), + Enum(EnumType), + + // Syscall definitions + SyscallDefinition(SyscallDefinition), +} + +impl syn::parse::Parse for DocumentItem { + fn parse(input: ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + + if lookahead.peek(Token![extern]) { + input.parse::()?; + return input.parse().map(Self::ExternTypeBlock); + } + + let item_attributes = syn::Attribute::parse_outer(input)?; + + let lookahead = input.lookahead1(); + + let mut item = if lookahead.peek(syn::Ident) { + // TODO parse attributes + let keyword: syn::Ident = input.parse()?; + + match keyword.to_string().as_str() { + "newtype" => NewType::parse(input).map(Self::NewType), + "bitfield" => BitfieldType::parse(input).map(Self::Bitfield), + "syscall" => SyscallDefinition::parse(input).map(Self::SyscallDefinition), + _ => todo!(), + } + } else if lookahead.peek(Token![enum]) { + input.parse::()?; + EnumType::parse(input).map(Self::Enum) + } else { + todo!() + }?; + + item.extend_attributes(item_attributes); + + Ok(item) + } +} + +impl DocumentItemAttributes for DocumentItem { + fn extend_attributes>(&mut self, attrs: I) { + match self { + Self::NewType(inner) => inner.extend_attributes(attrs), + Self::Enum(inner) => inner.extend_attributes(attrs), + Self::Bitfield(inner) => inner.extend_attributes(attrs), + Self::SyscallDefinition(inner) => inner.extend_attributes(attrs), + Self::ExternTypeBlock(_) => unreachable!(), + } + } +} + +impl syn::parse::Parse for Document { + fn parse(input: ParseStream) -> syn::Result { + let mut types = vec![]; + let mut syscalls = vec![]; + + loop { + if input.is_empty() { + break; + } + + let toplevel = DocumentItem::parse(input)?; + + match toplevel { + DocumentItem::Enum(inner) => types.push(DocumentTypeDefinition::Enum(inner)), + DocumentItem::NewType(inner) => types.push(DocumentTypeDefinition::NewType(inner)), + DocumentItem::Bitfield(inner) => { + types.push(DocumentTypeDefinition::Bitfield(inner)) + } + DocumentItem::ExternTypeBlock(inner) => { + types.extend( + inner + .types + .into_iter() + .map(DocumentTypeDefinition::ExternType), + ); + } + DocumentItem::SyscallDefinition(inner) => { + syscalls.push(inner); + } + } + } + + Ok(Self { types, syscalls }) + } +} + +impl ToTokens for DocumentItem { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + match self { + Self::Enum(inner) => inner.to_tokens(tokens), + Self::Bitfield(inner) => inner.to_tokens(tokens), + Self::NewType(inner) => inner.to_tokens(tokens), + Self::SyscallDefinition(inner) => inner.to_tokens(tokens), + Self::ExternTypeBlock(inner) => inner.to_tokens(tokens), + } + } +} diff --git a/src/syntax/enum_type.rs b/src/syntax/enum_type.rs new file mode 100644 index 00000000..824454c1 --- /dev/null +++ b/src/syntax/enum_type.rs @@ -0,0 +1,97 @@ +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use syn::{braced, punctuated::Punctuated, Token}; + +use super::{ + common::{Attributes, TypeRepr}, + document::DocumentItemAttributes, +}; + +#[derive(Clone)] +pub struct EnumTypeVariant { + pub attributes: Attributes, + pub discriminant: syn::Ident, + pub value: Option, +} + +#[derive(Clone)] +pub struct EnumType { + pub attributes: Attributes, + pub name: syn::Ident, + pub repr: TypeRepr, + pub variants: Punctuated, +} + +impl syn::parse::Parse for EnumTypeVariant { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let attributes = input.parse()?; + let discriminant = input.parse()?; + let value = if input.peek(Token![=]) { + input.parse::()?; + let value = input.parse()?; + Some(value) + } else { + None + }; + Ok(Self { + attributes, + discriminant, + value, + }) + } +} + +impl syn::parse::Parse for EnumType { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let variants; + let attributes = input.parse()?; + let name = input.parse()?; + let repr = input.parse()?; + braced!(variants in input); + let variants = variants.parse_terminated(EnumTypeVariant::parse, Token![,])?; + Ok(Self { + attributes, + name, + repr, + variants, + }) + } +} + +impl DocumentItemAttributes for EnumType { + fn extend_attributes>(&mut self, attrs: I) { + self.attributes.extend(attrs); + } +} + +impl ToTokens for EnumTypeVariant { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + attributes, + discriminant, + value, + } = self; + let value = value.as_ref().map(|val| quote!(= #val)); + tokens.extend(quote! { + #attributes + #discriminant #value + }) + } +} + +impl ToTokens for EnumType { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + attributes, + name, + repr, + variants, + } = self; + tokens.extend(quote! { + #attributes + enum #name #repr { + #variants + } + }) + } +} diff --git a/src/syntax/error.rs b/src/syntax/error.rs new file mode 100644 index 00000000..bb75d505 --- /dev/null +++ b/src/syntax/error.rs @@ -0,0 +1,103 @@ +use std::path::Path; + +use proc_macro2::Span; +use syn::spanned::Spanned; + +pub enum SyntaxError { + UnhandledType(syn::Type), + UndefinedIdentifier(syn::Ident), +} + +pub enum ParseError { + HardError(syn::parse::Error), + IoError(std::io::Error), + SyntaxError(Vec), +} + +pub trait UnwrapFancy { + fn unwrap_fancy>(self, context: P) -> T; +} + +impl From for ParseError { + fn from(value: std::io::Error) -> Self { + Self::IoError(value) + } +} + +impl From for ParseError { + fn from(value: syn::parse::Error) -> Self { + Self::HardError(value) + } +} + +impl From for ParseError { + fn from(value: SyntaxError) -> Self { + Self::SyntaxError(vec![value]) + } +} + +impl SyntaxError { + pub fn span(&self) -> Span { + match self { + Self::UndefinedIdentifier(id) => id.span(), + Self::UnhandledType(ty) => ty.span(), + } + } + + pub fn describe(&self) -> String { + match self { + Self::UnhandledType(_ty) => "Unhandled type".into(), + Self::UndefinedIdentifier(id) => format!("Undefined identifier '{}'", id), + } + } +} + +impl UnwrapFancy for Result { + fn unwrap_fancy>(self, path: P) -> T { + match self { + Self::Ok(value) => value, + Self::Err(err) => { + match err { + ParseError::IoError(err) => { + eprintln!("{}: {}", path.as_ref().display(), err); + } + ParseError::HardError(err) => { + eprintln!("{}: {}", path.as_ref().display(), err); + } + ParseError::SyntaxError(errs) => { + eprintln!("{}:", path.as_ref().display()); + for err in errs { + eprintln!("* ...: {}", err.describe()); + } + } + } + panic!("Compilation aborted"); + } + } + } +} + +impl ParseError { + pub fn syntax1(e: SyntaxError) -> Self { + Self::SyntaxError(vec![e]) + } + + pub fn fold>, Q: Extend>( + mut acc: Q, + it: I, + ) -> Result { + let mut errors = vec![]; + for item in it { + match item { + Ok(val) => acc.extend_one(val), + Err(ParseError::SyntaxError(err)) => errors.extend(err), + Err(err) => return Err(err), + } + } + if errors.is_empty() { + Ok(acc) + } else { + Err(ParseError::SyntaxError(errors)) + } + } +} diff --git a/src/syntax/extern_block.rs b/src/syntax/extern_block.rs new file mode 100644 index 00000000..2f5cd183 --- /dev/null +++ b/src/syntax/extern_block.rs @@ -0,0 +1,55 @@ +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use syn::{braced, punctuated::Punctuated, Token}; + +#[derive(Clone)] +pub struct ExternType { + pub name: syn::Ident, + pub alias: Option, +} + +pub struct ExternTypeBlock { + pub types: Punctuated, +} + +impl syn::parse::Parse for ExternType { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let name = input.parse()?; + let alias = if input.peek(Token![=]) { + input.parse::()?; + Some(input.parse()?) + } else { + None + }; + Ok(Self { name, alias }) + } +} + +impl syn::parse::Parse for ExternTypeBlock { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let types; + braced!(types in input); + let types = types.parse_terminated(ExternType::parse, Token![;])?; + Ok(Self { types }) + } +} + +impl ToTokens for ExternType { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { name, alias } = self; + let alias = alias.as_ref().map(|ty| quote!(= #ty)); + tokens.extend(quote!(type #name #alias)); + } +} + +impl ToTokens for ExternTypeBlock { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { types } = self; + tokens.extend(quote! { + extern { + #types + } + }) + } +} diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs new file mode 100644 index 00000000..b32dcf47 --- /dev/null +++ b/src/syntax/mod.rs @@ -0,0 +1,287 @@ +use std::{collections::HashMap, rc::Rc, str::FromStr}; + +use syn::{punctuated::Punctuated, Expr, ExprLit, Lit, PathSegment}; + +use crate::{ + abi::{ + syscall_enum_variant_identifier, + ty::{AbiPrimitive, ComplexType, SimpleType}, + Abi, + }, + Syscall, +}; + +mod common; + +// Types +mod bitfield_type; +mod enum_type; +mod extern_block; +mod newtype; + +mod document; + +mod error; +mod syscall; + +pub use bitfield_type::{BitfieldRange, BitfieldType, BitfieldTypeField}; +pub use common::{Attributes, TypeRepr}; +pub use document::{Document, DocumentTypeDefinition}; +pub use enum_type::{EnumType, EnumTypeVariant}; +pub use error::{ParseError, SyntaxError, UnwrapFancy}; +pub use extern_block::{ExternType, ExternTypeBlock}; +pub use newtype::NewType; + +type TypeMap = HashMap, DocumentTypeDefinition)>; + +pub fn parse_abi_document(input: &str) -> Result { + let document: Document = syn::parse_str(input)?; + Ok(document) +} + +fn as_single_segment_path(path: &syn::TypePath) -> Option<&PathSegment> { + if path.qself.is_some() || path.path.leading_colon.is_some() || path.path.segments.len() != 1 { + return None; + } + + Some(&path.path.segments[0]) +} + +fn as_single_type_argument(args: &syn::PathArguments) -> Option<&syn::Type> { + let syn::PathArguments::AngleBracketed(args) = args else { + return None; + }; + if args.args.len() != 1 { + return None; + } + let syn::GenericArgument::Type(ty) = &args.args[0] else { + return None; + }; + + Some(ty) +} + +fn as_const_array_len(len: &Expr) -> Option { + let Expr::Lit(ExprLit { + attrs, + lit: Lit::Int(lit), + }) = len + else { + return None; + }; + + if !attrs.is_empty() { + todo!() + } + + lit.base10_parse().ok() +} + +fn process_type(t: &syn::Type, known_types: &TypeMap) -> Result, ParseError> { + match t { + syn::Type::Path(path) => { + let segment = as_single_segment_path(path).expect("TODO: more complex paths"); + + match segment.ident.to_string().as_str() { + seg if let Ok(ty) = AbiPrimitive::from_str(seg) => { + Ok(Rc::new(ComplexType::primitive(ty))) + } + "NonZeroUsize" => Ok(Rc::new(ComplexType::Simple(SimpleType::NonZeroUsize))), + "Result" if let Some(ty) = as_single_type_argument(&segment.arguments) => { + let ty = process_type(ty, known_types)?; + Ok(Rc::new(ComplexType::Result(ty))) + } + "Option" if let Some(ty) = as_single_type_argument(&segment.arguments) => { + let ty = process_type(ty, known_types)?; + Ok(Rc::new(ComplexType::Option(ty))) + } + "MaybeUninit" if let Some(ty) = as_single_type_argument(&segment.arguments) => { + let ty = process_type(ty, known_types)?; + Ok(Rc::new(ComplexType::MaybeUninit(ty))) + } + name if let Some((ty, _)) = known_types.get(name) => Ok(ty.clone()), + _ => Err(ParseError::syntax1(SyntaxError::UndefinedIdentifier( + segment.ident.clone(), + ))), + } + } + syn::Type::Never(_) => Ok(Rc::new(ComplexType::Simple(SimpleType::Never))), + syn::Type::Reference(r) => { + // TODO lifetimes + if r.lifetime.is_some() { + todo!() + } + + let mutable = r.mutability.is_some(); + + match r.elem.as_ref() { + syn::Type::Slice(ty) => { + let element = process_type(&ty.elem, known_types)?; + Ok(Rc::new(ComplexType::Simple(SimpleType::Slice { + mutable, + element, + }))) + } + syn::Type::Path(path) + if !mutable + && as_single_segment_path(path) + .map(|seg| seg.ident.to_string() == "str" && seg.arguments.is_empty()) + .unwrap_or(false) => + { + Ok(Rc::new(ComplexType::Simple(SimpleType::Str))) + } + syn::Type::Array(array) if let Some(length) = as_const_array_len(&array.len) => { + let element = process_type(&array.elem, known_types)?; + Ok(Rc::new(ComplexType::Simple(SimpleType::Array { + mutable, + element, + length, + }))) + } + _ => { + let pointee = process_type(&r.elem, known_types)?; + Ok(Rc::new(ComplexType::Simple(SimpleType::Reference { + mutable, + pointee, + }))) + } + } + } + syn::Type::Tuple(t) if t.elems.is_empty() => { + Ok(Rc::new(ComplexType::primitive(AbiPrimitive::Unit))) + } + _ => todo!(), + } +} + +fn require_primitive_repr(repr: &TypeRepr) -> Result { + let TypeRepr(repr) = repr; + Ok(AbiPrimitive::from_str(repr.to_string().as_str()).expect("TODO proper error message")) +} + +fn make_single_ident_type(ident: syn::Ident) -> syn::Type { + syn::Type::Path(syn::TypePath { + qself: None, + path: syn::Path { + leading_colon: None, + segments: Punctuated::from_iter([syn::PathSegment { + arguments: syn::PathArguments::None, + ident, + }]), + }, + }) +} + +fn process_extern_type( + def: &ExternType, + _known_types: &TypeMap, +) -> Result<(syn::Ident, Rc), ParseError> { + let ty = def + .alias + .clone() + .unwrap_or_else(|| make_single_ident_type(def.name.clone())); + Ok((def.name.clone(), Rc::new(ComplexType::Extern(ty)))) +} + +fn process_bitfield_type( + def: &BitfieldType, + _known_types: &TypeMap, +) -> Result<(syn::Ident, Rc), ParseError> { + let repr = require_primitive_repr(&def.repr)?; + Ok(( + def.name.clone(), + Rc::new(ComplexType::Simple(SimpleType::Transparent { + name: def.name.clone(), + inner: repr, + })), + )) +} + +fn process_enum_type( + def: &EnumType, + _known_types: &TypeMap, +) -> Result<(syn::Ident, Rc), ParseError> { + let repr = require_primitive_repr(&def.repr)?; + Ok(( + def.name.clone(), + Rc::new(ComplexType::Simple(SimpleType::Transparent { + name: def.name.clone(), + inner: repr, + })), + )) +} + +fn process_newtype( + def: &NewType, + _known_types: &TypeMap, +) -> Result<(syn::Ident, Rc), ParseError> { + let repr = require_primitive_repr(&def.repr)?; + Ok(( + def.name.clone(), + Rc::new(ComplexType::Simple(SimpleType::Transparent { + name: def.name.clone(), + inner: repr, + })), + )) +} + +fn process_type_definition( + def: &DocumentTypeDefinition, + known_types: &TypeMap, +) -> Result<(syn::Ident, Rc), ParseError> { + match def { + DocumentTypeDefinition::ExternType(inner) => process_extern_type(inner, known_types), + DocumentTypeDefinition::Bitfield(inner) => process_bitfield_type(inner, known_types), + DocumentTypeDefinition::Enum(inner) => process_enum_type(inner, known_types), + DocumentTypeDefinition::NewType(inner) => process_newtype(inner, known_types), + } +} + +pub fn parse_abi(input: &str) -> Result { + let doc = parse_abi_document(input)?; + let mut abi = Abi { + types: HashMap::new(), + syscalls: Vec::new(), + }; + + ParseError::fold( + (), + doc.types.iter().map(|nt| { + let (name, value) = process_type_definition(nt, &abi.types)?; + abi.types.insert(name.to_string(), (value, nt.clone())); + Ok(()) + }), + )?; + + ParseError::fold( + (), + doc.syscalls.iter().map(|syscall| { + let args = syscall + .arguments + .iter() + .map(|arg| { + let ty = process_type(&arg.ty, &abi.types)?; + Ok((arg.name.clone(), ty)) + }) + .collect::, ParseError>>()?; + let return_type = syscall + .return_type + .as_ref() + .map(|ty| process_type(ty, &abi.types)) + .transpose()?; + + let variant = syscall_enum_variant_identifier(&syscall.name); + let def = Syscall { + name: syscall.name.clone(), + args, + return_type, + }; + + abi.syscalls.push((variant, def)); + + Ok(()) + }), + )?; + + Ok(abi) +} diff --git a/src/syntax/newtype.rs b/src/syntax/newtype.rs new file mode 100644 index 00000000..f46d6475 --- /dev/null +++ b/src/syntax/newtype.rs @@ -0,0 +1,50 @@ +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use syn::Token; + +use super::{ + common::{Attributes, TypeRepr}, + document::DocumentItemAttributes, +}; + +#[derive(Clone)] +pub struct NewType { + pub attributes: Attributes, + pub name: syn::Ident, + // No need for a full type, since only certain primitives are allowed + pub repr: TypeRepr, +} + +impl DocumentItemAttributes for NewType { + fn extend_attributes>(&mut self, attrs: I) { + self.attributes.extend(attrs); + } +} + +impl syn::parse::Parse for NewType { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let attributes = input.parse()?; + let name = input.parse()?; + let repr = input.parse()?; + input.parse::()?; + Ok(Self { + attributes, + name, + repr, + }) + } +} + +impl ToTokens for NewType { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + attributes, + name, + repr, + } = self; + tokens.extend(quote! { + #attributes + newtype #name #repr; + }) + } +} diff --git a/src/syntax/syscall.rs b/src/syntax/syscall.rs new file mode 100644 index 00000000..1e670882 --- /dev/null +++ b/src/syntax/syscall.rs @@ -0,0 +1,82 @@ +use quote::{quote, ToTokens}; +use syn::{parenthesized, parse::ParseStream, punctuated::Punctuated, Ident, Token}; + +use super::{common::Attributes, document::DocumentItemAttributes}; + +pub struct SyscallDefinition { + pub attributes: Attributes, + pub name: Ident, + pub arguments: Punctuated, + pub return_type: Option, +} + +pub struct SyscallArgument { + pub name: syn::Ident, + pub ty: syn::Type, +} + +impl DocumentItemAttributes for SyscallDefinition { + fn extend_attributes>(&mut self, attrs: I) { + self.attributes.extend(attrs); + } +} + +impl syn::parse::Parse for SyscallDefinition { + fn parse(input: ParseStream) -> syn::Result { + let arguments; + + let name = input.parse()?; + parenthesized!(arguments in input); + let arguments = arguments.parse_terminated(SyscallArgument::parse, Token![,])?; + + let return_type = if input.peek(Token![->]) { + input.parse::]>()?; + let ty = input.parse()?; + Some(ty) + } else { + None + }; + + input.parse::()?; + + Ok(Self { + attributes: Attributes::new(), + name, + arguments, + return_type, + }) + } +} + +impl syn::parse::Parse for SyscallArgument { + fn parse(input: ParseStream) -> syn::Result { + let name = input.parse()?; + input.parse::()?; + let ty = input.parse()?; + + Ok(Self { name, ty }) + } +} + +impl ToTokens for SyscallArgument { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let Self { name, ty } = self; + tokens.extend(quote!(#name: #ty)) + } +} + +impl ToTokens for SyscallDefinition { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + let Self { + attributes, + name, + arguments, + return_type, + } = self; + let return_type = return_type.as_ref().map(|ty| quote!(-> #ty)); + tokens.extend(quote! { + #attributes + syscall #name (#arguments) #return_type; + }); + } +} From 05fa15df56e0657ff5744d138d4fb022296e4579 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Sat, 9 Mar 2024 12:59:55 +0200 Subject: [PATCH 3/9] Add #[thin]/#[fat] extern types --- example-abi/src/main.rs | 49 +++++++++++++++++++++++++++++++++++ example-abi/syscall.abi | 7 +++-- src/abi/mod.rs | 7 +++++ src/abi/ty/complex.rs | 28 +++++++++++++++++--- src/abi/ty/mod.rs | 2 +- src/lib.rs | 53 -------------------------------------- src/syntax/common.rs | 16 ++++++++++-- src/syntax/extern_block.rs | 21 ++++++++++++--- src/syntax/mod.rs | 19 +++++++++++--- 9 files changed, 134 insertions(+), 68 deletions(-) diff --git a/example-abi/src/main.rs b/example-abi/src/main.rs index 5df8612f..f1d0cddb 100644 --- a/example-abi/src/main.rs +++ b/example-abi/src/main.rs @@ -25,6 +25,55 @@ pub enum ProcessInfoElement {} pub enum SocketOption {} pub enum SystemInfo {} +pub enum ExitCode { + Exited(u32), + Killed(u32), +} + +pub enum SeekFrom { + Start(u64), + Current(i64), + End(i64), +} + +impl SyscallRegister for ExitCode { + fn into_syscall_register(self) -> usize { + match self { + Self::Killed(value) => (value as usize) | (1 << u32::BITS), + Self::Exited(value) => value as usize, + } + } + + fn from_syscall_register(value: usize) -> Self { + if value & (1 << u32::BITS) != 0 { + Self::Killed(value as _) + } else { + Self::Exited(value as _) + } + } +} + +impl SyscallFatRegister for SeekFrom { + fn as_syscall_meta(&self) -> usize { + match self { + Self::Start(_) => 0, + Self::Current(_) => 1, + Self::End(_) => 2, + } + } + + fn as_syscall_data(&self) -> usize { + match self { + &Self::Start(value) => value as _, + &Self::Current(value) | &Self::End(value) => value as _, + } + } + + fn from_syscall_registers(_meta: usize, _data: usize) -> Self { + todo!() + } +} + impl SyscallRegister for Result<(), Error> { fn into_syscall_register(self) -> usize { todo!() diff --git a/example-abi/syscall.abi b/example-abi/syscall.abi index 7ad9e26f..e008f120 100644 --- a/example-abi/syscall.abi +++ b/example-abi/syscall.abi @@ -24,6 +24,11 @@ extern { type TerminalSize; type FileMetadataUpdate; type SocketOption; + + #[thin] + type ExitCode; + #[fat] + type SeekFrom; } newtype Fd(u32); @@ -32,8 +37,6 @@ newtype ThreadId(u32); newtype ProcessGroupId(u32); // TODO define these as enums -newtype ExitCode(i32); -newtype SeekFrom(u64); newtype MessageDestination(u32); bitfield OpenOptions(u32) { diff --git a/src/abi/mod.rs b/src/abi/mod.rs index b0fd70e3..02add9a5 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -69,6 +69,13 @@ impl AbiBuilder { fn into_syscall_register(self) -> usize; fn from_syscall_register(value: usize) -> Self; } + + pub trait SyscallFatRegister { + fn as_syscall_meta(&self) -> usize; + fn as_syscall_data(&self) -> usize; + + fn from_syscall_registers(meta: usize, data: usize) -> Self; + } }); } diff --git a/src/abi/ty/complex.rs b/src/abi/ty/complex.rs index f483ca0d..97188168 100644 --- a/src/abi/ty/complex.rs +++ b/src/abi/ty/complex.rs @@ -8,13 +8,20 @@ use crate::TargetEnv; use super::{AbiPrimitive, SimpleType, Type, TypeKind, TypeWidth}; +#[derive(Clone, Copy)] +pub enum ExternKind { + None, + Thin, + Fat, +} + #[derive(Clone)] pub enum ComplexType { Simple(SimpleType), MaybeUninit(Rc), Result(Rc), Option(Rc), - Extern(syn::Type), + Extern { kind: ExternKind, alias: syn::Type }, } impl ComplexType { @@ -48,7 +55,7 @@ impl Type for ComplexType { // None -> null TypeKind::ThinPointer => env.thin_pointer_width, }, - Self::Extern(_) => TypeWidth::Unknown, + Self::Extern { .. } => TypeWidth::Unknown, // MaybeUninit has the same layout as its underlying data Self::MaybeUninit(ty) => ty.width(env), } @@ -74,7 +81,7 @@ impl Type for ComplexType { // TODO take from TargetEnv quote!(Result<#ty, Error>) } - Self::Extern(ty) => quote!(#ty), + Self::Extern { alias, .. } => quote!(#alias), Self::MaybeUninit(ty) => { let ty = ty.as_rust_type(); quote!(core::mem::MaybeUninit<#ty>) @@ -83,6 +90,7 @@ impl Type for ComplexType { } fn emit_to_syscall_arguments(&self, env: &TargetEnv, value: &Ident) -> TokenStream { + // TODO: Option/Result match self { Self::Simple(ty) => ty.emit_to_syscall_arguments(env, value), Self::Option(ty) @@ -90,6 +98,18 @@ impl Type for ComplexType { { quote!(#value.into_syscall_register()) } + Self::Extern { + kind: ExternKind::Thin, + .. + } => { + quote!(#value.into_syscall_register()) + } + Self::Extern { + kind: ExternKind::Fat, + .. + } => { + quote!(#value.as_syscall_meta(), #value.as_syscall_data()) + } _ => todo!(), } } @@ -101,7 +121,7 @@ impl fmt::Debug for ComplexType { Self::Simple(ty) => f.debug_tuple("Simple").field(&ty).finish(), Self::Result(ty) => f.debug_tuple("Result").field(&ty).finish(), Self::Option(ty) => f.debug_tuple("Option").field(&ty).finish(), - Self::Extern(ty) => f.debug_tuple("Extern").field("e!(#ty)).finish(), + Self::Extern { alias, .. } => f.debug_tuple("Extern").field("e!(#alias)).finish(), Self::MaybeUninit(ty) => f.debug_tuple("MaybeUninit").field(&ty).finish(), } } diff --git a/src/abi/ty/mod.rs b/src/abi/ty/mod.rs index 04a76bce..dc6fc37e 100644 --- a/src/abi/ty/mod.rs +++ b/src/abi/ty/mod.rs @@ -12,7 +12,7 @@ pub use complex::ComplexType; pub use primitive::AbiPrimitive; pub use simple::SimpleType; -#[derive(PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq)] pub enum TypeKind { ThinPointer, FatPointer, diff --git a/src/lib.rs b/src/lib.rs index bf0665b0..da99e273 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,56 +86,3 @@ impl Syscall { v } } - -#[cfg(test)] -mod tests { - use std::rc::Rc; - - use proc_macro2::Span; - use quote::quote; - use syn::Ident; - - use crate::{ - abi::ty::{AbiPrimitive, ComplexType, SimpleType, TypeWidth}, - Syscall, TargetEnv, - }; - - #[test] - fn emit_syscall_stub() { - let target = TargetEnv { - thin_pointer_width: TypeWidth::U64, - fat_pointer_width: TypeWidth::U128, - }; - let syscall = Syscall { - name: Ident::new("read", Span::call_site()), - args: vec![ - ( - Ident::new("fd", Span::call_site()), - ComplexType::Simple(SimpleType::Primitive(AbiPrimitive::U32)), - ), - ( - Ident::new("buffer", Span::call_site()), - ComplexType::Simple(SimpleType::Slice { - mutable: true, - element: Rc::new(ComplexType::Simple(SimpleType::Primitive( - AbiPrimitive::U8, - ))), - }), - ), - ], - return_type: Some(ComplexType::Result(SimpleType::Primitive( - AbiPrimitive::USize, - ))), - }; - let res = syscall.emit_stub(&target); - assert_eq!( - res.to_string(), - quote! { - unsafe fn read(fd: u32, buffer: &mut [u8],) -> Result { - >::from_syscall_result(syscall!(SyscallFunction::read, fd as usize, buffer.as_mut_ptr().expose_addr(), buffer.len(),)) - } - } - .to_string() - ); - } -} diff --git a/src/syntax/common.rs b/src/syntax/common.rs index edabe009..292a692d 100644 --- a/src/syntax/common.rs +++ b/src/syntax/common.rs @@ -1,3 +1,5 @@ +use std::ops::{Deref, DerefMut}; + use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{parenthesized, Token}; @@ -11,9 +13,19 @@ impl Attributes { pub fn new() -> Self { Self(vec![]) } +} - pub fn extend>(&mut self, attrs: I) { - self.0.extend(attrs); +impl Deref for Attributes { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Attributes { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 } } diff --git a/src/syntax/extern_block.rs b/src/syntax/extern_block.rs index 2f5cd183..9e25fd2d 100644 --- a/src/syntax/extern_block.rs +++ b/src/syntax/extern_block.rs @@ -2,8 +2,11 @@ use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{braced, punctuated::Punctuated, Token}; +use super::Attributes; + #[derive(Clone)] pub struct ExternType { + pub attributes: Attributes, pub name: syn::Ident, pub alias: Option, } @@ -14,6 +17,7 @@ pub struct ExternTypeBlock { impl syn::parse::Parse for ExternType { fn parse(input: syn::parse::ParseStream) -> syn::Result { + let attributes = input.parse()?; input.parse::()?; let name = input.parse()?; let alias = if input.peek(Token![=]) { @@ -22,7 +26,11 @@ impl syn::parse::Parse for ExternType { } else { None }; - Ok(Self { name, alias }) + Ok(Self { + attributes, + name, + alias, + }) } } @@ -37,9 +45,16 @@ impl syn::parse::Parse for ExternTypeBlock { impl ToTokens for ExternType { fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { name, alias } = self; + let Self { + attributes, + name, + alias, + } = self; let alias = alias.as_ref().map(|ty| quote!(= #ty)); - tokens.extend(quote!(type #name #alias)); + tokens.extend(quote! { + #attributes + type #name #alias + }); } } diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index b32dcf47..ad5096cc 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -5,7 +5,7 @@ use syn::{punctuated::Punctuated, Expr, ExprLit, Lit, PathSegment}; use crate::{ abi::{ syscall_enum_variant_identifier, - ty::{AbiPrimitive, ComplexType, SimpleType}, + ty::{complex::ExternKind, AbiPrimitive, ComplexType, SimpleType}, Abi, }, Syscall, @@ -176,11 +176,24 @@ fn process_extern_type( def: &ExternType, _known_types: &TypeMap, ) -> Result<(syn::Ident, Rc), ParseError> { - let ty = def + // Process extern attributes + let mut kind = ExternKind::None; + for attr in def.attributes.iter() { + if attr.path().is_ident("thin") { + kind = ExternKind::Thin; + } else if attr.path().is_ident("fat") { + kind = ExternKind::Fat; + } + } + + let alias = def .alias .clone() .unwrap_or_else(|| make_single_ident_type(def.name.clone())); - Ok((def.name.clone(), Rc::new(ComplexType::Extern(ty)))) + Ok(( + def.name.clone(), + Rc::new(ComplexType::Extern { alias, kind }), + )) } fn process_bitfield_type( From 55caa8d61b7c71e03d506ec79a76e5ae28612ce3 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Sat, 9 Mar 2024 15:49:42 +0200 Subject: [PATCH 4/9] Initial commit for kernel-side --- Cargo.lock | 5 ++ Cargo.toml | 14 +++-- handler/Cargo.toml | 9 +++ handler/src/lib.rs | 0 src/abi/mod.rs | 12 +++- src/abi/syscall.rs | 121 ++++++++++++++++++++++++++++++++++++ src/abi/ty/complex.rs | 9 +++ src/abi/ty/mod.rs | 6 ++ src/abi/ty/primitive.rs | 9 +++ src/abi/ty/simple.rs | 9 +++ src/lib.rs | 77 +---------------------- src/syntax/bitfield_type.rs | 6 +- src/syntax/common.rs | 4 +- src/syntax/document.rs | 4 +- src/syntax/enum_type.rs | 4 +- src/syntax/extern_block.rs | 4 +- src/syntax/mod.rs | 11 ++-- src/syntax/newtype.rs | 2 +- src/syntax/syscall.rs | 4 +- 19 files changed, 206 insertions(+), 104 deletions(-) create mode 100644 handler/Cargo.toml create mode 100644 handler/src/lib.rs create mode 100644 src/abi/syscall.rs diff --git a/Cargo.lock b/Cargo.lock index cb89790b..350c4606 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,6 +9,10 @@ dependencies = [ "syscall-generator", ] +[[package]] +name = "handler" +version = "0.1.0" + [[package]] name = "prettyplease" version = "0.2.16" @@ -52,6 +56,7 @@ dependencies = [ name = "syscall-generator" version = "0.1.0" dependencies = [ + "handler", "prettyplease", "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index bc98dd3a..2f369341 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,11 +4,13 @@ version = "0.1.0" edition = "2021" [dependencies] -prettyplease = "0.2.16" -proc-macro2 = "1.0.78" -quote = "1.0.35" -syn = { version = "2.0.52", features = ["full"] } -thiserror = "1.0.57" +prettyplease = "0.2.15" +proc-macro2 = "^1.0.63" +quote = "^1.0" +syn = { version = "2.0.32", features = ["full"] } +thiserror = "1.0.47" + +handler = { path = "handler", optional = true } [workspace] -members = ["example-abi"] +members = ["example-abi", "handler"] diff --git a/handler/Cargo.toml b/handler/Cargo.toml new file mode 100644 index 00000000..a3818d5d --- /dev/null +++ b/handler/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "handler" +version = "0.1.0" +edition = "2021" + +[dependencies] + +[lib] +proc-macro = true diff --git a/handler/src/lib.rs b/handler/src/lib.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/abi/mod.rs b/src/abi/mod.rs index 02add9a5..b33cf0de 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -13,11 +13,15 @@ use crate::{ parse_abi, Attributes, BitfieldRange, BitfieldType, BitfieldTypeField, DocumentTypeDefinition, EnumType, NewType, ParseError, TypeRepr, }, - Syscall, TargetEnv, + TargetEnv, }; use self::ty::Type; +mod syscall; + +pub use syscall::Syscall; + pub trait GenerateTypeDefinition { fn generate_type_definition(&self, abi_type: &ComplexType, env: &TargetEnv) -> TokenStream; } @@ -95,6 +99,12 @@ impl AbiBuilder { pub enum SyscallFunction { #syscall_discriminants } + + impl From for usize { + fn from(value: SyscallFunction) -> usize { + value as usize + } + } }); stream.append_all(self.abi.generate_user_side(&self.target_env)); diff --git a/src/abi/syscall.rs b/src/abi/syscall.rs new file mode 100644 index 00000000..c5127357 --- /dev/null +++ b/src/abi/syscall.rs @@ -0,0 +1,121 @@ +use core::fmt; +use std::rc::Rc; + +use proc_macro2::TokenStream; +use quote::quote; + +use crate::{ + abi::ty::{SimpleType, Type}, + TargetEnv, +}; + +use super::ty::ComplexType; + +pub struct Syscall { + pub name: syn::Ident, + pub args: Vec<(syn::Ident, Rc)>, + pub return_type: Option>, +} + +impl fmt::Debug for Syscall { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Syscall") + .field("name", &self.name) + .field("args", &self.args) + .field("return_type", &self.return_type) + .finish_non_exhaustive() + } +} + +impl Syscall { + // Syntax: + // + // syscall_handler!("abi-document.abi", pub syscall_handler { + // read(fd, buffer) => ..., + // write(fd, buffer) => ..., + // }) + pub fn emit_handler_branch( + &self, + named_args: &[&syn::Ident], + raw_args: &syn::Ident, + body: &syn::ExprBlock, + enum_variant: &syn::Ident, + env: &TargetEnv, + ) -> TokenStream { + if self.args.len() != named_args.len() { + todo!() + } + + let mut extracted_arguments = TokenStream::new(); + let mut index = 0; + + for ((_, arg), &name) in std::iter::zip(self.args.iter(), named_args) { + let (arg_expr, arg_width) = arg.emit_from_syscall_arguments(env, raw_args, index); + let arg_assignment = quote! { + let #name = #arg_expr; + }; + index += arg_width; + + extracted_arguments.extend(arg_assignment); + } + + quote! { + #enum_variant => { + #extracted_arguments + + #body + } + } + } + + pub fn emit_stub(&self, enum_variant: &syn::Ident, env: &TargetEnv) -> TokenStream { + let Self { + name, + args, + return_type, + } = self; + + let syscall_args = args + .iter() + .map(|(name, ty)| { + let arg = ty.emit_to_syscall_arguments(env, name); + quote!(#arg,) + }) + .collect::(); + let args = args + .iter() + .map(|(name, ty)| { + let ty = ty.as_rust_type(); + quote!(#name: #ty,) + }) + .collect::(); + let return_type_arrow = return_type.as_ref().map(|ty| { + let ty = ty.as_rust_type(); + quote!(-> #ty) + }); + + let sys_call = quote!(syscall!(SyscallFunction::#enum_variant, #syscall_args)); + + let wrapped = match return_type.as_deref() { + Some(ComplexType::Simple(SimpleType::Never)) => { + quote! { + #sys_call; + unreachable!() + } + } + Some(ty) => { + let ty = ty.as_rust_type(); + quote!(<#ty>::from_syscall_register(#sys_call)) + } + None => quote!(#sys_call;), + }; + + let v = quote! { + pub unsafe fn #name(#args) #return_type_arrow { + #wrapped + } + }; + println!("{}", v); + v + } +} diff --git a/src/abi/ty/complex.rs b/src/abi/ty/complex.rs index 97188168..b0a2c29a 100644 --- a/src/abi/ty/complex.rs +++ b/src/abi/ty/complex.rs @@ -113,6 +113,15 @@ impl Type for ComplexType { _ => todo!(), } } + + fn emit_from_syscall_arguments( + &self, + _env: &TargetEnv, + _args: &Ident, + _index: usize, + ) -> (TokenStream, usize) { + todo!() + } } impl fmt::Debug for ComplexType { diff --git a/src/abi/ty/mod.rs b/src/abi/ty/mod.rs index dc6fc37e..5225aa6b 100644 --- a/src/abi/ty/mod.rs +++ b/src/abi/ty/mod.rs @@ -25,6 +25,12 @@ pub trait Type { fn as_rust_type(&self) -> TokenStream; fn emit_to_syscall_arguments(&self, env: &TargetEnv, value: &Ident) -> TokenStream; + fn emit_from_syscall_arguments( + &self, + env: &TargetEnv, + args: &Ident, + index: usize, + ) -> (TokenStream, usize); fn as_rust_ref_type(&self, mutable: bool) -> TokenStream { let inner = self.as_rust_type(); diff --git a/src/abi/ty/primitive.rs b/src/abi/ty/primitive.rs index 804d8c68..1288f06e 100644 --- a/src/abi/ty/primitive.rs +++ b/src/abi/ty/primitive.rs @@ -60,6 +60,15 @@ impl Type for AbiPrimitive { fn emit_to_syscall_arguments(&self, _env: &TargetEnv, value: &Ident) -> TokenStream { quote!(#value as usize) } + + fn emit_from_syscall_arguments( + &self, + _env: &TargetEnv, + _args: &Ident, + _index: usize, + ) -> (TokenStream, usize) { + todo!() + } } impl FromStr for AbiPrimitive { diff --git a/src/abi/ty/simple.rs b/src/abi/ty/simple.rs index 9a3c43aa..9eb8ddd5 100644 --- a/src/abi/ty/simple.rs +++ b/src/abi/ty/simple.rs @@ -101,4 +101,13 @@ impl Type for SimpleType { }, } } + + fn emit_from_syscall_arguments( + &self, + _env: &TargetEnv, + _args: &Ident, + _index: usize, + ) -> (TokenStream, usize) { + todo!() + } } diff --git a/src/lib.rs b/src/lib.rs index da99e273..bc49ae63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,88 +1,13 @@ #![feature(if_let_guard, extend_one, proc_macro_span)] -use std::{fmt, rc::Rc}; - -use proc_macro2::TokenStream; -use quote::quote; -use syn::Ident; - -use crate::abi::ty::{ComplexType, SimpleType, Type, TypeWidth}; +use crate::abi::ty::TypeWidth; pub mod abi; pub mod error; pub mod syntax; -pub struct Syscall { - pub name: Ident, - pub args: Vec<(Ident, Rc)>, - pub return_type: Option>, -} - -impl fmt::Debug for Syscall { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Syscall") - .field("name", &self.name) - .field("args", &self.args) - .field("return_type", &self.return_type) - .finish_non_exhaustive() - } -} - #[derive(Debug, PartialEq, Eq, Clone)] pub struct TargetEnv { pub thin_pointer_width: TypeWidth, pub fat_pointer_width: TypeWidth, } - -impl Syscall { - pub fn emit_stub(&self, enum_variant: &syn::Ident, env: &TargetEnv) -> TokenStream { - let Self { - name, - args, - return_type, - } = self; - - let syscall_args = args - .iter() - .map(|(name, ty)| { - let arg = ty.emit_to_syscall_arguments(env, name); - quote!(#arg,) - }) - .collect::(); - let args = args - .iter() - .map(|(name, ty)| { - let ty = ty.as_rust_type(); - quote!(#name: #ty,) - }) - .collect::(); - let return_type_arrow = return_type.as_ref().map(|ty| { - let ty = ty.as_rust_type(); - quote!(-> #ty) - }); - - let sys_call = quote!(syscall!(SyscallFunction::#enum_variant, #syscall_args)); - - let wrapped = match return_type.as_deref() { - Some(ComplexType::Simple(SimpleType::Never)) => { - quote! { - #sys_call; - unreachable!() - } - } - Some(ty) => { - let ty = ty.as_rust_type(); - quote!(<#ty>::from_syscall_register(#sys_call)) - } - None => quote!(#sys_call;), - }; - - let v = quote! { - pub unsafe fn #name(#args) #return_type_arrow { - #wrapped - } - }; - println!("{}", v); - v - } -} diff --git a/src/syntax/bitfield_type.rs b/src/syntax/bitfield_type.rs index 9b0c318d..da3288ef 100644 --- a/src/syntax/bitfield_type.rs +++ b/src/syntax/bitfield_type.rs @@ -35,7 +35,7 @@ impl DocumentItemAttributes for BitfieldType { } impl syn::parse::Parse for BitfieldRange { - fn parse(input: syn::parse::ParseStream) -> syn::Result { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { let start = input.parse()?; if input.peek(Token![..]) { input.parse::()?; @@ -48,7 +48,7 @@ impl syn::parse::Parse for BitfieldRange { } impl syn::parse::Parse for BitfieldTypeField { - fn parse(input: syn::parse::ParseStream) -> syn::Result { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { let attributes = input.parse()?; let name = input.parse()?; input.parse::()?; @@ -62,7 +62,7 @@ impl syn::parse::Parse for BitfieldTypeField { } impl syn::parse::Parse for BitfieldType { - fn parse(input: syn::parse::ParseStream) -> syn::Result { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { let fields; let attributes = input.parse()?; let name = input.parse()?; diff --git a/src/syntax/common.rs b/src/syntax/common.rs index 292a692d..06d7ff39 100644 --- a/src/syntax/common.rs +++ b/src/syntax/common.rs @@ -30,13 +30,13 @@ impl DerefMut for Attributes { } impl syn::parse::Parse for Attributes { - fn parse(input: syn::parse::ParseStream) -> syn::Result { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { syn::Attribute::parse_outer(input).map(Self) } } impl syn::parse::Parse for TypeRepr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { let content; parenthesized!(content in input); let content = content.parse_terminated(syn::Ident::parse, Token![,])?; diff --git a/src/syntax/document.rs b/src/syntax/document.rs index be1c2b3b..71cee8ea 100644 --- a/src/syntax/document.rs +++ b/src/syntax/document.rs @@ -39,7 +39,7 @@ enum DocumentItem { } impl syn::parse::Parse for DocumentItem { - fn parse(input: ParseStream) -> syn::Result { + fn parse(input: ParseStream<'_>) -> syn::Result { let lookahead = input.lookahead1(); if lookahead.peek(Token![extern]) { @@ -87,7 +87,7 @@ impl DocumentItemAttributes for DocumentItem { } impl syn::parse::Parse for Document { - fn parse(input: ParseStream) -> syn::Result { + fn parse(input: ParseStream<'_>) -> syn::Result { let mut types = vec![]; let mut syscalls = vec![]; diff --git a/src/syntax/enum_type.rs b/src/syntax/enum_type.rs index 824454c1..328d165d 100644 --- a/src/syntax/enum_type.rs +++ b/src/syntax/enum_type.rs @@ -23,7 +23,7 @@ pub struct EnumType { } impl syn::parse::Parse for EnumTypeVariant { - fn parse(input: syn::parse::ParseStream) -> syn::Result { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { let attributes = input.parse()?; let discriminant = input.parse()?; let value = if input.peek(Token![=]) { @@ -42,7 +42,7 @@ impl syn::parse::Parse for EnumTypeVariant { } impl syn::parse::Parse for EnumType { - fn parse(input: syn::parse::ParseStream) -> syn::Result { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { let variants; let attributes = input.parse()?; let name = input.parse()?; diff --git a/src/syntax/extern_block.rs b/src/syntax/extern_block.rs index 9e25fd2d..6c204572 100644 --- a/src/syntax/extern_block.rs +++ b/src/syntax/extern_block.rs @@ -16,7 +16,7 @@ pub struct ExternTypeBlock { } impl syn::parse::Parse for ExternType { - fn parse(input: syn::parse::ParseStream) -> syn::Result { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { let attributes = input.parse()?; input.parse::()?; let name = input.parse()?; @@ -35,7 +35,7 @@ impl syn::parse::Parse for ExternType { } impl syn::parse::Parse for ExternTypeBlock { - fn parse(input: syn::parse::ParseStream) -> syn::Result { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { let types; braced!(types in input); let types = types.parse_terminated(ExternType::parse, Token![;])?; diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index ad5096cc..a158f9f3 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -2,13 +2,10 @@ use std::{collections::HashMap, rc::Rc, str::FromStr}; use syn::{punctuated::Punctuated, Expr, ExprLit, Lit, PathSegment}; -use crate::{ - abi::{ - syscall_enum_variant_identifier, - ty::{complex::ExternKind, AbiPrimitive, ComplexType, SimpleType}, - Abi, - }, - Syscall, +use crate::abi::{ + syscall_enum_variant_identifier, + ty::{complex::ExternKind, AbiPrimitive, ComplexType, SimpleType}, + Abi, Syscall, }; mod common; diff --git a/src/syntax/newtype.rs b/src/syntax/newtype.rs index f46d6475..40242182 100644 --- a/src/syntax/newtype.rs +++ b/src/syntax/newtype.rs @@ -22,7 +22,7 @@ impl DocumentItemAttributes for NewType { } impl syn::parse::Parse for NewType { - fn parse(input: syn::parse::ParseStream) -> syn::Result { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { let attributes = input.parse()?; let name = input.parse()?; let repr = input.parse()?; diff --git a/src/syntax/syscall.rs b/src/syntax/syscall.rs index 1e670882..5c6caf56 100644 --- a/src/syntax/syscall.rs +++ b/src/syntax/syscall.rs @@ -22,7 +22,7 @@ impl DocumentItemAttributes for SyscallDefinition { } impl syn::parse::Parse for SyscallDefinition { - fn parse(input: ParseStream) -> syn::Result { + fn parse(input: ParseStream<'_>) -> syn::Result { let arguments; let name = input.parse()?; @@ -49,7 +49,7 @@ impl syn::parse::Parse for SyscallDefinition { } impl syn::parse::Parse for SyscallArgument { - fn parse(input: ParseStream) -> syn::Result { + fn parse(input: ParseStream<'_>) -> syn::Result { let name = input.parse()?; input.parse::()?; let ty = input.parse()?; From 51ba16c591b10249ded89672f29017e743bb2c5f Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Sat, 9 Mar 2024 16:42:07 +0200 Subject: [PATCH 5/9] Split ABI type and syscall generation --- Cargo.lock | 7 +- Cargo.toml | 3 +- abi-lib/Cargo.toml | 6 + abi-lib/src/lib.rs | 18 +++ abi-lib/src/primitive_impls.rs | 46 +++++++ abi-lib/src/wrapper_impls.rs | 59 ++++++++ example-abi/Cargo.toml | 4 + example-abi/build.rs | 13 +- example-abi/src/main.rs | 198 ++++++++++++--------------- example-abi/syscall.abi | 4 + src/abi/mod.rs | 239 +++++++++++++-------------------- 11 files changed, 337 insertions(+), 260 deletions(-) create mode 100644 abi-lib/Cargo.toml create mode 100644 abi-lib/src/lib.rs create mode 100644 abi-lib/src/primitive_impls.rs create mode 100644 abi-lib/src/wrapper_impls.rs diff --git a/Cargo.lock b/Cargo.lock index 350c4606..88b94f72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,10 +2,16 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "abi-lib" +version = "0.1.0" + [[package]] name = "example-abi" version = "0.1.0" dependencies = [ + "abi-lib", + "prettyplease", "syscall-generator", ] @@ -57,7 +63,6 @@ name = "syscall-generator" version = "0.1.0" dependencies = [ "handler", - "prettyplease", "proc-macro2", "quote", "syn", diff --git a/Cargo.toml b/Cargo.toml index 2f369341..494337b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] -prettyplease = "0.2.15" proc-macro2 = "^1.0.63" quote = "^1.0" syn = { version = "2.0.32", features = ["full"] } @@ -13,4 +12,4 @@ thiserror = "1.0.47" handler = { path = "handler", optional = true } [workspace] -members = ["example-abi", "handler"] +members = [ "abi-lib","example-abi", "handler"] diff --git a/abi-lib/Cargo.toml b/abi-lib/Cargo.toml new file mode 100644 index 00000000..f56de8c9 --- /dev/null +++ b/abi-lib/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "abi-lib" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/abi-lib/src/lib.rs b/abi-lib/src/lib.rs new file mode 100644 index 00000000..cf6fecf1 --- /dev/null +++ b/abi-lib/src/lib.rs @@ -0,0 +1,18 @@ +#![no_std] + +mod primitive_impls; +mod wrapper_impls; + +pub trait SyscallRegister { + fn into_syscall_register(self) -> usize; + fn from_syscall_register(value: usize) -> Self; +} + +pub trait SyscallFatRegister { + fn as_syscall_meta(&self) -> usize; + fn as_syscall_data(&self) -> usize; + + fn from_syscall_registers(meta: usize, data: usize) -> Self; +} + +pub unsafe trait ThinWrapped: SyscallRegister {} diff --git a/abi-lib/src/primitive_impls.rs b/abi-lib/src/primitive_impls.rs new file mode 100644 index 00000000..4e7cea11 --- /dev/null +++ b/abi-lib/src/primitive_impls.rs @@ -0,0 +1,46 @@ +use core::num::NonZeroUsize; + +use crate::{SyscallRegister, ThinWrapped}; + +macro_rules! numeric_impl { + ($($ty:ident),+ $(,)?) => { + $( + unsafe impl ThinWrapped for $ty {} + + impl SyscallRegister for $ty { + fn into_syscall_register(self) -> usize { + self as _ + } + + fn from_syscall_register(value: usize) -> Self { + value as _ + } + } + )+ + }; +} + +numeric_impl!(u8, u16, u32, u64); +numeric_impl!(i8, i16, i32, i64); + +unsafe impl ThinWrapped for () {} + +impl SyscallRegister for NonZeroUsize { + fn from_syscall_register(value: usize) -> Self { + unsafe { Self::new_unchecked(value) } + } + + fn into_syscall_register(self) -> usize { + self.into() + } +} + +impl SyscallRegister for () { + fn into_syscall_register(self) -> usize { + 0 + } + + fn from_syscall_register(_value: usize) -> Self { + () + } +} diff --git a/abi-lib/src/wrapper_impls.rs b/abi-lib/src/wrapper_impls.rs new file mode 100644 index 00000000..9385e485 --- /dev/null +++ b/abi-lib/src/wrapper_impls.rs @@ -0,0 +1,59 @@ +use core::num::NonZeroUsize; + +use crate::{SyscallRegister, ThinWrapped}; + +// Result is special: usize is not thinly-wrapped, but is represented in the ABI similar +// to Unix ssize_t-returning system calls +impl SyscallRegister for Result { + fn into_syscall_register(self) -> usize { + todo!() + } + + fn from_syscall_register(value: usize) -> Self { + if (value as isize) < 0 { + todo!() + } else { + Ok(value as _) + } + } +} + +impl SyscallRegister for Option { + fn into_syscall_register(self) -> usize { + todo!() + } + + fn from_syscall_register(_value: usize) -> Self { + todo!() + } +} + +impl SyscallRegister for Result { + fn into_syscall_register(self) -> usize { + todo!() + } + + fn from_syscall_register(_value: usize) -> Self { + todo!() + } +} + +impl SyscallRegister for Option { + fn into_syscall_register(self) -> usize { + todo!() + } + + fn from_syscall_register(_value: usize) -> Self { + todo!() + } +} + +impl SyscallRegister for Result { + fn into_syscall_register(self) -> usize { + todo!() + } + + fn from_syscall_register(_value: usize) -> Self { + todo!() + } +} diff --git a/example-abi/Cargo.toml b/example-abi/Cargo.toml index 4495e912..f6100b26 100644 --- a/example-abi/Cargo.toml +++ b/example-abi/Cargo.toml @@ -3,5 +3,9 @@ name = "example-abi" version = "0.1.0" edition = "2021" +[dependencies] +abi-lib = { path = "../abi-lib" } + [build-dependencies] syscall-generator = { path = ".." } +prettyplease = "0.2.15" diff --git a/example-abi/build.rs b/example-abi/build.rs index f264e139..435993dd 100644 --- a/example-abi/build.rs +++ b/example-abi/build.rs @@ -20,6 +20,15 @@ fn main() { ) .unwrap_fancy("syscall.abi"); - abi.write(output.join("generated_abi.rs")) - .expect("Could not write the generated ABI file"); + let types_file = prettyplease::unparse( + &abi.emit_file(true, false) + .unwrap_fancy("Could not emit generated types"), + ); + let syscalls_file = prettyplease::unparse( + &abi.emit_file(false, true) + .unwrap_fancy("Could not emit generated syscalls"), + ); + + std::fs::write(output.join("generated_types.rs"), types_file).unwrap(); + std::fs::write(output.join("generated_syscalls.rs"), syscalls_file).unwrap(); } diff --git a/example-abi/src/main.rs b/example-abi/src/main.rs index f1d0cddb..e572be01 100644 --- a/example-abi/src/main.rs +++ b/example-abi/src/main.rs @@ -1,137 +1,111 @@ #![feature(exposed_provenance)] -use std::{num::NonZeroUsize, sync::Mutex}; +use std::sync::Mutex; -#[derive(Debug)] -pub enum Error {} +mod types { + use abi_lib::{SyscallFatRegister, SyscallRegister}; -pub enum MappingSource {} -pub struct MountOptions; -pub struct UnmountOptions; -pub struct DirectoryEntry; -pub struct FileAttr; -pub struct SpawnOptions; -pub struct ThreadSpawnOptions; -pub enum MutexOperation {} -pub struct SignalEntryData; -pub struct DeviceRequest; -pub enum SentMessage {} -pub enum ReceivedMessageMetadata {} -pub struct TerminalOptions; -pub struct TerminalSize; -pub struct ExecveOptions; -pub struct FileMetadataUpdate; -pub enum ProcessInfoElement {} -pub enum SocketOption {} -pub enum SystemInfo {} + pub enum MappingSource {} + pub struct MountOptions; + pub struct UnmountOptions; + pub struct DirectoryEntry; + pub struct FileAttr; + pub struct SpawnOptions; + pub struct ThreadSpawnOptions; + pub enum MutexOperation {} + pub struct SignalEntryData; + pub struct DeviceRequest; + pub enum SentMessage {} + pub enum ReceivedMessageMetadata {} + pub struct TerminalOptions; + pub struct TerminalSize; + pub struct ExecveOptions; + pub struct FileMetadataUpdate; + pub enum ProcessInfoElement {} + pub enum SocketOption {} + pub enum SystemInfo {} -pub enum ExitCode { - Exited(u32), - Killed(u32), -} + pub enum ExitCode { + Exited(u32), + Killed(u32), + } -pub enum SeekFrom { - Start(u64), - Current(i64), - End(i64), -} + pub enum SeekFrom { + Start(u64), + Current(i64), + End(i64), + } -impl SyscallRegister for ExitCode { - fn into_syscall_register(self) -> usize { - match self { - Self::Killed(value) => (value as usize) | (1 << u32::BITS), - Self::Exited(value) => value as usize, + impl SyscallRegister for ExitCode { + fn into_syscall_register(self) -> usize { + match self { + Self::Killed(value) => (value as usize) | (1 << u32::BITS), + Self::Exited(value) => value as usize, + } + } + + fn from_syscall_register(value: usize) -> Self { + if value & (1 << u32::BITS) != 0 { + Self::Killed(value as _) + } else { + Self::Exited(value as _) + } } } - fn from_syscall_register(value: usize) -> Self { - if value & (1 << u32::BITS) != 0 { - Self::Killed(value as _) - } else { - Self::Exited(value as _) + impl SyscallFatRegister for SeekFrom { + fn as_syscall_meta(&self) -> usize { + match self { + Self::Start(_) => 0, + Self::Current(_) => 1, + Self::End(_) => 2, + } } - } -} -impl SyscallFatRegister for SeekFrom { - fn as_syscall_meta(&self) -> usize { - match self { - Self::Start(_) => 0, - Self::Current(_) => 1, - Self::End(_) => 2, + fn as_syscall_data(&self) -> usize { + match self { + &Self::Start(value) => value as _, + &Self::Current(value) | &Self::End(value) => value as _, + } } - } - fn as_syscall_data(&self) -> usize { - match self { - &Self::Start(value) => value as _, - &Self::Current(value) | &Self::End(value) => value as _, - } - } - - fn from_syscall_registers(_meta: usize, _data: usize) -> Self { - todo!() - } -} - -impl SyscallRegister for Result<(), Error> { - fn into_syscall_register(self) -> usize { - todo!() - } - - fn from_syscall_register(value: usize) -> Self { - if (value as isize) < 0 { + fn from_syscall_registers(_meta: usize, _data: usize) -> Self { todo!() - } else { - Ok(()) } } + + include!(concat!(env!("OUT_DIR"), "/generated_types.rs")); } -impl SyscallRegister for Result { - fn into_syscall_register(self) -> usize { - todo!() +mod calls { + use super::types::*; + + macro_rules! syscall { + ($f:expr $(,)?) => { + $crate::syscall0($f) + }; + ($f:expr, $a0:expr $(,)?) => { + $crate::syscall1($f, $a0) + }; + ($f:expr, $a0:expr, $a1:expr $(,)?) => { + $crate::syscall2($f, $a0, $a1) + }; + ($f:expr, $a0:expr, $a1:expr, $a2:expr $(,)?) => { + $crate::syscall3($f, $a0, $a1, $a2) + }; + ($f:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr $(,)?) => { + $crate::syscall4($f, $a0, $a1, $a2, $a3) + }; + ($f:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr $(,)?) => { + $crate::syscall5($f, $a0, $a1, $a2, $a3, $a4) + }; } - fn from_syscall_register(value: usize) -> Self { - if (value as isize) < 0 { - todo!() - } else { - Ok(value as _) - } - } + include!(concat!(env!("OUT_DIR"), "/generated_syscalls.rs")); } -impl SyscallRegister for Option { - fn into_syscall_register(self) -> usize { - todo!() - } - - fn from_syscall_register(_value: usize) -> Self { - todo!() - } -} - -macro_rules! syscall { - ($f:expr $(,)?) => { - syscall0($f) - }; - ($f:expr, $a0:expr $(,)?) => { - syscall1($f, $a0) - }; - ($f:expr, $a0:expr, $a1:expr $(,)?) => { - syscall2($f, $a0, $a1) - }; - ($f:expr, $a0:expr, $a1:expr, $a2:expr $(,)?) => { - syscall3($f, $a0, $a1, $a2) - }; - ($f:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr $(,)?) => { - syscall4($f, $a0, $a1, $a2, $a3) - }; - ($f:expr, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr $(,)?) => { - syscall5($f, $a0, $a1, $a2, $a3, $a4) - }; -} +pub use calls::*; +pub use types::*; static SYSCALL_TRACE: Mutex)>> = Mutex::new(Vec::new()); @@ -171,6 +145,4 @@ fn syscall5(f: SyscallFunction, a0: usize, a1: usize, a2: usize, a3: usize, a4: 0 } -include!(concat!(env!("OUT_DIR"), "/generated_abi.rs")); - fn main() {} diff --git a/example-abi/syscall.abi b/example-abi/syscall.abi index e008f120..32d3b005 100644 --- a/example-abi/syscall.abi +++ b/example-abi/syscall.abi @@ -94,6 +94,10 @@ enum SocketType(u32) { UdpPacket } +enum Error(u32) { + Dummy +} + // Misc syscall debug_trace(msg: &str); diff --git a/src/abi/mod.rs b/src/abi/mod.rs index b33cf0de..e89215c9 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -1,14 +1,17 @@ -use std::{collections::HashMap, fs::File, io::Write, path::Path, rc::Rc}; +use std::{collections::HashMap, path::Path, rc::Rc}; use proc_macro2::TokenStream; use quote::{quote, TokenStreamExt}; -use syn::{punctuated::Punctuated, spanned::Spanned, token}; +use syn::{ + punctuated::Punctuated, + spanned::Spanned, + token::{self}, +}; pub mod ty; use crate::{ - abi::ty::{ComplexType, TypeWidth}, - error::Error, + abi::ty::ComplexType, syntax::{ parse_abi, Attributes, BitfieldRange, BitfieldType, BitfieldTypeField, DocumentTypeDefinition, EnumType, NewType, ParseError, TypeRepr, @@ -35,7 +38,6 @@ pub struct Abi { pub struct AbiBuilder { abi: Abi, target_env: TargetEnv, - emit_syscall_traits: bool, } pub fn syscall_enum_variant_identifier(name: &syn::Ident) -> syn::Ident { @@ -56,33 +58,31 @@ pub fn syscall_enum_variant_identifier(name: &syn::Ident) -> syn::Ident { impl AbiBuilder { pub fn from_file>(path: P, target_env: TargetEnv) -> Result { let abi = Abi::read(path)?; - Ok(Self { - abi, - target_env, - emit_syscall_traits: true, - }) + Ok(Self { abi, target_env }) } - pub fn write>(self, path: P) -> Result<(), Error> { - // Generate preamble + pub fn emit_file(&self, emit_types: bool, emit_calls: bool) -> Result { let mut stream = TokenStream::new(); - if self.emit_syscall_traits { - stream.append_all(quote! { - pub trait SyscallRegister { - fn into_syscall_register(self) -> usize; - fn from_syscall_register(value: usize) -> Self; - } - - pub trait SyscallFatRegister { - fn as_syscall_meta(&self) -> usize; - fn as_syscall_data(&self) -> usize; - - fn from_syscall_registers(meta: usize, data: usize) -> Self; - } - }); + if emit_types { + stream.extend(self.emit_syscall_enum()); + stream.extend(self.emit_type_definitions()); } + if emit_calls { + // Preamble + stream.extend(quote! { + #[allow(dead_code)] + use abi_lib::{SyscallRegister, SyscallFatRegister}; + }); + stream.extend(self.emit_syscalls()); + } + + let file = syn::parse_file(&stream.to_string())?; + Ok(file) + } + + fn emit_syscall_enum(&self) -> TokenStream { let syscall_discriminants = self .abi .syscalls @@ -93,30 +93,58 @@ impl AbiBuilder { }) .collect::>(); - stream.append_all(quote! { - #[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] - #[repr(usize)] - pub enum SyscallFunction { - #syscall_discriminants - } + quote! { + #[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] + #[repr(usize)] + pub enum SyscallFunction { + #syscall_discriminants + } - impl From for usize { - fn from(value: SyscallFunction) -> usize { - value as usize - } - } - }); - - stream.append_all(self.abi.generate_user_side(&self.target_env)); - - let file = syn::parse_file(&stream.to_string())?; - let out = prettyplease::unparse(&file); - - let mut output = File::create(path)?; - output.write_all(out.as_bytes())?; - - Ok(()) + impl From for usize { + fn from(value: SyscallFunction) -> usize { + value as usize + } + } + } } + + fn emit_type_definitions(&self) -> TokenStream { + let mut stream = TokenStream::new(); + + for (_, (ty, def)) in &self.abi.types { + stream.extend(def.generate_type_definition(ty, &self.target_env)); + } + + stream + } + + fn emit_syscalls(&self) -> TokenStream { + let mut stream = TokenStream::new(); + + for (variant, syscall) in &self.abi.syscalls { + stream.append_all(syscall.emit_stub(variant, &self.target_env)); + } + + stream + } + + // pub fn write>(self, path: P) -> Result<(), Error> { + // // Generate preamble + // let mut stream = TokenStream::new(); + + // stream.append_all(quote! { + // }); + + // stream.append_all(self.abi.generate_user_side(&self.target_env)); + + // let file = syn::parse_file(&stream.to_string())?; + // let out = prettyplease::unparse(&file); + + // let mut output = File::create(path)?; + // output.write_all(out.as_bytes())?; + + // Ok(()) + // } } impl Abi { @@ -125,33 +153,24 @@ impl Abi { parse_abi(&data) } - pub fn write>(&self, path: P) -> Result<(), Error> { - // TODO read TargetEnv - let target = TargetEnv { - thin_pointer_width: TypeWidth::U64, - fat_pointer_width: TypeWidth::U128, - }; - let mut output = File::create(path)?; - let stream = self.generate_user_side(&target); - let file = syn::parse_file(&stream.to_string())?; - let out = prettyplease::unparse(&file); - output.write_all(out.as_bytes())?; - Ok(()) - } + // pub fn write>(&self, path: P) -> Result<(), Error> { + // // TODO read TargetEnv + // let target = TargetEnv { + // thin_pointer_width: TypeWidth::U64, + // fat_pointer_width: TypeWidth::U128, + // }; + // let mut output = File::create(path)?; + // let stream = self.generate_user_side(&target); + // let file = syn::parse_file(&stream.to_string())?; + // let out = prettyplease::unparse(&file); + // output.write_all(out.as_bytes())?; + // Ok(()) + // } - pub fn generate_user_side(&self, env: &TargetEnv) -> TokenStream { - let mut stream = TokenStream::new(); + // pub fn generate_user_side(&self, env: &TargetEnv) -> TokenStream { + // let mut stream = TokenStream::new(); - for (_, (ty, def)) in &self.types { - stream.extend(def.generate_type_definition(ty, env)); - } - - for (variant, syscall) in &self.syscalls { - stream.append_all(syscall.emit_stub(variant, env)); - } - - stream - } + // } } impl GenerateTypeDefinition for EnumType { @@ -172,15 +191,13 @@ impl GenerateTypeDefinition for EnumType { #variants } - impl SyscallRegister for #name { + impl abi_lib::SyscallRegister for #name { fn into_syscall_register(self) -> usize { self as #repr as usize } fn from_syscall_register(value: usize) -> Self { - unsafe { - core::mem::transmute(value as #repr) - } + unsafe { core::mem::transmute(value as #repr) } } } }; @@ -189,38 +206,7 @@ impl GenerateTypeDefinition for EnumType { quote! { #direct - impl SyscallRegister for Result<#name, Error> { - fn into_syscall_register(self) -> usize { - match self { - Ok(value) => value as #repr as usize, - Err(_err) => todo!(), - } - } - - fn from_syscall_register(value: usize) -> Self { - if (value as isize) >= 0 { - Ok(#name::from_syscall_register(value)) - } else { - todo!() - } - } - } - - impl SyscallRegister for Option<#name> { - fn into_syscall_register(self) -> usize { - match self { - Some(value) => value as #repr as usize, - None => usize::MAX - } - } - - fn from_syscall_register(value: usize) -> Self { - match value { - usize::MAX => None, - _ => Some(#name::from_syscall_register(value)) - } - } - } + unsafe impl abi_lib::ThinWrapped for #name {} } } else { direct @@ -243,7 +229,7 @@ fn generate_transparent_struct( #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct #name(pub(crate) #repr); - impl SyscallRegister for #name { + impl abi_lib::SyscallRegister for #name { fn into_syscall_register(self) -> usize { self.0 as _ } @@ -258,38 +244,7 @@ fn generate_transparent_struct( quote! { #direct - impl SyscallRegister for Result<#name, Error> { - fn into_syscall_register(self) -> usize { - match self { - Ok(value) => value.0 as _, - Err(_err) => todo!(), - } - } - - fn from_syscall_register(value: usize) -> Self { - if (value as isize) >= 0 { - Ok(#name(value as _)) - } else { - todo!() - } - } - } - - impl SyscallRegister for Option<#name> { - fn into_syscall_register(self) -> usize { - match self { - Some(value) => value.0 as _, - None => usize::MAX - } - } - - fn from_syscall_register(value: usize) -> Self { - match value { - usize::MAX => None, - _ => Some(#name(value as _)) - } - } - } + unsafe impl abi_lib::ThinWrapped for #name {} } } else { direct From b39f68e78f7857e8e3e76bf97f26a23af499ac24 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Sun, 10 Mar 2024 13:19:12 +0200 Subject: [PATCH 6/9] Better type generation --- Cargo.lock | 19 +++++++++ abi-lib/Cargo.toml | 9 +++++ src/abi/mod.rs | 99 +++++++++++++++++++++++++++++----------------- 3 files changed, 90 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88b94f72..8e61f3b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,6 +5,19 @@ version = 3 [[package]] name = "abi-lib" version = "0.1.0" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "compiler_builtins" +version = "0.1.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68bc55329711cd719c2687bb147bc06211b0521f97ef398280108ccb23227e9" +dependencies = [ + "rustc-std-workspace-core", +] [[package]] name = "example-abi" @@ -47,6 +60,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rustc-std-workspace-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1956f5517128a2b6f23ab2dadf1a976f4f5b27962e7724c2bf3d45e539ec098c" + [[package]] name = "syn" version = "2.0.52" diff --git a/abi-lib/Cargo.toml b/abi-lib/Cargo.toml index f56de8c9..4845c848 100644 --- a/abi-lib/Cargo.toml +++ b/abi-lib/Cargo.toml @@ -4,3 +4,12 @@ version = "0.1.0" edition = "2021" [dependencies] +core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" } +compiler_builtins = { version = "0.1", optional = true } + +[features] +default = [] +rustc-dep-of-std = [ + "core", + "compiler_builtins/rustc-dep-of-std", +] diff --git a/src/abi/mod.rs b/src/abi/mod.rs index e89215c9..72bbc629 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -127,24 +127,6 @@ impl AbiBuilder { stream } - - // pub fn write>(self, path: P) -> Result<(), Error> { - // // Generate preamble - // let mut stream = TokenStream::new(); - - // stream.append_all(quote! { - // }); - - // stream.append_all(self.abi.generate_user_side(&self.target_env)); - - // let file = syn::parse_file(&stream.to_string())?; - // let out = prettyplease::unparse(&file); - - // let mut output = File::create(path)?; - // output.write_all(out.as_bytes())?; - - // Ok(()) - // } } impl Abi { @@ -152,25 +134,6 @@ impl Abi { let data = std::fs::read_to_string(path)?; parse_abi(&data) } - - // pub fn write>(&self, path: P) -> Result<(), Error> { - // // TODO read TargetEnv - // let target = TargetEnv { - // thin_pointer_width: TypeWidth::U64, - // fat_pointer_width: TypeWidth::U128, - // }; - // let mut output = File::create(path)?; - // let stream = self.generate_user_side(&target); - // let file = syn::parse_file(&stream.to_string())?; - // let out = prettyplease::unparse(&file); - // output.write_all(out.as_bytes())?; - // Ok(()) - // } - - // pub fn generate_user_side(&self, env: &TargetEnv) -> TokenStream { - // let mut stream = TokenStream::new(); - - // } } impl GenerateTypeDefinition for EnumType { @@ -191,6 +154,23 @@ impl GenerateTypeDefinition for EnumType { #variants } + impl #name { + pub const fn into_raw(self) -> #repr { + self as #repr + } + + pub const unsafe fn from_raw(value: #repr) -> Self { + core::mem::transmute(value) + } + } + + impl From<#name> for #repr { + #[inline] + fn from(value: #name) -> #repr { + value as #repr + } + } + impl abi_lib::SyscallRegister for #name { fn into_syscall_register(self) -> usize { self as #repr as usize @@ -229,6 +209,23 @@ fn generate_transparent_struct( #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct #name(pub(crate) #repr); + impl #name { + pub const fn into_raw(self) -> #repr { + self.0 + } + + pub const unsafe fn from_raw(value: #repr) -> Self { + Self(value) + } + } + + impl From<#name> for #repr { + #[inline] + fn from(value: #name) -> #repr { + value.0 + } + } + impl abi_lib::SyscallRegister for #name { fn into_syscall_register(self) -> usize { self.0 as _ @@ -309,6 +306,34 @@ impl GenerateTypeDefinition for BitfieldType { impl #name { #field_impls + + pub const fn contains(&self, value: Self) -> bool { + self.0 & value.0 == value.0 + } + } + + impl core::ops::BitAnd for #name { + type Output = #name; + + fn bitand(self, other: Self) -> Self { + Self(self.0 & other.0) + } + } + + impl core::ops::BitOr for #name { + type Output = #name; + + fn bitor(self, other: Self) -> Self { + Self(self.0 | other.0) + } + } + + impl core::ops::Not for #name { + type Output = #name; + + fn not(self) -> Self { + Self(!self.0) + } } }; From 28df46dec52d666c3f3f26f6b3edef2641db5199 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Tue, 12 Mar 2024 12:42:13 +0200 Subject: [PATCH 7/9] Implement kernel-side dispatcher generator --- Cargo.lock | 7 +- Cargo.toml | 2 - abi-lib/src/lib.rs | 1 + abi-lib/src/primitive_impls.rs | 10 ++ abi-lib/src/wrapper_impls.rs | 64 +++++++--- example-abi/src/main.rs | 6 +- example-abi/syscall.abi | 216 +++++++++++++++++---------------- handler/Cargo.toml | 5 + handler/src/lib.rs | 106 ++++++++++++++++ src/abi/mod.rs | 138 ++++++++++++++++++--- src/abi/syscall.rs | 15 ++- src/abi/ty/complex.rs | 60 +++++++-- src/abi/ty/primitive.rs | 12 +- src/abi/ty/simple.rs | 53 +++++++- src/syntax/enum_type.rs | 22 ++-- src/syntax/mod.rs | 82 +++++++++---- 16 files changed, 601 insertions(+), 198 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8e61f3b0..bfc6ff25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,6 +31,12 @@ dependencies = [ [[package]] name = "handler" version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "syscall-generator", +] [[package]] name = "prettyplease" @@ -81,7 +87,6 @@ dependencies = [ name = "syscall-generator" version = "0.1.0" dependencies = [ - "handler", "proc-macro2", "quote", "syn", diff --git a/Cargo.toml b/Cargo.toml index 494337b6..e1a577e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,5 @@ quote = "^1.0" syn = { version = "2.0.32", features = ["full"] } thiserror = "1.0.47" -handler = { path = "handler", optional = true } - [workspace] members = [ "abi-lib","example-abi", "handler"] diff --git a/abi-lib/src/lib.rs b/abi-lib/src/lib.rs index cf6fecf1..e38307c7 100644 --- a/abi-lib/src/lib.rs +++ b/abi-lib/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(never_type)] #![no_std] mod primitive_impls; diff --git a/abi-lib/src/primitive_impls.rs b/abi-lib/src/primitive_impls.rs index 4e7cea11..a2918a49 100644 --- a/abi-lib/src/primitive_impls.rs +++ b/abi-lib/src/primitive_impls.rs @@ -44,3 +44,13 @@ impl SyscallRegister for () { () } } + +impl SyscallRegister for ! { + fn from_syscall_register(_value: usize) -> Self { + unreachable!() + } + + fn into_syscall_register(self) -> usize { + self + } +} diff --git a/abi-lib/src/wrapper_impls.rs b/abi-lib/src/wrapper_impls.rs index 9385e485..860a8b52 100644 --- a/abi-lib/src/wrapper_impls.rs +++ b/abi-lib/src/wrapper_impls.rs @@ -2,16 +2,27 @@ use core::num::NonZeroUsize; use crate::{SyscallRegister, ThinWrapped}; +pub fn make_error(e: E) -> usize { + (-(e.into_syscall_register() as isize)) as usize +} + +pub fn get_error(value: usize) -> E { + E::from_syscall_register((-(value as isize)) as usize) +} + // Result is special: usize is not thinly-wrapped, but is represented in the ABI similar // to Unix ssize_t-returning system calls impl SyscallRegister for Result { fn into_syscall_register(self) -> usize { - todo!() + match self { + Ok(value) => value, + Err(err) => make_error(err), + } } fn from_syscall_register(value: usize) -> Self { if (value as isize) < 0 { - todo!() + Err(get_error(value)) } else { Ok(value as _) } @@ -20,40 +31,65 @@ impl SyscallRegister for Result { impl SyscallRegister for Option { fn into_syscall_register(self) -> usize { - todo!() + match self { + None => usize::MAX, + Some(value) => value.into_syscall_register(), + } } - fn from_syscall_register(_value: usize) -> Self { - todo!() + fn from_syscall_register(value: usize) -> Self { + match value { + usize::MAX => None, + _ => Some(T::from_syscall_register(value)), + } } } impl SyscallRegister for Result { fn into_syscall_register(self) -> usize { - todo!() + match self { + Ok(value) => value.into_syscall_register(), + Err(err) => make_error(err), + } } - fn from_syscall_register(_value: usize) -> Self { - todo!() + fn from_syscall_register(value: usize) -> Self { + if (value as isize) < 0 { + Err(get_error(value)) + } else { + Ok(T::from_syscall_register(value)) + } } } impl SyscallRegister for Option { fn into_syscall_register(self) -> usize { - todo!() + match self { + Some(value) => value.into(), + None => 0, + } } - fn from_syscall_register(_value: usize) -> Self { - todo!() + fn from_syscall_register(value: usize) -> Self { + NonZeroUsize::new(value) } } impl SyscallRegister for Result { fn into_syscall_register(self) -> usize { - todo!() + match self { + Ok(value) => value.into(), + Err(err) => make_error(err), + } } - fn from_syscall_register(_value: usize) -> Self { - todo!() + fn from_syscall_register(value: usize) -> Self { + if (value as isize) < 0 { + Err(get_error(value)) + } else if value != 0 { + Ok(unsafe { NonZeroUsize::new_unchecked(value) }) + } else { + unreachable!() + } } } diff --git a/example-abi/src/main.rs b/example-abi/src/main.rs index e572be01..a25c508c 100644 --- a/example-abi/src/main.rs +++ b/example-abi/src/main.rs @@ -3,6 +3,8 @@ use std::sync::Mutex; mod types { + use std::marker::PhantomData; + use abi_lib::{SyscallFatRegister, SyscallRegister}; pub enum MappingSource {} @@ -10,7 +12,9 @@ mod types { pub struct UnmountOptions; pub struct DirectoryEntry; pub struct FileAttr; - pub struct SpawnOptions; + pub struct SpawnOptions<'a> { + _a: PhantomData<&'a ()>, + } pub struct ThreadSpawnOptions; pub enum MutexOperation {} pub struct SignalEntryData; diff --git a/example-abi/syscall.abi b/example-abi/syscall.abi index 32d3b005..44fadaa5 100644 --- a/example-abi/syscall.abi +++ b/example-abi/syscall.abi @@ -78,120 +78,124 @@ bitfield FileMode(u32) { } enum Signal(u32) { - MemoryAccessViolation, - Aborted, - Killed, - Interrupted + MemoryAccessViolation = 1, + Aborted = 2, + Killed = 3, + Interrupted = 4 } enum PollControl(u32) { - AddFd + AddFd = 1 } enum SocketType(u32) { - RawPacket, - TcpStream, - UdpPacket + RawPacket = 1, + TcpStream = 2, + UdpPacket = 3 } enum Error(u32) { - Dummy + Dummy = 1, + UndefinedSyscall = 2, + InvalidArgument = 3, } -// Misc - -syscall debug_trace(msg: &str); -syscall get_random(buffer: &mut [u8]); -syscall get_system_info(info: &mut SystemInfo) -> Result; - -// Memory management - -syscall map_memory(hint: Option, len: usize, source: &MappingSource) -> Result; -syscall unmap_memory(virt: usize, len: usize) -> Result<()>; - -// Process/thread management - -syscall spawn_process(options: &SpawnOptions) -> Result; -syscall spawn_thread(options: &ThreadSpawnOptions) -> Result; - -syscall exit_thread(code: ExitCode) -> !; -syscall exit_process(code: ExitCode) -> !; -syscall wait_process(pid: ProcessId, status: &mut ExitCode) -> Result<()>; -syscall send_signal(pid: ProcessId, signal: Signal) -> Result<()>; -syscall nanosleep(duration: &Duration); - -syscall mutex(mutex: &Mutex, op: &MutexOperation) -> Result<()>; - -syscall set_signal_entry(entry: usize, stack: usize); -syscall exit_signal(frame: &SignalEntryData) -> !; - -syscall get_pid() -> ProcessId; - -syscall start_session() -> Result<()>; -syscall set_process_group_id(pid: ProcessId, pgid: Option) -> Result; - -syscall get_process_info(what: &mut ProcessInfoElement) -> Result<()>; -syscall set_process_info(what: &ProcessInfoElement) -> Result<()>; - -// C compat - -syscall fork() -> Result; -syscall execve(opts: &ExecveOptions) -> Result<()>; - -// I/O - -syscall write(fd: Fd, data: &[u8]) -> Result; -syscall read(fd: Fd, data: &mut [u8]) -> Result; -syscall open(at: Option, path: &str, opts: OpenOptions, mode: FileMode) -> Result; -syscall close(fd: Fd) -> Result<()>; -syscall mount(options: &MountOptions) -> Result<()>; -syscall unmount(options: &UnmountOptions) -> Result<()>; -syscall open_directory(at: Option, path: &str) -> Result; -syscall read_directory_entries(fd: Fd, buffer: &mut [MaybeUninit]) -> Result; -syscall create_directory(at: Option, path: &str, mode: FileMode) -> Result<()>; -syscall remove(at: Option, path: &str) -> Result<()>; -syscall remove_directory(at: Option, path: &str) -> Result<()>; -syscall get_metadata(at: Option, path: &str, metadata: &mut FileAttr, follow: bool) -> Result<()>; -// TODO this one is broken -syscall seek(fd: Fd, pos: SeekFrom) -> Result; - -syscall device_request(fd: Fd, req: &mut DeviceRequest) -> Result<()>; - -syscall create_pipe(ends: &mut [MaybeUninit; 2]) -> Result<()>; - -syscall create_poll_channel() -> Result; -syscall poll_channel_control(poll_fd: Fd, ctl: PollControl, fd: Fd) -> Result<()>; -syscall poll_channel_wait(poll_fd: Fd, timeout: &Option, out: &mut Option) -> Result<()>; - -syscall open_channel(name: &str, subscribe: bool) -> Result; -syscall send_message(fd: Fd, message: &SentMessage, destination: MessageDestination) -> Result<()>; -syscall receive_message( - fd: Fd, - metadata: &mut MaybeUninit, - buffer: &mut [u8], - from: &mut MaybeUninit -) -> Result; - -syscall create_shared_memory(size: usize) -> Result; -syscall create_pty(options: &TerminalOptions, size: &TerminalSize, fds: &mut [MaybeUninit; 2]) -> Result<()>; - -syscall clone_fd(source: Fd, target: Option) -> Result; - -syscall update_metadata(at: Option, path: &str, update: &FileMetadataUpdate) -> Result<()>; - -syscall create_timer(repeat: bool) -> Result; - -// Network - -syscall bind_socket(listen_address: &SocketAddr, ty: SocketType) -> Result; -syscall connect_socket( - socket_fd: Option, - remote_address: &SocketAddr, - ty: SocketType, - local_address: &mut MaybeUninit -) -> Result; -syscall send_to(socket_fd: Fd, data: &[u8], recepient: &Option) -> Result; -syscall receive_from(socket_fd: Fd, buffer: &mut [u8], sender: &mut MaybeUninit) -> Result; -syscall get_socket_option(socket_fd: Fd, option: &mut SocketOption) -> Result<()>; -syscall set_socket_option(socket_fd: Fd, option: &SocketOption) -> Result<()>; -syscall accept(listener: Fd, address: &mut MaybeUninit) -> Result; +syscall test_syscall(a: &(u32, u64)); +// +// // Misc +// +// syscall debug_trace(msg: &str); +// syscall get_random(buffer: &mut [u8]); +// syscall get_system_info(info: &mut SystemInfo) -> Result; +// +// // Memory management +// +// syscall map_memory(hint: Option, len: usize, source: &MappingSource) -> Result; +// syscall unmap_memory(virt: usize, len: usize) -> Result<()>; +// +// // Process/thread management +// +// syscall spawn_process(options: &SpawnOptions<'_>) -> Result; +// syscall spawn_thread(options: &ThreadSpawnOptions) -> Result; +// +// syscall exit_thread(code: ExitCode) -> !; +// syscall exit_process(code: ExitCode) -> !; +// syscall wait_process(pid: ProcessId, status: &mut ExitCode) -> Result<()>; +// syscall send_signal(pid: ProcessId, signal: Signal) -> Result<()>; +// syscall nanosleep(duration: &Duration); +// +// syscall mutex(mutex: &Mutex, op: &MutexOperation) -> Result<()>; +// +// syscall set_signal_entry(entry: usize, stack: usize); +// syscall exit_signal(frame: &SignalEntryData) -> !; +// +// syscall get_pid() -> ProcessId; +// +// syscall start_session() -> Result<()>; +// syscall set_process_group_id(pid: ProcessId, pgid: Option) -> Result; +// +// syscall get_process_info(what: &mut ProcessInfoElement) -> Result<()>; +// syscall set_process_info(what: &ProcessInfoElement) -> Result<()>; +// +// // C compat +// +// syscall fork() -> Result; +// syscall execve(opts: &ExecveOptions) -> Result<()>; +// +// // I/O +// +// syscall write(fd: Fd, data: &[u8]) -> Result; +// syscall read(fd: Fd, data: &mut [u8]) -> Result; +// syscall open(at: Option, path: &str, opts: OpenOptions, mode: FileMode) -> Result; +// syscall close(fd: Fd) -> Result<()>; +// syscall mount(options: &MountOptions) -> Result<()>; +// syscall unmount(options: &UnmountOptions) -> Result<()>; +// syscall open_directory(at: Option, path: &str) -> Result; +// syscall read_directory_entries(fd: Fd, buffer: &mut [MaybeUninit]) -> Result; +// syscall create_directory(at: Option, path: &str, mode: FileMode) -> Result<()>; +// syscall remove(at: Option, path: &str) -> Result<()>; +// syscall remove_directory(at: Option, path: &str) -> Result<()>; +// syscall get_metadata(at: Option, path: &str, metadata: &mut FileAttr, follow: bool) -> Result<()>; +// // TODO this one is broken +// syscall seek(fd: Fd, pos: SeekFrom) -> Result; +// +// syscall device_request(fd: Fd, req: &mut DeviceRequest) -> Result<()>; +// +// syscall create_pipe(ends: &mut [MaybeUninit; 2]) -> Result<()>; +// +// syscall create_poll_channel() -> Result; +// syscall poll_channel_control(poll_fd: Fd, ctl: PollControl, fd: Fd) -> Result<()>; +// syscall poll_channel_wait(poll_fd: Fd, timeout: &Option, out: &mut Option) -> Result<()>; +// +// syscall open_channel(name: &str, subscribe: bool) -> Result; +// syscall send_message(fd: Fd, message: &SentMessage, destination: MessageDestination) -> Result<()>; +// syscall receive_message( +// fd: Fd, +// metadata: &mut MaybeUninit, +// buffer: &mut [u8], +// from: &mut MaybeUninit +// ) -> Result; +// +// syscall create_shared_memory(size: usize) -> Result; +// syscall create_pty(options: &TerminalOptions, size: &TerminalSize, fds: &mut [MaybeUninit; 2]) -> Result<()>; +// +// syscall clone_fd(source: Fd, target: Option) -> Result; +// +// syscall update_metadata(at: Option, path: &str, update: &FileMetadataUpdate) -> Result<()>; +// +// syscall create_timer(repeat: bool) -> Result; +// +// // Network +// +// syscall bind_socket(listen_address: &SocketAddr, ty: SocketType) -> Result; +// syscall connect_socket( +// socket_fd: Option, +// remote_address: &SocketAddr, +// ty: SocketType, +// local_address: &mut MaybeUninit +// ) -> Result; +// syscall send_to(socket_fd: Fd, data: &[u8], recepient: &Option) -> Result; +// syscall receive_from(socket_fd: Fd, buffer: &mut [u8], sender: &mut MaybeUninit) -> Result; +// syscall get_socket_option(socket_fd: Fd, option: &mut SocketOption) -> Result<()>; +// syscall set_socket_option(socket_fd: Fd, option: &SocketOption) -> Result<()>; +// syscall accept(listener: Fd, address: &mut MaybeUninit) -> Result; diff --git a/handler/Cargo.toml b/handler/Cargo.toml index a3818d5d..73e40651 100644 --- a/handler/Cargo.toml +++ b/handler/Cargo.toml @@ -4,6 +4,11 @@ version = "0.1.0" edition = "2021" [dependencies] +proc-macro2 = "1.0.78" +quote = "1.0.35" +syn = { version = "2.0.52", features = ["full"] } + +syscall-generator = { path = ".." } [lib] proc-macro = true diff --git a/handler/src/lib.rs b/handler/src/lib.rs index e69de29b..99acebe5 100644 --- a/handler/src/lib.rs +++ b/handler/src/lib.rs @@ -0,0 +1,106 @@ +use proc_macro::TokenStream as TokenStream1; +use proc_macro2::Span; +use quote::quote; +use syn::{parse_macro_input, punctuated::Punctuated, Token}; +use syscall_generator::{ + abi::{ty::TypeWidth, Abi}, + syntax::UnwrapFancy, + TargetEnv, +}; + +enum DispatcherAbiPath { + Env(syn::LitStr), +} + +struct DispatcherInput { + abi_document_path: DispatcherAbiPath, + dispatcher_name: syn::Ident, + impl_module: syn::Path, +} + +impl syn::parse::Parse for DispatcherAbiPath { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let key = input.parse::()?; + input.parse::()?; + let value = input.parse::()?; + + match key.to_string().as_str() { + "env" => Ok(Self::Env(value)), + _ => todo!(), + } + } +} + +impl syn::parse::Parse for DispatcherInput { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let abi_document_path = input.parse()?; + input.parse::()?; + let dispatcher_name = input.parse()?; + input.parse::()?; + let impl_module = input.parse()?; + Ok(Self { + abi_document_path, + dispatcher_name, + impl_module, + }) + } +} + +impl DispatcherAbiPath { + pub fn value(&self) -> String { + match self { + Self::Env(var) => std::env::var(var.value()).unwrap(), + } + } +} + +#[proc_macro] +pub fn syscall_dispatcher(input: TokenStream1) -> TokenStream1 { + let input = parse_macro_input!(input as DispatcherInput); + let abi = Abi::read(input.abi_document_path.value()).unwrap_fancy("TODO: proper error here"); + + let env = TargetEnv { + thin_pointer_width: TypeWidth::U64, + fat_pointer_width: TypeWidth::U128, + }; + + let handler_module = input.impl_module; + + let raw_fn = syn::Ident::new("__fn", Span::call_site()); + let raw_args = syn::Ident::new("__args", Span::call_site()); + + let mut branches = Punctuated::<_, Token![,]>::new(); + + for (enum_variant, syscall) in abi.syscalls.iter() { + let named_args = (0..syscall.args.len()) + .map(|i| syn::Ident::new(&format!("__a{}", i), Span::call_site())) + .collect::>(); + let call_args = named_args + .iter() + .cloned() + .collect::>(); + let syscall_name = &syscall.name; + + let handler_fn = quote! { #handler_module::#syscall_name }; + + let body = quote! { + Ok((#handler_fn(#call_args)).into_syscall_register()) + }; + + let match_arm = + syscall.emit_handler_branch(&named_args, &raw_args, body, enum_variant, &env); + + branches.push(match_arm); + } + + let dispatcher_name = input.dispatcher_name; + let dispatcher_fn = quote! { + pub(crate) fn #dispatcher_name(#raw_fn: SyscallFunction, #raw_args: &[usize]) -> Result { + match #raw_fn { + #branches + } + } + }; + + dispatcher_fn.into() +} diff --git a/src/abi/mod.rs b/src/abi/mod.rs index 72bbc629..5a6c7a42 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -14,12 +14,12 @@ use crate::{ abi::ty::ComplexType, syntax::{ parse_abi, Attributes, BitfieldRange, BitfieldType, BitfieldTypeField, - DocumentTypeDefinition, EnumType, NewType, ParseError, TypeRepr, + DocumentTypeDefinition, EnumType, EnumTypeVariant, NewType, ParseError, TypeRepr, }, TargetEnv, }; -use self::ty::Type; +use self::ty::{complex::ExternKind, Type}; mod syscall; @@ -29,8 +29,43 @@ pub trait GenerateTypeDefinition { fn generate_type_definition(&self, abi_type: &ComplexType, env: &TargetEnv) -> TokenStream; } +pub struct TypeEnv { + pub locals: HashMap, DocumentTypeDefinition)>, + pub externs: HashMap, +} + +impl TypeEnv { + pub fn new() -> Self { + Self { + locals: HashMap::new(), + externs: HashMap::new(), + } + } + + pub fn lookup_local(&self, name: &str) -> Option<&(Rc, DocumentTypeDefinition)> { + self.locals.get(name) + } + + pub fn lookup_extern(&self, name: &str) -> Option<&(syn::Type, ExternKind)> { + self.externs.get(name) + } + + pub fn insert_local( + &mut self, + name: String, + abi_type: Rc, + def: DocumentTypeDefinition, + ) { + self.locals.insert(name, (abi_type, def)); + } + + pub fn insert_extern(&mut self, name: String, def: syn::Type, kind: ExternKind) { + self.externs.insert(name, (def, kind)); + } +} + pub struct Abi { - pub types: HashMap, DocumentTypeDefinition)>, + pub types: TypeEnv, // Contains syscalls and their corresponding SyscallFunction enum variants pub syscalls: Vec<(syn::Ident, Syscall)>, } @@ -72,7 +107,7 @@ impl AbiBuilder { if emit_calls { // Preamble stream.extend(quote! { - #[allow(dead_code)] + #[allow(unused_imports)] use abi_lib::{SyscallRegister, SyscallFatRegister}; }); stream.extend(self.emit_syscalls()); @@ -89,29 +124,52 @@ impl AbiBuilder { .iter() .enumerate() .map(|(index, (variant, _))| { + let index = index + 1; quote! { #variant = #index } }) .collect::>(); - quote! { - #[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] - #[repr(usize)] - pub enum SyscallFunction { - #syscall_discriminants - } + let syscall_try_from_branches = self + .abi + .syscalls + .iter() + .enumerate() + .map(|(index, (variant, _))| { + let index = index + 1; + quote! { #index => Ok(Self::#variant) } + }) + .collect::>(); - impl From for usize { - fn from(value: SyscallFunction) -> usize { - value as usize - } - } + quote! { + #[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] + #[repr(usize)] + pub enum SyscallFunction { + #syscall_discriminants + } + + impl From for usize { + fn from(value: SyscallFunction) -> usize { + value as usize + } + } + + impl TryFrom for SyscallFunction { + type Error = Error; + + fn try_from(value: usize) -> Result { + match value { + #syscall_try_from_branches, + _ => Err(Error::UndefinedSyscall) + } + } + } } } fn emit_type_definitions(&self) -> TokenStream { let mut stream = TokenStream::new(); - for (_, (ty, def)) in &self.abi.types { + for (_, (ty, def)) in &self.abi.types.locals { stream.extend(def.generate_type_definition(ty, &self.target_env)); } @@ -146,9 +204,22 @@ impl GenerateTypeDefinition for EnumType { } = self; let TypeRepr(repr) = &repr; + // TODO auto-assign numbers to enum variants + let try_from_branches = variants + .iter() + .map(|variant| { + let EnumTypeVariant { + discriminant, + value, + .. + } = variant; + quote! { #value => Ok(Self::#discriminant) } + }) + .collect::>(); + let direct = quote! { #attributes - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] #[repr(#repr)] pub enum #name { #variants @@ -171,6 +242,17 @@ impl GenerateTypeDefinition for EnumType { } } + impl TryFrom<#repr> for #name { + type Error = Error; + + fn try_from(value: #repr) -> Result<#name, Error> { + match value { + #try_from_branches, + _ => Err(Error::InvalidArgument) + } + } + } + impl abi_lib::SyscallRegister for #name { fn into_syscall_register(self) -> usize { self as #repr as usize @@ -206,7 +288,7 @@ fn generate_transparent_struct( let direct = quote! { #attributes - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct #name(pub(crate) #repr); impl #name { @@ -214,6 +296,10 @@ fn generate_transparent_struct( self.0 } + pub const fn bits(self) -> #repr { + self.0 + } + pub const unsafe fn from_raw(value: #repr) -> Self { Self(value) } @@ -307,11 +393,27 @@ impl GenerateTypeDefinition for BitfieldType { impl #name { #field_impls + pub const fn empty() -> Self { + Self(0) + } + pub const fn contains(&self, value: Self) -> bool { self.0 & value.0 == value.0 } } + impl core::ops::BitOrAssign for #name { + fn bitor_assign(&mut self, other: Self) { + self.0 |= other.0; + } + } + + impl core::ops::BitAndAssign for #name { + fn bitand_assign(&mut self, other: Self) { + self.0 &= other.0; + } + } + impl core::ops::BitAnd for #name { type Output = #name; diff --git a/src/abi/syscall.rs b/src/abi/syscall.rs index c5127357..2306426c 100644 --- a/src/abi/syscall.rs +++ b/src/abi/syscall.rs @@ -36,20 +36,24 @@ impl Syscall { // }) pub fn emit_handler_branch( &self, - named_args: &[&syn::Ident], + named_args: &[syn::Ident], raw_args: &syn::Ident, - body: &syn::ExprBlock, + body: TokenStream, enum_variant: &syn::Ident, env: &TargetEnv, ) -> TokenStream { if self.args.len() != named_args.len() { - todo!() + todo!( + "Argument length mismatch: got {} expectd {}", + named_args.len(), + self.args.len() + ) } let mut extracted_arguments = TokenStream::new(); let mut index = 0; - for ((_, arg), &name) in std::iter::zip(self.args.iter(), named_args) { + for ((_, arg), name) in std::iter::zip(self.args.iter(), named_args) { let (arg_expr, arg_width) = arg.emit_from_syscall_arguments(env, raw_args, index); let arg_assignment = quote! { let #name = #arg_expr; @@ -60,7 +64,7 @@ impl Syscall { } quote! { - #enum_variant => { + SyscallFunction::#enum_variant => { #extracted_arguments #body @@ -115,7 +119,6 @@ impl Syscall { #wrapped } }; - println!("{}", v); v } } diff --git a/src/abi/ty/complex.rs b/src/abi/ty/complex.rs index b0a2c29a..a019fa19 100644 --- a/src/abi/ty/complex.rs +++ b/src/abi/ty/complex.rs @@ -2,7 +2,7 @@ use std::{fmt, rc::Rc}; use proc_macro2::TokenStream; use quote::quote; -use syn::Ident; +use syn::{punctuated::Punctuated, Ident, Token}; use crate::TargetEnv; @@ -21,7 +21,12 @@ pub enum ComplexType { MaybeUninit(Rc), Result(Rc), Option(Rc), - Extern { kind: ExternKind, alias: syn::Type }, + Tuple(Vec>), + Extern { + kind: ExternKind, + alias: syn::Type, + arguments: syn::PathArguments, + }, } impl ComplexType { @@ -55,6 +60,7 @@ impl Type for ComplexType { // None -> null TypeKind::ThinPointer => env.thin_pointer_width, }, + Self::Tuple(_) => TypeWidth::Unknown, Self::Extern { .. } => TypeWidth::Unknown, // MaybeUninit has the same layout as its underlying data Self::MaybeUninit(ty) => ty.width(env), @@ -76,12 +82,23 @@ impl Type for ComplexType { let ty = ty.as_rust_type(); quote!(Option<#ty>) } + Self::Tuple(elems) => { + let elems = elems + .iter() + .map(|e| e.as_rust_type()) + .collect::>(); + quote!((#elems)) + } Self::Result(ty) => { let ty = ty.as_rust_type(); // TODO take from TargetEnv quote!(Result<#ty, Error>) } - Self::Extern { alias, .. } => quote!(#alias), + Self::Extern { + alias, arguments, .. + } => { + quote!(#alias #arguments) + } Self::MaybeUninit(ty) => { let ty = ty.as_rust_type(); quote!(core::mem::MaybeUninit<#ty>) @@ -110,17 +127,37 @@ impl Type for ComplexType { } => { quote!(#value.as_syscall_meta(), #value.as_syscall_data()) } - _ => todo!(), + _ => todo!("???"), } } fn emit_from_syscall_arguments( &self, - _env: &TargetEnv, - _args: &Ident, - _index: usize, + env: &TargetEnv, + args: &Ident, + index: usize, ) -> (TokenStream, usize) { - todo!() + match self { + Self::Simple(ty) => ty.emit_from_syscall_arguments(env, args, index), + Self::Option(ty) + if ty.width(env) < env.thin_pointer_width || ty.kind() == TypeKind::ThinPointer => + { + let ty = ty.as_rust_type(); + ( + quote!(>::from_syscall_register(#args[#index])), + 1, + ) + } + Self::Result(_) => todo!("Result"), + Self::Extern { + kind: ExternKind::Thin, + alias, + .. + } => (quote!(<#alias>::from_syscall_register(#args[#index])), 1), + Self::MaybeUninit(_) => todo!("MaybeUninit"), + Self::Tuple(_) => unimplemented!(), + _ => todo!("{:?}", self), + } } } @@ -130,6 +167,13 @@ impl fmt::Debug for ComplexType { Self::Simple(ty) => f.debug_tuple("Simple").field(&ty).finish(), Self::Result(ty) => f.debug_tuple("Result").field(&ty).finish(), Self::Option(ty) => f.debug_tuple("Option").field(&ty).finish(), + Self::Tuple(elems) => { + let mut r = f.debug_tuple("Tuple"); + for elem in elems { + r.field(elem); + } + r.finish() + } Self::Extern { alias, .. } => f.debug_tuple("Extern").field("e!(#alias)).finish(), Self::MaybeUninit(ty) => f.debug_tuple("MaybeUninit").field(&ty).finish(), } diff --git a/src/abi/ty/primitive.rs b/src/abi/ty/primitive.rs index 1288f06e..7a2805bc 100644 --- a/src/abi/ty/primitive.rs +++ b/src/abi/ty/primitive.rs @@ -64,10 +64,16 @@ impl Type for AbiPrimitive { fn emit_from_syscall_arguments( &self, _env: &TargetEnv, - _args: &Ident, - _index: usize, + args: &Ident, + index: usize, ) -> (TokenStream, usize) { - todo!() + match self { + Self::Bool => (quote!(#args[#index] != 0), 1), + _ => { + let ty = self.as_rust_type(); + (quote!(#args[#index] as #ty), 1) + } + } } } diff --git a/src/abi/ty/simple.rs b/src/abi/ty/simple.rs index 9eb8ddd5..bba2df7c 100644 --- a/src/abi/ty/simple.rs +++ b/src/abi/ty/simple.rs @@ -104,10 +104,55 @@ impl Type for SimpleType { fn emit_from_syscall_arguments( &self, - _env: &TargetEnv, - _args: &Ident, - _index: usize, + env: &TargetEnv, + args: &Ident, + index: usize, ) -> (TokenStream, usize) { - todo!() + match self { + Self::Str => (quote!(arg::str_ref(#args[#index], #args[#index + 1])?), 2), + Self::Never => todo!("!"), + Self::Slice { mutable, element } => { + let element = element.as_rust_type(); + match mutable { + true => ( + quote!(arg::slice_mut::<#element>(#args[#index], #args[#index + 1])?), + 2, + ), + false => ( + quote!(arg::slice_ref::<#element>(#args[#index], #args[#index + 1])?), + 2, + ), + } + } + Self::Reference { mutable, pointee } => { + let pointee = pointee.as_rust_type(); + match mutable { + true => (quote!(arg::ref_mut::<#pointee>(#args[#index])?), 1), + false => (quote!(arg::ref_const::<#pointee>(#args[#index])?), 1), + } + } + Self::Array { + mutable, + element, + length, + } => { + let element = element.as_rust_type(); + match mutable { + true => ( + quote!(arg::ref_mut::<[#element; #length]>(#args[#index])?), + 1, + ), + false => ( + quote!(arg::ref_const::<[#element; #length]>(#args[#index])?), + 1, + ), + } + } + Self::NonZeroUsize => todo!("NonZeroUsize"), + Self::Primitive(ty) => ty.emit_from_syscall_arguments(env, args, index), + Self::Transparent { name, .. } => { + (quote!(<#name>::from_syscall_register(#args[#index])), 1) + } + } } } diff --git a/src/syntax/enum_type.rs b/src/syntax/enum_type.rs index 328d165d..21657c5b 100644 --- a/src/syntax/enum_type.rs +++ b/src/syntax/enum_type.rs @@ -11,7 +11,7 @@ use super::{ pub struct EnumTypeVariant { pub attributes: Attributes, pub discriminant: syn::Ident, - pub value: Option, + pub value: syn::LitInt, } #[derive(Clone)] @@ -26,13 +26,15 @@ impl syn::parse::Parse for EnumTypeVariant { fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { let attributes = input.parse()?; let discriminant = input.parse()?; - let value = if input.peek(Token![=]) { - input.parse::()?; - let value = input.parse()?; - Some(value) - } else { - None - }; + input.parse::()?; + let value = input.parse()?; + // let value = if input.peek(Token![=]) { + // input.parse::()?; + // let value = input.parse()?; + // Some(value) + // } else { + // None + // }; Ok(Self { attributes, discriminant, @@ -71,10 +73,10 @@ impl ToTokens for EnumTypeVariant { discriminant, value, } = self; - let value = value.as_ref().map(|val| quote!(= #val)); + // let value = value.as_ref().map(|val| quote!(= #val)); tokens.extend(quote! { #attributes - #discriminant #value + #discriminant = #value }) } } diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index a158f9f3..f9df25e4 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -1,11 +1,11 @@ -use std::{collections::HashMap, rc::Rc, str::FromStr}; +use std::{rc::Rc, str::FromStr}; use syn::{punctuated::Punctuated, Expr, ExprLit, Lit, PathSegment}; use crate::abi::{ syscall_enum_variant_identifier, ty::{complex::ExternKind, AbiPrimitive, ComplexType, SimpleType}, - Abi, Syscall, + Abi, Syscall, TypeEnv, }; mod common; @@ -29,8 +29,6 @@ pub use error::{ParseError, SyntaxError, UnwrapFancy}; pub use extern_block::{ExternType, ExternTypeBlock}; pub use newtype::NewType; -type TypeMap = HashMap, DocumentTypeDefinition)>; - pub fn parse_abi_document(input: &str) -> Result { let document: Document = syn::parse_str(input)?; Ok(document) @@ -74,7 +72,7 @@ fn as_const_array_len(len: &Expr) -> Option { lit.base10_parse().ok() } -fn process_type(t: &syn::Type, known_types: &TypeMap) -> Result, ParseError> { +fn process_type(t: &syn::Type, known_types: &TypeEnv) -> Result, ParseError> { match t { syn::Type::Path(path) => { let segment = as_single_segment_path(path).expect("TODO: more complex paths"); @@ -96,7 +94,14 @@ fn process_type(t: &syn::Type, known_types: &TypeMap) -> Result, let ty = process_type(ty, known_types)?; Ok(Rc::new(ComplexType::MaybeUninit(ty))) } - name if let Some((ty, _)) = known_types.get(name) => Ok(ty.clone()), + name if let Some((ty, _)) = known_types.lookup_local(name) => Ok(ty.clone()), + name if let Some((ty, kind)) = known_types.lookup_extern(name) => { + Ok(Rc::new(ComplexType::Extern { + kind: *kind, + alias: ty.clone(), + arguments: segment.arguments.clone(), + })) + } _ => Err(ParseError::syntax1(SyntaxError::UndefinedIdentifier( segment.ident.clone(), ))), @@ -144,8 +149,17 @@ fn process_type(t: &syn::Type, known_types: &TypeMap) -> Result, } } } - syn::Type::Tuple(t) if t.elems.is_empty() => { - Ok(Rc::new(ComplexType::primitive(AbiPrimitive::Unit))) + syn::Type::Tuple(t) => { + if t.elems.is_empty() { + Ok(Rc::new(ComplexType::primitive(AbiPrimitive::Unit))) + } else { + let elems = t + .elems + .iter() + .map(|e| process_type(e, known_types)) + .collect::>()?; + Ok(Rc::new(ComplexType::Tuple(elems))) + } } _ => todo!(), } @@ -169,10 +183,20 @@ fn make_single_ident_type(ident: syn::Ident) -> syn::Type { }) } +pub enum ProcessedType { + Extern(syn::Type, ExternKind), + Local(Rc, DocumentTypeDefinition), +} + fn process_extern_type( def: &ExternType, - _known_types: &TypeMap, -) -> Result<(syn::Ident, Rc), ParseError> { + _known_types: &TypeEnv, +) -> Result<(syn::Ident, syn::Type, ExternKind), ParseError> { + // TODO allow mixing arguments from + // extern { type X = ::A }; + // and use sites like + // syscall my_call(arg: X); + // Process extern attributes let mut kind = ExternKind::None; for attr in def.attributes.iter() { @@ -187,15 +211,12 @@ fn process_extern_type( .alias .clone() .unwrap_or_else(|| make_single_ident_type(def.name.clone())); - Ok(( - def.name.clone(), - Rc::new(ComplexType::Extern { alias, kind }), - )) + Ok((def.name.clone(), alias, kind)) } fn process_bitfield_type( def: &BitfieldType, - _known_types: &TypeMap, + _known_types: &TypeEnv, ) -> Result<(syn::Ident, Rc), ParseError> { let repr = require_primitive_repr(&def.repr)?; Ok(( @@ -209,7 +230,7 @@ fn process_bitfield_type( fn process_enum_type( def: &EnumType, - _known_types: &TypeMap, + _known_types: &TypeEnv, ) -> Result<(syn::Ident, Rc), ParseError> { let repr = require_primitive_repr(&def.repr)?; Ok(( @@ -223,7 +244,7 @@ fn process_enum_type( fn process_newtype( def: &NewType, - _known_types: &TypeMap, + _known_types: &TypeEnv, ) -> Result<(syn::Ident, Rc), ParseError> { let repr = require_primitive_repr(&def.repr)?; Ok(( @@ -237,20 +258,24 @@ fn process_newtype( fn process_type_definition( def: &DocumentTypeDefinition, - known_types: &TypeMap, -) -> Result<(syn::Ident, Rc), ParseError> { + known_types: &TypeEnv, +) -> Result<(syn::Ident, ProcessedType), ParseError> { match def { - DocumentTypeDefinition::ExternType(inner) => process_extern_type(inner, known_types), - DocumentTypeDefinition::Bitfield(inner) => process_bitfield_type(inner, known_types), - DocumentTypeDefinition::Enum(inner) => process_enum_type(inner, known_types), - DocumentTypeDefinition::NewType(inner) => process_newtype(inner, known_types), + DocumentTypeDefinition::ExternType(inner) => process_extern_type(inner, known_types) + .map(|(ident, def_ty, kind)| (ident, ProcessedType::Extern(def_ty, kind))), + DocumentTypeDefinition::Enum(inner) => process_enum_type(inner, known_types) + .map(|(ident, def_ty)| (ident, ProcessedType::Local(def_ty, def.clone()))), + DocumentTypeDefinition::Bitfield(inner) => process_bitfield_type(inner, known_types) + .map(|(ident, def_ty)| (ident, ProcessedType::Local(def_ty, def.clone()))), + DocumentTypeDefinition::NewType(inner) => process_newtype(inner, known_types) + .map(|(ident, def_ty)| (ident, ProcessedType::Local(def_ty, def.clone()))), } } pub fn parse_abi(input: &str) -> Result { let doc = parse_abi_document(input)?; let mut abi = Abi { - types: HashMap::new(), + types: TypeEnv::new(), syscalls: Vec::new(), }; @@ -258,7 +283,14 @@ pub fn parse_abi(input: &str) -> Result { (), doc.types.iter().map(|nt| { let (name, value) = process_type_definition(nt, &abi.types)?; - abi.types.insert(name.to_string(), (value, nt.clone())); + match value { + ProcessedType::Local(abi_ty, def_ty) => { + abi.types.insert_local(name.to_string(), abi_ty, def_ty) + } + ProcessedType::Extern(def_ty, kind) => { + abi.types.insert_extern(name.to_string(), def_ty, kind) + } + } Ok(()) }), )?; From aab7ac67c3a746168b9346f46f87608d7a3abd74 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Tue, 12 Mar 2024 12:45:31 +0200 Subject: [PATCH 8/9] Rename to abi-generator --- Cargo.lock | 42 +++++++++++------------ Cargo.toml | 4 +-- {handler => abi-kernel-macros}/Cargo.toml | 4 +-- {handler => abi-kernel-macros}/src/lib.rs | 10 +++--- example-abi/Cargo.toml | 2 +- example-abi/build.rs | 2 +- 6 files changed, 32 insertions(+), 32 deletions(-) rename {handler => abi-kernel-macros}/Cargo.toml (75%) rename {handler => abi-kernel-macros}/src/lib.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index bfc6ff25..4e29cec6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,26 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "abi-generator" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "thiserror", +] + +[[package]] +name = "abi-kernel-macros" +version = "0.1.0" +dependencies = [ + "abi-generator", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "abi-lib" version = "0.1.0" @@ -23,19 +43,9 @@ dependencies = [ name = "example-abi" version = "0.1.0" dependencies = [ + "abi-generator", "abi-lib", "prettyplease", - "syscall-generator", -] - -[[package]] -name = "handler" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "syscall-generator", ] [[package]] @@ -83,16 +93,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "syscall-generator" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "thiserror", -] - [[package]] name = "thiserror" version = "1.0.57" diff --git a/Cargo.toml b/Cargo.toml index e1a577e9..3cd882ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "syscall-generator" +name = "abi-generator" version = "0.1.0" edition = "2021" @@ -10,4 +10,4 @@ syn = { version = "2.0.32", features = ["full"] } thiserror = "1.0.47" [workspace] -members = [ "abi-lib","example-abi", "handler"] +members = ["abi-lib", "example-abi", "abi-kernel-macros"] diff --git a/handler/Cargo.toml b/abi-kernel-macros/Cargo.toml similarity index 75% rename from handler/Cargo.toml rename to abi-kernel-macros/Cargo.toml index 73e40651..4538d8e9 100644 --- a/handler/Cargo.toml +++ b/abi-kernel-macros/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "handler" +name = "abi-kernel-macros" version = "0.1.0" edition = "2021" @@ -8,7 +8,7 @@ proc-macro2 = "1.0.78" quote = "1.0.35" syn = { version = "2.0.52", features = ["full"] } -syscall-generator = { path = ".." } +abi-generator = { path = ".." } [lib] proc-macro = true diff --git a/handler/src/lib.rs b/abi-kernel-macros/src/lib.rs similarity index 99% rename from handler/src/lib.rs rename to abi-kernel-macros/src/lib.rs index 99acebe5..fb9b30c8 100644 --- a/handler/src/lib.rs +++ b/abi-kernel-macros/src/lib.rs @@ -1,12 +1,12 @@ -use proc_macro::TokenStream as TokenStream1; -use proc_macro2::Span; -use quote::quote; -use syn::{parse_macro_input, punctuated::Punctuated, Token}; -use syscall_generator::{ +use abi_generator::{ abi::{ty::TypeWidth, Abi}, syntax::UnwrapFancy, TargetEnv, }; +use proc_macro::TokenStream as TokenStream1; +use proc_macro2::Span; +use quote::quote; +use syn::{parse_macro_input, punctuated::Punctuated, Token}; enum DispatcherAbiPath { Env(syn::LitStr), diff --git a/example-abi/Cargo.toml b/example-abi/Cargo.toml index f6100b26..fd163c29 100644 --- a/example-abi/Cargo.toml +++ b/example-abi/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" abi-lib = { path = "../abi-lib" } [build-dependencies] -syscall-generator = { path = ".." } +abi-generator = { path = ".." } prettyplease = "0.2.15" diff --git a/example-abi/build.rs b/example-abi/build.rs index 435993dd..1f11464d 100644 --- a/example-abi/build.rs +++ b/example-abi/build.rs @@ -1,6 +1,6 @@ use std::{env, path::Path}; -use syscall_generator::{ +use abi_generator::{ abi::{ty::TypeWidth, AbiBuilder}, syntax::UnwrapFancy, TargetEnv, From 635bf51bb1cde626d477df477604d58d107bc037 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Tue, 12 Mar 2024 13:17:17 +0200 Subject: [PATCH 9/9] Ability to read ABI from string --- Cargo.lock | 10 ---- Cargo.toml | 2 +- abi-kernel-macros/Cargo.toml | 14 ----- abi-kernel-macros/src/lib.rs | 106 ----------------------------------- src/abi/mod.rs | 73 +++++++++++++++++++++--- 5 files changed, 66 insertions(+), 139 deletions(-) delete mode 100644 abi-kernel-macros/Cargo.toml delete mode 100644 abi-kernel-macros/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 4e29cec6..b0682bde 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,16 +12,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "abi-kernel-macros" -version = "0.1.0" -dependencies = [ - "abi-generator", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "abi-lib" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 3cd882ba..d4e50df6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,4 @@ syn = { version = "2.0.32", features = ["full"] } thiserror = "1.0.47" [workspace] -members = ["abi-lib", "example-abi", "abi-kernel-macros"] +members = ["abi-lib", "example-abi"] diff --git a/abi-kernel-macros/Cargo.toml b/abi-kernel-macros/Cargo.toml deleted file mode 100644 index 4538d8e9..00000000 --- a/abi-kernel-macros/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "abi-kernel-macros" -version = "0.1.0" -edition = "2021" - -[dependencies] -proc-macro2 = "1.0.78" -quote = "1.0.35" -syn = { version = "2.0.52", features = ["full"] } - -abi-generator = { path = ".." } - -[lib] -proc-macro = true diff --git a/abi-kernel-macros/src/lib.rs b/abi-kernel-macros/src/lib.rs deleted file mode 100644 index fb9b30c8..00000000 --- a/abi-kernel-macros/src/lib.rs +++ /dev/null @@ -1,106 +0,0 @@ -use abi_generator::{ - abi::{ty::TypeWidth, Abi}, - syntax::UnwrapFancy, - TargetEnv, -}; -use proc_macro::TokenStream as TokenStream1; -use proc_macro2::Span; -use quote::quote; -use syn::{parse_macro_input, punctuated::Punctuated, Token}; - -enum DispatcherAbiPath { - Env(syn::LitStr), -} - -struct DispatcherInput { - abi_document_path: DispatcherAbiPath, - dispatcher_name: syn::Ident, - impl_module: syn::Path, -} - -impl syn::parse::Parse for DispatcherAbiPath { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let key = input.parse::()?; - input.parse::()?; - let value = input.parse::()?; - - match key.to_string().as_str() { - "env" => Ok(Self::Env(value)), - _ => todo!(), - } - } -} - -impl syn::parse::Parse for DispatcherInput { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let abi_document_path = input.parse()?; - input.parse::()?; - let dispatcher_name = input.parse()?; - input.parse::()?; - let impl_module = input.parse()?; - Ok(Self { - abi_document_path, - dispatcher_name, - impl_module, - }) - } -} - -impl DispatcherAbiPath { - pub fn value(&self) -> String { - match self { - Self::Env(var) => std::env::var(var.value()).unwrap(), - } - } -} - -#[proc_macro] -pub fn syscall_dispatcher(input: TokenStream1) -> TokenStream1 { - let input = parse_macro_input!(input as DispatcherInput); - let abi = Abi::read(input.abi_document_path.value()).unwrap_fancy("TODO: proper error here"); - - let env = TargetEnv { - thin_pointer_width: TypeWidth::U64, - fat_pointer_width: TypeWidth::U128, - }; - - let handler_module = input.impl_module; - - let raw_fn = syn::Ident::new("__fn", Span::call_site()); - let raw_args = syn::Ident::new("__args", Span::call_site()); - - let mut branches = Punctuated::<_, Token![,]>::new(); - - for (enum_variant, syscall) in abi.syscalls.iter() { - let named_args = (0..syscall.args.len()) - .map(|i| syn::Ident::new(&format!("__a{}", i), Span::call_site())) - .collect::>(); - let call_args = named_args - .iter() - .cloned() - .collect::>(); - let syscall_name = &syscall.name; - - let handler_fn = quote! { #handler_module::#syscall_name }; - - let body = quote! { - Ok((#handler_fn(#call_args)).into_syscall_register()) - }; - - let match_arm = - syscall.emit_handler_branch(&named_args, &raw_args, body, enum_variant, &env); - - branches.push(match_arm); - } - - let dispatcher_name = input.dispatcher_name; - let dispatcher_fn = quote! { - pub(crate) fn #dispatcher_name(#raw_fn: SyscallFunction, #raw_args: &[usize]) -> Result { - match #raw_fn { - #branches - } - } - }; - - dispatcher_fn.into() -} diff --git a/src/abi/mod.rs b/src/abi/mod.rs index 5a6c7a42..480cb558 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -1,12 +1,8 @@ use std::{collections::HashMap, path::Path, rc::Rc}; -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::{quote, TokenStreamExt}; -use syn::{ - punctuated::Punctuated, - spanned::Spanned, - token::{self}, -}; +use syn::{punctuated::Punctuated, spanned::Spanned, token, Token}; pub mod ty; @@ -92,10 +88,67 @@ pub fn syscall_enum_variant_identifier(name: &syn::Ident) -> syn::Ident { impl AbiBuilder { pub fn from_file>(path: P, target_env: TargetEnv) -> Result { - let abi = Abi::read(path)?; + let abi = Abi::read_file(path)?; Ok(Self { abi, target_env }) } + pub fn from_string>(data: S, target_env: TargetEnv) -> Result { + let abi = Abi::read_string(data)?; + Ok(Self { abi, target_env }) + } + + pub fn emit_syscall_dispatcher( + &self, + dispatcher_fn: &str, + impl_mod: &str, + ) -> Result { + let dispatcher_fn = syn::Ident::new(dispatcher_fn, Span::call_site()); + let impl_mod = syn::Ident::new(impl_mod, Span::call_site()); + + let raw_fn = syn::Ident::new("__fn", Span::call_site()); + let raw_args = syn::Ident::new("__args", Span::call_site()); + + let mut branches = Punctuated::<_, Token![,]>::new(); + + for (enum_variant, syscall) in self.abi.syscalls.iter() { + let named_args = (0..syscall.args.len()) + .map(|i| syn::Ident::new(&format!("__a{}", i), Span::call_site())) + .collect::>(); + let call_args = named_args + .iter() + .cloned() + .collect::>(); + let syscall_name = &syscall.name; + + let handler_fn = quote! { #impl_mod::#syscall_name }; + + let body = quote! { + Ok((#handler_fn(#call_args)).into_syscall_register()) + }; + + let match_arm = syscall.emit_handler_branch( + &named_args, + &raw_args, + body, + enum_variant, + &self.target_env, + ); + + branches.push(match_arm); + } + + let dispatcher_fn = quote! { + pub(crate) fn #dispatcher_fn(#raw_fn: SyscallFunction, #raw_args: &[usize]) -> Result { + match #raw_fn { + #branches + } + } + }; + + let file = syn::parse_file(&dispatcher_fn.to_string())?; + Ok(file) + } + pub fn emit_file(&self, emit_types: bool, emit_calls: bool) -> Result { let mut stream = TokenStream::new(); @@ -188,10 +241,14 @@ impl AbiBuilder { } impl Abi { - pub fn read>(path: P) -> Result { + pub fn read_file>(path: P) -> Result { let data = std::fs::read_to_string(path)?; parse_abi(&data) } + + pub fn read_string>(data: S) -> Result { + parse_abi(data.as_ref()) + } } impl GenerateTypeDefinition for EnumType {