1mod builder;
4mod printing_flags;
5mod result;
6
7pub use self::{
8 builder::OperationBuilder, printing_flags::OperationPrintingFlags, result::OperationResult,
9};
10use super::{Attribute, AttributeLike, BlockRef, Identifier, Location, RegionRef, Value};
11use crate::{
12 context::{Context, ContextRef},
13 utility::{print_callback, print_string_callback},
14 Error, StringRef,
15};
16use core::{
17 fmt,
18 mem::{forget, transmute},
19};
20use mlir_sys::{
21 mlirOperationClone, mlirOperationDestroy, mlirOperationDump, mlirOperationEqual,
22 mlirOperationGetAttribute, mlirOperationGetAttributeByName, mlirOperationGetBlock,
23 mlirOperationGetContext, mlirOperationGetLocation, mlirOperationGetName,
24 mlirOperationGetNextInBlock, mlirOperationGetNumAttributes, mlirOperationGetNumOperands,
25 mlirOperationGetNumRegions, mlirOperationGetNumResults, mlirOperationGetNumSuccessors,
26 mlirOperationGetOperand, mlirOperationGetParentOperation, mlirOperationGetRegion,
27 mlirOperationGetResult, mlirOperationGetSuccessor, mlirOperationPrint,
28 mlirOperationPrintWithFlags, mlirOperationRemoveAttributeByName, mlirOperationRemoveFromParent,
29 mlirOperationSetAttributeByName, mlirOperationVerify, MlirOperation,
30};
31use std::{
32 ffi::c_void,
33 fmt::{Debug, Display, Formatter},
34 marker::PhantomData,
35 ops::{Deref, DerefMut},
36};
37
38pub struct Operation<'c> {
40 raw: MlirOperation,
41 _context: PhantomData<&'c Context>,
42}
43
44impl<'c> Operation<'c> {
45 pub fn context(&self) -> ContextRef<'c> {
47 unsafe { ContextRef::from_raw(mlirOperationGetContext(self.raw)) }
48 }
49
50 pub fn name(&self) -> Identifier<'c> {
52 unsafe { Identifier::from_raw(mlirOperationGetName(self.raw)) }
53 }
54
55 pub fn block(&self) -> Option<BlockRef<'c, '_>> {
59 unsafe { BlockRef::from_option_raw(mlirOperationGetBlock(self.raw)) }
60 }
61
62 pub fn operand_count(&self) -> usize {
64 unsafe { mlirOperationGetNumOperands(self.raw) as usize }
65 }
66
67 pub fn operand(&self, index: usize) -> Result<Value<'c, '_>, Error> {
69 if index < self.operand_count() {
70 Ok(unsafe { Value::from_raw(mlirOperationGetOperand(self.raw, index as isize)) })
71 } else {
72 Err(Error::PositionOutOfBounds {
73 name: "operation operand",
74 value: self.to_string(),
75 index,
76 })
77 }
78 }
79
80 pub fn operands(&self) -> impl Iterator<Item = Value<'c, '_>> {
82 (0..self.operand_count()).map(|index| self.operand(index).expect("valid operand index"))
83 }
84
85 pub fn result_count(&self) -> usize {
87 unsafe { mlirOperationGetNumResults(self.raw) as usize }
88 }
89
90 pub fn result(&self, index: usize) -> Result<OperationResult<'c, '_>, Error> {
92 if index < self.result_count() {
93 Ok(unsafe {
94 OperationResult::from_raw(mlirOperationGetResult(self.raw, index as isize))
95 })
96 } else {
97 Err(Error::PositionOutOfBounds {
98 name: "operation result",
99 value: self.to_string(),
100 index,
101 })
102 }
103 }
104
105 pub fn results(&self) -> impl Iterator<Item = OperationResult<'c, '_>> {
107 (0..self.result_count()).map(|index| self.result(index).expect("valid result index"))
108 }
109
110 pub fn region_count(&self) -> usize {
112 unsafe { mlirOperationGetNumRegions(self.raw) as usize }
113 }
114
115 pub fn region(&self, index: usize) -> Result<RegionRef<'c, '_>, Error> {
117 if index < self.region_count() {
118 Ok(unsafe { RegionRef::from_raw(mlirOperationGetRegion(self.raw, index as isize)) })
119 } else {
120 Err(Error::PositionOutOfBounds {
121 name: "region",
122 value: self.to_string(),
123 index,
124 })
125 }
126 }
127
128 pub fn regions(&self) -> impl Iterator<Item = RegionRef<'c, '_>> {
130 (0..self.region_count()).map(|index| self.region(index).expect("valid result index"))
131 }
132
133 pub fn location(&self) -> Location<'c> {
135 unsafe { Location::from_raw(mlirOperationGetLocation(self.raw)) }
136 }
137
138 pub fn successor_count(&self) -> usize {
140 unsafe { mlirOperationGetNumSuccessors(self.raw) as usize }
141 }
142
143 pub fn successor(&self, index: usize) -> Result<BlockRef<'c, '_>, Error> {
145 if index < self.successor_count() {
146 Ok(unsafe { BlockRef::from_raw(mlirOperationGetSuccessor(self.raw, index as isize)) })
147 } else {
148 Err(Error::PositionOutOfBounds {
149 name: "successor",
150 value: self.to_string(),
151 index,
152 })
153 }
154 }
155
156 pub fn successors(&self) -> impl Iterator<Item = BlockRef<'c, '_>> {
158 (0..self.successor_count())
159 .map(|index| self.successor(index).expect("valid successor index"))
160 }
161
162 pub fn attribute_count(&self) -> usize {
164 unsafe { mlirOperationGetNumAttributes(self.raw) as usize }
165 }
166
167 pub fn attribute_at(&self, index: usize) -> Result<(Identifier<'c>, Attribute<'c>), Error> {
169 if index < self.attribute_count() {
170 unsafe {
171 let named_attribute = mlirOperationGetAttribute(self.raw, index as isize);
172 Ok((
173 Identifier::from_raw(named_attribute.name),
174 Attribute::from_raw(named_attribute.attribute),
175 ))
176 }
177 } else {
178 Err(Error::PositionOutOfBounds {
179 name: "attribute",
180 value: self.to_string(),
181 index,
182 })
183 }
184 }
185
186 pub fn attributes(&self) -> impl Iterator<Item = (Identifier<'c>, Attribute<'c>)> + '_ {
188 (0..self.attribute_count())
189 .map(|index| self.attribute_at(index).expect("valid attribute index"))
190 }
191
192 pub fn attribute(&self, name: &str) -> Result<Attribute<'c>, Error> {
194 unsafe {
195 Attribute::from_option_raw(mlirOperationGetAttributeByName(
196 self.raw,
197 StringRef::new(name).to_raw(),
198 ))
199 }
200 .ok_or_else(|| Error::AttributeNotFound(name.into()))
201 }
202
203 pub fn has_attribute(&self, name: &str) -> bool {
205 self.attribute(name).is_ok()
206 }
207
208 pub fn set_attribute(&mut self, name: &str, attribute: Attribute<'c>) {
210 unsafe {
211 mlirOperationSetAttributeByName(
212 self.raw,
213 StringRef::new(name).to_raw(),
214 attribute.to_raw(),
215 )
216 }
217 }
218
219 pub fn remove_attribute(&mut self, name: &str) -> Result<(), Error> {
221 unsafe { mlirOperationRemoveAttributeByName(self.raw, StringRef::new(name).to_raw()) }
222 .then_some(())
223 .ok_or_else(|| Error::AttributeNotFound(name.into()))
224 }
225
226 pub fn next_in_block(&self) -> Option<OperationRef<'c, '_>> {
228 unsafe { OperationRef::from_option_raw(mlirOperationGetNextInBlock(self.raw)) }
229 }
230
231 pub fn next_in_block_mut(&self) -> Option<OperationRefMut<'c, '_>> {
233 unsafe { OperationRefMut::from_option_raw(mlirOperationGetNextInBlock(self.raw)) }
234 }
235
236 pub fn previous_in_block(&self) -> Option<OperationRef<'c, '_>> {
238 todo!("mlirOperationGetPrevInBlock is not exposed in the C API")
239 }
240
241 pub fn parent_operation(&self) -> Option<OperationRef<'c, '_>> {
243 unsafe { OperationRef::from_option_raw(mlirOperationGetParentOperation(self.raw)) }
244 }
245
246 pub fn remove_from_parent(&mut self) {
248 unsafe { mlirOperationRemoveFromParent(self.raw) }
249 }
250
251 pub fn verify(&self) -> bool {
253 unsafe { mlirOperationVerify(self.raw) }
254 }
255
256 pub fn dump(&self) {
258 unsafe { mlirOperationDump(self.raw) }
259 }
260
261 pub fn to_string_with_flags(&self, flags: OperationPrintingFlags) -> Result<String, Error> {
263 let mut data = (String::new(), Ok::<_, Error>(()));
264
265 unsafe {
266 mlirOperationPrintWithFlags(
267 self.raw,
268 flags.to_raw(),
269 Some(print_string_callback),
270 &mut data as *mut _ as *mut _,
271 );
272 }
273
274 data.1?;
275
276 Ok(data.0)
277 }
278
279 pub unsafe fn from_raw(raw: MlirOperation) -> Self {
285 Self {
286 raw,
287 _context: Default::default(),
288 }
289 }
290
291 pub unsafe fn from_option_raw(raw: MlirOperation) -> Option<Self> {
297 if raw.ptr.is_null() {
298 None
299 } else {
300 Some(Self::from_raw(raw))
301 }
302 }
303
304 pub const fn into_raw(self) -> MlirOperation {
306 let operation = self.raw;
307
308 forget(self);
309
310 operation
311 }
312}
313
314impl Clone for Operation<'_> {
315 fn clone(&self) -> Self {
316 unsafe { Self::from_raw(mlirOperationClone(self.raw)) }
317 }
318}
319
320impl Drop for Operation<'_> {
321 fn drop(&mut self) {
322 unsafe { mlirOperationDestroy(self.raw) };
323 }
324}
325
326impl PartialEq for Operation<'_> {
327 fn eq(&self, other: &Self) -> bool {
328 unsafe { mlirOperationEqual(self.raw, other.raw) }
329 }
330}
331
332impl Eq for Operation<'_> {}
333
334impl Display for Operation<'_> {
335 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
336 let mut data = (formatter, Ok(()));
337
338 unsafe {
339 mlirOperationPrint(
340 self.raw,
341 Some(print_callback),
342 &mut data as *mut _ as *mut c_void,
343 );
344 }
345
346 data.1
347 }
348}
349
350impl Debug for Operation<'_> {
351 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
352 writeln!(formatter, "Operation(")?;
353 Display::fmt(self, formatter)?;
354 write!(formatter, ")")
355 }
356}
357
358#[derive(Clone, Copy)]
360pub struct OperationRef<'c, 'a> {
361 raw: MlirOperation,
362 _reference: PhantomData<&'a Operation<'c>>,
363}
364
365impl<'c, 'a> OperationRef<'c, 'a> {
366 pub fn result(self, index: usize) -> Result<OperationResult<'c, 'a>, Error> {
368 unsafe { self.to_ref() }.result(index)
369 }
370
371 pub unsafe fn to_ref(&self) -> &'a Operation<'c> {
381 transmute(self)
385 }
386
387 pub const fn to_raw(self) -> MlirOperation {
389 self.raw
390 }
391
392 pub unsafe fn from_raw(raw: MlirOperation) -> Self {
398 Self {
399 raw,
400 _reference: Default::default(),
401 }
402 }
403
404 pub unsafe fn from_option_raw(raw: MlirOperation) -> Option<Self> {
410 if raw.ptr.is_null() {
411 None
412 } else {
413 Some(Self::from_raw(raw))
414 }
415 }
416}
417
418impl<'c> Deref for OperationRef<'c, '_> {
419 type Target = Operation<'c>;
420
421 fn deref(&self) -> &Self::Target {
422 unsafe { transmute(self) }
423 }
424}
425
426impl PartialEq for OperationRef<'_, '_> {
427 fn eq(&self, other: &Self) -> bool {
428 unsafe { mlirOperationEqual(self.raw, other.raw) }
429 }
430}
431
432impl Eq for OperationRef<'_, '_> {}
433
434impl Display for OperationRef<'_, '_> {
435 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
436 Display::fmt(self.deref(), formatter)
437 }
438}
439
440impl Debug for OperationRef<'_, '_> {
441 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
442 Debug::fmt(self.deref(), formatter)
443 }
444}
445
446#[derive(Clone, Copy)]
448pub struct OperationRefMut<'c, 'a> {
449 raw: MlirOperation,
450 _reference: PhantomData<&'a Operation<'c>>,
451}
452
453impl OperationRefMut<'_, '_> {
454 pub const fn to_raw(self) -> MlirOperation {
456 self.raw
457 }
458
459 pub unsafe fn from_raw(raw: MlirOperation) -> Self {
465 Self {
466 raw,
467 _reference: Default::default(),
468 }
469 }
470
471 pub unsafe fn from_option_raw(raw: MlirOperation) -> Option<Self> {
477 if raw.ptr.is_null() {
478 None
479 } else {
480 Some(Self::from_raw(raw))
481 }
482 }
483}
484
485impl<'c> Deref for OperationRefMut<'c, '_> {
486 type Target = Operation<'c>;
487
488 fn deref(&self) -> &Self::Target {
489 unsafe { transmute(self) }
490 }
491}
492
493impl DerefMut for OperationRefMut<'_, '_> {
494 fn deref_mut(&mut self) -> &mut Self::Target {
495 unsafe { transmute(self) }
496 }
497}
498
499impl PartialEq for OperationRefMut<'_, '_> {
500 fn eq(&self, other: &Self) -> bool {
501 unsafe { mlirOperationEqual(self.raw, other.raw) }
502 }
503}
504
505impl Eq for OperationRefMut<'_, '_> {}
506
507impl Display for OperationRefMut<'_, '_> {
508 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
509 Display::fmt(self.deref(), formatter)
510 }
511}
512
513impl Debug for OperationRefMut<'_, '_> {
514 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
515 Debug::fmt(self.deref(), formatter)
516 }
517}
518
519#[cfg(test)]
520mod tests {
521 use super::*;
522 use crate::{
523 context::Context,
524 ir::{attribute::StringAttribute, Block, Location, Region, Type},
525 test::create_test_context,
526 };
527 use pretty_assertions::assert_eq;
528
529 #[test]
530 fn new() {
531 let context = create_test_context();
532 context.set_allow_unregistered_dialects(true);
533 OperationBuilder::new("foo", Location::unknown(&context))
534 .build()
535 .unwrap();
536 }
537
538 #[test]
539 fn name() {
540 let context = Context::new();
541 context.set_allow_unregistered_dialects(true);
542
543 assert_eq!(
544 OperationBuilder::new("foo", Location::unknown(&context),)
545 .build()
546 .unwrap()
547 .name(),
548 Identifier::new(&context, "foo")
549 );
550 }
551
552 #[test]
553 fn block() {
554 let context = create_test_context();
555 context.set_allow_unregistered_dialects(true);
556 let block = Block::new(&[]);
557 let operation = block.append_operation(
558 OperationBuilder::new("foo", Location::unknown(&context))
559 .build()
560 .unwrap(),
561 );
562
563 assert_eq!(operation.block().as_deref(), Some(&block));
564 }
565
566 #[test]
567 fn block_none() {
568 let context = create_test_context();
569 context.set_allow_unregistered_dialects(true);
570 assert_eq!(
571 OperationBuilder::new("foo", Location::unknown(&context))
572 .build()
573 .unwrap()
574 .block(),
575 None
576 );
577 }
578
579 #[test]
580 fn result_error() {
581 let context = create_test_context();
582 context.set_allow_unregistered_dialects(true);
583 assert_eq!(
584 OperationBuilder::new("foo", Location::unknown(&context))
585 .build()
586 .unwrap()
587 .result(0)
588 .unwrap_err(),
589 Error::PositionOutOfBounds {
590 name: "operation result",
591 value: "\"foo\"() : () -> ()\n".into(),
592 index: 0
593 }
594 );
595 }
596
597 #[test]
598 fn region_none() {
599 let context = create_test_context();
600 context.set_allow_unregistered_dialects(true);
601 assert_eq!(
602 OperationBuilder::new("foo", Location::unknown(&context),)
603 .build()
604 .unwrap()
605 .region(0),
606 Err(Error::PositionOutOfBounds {
607 name: "region",
608 value: "\"foo\"() : () -> ()\n".into(),
609 index: 0
610 })
611 );
612 }
613
614 #[test]
615 fn operands() {
616 let context = create_test_context();
617 context.set_allow_unregistered_dialects(true);
618
619 let location = Location::unknown(&context);
620 let r#type = Type::index(&context);
621 let block = Block::new(&[(r#type, location)]);
622 let argument: Value = block.argument(0).unwrap().into();
623
624 let operands = vec![argument, argument, argument];
625 let operation = OperationBuilder::new("foo", Location::unknown(&context))
626 .add_operands(&operands)
627 .build()
628 .unwrap();
629
630 assert_eq!(
631 operation.operands().skip(1).collect::<Vec<_>>(),
632 vec![argument, argument]
633 );
634 }
635
636 #[test]
637 fn regions() {
638 let context = create_test_context();
639 context.set_allow_unregistered_dialects(true);
640
641 let operation = OperationBuilder::new("foo", Location::unknown(&context))
642 .add_regions([Region::new()])
643 .build()
644 .unwrap();
645
646 assert_eq!(
647 operation.regions().collect::<Vec<_>>(),
648 vec![operation.region(0).unwrap()]
649 );
650 }
651
652 #[test]
653 fn location() {
654 let context = create_test_context();
655 context.set_allow_unregistered_dialects(true);
656 let location = Location::new(&context, "test", 1, 1);
657
658 let operation = OperationBuilder::new("foo", location)
659 .add_regions([Region::new()])
660 .build()
661 .unwrap();
662
663 assert_eq!(operation.location(), location);
664 }
665
666 #[test]
667 fn attribute() {
668 let context = create_test_context();
669 context.set_allow_unregistered_dialects(true);
670
671 let mut operation = OperationBuilder::new("foo", Location::unknown(&context))
672 .add_attributes(&[(
673 Identifier::new(&context, "foo"),
674 StringAttribute::new(&context, "bar").into(),
675 )])
676 .build()
677 .unwrap();
678 assert!(operation.has_attribute("foo"));
679 assert_eq!(
680 operation.attribute("foo").map(|a| a.to_string()),
681 Ok("\"bar\"".into())
682 );
683 assert!(operation.remove_attribute("foo").is_ok());
684 assert!(operation.remove_attribute("foo").is_err());
685 operation.set_attribute("foo", StringAttribute::new(&context, "foo").into());
686 assert_eq!(
687 operation.attribute("foo").map(|a| a.to_string()),
688 Ok("\"foo\"".into())
689 );
690 assert_eq!(
691 operation.attributes().next(),
692 Some((
693 Identifier::new(&context, "foo"),
694 StringAttribute::new(&context, "foo").into()
695 ))
696 )
697 }
698
699 #[test]
700 fn clone() {
701 let context = create_test_context();
702 context.set_allow_unregistered_dialects(true);
703 let operation = OperationBuilder::new("foo", Location::unknown(&context))
704 .build()
705 .unwrap();
706
707 let _ = operation.clone();
708 }
709
710 #[test]
711 fn display() {
712 let context = create_test_context();
713 context.set_allow_unregistered_dialects(true);
714
715 assert_eq!(
716 OperationBuilder::new("foo", Location::unknown(&context),)
717 .build()
718 .unwrap()
719 .to_string(),
720 "\"foo\"() : () -> ()\n"
721 );
722 }
723
724 #[test]
725 fn debug() {
726 let context = create_test_context();
727 context.set_allow_unregistered_dialects(true);
728
729 assert_eq!(
730 format!(
731 "{:?}",
732 OperationBuilder::new("foo", Location::unknown(&context))
733 .build()
734 .unwrap()
735 ),
736 "Operation(\n\"foo\"() : () -> ()\n)"
737 );
738 }
739
740 #[test]
741 fn to_string_with_flags() {
742 let context = create_test_context();
743 context.set_allow_unregistered_dialects(true);
744
745 assert_eq!(
746 OperationBuilder::new("foo", Location::unknown(&context))
747 .build()
748 .unwrap()
749 .to_string_with_flags(
750 OperationPrintingFlags::new()
751 .elide_large_elements_attributes(100)
752 .enable_debug_info(true, true)
753 .print_generic_operation_form()
754 .use_local_scope()
755 ),
756 Ok("\"foo\"() : () -> () [unknown]".into())
757 );
758 }
759
760 #[test]
761 fn remove_from_parent() {
762 let context = create_test_context();
763 context.set_allow_unregistered_dialects(true);
764
765 let location = Location::unknown(&context);
766 let mut block = Block::new(&[]);
767
768 let first_operation = block.append_operation(
769 OperationBuilder::new("foo", location)
770 .add_results(&[Type::index(&context)])
771 .build()
772 .unwrap(),
773 );
774 block.append_operation(
775 OperationBuilder::new("bar", location)
776 .add_operands(&[first_operation.result(0).unwrap().into()])
777 .build()
778 .unwrap(),
779 );
780 block.first_operation_mut().unwrap().remove_from_parent();
781
782 assert_eq!(block.first_operation().unwrap().next_in_block(), None);
783 assert_eq!(
784 block.first_operation().unwrap().to_string(),
785 "\"bar\"(<<UNKNOWN SSA VALUE>>) : (index) -> ()"
786 );
787 }
788
789 #[test]
790 fn parent_operation() {
791 let context = create_test_context();
792 context.set_allow_unregistered_dialects(true);
793
794 let location = Location::unknown(&context);
795 let block = Block::new(&[]);
796
797 let operation = block.append_operation(
798 OperationBuilder::new("foo", location)
799 .add_results(&[Type::index(&context)])
800 .add_regions([{
801 let region = Region::new();
802
803 let block = Block::new(&[]);
804 block.append_operation(OperationBuilder::new("bar", location).build().unwrap());
805
806 region.append_block(block);
807 region
808 }])
809 .build()
810 .unwrap(),
811 );
812
813 assert_eq!(operation.parent_operation(), None);
814 assert_eq!(
815 &operation
816 .region(0)
817 .unwrap()
818 .first_block()
819 .unwrap()
820 .first_operation()
821 .unwrap()
822 .parent_operation()
823 .unwrap(),
824 &operation
825 );
826 }
827}