From dd3048f622c10a536eb32ebf046e395b76f966a6 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 7 Sep 2024 16:52:53 +0200 Subject: [PATCH 01/26] Define trait --- crates/ir/src/inst.rs | 8 ++++++++ crates/ir/src/lib.rs | 1 + 2 files changed, 9 insertions(+) create mode 100644 crates/ir/src/inst.rs diff --git a/crates/ir/src/inst.rs b/crates/ir/src/inst.rs new file mode 100644 index 00000000..5deb8739 --- /dev/null +++ b/crates/ir/src/inst.rs @@ -0,0 +1,8 @@ +use crate::Value; + +pub trait Inst { + fn args(&self) -> &[Value]; + fn args_mut(&mut self) -> &mut [Value]; + fn has_side_effect(&self) -> bool; + fn as_str(&self) -> &'static str; +} diff --git a/crates/ir/src/lib.rs b/crates/ir/src/lib.rs index d36813f2..a3b84940 100644 --- a/crates/ir/src/lib.rs +++ b/crates/ir/src/lib.rs @@ -6,6 +6,7 @@ pub mod function; pub mod global_variable; pub mod graphviz; pub mod insn; +pub mod inst; pub mod ir_writer; pub mod isa; pub mod layout; From 2a9da447b515f6e06a23d41922f5e07dc6c6ef6f Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 7 Sep 2024 16:55:50 +0200 Subject: [PATCH 02/26] Implement a macro to provide an easy way to define `Inst` types --- crates/ir/src/inst.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/crates/ir/src/inst.rs b/crates/ir/src/inst.rs index 5deb8739..8be3e220 100644 --- a/crates/ir/src/inst.rs +++ b/crates/ir/src/inst.rs @@ -6,3 +6,43 @@ pub trait Inst { fn has_side_effect(&self) -> bool; fn as_str(&self) -> &'static str; } + +macro_rules! define_inst { + ($(($purity:ident, $ty:ident, $name:literal, $arity:literal)),* $(,)?) => { + $( + define_inst!($purity, $ty, $name, $arity); + )* + }; + + ($purity: ident, $ty: ident, $name:literal, $arity:literal) => { + pub struct $ty { + args: [Value; $arity], + } + + impl Inst for $ty { + fn args(&self) -> &[Value] { + &self.args + } + + fn args_mut(&mut self) -> &mut [Value] { + &mut self.args + } + + fn has_side_effect(&self) -> bool { + define_inst!(has_side_effect_impl $purity) + } + + fn as_str(&self) -> &'static str { + $name + } + } + }; + + (has_side_effect_impl pure) => { + true + }; + + (has_side_effect_impl non_pure) => { + true + }; +} From c40008efa8f30f94277b5bcb4c29d0389878e5a5 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sat, 7 Sep 2024 19:06:43 +0200 Subject: [PATCH 03/26] Define basic instructions --- crates/ir/src/inst.rs | 283 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 270 insertions(+), 13 deletions(-) diff --git a/crates/ir/src/inst.rs b/crates/ir/src/inst.rs index 8be3e220..6b3fc391 100644 --- a/crates/ir/src/inst.rs +++ b/crates/ir/src/inst.rs @@ -1,8 +1,10 @@ -use crate::Value; +use smallvec::SmallVec; + +use crate::{module::FuncRef, Block, Type, Value}; pub trait Inst { - fn args(&self) -> &[Value]; - fn args_mut(&mut self) -> &mut [Value]; + fn visit_values(&self, f: &mut dyn FnMut(Value)); + fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)); fn has_side_effect(&self) -> bool; fn as_str(&self) -> &'static str; } @@ -15,21 +17,51 @@ macro_rules! define_inst { }; ($purity: ident, $ty: ident, $name:literal, $arity:literal) => { + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub struct $ty { + args: define_inst!(__arg_ty $arity), + } + + impl Inst for $ty { + fn visit_values(&self, f: &mut dyn FnMut(Value)) { + for &v in &self.args { + f(v) + } + } + + fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { + for v in &mut self.args { + f(v) + } + } + + fn has_side_effect(&self) -> bool { + define_inst!(__has_side_effect_impl $purity) + } + + fn as_str(&self) -> &'static str { + $name + } + } + }; + + ($purity: ident, $ty: ident, $name:literal, $arity:literal) => { + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct $ty { - args: [Value; $arity], + pub args: define_inst!(__arg_ty $arity), } impl Inst for $ty { - fn args(&self) -> &[Value] { - &self.args + fn visit_values(&self, f: &mut dyn FnMut(Value)) { + self.args.iter().copied().for_each(f) } - fn args_mut(&mut self) -> &mut [Value] { - &mut self.args + fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { + self.args.iter_mut().for_each(f) } fn has_side_effect(&self) -> bool { - define_inst!(has_side_effect_impl $purity) + define_inst!(__has_side_effect_impl $purity) } fn as_str(&self) -> &'static str { @@ -38,11 +70,236 @@ macro_rules! define_inst { } }; - (has_side_effect_impl pure) => { + (__arg_ty 1) => { + Value + }; + + (__arg_ty $arity:literal) => { + [Value; $arity] + }; + + (__has_side_effect_impl pure) => { true - }; + }; - (has_side_effect_impl non_pure) => { + (__has_side_effect_impl non_pure) => { true - }; + }; +} + +define_inst! { + (pure, Not, "not", 1), + (pure, Neg, "neg", 1), + // Arithmetic instructions. + (pure, Add, "add", 2), + (pure, Mul, "mul", 2), + (pure, Sub, "sub", 2), + (non_pure, Sdiv, "sdiv", 2), + (non_pure, Udiv, "udiv", 2), + // Comp instructions. + (pure, Lt, "lt", 2), + (pure, Gt, "gt", 2), + (pure, Slt, "slt", 2), + (pure, Sgt, "sgt", 2), + (pure, Le, "le", 2), + (pure, Ge, "ge", 2), + (pure, Sle, "sle", 2), + (pure, Sge, "sge", 2), + (pure, Eq, "eq", 2), + (pure, Ne, "ne", 2), + (pure, And, "And", 2), + (pure, Or, "Or", 2), + (pure, Xor, "xor", 2), + // Cast instructions. + (pure, Sext, "sext", 2), + (pure, Zext, "zext", 2), + (pure, Trunc, "trunc", 2), + (pure, BitCast, "bitcast", 2), + // Memory operations. + (non_pure, Mload, "mload", 1), + (non_pure, Mstore, "mstore", 2), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Call { + pub args: SmallVec<[Value; 8]>, + pub callee: FuncRef, + pub ret_ty: Type, +} + +impl Inst for Call { + fn visit_values(&self, f: &mut dyn FnMut(Value)) { + self.args.iter().copied().for_each(f) + } + + fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { + self.args.iter_mut().for_each(f) + } + + fn has_side_effect(&self) -> bool { + // TODO: We need to add funciton attribute that can specify a purity of function. + true + } + + fn as_str(&self) -> &'static str { + "call" + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Jump { + pub dest: Block, +} +impl Inst for Jump { + fn visit_values(&self, _f: &mut dyn FnMut(Value)) {} + + fn visit_values_mut(&mut self, _f: &mut dyn FnMut(&mut Value)) {} + + fn has_side_effect(&self) -> bool { + false + } + + fn as_str(&self) -> &'static str { + "jump" + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Br { + pub cond: Value, + pub z_dest: Block, + pub nz_dest: Block, +} +impl Inst for Br { + fn visit_values(&self, f: &mut dyn FnMut(Value)) { + f(self.cond) + } + + fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { + f(&mut self.cond) + } + + fn has_side_effect(&self) -> bool { + false + } + + fn as_str(&self) -> &'static str { + "br" + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BrTable { + pub scrutinee: Value, + pub table: Vec<(Value, Block)>, + pub default: Option, +} + +impl Inst for BrTable { + fn visit_values(&self, f: &mut dyn FnMut(Value)) { + f(self.scrutinee); + self.table.iter().for_each(|(v, _)| f(*v)) + } + + fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { + f(&mut self.scrutinee); + self.table.iter_mut().for_each(|(v, _)| f(v)) + } + + fn has_side_effect(&self) -> bool { + false + } + + fn as_str(&self) -> &'static str { + "br_table" + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Alloca { + pub ty: Type, +} +impl Inst for Alloca { + fn visit_values(&self, _f: &mut dyn FnMut(Value)) {} + + fn visit_values_mut(&mut self, _f: &mut dyn FnMut(&mut Value)) {} + + fn has_side_effect(&self) -> bool { + true + } + + fn as_str(&self) -> &'static str { + "alloca" + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Return { + pub arg: Option, +} +impl Inst for Return { + fn visit_values(&self, f: &mut dyn FnMut(Value)) { + if let Some(v) = self.arg { + f(v) + } + } + + fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { + if let Some(v) = self.arg.as_mut() { + f(v) + } + } + + fn has_side_effect(&self) -> bool { + true + } + + fn as_str(&self) -> &'static str { + "return" + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Gep { + values: SmallVec<[Value; 8]>, +} +impl Inst for Gep { + fn visit_values(&self, f: &mut dyn FnMut(Value)) { + self.values.iter().copied().for_each(f) + } + + fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { + self.values.iter_mut().for_each(f) + } + + fn has_side_effect(&self) -> bool { + false + } + + fn as_str(&self) -> &'static str { + "gep" + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Phi { + pub values: Vec<(Value, Block)>, + pub ty: Type, +} +impl Inst for Phi { + fn visit_values(&self, f: &mut dyn FnMut(Value)) { + self.values.iter().for_each(|(v, _)| f(*v)) + } + + fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { + self.values.iter_mut().for_each(|(v, _)| f(v)) + } + + fn has_side_effect(&self) -> bool { + true + } + + fn as_str(&self) -> &'static str { + "phi" + } } From 38d61d9b9b928fb12104759a89b5c5ea363e93d0 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Sun, 8 Sep 2024 22:03:06 +0200 Subject: [PATCH 04/26] `Define HasInst` as a bulding block of IsaGroup --- crates/ir/src/inst.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/crates/ir/src/inst.rs b/crates/ir/src/inst.rs index 6b3fc391..c4be79ad 100644 --- a/crates/ir/src/inst.rs +++ b/crates/ir/src/inst.rs @@ -1,14 +1,24 @@ +use std::any::{Any, TypeId}; + use smallvec::SmallVec; use crate::{module::FuncRef, Block, Type, Value}; -pub trait Inst { +pub trait Inst: Any { fn visit_values(&self, f: &mut dyn FnMut(Value)); fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)); fn has_side_effect(&self) -> bool; fn as_str(&self) -> &'static str; } +/// This trait works as a "proof" that a specific ISA contains `I`, +/// and then allows a construction and reflection of type `I` in that specific ISA context. +pub trait HasInst { + fn is(&self, inst: &dyn Inst) -> bool { + inst.type_id() == TypeId::of::() + } +} + macro_rules! define_inst { ($(($purity:ident, $ty:ident, $name:literal, $arity:literal)),* $(,)?) => { $( From dac7644e0df6b848c769752bbb6b76bf65806713 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 9 Sep 2024 13:34:40 +0200 Subject: [PATCH 05/26] Define `Inst` derive proc macro --- Cargo.toml | 1 + crates/macros/Cargo.toml | 19 ++++++ crates/macros/src/lib.rs | 129 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 crates/macros/Cargo.toml create mode 100644 crates/macros/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index cf02c73c..99947b3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,6 @@ members = [ "crates/parser", "crates/filecheck", "crates/triple", + "crates/macros", "crates/interpreter", ] diff --git a/crates/macros/Cargo.toml b/crates/macros/Cargo.toml new file mode 100644 index 00000000..408690ef --- /dev/null +++ b/crates/macros/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "sonatina-macros" +version = "0.0.3-alpha" +edition = "2021" +authors = ["Sonatina Developers"] +license = "Apache-2.0" +readme = "../../README.md" +homepage = "https://github.com/fe-lang/sonatina/tree/main/crates/ir" +repository = "https://github.com/fe-lang/sonatina" +categories = ["compilers", "wasm"] +keywords = ["compiler", "evm", "wasm", "smart-contract"] + +[lib] +proc_macro = true + +[dependencies] +syn = { version = "1.0", features = ["full"] } +proc-macro2 = "1.0" +quote = "1.0" diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs new file mode 100644 index 00000000..6aa09577 --- /dev/null +++ b/crates/macros/src/lib.rs @@ -0,0 +1,129 @@ +use quote::quote; + +#[proc_macro_derive(Inst)] +pub fn derive_inst(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let item_struct = syn::parse_macro_input!(item as syn::ItemStruct); + + match InstBuilder::new(item_struct).and_then(InstBuilder::build) { + Ok(impls) => quote! { + #impls + } + .into(), + + Err(e) => e.to_compile_error().into(), + } +} + +struct InstBuilder { + struct_name: syn::Ident, + fields: syn::FieldsNamed, +} + +impl InstBuilder { + fn new(item_struct: syn::ItemStruct) -> syn::Result { + let struct_ident = item_struct.ident; + let syn::Fields::Named(fields) = item_struct.fields else { + return Err(syn::Error::new_spanned( + item_struct.fields, + "tuple struct is not allowed for inst types", + )); + }; + + if item_struct.generics.lt_token.is_some() { + Err(syn::Error::new_spanned( + item_struct.generics, + "generics is not allowed for inst types", + )) + } else { + Ok(Self { + struct_name: struct_ident, + fields, + }) + } + } + + fn build(self) -> syn::Result { + let mut fields = Vec::with_capacity(self.fields.named.len()); + + for f in &self.fields.named { + if !matches!(f.vis, syn::Visibility::Inherited) { + return Err(syn::Error::new_spanned( + &f.vis, + "all members of inst types should be private", + )); + }; + + fields.push((f.ident.clone().unwrap(), f.ty.clone())); + } + + let ctor = self.make_ctor(&fields); + let accessors = self.make_accessors(&fields); + let cast_fn = self.make_cast_fn(); + + let struct_name = &self.struct_name; + Ok(quote! { + impl #struct_name { + #ctor + + #accessors + + #cast_fn + } + }) + } + + fn make_ctor(&self, fields: &[(syn::Ident, syn::Type)]) -> proc_macro2::TokenStream { + let ctor_args = fields.iter().map(|(ident, ty)| quote! {#ident: #ty}); + let field_names = fields.iter().map(|(ident, _)| ident); + quote! { + pub fn new(hi: &dyn crate::HasInst, #(#ctor_args),*) -> Self { + Self { + #(#field_names: #field_names),* + } + } + } + } + + fn make_cast_fn(&self) -> proc_macro2::TokenStream { + quote! { + pub fn cast<'i>(hi: &dyn crate::HasInst, inst: &'i dyn Inst) -> Option<&'i Self> { + if hi.is(inst) { + unsafe { Some(&*(inst as *const dyn Inst as *const Self)) } + } else { + None + } + } + + pub fn cast_mut<'i>( + hi: &dyn HasInst, + inst: &'i mut dyn Inst, + ) -> Option<&'i mut Self> { + if hi.is(inst) { + unsafe { Some(&mut *(inst as *mut dyn Inst as *mut Self)) } + } else { + None + } + } + } + } + + fn make_accessors(&self, fields: &[(syn::Ident, syn::Type)]) -> proc_macro2::TokenStream { + let accessors = fields.iter().map(|(ident, ty)| { + let getter = quote::format_ident!("{ident}"); + let get_mut = quote::format_ident!("{ident}_mut"); + quote! { + pub fn #getter(&self) -> &#ty { + &self.#ident + } + + pub fn #get_mut(&mut self) -> &mut #ty{ + &mut self.#ident + } + } + }); + + quote! { + #(#accessors)* + } + } +} From 2271f8029c4886a7f19fc0450b1349a23f27526d Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 9 Sep 2024 15:30:47 +0200 Subject: [PATCH 06/26] Define `ValueVisitable` trait --- crates/ir/src/inst.rs | 74 +++++++++++++++++++++++++++++++++++++++++-- crates/ir/src/lib.rs | 1 + 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/crates/ir/src/inst.rs b/crates/ir/src/inst.rs index c4be79ad..0fe32dd1 100644 --- a/crates/ir/src/inst.rs +++ b/crates/ir/src/inst.rs @@ -275,7 +275,7 @@ pub struct Gep { } impl Inst for Gep { fn visit_values(&self, f: &mut dyn FnMut(Value)) { - self.values.iter().copied().for_each(f) + self.values.visit_with(f) } fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { @@ -298,11 +298,11 @@ pub struct Phi { } impl Inst for Phi { fn visit_values(&self, f: &mut dyn FnMut(Value)) { - self.values.iter().for_each(|(v, _)| f(*v)) + self.values.visit_with(f) } fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { - self.values.iter_mut().for_each(|(v, _)| f(v)) + self.values.visit_mut_with(f) } fn has_side_effect(&self) -> bool { @@ -313,3 +313,71 @@ impl Inst for Phi { "phi" } } + +pub(crate) trait ValueVisitable { + fn visit_with(&self, f: &mut dyn FnMut(Value)); + fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)); +} + +impl ValueVisitable for Value { + fn visit_with(&self, f: &mut dyn FnMut(Value)) { + f(*self) + } + + fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { + f(self) + } +} + +impl ValueVisitable for Option { + fn visit_with(&self, f: &mut dyn FnMut(Value)) { + if let Some(value) = *self { + f(value) + } + } + + fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { + if let Some(value) = self.as_mut() { + f(value) + } + } +} + +impl ValueVisitable for (V, T) +where + V: ValueVisitable, +{ + fn visit_with(&self, f: &mut dyn FnMut(Value)) { + self.0.visit_with(f) + } + + fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { + self.0.visit_mut_with(f) + } +} + +impl ValueVisitable for Vec +where + V: ValueVisitable, +{ + fn visit_with(&self, f: &mut dyn FnMut(Value)) { + self.iter().for_each(|v| v.visit_with(f)) + } + + fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { + self.iter_mut().for_each(|v| v.visit_mut_with(f)) + } +} + +impl ValueVisitable for [V] +where + V: ValueVisitable, +{ + fn visit_with(&self, f: &mut dyn FnMut(Value)) { + self.iter().for_each(|v| v.visit_with(f)) + } + + fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { + self.iter_mut().for_each(|v| v.visit_mut_with(f)) + } +} diff --git a/crates/ir/src/lib.rs b/crates/ir/src/lib.rs index a3b84940..c409caa7 100644 --- a/crates/ir/src/lib.rs +++ b/crates/ir/src/lib.rs @@ -25,6 +25,7 @@ pub use function::{Function, Signature}; pub use global_variable::{GlobalVariable, GlobalVariableData}; pub use graphviz::render_to; pub use insn::{BranchInfo, DataLocationKind, Insn, InsnData}; +pub use inst::HasInst; pub use layout::Layout; pub use linkage::Linkage; pub use module::Module; From c7b682641f3e602402b94b2bdc2baceeea8c3b25 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 9 Sep 2024 16:43:18 +0200 Subject: [PATCH 07/26] Improve `Inst` derive macro --- crates/macros/src/lib.rs | 236 ++++++++++++++++++++++++++++++++------- 1 file changed, 193 insertions(+), 43 deletions(-) diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs index 6aa09577..3c421bfe 100644 --- a/crates/macros/src/lib.rs +++ b/crates/macros/src/lib.rs @@ -1,10 +1,10 @@ use quote::quote; -#[proc_macro_derive(Inst)] +#[proc_macro_derive(Inst, attributes(inst))] pub fn derive_inst(item: proc_macro::TokenStream) -> proc_macro::TokenStream { let item_struct = syn::parse_macro_input!(item as syn::ItemStruct); - match InstBuilder::new(item_struct).and_then(InstBuilder::build) { + match InstStruct::new(item_struct).and_then(InstStruct::build) { Ok(impls) => quote! { #impls } @@ -14,53 +14,124 @@ pub fn derive_inst(item: proc_macro::TokenStream) -> proc_macro::TokenStream { } } -struct InstBuilder { +struct InstStruct { struct_name: syn::Ident, - fields: syn::FieldsNamed, + has_side_effect: bool, + fields: Vec, } -impl InstBuilder { +struct InstField { + ident: syn::Ident, + ty: syn::Type, + visit_value: bool, +} + +impl InstStruct { fn new(item_struct: syn::ItemStruct) -> syn::Result { + let has_side_effect = Self::check_side_effect_attr(&item_struct)?; + let struct_ident = item_struct.ident; - let syn::Fields::Named(fields) = item_struct.fields else { - return Err(syn::Error::new_spanned( - item_struct.fields, - "tuple struct is not allowed for inst types", - )); - }; + + let fields = Self::parse_fields(&item_struct.fields)?; if item_struct.generics.lt_token.is_some() { - Err(syn::Error::new_spanned( + return Err(syn::Error::new_spanned( item_struct.generics, "generics is not allowed for inst types", - )) - } else { - Ok(Self { - struct_name: struct_ident, - fields, - }) + )); } + + Ok(Self { + struct_name: struct_ident, + has_side_effect, + fields, + }) } - fn build(self) -> syn::Result { - let mut fields = Vec::with_capacity(self.fields.named.len()); + fn check_side_effect_attr(item_struct: &syn::ItemStruct) -> syn::Result { + let mut has_side_effect = None; + + for attr in &item_struct.attrs { + if attr.path.is_ident("inst") { + let meta = attr.parse_args::()?; + if let syn::Meta::NameValue(name_value) = meta { + if name_value.path.is_ident("side_effect") { + if has_side_effect.is_some() { + return Err(syn::Error::new_spanned( + item_struct, + "only one #[inst(side_effect = ...)]` attribute is allowed", + )); + } + + if let syn::Lit::Bool(bool_lit) = name_value.lit { + has_side_effect = Some(bool_lit.value); + } + } + } + } + } + + has_side_effect.ok_or_else(|| { + syn::Error::new_spanned( + item_struct, + "unique #[inst(side_effect = ...)]` attributed is required", + ) + }) + } - for f in &self.fields.named { - if !matches!(f.vis, syn::Visibility::Inherited) { + fn parse_fields(fields: &syn::Fields) -> syn::Result> { + let syn::Fields::Named(fields) = fields else { + return Err(syn::Error::new_spanned( + fields, + "tuple struct is not allowed for inst types", + )); + }; + + let mut inst_fields = Vec::new(); + + for field in &fields.named { + let mut visit_value = false; + + if !matches!(field.vis, syn::Visibility::Inherited) { return Err(syn::Error::new_spanned( - &f.vis, - "all members of inst types should be private", + field, + "public visibility is not allowed", )); - }; + } + + for attr in &field.attrs { + if attr.path.is_ident("inst") { + let meta = attr.parse_args::()?; + if let syn::Meta::Path(path) = meta { + if path.is_ident("visit_value") { + visit_value = true; + } else { + return Err(syn::Error::new_spanned( + attr, + "only `visit_value` is allowed", + )); + } + } + } + } - fields.push((f.ident.clone().unwrap(), f.ty.clone())); + inst_fields.push(InstField { + ident: field.ident.clone().unwrap(), + ty: field.ty.clone(), + visit_value, + }); } - let ctor = self.make_ctor(&fields); - let accessors = self.make_accessors(&fields); + Ok(inst_fields) + } + + fn build(self) -> syn::Result { + let ctor = self.make_ctor(); + let accessors = self.make_accessors(); let cast_fn = self.make_cast_fn(); let struct_name = &self.struct_name; + let impl_inst = self.impl_inst(); Ok(quote! { impl #struct_name { #ctor @@ -69,12 +140,19 @@ impl InstBuilder { #cast_fn } + + #impl_inst }) } - fn make_ctor(&self, fields: &[(syn::Ident, syn::Type)]) -> proc_macro2::TokenStream { - let ctor_args = fields.iter().map(|(ident, ty)| quote! {#ident: #ty}); - let field_names = fields.iter().map(|(ident, _)| ident); + fn make_ctor(&self) -> proc_macro2::TokenStream { + let ctor_args = self.fields.iter().map(|f| { + let ident = &f.ident; + let ty = &f.ty; + quote! {#ident: #ty} + }); + + let field_names = self.fields.iter().map(|f| &f.ident); quote! { pub fn new(hi: &dyn crate::HasInst, #(#ctor_args),*) -> Self { Self { @@ -84,6 +162,28 @@ impl InstBuilder { } } + fn make_accessors(&self) -> proc_macro2::TokenStream { + let accessors = self.fields.iter().map(|f| { + let ident = &f.ident; + let ty = &f.ty; + let getter = quote::format_ident!("{ident}"); + let get_mut = quote::format_ident!("{ident}_mut"); + quote! { + pub fn #getter(&self) -> &#ty { + &self.#ident + } + + pub fn #get_mut(&mut self) -> &mut #ty{ + &mut self.#ident + } + } + }); + + quote! { + #(#accessors)* + } + } + fn make_cast_fn(&self) -> proc_macro2::TokenStream { quote! { pub fn cast<'i>(hi: &dyn crate::HasInst, inst: &'i dyn Inst) -> Option<&'i Self> { @@ -107,23 +207,73 @@ impl InstBuilder { } } - fn make_accessors(&self, fields: &[(syn::Ident, syn::Type)]) -> proc_macro2::TokenStream { - let accessors = fields.iter().map(|(ident, ty)| { - let getter = quote::format_ident!("{ident}"); - let get_mut = quote::format_ident!("{ident}_mut"); - quote! { - pub fn #getter(&self) -> &#ty { - &self.#ident + fn impl_inst(&self) -> proc_macro2::TokenStream { + let struct_name = &self.struct_name; + let has_side_effect = self.has_side_effect; + let visit_fields: Vec<_> = self + .fields + .iter() + .filter(|f| f.visit_value) + .map(|f| &f.ident) + .collect(); + let text_form = convert_to_snake(&self.struct_name.to_string()); + + quote! { + impl crate::Inst for #struct_name { + fn visit_values(&self, f: &mut dyn FnMut(crate::Value)) { + #(crate::ValueVisitable::visit_with(&self.#visit_fields, (f));)* } - pub fn #get_mut(&mut self) -> &mut #ty{ - &mut self.#ident + fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut crate::Value)) { + #(crate::ValueVisitable::visit_mut_with(&mut self.#visit_fields, (f));)* + } + + fn has_side_effect(&self) -> bool { + #has_side_effect + } + + fn as_text(&self) -> &'static str { + #text_form } } - }); + } + } +} - quote! { - #(#accessors)* +fn convert_to_snake(s: &str) -> String { + let mut res = String::new(); + let mut is_upper = false; + for (i, c) in s.chars().enumerate() { + if c.is_uppercase() { + if !is_upper && i != 0 { + res.push('_'); + } + is_upper = true; + } else { + is_upper = false; } + + res.push(c.to_ascii_lowercase()); + } + + res +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_convert_to_snake() { + let snake = "foo_bar_baz"; + assert_eq!(convert_to_snake("FooBarBaz"), snake); + assert_eq!(convert_to_snake("FOoBarBaz"), snake); + assert_eq!(convert_to_snake("FOoBArBAZ"), snake); + } + + #[test] + fn test_convert_to_snake2() { + let snake = "foo"; + assert_eq!(convert_to_snake("Foo"), snake); } } From 327ac6107c8c1c6e9eb5c72f26e89f9fb173baa4 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 9 Sep 2024 17:50:20 +0200 Subject: [PATCH 08/26] Use `Inst` derive macro --- crates/ir/Cargo.toml | 1 + crates/ir/src/inst.rs | 509 +++++++++++++++++++++--------------------- crates/ir/src/lib.rs | 4 +- 3 files changed, 264 insertions(+), 250 deletions(-) diff --git a/crates/ir/Cargo.toml b/crates/ir/Cargo.toml index 0d6789d5..233d60c5 100644 --- a/crates/ir/Cargo.toml +++ b/crates/ir/Cargo.toml @@ -20,5 +20,6 @@ smallvec = "1.7.0" rustc-hash = "2.0.0" dyn-clone = "1.0.4" sonatina-triple = { path = "../triple", version = "0.0.3-alpha" } +sonatina-macros = { path = "../macros", version = "0.0.3-alpha" } indexmap = "2.0.0" dot2 = { git = "https://github.com/sanpii/dot2.rs.git" } diff --git a/crates/ir/src/inst.rs b/crates/ir/src/inst.rs index 0fe32dd1..9a52fc19 100644 --- a/crates/ir/src/inst.rs +++ b/crates/ir/src/inst.rs @@ -1,3 +1,4 @@ +use sonatina_macros::Inst; use std::any::{Any, TypeId}; use smallvec::SmallVec; @@ -8,7 +9,7 @@ pub trait Inst: Any { fn visit_values(&self, f: &mut dyn FnMut(Value)); fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)); fn has_side_effect(&self) -> bool; - fn as_str(&self) -> &'static str; + fn as_text(&self) -> &'static str; } /// This trait works as a "proof" that a specific ISA contains `I`, @@ -19,299 +20,292 @@ pub trait HasInst { } } -macro_rules! define_inst { - ($(($purity:ident, $ty:ident, $name:literal, $arity:literal)),* $(,)?) => { - $( - define_inst!($purity, $ty, $name, $arity); - )* - }; - - ($purity: ident, $ty: ident, $name:literal, $arity:literal) => { - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $ty { - args: define_inst!(__arg_ty $arity), - } - - impl Inst for $ty { - fn visit_values(&self, f: &mut dyn FnMut(Value)) { - for &v in &self.args { - f(v) - } - } - - fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { - for v in &mut self.args { - f(v) - } - } - - fn has_side_effect(&self) -> bool { - define_inst!(__has_side_effect_impl $purity) - } - - fn as_str(&self) -> &'static str { - $name - } - } - }; - - ($purity: ident, $ty: ident, $name:literal, $arity:literal) => { - #[derive(Debug, Clone, PartialEq, Eq, Hash)] - pub struct $ty { - pub args: define_inst!(__arg_ty $arity), - } - - impl Inst for $ty { - fn visit_values(&self, f: &mut dyn FnMut(Value)) { - self.args.iter().copied().for_each(f) - } - - fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { - self.args.iter_mut().for_each(f) - } - - fn has_side_effect(&self) -> bool { - define_inst!(__has_side_effect_impl $purity) - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Not { + #[inst(visit_value)] + arg: Value, +} - fn as_str(&self) -> &'static str { - $name - } - } - }; - - (__arg_ty 1) => { - Value - }; - - (__arg_ty $arity:literal) => { - [Value; $arity] - }; - - (__has_side_effect_impl pure) => { - true - }; - - (__has_side_effect_impl non_pure) => { - true - }; -} - -define_inst! { - (pure, Not, "not", 1), - (pure, Neg, "neg", 1), - // Arithmetic instructions. - (pure, Add, "add", 2), - (pure, Mul, "mul", 2), - (pure, Sub, "sub", 2), - (non_pure, Sdiv, "sdiv", 2), - (non_pure, Udiv, "udiv", 2), - // Comp instructions. - (pure, Lt, "lt", 2), - (pure, Gt, "gt", 2), - (pure, Slt, "slt", 2), - (pure, Sgt, "sgt", 2), - (pure, Le, "le", 2), - (pure, Ge, "ge", 2), - (pure, Sle, "sle", 2), - (pure, Sge, "sge", 2), - (pure, Eq, "eq", 2), - (pure, Ne, "ne", 2), - (pure, And, "And", 2), - (pure, Or, "Or", 2), - (pure, Xor, "xor", 2), - // Cast instructions. - (pure, Sext, "sext", 2), - (pure, Zext, "zext", 2), - (pure, Trunc, "trunc", 2), - (pure, BitCast, "bitcast", 2), - // Memory operations. - (non_pure, Mload, "mload", 1), - (non_pure, Mstore, "mstore", 2), -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Call { - pub args: SmallVec<[Value; 8]>, - pub callee: FuncRef, - pub ret_ty: Type, +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Neg { + #[inst(visit_value)] + arg: Value, } -impl Inst for Call { - fn visit_values(&self, f: &mut dyn FnMut(Value)) { - self.args.iter().copied().for_each(f) - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Add { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, +} - fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { - self.args.iter_mut().for_each(f) - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Mul { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, +} - fn has_side_effect(&self) -> bool { - // TODO: We need to add funciton attribute that can specify a purity of function. - true - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Sub { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, +} - fn as_str(&self) -> &'static str { - "call" - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = true)] +pub struct Sdiv { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Jump { - pub dest: Block, +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = true)] +pub struct Udiv { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, } -impl Inst for Jump { - fn visit_values(&self, _f: &mut dyn FnMut(Value)) {} - fn visit_values_mut(&mut self, _f: &mut dyn FnMut(&mut Value)) {} +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Lt { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, +} - fn has_side_effect(&self) -> bool { - false - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Gt { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, +} - fn as_str(&self) -> &'static str { - "jump" - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Slt { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Br { - pub cond: Value, - pub z_dest: Block, - pub nz_dest: Block, +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Sgt { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, } -impl Inst for Br { - fn visit_values(&self, f: &mut dyn FnMut(Value)) { - f(self.cond) - } - fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { - f(&mut self.cond) - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Le { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, +} - fn has_side_effect(&self) -> bool { - false - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Ge { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, +} - fn as_str(&self) -> &'static str { - "br" - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Sle { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct BrTable { - pub scrutinee: Value, - pub table: Vec<(Value, Block)>, - pub default: Option, +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Sge { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, } -impl Inst for BrTable { - fn visit_values(&self, f: &mut dyn FnMut(Value)) { - f(self.scrutinee); - self.table.iter().for_each(|(v, _)| f(*v)) - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Eq { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, +} - fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { - f(&mut self.scrutinee); - self.table.iter_mut().for_each(|(v, _)| f(v)) - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Ne { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, +} - fn has_side_effect(&self) -> bool { - false - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct And { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, +} - fn as_str(&self) -> &'static str { - "br_table" - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Or { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Alloca { - pub ty: Type, +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Xor { + #[inst(visit_value)] + lhs: Value, + #[inst(visit_value)] + rhs: Value, } -impl Inst for Alloca { - fn visit_values(&self, _f: &mut dyn FnMut(Value)) {} - fn visit_values_mut(&mut self, _f: &mut dyn FnMut(&mut Value)) {} +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Sext { + #[inst(visit_value)] + from: Value, + ty: Type, +} - fn has_side_effect(&self) -> bool { - true - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Zext { + #[inst(visit_value)] + from: Value, + ty: Type, +} - fn as_str(&self) -> &'static str { - "alloca" - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Trunc { + #[inst(visit_value)] + from: Value, + ty: Type, } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Return { - pub arg: Option, +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct BitCast { + #[inst(visit_value)] + from: Value, + ty: Type, } -impl Inst for Return { - fn visit_values(&self, f: &mut dyn FnMut(Value)) { - if let Some(v) = self.arg { - f(v) - } - } - fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { - if let Some(v) = self.arg.as_mut() { - f(v) - } - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = true)] +pub struct Mload { + #[inst(visit_value)] + addr: Value, +} - fn has_side_effect(&self) -> bool { - true - } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = true)] +pub struct Sload { + #[inst(visit_value)] + value: Value, + #[inst(visit_value)] + addr: Value, +} - fn as_str(&self) -> &'static str { - "return" - } +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = true)] +pub struct Call { + #[inst(visit_value)] + args: SmallVec<[Value; 8]>, + callee: FuncRef, + ret_ty: Type, } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Gep { - values: SmallVec<[Value; 8]>, +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Jump { + dest: Block, } -impl Inst for Gep { - fn visit_values(&self, f: &mut dyn FnMut(Value)) { - self.values.visit_with(f) - } - fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { - self.values.iter_mut().for_each(f) - } +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Br { + #[inst(visit_value)] + cond: Value, - fn has_side_effect(&self) -> bool { - false - } + z_dest: Block, + nz_dest: Block, +} - fn as_str(&self) -> &'static str { - "gep" - } +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct BrTable { + #[inst(visit_value)] + scrutinee: Value, + #[inst(visit_value)] + table: Vec<(Value, Block)>, + + default: Option, } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Phi { - pub values: Vec<(Value, Block)>, - pub ty: Type, +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = true)] +pub struct Alloca { + ty: Type, } -impl Inst for Phi { - fn visit_values(&self, f: &mut dyn FnMut(Value)) { - self.values.visit_with(f) - } - fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)) { - self.values.visit_mut_with(f) - } +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = true)] +pub struct Return { + #[inst(visit_value)] + arg: Option, +} - fn has_side_effect(&self) -> bool { - true - } +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Gep { + #[inst(visit_value)] + values: SmallVec<[Value; 8]>, +} - fn as_str(&self) -> &'static str { - "phi" - } +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Phi { + #[inst(visit_value)] + values: Vec<(Value, Block)>, + ty: Type, } pub(crate) trait ValueVisitable { @@ -329,16 +323,19 @@ impl ValueVisitable for Value { } } -impl ValueVisitable for Option { +impl ValueVisitable for Option +where + V: ValueVisitable, +{ fn visit_with(&self, f: &mut dyn FnMut(Value)) { - if let Some(value) = *self { - f(value) + if let Some(value) = self { + value.visit_with(f) } } fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { if let Some(value) = self.as_mut() { - f(value) + value.visit_mut_with(f) } } } @@ -381,3 +378,17 @@ where self.iter_mut().for_each(|v| v.visit_mut_with(f)) } } + +impl ValueVisitable for SmallVec<[V; N]> +where + V: ValueVisitable, + [V; N]: smallvec::Array, +{ + fn visit_with(&self, f: &mut dyn FnMut(Value)) { + self.iter().for_each(|v| v.visit_with(f)) + } + + fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { + self.iter_mut().for_each(|v| v.visit_mut_with(f)) + } +} diff --git a/crates/ir/src/lib.rs b/crates/ir/src/lib.rs index c409caa7..79afd0fc 100644 --- a/crates/ir/src/lib.rs +++ b/crates/ir/src/lib.rs @@ -25,9 +25,11 @@ pub use function::{Function, Signature}; pub use global_variable::{GlobalVariable, GlobalVariableData}; pub use graphviz::render_to; pub use insn::{BranchInfo, DataLocationKind, Insn, InsnData}; -pub use inst::HasInst; +pub use inst::{HasInst, Inst}; pub use layout::Layout; pub use linkage::Linkage; pub use module::Module; pub use types::Type; pub use value::{Immediate, Value, ValueData}; + +pub(crate) use inst::ValueVisitable; From fe88bff1a427b1ed8e81a05a984a19c014bf780d Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 9 Sep 2024 18:16:31 +0200 Subject: [PATCH 09/26] Cleanup related module structure --- crates/ir/src/{inst.rs => inst/basic.rs} | 108 ++--------------------- crates/ir/src/inst/mod.rs | 107 ++++++++++++++++++++++ crates/macros/src/lib.rs | 10 +-- 3 files changed, 117 insertions(+), 108 deletions(-) rename crates/ir/src/{inst.rs => inst/basic.rs} (70%) create mode 100644 crates/ir/src/inst/mod.rs diff --git a/crates/ir/src/inst.rs b/crates/ir/src/inst/basic.rs similarity index 70% rename from crates/ir/src/inst.rs rename to crates/ir/src/inst/basic.rs index 9a52fc19..35dd530a 100644 --- a/crates/ir/src/inst.rs +++ b/crates/ir/src/inst/basic.rs @@ -1,24 +1,7 @@ -use sonatina_macros::Inst; -use std::any::{Any, TypeId}; - -use smallvec::SmallVec; - use crate::{module::FuncRef, Block, Type, Value}; +use smallvec::SmallVec; -pub trait Inst: Any { - fn visit_values(&self, f: &mut dyn FnMut(Value)); - fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)); - fn has_side_effect(&self) -> bool; - fn as_text(&self) -> &'static str; -} - -/// This trait works as a "proof" that a specific ISA contains `I`, -/// and then allows a construction and reflection of type `I` in that specific ISA context. -pub trait HasInst { - fn is(&self, inst: &dyn Inst) -> bool { - inst.type_id() == TypeId::of::() - } -} +use sonatina_macros::Inst; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] #[inst(side_effect = false)] @@ -308,87 +291,6 @@ pub struct Phi { ty: Type, } -pub(crate) trait ValueVisitable { - fn visit_with(&self, f: &mut dyn FnMut(Value)); - fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)); -} - -impl ValueVisitable for Value { - fn visit_with(&self, f: &mut dyn FnMut(Value)) { - f(*self) - } - - fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { - f(self) - } -} - -impl ValueVisitable for Option -where - V: ValueVisitable, -{ - fn visit_with(&self, f: &mut dyn FnMut(Value)) { - if let Some(value) = self { - value.visit_with(f) - } - } - - fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { - if let Some(value) = self.as_mut() { - value.visit_mut_with(f) - } - } -} - -impl ValueVisitable for (V, T) -where - V: ValueVisitable, -{ - fn visit_with(&self, f: &mut dyn FnMut(Value)) { - self.0.visit_with(f) - } - - fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { - self.0.visit_mut_with(f) - } -} - -impl ValueVisitable for Vec -where - V: ValueVisitable, -{ - fn visit_with(&self, f: &mut dyn FnMut(Value)) { - self.iter().for_each(|v| v.visit_with(f)) - } - - fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { - self.iter_mut().for_each(|v| v.visit_mut_with(f)) - } -} - -impl ValueVisitable for [V] -where - V: ValueVisitable, -{ - fn visit_with(&self, f: &mut dyn FnMut(Value)) { - self.iter().for_each(|v| v.visit_with(f)) - } - - fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { - self.iter_mut().for_each(|v| v.visit_mut_with(f)) - } -} - -impl ValueVisitable for SmallVec<[V; N]> -where - V: ValueVisitable, - [V; N]: smallvec::Array, -{ - fn visit_with(&self, f: &mut dyn FnMut(Value)) { - self.iter().for_each(|v| v.visit_with(f)) - } - - fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { - self.iter_mut().for_each(|v| v.visit_mut_with(f)) - } -} +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(side_effect = false)] +pub struct Nop {} diff --git a/crates/ir/src/inst/mod.rs b/crates/ir/src/inst/mod.rs new file mode 100644 index 00000000..2d11dee9 --- /dev/null +++ b/crates/ir/src/inst/mod.rs @@ -0,0 +1,107 @@ +pub mod basic; + +use std::any::{Any, TypeId}; + +use smallvec::SmallVec; + +use crate::Value; + +pub trait Inst: Any { + fn visit_values(&self, f: &mut dyn FnMut(Value)); + fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)); + fn has_side_effect(&self) -> bool; + fn as_text(&self) -> &'static str; +} + +/// This trait works as a "proof" that a specific ISA contains `I`, +/// and then allows a construction and reflection of type `I` in that specific ISA context. +pub trait HasInst { + fn is(&self, inst: &dyn Inst) -> bool { + inst.type_id() == TypeId::of::() + } +} + +pub(crate) trait ValueVisitable { + fn visit_with(&self, f: &mut dyn FnMut(Value)); + fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)); +} + +impl ValueVisitable for Value { + fn visit_with(&self, f: &mut dyn FnMut(Value)) { + f(*self) + } + + fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { + f(self) + } +} + +impl ValueVisitable for Option +where + V: ValueVisitable, +{ + fn visit_with(&self, f: &mut dyn FnMut(Value)) { + if let Some(value) = self { + value.visit_with(f) + } + } + + fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { + if let Some(value) = self.as_mut() { + value.visit_mut_with(f) + } + } +} + +impl ValueVisitable for (V, T) +where + V: ValueVisitable, +{ + fn visit_with(&self, f: &mut dyn FnMut(Value)) { + self.0.visit_with(f) + } + + fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { + self.0.visit_mut_with(f) + } +} + +impl ValueVisitable for Vec +where + V: ValueVisitable, +{ + fn visit_with(&self, f: &mut dyn FnMut(Value)) { + self.iter().for_each(|v| v.visit_with(f)) + } + + fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { + self.iter_mut().for_each(|v| v.visit_mut_with(f)) + } +} + +impl ValueVisitable for [V] +where + V: ValueVisitable, +{ + fn visit_with(&self, f: &mut dyn FnMut(Value)) { + self.iter().for_each(|v| v.visit_with(f)) + } + + fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { + self.iter_mut().for_each(|v| v.visit_mut_with(f)) + } +} + +impl ValueVisitable for SmallVec<[V; N]> +where + V: ValueVisitable, + [V; N]: smallvec::Array, +{ + fn visit_with(&self, f: &mut dyn FnMut(Value)) { + self.iter().for_each(|v| v.visit_with(f)) + } + + fn visit_mut_with(&mut self, f: &mut dyn FnMut(&mut Value)) { + self.iter_mut().for_each(|v| v.visit_mut_with(f)) + } +} diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs index 3c421bfe..940b71fc 100644 --- a/crates/macros/src/lib.rs +++ b/crates/macros/src/lib.rs @@ -186,20 +186,20 @@ impl InstStruct { fn make_cast_fn(&self) -> proc_macro2::TokenStream { quote! { - pub fn cast<'i>(hi: &dyn crate::HasInst, inst: &'i dyn Inst) -> Option<&'i Self> { + pub fn cast<'i>(hi: &dyn crate::HasInst, inst: &'i dyn crate::Inst) -> Option<&'i Self> { if hi.is(inst) { - unsafe { Some(&*(inst as *const dyn Inst as *const Self)) } + unsafe { Some(&*(inst as *const dyn crate::Inst as *const Self)) } } else { None } } pub fn cast_mut<'i>( - hi: &dyn HasInst, - inst: &'i mut dyn Inst, + hi: &dyn crate::HasInst, + inst: &'i mut dyn crate::Inst, ) -> Option<&'i mut Self> { if hi.is(inst) { - unsafe { Some(&mut *(inst as *mut dyn Inst as *mut Self)) } + unsafe { Some(&mut *(inst as *mut dyn crate::Inst as *mut Self)) } } else { None } From 0fe0d38282961653d10cc7be635828e54069caab Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 9 Sep 2024 23:22:26 +0200 Subject: [PATCH 10/26] Define macro to define `DynINstGroup` --- crates/macros/Cargo.toml | 2 +- crates/macros/src/inst.rs | 242 ++++++++++++++++++++++++++++++++ crates/macros/src/inst_group.rs | 65 +++++++++ crates/macros/src/lib.rs | 241 +------------------------------ 4 files changed, 314 insertions(+), 236 deletions(-) create mode 100644 crates/macros/src/inst.rs create mode 100644 crates/macros/src/inst_group.rs diff --git a/crates/macros/Cargo.toml b/crates/macros/Cargo.toml index 408690ef..0952fe7a 100644 --- a/crates/macros/Cargo.toml +++ b/crates/macros/Cargo.toml @@ -11,7 +11,7 @@ categories = ["compilers", "wasm"] keywords = ["compiler", "evm", "wasm", "smart-contract"] [lib] -proc_macro = true +proc-macro = true [dependencies] syn = { version = "1.0", features = ["full"] } diff --git a/crates/macros/src/inst.rs b/crates/macros/src/inst.rs new file mode 100644 index 00000000..1a3e64e9 --- /dev/null +++ b/crates/macros/src/inst.rs @@ -0,0 +1,242 @@ +use super::convert_to_snake; + +use quote::quote; + +pub fn derive_inst(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let item_struct = syn::parse_macro_input!(item as syn::ItemStruct); + + match InstStruct::new(item_struct).and_then(InstStruct::build) { + Ok(impls) => quote! { + #impls + } + .into(), + + Err(e) => e.to_compile_error().into(), + } +} + +struct InstStruct { + struct_name: syn::Ident, + has_side_effect: bool, + fields: Vec, +} + +struct InstField { + ident: syn::Ident, + ty: syn::Type, + visit_value: bool, +} + +impl InstStruct { + fn new(item_struct: syn::ItemStruct) -> syn::Result { + let has_side_effect = Self::check_side_effect_attr(&item_struct)?; + + let struct_ident = item_struct.ident; + + let fields = Self::parse_fields(&item_struct.fields)?; + + if item_struct.generics.lt_token.is_some() { + return Err(syn::Error::new_spanned( + item_struct.generics, + "generics is not allowed for inst types", + )); + } + + Ok(Self { + struct_name: struct_ident, + has_side_effect, + fields, + }) + } + + fn build(self) -> syn::Result { + let ctor = self.make_ctor(); + let accessors = self.make_accessors(); + let cast_fn = self.make_cast_fn(); + + let struct_name = &self.struct_name; + let impl_inst = self.impl_inst(); + Ok(quote! { + impl #struct_name { + #ctor + + #accessors + + #cast_fn + } + + #impl_inst + }) + } + + fn check_side_effect_attr(item_struct: &syn::ItemStruct) -> syn::Result { + let mut has_side_effect = None; + + for attr in &item_struct.attrs { + if attr.path.is_ident("inst") { + let meta = attr.parse_args::()?; + if let syn::Meta::NameValue(name_value) = meta { + if name_value.path.is_ident("side_effect") { + if has_side_effect.is_some() { + return Err(syn::Error::new_spanned( + item_struct, + "only one #[inst(side_effect = ...)]` attribute is allowed", + )); + } + + if let syn::Lit::Bool(bool_lit) = name_value.lit { + has_side_effect = Some(bool_lit.value); + } + } + } + } + } + + has_side_effect.ok_or_else(|| { + syn::Error::new_spanned( + item_struct, + "unique #[inst(side_effect = ...)]` attributed is required", + ) + }) + } + + fn parse_fields(fields: &syn::Fields) -> syn::Result> { + let syn::Fields::Named(fields) = fields else { + return Err(syn::Error::new_spanned( + fields, + "tuple struct is not allowed for inst types", + )); + }; + + let mut inst_fields = Vec::new(); + + for field in &fields.named { + let mut visit_value = false; + + if !matches!(field.vis, syn::Visibility::Inherited) { + return Err(syn::Error::new_spanned( + field, + "public visibility is not allowed", + )); + } + + for attr in &field.attrs { + if attr.path.is_ident("inst") { + let meta = attr.parse_args::()?; + if let syn::Meta::Path(path) = meta { + if path.is_ident("visit_value") { + visit_value = true; + } else { + return Err(syn::Error::new_spanned( + attr, + "only `visit_value` is allowed", + )); + } + } + } + } + + inst_fields.push(InstField { + ident: field.ident.clone().unwrap(), + ty: field.ty.clone(), + visit_value, + }); + } + + Ok(inst_fields) + } + + fn make_ctor(&self) -> proc_macro2::TokenStream { + let ctor_args = self.fields.iter().map(|f| { + let ident = &f.ident; + let ty = &f.ty; + quote! {#ident: #ty} + }); + + let field_names = self.fields.iter().map(|f| &f.ident); + quote! { + pub fn new(hi: &dyn crate::HasInst, #(#ctor_args),*) -> Self { + Self { + #(#field_names: #field_names),* + } + } + } + } + + fn make_accessors(&self) -> proc_macro2::TokenStream { + let accessors = self.fields.iter().map(|f| { + let ident = &f.ident; + let ty = &f.ty; + let getter = quote::format_ident!("{ident}"); + let get_mut = quote::format_ident!("{ident}_mut"); + quote! { + pub fn #getter(&self) -> &#ty { + &self.#ident + } + + pub fn #get_mut(&mut self) -> &mut #ty{ + &mut self.#ident + } + } + }); + + quote! { + #(#accessors)* + } + } + + fn make_cast_fn(&self) -> proc_macro2::TokenStream { + quote! { + pub fn cast<'i>(hi: &dyn crate::HasInst, inst: &'i dyn crate::Inst) -> Option<&'i Self> { + if hi.is(inst) { + unsafe { Some(&*(inst as *const dyn crate::Inst as *const Self)) } + } else { + None + } + } + + pub fn cast_mut<'i>( + hi: &dyn crate::HasInst, + inst: &'i mut dyn crate::Inst, + ) -> Option<&'i mut Self> { + if hi.is(inst) { + unsafe { Some(&mut *(inst as *mut dyn crate::Inst as *mut Self)) } + } else { + None + } + } + } + } + + fn impl_inst(&self) -> proc_macro2::TokenStream { + let struct_name = &self.struct_name; + let has_side_effect = self.has_side_effect; + let visit_fields: Vec<_> = self + .fields + .iter() + .filter(|f| f.visit_value) + .map(|f| &f.ident) + .collect(); + let text_form = convert_to_snake(&self.struct_name.to_string()); + + quote! { + impl crate::Inst for #struct_name { + fn visit_values(&self, f: &mut dyn FnMut(crate::Value)) { + #(crate::ValueVisitable::visit_with(&self.#visit_fields, (f));)* + } + + fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut crate::Value)) { + #(crate::ValueVisitable::visit_mut_with(&mut self.#visit_fields, (f));)* + } + + fn has_side_effect(&self) -> bool { + #has_side_effect + } + + fn as_text(&self) -> &'static str { + #text_form + } + } + } + } +} diff --git a/crates/macros/src/inst_group.rs b/crates/macros/src/inst_group.rs new file mode 100644 index 00000000..d017c625 --- /dev/null +++ b/crates/macros/src/inst_group.rs @@ -0,0 +1,65 @@ +use quote::quote; + +use crate::convert_to_snake; + +pub fn define_dyn_inst_group(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let def = syn::parse_macro_input! {input as TraitDefinition}; + match def.build() { + Ok(ts) => quote! {#ts}.into(), + Err(e) => e.to_compile_error().into(), + } +} + +struct TraitDefinition(syn::punctuated::Punctuated); + +impl TraitDefinition { + fn build(self) -> syn::Result { + let trait_def = self.define_trait(); + let impls = self.impl_registered(); + + Ok(quote! { + #trait_def + #impls + }) + } + + fn define_trait(&self) -> proc_macro2::TokenStream { + let methods = self.0.iter().map(|path| { + let method_name = path_to_method_name(path); + quote! { + fn #method_name(&self) -> Option<&dyn crate::HasInst<#path>> { None } + } + }); + quote! { + pub trait DynInstGroup { + #(#methods)* + } + } + } + + fn impl_registered(&self) -> proc_macro2::TokenStream { + let impls = self.0.iter().map(|path| { + quote! { + impl crate::inst::inst_group::sealed::Registered for #path {} + } + }); + + quote! { + #(#impls)* + } + } +} + +impl syn::parse::Parse for TraitDefinition { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let insts = + syn::punctuated::Punctuated::::parse_terminated(input)?; + Ok(Self(insts)) + } +} + +fn path_to_method_name(p: &syn::Path) -> syn::Ident { + let ident = &p.segments.last().as_ref().unwrap().ident; + let s_ident = convert_to_snake(&ident.to_string()); + quote::format_ident!("has_{s_ident}") +} diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs index 940b71fc..b961c81b 100644 --- a/crates/macros/src/lib.rs +++ b/crates/macros/src/lib.rs @@ -1,243 +1,14 @@ -use quote::quote; +mod inst; +mod inst_group; #[proc_macro_derive(Inst, attributes(inst))] pub fn derive_inst(item: proc_macro::TokenStream) -> proc_macro::TokenStream { - let item_struct = syn::parse_macro_input!(item as syn::ItemStruct); - - match InstStruct::new(item_struct).and_then(InstStruct::build) { - Ok(impls) => quote! { - #impls - } - .into(), - - Err(e) => e.to_compile_error().into(), - } -} - -struct InstStruct { - struct_name: syn::Ident, - has_side_effect: bool, - fields: Vec, -} - -struct InstField { - ident: syn::Ident, - ty: syn::Type, - visit_value: bool, + inst::derive_inst(item) } -impl InstStruct { - fn new(item_struct: syn::ItemStruct) -> syn::Result { - let has_side_effect = Self::check_side_effect_attr(&item_struct)?; - - let struct_ident = item_struct.ident; - - let fields = Self::parse_fields(&item_struct.fields)?; - - if item_struct.generics.lt_token.is_some() { - return Err(syn::Error::new_spanned( - item_struct.generics, - "generics is not allowed for inst types", - )); - } - - Ok(Self { - struct_name: struct_ident, - has_side_effect, - fields, - }) - } - - fn check_side_effect_attr(item_struct: &syn::ItemStruct) -> syn::Result { - let mut has_side_effect = None; - - for attr in &item_struct.attrs { - if attr.path.is_ident("inst") { - let meta = attr.parse_args::()?; - if let syn::Meta::NameValue(name_value) = meta { - if name_value.path.is_ident("side_effect") { - if has_side_effect.is_some() { - return Err(syn::Error::new_spanned( - item_struct, - "only one #[inst(side_effect = ...)]` attribute is allowed", - )); - } - - if let syn::Lit::Bool(bool_lit) = name_value.lit { - has_side_effect = Some(bool_lit.value); - } - } - } - } - } - - has_side_effect.ok_or_else(|| { - syn::Error::new_spanned( - item_struct, - "unique #[inst(side_effect = ...)]` attributed is required", - ) - }) - } - - fn parse_fields(fields: &syn::Fields) -> syn::Result> { - let syn::Fields::Named(fields) = fields else { - return Err(syn::Error::new_spanned( - fields, - "tuple struct is not allowed for inst types", - )); - }; - - let mut inst_fields = Vec::new(); - - for field in &fields.named { - let mut visit_value = false; - - if !matches!(field.vis, syn::Visibility::Inherited) { - return Err(syn::Error::new_spanned( - field, - "public visibility is not allowed", - )); - } - - for attr in &field.attrs { - if attr.path.is_ident("inst") { - let meta = attr.parse_args::()?; - if let syn::Meta::Path(path) = meta { - if path.is_ident("visit_value") { - visit_value = true; - } else { - return Err(syn::Error::new_spanned( - attr, - "only `visit_value` is allowed", - )); - } - } - } - } - - inst_fields.push(InstField { - ident: field.ident.clone().unwrap(), - ty: field.ty.clone(), - visit_value, - }); - } - - Ok(inst_fields) - } - - fn build(self) -> syn::Result { - let ctor = self.make_ctor(); - let accessors = self.make_accessors(); - let cast_fn = self.make_cast_fn(); - - let struct_name = &self.struct_name; - let impl_inst = self.impl_inst(); - Ok(quote! { - impl #struct_name { - #ctor - - #accessors - - #cast_fn - } - - #impl_inst - }) - } - - fn make_ctor(&self) -> proc_macro2::TokenStream { - let ctor_args = self.fields.iter().map(|f| { - let ident = &f.ident; - let ty = &f.ty; - quote! {#ident: #ty} - }); - - let field_names = self.fields.iter().map(|f| &f.ident); - quote! { - pub fn new(hi: &dyn crate::HasInst, #(#ctor_args),*) -> Self { - Self { - #(#field_names: #field_names),* - } - } - } - } - - fn make_accessors(&self) -> proc_macro2::TokenStream { - let accessors = self.fields.iter().map(|f| { - let ident = &f.ident; - let ty = &f.ty; - let getter = quote::format_ident!("{ident}"); - let get_mut = quote::format_ident!("{ident}_mut"); - quote! { - pub fn #getter(&self) -> &#ty { - &self.#ident - } - - pub fn #get_mut(&mut self) -> &mut #ty{ - &mut self.#ident - } - } - }); - - quote! { - #(#accessors)* - } - } - - fn make_cast_fn(&self) -> proc_macro2::TokenStream { - quote! { - pub fn cast<'i>(hi: &dyn crate::HasInst, inst: &'i dyn crate::Inst) -> Option<&'i Self> { - if hi.is(inst) { - unsafe { Some(&*(inst as *const dyn crate::Inst as *const Self)) } - } else { - None - } - } - - pub fn cast_mut<'i>( - hi: &dyn crate::HasInst, - inst: &'i mut dyn crate::Inst, - ) -> Option<&'i mut Self> { - if hi.is(inst) { - unsafe { Some(&mut *(inst as *mut dyn crate::Inst as *mut Self)) } - } else { - None - } - } - } - } - - fn impl_inst(&self) -> proc_macro2::TokenStream { - let struct_name = &self.struct_name; - let has_side_effect = self.has_side_effect; - let visit_fields: Vec<_> = self - .fields - .iter() - .filter(|f| f.visit_value) - .map(|f| &f.ident) - .collect(); - let text_form = convert_to_snake(&self.struct_name.to_string()); - - quote! { - impl crate::Inst for #struct_name { - fn visit_values(&self, f: &mut dyn FnMut(crate::Value)) { - #(crate::ValueVisitable::visit_with(&self.#visit_fields, (f));)* - } - - fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut crate::Value)) { - #(crate::ValueVisitable::visit_mut_with(&mut self.#visit_fields, (f));)* - } - - fn has_side_effect(&self) -> bool { - #has_side_effect - } - - fn as_text(&self) -> &'static str { - #text_form - } - } - } - } +#[proc_macro] +pub fn define_dyn_inst_group(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + inst_group::define_dyn_inst_group(input) } fn convert_to_snake(s: &str) -> String { From 3e5781767e5c66708983502724524216c2a4e35b Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 9 Sep 2024 23:23:16 +0200 Subject: [PATCH 11/26] Register basic insts to `InstGroup` --- crates/ir/src/inst/basic.rs | 4 +-- crates/ir/src/inst/inst_group.rs | 57 ++++++++++++++++++++++++++++++++ crates/ir/src/inst/mod.rs | 3 +- 3 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 crates/ir/src/inst/inst_group.rs diff --git a/crates/ir/src/inst/basic.rs b/crates/ir/src/inst/basic.rs index 35dd530a..08d7529c 100644 --- a/crates/ir/src/inst/basic.rs +++ b/crates/ir/src/inst/basic.rs @@ -205,7 +205,7 @@ pub struct Trunc { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] #[inst(side_effect = false)] -pub struct BitCast { +pub struct Bitcast { #[inst(visit_value)] from: Value, ty: Type, @@ -220,7 +220,7 @@ pub struct Mload { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] #[inst(side_effect = true)] -pub struct Sload { +pub struct Mstore { #[inst(visit_value)] value: Value, #[inst(visit_value)] diff --git a/crates/ir/src/inst/inst_group.rs b/crates/ir/src/inst/inst_group.rs new file mode 100644 index 00000000..9286e070 --- /dev/null +++ b/crates/ir/src/inst/inst_group.rs @@ -0,0 +1,57 @@ +use super::{basic, Inst}; + +use sonatina_macros::define_dyn_inst_group; + +pub(super) mod sealed { + /// This trait has two roles, + /// 1. works as a sealed trait. + /// 2. ensure that an `Inst` is definitely registered to the `InstGroup`. + pub trait Registered {} +} + +define_dyn_inst_group! { + basic::Not, + basic::Neg, + basic::Add, + basic::Mul, + basic::Sub, + basic::Sdiv, + basic::Udiv, + basic::Lt, + basic::Gt, + basic::Slt, + basic::Sgt, + basic::Le, + basic::Ge, + basic::Sle, + basic::Sge, + basic::Eq, + basic::Ne, + basic::And, + basic::Or, + basic::Xor, + basic::Sext, + basic::Zext, + basic::Trunc, + basic::Bitcast, + basic::Mload, + basic::Mstore, + basic::Call, + basic::Jump, + basic::Br, + basic::BrTable, + basic::Alloca, + basic::Return, + basic::Gep, + basic::Phi, + basic::Nop, + +} + +pub trait ConcreteInstGroup: DynInstGroup { + type InstSet; + type InstSetMut; + + fn downcast(inst: &dyn Inst) -> Self::InstSet; + fn downcast_mut(inst: &mut dyn Inst) -> Self::InstSetMut; +} diff --git a/crates/ir/src/inst/mod.rs b/crates/ir/src/inst/mod.rs index 2d11dee9..fb02b19c 100644 --- a/crates/ir/src/inst/mod.rs +++ b/crates/ir/src/inst/mod.rs @@ -1,4 +1,5 @@ pub mod basic; +pub mod inst_group; use std::any::{Any, TypeId}; @@ -6,7 +7,7 @@ use smallvec::SmallVec; use crate::Value; -pub trait Inst: Any { +pub trait Inst: inst_group::sealed::Registered + Any { fn visit_values(&self, f: &mut dyn FnMut(Value)); fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)); fn has_side_effect(&self) -> bool; From 152665c92d2cbbba2ce6135abf7d028fbf965c78 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 10 Sep 2024 16:33:09 +0200 Subject: [PATCH 12/26] Make the naming rule more clearer --- crates/ir/src/inst/evm.rs | 0 .../ir/src/inst/{inst_group.rs => inst_set.rs} | 16 ++++++++-------- crates/ir/src/inst/mod.rs | 4 ++-- crates/ir/src/lib.rs | 5 ++++- 4 files changed, 14 insertions(+), 11 deletions(-) create mode 100644 crates/ir/src/inst/evm.rs rename crates/ir/src/inst/{inst_group.rs => inst_set.rs} (68%) diff --git a/crates/ir/src/inst/evm.rs b/crates/ir/src/inst/evm.rs new file mode 100644 index 00000000..e69de29b diff --git a/crates/ir/src/inst/inst_group.rs b/crates/ir/src/inst/inst_set.rs similarity index 68% rename from crates/ir/src/inst/inst_group.rs rename to crates/ir/src/inst/inst_set.rs index 9286e070..3de7a825 100644 --- a/crates/ir/src/inst/inst_group.rs +++ b/crates/ir/src/inst/inst_set.rs @@ -1,6 +1,6 @@ use super::{basic, Inst}; -use sonatina_macros::define_dyn_inst_group; +use sonatina_macros::define_inst_set_base; pub(super) mod sealed { /// This trait has two roles, @@ -9,7 +9,8 @@ pub(super) mod sealed { pub trait Registered {} } -define_dyn_inst_group! { +// All instructions defined in the IR must be listed(otherwise, you'll get a compile error). +define_inst_set_base! { basic::Not, basic::Neg, basic::Add, @@ -45,13 +46,12 @@ define_dyn_inst_group! { basic::Gep, basic::Phi, basic::Nop, - } -pub trait ConcreteInstGroup: DynInstGroup { - type InstSet; - type InstSetMut; +pub trait StaticInstSet: InstSetBase { + type InstSetKind; + type InstSetKindMut; - fn downcast(inst: &dyn Inst) -> Self::InstSet; - fn downcast_mut(inst: &mut dyn Inst) -> Self::InstSetMut; + fn resolve_inst(inst: &dyn Inst) -> Self::InstSetKind; + fn resolve_inst_mut(inst: &mut dyn Inst) -> Self::InstSetKindMut; } diff --git a/crates/ir/src/inst/mod.rs b/crates/ir/src/inst/mod.rs index fb02b19c..bdc1b15f 100644 --- a/crates/ir/src/inst/mod.rs +++ b/crates/ir/src/inst/mod.rs @@ -1,5 +1,5 @@ pub mod basic; -pub mod inst_group; +pub mod inst_set; use std::any::{Any, TypeId}; @@ -7,7 +7,7 @@ use smallvec::SmallVec; use crate::Value; -pub trait Inst: inst_group::sealed::Registered + Any { +pub trait Inst: inst_set::sealed::Registered + Any { fn visit_values(&self, f: &mut dyn FnMut(Value)); fn visit_values_mut(&mut self, f: &mut dyn FnMut(&mut Value)); fn has_side_effect(&self) -> bool; diff --git a/crates/ir/src/lib.rs b/crates/ir/src/lib.rs index 79afd0fc..d9ce9d4d 100644 --- a/crates/ir/src/lib.rs +++ b/crates/ir/src/lib.rs @@ -25,7 +25,10 @@ pub use function::{Function, Signature}; pub use global_variable::{GlobalVariable, GlobalVariableData}; pub use graphviz::render_to; pub use insn::{BranchInfo, DataLocationKind, Insn, InsnData}; -pub use inst::{HasInst, Inst}; +pub use inst::{ + inst_set::{InstSetBase, StaticInstSet}, + HasInst, Inst, +}; pub use layout::Layout; pub use linkage::Linkage; pub use module::Module; From 0436c23c294d177a8c053b62781c8169dcef3b67 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 10 Sep 2024 16:33:44 +0200 Subject: [PATCH 13/26] Define `inst_set` macro --- crates/macros/src/inst_set.rs | 105 ++++++++++++++++++ .../src/{inst_group.rs => inst_set_base.rs} | 8 +- crates/macros/src/lib.rs | 15 ++- 3 files changed, 121 insertions(+), 7 deletions(-) create mode 100644 crates/macros/src/inst_set.rs rename crates/macros/src/{inst_group.rs => inst_set_base.rs} (85%) diff --git a/crates/macros/src/inst_set.rs b/crates/macros/src/inst_set.rs new file mode 100644 index 00000000..02e56cf5 --- /dev/null +++ b/crates/macros/src/inst_set.rs @@ -0,0 +1,105 @@ +use quote::quote; + +use crate::inst_set_base; + +pub fn define_inst_set( + _attr: proc_macro::TokenStream, + item: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let item_struct = syn::parse_macro_input!(item as syn::ItemStruct); + match InstSet::new(item_struct).and_then(InstSet::build) { + Ok(impls) => quote! { + #impls + } + .into(), + + Err(e) => e.to_compile_error().into(), + } +} + +struct InstSet { + vis: syn::Visibility, + ident: syn::Ident, + insts: Vec, +} + +impl InstSet { + fn new(s: syn::ItemStruct) -> syn::Result { + let ident = s.ident; + let vis = s.vis; + let insts = Self::parse_insts(&s.fields)?; + Ok(Self { vis, ident, insts }) + } + + fn build(self) -> syn::Result { + let inst_set = self.define_inst_set(); + let has_inst_impls = self.impl_has_inst(); + let inst_set_base_impl = self.impl_inst_set_base(); + Ok(quote! { + #inst_set + #has_inst_impls + #inst_set_base_impl + }) + } + + fn parse_insts(fields: &syn::Fields) -> syn::Result> { + let syn::Fields::Unnamed(fields) = fields else { + return Err(syn::Error::new_spanned( + fields, + "only tuple struct is allowed", + )); + }; + + let mut insts = Vec::with_capacity(fields.unnamed.len()); + for f in fields.unnamed.iter() { + let syn::Type::Path(p) = &f.ty else { + return Err(syn::Error::new_spanned( + f, + "expected path to inst type here", + )); + }; + insts.push(p.path.clone()); + } + + Ok(insts) + } + + fn define_inst_set(&self) -> proc_macro2::TokenStream { + let ident = &self.ident; + let vis = &self.vis; + quote! { + #vis struct #ident {} + } + } + + fn impl_has_inst(&self) -> proc_macro2::TokenStream { + let ident = &self.ident; + let impls = self.insts.iter().map(|p| { + quote! { + impl crate::HasInst<#p> for #ident {} + } + }); + + quote! { + #(#impls)* + } + } + + fn impl_inst_set_base(&self) -> proc_macro2::TokenStream { + let methods = self.insts.iter().map(|p| { + let method_name = inst_set_base::path_to_method_name(p); + quote! { + fn #method_name(&self) -> Option<&dyn crate::HasInst<#p>> { + Some(self) + } + } + }); + + let ident = &self.ident; + quote! { + impl crate::InstSetBase for #ident { + #(#methods)* + } + } + } +} diff --git a/crates/macros/src/inst_group.rs b/crates/macros/src/inst_set_base.rs similarity index 85% rename from crates/macros/src/inst_group.rs rename to crates/macros/src/inst_set_base.rs index d017c625..f7cfcd3f 100644 --- a/crates/macros/src/inst_group.rs +++ b/crates/macros/src/inst_set_base.rs @@ -2,7 +2,7 @@ use quote::quote; use crate::convert_to_snake; -pub fn define_dyn_inst_group(input: proc_macro::TokenStream) -> proc_macro::TokenStream { +pub fn define_inst_set_base(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let def = syn::parse_macro_input! {input as TraitDefinition}; match def.build() { Ok(ts) => quote! {#ts}.into(), @@ -31,7 +31,7 @@ impl TraitDefinition { } }); quote! { - pub trait DynInstGroup { + pub trait InstSetBase { #(#methods)* } } @@ -40,7 +40,7 @@ impl TraitDefinition { fn impl_registered(&self) -> proc_macro2::TokenStream { let impls = self.0.iter().map(|path| { quote! { - impl crate::inst::inst_group::sealed::Registered for #path {} + impl crate::inst::inst_set::sealed::Registered for #path {} } }); @@ -58,7 +58,7 @@ impl syn::parse::Parse for TraitDefinition { } } -fn path_to_method_name(p: &syn::Path) -> syn::Ident { +pub(super) fn path_to_method_name(p: &syn::Path) -> syn::Ident { let ident = &p.segments.last().as_ref().unwrap().ident; let s_ident = convert_to_snake(&ident.to_string()); quote::format_ident!("has_{s_ident}") diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs index b961c81b..95f274c9 100644 --- a/crates/macros/src/lib.rs +++ b/crates/macros/src/lib.rs @@ -1,5 +1,6 @@ mod inst; -mod inst_group; +mod inst_set; +mod inst_set_base; #[proc_macro_derive(Inst, attributes(inst))] pub fn derive_inst(item: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -7,8 +8,16 @@ pub fn derive_inst(item: proc_macro::TokenStream) -> proc_macro::TokenStream { } #[proc_macro] -pub fn define_dyn_inst_group(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - inst_group::define_dyn_inst_group(input) +pub fn define_inst_set_base(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + inst_set_base::define_inst_set_base(input) +} + +#[proc_macro_attribute] +pub fn inst_set( + attr: proc_macro::TokenStream, + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + inst_set::define_inst_set(attr, input) } fn convert_to_snake(s: &str) -> String { From d58f60ff7486145ac320232a0757210bdf3fd074 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 10 Sep 2024 18:04:12 +0200 Subject: [PATCH 14/26] Add `prelude` for inst related traits --- crates/ir/Cargo.toml | 2 +- crates/ir/src/inst/basic.rs | 2 +- crates/ir/src/inst/inst_set.rs | 10 +++++----- crates/ir/src/lib.rs | 7 +++++++ 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/crates/ir/Cargo.toml b/crates/ir/Cargo.toml index 233d60c5..9888a450 100644 --- a/crates/ir/Cargo.toml +++ b/crates/ir/Cargo.toml @@ -20,6 +20,6 @@ smallvec = "1.7.0" rustc-hash = "2.0.0" dyn-clone = "1.0.4" sonatina-triple = { path = "../triple", version = "0.0.3-alpha" } -sonatina-macros = { path = "../macros", version = "0.0.3-alpha" } +macros = { package = "sonatina-macros", path = "../macros", version = "0.0.3-alpha" } indexmap = "2.0.0" dot2 = { git = "https://github.com/sanpii/dot2.rs.git" } diff --git a/crates/ir/src/inst/basic.rs b/crates/ir/src/inst/basic.rs index 08d7529c..4307718c 100644 --- a/crates/ir/src/inst/basic.rs +++ b/crates/ir/src/inst/basic.rs @@ -1,7 +1,7 @@ use crate::{module::FuncRef, Block, Type, Value}; use smallvec::SmallVec; -use sonatina_macros::Inst; +use macros::Inst; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] #[inst(side_effect = false)] diff --git a/crates/ir/src/inst/inst_set.rs b/crates/ir/src/inst/inst_set.rs index 3de7a825..e5220dd3 100644 --- a/crates/ir/src/inst/inst_set.rs +++ b/crates/ir/src/inst/inst_set.rs @@ -1,6 +1,6 @@ use super::{basic, Inst}; -use sonatina_macros::define_inst_set_base; +use macros::define_inst_set_base; pub(super) mod sealed { /// This trait has two roles, @@ -49,9 +49,9 @@ define_inst_set_base! { } pub trait StaticInstSet: InstSetBase { - type InstSetKind; - type InstSetKindMut; + type InstKind; + type InstKindMut; - fn resolve_inst(inst: &dyn Inst) -> Self::InstSetKind; - fn resolve_inst_mut(inst: &mut dyn Inst) -> Self::InstSetKindMut; + fn resolve_inst(inst: &dyn Inst) -> Self::InstKind; + fn resolve_inst_mut(inst: &mut dyn Inst) -> Self::InstKindMut; } diff --git a/crates/ir/src/lib.rs b/crates/ir/src/lib.rs index d9ce9d4d..67bc4a99 100644 --- a/crates/ir/src/lib.rs +++ b/crates/ir/src/lib.rs @@ -36,3 +36,10 @@ pub use types::Type; pub use value::{Immediate, Value, ValueData}; pub(crate) use inst::ValueVisitable; + +pub mod prelude { + pub use crate::inst::{ + inst_set::{InstSetBase, StaticInstSet}, + HasInst, Inst, + }; +} From 41bd4b469c33b0c63180042ee2cdddd88f967668 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 10 Sep 2024 18:04:53 +0200 Subject: [PATCH 15/26] Let `inst_set` macro generate `InstKind` enum from a given inst set --- crates/macros/src/inst_set.rs | 76 +++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/crates/macros/src/inst_set.rs b/crates/macros/src/inst_set.rs index 02e56cf5..53decbda 100644 --- a/crates/macros/src/inst_set.rs +++ b/crates/macros/src/inst_set.rs @@ -6,8 +6,10 @@ pub fn define_inst_set( _attr: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { + let attr_args = syn::parse_macro_input!(_attr as syn::AttributeArgs); let item_struct = syn::parse_macro_input!(item as syn::ItemStruct); - match InstSet::new(item_struct).and_then(InstSet::build) { + + match InstSet::new(attr_args, item_struct).and_then(InstSet::build) { Ok(impls) => quote! { #impls } @@ -21,27 +23,66 @@ struct InstSet { vis: syn::Visibility, ident: syn::Ident, insts: Vec, + inst_kind_name: syn::Ident, } impl InstSet { - fn new(s: syn::ItemStruct) -> syn::Result { + fn new(args: Vec, s: syn::ItemStruct) -> syn::Result { let ident = s.ident; let vis = s.vis; let insts = Self::parse_insts(&s.fields)?; - Ok(Self { vis, ident, insts }) + let inst_kind_name = Self::parse_inst_kind_name(&args)?; + + Ok(Self { + vis, + ident, + insts, + inst_kind_name, + }) } fn build(self) -> syn::Result { let inst_set = self.define_inst_set(); let has_inst_impls = self.impl_has_inst(); let inst_set_base_impl = self.impl_inst_set_base(); + + let inst_kind = self.define_inst_kind(); + Ok(quote! { #inst_set #has_inst_impls #inst_set_base_impl + #inst_kind }) } + fn parse_inst_kind_name(args: &[syn::NestedMeta]) -> syn::Result { + let make_err = || { + Err(syn::Error::new( + proc_macro2::Span::call_site(), + "`#[inst_set(InstKind = \"{InstKindName}\")]` is required", + )) + }; + + if args.len() != 1 { + return make_err(); + } + + let syn::NestedMeta::Meta(syn::Meta::NameValue(name_value)) = &args[0] else { + return make_err(); + }; + + let inst_kind_name = match (name_value.path.get_ident(), &name_value.lit) { + (Some(ident), syn::Lit::Str(s)) if ident == "InstKind" => s.value(), + _ => return make_err(), + }; + + Ok(syn::Ident::new( + &inst_kind_name, + proc_macro2::Span::call_site(), + )) + } + fn parse_insts(fields: &syn::Fields) -> syn::Result> { let syn::Fields::Unnamed(fields) = fields else { return Err(syn::Error::new_spanned( @@ -102,4 +143,33 @@ impl InstSet { } } } + + fn define_inst_kind(&self) -> proc_macro2::TokenStream { + let lt = syn::Lifetime::new("'i", proc_macro2::Span::call_site()); + + let variants = self.insts.iter().map(|p| { + let variant_name = p.segments.last().unwrap(); + quote! { #variant_name(&#lt #p) } + }); + + let variants_mut = self.insts.iter().map(|p| { + let variant_name = p.segments.last().unwrap(); + quote! { #variant_name(&#lt mut #p) } + }); + + let inst_kind_name = &self.inst_kind_name; + let inst_kind_mut_name = quote::format_ident!("{inst_kind_name}Mut"); + + let vis = &self.vis; + + quote! { + #vis enum #inst_kind_name<#lt> { + #(#variants),* + } + + #vis enum #inst_kind_mut_name<#lt> { + #(#variants_mut),* + } + } + } } From 5ecbe35349f13690f9f3233e9675c1c1be63115b Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 10 Sep 2024 22:38:30 +0200 Subject: [PATCH 16/26] Extend `inst_set` macro so it can generate `InstSetExt` impl and constructor --- crates/macros/src/inst_set.rs | 154 +++++++++++++++++++++++++++------- 1 file changed, 126 insertions(+), 28 deletions(-) diff --git a/crates/macros/src/inst_set.rs b/crates/macros/src/inst_set.rs index 53decbda..aa18ea40 100644 --- a/crates/macros/src/inst_set.rs +++ b/crates/macros/src/inst_set.rs @@ -1,12 +1,12 @@ use quote::quote; -use crate::inst_set_base; +use crate::{convert_to_snake, inst_set_base}; pub fn define_inst_set( - _attr: proc_macro::TokenStream, + attr: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { - let attr_args = syn::parse_macro_input!(_attr as syn::AttributeArgs); + let attr_args = syn::parse_macro_input!(attr as syn::AttributeArgs); let item_struct = syn::parse_macro_input!(item as syn::ItemStruct); match InstSet::new(attr_args, item_struct).and_then(InstSet::build) { @@ -24,6 +24,7 @@ struct InstSet { ident: syn::Ident, insts: Vec, inst_kind_name: syn::Ident, + inst_kind_mut_name: syn::Ident, } impl InstSet { @@ -31,27 +32,36 @@ impl InstSet { let ident = s.ident; let vis = s.vis; let insts = Self::parse_insts(&s.fields)?; - let inst_kind_name = Self::parse_inst_kind_name(&args)?; + let inst_kind_ident = Self::parse_inst_kind_name(&args)?; + let inst_kind_mut_ident = quote::format_ident!("{inst_kind_ident}Mut"); Ok(Self { vis, ident, insts, - inst_kind_name, + inst_kind_name: inst_kind_ident, + inst_kind_mut_name: inst_kind_mut_ident, }) } fn build(self) -> syn::Result { let inst_set = self.define_inst_set(); + let inherent_methods = self.impl_inherent_methods(); + let has_inst_impls = self.impl_has_inst(); let inst_set_base_impl = self.impl_inst_set_base(); + let inst_set_ext_impl = self.impl_inst_set_ext(); let inst_kind = self.define_inst_kind(); Ok(quote! { #inst_set + #inherent_methods + #has_inst_impls #inst_set_base_impl + #inst_set_ext_impl + #inst_kind }) } @@ -109,7 +119,95 @@ impl InstSet { let ident = &self.ident; let vis = &self.vis; quote! { - #vis struct #ident {} + #vis struct #ident { + #[allow(clippy::type_complexity)] + table: ::rustc_hash::FxHashMap< + std::any::TypeId, + ( + &'static for<'i> fn(&Self, &'i dyn Inst) -> ::InstKind<'i>, + &'static for<'i> fn( + &Self, + &'i mut dyn Inst, + ) -> ::InstKindMut<'i>, + ), + >, + + } + } + } + + fn define_inst_kind(&self) -> proc_macro2::TokenStream { + let lt = syn::Lifetime::new("'i", proc_macro2::Span::call_site()); + + let variants = self.insts.iter().map(|p| { + let variant_name = self.variant_name_from_inst_path(p); + quote! { #variant_name(&#lt #p) } + }); + let variants_mut = self.insts.iter().map(|p| { + let variant_name = self.variant_name_from_inst_path(p); + quote! { #variant_name(&#lt mut #p) } + }); + + let inst_kind_name = &self.inst_kind_name; + let inst_kind_mut_name = quote::format_ident!("{inst_kind_name}Mut"); + + let vis = &self.vis; + + quote! { + #vis enum #inst_kind_name<#lt> { + #(#variants),* + } + + #vis enum #inst_kind_mut_name<#lt> { + #(#variants_mut),* + } + } + } + + fn impl_inherent_methods(&self) -> proc_macro2::TokenStream { + let insert_table_ent = |p: &syn::Path| { + let inst_name_snake = convert_to_snake(&p.segments.last().unwrap().ident.to_string()); + let cast_fn_name = quote::format_ident!("cast_{inst_name_snake}"); + let cast_mut_fn_name = quote::format_ident!("{cast_fn_name}_mut"); + let ident = &self.ident; + let inst_kind_name = &self.inst_kind_name; + let inst_kind_mut_name = &self.inst_kind_mut_name; + let variant_name = self.variant_name_from_inst_path(p); + + quote! { + let tid = std::any::TypeId::of::<#p>(); + fn #cast_fn_name<'i>(self_: &#ident, inst: &'i dyn Inst) -> #inst_kind_name<'i> { + let inst = #p::cast(self_, inst).unwrap(); + #inst_kind_name::#variant_name(inst) + } + fn #cast_mut_fn_name<'i>(self_: &#ident, inst: &'i mut dyn Inst) -> #inst_kind_mut_name<'i> { + let inst = #p::cast_mut(self_, inst).unwrap(); + #inst_kind_mut_name::#variant_name(inst) + } + + let f: &'static for<'a, 'i> fn(&'a #ident, &'i dyn Inst) -> #inst_kind_name<'i> = + &(#cast_fn_name as for<'a, 'i> fn(&'a #ident, &'i dyn Inst) -> #inst_kind_name<'i>); + let f_mut: &'static for<'a, 'i> fn(&'a #ident, &'i mut dyn Inst) -> #inst_kind_mut_name<'i> = + &(#cast_mut_fn_name as for<'a, 'i> fn(&'a #ident, &'i mut dyn Inst) -> #inst_kind_mut_name<'i>); + table.insert(tid, (f, f_mut)); + + } + }; + + let insert_ents = self.insts.iter().map(insert_table_ent); + let ctor = quote! { + pub(crate) fn new() -> Self { + let mut table = ::rustc_hash::FxHashMap::default(); + #(#insert_ents)* + Self { table } + } + }; + + let ident = &self.ident; + quote! { + impl #ident { + #ctor + } } } @@ -144,32 +242,32 @@ impl InstSet { } } - fn define_inst_kind(&self) -> proc_macro2::TokenStream { - let lt = syn::Lifetime::new("'i", proc_macro2::Span::call_site()); - - let variants = self.insts.iter().map(|p| { - let variant_name = p.segments.last().unwrap(); - quote! { #variant_name(&#lt #p) } - }); - - let variants_mut = self.insts.iter().map(|p| { - let variant_name = p.segments.last().unwrap(); - quote! { #variant_name(&#lt mut #p) } - }); - + fn impl_inst_set_ext(&self) -> proc_macro2::TokenStream { + let ident = &self.ident; let inst_kind_name = &self.inst_kind_name; - let inst_kind_mut_name = quote::format_ident!("{inst_kind_name}Mut"); - - let vis = &self.vis; + let inst_kind_mut_name = &self.inst_kind_mut_name; quote! { - #vis enum #inst_kind_name<#lt> { - #(#variants),* - } + impl crate::prelude::InstSetExt for #ident { + type InstKind<'i> = #inst_kind_name<'i>; + type InstKindMut<'i> = #inst_kind_mut_name<'i>; - #vis enum #inst_kind_mut_name<#lt> { - #(#variants_mut),* - } + fn resolve_inst<'i>(&self, inst: &'i dyn Inst) -> Self::InstKind<'i> { + let tid = inst.type_id(); + debug_assert!(self.table.contains_key(&tid)); + self.table[&tid].0(self, inst) + } + + fn resolve_inst_mut<'i>(&self, inst: &'i mut dyn Inst) -> Self::InstKindMut<'i> { + let tid = inst.type_id(); + debug_assert!(self.table.contains_key(&tid)); + self.table[&tid].1(self, inst) + } + } } } + + fn variant_name_from_inst_path<'a>(&self, p: &'a syn::Path) -> &'a syn::Ident { + &p.segments.last().unwrap().ident + } } From 1deeb4eb3b4362170cf8e35c1d942f4dd41dea1b Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 10 Sep 2024 22:52:50 +0200 Subject: [PATCH 17/26] Add runtime tests for macro-generated instruction sets --- crates/ir/src/inst/inst_set.rs | 75 +++++++++++++++++++++++++++++++--- crates/ir/src/lib.rs | 4 +- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/crates/ir/src/inst/inst_set.rs b/crates/ir/src/inst/inst_set.rs index e5220dd3..b99d8ffd 100644 --- a/crates/ir/src/inst/inst_set.rs +++ b/crates/ir/src/inst/inst_set.rs @@ -48,10 +48,75 @@ define_inst_set_base! { basic::Nop, } -pub trait StaticInstSet: InstSetBase { - type InstKind; - type InstKindMut; +pub trait InstSetExt: InstSetBase { + type InstKind<'i>; + type InstKindMut<'i>; - fn resolve_inst(inst: &dyn Inst) -> Self::InstKind; - fn resolve_inst_mut(inst: &mut dyn Inst) -> Self::InstKindMut; + fn resolve_inst<'i>(&self, inst: &'i dyn Inst) -> Self::InstKind<'i>; + fn resolve_inst_mut<'i>(&self, inst: &'i mut dyn Inst) -> Self::InstKindMut<'i>; +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::Value; + use basic::*; + use macros::inst_set; + + #[inst_set(InstKind = "TestInstSetKind")] + struct TestInstSet(Add, Sub, Not, Phi, Jump); + + #[test] + fn ctor() { + let _ = TestInstSet::new(); + } + + #[test] + fn test_cast_isa() { + let inst_set = TestInstSet::new(); + assert!(inst_set.has_add().is_some()); + assert!(inst_set.has_sub().is_some()); + assert!(inst_set.has_not().is_some()); + assert!(inst_set.has_phi().is_some()); + assert!(inst_set.has_jump().is_some()); + + assert!(inst_set.has_lt().is_none()); + assert!(inst_set.has_br().is_none()); + } + + #[test] + fn inst_creation() { + let inst_set = TestInstSet::new(); + let v = Value::from_u32(1); + let _add = Add::new(&inst_set, v, v); + let _sub = Sub::new(&inst_set, v, v); + } + + #[test] + fn inst_resolution() { + let inst_set = TestInstSet::new(); + let mut insts: Vec> = Vec::new(); + + let value = Value::from_u32(1); + let add = Add::new(&inst_set, value, value); + insts.push(Box::new(add)); + let sub = Sub::new(&inst_set, value, value); + insts.push(Box::new(sub)); + let not = Not::new(&inst_set, value); + insts.push(Box::new(not)); + + let resolved = inst_set.resolve_inst(insts[0].as_ref()); + assert!(matches!(resolved, TestInstSetKind::Add(_))); + let resolved = inst_set.resolve_inst(insts[1].as_ref()); + assert!(matches!(resolved, TestInstSetKind::Sub(_))); + let resolved = inst_set.resolve_inst(insts[2].as_ref()); + assert!(matches!(resolved, TestInstSetKind::Not(_))); + + let resolved = inst_set.resolve_inst_mut(insts[0].as_mut()); + assert!(matches!(resolved, TestInstSetKindMut::Add(_))); + let resolved = inst_set.resolve_inst_mut(insts[1].as_mut()); + assert!(matches!(resolved, TestInstSetKindMut::Sub(_))); + let resolved = inst_set.resolve_inst_mut(insts[2].as_mut()); + assert!(matches!(resolved, TestInstSetKindMut::Not(_))); + } } diff --git a/crates/ir/src/lib.rs b/crates/ir/src/lib.rs index 67bc4a99..0635e983 100644 --- a/crates/ir/src/lib.rs +++ b/crates/ir/src/lib.rs @@ -26,7 +26,7 @@ pub use global_variable::{GlobalVariable, GlobalVariableData}; pub use graphviz::render_to; pub use insn::{BranchInfo, DataLocationKind, Insn, InsnData}; pub use inst::{ - inst_set::{InstSetBase, StaticInstSet}, + inst_set::{InstSetBase, InstSetExt}, HasInst, Inst, }; pub use layout::Layout; @@ -39,7 +39,7 @@ pub(crate) use inst::ValueVisitable; pub mod prelude { pub use crate::inst::{ - inst_set::{InstSetBase, StaticInstSet}, + inst_set::{InstSetBase, InstSetExt}, HasInst, Inst, }; } From 23e2143ad7a06258f58446c624af7d21bef42d49 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 11 Sep 2024 10:37:42 +0200 Subject: [PATCH 18/26] Simplify `has_side_effect` specifier --- crates/ir/src/inst/basic.rs | 41 ++++++------------------------------- crates/macros/src/inst.rs | 24 +++++----------------- 2 files changed, 11 insertions(+), 54 deletions(-) diff --git a/crates/ir/src/inst/basic.rs b/crates/ir/src/inst/basic.rs index 4307718c..92694f64 100644 --- a/crates/ir/src/inst/basic.rs +++ b/crates/ir/src/inst/basic.rs @@ -4,21 +4,18 @@ use smallvec::SmallVec; use macros::Inst; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Not { #[inst(visit_value)] arg: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Neg { #[inst(visit_value)] arg: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Add { #[inst(visit_value)] lhs: Value, @@ -27,7 +24,6 @@ pub struct Add { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Mul { #[inst(visit_value)] lhs: Value, @@ -36,7 +32,6 @@ pub struct Mul { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Sub { #[inst(visit_value)] lhs: Value, @@ -45,7 +40,7 @@ pub struct Sub { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = true)] +#[inst(has_side_effect)] pub struct Sdiv { #[inst(visit_value)] lhs: Value, @@ -54,7 +49,7 @@ pub struct Sdiv { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = true)] +#[inst(has_side_effect)] pub struct Udiv { #[inst(visit_value)] lhs: Value, @@ -63,7 +58,6 @@ pub struct Udiv { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Lt { #[inst(visit_value)] lhs: Value, @@ -72,7 +66,6 @@ pub struct Lt { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Gt { #[inst(visit_value)] lhs: Value, @@ -81,7 +74,6 @@ pub struct Gt { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Slt { #[inst(visit_value)] lhs: Value, @@ -90,7 +82,6 @@ pub struct Slt { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Sgt { #[inst(visit_value)] lhs: Value, @@ -99,7 +90,6 @@ pub struct Sgt { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Le { #[inst(visit_value)] lhs: Value, @@ -108,7 +98,6 @@ pub struct Le { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Ge { #[inst(visit_value)] lhs: Value, @@ -117,7 +106,6 @@ pub struct Ge { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Sle { #[inst(visit_value)] lhs: Value, @@ -126,7 +114,6 @@ pub struct Sle { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Sge { #[inst(visit_value)] lhs: Value, @@ -135,7 +122,6 @@ pub struct Sge { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Eq { #[inst(visit_value)] lhs: Value, @@ -144,7 +130,6 @@ pub struct Eq { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Ne { #[inst(visit_value)] lhs: Value, @@ -153,7 +138,6 @@ pub struct Ne { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct And { #[inst(visit_value)] lhs: Value, @@ -162,7 +146,6 @@ pub struct And { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Or { #[inst(visit_value)] lhs: Value, @@ -171,7 +154,6 @@ pub struct Or { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Xor { #[inst(visit_value)] lhs: Value, @@ -180,7 +162,6 @@ pub struct Xor { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Sext { #[inst(visit_value)] from: Value, @@ -188,7 +169,6 @@ pub struct Sext { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Zext { #[inst(visit_value)] from: Value, @@ -196,7 +176,6 @@ pub struct Zext { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Trunc { #[inst(visit_value)] from: Value, @@ -204,7 +183,6 @@ pub struct Trunc { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Bitcast { #[inst(visit_value)] from: Value, @@ -212,14 +190,13 @@ pub struct Bitcast { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = true)] pub struct Mload { #[inst(visit_value)] addr: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = true)] +#[inst(has_side_effect)] pub struct Mstore { #[inst(visit_value)] value: Value, @@ -228,7 +205,7 @@ pub struct Mstore { } #[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = true)] +#[inst(has_side_effect)] pub struct Call { #[inst(visit_value)] args: SmallVec<[Value; 8]>, @@ -237,13 +214,11 @@ pub struct Call { } #[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Jump { dest: Block, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Br { #[inst(visit_value)] cond: Value, @@ -253,7 +228,6 @@ pub struct Br { } #[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct BrTable { #[inst(visit_value)] scrutinee: Value, @@ -264,27 +238,25 @@ pub struct BrTable { } #[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = true)] +#[inst(has_side_effect)] pub struct Alloca { ty: Type, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = true)] +#[inst(has_side_effect)] pub struct Return { #[inst(visit_value)] arg: Option, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Gep { #[inst(visit_value)] values: SmallVec<[Value; 8]>, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Phi { #[inst(visit_value)] values: Vec<(Value, Block)>, @@ -292,5 +264,4 @@ pub struct Phi { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(side_effect = false)] pub struct Nop {} diff --git a/crates/macros/src/inst.rs b/crates/macros/src/inst.rs index 1a3e64e9..7e8c7892 100644 --- a/crates/macros/src/inst.rs +++ b/crates/macros/src/inst.rs @@ -70,34 +70,20 @@ impl InstStruct { } fn check_side_effect_attr(item_struct: &syn::ItemStruct) -> syn::Result { - let mut has_side_effect = None; + let mut has_side_effect = false; for attr in &item_struct.attrs { if attr.path.is_ident("inst") { let meta = attr.parse_args::()?; - if let syn::Meta::NameValue(name_value) = meta { - if name_value.path.is_ident("side_effect") { - if has_side_effect.is_some() { - return Err(syn::Error::new_spanned( - item_struct, - "only one #[inst(side_effect = ...)]` attribute is allowed", - )); - } - - if let syn::Lit::Bool(bool_lit) = name_value.lit { - has_side_effect = Some(bool_lit.value); - } + if let syn::Meta::Path(path) = meta { + if path.is_ident("has_side_effect") { + has_side_effect = true; } } } } - has_side_effect.ok_or_else(|| { - syn::Error::new_spanned( - item_struct, - "unique #[inst(side_effect = ...)]` attributed is required", - ) - }) + Ok(has_side_effect) } fn parse_fields(fields: &syn::Fields) -> syn::Result> { From ae17d474ce284835707e31d9224920125adac60c Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 11 Sep 2024 12:19:23 +0200 Subject: [PATCH 19/26] Simplify inst def --- crates/ir/src/inst/basic.rs | 104 ++++++++++++++++++------------------ crates/macros/src/inst.rs | 17 +++--- 2 files changed, 59 insertions(+), 62 deletions(-) diff --git a/crates/ir/src/inst/basic.rs b/crates/ir/src/inst/basic.rs index 92694f64..8bee0e41 100644 --- a/crates/ir/src/inst/basic.rs +++ b/crates/ir/src/inst/basic.rs @@ -5,209 +5,209 @@ use macros::Inst; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Not { - #[inst(visit_value)] + #[inst(value)] arg: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Neg { - #[inst(visit_value)] + #[inst(value)] arg: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Add { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Mul { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Sub { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] #[inst(has_side_effect)] pub struct Sdiv { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] #[inst(has_side_effect)] pub struct Udiv { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Lt { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Gt { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Slt { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Sgt { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Le { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Ge { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Sle { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Sge { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Eq { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Ne { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct And { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Or { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Xor { - #[inst(visit_value)] + #[inst(value)] lhs: Value, - #[inst(visit_value)] + #[inst(value)] rhs: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Sext { - #[inst(visit_value)] + #[inst(value)] from: Value, ty: Type, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Zext { - #[inst(visit_value)] + #[inst(value)] from: Value, ty: Type, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Trunc { - #[inst(visit_value)] + #[inst(value)] from: Value, ty: Type, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Bitcast { - #[inst(visit_value)] + #[inst(value)] from: Value, ty: Type, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Mload { - #[inst(visit_value)] + #[inst(value)] addr: Value, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] #[inst(has_side_effect)] pub struct Mstore { - #[inst(visit_value)] + #[inst(value)] value: Value, - #[inst(visit_value)] + #[inst(value)] addr: Value, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] #[inst(has_side_effect)] pub struct Call { - #[inst(visit_value)] + #[inst(value)] args: SmallVec<[Value; 8]>, callee: FuncRef, ret_ty: Type, @@ -220,7 +220,7 @@ pub struct Jump { #[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] pub struct Br { - #[inst(visit_value)] + #[inst(value)] cond: Value, z_dest: Block, @@ -229,9 +229,9 @@ pub struct Br { #[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] pub struct BrTable { - #[inst(visit_value)] + #[inst(value)] scrutinee: Value, - #[inst(visit_value)] + #[inst(value)] table: Vec<(Value, Block)>, default: Option, @@ -246,19 +246,19 @@ pub struct Alloca { #[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] #[inst(has_side_effect)] pub struct Return { - #[inst(visit_value)] + #[inst(value)] arg: Option, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] pub struct Gep { - #[inst(visit_value)] + #[inst(value)] values: SmallVec<[Value; 8]>, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] pub struct Phi { - #[inst(visit_value)] + #[inst(value)] values: Vec<(Value, Block)>, ty: Type, } diff --git a/crates/macros/src/inst.rs b/crates/macros/src/inst.rs index 7e8c7892..e87ab5cd 100644 --- a/crates/macros/src/inst.rs +++ b/crates/macros/src/inst.rs @@ -24,7 +24,7 @@ struct InstStruct { struct InstField { ident: syn::Ident, ty: syn::Type, - visit_value: bool, + value: bool, } impl InstStruct { @@ -97,7 +97,7 @@ impl InstStruct { let mut inst_fields = Vec::new(); for field in &fields.named { - let mut visit_value = false; + let mut value = false; if !matches!(field.vis, syn::Visibility::Inherited) { return Err(syn::Error::new_spanned( @@ -110,13 +110,10 @@ impl InstStruct { if attr.path.is_ident("inst") { let meta = attr.parse_args::()?; if let syn::Meta::Path(path) = meta { - if path.is_ident("visit_value") { - visit_value = true; + if path.is_ident("value") { + value = true; } else { - return Err(syn::Error::new_spanned( - attr, - "only `visit_value` is allowed", - )); + return Err(syn::Error::new_spanned(attr, "only `value` is allowed")); } } } @@ -125,7 +122,7 @@ impl InstStruct { inst_fields.push(InstField { ident: field.ident.clone().unwrap(), ty: field.ty.clone(), - visit_value, + value, }); } @@ -200,7 +197,7 @@ impl InstStruct { let visit_fields: Vec<_> = self .fields .iter() - .filter(|f| f.visit_value) + .filter(|f| f.value) .map(|f| &f.ident) .collect(); let text_form = convert_to_snake(&self.struct_name.to_string()); From 35f6052d59f02b2fa46dbf11337afeefd58682a8 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 11 Sep 2024 12:21:27 +0200 Subject: [PATCH 20/26] Allow attributes in `InstSetBase` definition --- crates/ir/src/inst/inst_set.rs | 72 +++++++++++++++--------------- crates/macros/src/inst_set_base.rs | 29 +++++++++--- 2 files changed, 60 insertions(+), 41 deletions(-) diff --git a/crates/ir/src/inst/inst_set.rs b/crates/ir/src/inst/inst_set.rs index b99d8ffd..ba54096b 100644 --- a/crates/ir/src/inst/inst_set.rs +++ b/crates/ir/src/inst/inst_set.rs @@ -11,42 +11,42 @@ pub(super) mod sealed { // All instructions defined in the IR must be listed(otherwise, you'll get a compile error). define_inst_set_base! { - basic::Not, - basic::Neg, - basic::Add, - basic::Mul, - basic::Sub, - basic::Sdiv, - basic::Udiv, - basic::Lt, - basic::Gt, - basic::Slt, - basic::Sgt, - basic::Le, - basic::Ge, - basic::Sle, - basic::Sge, - basic::Eq, - basic::Ne, - basic::And, - basic::Or, - basic::Xor, - basic::Sext, - basic::Zext, - basic::Trunc, - basic::Bitcast, - basic::Mload, - basic::Mstore, - basic::Call, - basic::Jump, - basic::Br, - basic::BrTable, - basic::Alloca, - basic::Return, - basic::Gep, - basic::Phi, - basic::Nop, -} + basic::Not, + basic::Neg, + basic::Add, + basic::Mul, + basic::Sub, + basic::Sdiv, + basic::Udiv, + basic::Lt, + basic::Gt, + basic::Slt, + basic::Sgt, + basic::Le, + basic::Ge, + basic::Sle, + basic::Sge, + basic::Eq, + basic::Ne, + basic::And, + basic::Or, + basic::Xor, + basic::Sext, + basic::Zext, + basic::Trunc, + basic::Bitcast, + basic::Mload, + basic::Mstore, + basic::Call, + basic::Jump, + basic::Br, + basic::BrTable, + basic::Alloca, + basic::Return, + basic::Gep, + basic::Phi, + basic::Nop, + } pub trait InstSetExt: InstSetBase { type InstKind<'i>; diff --git a/crates/macros/src/inst_set_base.rs b/crates/macros/src/inst_set_base.rs index f7cfcd3f..6801c54f 100644 --- a/crates/macros/src/inst_set_base.rs +++ b/crates/macros/src/inst_set_base.rs @@ -10,7 +10,10 @@ pub fn define_inst_set_base(input: proc_macro::TokenStream) -> proc_macro::Token } } -struct TraitDefinition(syn::punctuated::Punctuated); +struct TraitDefinition { + attrs: Vec, + insts: syn::punctuated::Punctuated, +} impl TraitDefinition { fn build(self) -> syn::Result { @@ -24,13 +27,16 @@ impl TraitDefinition { } fn define_trait(&self) -> proc_macro2::TokenStream { - let methods = self.0.iter().map(|path| { + let methods = self.insts.iter().map(|path| { let method_name = path_to_method_name(path); quote! { fn #method_name(&self) -> Option<&dyn crate::HasInst<#path>> { None } } }); + let attrs = &self.attrs; + quote! { + #(#attrs)* pub trait InstSetBase { #(#methods)* } @@ -38,7 +44,7 @@ impl TraitDefinition { } fn impl_registered(&self) -> proc_macro2::TokenStream { - let impls = self.0.iter().map(|path| { + let impls = self.insts.iter().map(|path| { quote! { impl crate::inst::inst_set::sealed::Registered for #path {} } @@ -52,9 +58,22 @@ impl TraitDefinition { impl syn::parse::Parse for TraitDefinition { fn parse(input: syn::parse::ParseStream) -> syn::Result { + let attrs = input.call(syn::Attribute::parse_outer)?; + input.parse::()?; + let ident = input.parse::()?; + if ident != "InstSetBase" { + return Err(syn::Error::new_spanned( + ident, + "the trait name must be `InstSetBase`", + )); + } + let content; + syn::braced!(content in input); + let insts = - syn::punctuated::Punctuated::::parse_terminated(input)?; - Ok(Self(insts)) + syn::punctuated::Punctuated::::parse_terminated(&content)?; + + Ok(Self { attrs, insts }) } } From 1be6d6e2894a2aebd0c67a35f2275a149a78d73c Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 11 Sep 2024 12:21:43 +0200 Subject: [PATCH 21/26] Docwork --- crates/ir/src/inst/inst_set.rs | 68 ++++++++++++++++++++++++++-------- crates/macros/src/lib.rs | 53 ++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 15 deletions(-) diff --git a/crates/ir/src/inst/inst_set.rs b/crates/ir/src/inst/inst_set.rs index ba54096b..90fc3bde 100644 --- a/crates/ir/src/inst/inst_set.rs +++ b/crates/ir/src/inst/inst_set.rs @@ -2,15 +2,16 @@ use super::{basic, Inst}; use macros::define_inst_set_base; -pub(super) mod sealed { - /// This trait has two roles, - /// 1. works as a sealed trait. - /// 2. ensure that an `Inst` is definitely registered to the `InstGroup`. - pub trait Registered {} -} - -// All instructions defined in the IR must be listed(otherwise, you'll get a compile error). define_inst_set_base! { + /// This trait is used to determine whether a certain instruction set includes a specific inst in runtime. + /// If a certain instruction set `IS` implements `HasInst`, + /// the corresponding `has_i(&self) -> Option<&dyn HasInst>` method always returns `Some`. + /// + /// Since all instruction set implements `HasInst` if it containst `Inst`, + /// this trait is naturally intened to be used as a trait object. + /// + /// NOTE: Do NOT implement this trait manually, use `sonatina-macro::inst_set` instead. + trait InstSetBase { basic::Not, basic::Neg, basic::Add, @@ -47,7 +48,37 @@ define_inst_set_base! { basic::Phi, basic::Nop, } +} +/// This trait provides the concrete mapping from `Inst` to corresponding enum variant. +/// All instruction set that are defined by `sonatina_macros::inst_set` automatically defines an enum which represents all instructions in the set. +/// e.g. +/// +/// ```rust,ignore +/// use sonatina_ir::inst::basic::*; +/// #[inst_set(InstKind = "InstKind")] +/// struct InstSet(Add, Sub); +/// ``` +/// defines +/// +/// ```rust +/// use sonatina_ir::inst::basic::*; +/// enum InstKind<'i> { +/// Add(&'i Add), +/// Sub(&'i Sub), +/// } +/// enum InstKindMut<'i> { +/// Add(&'i mut Add), +/// Sub(&'i mut Sub), +/// } +/// ``` +/// +/// Assuming that the all instructions are created with this instruction set, +/// the cast(resolution) from dynamic inst object to this enum always succeed. +/// +/// This macro provides the way to these safe downcast, and allow us to focus on the +/// restricted concrete instruction set, instead of "all possible" instructions. +/// pub trait InstSetExt: InstSetBase { type InstKind<'i>; type InstKindMut<'i>; @@ -63,7 +94,7 @@ mod tests { use basic::*; use macros::inst_set; - #[inst_set(InstKind = "TestInstSetKind")] + #[inst_set(InstKind = "TestInstKind")] struct TestInstSet(Add, Sub, Not, Phi, Jump); #[test] @@ -106,17 +137,24 @@ mod tests { insts.push(Box::new(not)); let resolved = inst_set.resolve_inst(insts[0].as_ref()); - assert!(matches!(resolved, TestInstSetKind::Add(_))); + assert!(matches!(resolved, TestInstKind::Add(_))); let resolved = inst_set.resolve_inst(insts[1].as_ref()); - assert!(matches!(resolved, TestInstSetKind::Sub(_))); + assert!(matches!(resolved, TestInstKind::Sub(_))); let resolved = inst_set.resolve_inst(insts[2].as_ref()); - assert!(matches!(resolved, TestInstSetKind::Not(_))); + assert!(matches!(resolved, TestInstKind::Not(_))); let resolved = inst_set.resolve_inst_mut(insts[0].as_mut()); - assert!(matches!(resolved, TestInstSetKindMut::Add(_))); + assert!(matches!(resolved, TestInstKindMut::Add(_))); let resolved = inst_set.resolve_inst_mut(insts[1].as_mut()); - assert!(matches!(resolved, TestInstSetKindMut::Sub(_))); + assert!(matches!(resolved, TestInstKindMut::Sub(_))); let resolved = inst_set.resolve_inst_mut(insts[2].as_mut()); - assert!(matches!(resolved, TestInstSetKindMut::Not(_))); + assert!(matches!(resolved, TestInstKindMut::Not(_))); } } + +pub(super) mod sealed { + /// This trait has two roles, + /// 1. works as a sealed trait. + /// 2. ensure that an `Inst` is definitely registered to the `InstGroup`. + pub trait Registered {} +} diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs index 95f274c9..8b850045 100644 --- a/crates/macros/src/lib.rs +++ b/crates/macros/src/lib.rs @@ -2,6 +2,30 @@ mod inst; mod inst_set; mod inst_set_base; +/// A derive macro to define each instruction type. +/// This macro dervies the `Isnt` trait for the macro, +/// and implements a consructor and acccessors for each fields. +/// +/// # Usage +/// ```rust, ignore +/// use sonatina_macros::Inst; +/// +/// #[derive(Inst)] +/// #[inst(has_side_effect)] +/// struct MStore { +/// #[inst(value)] +/// lhs: Value, +/// #[inst(value)] +/// rhs: Value, +/// } +/// ``` +/// +/// # Arguments +/// - `has_side_effect`: Marks the instruction as having a side effect. +/// - `value`: Marks the field that contains value, +/// the specified field must implements `sonatina-ir::inst::ValueVisitable` trait. +/// +/// # Usage #[proc_macro_derive(Inst, attributes(inst))] pub fn derive_inst(item: proc_macro::TokenStream) -> proc_macro::TokenStream { inst::derive_inst(item) @@ -12,6 +36,27 @@ pub fn define_inst_set_base(input: proc_macro::TokenStream) -> proc_macro::Token inst_set_base::define_inst_set_base(input) } +/// A macro to define an instruction set that is specific to an target arch. +/// In sonatina, an InstructionSet is defined as a type that implements `HasInst<{Inst}>` for all `{Inst}` it contains, +/// and also implements `InstSetBase` and `InstSetExt`. +/// This macro automatically implements these traits and modify the type definition to enable an effective cast of instruction. +/// +/// # Usage +/// ```rust, ignore +/// #[inst_set(InstKind = "TestInstKind")] +/// struct TestInstSet(Add, Sub); +/// ``` +/// +/// # Arguments +/// ## InstKind = "TestInstKind"` +/// This arguments specifies an `enum` used in `InstSetExt::InstKind`. This enum is also generated automatically. +/// In the abobe example, the below enum is generated, and can be obtained via `InstSetExt::resolve_inst` method. +/// ```rust, ignore +/// enum TestInstKind<'i> { +/// Add(&'i Add), +/// Sub(&'i Sub), +/// } +/// ``` #[proc_macro_attribute] pub fn inst_set( attr: proc_macro::TokenStream, @@ -20,6 +65,14 @@ pub fn inst_set( inst_set::define_inst_set(attr, input) } +/// Converts a given string to snake case. +/// +/// The function iterates through each character in the string. If the character is uppercase, +/// it checks if the previous character was also uppercase. If it wasn't, it adds an underscore before +/// the current character. It then converts the character to lowercase and adds it to the result string. +/// e.g., +/// * `FooBar -> foo_bar` +/// * `FooBAR -> foo_bar` fn convert_to_snake(s: &str) -> String { let mut res = String::new(); let mut is_upper = false; From 84dfc541ff836b8d111e34125cd19c6afa80e232 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 11 Sep 2024 21:07:35 +0200 Subject: [PATCH 22/26] Cleanup inst module structure --- crates/ir/src/inst/{evm.rs => access.rs} | 0 crates/ir/src/inst/arith.rs | 51 +++++ crates/ir/src/inst/basic.rs | 267 ----------------------- crates/ir/src/inst/cast.rs | 31 +++ crates/ir/src/inst/cmp.rs | 83 +++++++ crates/ir/src/inst/control_flow.rs | 54 +++++ crates/ir/src/inst/data.rs | 31 +++ crates/ir/src/inst/evm/mod.rs | 0 crates/ir/src/inst/inst_set.rs | 76 +++---- crates/ir/src/inst/logic.rs | 33 +++ crates/ir/src/inst/mod.rs | 8 +- 11 files changed, 329 insertions(+), 305 deletions(-) rename crates/ir/src/inst/{evm.rs => access.rs} (100%) create mode 100644 crates/ir/src/inst/arith.rs delete mode 100644 crates/ir/src/inst/basic.rs create mode 100644 crates/ir/src/inst/cast.rs create mode 100644 crates/ir/src/inst/cmp.rs create mode 100644 crates/ir/src/inst/control_flow.rs create mode 100644 crates/ir/src/inst/data.rs create mode 100644 crates/ir/src/inst/evm/mod.rs create mode 100644 crates/ir/src/inst/logic.rs diff --git a/crates/ir/src/inst/evm.rs b/crates/ir/src/inst/access.rs similarity index 100% rename from crates/ir/src/inst/evm.rs rename to crates/ir/src/inst/access.rs diff --git a/crates/ir/src/inst/arith.rs b/crates/ir/src/inst/arith.rs new file mode 100644 index 00000000..400553ae --- /dev/null +++ b/crates/ir/src/inst/arith.rs @@ -0,0 +1,51 @@ +use macros::Inst; + +use crate::Value; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Neg { + #[inst(value)] + arg: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Add { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Mul { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Sub { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct Sdiv { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct Udiv { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} diff --git a/crates/ir/src/inst/basic.rs b/crates/ir/src/inst/basic.rs deleted file mode 100644 index 8bee0e41..00000000 --- a/crates/ir/src/inst/basic.rs +++ /dev/null @@ -1,267 +0,0 @@ -use crate::{module::FuncRef, Block, Type, Value}; -use smallvec::SmallVec; - -use macros::Inst; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Not { - #[inst(value)] - arg: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Neg { - #[inst(value)] - arg: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Add { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Mul { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Sub { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(has_side_effect)] -pub struct Sdiv { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(has_side_effect)] -pub struct Udiv { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Lt { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Gt { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Slt { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Sgt { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Le { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Ge { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Sle { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Sge { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Eq { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Ne { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct And { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Or { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Xor { - #[inst(value)] - lhs: Value, - #[inst(value)] - rhs: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Sext { - #[inst(value)] - from: Value, - ty: Type, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Zext { - #[inst(value)] - from: Value, - ty: Type, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Trunc { - #[inst(value)] - from: Value, - ty: Type, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Bitcast { - #[inst(value)] - from: Value, - ty: Type, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Mload { - #[inst(value)] - addr: Value, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -#[inst(has_side_effect)] -pub struct Mstore { - #[inst(value)] - value: Value, - #[inst(value)] - addr: Value, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -#[inst(has_side_effect)] -pub struct Call { - #[inst(value)] - args: SmallVec<[Value; 8]>, - callee: FuncRef, - ret_ty: Type, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -pub struct Jump { - dest: Block, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -pub struct Br { - #[inst(value)] - cond: Value, - - z_dest: Block, - nz_dest: Block, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -pub struct BrTable { - #[inst(value)] - scrutinee: Value, - #[inst(value)] - table: Vec<(Value, Block)>, - - default: Option, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -#[inst(has_side_effect)] -pub struct Alloca { - ty: Type, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -#[inst(has_side_effect)] -pub struct Return { - #[inst(value)] - arg: Option, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -pub struct Gep { - #[inst(value)] - values: SmallVec<[Value; 8]>, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -pub struct Phi { - #[inst(value)] - values: Vec<(Value, Block)>, - ty: Type, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Nop {} diff --git a/crates/ir/src/inst/cast.rs b/crates/ir/src/inst/cast.rs new file mode 100644 index 00000000..b184b1a0 --- /dev/null +++ b/crates/ir/src/inst/cast.rs @@ -0,0 +1,31 @@ +use macros::Inst; + +use crate::{Type, Value}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Sext { + #[inst(value)] + from: Value, + ty: Type, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Zext { + #[inst(value)] + from: Value, + ty: Type, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Trunc { + #[inst(value)] + from: Value, + ty: Type, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Bitcast { + #[inst(value)] + from: Value, + ty: Type, +} diff --git a/crates/ir/src/inst/cmp.rs b/crates/ir/src/inst/cmp.rs new file mode 100644 index 00000000..13eca7e4 --- /dev/null +++ b/crates/ir/src/inst/cmp.rs @@ -0,0 +1,83 @@ +use macros::Inst; + +use crate::Value; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Lt { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Gt { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Slt { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Sgt { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Le { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Ge { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Sle { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Sge { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Eq { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Ne { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} diff --git a/crates/ir/src/inst/control_flow.rs b/crates/ir/src/inst/control_flow.rs new file mode 100644 index 00000000..84ba43ba --- /dev/null +++ b/crates/ir/src/inst/control_flow.rs @@ -0,0 +1,54 @@ +use macros::Inst; +use smallvec::SmallVec; + +use crate::{module::FuncRef, Block, Type, Value}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +pub struct Jump { + dest: Block, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +pub struct Br { + #[inst(value)] + cond: Value, + + z_dest: Block, + nz_dest: Block, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +pub struct BrTable { + #[inst(value)] + scrutinee: Value, + #[inst(value)] + table: Vec<(Value, Block)>, + + default: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct Call { + #[inst(value)] + args: SmallVec<[Value; 8]>, + callee: FuncRef, + ret_ty: Type, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct Return { + #[inst(value)] + arg: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +pub struct Phi { + #[inst(value)] + values: Vec<(Value, Block)>, + ty: Type, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Nop {} diff --git a/crates/ir/src/inst/data.rs b/crates/ir/src/inst/data.rs new file mode 100644 index 00000000..a60556fd --- /dev/null +++ b/crates/ir/src/inst/data.rs @@ -0,0 +1,31 @@ +use macros::Inst; +use smallvec::SmallVec; + +use crate::{module::FuncRef, Type, Value}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Mload { + #[inst(value)] + addr: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct Mstore { + #[inst(value)] + value: Value, + #[inst(value)] + addr: Value, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct Alloca { + ty: Type, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +pub struct Gep { + #[inst(value)] + values: SmallVec<[Value; 8]>, +} diff --git a/crates/ir/src/inst/evm/mod.rs b/crates/ir/src/inst/evm/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/crates/ir/src/inst/inst_set.rs b/crates/ir/src/inst/inst_set.rs index 90fc3bde..fa1cf121 100644 --- a/crates/ir/src/inst/inst_set.rs +++ b/crates/ir/src/inst/inst_set.rs @@ -1,4 +1,4 @@ -use super::{basic, Inst}; +use super::{arith, basic, cast, cmp, control_flow, data, logic, Inst}; use macros::define_inst_set_base; @@ -12,41 +12,41 @@ define_inst_set_base! { /// /// NOTE: Do NOT implement this trait manually, use `sonatina-macro::inst_set` instead. trait InstSetBase { - basic::Not, - basic::Neg, - basic::Add, - basic::Mul, - basic::Sub, - basic::Sdiv, - basic::Udiv, - basic::Lt, - basic::Gt, - basic::Slt, - basic::Sgt, - basic::Le, - basic::Ge, - basic::Sle, - basic::Sge, - basic::Eq, - basic::Ne, - basic::And, - basic::Or, - basic::Xor, - basic::Sext, - basic::Zext, - basic::Trunc, - basic::Bitcast, - basic::Mload, - basic::Mstore, - basic::Call, - basic::Jump, - basic::Br, - basic::BrTable, - basic::Alloca, - basic::Return, - basic::Gep, - basic::Phi, - basic::Nop, + arith::Neg, + arith::Add, + arith::Mul, + arith::Sub, + arith::Sdiv, + arith::Udiv, + cmp::Lt, + cmp::Gt, + cmp::Slt, + cmp::Sgt, + cmp::Le, + cmp::Ge, + cmp::Sle, + cmp::Sge, + cmp::Eq, + cmp::Ne, + logic::Not, + logic::And, + logic::Or, + logic::Xor, + cast::Sext, + cast::Zext, + cast::Trunc, + cast::Bitcast, + data::Mload, + data::Mstore, + data::Alloca, + data::Gep, + control_flow::Call, + control_flow::Jump, + control_flow::Br, + control_flow::BrTable, + control_flow::Return, + control_flow::Phi, + control_flow::Nop, } } @@ -91,7 +91,9 @@ pub trait InstSetExt: InstSetBase { mod tests { use super::*; use crate::Value; - use basic::*; + use arith::*; + use control_flow::*; + use logic::*; use macros::inst_set; #[inst_set(InstKind = "TestInstKind")] diff --git a/crates/ir/src/inst/logic.rs b/crates/ir/src/inst/logic.rs new file mode 100644 index 00000000..e617bdff --- /dev/null +++ b/crates/ir/src/inst/logic.rs @@ -0,0 +1,33 @@ +use macros::Inst; + +use crate::Value; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Not { + #[inst(value)] + arg: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct And { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Or { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Xor { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} diff --git a/crates/ir/src/inst/mod.rs b/crates/ir/src/inst/mod.rs index bdc1b15f..7e6c6415 100644 --- a/crates/ir/src/inst/mod.rs +++ b/crates/ir/src/inst/mod.rs @@ -1,5 +1,11 @@ -pub mod basic; +pub mod access; +pub mod arith; +pub mod cast; +pub mod cmp; +pub mod control_flow; +pub mod data; pub mod inst_set; +pub mod logic; use std::any::{Any, TypeId}; From 9d5d3e94e54ba7594f87e8e0a60a52271f44b9e0 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 11 Sep 2024 23:15:31 +0200 Subject: [PATCH 23/26] Define evm instructions --- crates/ir/src/inst/arith.rs | 42 ++++ crates/ir/src/inst/cmp.rs | 6 + crates/ir/src/inst/data.rs | 2 +- crates/ir/src/inst/evm/mod.rs | 397 +++++++++++++++++++++++++++++++++ crates/ir/src/inst/inst_set.rs | 63 +++++- crates/ir/src/inst/mod.rs | 1 + 6 files changed, 508 insertions(+), 3 deletions(-) diff --git a/crates/ir/src/inst/arith.rs b/crates/ir/src/inst/arith.rs index 400553ae..a198d90b 100644 --- a/crates/ir/src/inst/arith.rs +++ b/crates/ir/src/inst/arith.rs @@ -49,3 +49,45 @@ pub struct Udiv { #[inst(value)] rhs: Value, } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct Umod { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct Smod { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Shl { + #[inst(value)] + bits: Value, + #[inst(value)] + value: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Shr { + #[inst(value)] + bits: Value, + #[inst(value)] + value: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct Sar { + #[inst(value)] + bits: Value, + #[inst(value)] + value: Value, +} diff --git a/crates/ir/src/inst/cmp.rs b/crates/ir/src/inst/cmp.rs index 13eca7e4..4e106ae6 100644 --- a/crates/ir/src/inst/cmp.rs +++ b/crates/ir/src/inst/cmp.rs @@ -81,3 +81,9 @@ pub struct Ne { #[inst(value)] rhs: Value, } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct IsZero { + #[inst(value)] + lhs: Value, +} diff --git a/crates/ir/src/inst/data.rs b/crates/ir/src/inst/data.rs index a60556fd..bf50b4c5 100644 --- a/crates/ir/src/inst/data.rs +++ b/crates/ir/src/inst/data.rs @@ -1,7 +1,7 @@ use macros::Inst; use smallvec::SmallVec; -use crate::{module::FuncRef, Type, Value}; +use crate::{Type, Value}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Mload { diff --git a/crates/ir/src/inst/evm/mod.rs b/crates/ir/src/inst/evm/mod.rs index e69de29b..a0368052 100644 --- a/crates/ir/src/inst/evm/mod.rs +++ b/crates/ir/src/inst/evm/mod.rs @@ -0,0 +1,397 @@ +use macros::Inst; + +use crate::Value; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmStop {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmAddMod { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, + #[inst(value)] + modulus: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmMulMod { + #[inst(value)] + lhs: Value, + #[inst(value)] + rhs: Value, + #[inst(value)] + modulus: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmExp { + #[inst(value)] + base: Value, + #[inst(value)] + exponent: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmByte { + #[inst(value)] + pos: Value, + #[inst(value)] + value: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmKeccak256 { + #[inst(value)] + addr: Value, + #[inst(value)] + len: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmAddress {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmBalance { + #[inst(value)] + contract_addr: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmOrigin {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmCaller {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmCallValue {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmCallDataLoad { + data_offset: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmCallDataCopy { + #[inst(value)] + dst_addr: Value, + #[inst(value)] + data_offset: Value, + #[inst(value)] + len: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmCodeSize {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmCodeCopy { + #[inst(value)] + dst_addr: Value, + #[inst(value)] + code_offset: Value, + #[inst(value)] + len: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmGasPrice {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmExtCodeSize { + #[inst(value)] + ext_addr: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmExtCodeCopy { + #[inst(value)] + ext_addr: Value, + #[inst(value)] + dst_addr: Value, + #[inst(value)] + code_offset: Value, + #[inst(value)] + len: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmReturnDataSize {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmReturnDataCopy { + #[inst(value)] + dst_addr: Value, + #[inst(value)] + data_offset: Value, + #[inst(value)] + len: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmExtCodeHash { + #[inst(value)] + ext_addr: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmBlockHash { + #[inst(value)] + block_num: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmCoinBase { + #[inst(value)] + block_num: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmTimestamp {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmNumber {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmPrevRandao {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmGasLimit {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmChainId {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmSelfBalance {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmBaseFee {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmBlobHash { + #[inst(value)] + idx: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmBlobBaseFee {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmMstore8 { + #[inst(value)] + addr: Value, + #[inst(value)] + val: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmSload { + #[inst(value)] + key: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmSstore { + #[inst(value)] + key: Value, + #[inst(value)] + val: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmMsize {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +pub struct EvmGas {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmTload { + #[inst(value)] + key: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmTstore { + #[inst(value)] + key: Value, + #[inst(value)] + val: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmLog0 { + #[inst(value)] + addr: Value, + #[inst(value)] + len: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmLog1 { + #[inst(value)] + addr: Value, + #[inst(value)] + len: Value, + #[inst(value)] + topic0: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmLog2 { + #[inst(value)] + addr: Value, + #[inst(value)] + len: Value, + #[inst(value)] + topic0: Value, + #[inst(value)] + topic1: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmLog3 { + #[inst(value)] + addr: Value, + #[inst(value)] + len: Value, + #[inst(value)] + topic0: Value, + #[inst(value)] + topic1: Value, + #[inst(value)] + topic2: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmLog4 { + #[inst(value)] + addr: Value, + #[inst(value)] + len: Value, + #[inst(value)] + topic0: Value, + #[inst(value)] + topic1: Value, + #[inst(value)] + topic2: Value, + #[inst(value)] + topic3: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmCreate { + #[inst(value)] + val: Value, + #[inst(value)] + addr: Value, + #[inst(value)] + len: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmCall { + #[inst(value)] + gas: Value, + #[inst(value)] + addr: Value, + #[inst(value)] + val: Value, + #[inst(value)] + arg_addr: Value, + #[inst(value)] + arg_len: Value, + #[inst(value)] + ret_addr: Value, + #[inst(value)] + ret_offset: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmReturn { + #[inst(value)] + addr: Value, + #[inst(value)] + len: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmDelegateCall { + #[inst(value)] + gas: Value, + #[inst(value)] + ext_addr: Value, + #[inst(value)] + arg_addr: Value, + #[inst(value)] + arg_len: Value, + #[inst(value)] + ret_addr: Value, + #[inst(value)] + ret_len: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmCreate2 { + #[inst(value)] + val: Value, + #[inst(value)] + addr: Value, + #[inst(value)] + len: Value, + #[inst(value)] + salt: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmStaticCall { + #[inst(value)] + gas: Value, + #[inst(value)] + ext_addr: Value, + #[inst(value)] + arg_addr: Value, + #[inst(value)] + arg_len: Value, + #[inst(value)] + ret_addr: Value, + #[inst(value)] + ret_len: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmRevert { + #[inst(value)] + addr: Value, + #[inst(value)] + len: Value, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] +#[inst(has_side_effect)] +pub struct EvmSelfDestruct { + #[inst(value)] + addr: Value, +} diff --git a/crates/ir/src/inst/inst_set.rs b/crates/ir/src/inst/inst_set.rs index fa1cf121..04b52d64 100644 --- a/crates/ir/src/inst/inst_set.rs +++ b/crates/ir/src/inst/inst_set.rs @@ -1,4 +1,4 @@ -use super::{arith, basic, cast, cmp, control_flow, data, logic, Inst}; +use super::{arith, cast, cmp, control_flow, data, evm, logic, Inst}; use macros::define_inst_set_base; @@ -18,6 +18,11 @@ define_inst_set_base! { arith::Sub, arith::Sdiv, arith::Udiv, + arith::Umod, + arith::Smod, + arith::Shl, + arith::Shr, + arith::Sar, cmp::Lt, cmp::Gt, cmp::Slt, @@ -28,6 +33,7 @@ define_inst_set_base! { cmp::Sge, cmp::Eq, cmp::Ne, + cmp::IsZero, logic::Not, logic::And, logic::Or, @@ -47,6 +53,59 @@ define_inst_set_base! { control_flow::Return, control_flow::Phi, control_flow::Nop, + // Evm specific + evm::EvmStop, + evm::EvmAddMod, + evm::EvmMulMod, + evm::EvmExp, + evm::EvmByte, + evm::EvmKeccak256, + evm::EvmAddress, + evm::EvmBalance, + evm::EvmOrigin, + evm::EvmCaller, + evm::EvmCallValue, + evm::EvmCallDataLoad, + evm::EvmCallDataCopy, + evm::EvmCodeSize, + evm::EvmCodeCopy, + evm::EvmGasPrice, + evm::EvmExtCodeSize, + evm::EvmExtCodeCopy, + evm::EvmReturnDataSize, + evm::EvmReturnDataCopy, + evm::EvmExtCodeHash, + evm::EvmBlockHash, + evm::EvmCoinBase, + evm::EvmTimestamp, + evm::EvmNumber, + evm::EvmPrevRandao, + evm::EvmGasLimit, + evm::EvmChainId, + evm::EvmSelfBalance, + evm::EvmBaseFee, + evm::EvmBlobHash, + evm::EvmBlobBaseFee, + evm::EvmMstore8, + evm::EvmSload, + evm::EvmSstore, + evm::EvmMsize, + evm::EvmGas, + evm::EvmTload, + evm::EvmTstore, + evm::EvmLog0, + evm::EvmLog1, + evm::EvmLog2, + evm::EvmLog3, + evm::EvmLog4, + evm::EvmCreate, + evm::EvmCall, + evm::EvmReturn, + evm::EvmDelegateCall, + evm::EvmCreate2, + evm::EvmStaticCall, + evm::EvmRevert, + evm::EvmSelfDestruct, } } @@ -62,7 +121,7 @@ define_inst_set_base! { /// defines /// /// ```rust -/// use sonatina_ir::inst::basic::*; +/// use sonatina_ir::inst::arith::*; /// enum InstKind<'i> { /// Add(&'i Add), /// Sub(&'i Sub), diff --git a/crates/ir/src/inst/mod.rs b/crates/ir/src/inst/mod.rs index 7e6c6415..c13cbbba 100644 --- a/crates/ir/src/inst/mod.rs +++ b/crates/ir/src/inst/mod.rs @@ -4,6 +4,7 @@ pub mod cast; pub mod cmp; pub mod control_flow; pub mod data; +pub mod evm; pub mod inst_set; pub mod logic; From 5e822481c6ce605fde134a1404b0f7c49dbadd79 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 11 Sep 2024 23:18:05 +0200 Subject: [PATCH 24/26] suppress clippy warning from macro generated code --- crates/ir/src/inst/access.rs | 0 crates/ir/src/inst/mod.rs | 1 - crates/macros/src/inst.rs | 1 + 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 crates/ir/src/inst/access.rs diff --git a/crates/ir/src/inst/access.rs b/crates/ir/src/inst/access.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/crates/ir/src/inst/mod.rs b/crates/ir/src/inst/mod.rs index c13cbbba..4843418e 100644 --- a/crates/ir/src/inst/mod.rs +++ b/crates/ir/src/inst/mod.rs @@ -1,4 +1,3 @@ -pub mod access; pub mod arith; pub mod cast; pub mod cmp; diff --git a/crates/macros/src/inst.rs b/crates/macros/src/inst.rs index e87ab5cd..8834ae4e 100644 --- a/crates/macros/src/inst.rs +++ b/crates/macros/src/inst.rs @@ -138,6 +138,7 @@ impl InstStruct { let field_names = self.fields.iter().map(|f| &f.ident); quote! { + #[allow(clippy::too_many_arguments)] pub fn new(hi: &dyn crate::HasInst, #(#ctor_args),*) -> Self { Self { #(#field_names: #field_names),* From 1b5a36f53a6dc6c6d2ecc48bd44a033e7b5571d5 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Thu, 12 Sep 2024 11:48:19 +0200 Subject: [PATCH 25/26] Fix bug in `inst_set` macro --- crates/macros/src/inst_set.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/macros/src/inst_set.rs b/crates/macros/src/inst_set.rs index aa18ea40..49a78cdf 100644 --- a/crates/macros/src/inst_set.rs +++ b/crates/macros/src/inst_set.rs @@ -124,10 +124,10 @@ impl InstSet { table: ::rustc_hash::FxHashMap< std::any::TypeId, ( - &'static for<'i> fn(&Self, &'i dyn Inst) -> ::InstKind<'i>, + &'static for<'i> fn(&Self, &'i dyn crate::Inst) -> ::InstKind<'i>, &'static for<'i> fn( &Self, - &'i mut dyn Inst, + &'i mut dyn crate::Inst, ) -> ::InstKindMut<'i>, ), >, @@ -176,19 +176,19 @@ impl InstSet { quote! { let tid = std::any::TypeId::of::<#p>(); - fn #cast_fn_name<'i>(self_: &#ident, inst: &'i dyn Inst) -> #inst_kind_name<'i> { + fn #cast_fn_name<'i>(self_: &#ident, inst: &'i dyn crate::Inst) -> #inst_kind_name<'i> { let inst = #p::cast(self_, inst).unwrap(); #inst_kind_name::#variant_name(inst) } - fn #cast_mut_fn_name<'i>(self_: &#ident, inst: &'i mut dyn Inst) -> #inst_kind_mut_name<'i> { + fn #cast_mut_fn_name<'i>(self_: &#ident, inst: &'i mut dyn crate::Inst) -> #inst_kind_mut_name<'i> { let inst = #p::cast_mut(self_, inst).unwrap(); #inst_kind_mut_name::#variant_name(inst) } - let f: &'static for<'a, 'i> fn(&'a #ident, &'i dyn Inst) -> #inst_kind_name<'i> = - &(#cast_fn_name as for<'a, 'i> fn(&'a #ident, &'i dyn Inst) -> #inst_kind_name<'i>); - let f_mut: &'static for<'a, 'i> fn(&'a #ident, &'i mut dyn Inst) -> #inst_kind_mut_name<'i> = - &(#cast_mut_fn_name as for<'a, 'i> fn(&'a #ident, &'i mut dyn Inst) -> #inst_kind_mut_name<'i>); + let f: &'static for<'a, 'i> fn(&'a #ident, &'i dyn crate::Inst) -> #inst_kind_name<'i> = + &(#cast_fn_name as for<'a, 'i> fn(&'a #ident, &'i dyn crate::Inst) -> #inst_kind_name<'i>); + let f_mut: &'static for<'a, 'i> fn(&'a #ident, &'i mut dyn crate::Inst) -> #inst_kind_mut_name<'i> = + &(#cast_mut_fn_name as for<'a, 'i> fn(&'a #ident, &'i mut dyn crate::Inst) -> #inst_kind_mut_name<'i>); table.insert(tid, (f, f_mut)); } @@ -252,14 +252,14 @@ impl InstSet { type InstKind<'i> = #inst_kind_name<'i>; type InstKindMut<'i> = #inst_kind_mut_name<'i>; - fn resolve_inst<'i>(&self, inst: &'i dyn Inst) -> Self::InstKind<'i> { + fn resolve_inst<'i>(&self, inst: &'i dyn crate::Inst) -> Self::InstKind<'i> { let tid = inst.type_id(); debug_assert!(self.table.contains_key(&tid)); self.table[&tid].0(self, inst) } - fn resolve_inst_mut<'i>(&self, inst: &'i mut dyn Inst) -> Self::InstKindMut<'i> { - let tid = inst.type_id(); + fn resolve_inst_mut<'i>(&self, inst: &'i mut dyn crate::Inst) -> Self::InstKindMut<'i> { + let tid = (*inst).type_id(); debug_assert!(self.table.contains_key(&tid)); self.table[&tid].1(self, inst) } From 9b471b92b04653b2f4bf05e16a46a2bb62234ca8 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Thu, 12 Sep 2024 11:48:44 +0200 Subject: [PATCH 26/26] Define `EvmInstSet` --- crates/ir/src/inst/control_flow.rs | 17 +++--- crates/ir/src/inst/data.rs | 8 +-- crates/ir/src/inst/evm/inst_set.rs | 93 ++++++++++++++++++++++++++++++ crates/ir/src/inst/evm/mod.rs | 1 + crates/ir/src/inst/inst_set.rs | 2 - 5 files changed, 102 insertions(+), 19 deletions(-) create mode 100644 crates/ir/src/inst/evm/inst_set.rs diff --git a/crates/ir/src/inst/control_flow.rs b/crates/ir/src/inst/control_flow.rs index 84ba43ba..7e7fef65 100644 --- a/crates/ir/src/inst/control_flow.rs +++ b/crates/ir/src/inst/control_flow.rs @@ -27,6 +27,13 @@ pub struct BrTable { default: Option, } +#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] +pub struct Phi { + #[inst(value)] + values: Vec<(Value, Block)>, + ty: Type, +} + #[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] #[inst(has_side_effect)] pub struct Call { @@ -42,13 +49,3 @@ pub struct Return { #[inst(value)] arg: Option, } - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -pub struct Phi { - #[inst(value)] - values: Vec<(Value, Block)>, - ty: Type, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] -pub struct Nop {} diff --git a/crates/ir/src/inst/data.rs b/crates/ir/src/inst/data.rs index bf50b4c5..c74a2ec1 100644 --- a/crates/ir/src/inst/data.rs +++ b/crates/ir/src/inst/data.rs @@ -1,7 +1,7 @@ use macros::Inst; use smallvec::SmallVec; -use crate::{Type, Value}; +use crate::Value; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Inst)] pub struct Mload { @@ -18,12 +18,6 @@ pub struct Mstore { addr: Value, } -#[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] -#[inst(has_side_effect)] -pub struct Alloca { - ty: Type, -} - #[derive(Debug, Clone, PartialEq, Eq, Hash, Inst)] pub struct Gep { #[inst(value)] diff --git a/crates/ir/src/inst/evm/inst_set.rs b/crates/ir/src/inst/evm/inst_set.rs new file mode 100644 index 00000000..8fa789b3 --- /dev/null +++ b/crates/ir/src/inst/evm/inst_set.rs @@ -0,0 +1,93 @@ +use crate::inst::*; +use macros::inst_set; + +#[inst_set(InstKind = "EvmInstKind")] +pub struct EvmInstSet( + arith::Add, + arith::Mul, + arith::Sub, + arith::Sdiv, + arith::Udiv, + arith::Umod, + arith::Smod, + arith::Shl, + arith::Shr, + arith::Sar, + cast::Sext, + cast::Zext, + cast::Trunc, + cast::Bitcast, + cmp::Lt, + cmp::Gt, + cmp::Slt, + cmp::Sgt, + cmp::Le, + cmp::Ge, + cmp::Sge, + cmp::Eq, + cmp::Ne, + cmp::IsZero, + control_flow::Jump, + control_flow::Br, + control_flow::Phi, + control_flow::BrTable, + control_flow::Call, + control_flow::Return, + data::Mload, + data::Mstore, + data::Gep, + logic::Not, + logic::And, + logic::Or, + logic::Xor, + evm::EvmStop, + evm::EvmAddMod, + evm::EvmMulMod, + evm::EvmExp, + evm::EvmByte, + evm::EvmKeccak256, + evm::EvmAddress, + evm::EvmBalance, + evm::EvmOrigin, + evm::EvmCaller, + evm::EvmCallValue, + evm::EvmCallDataLoad, + evm::EvmCallDataCopy, + evm::EvmCodeSize, + evm::EvmCodeCopy, + evm::EvmExtCodeCopy, + evm::EvmReturnDataSize, + evm::EvmReturnDataCopy, + evm::EvmExtCodeHash, + evm::EvmBlockHash, + evm::EvmCoinBase, + evm::EvmTimestamp, + evm::EvmNumber, + evm::EvmPrevRandao, + evm::EvmGasLimit, + evm::EvmChainId, + evm::EvmSelfBalance, + evm::EvmBaseFee, + evm::EvmBlobHash, + evm::EvmBlobBaseFee, + evm::EvmMstore8, + evm::EvmSload, + evm::EvmSstore, + evm::EvmMsize, + evm::EvmGas, + evm::EvmTload, + evm::EvmTstore, + evm::EvmLog0, + evm::EvmLog1, + evm::EvmLog2, + evm::EvmLog3, + evm::EvmLog4, + evm::EvmCreate, + evm::EvmCall, + evm::EvmReturn, + evm::EvmDelegateCall, + evm::EvmCreate2, + evm::EvmStaticCall, + evm::EvmRevert, + evm::EvmSelfDestruct, +); diff --git a/crates/ir/src/inst/evm/mod.rs b/crates/ir/src/inst/evm/mod.rs index a0368052..6b2b0af6 100644 --- a/crates/ir/src/inst/evm/mod.rs +++ b/crates/ir/src/inst/evm/mod.rs @@ -1,4 +1,5 @@ use macros::Inst; +pub mod inst_set; use crate::Value; diff --git a/crates/ir/src/inst/inst_set.rs b/crates/ir/src/inst/inst_set.rs index 04b52d64..2a5d14df 100644 --- a/crates/ir/src/inst/inst_set.rs +++ b/crates/ir/src/inst/inst_set.rs @@ -44,7 +44,6 @@ define_inst_set_base! { cast::Bitcast, data::Mload, data::Mstore, - data::Alloca, data::Gep, control_flow::Call, control_flow::Jump, @@ -52,7 +51,6 @@ define_inst_set_base! { control_flow::BrTable, control_flow::Return, control_flow::Phi, - control_flow::Nop, // Evm specific evm::EvmStop, evm::EvmAddMod,