Skip to main content

intarsia_macros/
lib.rs

1//! Procedural macros for integrating [ISLE] DSL with Intarsia optimizers.
2//!
3//! This crate provides macros to help integrate ISLE-generated code into your Rust optimizer
4//! framework. [ISLE] (Instruction Selection Lowering Expressions) is a domain-specific language
5//! for pattern matching and rewriting, originally developed for [Cranelift].
6//!
7//! [ISLE]: https://github.com/bytecodealliance/wasmtime/blob/main/cranelift/isle/docs/language-reference.md
8//! [Cranelift]: https://cranelift.dev/
9//!
10//! # Overview
11//!
12//! The macros fall into three categories:
13//!
14//! ## 1. Accessor Generation (Non-Multi)
15//!
16//! For basic ISLE operators that match/construct single nodes:
17//! - [`isle_extractor!`] - Pattern matching (e-node → children IDs)
18//! - [`isle_constructor!`] - Node construction (children IDs → e-node)
19//! - [`isle_accessors!`] - Both extractor and constructor
20//!
21//! ## 2. Multi-Accessor Generation
22//!
23//! For ISLE multi-extractors/constructors that explore all nodes in an e-class:
24//! - [`isle_multi_extractor!`] - Match all nodes in an e-class
25//! - [`isle_multi_constructor!`] - Construct and add to results vector
26//! - [`isle_multi_accessors!`] - Both multi-extractor and multi-constructor
27//!
28//! ## 3. Integration Setup
29//!
30//! - [`isle_integration!`] - Generate required type definitions
31//! - [`isle_integration_full!`] - Module declaration + type definitions
32//!
33//! # Quick Start Example
34//!
35//! ```ignore
36//! // In your optimizer module (e.g., src/optimizer.rs)
37//! use intarsia_macros::{isle_integration, isle_accessors};
38//!
39//! // 1. Declare the ISLE-generated rules module
40//! #[allow(dead_code, unused_variables, unused_imports, non_snake_case)]
41//! #[path = "isle/rules.rs"]
42//! pub(crate) mod rules;
43//!
44//! // 2. Generate required ISLE type definitions
45//! isle_integration!();
46//!
47//! // 3. In your Context implementation, generate accessor functions
48//! impl rules::Context for MyContext {
49//!     isle_accessors! {
50//!         And(extractor_and, constructor_and, 2);
51//!         Or(extractor_or, constructor_or, 2);
52//!         Not(extractor_not, constructor_not, 1);
53//!     }
54//!     // ... other required methods
55//! }
56//! ```
57//!
58//! # Build Script Integration
59//!
60//! Use [`intarsia-build`] to compile ISLE files in your `build.rs`:
61//!
62//! [`intarsia-build`]: https://docs.rs/intarsia-build/
63//!
64//! ```no_run
65//! // build.rs
66//! fn main() -> Result<(), Box<dyn std::error::Error>> {
67//!     intarsia_build::compile_isle_auto()?;
68//!     Ok(())
69//! }
70//! ```
71
72use proc_macro::TokenStream;
73use quote::quote;
74use syn::{Error, Ident, LitInt, Token, parse::Parse, parse::ParseStream, parse_macro_input};
75
76// Parser for isle_integration macro arguments
77struct IsleIntegrationArgs {
78    max_returns: Option<usize>,
79}
80
81impl Parse for IsleIntegrationArgs {
82    fn parse(input: ParseStream) -> syn::Result<Self> {
83        if input.is_empty() {
84            return Ok(IsleIntegrationArgs { max_returns: None });
85        }
86
87        let key: Ident = input.parse()?;
88        if key != "max_returns" {
89            return Err(Error::new_spanned(key, "Expected 'max_returns'"));
90        }
91        let _colon: Token![:] = input.parse()?;
92        let value: LitInt = input.parse()?;
93        let max_returns = value.base10_parse()?;
94
95        // Optional trailing comma
96        let _ = input.parse::<Token![,]>();
97
98        Ok(IsleIntegrationArgs {
99            max_returns: Some(max_returns),
100        })
101    }
102}
103
104/// Generate type definitions required by ISLE-generated code.
105///
106/// This macro generates `ConstructorVec<T>` and `MAX_ISLE_RETURNS` which are referenced
107/// by ISLE-generated multi-constructor functions. You still need to manually declare the
108/// `rules` module with a [`#[path]`][path-attr] attribute.
109///
110/// See [`isle_integration_full!`] for a version that also declares the module.
111///
112/// [path-attr]: https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute
113///
114/// # Arguments
115///
116/// * `max_returns: usize` - (Optional) Maximum number of values a multi-constructor can return.
117///   Defaults to 100. Increase if you have rules generating many alternatives.
118///
119/// # Example
120///
121/// ```ignore
122/// // In your optimizer module (e.g., src/optimizer/mod.rs)
123/// use intarsia_macros::isle_integration;
124///
125/// // First, declare the rules module with the path to generated code
126/// #[allow(dead_code, unused_variables, unused_imports, non_snake_case)]
127/// #[allow(irrefutable_let_patterns, unused_assignments, non_camel_case_types)]
128/// #[allow(unreachable_patterns, unreachable_code)]
129/// #[path = "isle/rules.rs"]
130/// pub(crate) mod rules;
131///
132/// // Then, generate the required type definitions
133/// isle_integration!();
134///
135/// // Or with custom max_returns:
136/// isle_integration!(max_returns: 200);
137/// ```
138///
139/// # Generated Code
140///
141/// ```ignore
142/// /// Type alias for vectors returned by ISLE multi-constructors.
143/// pub type ConstructorVec<T> = Vec<T>;
144///
145/// /// Maximum number of values an ISLE multi-constructor can return.
146/// pub const MAX_ISLE_RETURNS: usize = 100;
147/// ```
148#[proc_macro]
149pub fn isle_integration(input: TokenStream) -> TokenStream {
150    let args = parse_macro_input!(input as IsleIntegrationArgs);
151    let max_returns = args.max_returns.unwrap_or(100);
152
153    let output = quote! {
154        /// Type alias for vectors returned by ISLE multi-constructors.
155        ///
156        /// ISLE multi-constructors can return multiple values. This type
157        /// is used internally by the generated code.
158        pub type ConstructorVec<T> = Vec<T>;
159
160        /// Maximum number of values an ISLE multi-constructor can return.
161        ///
162        /// This constant is used by ISLE-generated code to limit the number
163        /// of alternatives explored. Increase this if you have rules that
164        /// generate many alternatives.
165        pub const MAX_ISLE_RETURNS: usize = #max_returns;
166    };
167
168    output.into()
169}
170
171// Parser for isle_integration_full macro arguments
172struct IsleIntegrationFullArgs {
173    path: String,
174    max_returns: Option<usize>,
175}
176
177impl Parse for IsleIntegrationFullArgs {
178    fn parse(input: ParseStream) -> syn::Result<Self> {
179        // Parse "path"
180        let path_key: Ident = input.parse()?;
181        if path_key != "path" {
182            return Err(Error::new_spanned(
183                path_key,
184                "Expected 'path' as first argument",
185            ));
186        }
187        let _colon: Token![:] = input.parse()?;
188        let path_lit: syn::LitStr = input.parse()?;
189        let path = path_lit.value();
190
191        // Optional comma
192        let has_comma = input.parse::<Token![,]>().is_ok();
193
194        let max_returns = if has_comma && !input.is_empty() {
195            // Parse "max_returns"
196            let max_returns_key: Ident = input.parse()?;
197            if max_returns_key != "max_returns" {
198                return Err(Error::new_spanned(
199                    max_returns_key,
200                    "Expected 'max_returns'",
201                ));
202            }
203            let _colon: Token![:] = input.parse()?;
204            let value: LitInt = input.parse()?;
205            Some(value.base10_parse()?)
206        } else {
207            None
208        };
209
210        // Optional trailing comma
211        let _ = input.parse::<Token![,]>();
212
213        Ok(IsleIntegrationFullArgs { path, max_returns })
214    }
215}
216
217/// Complete ISLE integration including module declaration and type definitions.
218///
219/// This is a convenience macro that combines module declaration (with appropriate `#[allow]`
220/// attributes) and [`isle_integration!`] in a single invocation.
221///
222/// Use this for simpler integration, or use [`isle_integration!`] if you need custom
223/// module attributes.
224///
225/// # Arguments
226///
227/// * `path: "..."` - Path to the generated `.rs` file, relative to current module
228/// * `max_returns: usize` - (Optional) Maximum number of values a multi-constructor can return
229///
230/// # Example
231///
232/// ```ignore
233/// use intarsia_macros::isle_integration_full;
234///
235/// // In your optimizer module, assuming isle/rules.rs exists
236/// isle_integration_full! {
237///     path: "isle/rules.rs",
238/// }
239///
240/// // With custom max_returns
241/// isle_integration_full! {
242///     path: "isle/rules.rs",
243///     max_returns: 200,
244/// }
245/// ```
246///
247/// # Generated Code
248///
249/// This generates a `rules` module declaration with lint suppressions plus the type
250/// definitions from [`isle_integration!`].
251#[proc_macro]
252pub fn isle_integration_full(input: TokenStream) -> TokenStream {
253    let args = parse_macro_input!(input as IsleIntegrationFullArgs);
254    let path = args.path;
255    let name = path
256        .split('/')
257        .last()
258        .and_then(|s| s.strip_suffix(".rs"))
259        .unwrap_or("rules");
260    let mod_name = Ident::new(name, proc_macro2::Span::call_site());
261    let max_returns = args.max_returns.unwrap_or(100);
262
263    let output = quote! {
264        // Declare the rules module with appropriate attributes
265        #[allow(dead_code, unused_variables, unused_imports, non_snake_case)]
266        #[allow(irrefutable_let_patterns, unused_assignments, non_camel_case_types)]
267        #[allow(unreachable_patterns, unreachable_code)]
268        #[path = #path]
269        pub(crate) mod #mod_name;
270
271        /// Type alias for vectors returned by ISLE multi-constructors.
272        ///
273        /// ISLE multi-constructors can return multiple values. This type
274        /// is used internally by the generated code.
275        pub type ConstructorVec<T> = Vec<T>;
276
277        /// Maximum number of values an ISLE multi-constructor can return.
278        ///
279        /// This constant is used by ISLE-generated code to limit the number
280        /// of alternatives explored. Increase this if you have rules that
281        /// generate many alternatives.
282        pub const MAX_ISLE_RETURNS: usize = #max_returns;
283
284        /// Import the context trait and wrapper type generated by ISLE.
285        use #mod_name::{Context, ContextIterWrapper}; // Pull in the Context trait and ContextIterWrapper generated by ISLE.
286    };
287
288    output.into()
289}