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;