Files
wasmtime/cranelift/peepmatic/crates/traits/src/operator.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

318 lines
9.7 KiB
Rust

/// Define a `wast::parser::Parse` implementation for an operator type.
#[macro_export]
macro_rules! define_parse_impl_for_operator {
(
$operator:ident {
$(
$keyword:ident => $variant:ident;
)*
}
) => {
impl<'a> wast::parser::Parse<'a> for $operator {
fn parse(p: wast::parser::Parser<'a>) -> wast::parser::Result<$operator> {
/// Token definitions for our `Opcode` keywords.
mod tok {
$(
wast::custom_keyword!($keyword);
)*
}
// Peek at the next token, and if it is the variant's
// keyword, then consume it with `parse`, and finally return
// the `Opcode` variant.
$(
if p.peek::<tok::$keyword>() {
p.parse::<tok::$keyword>()?;
return Ok(Self::$variant);
}
)*
// If none of the keywords matched, then we get a parse error.
Err(p.error(concat!("expected `", stringify!($operator), "`")))
}
}
}
}
/// Define a `peepmatic_traits::TypingRules` implementation for the given
/// operator type.
#[macro_export]
macro_rules! define_typing_rules_impl_for_operator {
(
$operator:ident {
$(
$variant:ident {
$( immediates( $($immediate:ident),* ); )?
$( parameters( $($parameter:ident),* ); )?
result( $result:ident );
$( is_reduce($is_reduce:expr); )?
$( is_extend($is_extend:expr); )?
}
)*
}
) => {
impl $crate::TypingRules for $operator {
fn result_type<'a, C>(
&self,
span: C::Span,
typing_context: &mut C,
) -> C::TypeVariable
where
C: $crate::TypingContext<'a> {
match self {
$(
Self::$variant => typing_context.$result(span),
)*
#[allow(dead_code)]
_ => $crate::unsupported("no typing rules defined for variant"),
}
}
fn immediates_arity(&self) -> u8 {
match self {
$(
Self::$variant => $crate::define_typing_rules_impl_for_operator!(
@arity;
$( $( $immediate, )* )?
),
)*
#[allow(dead_code)]
_ => $crate::unsupported("no typing rules defined for variant"),
}
}
fn immediate_types<'a, C>(
&self,
span: C::Span,
typing_context: &mut C,
types: &mut impl Extend<C::TypeVariable>,
)
where
C: $crate::TypingContext<'a>
{
match self {
$(
Self::$variant => types.extend(
None.into_iter()
$(
$(
.chain(Some(typing_context.$immediate(span)))
)*
)?
),
)*
#[allow(dead_code)]
_ => $crate::unsupported("no typing rules defined for variant"),
}
}
fn parameters_arity(&self) -> u8 {
match self {
$(
Self::$variant => $crate::define_typing_rules_impl_for_operator!(
@arity;
$( $( $parameter, )* )?
),
)*
#[allow(dead_code)]
_ => $crate::unsupported("no typing rules defined for variant"),
}
}
fn parameter_types<'a, C>(
&self,
span: C::Span,
typing_context: &mut C,
types: &mut impl Extend<C::TypeVariable>,
)
where
C: $crate::TypingContext<'a>
{
match self {
$(
Self::$variant => types.extend(
None.into_iter()
$(
$(
.chain(Some(typing_context.$parameter(span)))
)*
)?
),
)*
#[allow(dead_code)]
_ => $crate::unsupported("no typing rules defined for variant"),
}
}
fn is_reduce(&self) -> bool {
match self {
$(
Self::$variant if false $( || $is_reduce )? => false $( || $is_reduce )?,
)*
_ => false,
}
}
fn is_extend(&self) -> bool {
match self {
$(
Self::$variant if false $( || $is_extend )? => false $( || $is_extend )?,
)*
_ => false,
}
}
}
};
// Base case: zero arity.
(
@arity;
) => {
0
};
// Recursive case: count one for the head and add that to the arity of the
// rest.
(
@arity;
$head:ident,
$( $rest:ident, )*
) => {
1 + $crate::define_typing_rules_impl_for_operator!(
@arity;
$( $rest, )*
)
}
}
/// Define both a `wast::parser::Parse` implementation and a
/// `peepmatic_traits::TypingRules` implementation for the given operator type.
#[macro_export]
macro_rules! define_parse_and_typing_rules_for_operator {
(
$operator:ident {
$(
$keyword:ident => $variant:ident {
$( immediates( $($immediate:ident),* ); )?
$( parameters( $($parameter:ident),* ); )?
result( $result:ident );
$( is_reduce($is_reduce:expr); )?
$( is_extend($is_extend:expr); )?
}
)*
}
$( parse_cfg($parse_cfg:meta); )?
) => {
$( #[cfg($parse_cfg)] )?
$crate::define_parse_impl_for_operator! {
$operator {
$(
$keyword => $variant;
)*
}
}
$crate::define_typing_rules_impl_for_operator! {
$operator {
$(
$variant {
$( immediates( $($immediate),* ); )?
$( parameters( $($parameter),* ); )?
result( $result );
$( is_reduce($is_reduce); )?
$( is_extend($is_extend); )?
}
)*
}
}
}
}
/// Define an operator type, as well as its parsing and typing rules.
#[macro_export]
macro_rules! define_operator {
(
$( #[$attr:meta] )*
$operator:ident {
$(
$keywrord:ident => $variant:ident {
$( immediates( $($immediate:ident),* ); )?
$( parameters( $($parameter:ident),* ); )?
result( $result:ident );
$( is_reduce($is_reduce:expr); )?
$( is_extend($is_extend:expr); )?
}
)*
}
$( parse_cfg($parse_cfg:meta); )?
) => {
$( #[$attr] )*
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[repr(u32)]
pub enum $operator {
$(
$variant,
)*
}
impl From<$operator> for u32 {
#[inline]
fn from(x: $operator) -> u32 {
x as u32
}
}
impl From<$operator> for core::num::NonZeroU32 {
#[inline]
fn from(x: $operator) -> core::num::NonZeroU32 {
let x: u32 = x.into();
core::num::NonZeroU32::new(x.checked_add(1).unwrap()).unwrap()
}
}
impl core::convert::TryFrom<u32> for $operator {
type Error = ();
#[inline]
fn try_from(x: u32) -> Result<Self, ()> {
match x {
$(
x if x == Self::$variant.into() => Ok(Self::$variant),
)*
_ => Err(())
}
}
}
impl core::convert::TryFrom<core::num::NonZeroU32> for $operator {
type Error = ();
#[inline]
fn try_from(x: core::num::NonZeroU32) -> Result<Self, ()> {
let x = x.get().checked_sub(1).ok_or(())?;
Self::try_from(x)
}
}
$crate::define_parse_and_typing_rules_for_operator! {
$operator {
$(
$keywrord => $variant {
$( immediates( $($immediate),* ); )?
$( parameters( $($parameter),* ); )?
result( $result );
$( is_reduce($is_reduce); )?
$( is_extend($is_extend); )?
}
)*
}
$( parse_cfg($parse_cfg); )?
}
}
}