Files
wasmtime/cranelift/peepmatic/crates/macro/src/child_nodes.rs
Nick Fitzgerald ee5982fd16 peepmatic: Be generic over the operator type
This lets us avoid the cost of `cranelift_codegen::ir::Opcode` to
`peepmatic_runtime::Operator` conversion overhead, and paves the way for
allowing Peepmatic to support non-clif optimizations (e.g. vcode optimizations).

Rather than defining our own `peepmatic::Operator` type like we used to, now the
whole `peepmatic` crate is effectively generic over a `TOperator` type
parameter. For the Cranelift integration, we use `cranelift_codegen::ir::Opcode`
as the concrete type for our `TOperator` type parameter. For testing, we also
define a `TestOperator` type, so that we can test Peepmatic code without
building all of Cranelift, and we can keep them somewhat isolated from each
other.

The methods that `peepmatic::Operator` had are now translated into trait bounds
on the `TOperator` type. These traits need to be shared between all of
`peepmatic`, `peepmatic-runtime`, and `cranelift-codegen`'s Peepmatic
integration. Therefore, these new traits live in a new crate:
`peepmatic-traits`. This crate acts as a header file of sorts for shared
trait/type/macro definitions.

Additionally, the `peepmatic-runtime` crate no longer depends on the
`peepmatic-macro` procedural macro crate, which should lead to faster build
times for Cranelift when it is using pre-built peephole optimizers.
2020-07-17 16:16:49 -07:00

116 lines
4.1 KiB
Rust

use quote::quote;
use syn::DeriveInput;
use syn::{parse_quote, GenericParam, Generics, Result};
pub fn derive_child_nodes(input: &DeriveInput) -> Result<impl quote::ToTokens> {
let children = get_child_nodes(&input.data)?;
let name = &input.ident;
let generics = add_trait_bounds(input.generics.clone());
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
Ok(quote! {
impl #impl_generics ChildNodes<'a, 'a, TOperator> for #name #ty_generics #where_clause {
fn child_nodes(&'a self, children: &mut impl Extend<DynAstRef<'a, TOperator>>) {
#children
}
}
})
}
fn get_child_nodes(data: &syn::Data) -> Result<impl quote::ToTokens> {
match data {
syn::Data::Struct(s) => {
let mut fields = vec![];
match &s.fields {
syn::Fields::Named(n) => {
for f in n.named.iter() {
let opts = crate::PeepmaticOpts::from_attrs(&mut f.attrs.clone())?;
if opts.skip_child {
continue;
}
let field_name = f.ident.as_ref().unwrap();
if opts.flatten {
fields.push(quote! {
self.#field_name.iter().map(DynAstRef::from)
});
} else {
fields.push(quote! {
std::iter::once(DynAstRef::from(&self.#field_name))
});
}
}
}
syn::Fields::Unnamed(u) => {
for (i, f) in u.unnamed.iter().enumerate() {
let opts = crate::PeepmaticOpts::from_attrs(&mut f.attrs.clone())?;
if opts.skip_child {
continue;
}
if opts.flatten {
return Err(syn::Error::new(
u.paren_token.span,
"#[peepmatic(flatten)] is only allowed with named fields",
));
}
fields.push(quote! {
std::iter::once(DynAstRef::from(&self.#i))
});
}
}
syn::Fields::Unit => {}
}
Ok(match fields.as_slice() {
[] => quote! { let _ = children; },
[f, rest @ ..] => {
let rest = rest.iter().map(|f| {
quote! {
.chain(#f)
}
});
quote! {
children.extend( #f #( #rest )* );
}
}
})
}
syn::Data::Enum(e) => {
let mut match_arms = vec![];
for v in e.variants.iter() {
match v.fields {
syn::Fields::Unnamed(ref u) if u.unnamed.len() == 1 => {
let variant = &v.ident;
match_arms.push(quote! {
Self::#variant(x) => children.extend(Some(x.into())),
});
}
_ => panic!("#[derive(ChildNodes)] only supports enums whose variants all ahve a single unnamed field")
}
}
Ok(quote! {
match self {
#( #match_arms )*
}
})
}
syn::Data::Union(_) => panic!("#[derive(ChildNodes)] is not supported on unions"),
}
}
fn add_trait_bounds(mut generics: Generics) -> Generics {
for param in &mut generics.params {
if let GenericParam::Type(type_param) = param {
if type_param.ident == "TOperator" {
continue;
}
type_param
.bounds
.push(parse_quote!(ChildNodes<'a, 'a, TOperator>));
}
}
generics
}