midenc_hir/ir/dialect.rs
1mod info;
2
3use alloc::boxed::Box;
4use core::ptr::{DynMetadata, Pointee};
5
6pub use self::info::DialectInfo;
7use crate::{
8    any::AsAny, interner, AttributeValue, Builder, OperationName, OperationRef, SourceSpan, Type,
9};
10
11pub type DialectRegistrationHook = Box<dyn Fn(&mut DialectInfo, &super::Context)>;
12
13/// A [Dialect] represents a collection of IR entities that are used in conjunction with one
14/// another. Multiple dialects can co-exist _or_ be mutually exclusive. Converting between dialects
15/// is the job of the conversion infrastructure, using a process called _legalization_.
16pub trait Dialect {
17    /// Get metadata about this dialect (it's operations, interfaces, etc.)
18    fn info(&self) -> &DialectInfo;
19
20    /// Get the name(space) of this dialect
21    fn name(&self) -> interner::Symbol {
22        self.info().name()
23    }
24
25    /// Get the set of registered operations associated with this dialect
26    fn registered_ops(&self) -> &[OperationName] {
27        self.info().operations()
28    }
29
30    /// A hook to materialize a single constant operation from a given attribute value and type.
31    ///
32    /// This method should use the provided builder to create the operation without changing the
33    /// insertion point. The generated operation is expected to be constant-like, i.e. single result
34    /// zero operands, no side effects, etc.
35    ///
36    /// Returns `None` if a constant cannot be materialized for the given attribute.
37    #[allow(unused_variables)]
38    #[inline]
39    fn materialize_constant(
40        &self,
41        builder: &mut dyn Builder,
42        attr: Box<dyn AttributeValue>,
43        ty: &Type,
44        span: SourceSpan,
45    ) -> Option<OperationRef> {
46        None
47    }
48}
49
50impl dyn Dialect {
51    /// Get the [OperationName] of the operation type `T`, if registered with this dialect.
52    pub fn registered_name<T>(&self) -> Option<OperationName>
53    where
54        T: crate::OpRegistration,
55    {
56        let opcode = <T as crate::OpRegistration>::name();
57        self.registered_ops().iter().find(|op| op.name() == opcode).cloned()
58    }
59
60    /// Get the [OperationName] of the operation type `T`.
61    ///
62    /// Panics if the operation is not registered with this dialect.
63    pub fn expect_registered_name<T>(&self) -> OperationName
64    where
65        T: crate::OpRegistration,
66    {
67        self.registered_name::<T>().unwrap_or_else(|| {
68            panic!(
69                "{} is not registered with dialect '{}'",
70                core::any::type_name::<T>(),
71                self.name()
72            )
73        })
74    }
75
76    /// Attempt to cast this operation reference to an implementation of `Trait`
77    pub fn as_registered_interface<Trait>(&self) -> Option<&Trait>
78    where
79        Trait: ?Sized + Pointee<Metadata = DynMetadata<Trait>> + 'static,
80    {
81        let this = self as *const dyn Dialect;
82        let (ptr, _) = this.to_raw_parts();
83        let info = self.info();
84        info.upcast(ptr)
85    }
86}
87
88/// A [DialectRegistration] must be implemented for any implementation of [Dialect], to allow the
89/// dialect to be registered with a [crate::Context] and instantiated on demand when building ops
90/// in the IR.
91///
92/// This is not part of the [Dialect] trait itself, as that trait must be object safe, and this
93/// trait is _not_ object safe.
94pub trait DialectRegistration: AsAny + Dialect {
95    /// The namespace of the dialect to register
96    ///
97    /// A dialect namespace serves both as a way to namespace the operations of that dialect, as
98    /// well as a way to uniquely name/identify the dialect itself. Thus, no two dialects can have
99    /// the same namespace at the same time.
100    const NAMESPACE: &'static str;
101
102    /// Initialize an instance of this dialect to be stored (uniqued) in the current
103    /// [crate::Context].
104    ///
105    /// A dialect will only ever be initialized once per context. A dialect must use interior
106    /// mutability to satisfy the requirements of the [Dialect] trait, and to allow the context to
107    /// store the returned instance in a reference-counted smart pointer.
108    fn init(info: DialectInfo) -> Self;
109
110    /// This is called when registering a dialect, to register operations of the dialect.
111    ///
112    /// This is called _before_ [DialectRegistration::init].
113    fn register_operations(info: &mut DialectInfo);
114}