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:
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "peepmatic-automata"
|
||||
version = "0.2.0"
|
||||
version = "0.66.0"
|
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "peepmatic-fuzzing"
|
||||
version = "0.2.0"
|
||||
version = "0.66.0"
|
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
@@ -17,6 +17,8 @@ peepmatic = { path = "../.." }
|
||||
peepmatic-automata = { path = "../automata", features = ["serde"] }
|
||||
peepmatic-runtime = { path = "../runtime", features = ["construct"] }
|
||||
peepmatic-test = { path = "../test" }
|
||||
peepmatic-test-operator = { path = "../test-operator" }
|
||||
peepmatic-traits = { path = "../traits" }
|
||||
rand = { version = "0.7.3", features = ["small_rng"] }
|
||||
serde = "1.0.106"
|
||||
wast = "15.0.0"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Fuzz testing utilities related to AST pattern matching.
|
||||
|
||||
use peepmatic_runtime::PeepholeOptimizations;
|
||||
use peepmatic_test_operator::TestOperator;
|
||||
use std::path::Path;
|
||||
use std::str;
|
||||
|
||||
@@ -19,18 +20,18 @@ pub fn compile(data: &[u8]) {
|
||||
Ok(s) => s,
|
||||
};
|
||||
|
||||
let opt = match peepmatic::compile_str(source, Path::new("fuzz")) {
|
||||
let opt = match peepmatic::compile_str::<TestOperator>(source, Path::new("fuzz")) {
|
||||
Err(_) => return,
|
||||
Ok(o) => o,
|
||||
};
|
||||
|
||||
// Should be able to serialize and deserialize the peephole optimizer.
|
||||
let opt_bytes = bincode::serialize(&opt).expect("should serialize peephole optimizations OK");
|
||||
let _: PeepholeOptimizations =
|
||||
let _: PeepholeOptimizations<TestOperator> =
|
||||
bincode::deserialize(&opt_bytes).expect("should deserialize peephole optimizations OK");
|
||||
|
||||
// Compiling the same source text again should be deterministic.
|
||||
let opt2 = peepmatic::compile_str(source, Path::new("fuzz"))
|
||||
let opt2 = peepmatic::compile_str::<TestOperator>(source, Path::new("fuzz"))
|
||||
.expect("should be able to compile source text again, if it compiled OK the first time");
|
||||
let opt2_bytes =
|
||||
bincode::serialize(&opt2).expect("should serialize second peephole optimizations OK");
|
||||
|
||||
@@ -6,12 +6,13 @@ use peepmatic::{
|
||||
};
|
||||
use peepmatic_runtime::{
|
||||
cc::ConditionCode,
|
||||
operator::TypingContext as TypingContextTrait,
|
||||
part::Constant,
|
||||
r#type::BitWidth,
|
||||
r#type::{Kind, Type},
|
||||
};
|
||||
use peepmatic_test::{Program, TestIsa};
|
||||
use peepmatic_test_operator::TestOperator;
|
||||
use peepmatic_traits::{TypingContext as TypingContextTrait, TypingRules};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::path::Path;
|
||||
use std::str;
|
||||
@@ -37,7 +38,7 @@ pub fn interp(data: &[u8]) {
|
||||
|
||||
// Okay, we know it compiles and verifies alright, so (re)parse the AST.
|
||||
let buf = wast::parser::ParseBuffer::new(&source).unwrap();
|
||||
let ast = wast::parser::parse::<Optimizations>(&buf).unwrap();
|
||||
let ast = wast::parser::parse::<Optimizations<TestOperator>>(&buf).unwrap();
|
||||
|
||||
// And we need access to the assigned types, so re-verify it as well.
|
||||
peepmatic::verify(&ast).unwrap();
|
||||
@@ -87,7 +88,7 @@ pub fn interp(data: &[u8]) {
|
||||
// Generate this operation's immediates.
|
||||
let mut imm_tys = vec![];
|
||||
op.operator
|
||||
.immediate_types(&mut TypingContext, op.span(), &mut imm_tys);
|
||||
.immediate_types((), &mut TypingContext, &mut imm_tys);
|
||||
let imms: Vec<_> = op
|
||||
.operands
|
||||
.iter()
|
||||
@@ -121,7 +122,7 @@ pub fn interp(data: &[u8]) {
|
||||
// this operation's arguments.
|
||||
let mut arg_tys = vec![];
|
||||
op.operator
|
||||
.param_types(&mut TypingContext, op.span(), &mut arg_tys);
|
||||
.parameter_types((), &mut TypingContext, &mut arg_tys);
|
||||
let args: Vec<_> = op
|
||||
.operands
|
||||
.iter()
|
||||
@@ -165,7 +166,7 @@ pub fn interp(data: &[u8]) {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let ty = match op.operator.result_type(&mut TypingContext, op.span()) {
|
||||
let ty = match op.operator.result_type((), &mut TypingContext) {
|
||||
TypeOrConditionCode::Type(ty) => ty,
|
||||
TypeOrConditionCode::ConditionCode => {
|
||||
unreachable!("condition codes cannot be operation results")
|
||||
@@ -206,41 +207,42 @@ enum TypeOrConditionCode {
|
||||
struct TypingContext;
|
||||
|
||||
impl<'a> TypingContextTrait<'a> for TypingContext {
|
||||
type Span = ();
|
||||
type TypeVariable = TypeOrConditionCode;
|
||||
|
||||
fn cc(&mut self, _: wast::Span) -> Self::TypeVariable {
|
||||
fn cc(&mut self, _: ()) -> Self::TypeVariable {
|
||||
TypeOrConditionCode::ConditionCode
|
||||
}
|
||||
|
||||
fn bNN(&mut self, _: wast::Span) -> Self::TypeVariable {
|
||||
fn bNN(&mut self, _: ()) -> Self::TypeVariable {
|
||||
TypeOrConditionCode::Type(Type::b1())
|
||||
}
|
||||
|
||||
fn iNN(&mut self, _: wast::Span) -> Self::TypeVariable {
|
||||
fn iNN(&mut self, _: ()) -> Self::TypeVariable {
|
||||
TypeOrConditionCode::Type(Type::i32())
|
||||
}
|
||||
|
||||
fn iMM(&mut self, _: wast::Span) -> Self::TypeVariable {
|
||||
fn iMM(&mut self, _: ()) -> Self::TypeVariable {
|
||||
TypeOrConditionCode::Type(Type::i32())
|
||||
}
|
||||
|
||||
fn cpu_flags(&mut self, _: wast::Span) -> Self::TypeVariable {
|
||||
fn cpu_flags(&mut self, _: ()) -> Self::TypeVariable {
|
||||
TypeOrConditionCode::Type(Type::cpu_flags())
|
||||
}
|
||||
|
||||
fn b1(&mut self, _: wast::Span) -> Self::TypeVariable {
|
||||
fn b1(&mut self, _: ()) -> Self::TypeVariable {
|
||||
TypeOrConditionCode::Type(Type::b1())
|
||||
}
|
||||
|
||||
fn void(&mut self, _: wast::Span) -> Self::TypeVariable {
|
||||
fn void(&mut self, _: ()) -> Self::TypeVariable {
|
||||
TypeOrConditionCode::Type(Type::void())
|
||||
}
|
||||
|
||||
fn bool_or_int(&mut self, _: wast::Span) -> Self::TypeVariable {
|
||||
fn bool_or_int(&mut self, _: ()) -> Self::TypeVariable {
|
||||
TypeOrConditionCode::Type(Type::b1())
|
||||
}
|
||||
|
||||
fn any_t(&mut self, _: wast::Span) -> Self::TypeVariable {
|
||||
fn any_t(&mut self, _: ()) -> Self::TypeVariable {
|
||||
TypeOrConditionCode::Type(Type::i32())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Utilities for fuzzing our DSL's parser.
|
||||
|
||||
use peepmatic::Optimizations;
|
||||
use peepmatic_test_operator::TestOperator;
|
||||
use std::str;
|
||||
|
||||
/// Attempt to parse the given string as if it were a snippet of our DSL.
|
||||
@@ -15,7 +16,7 @@ pub fn parse(data: &[u8]) {
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
let _ = wast::parser::parse::<Optimizations>(&buf);
|
||||
let _ = wast::parser::parse::<Optimizations<TestOperator>>(&buf);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "peepmatic-macro"
|
||||
version = "0.2.0"
|
||||
version = "0.66.0"
|
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "peepmatic-runtime"
|
||||
version = "0.2.0"
|
||||
version = "0.66.0"
|
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
@@ -12,13 +12,14 @@ description = "Runtime support for peepmatic peephole optimizers"
|
||||
bincode = "1.2.1"
|
||||
bumpalo = "3.2.0"
|
||||
log = "0.4.8"
|
||||
peepmatic-automata = { version = "0.2.0", path = "../automata", features = ["serde"] }
|
||||
peepmatic-macro = { version = "0.2.0", path = "../macro" }
|
||||
peepmatic-automata = { version = "0.66.0", path = "../automata", features = ["serde"] }
|
||||
peepmatic-traits = { version = "0.66.0", path = "../traits" }
|
||||
serde = { version = "1.0.105", features = ["derive"] }
|
||||
thiserror = "1.0.15"
|
||||
wast = { version = "15.0.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
peepmatic-test-operator = { version = "0.66.0", path = "../test-operator" }
|
||||
serde_test = "1.0.114"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
//! Interfacing with actual instructions.
|
||||
|
||||
use crate::operator::Operator;
|
||||
use crate::part::{Constant, Part};
|
||||
use crate::paths::Path;
|
||||
use crate::r#type::Type;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
/// A trait for interfacing with actual instruction sequences.
|
||||
///
|
||||
@@ -32,6 +33,9 @@ pub unsafe trait InstructionSet<'a> {
|
||||
/// implementation.
|
||||
type Context;
|
||||
|
||||
/// An operator.
|
||||
type Operator: 'static + Copy + Debug + Eq + Hash + Into<NonZeroU32>;
|
||||
|
||||
/// An instruction (or identifier for an instruction).
|
||||
type Instruction: Copy + Debug + Eq;
|
||||
|
||||
@@ -64,10 +68,12 @@ pub unsafe trait InstructionSet<'a> {
|
||||
|
||||
/// Get the given instruction's operator.
|
||||
///
|
||||
/// If the instruction's opcode does not have an associated
|
||||
/// `peepmatic_runtime::operator::Operator` variant (i.e. that instruction
|
||||
/// isn't supported by `peepmatic` yet) then `None` should be returned.
|
||||
fn operator(&self, context: &mut Self::Context, instr: Self::Instruction) -> Option<Operator>;
|
||||
/// If the instruction isn't supported, then `None` should be returned.
|
||||
fn operator(
|
||||
&self,
|
||||
context: &mut Self::Context,
|
||||
instr: Self::Instruction,
|
||||
) -> Option<Self::Operator>;
|
||||
|
||||
/// Make a unary instruction.
|
||||
///
|
||||
@@ -76,7 +82,7 @@ pub unsafe trait InstructionSet<'a> {
|
||||
&self,
|
||||
context: &mut Self::Context,
|
||||
root: Self::Instruction,
|
||||
operator: Operator,
|
||||
operator: Self::Operator,
|
||||
r#type: Type,
|
||||
a: Part<Self::Instruction>,
|
||||
) -> Self::Instruction;
|
||||
@@ -92,7 +98,7 @@ pub unsafe trait InstructionSet<'a> {
|
||||
&self,
|
||||
context: &mut Self::Context,
|
||||
root: Self::Instruction,
|
||||
operator: Operator,
|
||||
operator: Self::Operator,
|
||||
r#type: Type,
|
||||
a: Part<Self::Instruction>,
|
||||
b: Part<Self::Instruction>,
|
||||
@@ -108,7 +114,7 @@ pub unsafe trait InstructionSet<'a> {
|
||||
&self,
|
||||
context: &mut Self::Context,
|
||||
root: Self::Instruction,
|
||||
operator: Operator,
|
||||
operator: Self::Operator,
|
||||
r#type: Type,
|
||||
a: Part<Self::Instruction>,
|
||||
b: Part<Self::Instruction>,
|
||||
|
||||
@@ -22,12 +22,12 @@ pub mod error;
|
||||
pub mod instruction_set;
|
||||
pub mod integer_interner;
|
||||
pub mod linear;
|
||||
pub mod operator;
|
||||
pub mod optimizations;
|
||||
pub mod optimizer;
|
||||
pub mod part;
|
||||
pub mod paths;
|
||||
pub mod r#type;
|
||||
pub mod unquote;
|
||||
|
||||
pub use error::{Error, Result};
|
||||
pub use optimizations::PeepholeOptimizations;
|
||||
|
||||
@@ -7,17 +7,22 @@
|
||||
|
||||
use crate::cc::ConditionCode;
|
||||
use crate::integer_interner::{IntegerId, IntegerInterner};
|
||||
use crate::operator::{Operator, UnquoteOperator};
|
||||
use crate::paths::{PathId, PathInterner};
|
||||
use crate::r#type::{BitWidth, Type};
|
||||
use crate::unquote::UnquoteOperator;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
/// A set of linear optimizations.
|
||||
#[derive(Debug)]
|
||||
pub struct Optimizations {
|
||||
pub struct Optimizations<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// The linear optimizations.
|
||||
pub optimizations: Vec<Optimization>,
|
||||
pub optimizations: Vec<Optimization<TOperator>>,
|
||||
|
||||
/// The de-duplicated paths referenced by these optimizations.
|
||||
pub paths: PathInterner,
|
||||
@@ -28,9 +33,12 @@ pub struct Optimizations {
|
||||
|
||||
/// A linearized optimization.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Optimization {
|
||||
pub struct Optimization<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// The chain of increments for this optimization.
|
||||
pub increments: Vec<Increment>,
|
||||
pub increments: Vec<Increment<TOperator>>,
|
||||
}
|
||||
|
||||
/// Match any value.
|
||||
@@ -63,7 +71,10 @@ pub fn bool_to_match_result(b: bool) -> MatchResult {
|
||||
/// basically become a state and a transition edge out of that state in the
|
||||
/// final automata.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Increment {
|
||||
pub struct Increment<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// The matching operation to perform.
|
||||
pub operation: MatchOp,
|
||||
|
||||
@@ -74,7 +85,7 @@ pub struct Increment {
|
||||
|
||||
/// Actions to perform, given that the operation resulted in the expected
|
||||
/// value.
|
||||
pub actions: Vec<Action>,
|
||||
pub actions: Vec<Action<TOperator>>,
|
||||
}
|
||||
|
||||
/// A matching operation to be performed on some Cranelift instruction as part
|
||||
@@ -163,7 +174,7 @@ pub struct RhsId(pub u16);
|
||||
/// When evaluating actions, the `i^th` action implicitly defines the
|
||||
/// `RhsId(i)`.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Action {
|
||||
pub enum Action<TOperator> {
|
||||
/// Reuse something from the left-hand side.
|
||||
GetLhs {
|
||||
/// The path to the instruction or value.
|
||||
@@ -215,13 +226,13 @@ pub enum Action {
|
||||
/// The type of this instruction's result.
|
||||
r#type: Type,
|
||||
/// The operator for this instruction.
|
||||
operator: Operator,
|
||||
operator: TOperator,
|
||||
},
|
||||
|
||||
/// Make a binary instruction.
|
||||
MakeBinaryInst {
|
||||
/// The opcode for this instruction.
|
||||
operator: Operator,
|
||||
operator: TOperator,
|
||||
/// The type of this instruction's result.
|
||||
r#type: Type,
|
||||
/// The operands for this instruction.
|
||||
@@ -231,7 +242,7 @@ pub enum Action {
|
||||
/// Make a ternary instruction.
|
||||
MakeTernaryInst {
|
||||
/// The opcode for this instruction.
|
||||
operator: Operator,
|
||||
operator: TOperator,
|
||||
/// The type of this instruction's result.
|
||||
r#type: Type,
|
||||
/// The operands for this instruction.
|
||||
@@ -242,6 +253,7 @@ pub enum Action {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use peepmatic_test_operator::TestOperator;
|
||||
|
||||
// These types all end up in the automaton, so we should take care that they
|
||||
// are small and don't fill up the data cache (or take up too much
|
||||
@@ -259,6 +271,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn action_size() {
|
||||
assert_eq!(std::mem::size_of::<Action>(), 16);
|
||||
assert_eq!(std::mem::size_of::<Action<TestOperator>>(), 16);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,306 +0,0 @@
|
||||
//! Operator definitions.
|
||||
|
||||
use peepmatic_macro::PeepmaticOperator;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// An operator.
|
||||
///
|
||||
/// These are a subset of Cranelift IR's operators.
|
||||
///
|
||||
/// ## Caveats for Branching and Trapping Operators
|
||||
///
|
||||
/// Branching operators are not fully modeled: we do not represent their label
|
||||
/// and jump arguments. It is up to the interpreter doing the instruction
|
||||
/// replacement to recognize when we are replacing one branch with another, and
|
||||
/// copy over the extra information.
|
||||
///
|
||||
/// Affected operations: `brz`, `brnz`, `trapz`, `trapnz`.
|
||||
#[derive(PeepmaticOperator, Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||
#[repr(u32)]
|
||||
pub enum Operator {
|
||||
/// `adjust_sp_down`
|
||||
#[peepmatic(params(iNN), result(void))]
|
||||
// NB: We convert `Operator`s into `NonZeroU32`s with unchecked casts;
|
||||
// memory safety relies on `Operator` starting at `1` and no variant ever
|
||||
// being zero.
|
||||
AdjustSpDown = 1,
|
||||
|
||||
/// `adjust_sp_down_imm`
|
||||
#[peepmatic(immediates(iNN), result(void))]
|
||||
AdjustSpDownImm,
|
||||
|
||||
/// `band`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Band,
|
||||
|
||||
/// `band_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
BandImm,
|
||||
|
||||
/// `bconst`
|
||||
#[peepmatic(immediates(b1), result(bNN))]
|
||||
Bconst,
|
||||
|
||||
/// `bint`
|
||||
#[peepmatic(params(bNN), result(iNN))]
|
||||
Bint,
|
||||
|
||||
/// `bnot`
|
||||
#[peepmatic(params(iNN), result(iNN))]
|
||||
Bnot,
|
||||
|
||||
/// `bor`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Bor,
|
||||
|
||||
/// `bor_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
BorImm,
|
||||
|
||||
/// `brnz`
|
||||
#[peepmatic(params(bool_or_int), result(void))]
|
||||
Brnz,
|
||||
|
||||
/// `brz`
|
||||
#[peepmatic(params(bool_or_int), result(void))]
|
||||
Brz,
|
||||
|
||||
/// `bxor`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Bxor,
|
||||
|
||||
/// `bxor_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
BxorImm,
|
||||
|
||||
/// `iadd`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Iadd,
|
||||
|
||||
/// `iadd_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
IaddImm,
|
||||
|
||||
/// `icmp`
|
||||
#[peepmatic(immediates(cc), params(iNN, iNN), result(b1))]
|
||||
Icmp,
|
||||
|
||||
/// `icmp_imm`
|
||||
#[peepmatic(immediates(cc, iNN), params(iNN), result(b1))]
|
||||
IcmpImm,
|
||||
|
||||
/// `iconst`
|
||||
#[peepmatic(immediates(iNN), result(iNN))]
|
||||
Iconst,
|
||||
|
||||
/// `ifcmp`
|
||||
#[peepmatic(params(iNN, iNN), result(cpu_flags))]
|
||||
Ifcmp,
|
||||
|
||||
/// `ifcmp_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(cpu_flags))]
|
||||
IfcmpImm,
|
||||
|
||||
/// `imul`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Imul,
|
||||
|
||||
/// `imul_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
ImulImm,
|
||||
|
||||
/// `ireduce`
|
||||
#[peepmatic(params(iNN), result(iMM))]
|
||||
Ireduce,
|
||||
|
||||
/// `irsub_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
IrsubImm,
|
||||
|
||||
/// `ishl`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Ishl,
|
||||
|
||||
/// `ishl_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
IshlImm,
|
||||
|
||||
/// `isub`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Isub,
|
||||
|
||||
/// `rotl`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Rotl,
|
||||
|
||||
/// `rotl_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
RotlImm,
|
||||
|
||||
/// `rotr`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Rotr,
|
||||
|
||||
/// `rotr_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
RotrImm,
|
||||
|
||||
/// `sdiv`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Sdiv,
|
||||
|
||||
/// `sdiv_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
SdivImm,
|
||||
|
||||
/// `select`
|
||||
#[peepmatic(params(bool_or_int, any_t, any_t), result(any_t))]
|
||||
Select,
|
||||
|
||||
/// `sextend`
|
||||
#[peepmatic(params(iNN), result(iMM))]
|
||||
Sextend,
|
||||
|
||||
/// `srem`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Srem,
|
||||
|
||||
/// `srem_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
SremImm,
|
||||
|
||||
/// `sshr`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Sshr,
|
||||
|
||||
/// `sshr_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
SshrImm,
|
||||
|
||||
/// `trapnz`
|
||||
#[peepmatic(params(bool_or_int), result(void))]
|
||||
Trapnz,
|
||||
|
||||
/// `trapz`
|
||||
#[peepmatic(params(bool_or_int), result(void))]
|
||||
Trapz,
|
||||
|
||||
/// `udiv`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Udiv,
|
||||
|
||||
/// `udiv_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
UdivImm,
|
||||
|
||||
/// `uextend`
|
||||
#[peepmatic(params(iNN), result(iMM))]
|
||||
Uextend,
|
||||
|
||||
/// `urem`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Urem,
|
||||
|
||||
/// `urem_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
UremImm,
|
||||
|
||||
/// `ushr`
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Ushr,
|
||||
|
||||
/// `ushr_imm`
|
||||
#[peepmatic(immediates(iNN), params(iNN), result(iNN))]
|
||||
UshrImm,
|
||||
}
|
||||
|
||||
/// Compile-time unquote operators.
|
||||
///
|
||||
/// These are used in the right-hand side to perform compile-time evaluation of
|
||||
/// constants matched on the left-hand side.
|
||||
#[derive(PeepmaticOperator, Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
|
||||
#[repr(u32)]
|
||||
pub enum UnquoteOperator {
|
||||
/// Compile-time `band` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Band,
|
||||
|
||||
/// Compile-time `bor` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Bor,
|
||||
|
||||
/// Compile-time `bxor` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Bxor,
|
||||
|
||||
/// Compile-time `iadd` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Iadd,
|
||||
|
||||
/// Compile-time `imul` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Imul,
|
||||
|
||||
/// Compile-time `isub` of two constant values.
|
||||
#[peepmatic(params(iNN, iNN), result(iNN))]
|
||||
Isub,
|
||||
|
||||
/// Take the base-2 log of a power of two integer.
|
||||
#[peepmatic(params(iNN), result(iNN))]
|
||||
Log2,
|
||||
|
||||
/// Wrapping negation of an integer.
|
||||
#[peepmatic(params(iNN), result(iNN))]
|
||||
Neg,
|
||||
}
|
||||
|
||||
/// A trait to represent a typing context.
|
||||
///
|
||||
/// This is used by the macro-generated operator methods that create the type
|
||||
/// variables for their immediates, parameters, and results. This trait is
|
||||
/// implemented by the concrete typing context in `peepmatic/src/verify.rs`.
|
||||
#[cfg(feature = "construct")]
|
||||
pub trait TypingContext<'a> {
|
||||
/// A type variable.
|
||||
type TypeVariable;
|
||||
|
||||
/// Create a condition code type.
|
||||
fn cc(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a boolean type with a polymorphic bit width.
|
||||
///
|
||||
/// Each use of `bNN` by the same operator refers to the same type variable.
|
||||
#[allow(non_snake_case)]
|
||||
fn bNN(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create an integer type with a polymorphic bit width.
|
||||
///
|
||||
/// Each use of `iNN` by the same operator refers to the same type variable.
|
||||
#[allow(non_snake_case)]
|
||||
fn iNN(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create an integer type with a polymorphic bit width.
|
||||
///
|
||||
/// Each use of `iMM` by the same operator refers to the same type variable.
|
||||
#[allow(non_snake_case)]
|
||||
fn iMM(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create the CPU flags type variable.
|
||||
fn cpu_flags(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a boolean type of size one bit.
|
||||
fn b1(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create the void type, used as the result of operators that branch away,
|
||||
/// or do not return anything.
|
||||
fn void(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a type variable that may be either a boolean or an integer.
|
||||
fn bool_or_int(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a type variable that can be any type T.
|
||||
///
|
||||
/// Each use of `any_t` by the same operator refers to the same type
|
||||
/// variable.
|
||||
fn any_t(&mut self, span: wast::Span) -> Self::TypeVariable;
|
||||
}
|
||||
@@ -8,6 +8,8 @@ use crate::optimizer::PeepholeOptimizer;
|
||||
use crate::paths::PathInterner;
|
||||
use peepmatic_automata::Automaton;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
|
||||
#[cfg(feature = "construct")]
|
||||
use std::fs;
|
||||
@@ -19,7 +21,10 @@ use std::path::Path;
|
||||
/// This is the compilation result of the `peepmatic` crate, after its taken a
|
||||
/// bunch of optimizations written in the DSL and lowered and combined them.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct PeepholeOptimizations {
|
||||
pub struct PeepholeOptimizations<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// The instruction paths referenced by the peephole optimizations.
|
||||
pub paths: PathInterner,
|
||||
|
||||
@@ -29,12 +34,18 @@ pub struct PeepholeOptimizations {
|
||||
|
||||
/// The underlying automata for matching optimizations' left-hand sides, and
|
||||
/// building up the corresponding right-hand side.
|
||||
pub automata: Automaton<MatchResult, MatchOp, Box<[Action]>>,
|
||||
pub automata: Automaton<MatchResult, MatchOp, Box<[Action<TOperator>]>>,
|
||||
}
|
||||
|
||||
impl PeepholeOptimizations {
|
||||
impl<TOperator> PeepholeOptimizations<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// Deserialize a `PeepholeOptimizations` from bytes.
|
||||
pub fn deserialize(serialized: &[u8]) -> Result<Self> {
|
||||
pub fn deserialize<'a>(serialized: &'a [u8]) -> Result<Self>
|
||||
where
|
||||
TOperator: serde::Deserialize<'a>,
|
||||
{
|
||||
let peep_opt: Self = bincode::deserialize(serialized)?;
|
||||
Ok(peep_opt)
|
||||
}
|
||||
@@ -43,12 +54,20 @@ impl PeepholeOptimizations {
|
||||
///
|
||||
/// Requires that the `"construct"` cargo feature is enabled.
|
||||
#[cfg(feature = "construct")]
|
||||
pub fn serialize_to_file(&self, path: &Path) -> Result<()> {
|
||||
pub fn serialize_to_file(&self, path: &Path) -> Result<()>
|
||||
where
|
||||
TOperator: serde::Serialize,
|
||||
{
|
||||
let file = fs::File::create(path)?;
|
||||
bincode::serialize_into(file, self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<TOperator> PeepholeOptimizations<TOperator>
|
||||
where
|
||||
TOperator: 'static + Copy + Debug + Eq + Hash,
|
||||
{
|
||||
/// Create a new peephole optimizer instance from this set of peephole
|
||||
/// optimizations.
|
||||
///
|
||||
@@ -58,9 +77,13 @@ impl PeepholeOptimizations {
|
||||
/// instance, rather than create a new one for each instruction. Reusing the
|
||||
/// peephole optimizer instance allows the reuse of a few internal
|
||||
/// allocations.
|
||||
pub fn optimizer<'peep, 'ctx, I>(&'peep self, instr_set: I) -> PeepholeOptimizer<'peep, 'ctx, I>
|
||||
pub fn optimizer<'peep, 'ctx, TInstructionSet>(
|
||||
&'peep self,
|
||||
instr_set: TInstructionSet,
|
||||
) -> PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
|
||||
where
|
||||
I: InstructionSet<'ctx>,
|
||||
TInstructionSet: InstructionSet<'ctx, Operator = TOperator>,
|
||||
TOperator: Into<std::num::NonZeroU32>,
|
||||
{
|
||||
PeepholeOptimizer {
|
||||
peep_opt: self,
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
use crate::instruction_set::InstructionSet;
|
||||
use crate::linear::{bool_to_match_result, Action, Else, MatchOp, MatchResult};
|
||||
use crate::operator::UnquoteOperator;
|
||||
use crate::optimizations::PeepholeOptimizations;
|
||||
use crate::part::{Constant, Part};
|
||||
use crate::r#type::{BitWidth, Type};
|
||||
use crate::unquote::UnquoteOperator;
|
||||
use peepmatic_automata::State;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::{self, Debug};
|
||||
@@ -21,20 +21,20 @@ use std::num::NonZeroU32;
|
||||
/// Reusing an instance when applying peephole optimizations to different
|
||||
/// instruction sequences means that you reuse internal allocations that are
|
||||
/// used to match left-hand sides and build up right-hand sides.
|
||||
pub struct PeepholeOptimizer<'peep, 'ctx, I>
|
||||
pub struct PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
|
||||
where
|
||||
I: InstructionSet<'ctx>,
|
||||
TInstructionSet: InstructionSet<'ctx>,
|
||||
{
|
||||
pub(crate) peep_opt: &'peep PeepholeOptimizations,
|
||||
pub(crate) instr_set: I,
|
||||
pub(crate) right_hand_sides: Vec<Part<I::Instruction>>,
|
||||
pub(crate) actions: Vec<Action>,
|
||||
pub(crate) peep_opt: &'peep PeepholeOptimizations<TInstructionSet::Operator>,
|
||||
pub(crate) instr_set: TInstructionSet,
|
||||
pub(crate) right_hand_sides: Vec<Part<TInstructionSet::Instruction>>,
|
||||
pub(crate) actions: Vec<Action<TInstructionSet::Operator>>,
|
||||
pub(crate) backtracking_states: Vec<(State, usize)>,
|
||||
}
|
||||
|
||||
impl<'peep, 'ctx, I> Debug for PeepholeOptimizer<'peep, 'ctx, I>
|
||||
impl<'peep, 'ctx, TInstructionSet> Debug for PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
|
||||
where
|
||||
I: InstructionSet<'ctx>,
|
||||
TInstructionSet: InstructionSet<'ctx>,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let PeepholeOptimizer {
|
||||
@@ -54,9 +54,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'peep, 'ctx, I> PeepholeOptimizer<'peep, 'ctx, I>
|
||||
impl<'peep, 'ctx, TInstructionSet> PeepholeOptimizer<'peep, 'ctx, TInstructionSet>
|
||||
where
|
||||
I: InstructionSet<'ctx>,
|
||||
TInstructionSet: InstructionSet<'ctx>,
|
||||
{
|
||||
fn eval_unquote_1(&self, operator: UnquoteOperator, a: Constant) -> Constant {
|
||||
use Constant::*;
|
||||
@@ -107,7 +107,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_actions(&mut self, context: &mut I::Context, root: I::Instruction) {
|
||||
fn eval_actions(
|
||||
&mut self,
|
||||
context: &mut TInstructionSet::Context,
|
||||
root: TInstructionSet::Instruction,
|
||||
) {
|
||||
let mut actions = mem::replace(&mut self.actions, vec![]);
|
||||
|
||||
for action in actions.drain(..) {
|
||||
@@ -272,8 +276,8 @@ where
|
||||
|
||||
fn eval_match_op(
|
||||
&mut self,
|
||||
context: &mut I::Context,
|
||||
root: I::Instruction,
|
||||
context: &mut TInstructionSet::Context,
|
||||
root: TInstructionSet::Instruction,
|
||||
match_op: MatchOp,
|
||||
) -> MatchResult {
|
||||
use crate::linear::MatchOp::*;
|
||||
@@ -288,13 +292,7 @@ where
|
||||
.ok_or(Else)?;
|
||||
let inst = part.as_instruction().ok_or(Else)?;
|
||||
let op = self.instr_set.operator(context, inst).ok_or(Else)?;
|
||||
let op = op as u32;
|
||||
debug_assert!(
|
||||
op != 0,
|
||||
"`Operator` doesn't have any variant represented
|
||||
with zero"
|
||||
);
|
||||
Ok(unsafe { NonZeroU32::new_unchecked(op as u32) })
|
||||
Ok(op.into())
|
||||
}
|
||||
IsConst { path } => {
|
||||
let path = self.peep_opt.paths.lookup(path);
|
||||
@@ -477,9 +475,9 @@ where
|
||||
/// untouched and `None` is returned.
|
||||
pub fn apply_one(
|
||||
&mut self,
|
||||
context: &mut I::Context,
|
||||
root: I::Instruction,
|
||||
) -> Option<I::Instruction> {
|
||||
context: &mut TInstructionSet::Context,
|
||||
root: TInstructionSet::Instruction,
|
||||
) -> Option<TInstructionSet::Instruction> {
|
||||
log::trace!("PeepholeOptimizer::apply_one");
|
||||
|
||||
self.backtracking_states.clear();
|
||||
@@ -566,7 +564,11 @@ where
|
||||
|
||||
/// Keep applying peephole optimizations to the given instruction until none
|
||||
/// can be applied anymore.
|
||||
pub fn apply_all(&mut self, context: &mut I::Context, mut inst: I::Instruction) {
|
||||
pub fn apply_all(
|
||||
&mut self,
|
||||
context: &mut TInstructionSet::Context,
|
||||
mut inst: TInstructionSet::Instruction,
|
||||
) {
|
||||
loop {
|
||||
if let Some(new_inst) = self.apply_one(context, inst) {
|
||||
inst = new_inst;
|
||||
|
||||
44
cranelift/peepmatic/crates/runtime/src/unquote.rs
Normal file
44
cranelift/peepmatic/crates/runtime/src/unquote.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
//! Unquote operator definition.
|
||||
|
||||
peepmatic_traits::define_operator! {
|
||||
/// Compile-time unquote operators.
|
||||
///
|
||||
/// These are used in the right-hand side to perform compile-time evaluation of
|
||||
/// constants matched on the left-hand side.
|
||||
#[allow(missing_docs)]
|
||||
UnquoteOperator {
|
||||
band => Band {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
bor => Bor {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
bxor => Bxor {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
iadd => Iadd {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
imul => Imul {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
isub => Isub {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
log2 => Log2 {
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
neg => Neg {
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
}
|
||||
parse_cfg(feature = "construct");
|
||||
}
|
||||
12
cranelift/peepmatic/crates/test-operator/Cargo.toml
Normal file
12
cranelift/peepmatic/crates/test-operator/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "peepmatic-test-operator"
|
||||
version = "0.66.0"
|
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
peepmatic-traits = { version = "0.66.0", path = "../traits" }
|
||||
serde = { version = "1.0.105", features = ["derive"] }
|
||||
wast = "15.0.0"
|
||||
219
cranelift/peepmatic/crates/test-operator/src/lib.rs
Normal file
219
cranelift/peepmatic/crates/test-operator/src/lib.rs
Normal file
@@ -0,0 +1,219 @@
|
||||
//! This crate defines `TestOperator`: a `TOperator` type for usage in tests.
|
||||
//!
|
||||
//! This allows us to write Peepmatic-specific tests that do not depend on
|
||||
//! building all of Cranelift.
|
||||
|
||||
peepmatic_traits::define_operator! {
|
||||
/// A `TOperator` type for use inside tests.
|
||||
TestOperator {
|
||||
adjust_sp_down => AdjustSpDown {
|
||||
parameters(iNN);
|
||||
result(void);
|
||||
}
|
||||
adjust_sp_down_imm => AdjustSpDownImm {
|
||||
immediates(iNN);
|
||||
result(void);
|
||||
}
|
||||
band => Band {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
band_imm => BandImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
bconst => Bconst {
|
||||
immediates(b1);
|
||||
result(bNN);
|
||||
}
|
||||
bint => Bint {
|
||||
parameters(bNN);
|
||||
result(iNN);
|
||||
}
|
||||
bor => Bor {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
bor_imm => BorImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
brnz => Brnz {
|
||||
parameters(bool_or_int);
|
||||
result(void);
|
||||
}
|
||||
brz => Brz {
|
||||
parameters(bool_or_int);
|
||||
result(void);
|
||||
}
|
||||
bxor => Bxor {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
bxor_imm => BxorImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
iadd => Iadd {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
iadd_imm => IaddImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
icmp => Icmp {
|
||||
immediates(cc);
|
||||
parameters(iNN, iNN);
|
||||
result(b1);
|
||||
}
|
||||
icmp_imm => IcmpImm {
|
||||
immediates(cc, iNN);
|
||||
parameters(iNN);
|
||||
result(b1);
|
||||
}
|
||||
iconst => Iconst {
|
||||
immediates(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
ifcmp => Ifcmp {
|
||||
parameters(iNN, iNN);
|
||||
result(cpu_flags);
|
||||
}
|
||||
ifcmp_imm => IfcmpImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(cpu_flags);
|
||||
}
|
||||
imul => Imul {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
imul_imm => ImulImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
ireduce => Ireduce {
|
||||
parameters(iNN);
|
||||
result(iMM);
|
||||
is_reduce(true);
|
||||
}
|
||||
irsub_imm => IrsubImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
ishl => Ishl {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
ishl_imm => IshlImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
isub => Isub {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
rotl => Rotl {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
rotl_imm => RotlImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
rotr => Rotr {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
rotr_imm => RotrImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
sdiv => Sdiv {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
sdiv_imm => SdivImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
select => Select {
|
||||
parameters(bool_or_int, any_t, any_t);
|
||||
result(any_t);
|
||||
}
|
||||
sextend => Sextend {
|
||||
parameters(iNN);
|
||||
result(iMM);
|
||||
is_extend(true);
|
||||
}
|
||||
srem => Srem {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
srem_imm => SremImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
sshr => Sshr {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
sshr_imm => SshrImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
trapnz => Trapnz {
|
||||
parameters(bool_or_int);
|
||||
result(void);
|
||||
}
|
||||
trapz => Trapz {
|
||||
parameters(bool_or_int);
|
||||
result(void);
|
||||
}
|
||||
udiv => Udiv {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
udiv_imm => UdivImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
uextend => Uextend {
|
||||
parameters(iNN);
|
||||
result(iMM);
|
||||
is_extend(true);
|
||||
}
|
||||
urem => Urem {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
urem_imm => UremImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
ushr => Ushr {
|
||||
parameters(iNN, iNN);
|
||||
result(iNN);
|
||||
}
|
||||
ushr_imm => UshrImm {
|
||||
immediates(iNN);
|
||||
parameters(iNN);
|
||||
result(iNN);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,3 +12,5 @@ env_logger = "0.7.1"
|
||||
log = "0.4.8"
|
||||
peepmatic = { path = "../.." }
|
||||
peepmatic-runtime = { path = "../runtime" }
|
||||
peepmatic-test-operator = { path = "../test-operator" }
|
||||
peepmatic-traits = { path = "../traits" }
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
use peepmatic_runtime::{
|
||||
cc::ConditionCode,
|
||||
instruction_set::InstructionSet,
|
||||
operator::Operator,
|
||||
part::{Constant, Part},
|
||||
paths::Path,
|
||||
r#type::{BitWidth, Kind, Type},
|
||||
};
|
||||
use peepmatic_test_operator::TestOperator;
|
||||
use peepmatic_traits::TypingRules;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryFrom;
|
||||
@@ -19,7 +20,7 @@ pub struct Instruction(pub usize);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InstructionData {
|
||||
pub operator: Operator,
|
||||
pub operator: TestOperator,
|
||||
pub r#type: Type,
|
||||
pub immediates: Vec<Immediate>,
|
||||
pub arguments: Vec<Instruction>,
|
||||
@@ -174,7 +175,7 @@ impl Program {
|
||||
|
||||
pub fn new_instruction(
|
||||
&mut self,
|
||||
operator: Operator,
|
||||
operator: TestOperator,
|
||||
r#type: Type,
|
||||
immediates: Vec<Immediate>,
|
||||
arguments: Vec<Instruction>,
|
||||
@@ -188,11 +189,11 @@ impl Program {
|
||||
immediates.len(),
|
||||
);
|
||||
assert_eq!(
|
||||
operator.params_arity() as usize,
|
||||
operator.parameters_arity() as usize,
|
||||
arguments.len(),
|
||||
"wrong number of arguments for {:?}: expected {}, found {}",
|
||||
operator,
|
||||
operator.params_arity(),
|
||||
operator.parameters_arity(),
|
||||
arguments.len(),
|
||||
);
|
||||
|
||||
@@ -222,7 +223,7 @@ impl Program {
|
||||
assert!(!root_bit_width.is_polymorphic());
|
||||
match c {
|
||||
Constant::Bool(_, bit_width) => self.new_instruction(
|
||||
Operator::Bconst,
|
||||
TestOperator::Bconst,
|
||||
if bit_width.is_polymorphic() {
|
||||
Type {
|
||||
kind: Kind::Bool,
|
||||
@@ -238,7 +239,7 @@ impl Program {
|
||||
vec![],
|
||||
),
|
||||
Constant::Int(_, bit_width) => self.new_instruction(
|
||||
Operator::Iconst,
|
||||
TestOperator::Iconst,
|
||||
if bit_width.is_polymorphic() {
|
||||
Type {
|
||||
kind: Kind::Int,
|
||||
@@ -259,12 +260,12 @@ impl Program {
|
||||
fn instruction_to_constant(&mut self, inst: Instruction) -> Option<Constant> {
|
||||
match self.data(inst) {
|
||||
InstructionData {
|
||||
operator: Operator::Iconst,
|
||||
operator: TestOperator::Iconst,
|
||||
immediates,
|
||||
..
|
||||
} => Some(immediates[0].unwrap_constant()),
|
||||
InstructionData {
|
||||
operator: Operator::Bconst,
|
||||
operator: TestOperator::Bconst,
|
||||
immediates,
|
||||
..
|
||||
} => Some(immediates[0].unwrap_constant()),
|
||||
@@ -310,6 +311,8 @@ pub struct TestIsa {
|
||||
// Unsafe because we must ensure that `instruction_result_bit_width` never
|
||||
// returns zero.
|
||||
unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
||||
type Operator = TestOperator;
|
||||
|
||||
type Context = Program;
|
||||
|
||||
type Instruction = Instruction;
|
||||
@@ -360,7 +363,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
||||
Some(part)
|
||||
}
|
||||
|
||||
fn operator(&self, program: &mut Program, instr: Instruction) -> Option<Operator> {
|
||||
fn operator(&self, program: &mut Program, instr: Instruction) -> Option<TestOperator> {
|
||||
log::debug!("operator({:?})", instr);
|
||||
let data = program.data(instr);
|
||||
Some(data.operator)
|
||||
@@ -370,7 +373,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
||||
&self,
|
||||
program: &mut Program,
|
||||
root: Instruction,
|
||||
operator: Operator,
|
||||
operator: TestOperator,
|
||||
r#type: Type,
|
||||
a: Part<Instruction>,
|
||||
) -> Instruction {
|
||||
@@ -383,11 +386,11 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
||||
|
||||
let (imms, args) = match operator.immediates_arity() {
|
||||
0 => {
|
||||
assert_eq!(operator.params_arity(), 1);
|
||||
assert_eq!(operator.parameters_arity(), 1);
|
||||
(vec![], vec![program.part_to_instruction(root, a).unwrap()])
|
||||
}
|
||||
1 => {
|
||||
assert_eq!(operator.params_arity(), 0);
|
||||
assert_eq!(operator.parameters_arity(), 0);
|
||||
(vec![program.part_to_immediate(a).unwrap()], vec![])
|
||||
}
|
||||
_ => unreachable!(),
|
||||
@@ -399,7 +402,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
||||
&self,
|
||||
program: &mut Program,
|
||||
root: Instruction,
|
||||
operator: Operator,
|
||||
operator: TestOperator,
|
||||
r#type: Type,
|
||||
a: Part<Instruction>,
|
||||
b: Part<Instruction>,
|
||||
@@ -414,7 +417,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
||||
|
||||
let (imms, args) = match operator.immediates_arity() {
|
||||
0 => {
|
||||
assert_eq!(operator.params_arity(), 2);
|
||||
assert_eq!(operator.parameters_arity(), 2);
|
||||
(
|
||||
vec![],
|
||||
vec![
|
||||
@@ -424,14 +427,14 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
||||
)
|
||||
}
|
||||
1 => {
|
||||
assert_eq!(operator.params_arity(), 1);
|
||||
assert_eq!(operator.parameters_arity(), 1);
|
||||
(
|
||||
vec![program.part_to_immediate(a).unwrap()],
|
||||
vec![program.part_to_instruction(root, b).unwrap()],
|
||||
)
|
||||
}
|
||||
2 => {
|
||||
assert_eq!(operator.params_arity(), 0);
|
||||
assert_eq!(operator.parameters_arity(), 0);
|
||||
(
|
||||
vec![
|
||||
program.part_to_immediate(a).unwrap(),
|
||||
@@ -449,7 +452,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
||||
&self,
|
||||
program: &mut Program,
|
||||
root: Instruction,
|
||||
operator: Operator,
|
||||
operator: TestOperator,
|
||||
r#type: Type,
|
||||
a: Part<Instruction>,
|
||||
b: Part<Instruction>,
|
||||
@@ -465,7 +468,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
||||
);
|
||||
let (imms, args) = match operator.immediates_arity() {
|
||||
0 => {
|
||||
assert_eq!(operator.params_arity(), 3);
|
||||
assert_eq!(operator.parameters_arity(), 3);
|
||||
(
|
||||
vec![],
|
||||
vec![
|
||||
@@ -476,7 +479,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
||||
)
|
||||
}
|
||||
1 => {
|
||||
assert_eq!(operator.params_arity(), 2);
|
||||
assert_eq!(operator.parameters_arity(), 2);
|
||||
(
|
||||
vec![program.part_to_immediate(a).unwrap()],
|
||||
vec![
|
||||
@@ -486,7 +489,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
||||
)
|
||||
}
|
||||
2 => {
|
||||
assert_eq!(operator.params_arity(), 1);
|
||||
assert_eq!(operator.parameters_arity(), 1);
|
||||
(
|
||||
vec![
|
||||
program.part_to_immediate(a).unwrap(),
|
||||
@@ -496,7 +499,7 @@ unsafe impl<'a> InstructionSet<'a> for TestIsa {
|
||||
)
|
||||
}
|
||||
3 => {
|
||||
assert_eq!(operator.params_arity(), 0);
|
||||
assert_eq!(operator.parameters_arity(), 0);
|
||||
(
|
||||
vec![
|
||||
program.part_to_immediate(a).unwrap(),
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use peepmatic_runtime::{
|
||||
cc::ConditionCode,
|
||||
operator::Operator,
|
||||
part::Constant,
|
||||
r#type::{BitWidth, Type},
|
||||
};
|
||||
use peepmatic_test::*;
|
||||
use peepmatic_test_operator::TestOperator;
|
||||
|
||||
const TEST_ISA: TestIsa = TestIsa {
|
||||
native_word_size_in_bits: 32,
|
||||
@@ -26,13 +26,13 @@ fn opcode() {
|
||||
let mut program = Program::default();
|
||||
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||
let zero = program.r#const(Constant::Int(0, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||
let add = program.new_instruction(Operator::Iadd, Type::i32(), vec![], vec![five, zero]);
|
||||
let add = program.new_instruction(TestOperator::Iadd, Type::i32(), vec![], vec![five, zero]);
|
||||
|
||||
let new = optimizer.apply_one(&mut program, add);
|
||||
let new = new.expect("optimization should have applied");
|
||||
assert!(program.structurally_eq(new, five));
|
||||
|
||||
let add = program.new_instruction(Operator::Iadd, Type::i32(), vec![], vec![five, five]);
|
||||
let add = program.new_instruction(TestOperator::Iadd, Type::i32(), vec![], vec![five, five]);
|
||||
let replacement = optimizer.apply_one(&mut program, add);
|
||||
assert!(replacement.is_none());
|
||||
}
|
||||
@@ -45,10 +45,10 @@ fn constant() {
|
||||
let mut program = Program::default();
|
||||
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||
let zero = program.r#const(Constant::Int(0, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||
let add = program.new_instruction(Operator::Iadd, Type::i32(), vec![], vec![five, zero]);
|
||||
let add = program.new_instruction(TestOperator::Iadd, Type::i32(), vec![], vec![five, zero]);
|
||||
|
||||
let expected = program.new_instruction(
|
||||
Operator::IaddImm,
|
||||
TestOperator::IaddImm,
|
||||
Type::i32(),
|
||||
vec![Constant::Int(5, BitWidth::ThirtyTwo).into()],
|
||||
vec![zero],
|
||||
@@ -58,8 +58,8 @@ fn constant() {
|
||||
let new = new.expect("optimization should have applied");
|
||||
assert!(program.structurally_eq(new, expected));
|
||||
|
||||
let mul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, zero]);
|
||||
let add = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![mul, five]);
|
||||
let mul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, zero]);
|
||||
let add = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![mul, five]);
|
||||
let replacement = optimizer.apply_one(&mut program, add);
|
||||
assert!(replacement.is_none());
|
||||
}
|
||||
@@ -71,7 +71,7 @@ fn boolean() {
|
||||
|
||||
let mut program = Program::default();
|
||||
let t = program.r#const(Constant::Bool(true, BitWidth::One), BitWidth::One);
|
||||
let bint = program.new_instruction(Operator::Bint, Type::i1(), vec![], vec![t]);
|
||||
let bint = program.new_instruction(TestOperator::Bint, Type::i1(), vec![], vec![t]);
|
||||
let one = program.r#const(Constant::Int(1, BitWidth::One), BitWidth::ThirtyTwo);
|
||||
|
||||
let new = optimizer.apply_one(&mut program, bint);
|
||||
@@ -79,7 +79,7 @@ fn boolean() {
|
||||
assert!(program.structurally_eq(new, one));
|
||||
|
||||
let f = program.r#const(Constant::Bool(false, BitWidth::One), BitWidth::One);
|
||||
let bint = program.new_instruction(Operator::Bint, Type::i1(), vec![], vec![f]);
|
||||
let bint = program.new_instruction(TestOperator::Bint, Type::i1(), vec![], vec![f]);
|
||||
let replacement = optimizer.apply_one(&mut program, bint);
|
||||
assert!(replacement.is_none());
|
||||
}
|
||||
@@ -92,7 +92,7 @@ fn condition_codes() {
|
||||
let mut program = Program::default();
|
||||
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::One);
|
||||
let icmp_eq = program.new_instruction(
|
||||
Operator::Icmp,
|
||||
TestOperator::Icmp,
|
||||
Type::b1(),
|
||||
vec![ConditionCode::Eq.into()],
|
||||
vec![five, five],
|
||||
@@ -104,7 +104,7 @@ fn condition_codes() {
|
||||
assert!(program.structurally_eq(new, t));
|
||||
|
||||
let icmp_ne = program.new_instruction(
|
||||
Operator::Icmp,
|
||||
TestOperator::Icmp,
|
||||
Type::b1(),
|
||||
vec![ConditionCode::Ne.into()],
|
||||
vec![five, five],
|
||||
@@ -128,17 +128,17 @@ fn is_power_of_two() {
|
||||
let mut program = Program::default();
|
||||
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||
let two = program.r#const(Constant::Int(2, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||
let imul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, two]);
|
||||
let imul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, two]);
|
||||
|
||||
let one = program.r#const(Constant::Int(1, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||
let ishl = program.new_instruction(Operator::Ishl, Type::i32(), vec![], vec![five, one]);
|
||||
let ishl = program.new_instruction(TestOperator::Ishl, Type::i32(), vec![], vec![five, one]);
|
||||
|
||||
let new = optimizer.apply_one(&mut program, imul);
|
||||
let new = new.expect("optimization should have applied");
|
||||
assert!(program.structurally_eq(new, ishl));
|
||||
|
||||
let three = program.r#const(Constant::Int(3, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||
let imul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, three]);
|
||||
let imul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, three]);
|
||||
|
||||
let replacement = optimizer.apply_one(&mut program, imul);
|
||||
assert!(replacement.is_none());
|
||||
@@ -159,10 +159,10 @@ fn bit_width() {
|
||||
let mut program = Program::default();
|
||||
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||
let two = program.r#const(Constant::Int(2, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||
let imul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, two]);
|
||||
let imul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, two]);
|
||||
|
||||
let imul_imm = program.new_instruction(
|
||||
Operator::ImulImm,
|
||||
TestOperator::ImulImm,
|
||||
Type::i32(),
|
||||
vec![Constant::Int(5, BitWidth::ThirtyTwo).into()],
|
||||
vec![two],
|
||||
@@ -174,7 +174,7 @@ fn bit_width() {
|
||||
|
||||
let five = program.r#const(Constant::Int(5, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
||||
let two = program.r#const(Constant::Int(2, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
||||
let imul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, two]);
|
||||
let imul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, two]);
|
||||
|
||||
let replacement = optimizer.apply_one(&mut program, imul);
|
||||
assert!(replacement.is_none());
|
||||
@@ -195,10 +195,10 @@ fn fits_in_native_word() {
|
||||
let mut program = Program::default();
|
||||
let five = program.r#const(Constant::Int(5, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||
let two = program.r#const(Constant::Int(2, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||
let imul = program.new_instruction(Operator::Imul, Type::i32(), vec![], vec![five, two]);
|
||||
let imul = program.new_instruction(TestOperator::Imul, Type::i32(), vec![], vec![five, two]);
|
||||
|
||||
let imul_imm = program.new_instruction(
|
||||
Operator::ImulImm,
|
||||
TestOperator::ImulImm,
|
||||
Type::i32(),
|
||||
vec![Constant::Int(5, BitWidth::ThirtyTwo).into()],
|
||||
vec![two],
|
||||
@@ -210,7 +210,7 @@ fn fits_in_native_word() {
|
||||
|
||||
let five = program.r#const(Constant::Int(5, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
||||
let two = program.r#const(Constant::Int(2, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
||||
let imul = program.new_instruction(Operator::Imul, Type::i64(), vec![], vec![five, two]);
|
||||
let imul = program.new_instruction(TestOperator::Imul, Type::i64(), vec![], vec![five, two]);
|
||||
|
||||
let replacement = optimizer.apply_one(&mut program, imul);
|
||||
assert!(replacement.is_none());
|
||||
@@ -230,10 +230,10 @@ fn unquote_neg() {
|
||||
let mut program = Program::default();
|
||||
let five = program.r#const(Constant::Int(5, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
||||
let two = program.r#const(Constant::Int(2, BitWidth::SixtyFour), BitWidth::SixtyFour);
|
||||
let isub = program.new_instruction(Operator::Isub, Type::i64(), vec![], vec![five, two]);
|
||||
let isub = program.new_instruction(TestOperator::Isub, Type::i64(), vec![], vec![five, two]);
|
||||
|
||||
let iadd_imm = program.new_instruction(
|
||||
Operator::IaddImm,
|
||||
TestOperator::IaddImm,
|
||||
Type::i64(),
|
||||
vec![Constant::Int(-2 as _, BitWidth::SixtyFour).into()],
|
||||
vec![five],
|
||||
@@ -276,13 +276,13 @@ fn subsumption() {
|
||||
|
||||
log::debug!("(iadd (iadd (iadd w x) y) z) => (iadd (iadd w x) (iadd y z))");
|
||||
|
||||
let iadd = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![w, x]);
|
||||
let iadd = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![iadd, y]);
|
||||
let iadd = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![iadd, z]);
|
||||
let expected_lhs = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![w, x]);
|
||||
let expected_rhs = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![y, z]);
|
||||
let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![w, x]);
|
||||
let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![iadd, y]);
|
||||
let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![iadd, z]);
|
||||
let expected_lhs = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![w, x]);
|
||||
let expected_rhs = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![y, z]);
|
||||
let expected = program.new_instruction(
|
||||
Operator::Iadd,
|
||||
TestOperator::Iadd,
|
||||
Type::i64(),
|
||||
vec![],
|
||||
vec![expected_lhs, expected_rhs],
|
||||
@@ -294,17 +294,17 @@ fn subsumption() {
|
||||
|
||||
log::debug!("(iadd w x) => y");
|
||||
|
||||
let iadd = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![w, x]);
|
||||
let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![w, x]);
|
||||
let new = optimizer.apply_one(&mut program, iadd);
|
||||
let new = new.expect("optimization should have applied");
|
||||
assert!(program.structurally_eq(new, y));
|
||||
|
||||
log::debug!("(iadd x (iadd y z)) => (iadd_imm x (iadd y z))");
|
||||
|
||||
let iadd_y_z = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![y, z]);
|
||||
let iadd = program.new_instruction(Operator::Iadd, Type::i64(), vec![], vec![x, iadd_y_z]);
|
||||
let iadd_y_z = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![y, z]);
|
||||
let iadd = program.new_instruction(TestOperator::Iadd, Type::i64(), vec![], vec![x, iadd_y_z]);
|
||||
let iadd_imm = program.new_instruction(
|
||||
Operator::IaddImm,
|
||||
TestOperator::IaddImm,
|
||||
Type::i64(),
|
||||
vec![Constant::Int(22, BitWidth::SixtyFour).into()],
|
||||
vec![iadd_y_z],
|
||||
@@ -316,19 +316,19 @@ fn subsumption() {
|
||||
log::debug!("(iadd (imul_imm x 1) (imul_imm x 1)) => (ishl_imm 1 (imul_imm x 1))");
|
||||
|
||||
let imul_imm = program.new_instruction(
|
||||
Operator::ImulImm,
|
||||
TestOperator::ImulImm,
|
||||
Type::i64(),
|
||||
vec![Constant::Int(1, BitWidth::SixtyFour).into()],
|
||||
vec![x],
|
||||
);
|
||||
let iadd = program.new_instruction(
|
||||
Operator::Iadd,
|
||||
TestOperator::Iadd,
|
||||
Type::i64(),
|
||||
vec![],
|
||||
vec![imul_imm, imul_imm],
|
||||
);
|
||||
let ishl_imm = program.new_instruction(
|
||||
Operator::IshlImm,
|
||||
TestOperator::IshlImm,
|
||||
Type::i64(),
|
||||
vec![Constant::Int(1, BitWidth::SixtyFour).into()],
|
||||
vec![imul_imm],
|
||||
@@ -339,10 +339,10 @@ fn subsumption() {
|
||||
|
||||
log::debug!("(iadd (imul w x) (imul y z)) does not match any optimization.");
|
||||
|
||||
let imul_w_x = program.new_instruction(Operator::Imul, Type::i64(), vec![], vec![w, x]);
|
||||
let imul_y_z = program.new_instruction(Operator::Imul, Type::i64(), vec![], vec![y, z]);
|
||||
let imul_w_x = program.new_instruction(TestOperator::Imul, Type::i64(), vec![], vec![w, x]);
|
||||
let imul_y_z = program.new_instruction(TestOperator::Imul, Type::i64(), vec![], vec![y, z]);
|
||||
let iadd = program.new_instruction(
|
||||
Operator::Iadd,
|
||||
TestOperator::Iadd,
|
||||
Type::i64(),
|
||||
vec![],
|
||||
vec![imul_w_x, imul_y_z],
|
||||
@@ -363,9 +363,9 @@ fn polymorphic_bit_widths() {
|
||||
|
||||
let x = program.r#const(Constant::Int(42, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||
let y = program.r#const(Constant::Int(420, BitWidth::ThirtyTwo), BitWidth::ThirtyTwo);
|
||||
let iadd = program.new_instruction(Operator::Iadd, Type::i32(), vec![], vec![x, y]);
|
||||
let iadd = program.new_instruction(TestOperator::Iadd, Type::i32(), vec![], vec![x, y]);
|
||||
let iadd_imm = program.new_instruction(
|
||||
Operator::IaddImm,
|
||||
TestOperator::IaddImm,
|
||||
Type::i32(),
|
||||
vec![Constant::Int(42, BitWidth::ThirtyTwo).into()],
|
||||
vec![y],
|
||||
@@ -379,9 +379,9 @@ fn polymorphic_bit_widths() {
|
||||
|
||||
let x = program.r#const(Constant::Int(42, BitWidth::Sixteen), BitWidth::Sixteen);
|
||||
let y = program.r#const(Constant::Int(420, BitWidth::Sixteen), BitWidth::Sixteen);
|
||||
let iadd = program.new_instruction(Operator::Iadd, Type::i16(), vec![], vec![x, y]);
|
||||
let iadd = program.new_instruction(TestOperator::Iadd, Type::i16(), vec![], vec![x, y]);
|
||||
let iadd_imm = program.new_instruction(
|
||||
Operator::IaddImm,
|
||||
TestOperator::IaddImm,
|
||||
Type::i16(),
|
||||
vec![Constant::Int(42, BitWidth::Sixteen).into()],
|
||||
vec![y],
|
||||
|
||||
9
cranelift/peepmatic/crates/traits/Cargo.toml
Normal file
9
cranelift/peepmatic/crates/traits/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "peepmatic-traits"
|
||||
version = "0.66.0"
|
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
26
cranelift/peepmatic/crates/traits/src/lib.rs
Normal file
26
cranelift/peepmatic/crates/traits/src/lib.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
//! Shared traits, types, and macros for Peepmatic.
|
||||
//!
|
||||
//! This crate is used both at build time when constructing peephole optimizers
|
||||
//! (i.e. in the `peepmatic` crate), and at run time when using pre-built
|
||||
//! peephole optimizers (i.e. in the `peepmatic-runtime` crate and in
|
||||
//! Cranelift's Peepmatic integration at `cranelift/codegen/src/peepmatic.rs`).
|
||||
//!
|
||||
//! This crate is similar to a header file: it should generally only contain
|
||||
//! trait/type/macro definitions, not any code.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![deny(missing_debug_implementations)]
|
||||
|
||||
#[macro_use]
|
||||
mod operator;
|
||||
pub use operator::*;
|
||||
|
||||
mod typing;
|
||||
pub use typing::*;
|
||||
|
||||
/// Raise a panic about an unsupported operation.
|
||||
#[cold]
|
||||
#[inline(never)]
|
||||
pub fn unsupported(msg: &str) -> ! {
|
||||
panic!("unsupported: {}", msg)
|
||||
}
|
||||
317
cranelift/peepmatic/crates/traits/src/operator.rs
Normal file
317
cranelift/peepmatic/crates/traits/src/operator.rs
Normal file
@@ -0,0 +1,317 @@
|
||||
/// 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); )?
|
||||
}
|
||||
}
|
||||
}
|
||||
97
cranelift/peepmatic/crates/traits/src/typing.rs
Normal file
97
cranelift/peepmatic/crates/traits/src/typing.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
/// A trait to represent a typing context.
|
||||
///
|
||||
/// This is used by the macro-generated operator methods that create the type
|
||||
/// variables for their immediates, parameters, and results. This trait is
|
||||
/// implemented by the concrete typing context in `peepmatic/src/verify.rs`.
|
||||
pub trait TypingContext<'a> {
|
||||
/// A source span.
|
||||
type Span: Copy;
|
||||
|
||||
/// A type variable.
|
||||
type TypeVariable;
|
||||
|
||||
/// Create a condition code type.
|
||||
fn cc(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a boolean type with a polymorphic bit width.
|
||||
///
|
||||
/// Each use of `bNN` by the same operator refers to the same type variable.
|
||||
#[allow(non_snake_case)]
|
||||
fn bNN(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create an integer type with a polymorphic bit width.
|
||||
///
|
||||
/// Each use of `iNN` by the same operator refers to the same type variable.
|
||||
#[allow(non_snake_case)]
|
||||
fn iNN(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create an integer type with a polymorphic bit width.
|
||||
///
|
||||
/// Each use of `iMM` by the same operator refers to the same type variable.
|
||||
#[allow(non_snake_case)]
|
||||
fn iMM(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create the CPU flags type variable.
|
||||
fn cpu_flags(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a boolean type of size one bit.
|
||||
fn b1(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create the void type, used as the result of operators that branch away,
|
||||
/// or do not return anything.
|
||||
fn void(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a type variable that may be either a boolean or an integer.
|
||||
fn bool_or_int(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
|
||||
/// Create a type variable that can be any type T.
|
||||
///
|
||||
/// Each use of `any_t` by the same operator refers to the same type
|
||||
/// variable.
|
||||
fn any_t(&mut self, span: Self::Span) -> Self::TypeVariable;
|
||||
}
|
||||
|
||||
/// The typing rules for a `TOperator` type.
|
||||
///
|
||||
/// This trait describes the types of immediates, parameters, and results of an
|
||||
/// operator type, as well as their arity.
|
||||
pub trait TypingRules {
|
||||
/// Get the result type of this operator.
|
||||
fn result_type<'a, C>(&self, span: C::Span, typing_context: &mut C) -> C::TypeVariable
|
||||
where
|
||||
C: TypingContext<'a>;
|
||||
|
||||
/// Get the number of immediates this operator has.
|
||||
fn immediates_arity(&self) -> u8;
|
||||
|
||||
/// Get the types of this operator's immediates.
|
||||
fn immediate_types<'a, C>(
|
||||
&self,
|
||||
span: C::Span,
|
||||
typing_context: &mut C,
|
||||
types: &mut impl Extend<C::TypeVariable>,
|
||||
) where
|
||||
C: TypingContext<'a>;
|
||||
|
||||
/// Get the number of parameters this operator has.
|
||||
fn parameters_arity(&self) -> u8;
|
||||
|
||||
/// Get the types of this operator's parameters.
|
||||
fn parameter_types<'a, C>(
|
||||
&self,
|
||||
span: C::Span,
|
||||
typing_context: &mut C,
|
||||
types: &mut impl Extend<C::TypeVariable>,
|
||||
) where
|
||||
C: TypingContext<'a>;
|
||||
|
||||
/// Is this a bit width reducing instruction?
|
||||
///
|
||||
/// E.g. Cranelift's `ireduce` instruction.
|
||||
fn is_reduce(&self) -> bool;
|
||||
|
||||
/// Is this a bit width extending instruction?
|
||||
///
|
||||
/// E.g. Cranelift's `uextend` and `sextend` instructions.
|
||||
fn is_extend(&self) -> bool;
|
||||
}
|
||||
Reference in New Issue
Block a user