Skip to main content

midenc_hir/ir/
dialect.rs

1mod info;
2
3use alloc::rc::Rc;
4use core::ptr::{DynMetadata, Pointee};
5
6pub use self::info::DialectInfo;
7use crate::{
8    AttributeRef, Builder, OperationName, OperationRef, SourceSpan, Type, any::AsAny,
9    attributes::AttributeName, interner,
10};
11
12pub type DialectRegistrationHook = fn(&mut DialectInfo);
13
14/// A [Dialect] represents a collection of IR entities that are used in conjunction with one
15/// another. Multiple dialects can co-exist _or_ be mutually exclusive. Converting between dialects
16/// is the job of the conversion infrastructure, using a process called _legalization_.
17pub trait Dialect {
18    /// Get metadata about this dialect (it's operations, interfaces, etc.)
19    fn info(&self) -> &DialectInfo;
20
21    /// Get the name(space) of this dialect
22    fn name(&self) -> interner::Symbol {
23        self.info().name()
24    }
25
26    /// Get the set of registered operations associated with this dialect
27    fn registered_ops(&self) -> &[OperationName] {
28        self.info().operations()
29    }
30
31    /// Get the set of registered attributes associated with this dialect
32    fn registered_attrs(&self) -> &[AttributeName] {
33        self.info().attributes()
34    }
35
36    /// A hook to materialize a single constant operation from a given attribute value.
37    ///
38    /// This method should use the provided builder to create the operation without changing the
39    /// insertion point. The generated operation is expected to be constant-like, i.e. single result
40    /// zero operands, no side effects, etc.
41    ///
42    /// Returns `None` if a constant cannot be materialized for the given attribute.
43    #[allow(unused_variables)]
44    #[inline]
45    fn materialize_constant(
46        &self,
47        builder: &mut dyn Builder,
48        attr: AttributeRef,
49        ty: &Type,
50        span: SourceSpan,
51    ) -> Option<OperationRef> {
52        None
53    }
54}
55
56impl dyn Dialect {
57    /// Get the [OperationName] of the operation type `T`, if registered with this dialect.
58    pub fn registered_name<T>(&self) -> Option<OperationName>
59    where
60        T: crate::OpRegistration,
61    {
62        let opcode = <T as crate::OpRegistration>::name();
63        self.registered_ops().iter().find(|op| op.name() == opcode).cloned()
64    }
65
66    /// Get the [AttributeName] of the attribute type `T`, if registered with this dialect.
67    pub fn registered_attribute_name<T>(&self) -> Option<AttributeName>
68    where
69        T: crate::AttributeRegistration,
70    {
71        let name = <T as crate::AttributeRegistration>::name();
72        self.registered_attrs().iter().find(|attr| attr.name() == name).cloned()
73    }
74
75    /// Get the [OperationName] of the operation type `T`.
76    ///
77    /// Panics if the operation is not registered with this dialect.
78    pub fn expect_registered_name<T>(&self) -> OperationName
79    where
80        T: crate::OpRegistration,
81    {
82        self.registered_name::<T>().unwrap_or_else(|| {
83            panic!(
84                "{} is not registered with dialect '{}'",
85                core::any::type_name::<T>(),
86                self.name()
87            )
88        })
89    }
90
91    /// Get the [AttributeName] of the operation type `T`.
92    ///
93    /// Panics if the attribute is not registered with this dialect.
94    pub fn expect_registered_attribute_name<T>(&self) -> AttributeName
95    where
96        T: crate::AttributeRegistration,
97    {
98        self.registered_attribute_name::<T>().unwrap_or_else(|| {
99            panic!(
100                "{} is not registered with dialect '{}'",
101                core::any::type_name::<T>(),
102                self.name()
103            )
104        })
105    }
106
107    /// Attempt to cast this operation reference to an implementation of `Trait`
108    pub fn as_registered_interface<Trait>(&self) -> Option<&Trait>
109    where
110        Trait: ?Sized + Pointee<Metadata = DynMetadata<Trait>> + 'static,
111    {
112        let this = self as *const dyn Dialect;
113        let (ptr, _) = this.to_raw_parts();
114        let info = self.info();
115        info.upcast(ptr)
116    }
117}
118
119/// A [DialectRegistration] must be implemented for any implementation of [Dialect], to allow the
120/// dialect to be registered with a [crate::Context] and instantiated on demand when building ops
121/// in the IR.
122///
123/// This is not part of the [Dialect] trait itself, as that trait must be object safe, and this
124/// trait is _not_ object safe.
125pub trait DialectRegistration: AsAny + Dialect {
126    /// The namespace of the dialect to register
127    ///
128    /// A dialect namespace serves both as a way to namespace the operations of that dialect, as
129    /// well as a way to uniquely name/identify the dialect itself. Thus, no two dialects can have
130    /// the same namespace at the same time.
131    const NAMESPACE: &'static str;
132
133    /// Initialize an instance of this dialect to be stored (uniqued) in the current
134    /// [crate::Context].
135    ///
136    /// A dialect will only ever be initialized once per context. A dialect must use interior
137    /// mutability to satisfy the requirements of the [Dialect] trait, and to allow the context to
138    /// store the returned instance in a reference-counted smart pointer.
139    fn init(info: DialectInfo) -> Self;
140
141    /// This is called when registering a dialect, to register operations of the dialect.
142    ///
143    /// This is called _before_ [DialectRegistration::init].
144    #[allow(unused_variables)]
145    fn register_operations(info: &mut DialectInfo) {}
146
147    /// This is called when registering a dialect, to register attributes of the dialect.
148    ///
149    /// This is called _before_ [DialectRegistration::init].
150    #[allow(unused_variables)]
151    fn register_attributes(info: &mut DialectInfo) {}
152}
153
154inventory::collect!(DialectRegistrationInfo);
155inventory::collect!(DialectRegistrationHookInfo);
156inventory::collect!(DialectOpRegistrationInfo);
157inventory::collect!(DialectAttributeRegistrationInfo);
158
159#[doc(hidden)]
160#[repr(transparent)]
161pub struct DialectRegistrationInfo(DialectRegistrationEntry);
162
163#[doc(hidden)]
164#[repr(transparent)]
165pub struct DialectRegistrationHookInfo(DialectRegistrationHookEntry);
166
167#[doc(hidden)]
168#[repr(transparent)]
169pub struct DialectOpRegistrationInfo(DialectOpRegistrationEntry);
170
171#[doc(hidden)]
172#[repr(transparent)]
173pub struct DialectAttributeRegistrationInfo(DialectAttributeRegistrationEntry);
174
175#[repr(C)]
176struct DialectRegistrationEntry {
177    namespace: &'static str,
178    type_name: &'static str,
179    type_id: core::any::TypeId,
180    builder: fn() -> Rc<dyn Dialect>,
181}
182
183impl DialectRegistrationInfo {
184    pub const fn new<T: DialectRegistration>() -> Self {
185        let namespace = <T as DialectRegistration>::NAMESPACE;
186        Self(DialectRegistrationEntry {
187            namespace,
188            type_name: core::any::type_name::<T>(),
189            type_id: core::any::TypeId::of::<T>(),
190            builder: dialect_init::<T>,
191        })
192    }
193
194    pub(crate) const fn namespace(&self) -> &'static str {
195        self.0.namespace
196    }
197
198    pub(crate) const fn type_name(&self) -> &'static str {
199        self.0.type_name
200    }
201
202    #[allow(unused)]
203    pub(crate) const fn type_id(&self) -> &core::any::TypeId {
204        &self.0.type_id
205    }
206
207    pub(crate) fn create(&self) -> Rc<dyn Dialect> {
208        (self.0.builder)()
209    }
210}
211
212#[repr(C)]
213struct DialectRegistrationHookEntry {
214    namespace: &'static str,
215    type_name: &'static str,
216    type_id: core::any::TypeId,
217    hook: DialectRegistrationHook,
218}
219
220impl DialectRegistrationHookInfo {
221    pub const fn new<T: DialectRegistration>(hook: DialectRegistrationHook) -> Self {
222        let namespace = <T as DialectRegistration>::NAMESPACE;
223        Self(DialectRegistrationHookEntry {
224            namespace,
225            type_name: core::any::type_name::<T>(),
226            type_id: core::any::TypeId::of::<T>(),
227            hook,
228        })
229    }
230}
231
232#[repr(C)]
233struct DialectOpRegistrationEntry {
234    dialect: &'static str,
235    dialect_type: core::any::TypeId,
236    get_opcode: fn() -> interner::Symbol,
237    init: fn(interner::Symbol, alloc::vec::Vec<super::traits::TraitInfo>) -> OperationName,
238}
239
240impl DialectOpRegistrationInfo {
241    pub const fn new<T: super::OpRegistration>() -> Self {
242        let dialect = <<T as super::OpRegistration>::Dialect as DialectRegistration>::NAMESPACE;
243        let dialect_type = core::any::TypeId::of::<<T as super::OpRegistration>::Dialect>();
244        Self(DialectOpRegistrationEntry {
245            dialect,
246            dialect_type,
247            get_opcode: <T as super::OpRegistration>::name,
248            init: OperationName::new::<T>,
249        })
250    }
251}
252
253#[repr(C)]
254struct DialectAttributeRegistrationEntry {
255    dialect: &'static str,
256    dialect_type: core::any::TypeId,
257    get_name: fn() -> interner::Symbol,
258    init: fn(interner::Symbol, alloc::vec::Vec<super::traits::TraitInfo>) -> AttributeName,
259}
260
261impl DialectAttributeRegistrationInfo {
262    pub const fn new<T: crate::AttributeRegistration>() -> Self {
263        let dialect =
264            <<T as crate::AttributeRegistration>::Dialect as DialectRegistration>::NAMESPACE;
265        let dialect_type = core::any::TypeId::of::<<T as crate::AttributeRegistration>::Dialect>();
266        Self(DialectAttributeRegistrationEntry {
267            dialect,
268            dialect_type,
269            get_name: <T as crate::AttributeRegistration>::name,
270            init: AttributeName::new::<T>,
271        })
272    }
273}
274
275pub(super) fn dialect_init<T: DialectRegistration>() -> Rc<dyn Dialect> {
276    let mut info = DialectInfo::new::<T>();
277
278    let dialect_name = <T as DialectRegistration>::NAMESPACE;
279    let dialect_type = core::any::TypeId::of::<T>();
280
281    for dialect_hook in inventory::iter::<DialectRegistrationHookInfo>() {
282        if dialect_hook.0.type_id == dialect_type && dialect_hook.0.namespace == dialect_name {
283            (dialect_hook.0.hook)(&mut info);
284        }
285    }
286
287    for op in inventory::iter::<DialectOpRegistrationInfo>() {
288        if op.0.dialect_type == dialect_type && op.0.dialect == dialect_name {
289            let opcode = (op.0.get_opcode)();
290            info.get_or_register_with(opcode, op.0.init);
291        }
292    }
293
294    for attr in inventory::iter::<DialectAttributeRegistrationInfo>() {
295        if attr.0.dialect_type == dialect_type && attr.0.dialect == dialect_name {
296            let name = (attr.0.get_name)();
297            info.get_or_register_attribute_with(name, attr.0.init);
298        }
299    }
300
301    Rc::new(<T as DialectRegistration>::init(info)) as Rc<dyn Dialect>
302}