Skip to main content

melior/ir/
block.rs

1//! Blocks.
2
3mod argument;
4
5pub use self::argument::BlockArgument;
6use super::{
7    operation::OperationRefMut, Location, Operation, OperationRef, RegionRef, Type, TypeLike, Value,
8};
9use crate::{context::Context, utility::print_callback, Error};
10use mlir_sys::{
11    mlirBlockAddArgument, mlirBlockAppendOwnedOperation, mlirBlockCreate, mlirBlockDestroy,
12    mlirBlockDetach, mlirBlockEqual, mlirBlockGetArgument, mlirBlockGetFirstOperation,
13    mlirBlockGetNextInRegion, mlirBlockGetNumArguments, mlirBlockGetParentOperation,
14    mlirBlockGetParentRegion, mlirBlockGetTerminator, mlirBlockInsertOwnedOperation,
15    mlirBlockInsertOwnedOperationAfter, mlirBlockInsertOwnedOperationBefore, mlirBlockPrint,
16    MlirBlock,
17};
18use std::{
19    ffi::c_void,
20    fmt::{self, Debug, Display, Formatter},
21    marker::PhantomData,
22    mem::{forget, transmute},
23    ops::Deref,
24};
25
26/// A block.
27pub struct Block<'c> {
28    raw: MlirBlock,
29    _context: PhantomData<&'c Context>,
30}
31
32impl<'c> Block<'c> {
33    /// Creates a block.
34    // TODO Should we accept types and locations separately?
35    pub fn new(arguments: &[(Type<'c>, Location<'c>)]) -> Self {
36        unsafe {
37            Self::from_raw(mlirBlockCreate(
38                arguments.len() as isize,
39                arguments
40                    .iter()
41                    .map(|(argument, _)| argument.to_raw())
42                    .collect::<Vec<_>>()
43                    .as_ptr() as *const _,
44                arguments
45                    .iter()
46                    .map(|(_, location)| location.to_raw())
47                    .collect::<Vec<_>>()
48                    .as_ptr() as *const _,
49            ))
50        }
51    }
52
53    /// Returns an argument at a position.
54    pub fn argument(&self, index: usize) -> Result<BlockArgument<'c, '_>, Error> {
55        unsafe {
56            if index < self.argument_count() {
57                Ok(BlockArgument::from_raw(mlirBlockGetArgument(
58                    self.raw,
59                    index as isize,
60                )))
61            } else {
62                Err(Error::PositionOutOfBounds {
63                    name: "block argument",
64                    value: self.to_string(),
65                    index,
66                })
67            }
68        }
69    }
70
71    /// Returns a number of arguments.
72    pub fn argument_count(&self) -> usize {
73        unsafe { mlirBlockGetNumArguments(self.raw) as usize }
74    }
75
76    /// Returns a reference to the first operation.
77    pub fn first_operation(&self) -> Option<OperationRef<'c, '_>> {
78        unsafe { OperationRef::from_option_raw(mlirBlockGetFirstOperation(self.raw)) }
79    }
80
81    /// Returns a mutable reference to the first operation.
82    pub fn first_operation_mut(&mut self) -> Option<OperationRefMut<'c, '_>> {
83        unsafe { OperationRefMut::from_option_raw(mlirBlockGetFirstOperation(self.raw)) }
84    }
85
86    /// Returns a reference to a terminator operation.
87    pub fn terminator(&self) -> Option<OperationRef<'c, '_>> {
88        unsafe { OperationRef::from_option_raw(mlirBlockGetTerminator(self.raw)) }
89    }
90
91    /// Returns a mutable reference to a terminator operation.
92    pub fn terminator_mut(&mut self) -> Option<OperationRefMut<'c, '_>> {
93        unsafe { OperationRefMut::from_option_raw(mlirBlockGetTerminator(self.raw)) }
94    }
95
96    /// Returns a parent region.
97    // TODO Store lifetime of regions in blocks, or create another type like
98    // `InsertedBlockRef`?
99    pub fn parent_region(&self) -> Option<RegionRef<'c, '_>> {
100        unsafe { RegionRef::from_option_raw(mlirBlockGetParentRegion(self.raw)) }
101    }
102
103    /// Returns a parent operation.
104    pub fn parent_operation(&self) -> Option<OperationRef<'c, '_>> {
105        unsafe { OperationRef::from_option_raw(mlirBlockGetParentOperation(self.raw)) }
106    }
107
108    /// Adds an argument.
109    pub fn add_argument(&self, r#type: Type<'c>, location: Location<'c>) -> Value<'c, '_> {
110        unsafe {
111            Value::from_raw(mlirBlockAddArgument(
112                self.raw,
113                r#type.to_raw(),
114                location.to_raw(),
115            ))
116        }
117    }
118
119    /// Appends an operation.
120    pub fn append_operation(&self, operation: Operation<'c>) -> OperationRef<'c, '_> {
121        unsafe {
122            let operation = operation.into_raw();
123
124            mlirBlockAppendOwnedOperation(self.raw, operation);
125
126            OperationRef::from_raw(operation)
127        }
128    }
129
130    /// Inserts an operation.
131    // TODO How can we make those update functions take `&mut self`?
132    // TODO Use cells?
133    pub fn insert_operation(
134        &self,
135        position: usize,
136        operation: Operation<'c>,
137    ) -> OperationRef<'c, '_> {
138        unsafe {
139            let operation = operation.into_raw();
140
141            mlirBlockInsertOwnedOperation(self.raw, position as isize, operation);
142
143            OperationRef::from_raw(operation)
144        }
145    }
146
147    /// Inserts an operation after another.
148    pub fn insert_operation_after(
149        &self,
150        one: OperationRef<'c, '_>,
151        other: Operation<'c>,
152    ) -> OperationRef<'c, '_> {
153        unsafe {
154            let other = other.into_raw();
155
156            mlirBlockInsertOwnedOperationAfter(self.raw, one.to_raw(), other);
157
158            OperationRef::from_raw(other)
159        }
160    }
161
162    /// Inserts an operation before another.
163    pub fn insert_operation_before(
164        &self,
165        one: OperationRef<'c, '_>,
166        other: Operation<'c>,
167    ) -> OperationRef<'c, '_> {
168        unsafe {
169            let other = other.into_raw();
170
171            mlirBlockInsertOwnedOperationBefore(self.raw, one.to_raw(), other);
172
173            OperationRef::from_raw(other)
174        }
175    }
176
177    /// Detaches a block from a region and assumes its ownership.
178    ///
179    /// # Safety
180    ///
181    /// This function might invalidate existing references to the block if you
182    /// drop it too early.
183    // TODO Implement this for BlockRefMut instead and mark it safe.
184    pub unsafe fn detach(&self) -> Option<Block<'c>> {
185        if self.parent_region().is_some() {
186            mlirBlockDetach(self.raw);
187
188            Some(Block::from_raw(self.raw))
189        } else {
190            None
191        }
192    }
193
194    /// Returns a next block in a region.
195    pub fn next_in_region(&self) -> Option<BlockRef<'c, '_>> {
196        unsafe { BlockRef::from_option_raw(mlirBlockGetNextInRegion(self.raw)) }
197    }
198
199    /// Creates a block from a raw object.
200    ///
201    /// # Safety
202    ///
203    /// A raw object must be valid.
204    pub unsafe fn from_raw(raw: MlirBlock) -> Self {
205        Self {
206            raw,
207            _context: Default::default(),
208        }
209    }
210
211    /// Converts a block into a raw object.
212    pub const fn into_raw(self) -> MlirBlock {
213        let block = self.raw;
214
215        forget(self);
216
217        block
218    }
219
220    /// Converts a block into a raw object.
221    pub const fn to_raw(&self) -> MlirBlock {
222        self.raw
223    }
224}
225
226impl Drop for Block<'_> {
227    fn drop(&mut self) {
228        unsafe { mlirBlockDestroy(self.raw) };
229    }
230}
231
232impl PartialEq for Block<'_> {
233    fn eq(&self, other: &Self) -> bool {
234        unsafe { mlirBlockEqual(self.raw, other.raw) }
235    }
236}
237
238impl Eq for Block<'_> {}
239
240impl Display for Block<'_> {
241    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
242        let mut data = (formatter, Ok(()));
243
244        unsafe {
245            mlirBlockPrint(
246                self.raw,
247                Some(print_callback),
248                &mut data as *mut _ as *mut c_void,
249            );
250        }
251
252        data.1
253    }
254}
255
256impl Debug for Block<'_> {
257    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
258        writeln!(formatter, "Block(")?;
259        Display::fmt(self, formatter)?;
260        write!(formatter, ")")
261    }
262}
263
264/// A reference of a block.
265#[derive(Clone, Copy)]
266pub struct BlockRef<'c, 'a> {
267    raw: MlirBlock,
268    _reference: PhantomData<&'a Block<'c>>,
269}
270
271impl BlockRef<'_, '_> {
272    /// Creates a block reference from a raw object.
273    ///
274    /// # Safety
275    ///
276    /// A raw object must be valid.
277    pub unsafe fn from_raw(raw: MlirBlock) -> Self {
278        Self {
279            raw,
280            _reference: Default::default(),
281        }
282    }
283
284    /// Creates an optional block reference from a raw object.
285    ///
286    /// # Safety
287    ///
288    /// A raw object must be valid.
289    pub unsafe fn from_option_raw(raw: MlirBlock) -> Option<Self> {
290        if raw.ptr.is_null() {
291            None
292        } else {
293            Some(Self::from_raw(raw))
294        }
295    }
296}
297
298impl<'a> Deref for BlockRef<'_, 'a> {
299    type Target = Block<'a>;
300
301    fn deref(&self) -> &Self::Target {
302        unsafe { transmute(self) }
303    }
304}
305
306impl PartialEq for BlockRef<'_, '_> {
307    fn eq(&self, other: &Self) -> bool {
308        unsafe { mlirBlockEqual(self.raw, other.raw) }
309    }
310}
311
312impl Eq for BlockRef<'_, '_> {}
313
314impl Display for BlockRef<'_, '_> {
315    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
316        Display::fmt(self.deref(), formatter)
317    }
318}
319
320impl Debug for BlockRef<'_, '_> {
321    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
322        Debug::fmt(self.deref(), formatter)
323    }
324}
325
326#[cfg(test)]
327mod tests {
328    use super::*;
329    use crate::{
330        ir::{operation::OperationBuilder, r#type::IntegerType, Module, Region, ValueLike},
331        test::create_test_context,
332    };
333    use pretty_assertions::assert_eq;
334
335    #[test]
336    fn new() {
337        Block::new(&[]);
338    }
339
340    #[test]
341    fn argument() {
342        let context = create_test_context();
343        let r#type = IntegerType::new(&context, 64).into();
344
345        assert_eq!(
346            Block::new(&[(r#type, Location::unknown(&context))])
347                .argument(0)
348                .unwrap()
349                .r#type(),
350            r#type
351        );
352    }
353
354    #[test]
355    fn argument_error() {
356        assert_eq!(
357            Block::new(&[]).argument(0).unwrap_err(),
358            Error::PositionOutOfBounds {
359                name: "block argument",
360                value: "<<UNLINKED BLOCK>>\n".into(),
361                index: 0,
362            }
363        );
364    }
365
366    #[test]
367    fn argument_count() {
368        assert_eq!(Block::new(&[]).argument_count(), 0);
369    }
370
371    #[test]
372    fn parent_region() {
373        let region = Region::new();
374        let block = region.append_block(Block::new(&[]));
375
376        assert_eq!(block.parent_region().as_deref(), Some(&region));
377    }
378
379    #[test]
380    fn parent_region_none() {
381        let block = Block::new(&[]);
382
383        assert_eq!(block.parent_region(), None);
384    }
385
386    #[test]
387    fn parent_operation() {
388        let context = create_test_context();
389        let module = Module::new(Location::unknown(&context));
390
391        assert_eq!(
392            module.body().parent_operation(),
393            Some(module.as_operation())
394        );
395    }
396
397    #[test]
398    fn parent_operation_none() {
399        let block = Block::new(&[]);
400
401        assert_eq!(block.parent_operation(), None);
402    }
403
404    #[test]
405    fn terminator() {
406        let context = create_test_context();
407
408        let block = Block::new(&[]);
409
410        let operation = block.append_operation(
411            OperationBuilder::new("func.return", Location::unknown(&context))
412                .build()
413                .unwrap(),
414        );
415
416        assert_eq!(block.terminator(), Some(operation));
417    }
418
419    #[test]
420    fn terminator_none() {
421        assert_eq!(Block::new(&[]).terminator(), None);
422    }
423
424    #[test]
425    fn first_operation() {
426        let context = create_test_context();
427        context.set_allow_unregistered_dialects(true);
428        let block = Block::new(&[]);
429
430        let operation = block.append_operation(
431            OperationBuilder::new("foo", Location::unknown(&context))
432                .build()
433                .unwrap(),
434        );
435
436        assert_eq!(block.first_operation(), Some(operation));
437    }
438
439    #[test]
440    fn first_operation_none() {
441        let block = Block::new(&[]);
442
443        assert_eq!(block.first_operation(), None);
444    }
445
446    #[test]
447    fn append_operation() {
448        let context = create_test_context();
449        context.set_allow_unregistered_dialects(true);
450        let block = Block::new(&[]);
451
452        block.append_operation(
453            OperationBuilder::new("foo", Location::unknown(&context))
454                .build()
455                .unwrap(),
456        );
457    }
458
459    #[test]
460    fn insert_operation() {
461        let context = create_test_context();
462        context.set_allow_unregistered_dialects(true);
463        let block = Block::new(&[]);
464
465        block.insert_operation(
466            0,
467            OperationBuilder::new("foo", Location::unknown(&context))
468                .build()
469                .unwrap(),
470        );
471    }
472
473    #[test]
474    fn insert_operation_after() {
475        let context = create_test_context();
476        context.set_allow_unregistered_dialects(true);
477        let block = Block::new(&[]);
478
479        let first_operation = block.append_operation(
480            OperationBuilder::new("foo", Location::unknown(&context))
481                .build()
482                .unwrap(),
483        );
484        let second_operation = block.insert_operation_after(
485            first_operation,
486            OperationBuilder::new("foo", Location::unknown(&context))
487                .build()
488                .unwrap(),
489        );
490
491        assert_eq!(block.first_operation(), Some(first_operation));
492        assert_eq!(
493            block.first_operation().unwrap().next_in_block(),
494            Some(second_operation)
495        );
496    }
497
498    #[test]
499    fn insert_operation_before() {
500        let context = create_test_context();
501        context.set_allow_unregistered_dialects(true);
502        let block = Block::new(&[]);
503
504        let second_operation = block.append_operation(
505            OperationBuilder::new("foo", Location::unknown(&context))
506                .build()
507                .unwrap(),
508        );
509        let first_operation = block.insert_operation_before(
510            second_operation,
511            OperationBuilder::new("foo", Location::unknown(&context))
512                .build()
513                .unwrap(),
514        );
515
516        assert_eq!(block.first_operation(), Some(first_operation));
517        assert_eq!(
518            block.first_operation().unwrap().next_in_block(),
519            Some(second_operation)
520        );
521    }
522
523    #[test]
524    fn next_in_region() {
525        let region = Region::new();
526
527        let first_block = region.append_block(Block::new(&[]));
528        let second_block = region.append_block(Block::new(&[]));
529
530        assert_eq!(first_block.next_in_region(), Some(second_block));
531    }
532
533    #[test]
534    fn detach() {
535        let region = Region::new();
536        let block = region.append_block(Block::new(&[]));
537
538        assert_eq!(
539            unsafe { block.detach() }.unwrap().to_string(),
540            "<<UNLINKED BLOCK>>\n"
541        );
542    }
543
544    #[test]
545    fn detach_detached() {
546        let block = Block::new(&[]);
547
548        assert!(unsafe { block.detach() }.is_none());
549    }
550
551    #[test]
552    fn display() {
553        assert_eq!(Block::new(&[]).to_string(), "<<UNLINKED BLOCK>>\n");
554    }
555
556    #[test]
557    fn debug() {
558        assert_eq!(
559            format!("{:?}", &Block::new(&[])),
560            "Block(\n<<UNLINKED BLOCK>>\n)"
561        );
562    }
563}