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.
This commit is contained in:
@@ -9,8 +9,8 @@ pub fn derive_child_nodes(input: &DeriveInput) -> Result<impl quote::ToTokens> {
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
Ok(quote! {
|
||||
impl #impl_generics ChildNodes<'a, 'a> for #name #ty_generics #where_clause {
|
||||
fn child_nodes(&'a self, children: &mut impl Extend<DynAstRef<'a>>) {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,12 @@ fn get_child_nodes(data: &syn::Data) -> Result<impl quote::ToTokens> {
|
||||
fn add_trait_bounds(mut generics: Generics) -> Generics {
|
||||
for param in &mut generics.params {
|
||||
if let GenericParam::Type(type_param) = param {
|
||||
type_param.bounds.push(parse_quote!(ChildNodes<'a, 'a>));
|
||||
if type_param.ident == "TOperator" {
|
||||
continue;
|
||||
}
|
||||
type_param
|
||||
.bounds
|
||||
.push(parse_quote!(ChildNodes<'a, 'a, TOperator>));
|
||||
}
|
||||
}
|
||||
generics
|
||||
|
||||
@@ -13,7 +13,7 @@ pub fn derive_into_dyn_ast_ref(input: &DeriveInput) -> Result<impl quote::ToToke
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
|
||||
Ok(quote! {
|
||||
impl #impl_generics From<&'a #ty #ty_generics> for DynAstRef<'a> #where_clause {
|
||||
impl #impl_generics From<&'a #ty #ty_generics> for DynAstRef<'a, TOperator> #where_clause {
|
||||
#[inline]
|
||||
fn from(x: &'a #ty #ty_generics) -> Self {
|
||||
Self::#ty(x)
|
||||
|
||||
@@ -11,14 +11,8 @@ use syn::{parse_macro_input, Ident, Result};
|
||||
|
||||
mod child_nodes;
|
||||
mod into_dyn_ast_ref;
|
||||
mod operator;
|
||||
mod span;
|
||||
|
||||
#[proc_macro_derive(PeepmaticOperator, attributes(peepmatic))]
|
||||
pub fn operator(input: TokenStream) -> TokenStream {
|
||||
operator::derive_operator(input)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Ast, attributes(peepmatic))]
|
||||
pub fn derive_ast(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
@@ -1,325 +0,0 @@
|
||||
//! Implementation of the `#[peepmatic]` macro for the `Operator` AST node.
|
||||
|
||||
use crate::proc_macro::TokenStream;
|
||||
use crate::PeepmaticOpts;
|
||||
use proc_macro2::{Ident, Span};
|
||||
use quote::quote;
|
||||
use syn::DeriveInput;
|
||||
use syn::Error;
|
||||
use syn::{parse_macro_input, Result};
|
||||
|
||||
pub fn derive_operator(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let variants = match get_enum_variants(&input) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return e.to_compile_error().into(),
|
||||
};
|
||||
|
||||
let arity = match create_arity(&variants) {
|
||||
Ok(a) => a,
|
||||
Err(e) => return e.to_compile_error().into(),
|
||||
};
|
||||
|
||||
let num_operators = variants.len();
|
||||
let type_methods = create_type_methods(&variants);
|
||||
let parse_impl = create_parse_impl(&input.ident, &variants);
|
||||
let display_impl = create_display_impl(&input.ident, &variants);
|
||||
let try_from_u32_impl = create_try_from_u32_impl(&input.ident, &variants);
|
||||
let ident = &input.ident;
|
||||
|
||||
let expanded = quote! {
|
||||
impl #ident {
|
||||
#arity
|
||||
#type_methods
|
||||
|
||||
/// Get the total number of different operators.
|
||||
pub const fn num_operators() -> usize {
|
||||
#num_operators
|
||||
}
|
||||
}
|
||||
|
||||
#display_impl
|
||||
#try_from_u32_impl
|
||||
#parse_impl
|
||||
};
|
||||
|
||||
// eprintln!("{}", expanded);
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
fn get_enum_variants(input: &DeriveInput) -> Result<Vec<OperatorVariant>> {
|
||||
let en = match &input.data {
|
||||
syn::Data::Enum(en) => en,
|
||||
syn::Data::Struct(_) => {
|
||||
panic!("can only put #[peepmatic] on an enum; found it on a struct")
|
||||
}
|
||||
syn::Data::Union(_) => panic!("can only put #[peepmatic] on an enum; found it on a union"),
|
||||
};
|
||||
en.variants
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|mut variant| {
|
||||
Ok(OperatorVariant {
|
||||
opts: PeepmaticOpts::from_attrs(&mut variant.attrs)?,
|
||||
syn: variant,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
struct OperatorVariant {
|
||||
syn: syn::Variant,
|
||||
opts: PeepmaticOpts,
|
||||
}
|
||||
|
||||
fn create_arity(variants: &[OperatorVariant]) -> Result<impl quote::ToTokens> {
|
||||
let mut imm_arities = vec![];
|
||||
let mut params_arities = vec![];
|
||||
|
||||
for v in variants {
|
||||
let variant = &v.syn.ident;
|
||||
|
||||
let imm_arity = v.opts.immediates.len();
|
||||
if imm_arity > std::u8::MAX as usize {
|
||||
return Err(Error::new(
|
||||
v.opts.immediates_paren.span,
|
||||
"cannot have more than u8::MAX immediates",
|
||||
));
|
||||
}
|
||||
let imm_arity = imm_arity as u8;
|
||||
|
||||
imm_arities.push(quote! {
|
||||
Self::#variant => #imm_arity,
|
||||
});
|
||||
|
||||
let params_arity = v.opts.params.len();
|
||||
if params_arity > std::u8::MAX as usize {
|
||||
return Err(Error::new(
|
||||
v.opts.params_paren.span,
|
||||
"cannot have more than u8::MAX params",
|
||||
));
|
||||
}
|
||||
let params_arity = params_arity as u8;
|
||||
|
||||
params_arities.push(quote! {
|
||||
Self::#variant => #params_arity,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(quote! {
|
||||
/// Get the number of immediates that this operator has.
|
||||
pub fn immediates_arity(&self) -> u8 {
|
||||
match *self {
|
||||
#( #imm_arities )*
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the number of parameters that this operator takes.
|
||||
pub fn params_arity(&self) -> u8 {
|
||||
match *self {
|
||||
#( #params_arities )*
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn create_type_methods(variants: &[OperatorVariant]) -> impl quote::ToTokens {
|
||||
let mut result_types = vec![];
|
||||
let mut imm_types = vec![];
|
||||
let mut param_types = vec![];
|
||||
|
||||
for v in variants {
|
||||
let variant = &v.syn.ident;
|
||||
|
||||
let result_ty = v.opts.result.as_ref().unwrap_or_else(|| {
|
||||
panic!(
|
||||
"must define #[peepmatic(result(..))] on operator `{}`",
|
||||
variant
|
||||
)
|
||||
});
|
||||
result_types.push(quote! {
|
||||
Self::#variant => {
|
||||
context.#result_ty(span)
|
||||
}
|
||||
});
|
||||
|
||||
let imm_tys = match &v.opts.immediates[..] {
|
||||
[] => quote! {},
|
||||
[ty, rest @ ..] => {
|
||||
let rest = rest.iter().map(|ty| {
|
||||
quote! { .chain(::std::iter::once(context.#ty(span))) }
|
||||
});
|
||||
quote! {
|
||||
types.extend(::std::iter::once(context.#ty(span))#( #rest )*);
|
||||
}
|
||||
}
|
||||
};
|
||||
imm_types.push(quote! {
|
||||
Self::#variant => {
|
||||
#imm_tys
|
||||
}
|
||||
});
|
||||
|
||||
let param_tys = match &v.opts.params[..] {
|
||||
[] => quote! {},
|
||||
[ty, rest @ ..] => {
|
||||
let rest = rest.iter().map(|ty| {
|
||||
quote! { .chain(::std::iter::once(context.#ty(span))) }
|
||||
});
|
||||
quote! {
|
||||
types.extend(::std::iter::once(context.#ty(span))#( #rest )*);
|
||||
}
|
||||
}
|
||||
};
|
||||
param_types.push(quote! {
|
||||
Self::#variant => {
|
||||
#param_tys
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
quote! {
|
||||
/// Get the result type of this operator.
|
||||
#[cfg(feature = "construct")]
|
||||
pub fn result_type<'a, C>(
|
||||
&self,
|
||||
context: &mut C,
|
||||
span: wast::Span,
|
||||
) -> C::TypeVariable
|
||||
where
|
||||
C: 'a + TypingContext<'a>,
|
||||
{
|
||||
match *self {
|
||||
#( #result_types )*
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the immediate types of this operator.
|
||||
#[cfg(feature = "construct")]
|
||||
pub fn immediate_types<'a, C>(
|
||||
&self,
|
||||
context: &mut C,
|
||||
span: wast::Span,
|
||||
types: &mut impl Extend<C::TypeVariable>,
|
||||
)
|
||||
where
|
||||
C: 'a + TypingContext<'a>,
|
||||
{
|
||||
match *self {
|
||||
#( #imm_types )*
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the parameter types of this operator.
|
||||
#[cfg(feature = "construct")]
|
||||
pub fn param_types<'a, C>(
|
||||
&self,
|
||||
context: &mut C,
|
||||
span: wast::Span,
|
||||
types: &mut impl Extend<C::TypeVariable>,
|
||||
)
|
||||
where
|
||||
C: 'a + TypingContext<'a>,
|
||||
{
|
||||
match *self {
|
||||
#( #param_types )*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn snake_case(s: &str) -> String {
|
||||
let mut t = String::with_capacity(s.len() + 1);
|
||||
for (i, ch) in s.chars().enumerate() {
|
||||
if i != 0 && ch.is_uppercase() {
|
||||
t.push('_');
|
||||
}
|
||||
t.extend(ch.to_lowercase());
|
||||
}
|
||||
t
|
||||
}
|
||||
|
||||
fn create_parse_impl(ident: &syn::Ident, variants: &[OperatorVariant]) -> impl quote::ToTokens {
|
||||
let token_defs = variants.iter().map(|v| {
|
||||
let tok = snake_case(&v.syn.ident.to_string());
|
||||
let tok = Ident::new(&tok, Span::call_site());
|
||||
quote! {
|
||||
wast::custom_keyword!(#tok);
|
||||
}
|
||||
});
|
||||
|
||||
let parses = variants.iter().map(|v| {
|
||||
let tok = snake_case(&v.syn.ident.to_string());
|
||||
let tok = Ident::new(&tok, Span::call_site());
|
||||
let ident = &v.syn.ident;
|
||||
quote! {
|
||||
if p.peek::<#tok>() {
|
||||
p.parse::<#tok>()?;
|
||||
return Ok(Self::#ident);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let expected = format!("expected {}", ident);
|
||||
|
||||
quote! {
|
||||
#[cfg(feature = "construct")]
|
||||
impl<'a> wast::parser::Parse<'a> for #ident {
|
||||
fn parse(p: wast::parser::Parser<'a>) -> wast::parser::Result<Self> {
|
||||
#( #token_defs )*
|
||||
|
||||
#( #parses )*
|
||||
|
||||
Err(p.error(#expected))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_display_impl(ident: &syn::Ident, variants: &[OperatorVariant]) -> impl quote::ToTokens {
|
||||
let displays = variants.iter().map(|v| {
|
||||
let variant = &v.syn.ident;
|
||||
let snake = snake_case(&v.syn.ident.to_string());
|
||||
quote! {
|
||||
Self::#variant => write!(f, #snake),
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
impl std::fmt::Display for #ident {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
#( #displays )*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_try_from_u32_impl(
|
||||
ident: &syn::Ident,
|
||||
variants: &[OperatorVariant],
|
||||
) -> impl quote::ToTokens {
|
||||
let matches = variants.iter().map(|v| {
|
||||
let variant = &v.syn.ident;
|
||||
quote! {
|
||||
x if Self::#variant as u32 == x => Ok(Self::#variant),
|
||||
}
|
||||
});
|
||||
|
||||
let error_msg = format!("value is not an `{}`", ident);
|
||||
|
||||
quote! {
|
||||
impl std::convert::TryFrom<u32> for #ident {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
#( #matches )*
|
||||
_ => Err(#error_msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,9 @@ pub fn derive_span(input: &DeriveInput) -> Result<impl quote::ToTokens> {
|
||||
fn add_span_trait_bounds(mut generics: Generics) -> Generics {
|
||||
for param in &mut generics.params {
|
||||
if let GenericParam::Type(ref mut type_param) = *param {
|
||||
if type_param.ident == "TOperator" {
|
||||
continue;
|
||||
}
|
||||
type_param.bounds.push(parse_quote!(Span));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user