Skip to main content

melior/
context.rs

1use crate::{
2    diagnostic::{Diagnostic, DiagnosticHandlerId},
3    dialect::{Dialect, DialectRegistry},
4    logical_result::LogicalResult,
5    string_ref::StringRef,
6};
7use mlir_sys::{
8    mlirContextAppendDialectRegistry, mlirContextAttachDiagnosticHandler, mlirContextCreate,
9    mlirContextDestroy, mlirContextDetachDiagnosticHandler, mlirContextEnableMultithreading,
10    mlirContextEqual, mlirContextGetAllowUnregisteredDialects, mlirContextGetNumLoadedDialects,
11    mlirContextGetNumRegisteredDialects, mlirContextGetOrLoadDialect,
12    mlirContextIsRegisteredOperation, mlirContextLoadAllAvailableDialects,
13    mlirContextSetAllowUnregisteredDialects, MlirContext, MlirDiagnostic, MlirLogicalResult,
14};
15use std::{ffi::c_void, marker::PhantomData, mem::transmute};
16
17/// A context of IR, dialects, and passes.
18///
19/// Contexts own various objects, such as types, locations, and dialect
20/// instances.
21#[derive(Debug)]
22pub struct Context {
23    raw: MlirContext,
24}
25
26impl Context {
27    /// Creates a context.
28    pub fn new() -> Self {
29        Self {
30            raw: unsafe { mlirContextCreate() },
31        }
32    }
33
34    /// Returns a number of registered dialects.
35    pub fn registered_dialect_count(&self) -> usize {
36        unsafe { mlirContextGetNumRegisteredDialects(self.raw) as usize }
37    }
38
39    /// Returns a number of loaded dialects.
40    pub fn loaded_dialect_count(&self) -> usize {
41        unsafe { mlirContextGetNumLoadedDialects(self.raw) as usize }
42    }
43
44    /// Returns or loads a dialect.
45    pub fn get_or_load_dialect(&self, name: &str) -> Dialect {
46        let name = StringRef::new(name);
47
48        unsafe { Dialect::from_raw(mlirContextGetOrLoadDialect(self.raw, name.to_raw())) }
49    }
50
51    /// Appends a dialect registry.
52    pub fn append_dialect_registry(&self, registry: &DialectRegistry) {
53        unsafe { mlirContextAppendDialectRegistry(self.raw, registry.to_raw()) }
54    }
55
56    /// Loads all available dialects.
57    pub fn load_all_available_dialects(&self) {
58        unsafe { mlirContextLoadAllAvailableDialects(self.raw) }
59    }
60
61    /// Enables multi-threading.
62    pub fn enable_multi_threading(&self, enabled: bool) {
63        unsafe { mlirContextEnableMultithreading(self.raw, enabled) }
64    }
65
66    /// Returns `true` if unregistered dialects are allowed.
67    pub fn allow_unregistered_dialects(&self) -> bool {
68        unsafe { mlirContextGetAllowUnregisteredDialects(self.raw) }
69    }
70
71    /// Sets if unregistered dialects are allowed.
72    pub fn set_allow_unregistered_dialects(&self, allowed: bool) {
73        unsafe { mlirContextSetAllowUnregisteredDialects(self.raw, allowed) }
74    }
75
76    /// Returns `true` if a given operation is registered in a context.
77    pub fn is_registered_operation(&self, name: &str) -> bool {
78        let name = StringRef::new(name);
79
80        unsafe { mlirContextIsRegisteredOperation(self.raw, name.to_raw()) }
81    }
82
83    /// Converts a context into a raw object.
84    pub const fn to_raw(&self) -> MlirContext {
85        self.raw
86    }
87
88    /// Attaches a diagnostic handler.
89    pub fn attach_diagnostic_handler<F: FnMut(Diagnostic) -> bool>(
90        &self,
91        handler: F,
92    ) -> DiagnosticHandlerId {
93        unsafe extern "C" fn handle<F: FnMut(Diagnostic) -> bool>(
94            diagnostic: MlirDiagnostic,
95            user_data: *mut c_void,
96        ) -> MlirLogicalResult {
97            LogicalResult::from((*(user_data as *mut F))(Diagnostic::from_raw(diagnostic))).to_raw()
98        }
99
100        unsafe extern "C" fn destroy<F: FnMut(Diagnostic) -> bool>(user_data: *mut c_void) {
101            drop(Box::from_raw(user_data as *mut F));
102        }
103
104        unsafe {
105            DiagnosticHandlerId::from_raw(mlirContextAttachDiagnosticHandler(
106                self.to_raw(),
107                Some(handle::<F>),
108                Box::into_raw(Box::new(handler)) as *mut _,
109                Some(destroy::<F>),
110            ))
111        }
112    }
113
114    /// Detaches a diagnostic handler.
115    pub fn detach_diagnostic_handler(&self, id: DiagnosticHandlerId) {
116        unsafe { mlirContextDetachDiagnosticHandler(self.to_raw(), id.to_raw()) }
117    }
118
119    pub(crate) fn to_ref(&self) -> ContextRef {
120        unsafe { ContextRef::from_raw(self.to_raw()) }
121    }
122}
123
124impl Drop for Context {
125    fn drop(&mut self) {
126        unsafe { mlirContextDestroy(self.raw) };
127    }
128}
129
130impl Default for Context {
131    fn default() -> Self {
132        Self::new()
133    }
134}
135
136impl PartialEq for Context {
137    fn eq(&self, other: &Self) -> bool {
138        unsafe { mlirContextEqual(self.raw, other.raw) }
139    }
140}
141
142impl<'a> PartialEq<ContextRef<'a>> for Context {
143    fn eq(&self, &other: &ContextRef<'a>) -> bool {
144        self.to_ref() == other
145    }
146}
147
148impl Eq for Context {}
149
150/// A reference to a context.
151#[derive(Clone, Copy, Debug)]
152pub struct ContextRef<'c> {
153    raw: MlirContext,
154    _reference: PhantomData<&'c Context>,
155}
156
157impl<'c> ContextRef<'c> {
158    /// Creates a context reference from a raw object.
159    ///
160    /// # Safety
161    ///
162    /// A raw object must be valid.
163    pub unsafe fn from_raw(raw: MlirContext) -> Self {
164        Self {
165            raw,
166            _reference: Default::default(),
167        }
168    }
169
170    /// Returns a context.
171    ///
172    /// This function is different from `deref` because the correct lifetime is
173    /// kept for the return type.
174    ///
175    /// # Safety
176    ///
177    /// The returned reference is safe to use only in the lifetime scope of the
178    /// context reference.
179    pub unsafe fn to_ref(&self) -> &'c Context {
180        // As we can't deref ContextRef<'a> into `&'a Context`, we forcibly cast its
181        // lifetime here to extend it from the lifetime of `ObjectRef<'a>` itself into
182        // `'a`.
183        transmute(self)
184    }
185}
186
187impl PartialEq for ContextRef<'_> {
188    fn eq(&self, other: &Self) -> bool {
189        unsafe { mlirContextEqual(self.raw, other.raw) }
190    }
191}
192
193impl PartialEq<Context> for ContextRef<'_> {
194    fn eq(&self, other: &Context) -> bool {
195        self == &other.to_ref()
196    }
197}
198
199impl Eq for ContextRef<'_> {}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    #[test]
206    fn new() {
207        Context::new();
208    }
209
210    #[test]
211    fn registered_dialect_count() {
212        let context = Context::new();
213
214        assert_eq!(context.registered_dialect_count(), 1);
215    }
216
217    #[test]
218    fn loaded_dialect_count() {
219        let context = Context::new();
220
221        assert_eq!(context.loaded_dialect_count(), 1);
222    }
223
224    #[test]
225    fn append_dialect_registry() {
226        let context = Context::new();
227
228        context.append_dialect_registry(&DialectRegistry::new());
229    }
230
231    #[test]
232    fn is_registered_operation() {
233        let context = Context::new();
234
235        assert!(context.is_registered_operation("builtin.module"));
236    }
237
238    #[test]
239    fn is_not_registered_operation() {
240        let context = Context::new();
241
242        assert!(!context.is_registered_operation("func.func"));
243    }
244
245    #[test]
246    fn enable_multi_threading() {
247        let context = Context::new();
248
249        context.enable_multi_threading(true);
250    }
251
252    #[test]
253    fn disable_multi_threading() {
254        let context = Context::new();
255
256        context.enable_multi_threading(false);
257    }
258
259    #[test]
260    fn allow_unregistered_dialects() {
261        let context = Context::new();
262
263        assert!(!context.allow_unregistered_dialects());
264    }
265
266    #[test]
267    fn set_allow_unregistered_dialects() {
268        let context = Context::new();
269
270        context.set_allow_unregistered_dialects(true);
271
272        assert!(context.allow_unregistered_dialects());
273    }
274
275    #[test]
276    fn attach_and_detach_diagnostic_handler() {
277        let context = Context::new();
278
279        let id = context.attach_diagnostic_handler(|diagnostic| {
280            println!("{}", diagnostic);
281            true
282        });
283
284        context.detach_diagnostic_handler(id);
285    }
286
287    #[test]
288    fn compare_contexts() {
289        let one = Context::new();
290        let other = Context::new();
291
292        assert_eq!(&one, &one);
293        assert_ne!(&one, &other);
294        assert_ne!(&other, &one);
295        assert_eq!(&other, &other);
296    }
297
298    #[test]
299    fn compare_context_refs() {
300        let one = Context::new();
301        let other = Context::new();
302
303        let one_ref = one.to_ref();
304        let other_ref = other.to_ref();
305
306        assert_eq!(&one, &one_ref);
307        assert_eq!(&one_ref, &one);
308
309        assert_eq!(&other, &other_ref);
310        assert_eq!(&other_ref, &other);
311
312        assert_ne!(&one, &other_ref);
313        assert_ne!(&other_ref, &one);
314
315        assert_ne!(&other, &one_ref);
316        assert_ne!(&one_ref, &other);
317    }
318
319    #[test]
320    fn context_to_ref() {
321        let ctx = Context::new();
322        let ctx_ref = ctx.to_ref();
323        let ctx_ref_to_ref: &Context = unsafe { ctx_ref.to_ref() };
324
325        assert_eq!(&ctx_ref, ctx_ref_to_ref);
326    }
327}