1mod builder;
2pub mod equivalence;
3mod name;
4
5use alloc::{boxed::Box, rc::Rc};
6use core::{
7 fmt,
8 ptr::{DynMetadata, NonNull, Pointee},
9 sync::atomic::AtomicU32,
10};
11
12use smallvec::SmallVec;
13
14pub use self::{builder::OperationBuilder, name::OperationName};
15use super::{
16 effects::{HasRecursiveMemoryEffects, MemoryEffect, MemoryEffectOpInterface},
17 *,
18};
19use crate::{
20 adt::SmallSet, patterns::RewritePatternSet, AttributeSet, AttributeValue, Forward, ProgramPoint,
21};
22
23pub type OperationRef = UnsafeIntrusiveEntityRef<Operation>;
24pub type OpList = EntityList<Operation>;
25pub type OpCursor<'a> = EntityCursor<'a, Operation>;
26pub type OpCursorMut<'a> = EntityCursorMut<'a, Operation>;
27
28#[derive(Spanned)]
78pub struct Operation {
79 context: NonNull<Context>,
81 name: OperationName,
83 offset: usize,
89 order: AtomicU32,
96 #[span]
97 pub span: SourceSpan,
98 pub attrs: AttributeSet,
100 pub operands: OpOperandStorage,
107 pub results: OpResultStorage,
109 pub successors: OpSuccessorStorage,
112 pub regions: RegionList,
114}
115
116impl Eq for Operation {}
119impl PartialEq for Operation {
120 fn eq(&self, other: &Self) -> bool {
121 core::ptr::addr_eq(self, other)
122 }
123}
124
125impl core::hash::Hash for Operation {
128 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
129 core::ptr::hash(self, state)
130 }
131}
132
133impl fmt::Debug for Operation {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 f.debug_struct("Operation")
136 .field_with("name", |f| write!(f, "{}", &self.name()))
137 .field("offset", &self.offset)
138 .field("order", &self.order)
139 .field("attrs", &self.attrs)
140 .field("block", &self.parent().as_ref().map(|b| b.borrow().id()))
141 .field_with("operands", |f| {
142 let mut list = f.debug_list();
143 for operand in self.operands().all() {
144 list.entry(&operand.borrow());
145 }
146 list.finish()
147 })
148 .field("results", &self.results)
149 .field("successors", &self.successors)
150 .finish_non_exhaustive()
151 }
152}
153
154impl fmt::Debug for OperationRef {
155 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
156 fmt::Debug::fmt(&self.borrow(), f)
157 }
158}
159
160impl fmt::Display for OperationRef {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 write!(f, "{}", &self.borrow().name())
163 }
164}
165
166impl AsRef<dyn Op> for Operation {
167 fn as_ref(&self) -> &dyn Op {
168 self.name.upcast(self.container()).unwrap()
169 }
170}
171
172impl AsMut<dyn Op> for Operation {
173 fn as_mut(&mut self) -> &mut dyn Op {
174 self.name.upcast_mut(self.container().cast_mut()).unwrap()
175 }
176}
177
178impl Entity for Operation {}
179impl EntityWithParent for Operation {
180 type Parent = Block;
181}
182impl EntityListItem for Operation {
183 fn on_inserted(this: OperationRef, _cursor: &mut EntityCursorMut<'_, Self>) {
184 if this.name().implements::<dyn Symbol>() {
186 let parent = this.nearest_symbol_table();
187 if let Some(mut parent) = parent {
188 if parent.name().implements::<dyn SymbolTable>() {
189 let mut symbol_table = parent.borrow_mut();
192 let sym_manager = symbol_table.as_trait_mut::<dyn SymbolTable>().unwrap();
193 let mut sym_manager = sym_manager.symbol_manager_mut();
194
195 let symbol_ref = this.borrow().as_symbol_ref().unwrap();
196
197 let is_new = sym_manager.insert_new(symbol_ref, ProgramPoint::Invalid);
198 assert!(
199 is_new,
200 "Unable to insert {} in symbol table of {}: symbol {} is already \
201 registered to another operation {}.",
202 this.name(),
203 parent.name(),
204 symbol_ref.borrow().name(),
205 sym_manager.lookup(symbol_ref.borrow().name()).unwrap().borrow().name()
206 );
207 }
208 }
209 }
210 let order_offset = core::mem::offset_of!(Operation, order);
211 unsafe {
212 let ptr = UnsafeIntrusiveEntityRef::as_ptr(&this);
213 let order_ptr = ptr.byte_add(order_offset).cast::<AtomicU32>();
214 (*order_ptr).store(Self::INVALID_ORDER, core::sync::atomic::Ordering::Release);
215 }
216 }
217
218 fn on_transfer(_this: OperationRef, _from: &mut EntityList<Self>, to: &mut EntityList<Self>) {
219 let mut to = to.parent();
221 to.borrow_mut().invalidate_op_order();
222 }
223
224 fn on_removed(this: OperationRef, _list: &mut EntityCursorMut<'_, Self>) {
225 if this.name().implements::<dyn Symbol>() {
227 let parent = this.nearest_symbol_table();
228 if let Some(mut parent) = parent {
229 if parent.name().implements::<dyn SymbolTable>() {
231 let mut symbol_table = parent.borrow_mut();
232 let sym_manager = symbol_table.as_trait_mut::<dyn SymbolTable>().unwrap();
233 let mut sym_manager = sym_manager.symbol_manager_mut();
234
235 let symbol_ref = this.borrow().as_symbol_ref().unwrap();
236
237 sym_manager.remove(symbol_ref);
238 };
239 }
240 }
241 }
242}
243
244impl EntityParent<Region> for Operation {
245 fn offset() -> usize {
246 core::mem::offset_of!(Operation, regions)
247 }
248}
249
250impl Operation {
252 #[doc(hidden)]
253 pub unsafe fn uninit<T: Op>(context: Rc<Context>, name: OperationName, offset: usize) -> Self {
254 assert!(name.is::<T>());
255
256 Self {
257 context: unsafe { NonNull::new_unchecked(Rc::as_ptr(&context).cast_mut()) },
258 name,
259 offset,
260 order: AtomicU32::new(0),
261 span: Default::default(),
262 attrs: Default::default(),
263 operands: Default::default(),
264 results: Default::default(),
265 successors: Default::default(),
266 regions: Default::default(),
267 }
268 }
269}
270
271impl OperationRef {
273 pub fn insert_at_start(self, mut block: BlockRef) {
274 assert!(
275 self.parent().is_none(),
276 "cannot insert operation that is already attached to another block"
277 );
278 {
279 let mut block = block.borrow_mut();
280 block.body_mut().push_front(self);
281 }
282 }
283
284 pub fn insert_at_end(self, mut block: BlockRef) {
285 assert!(
286 self.parent().is_none(),
287 "cannot insert operation that is already attached to another block"
288 );
289 {
290 let mut block = block.borrow_mut();
291 block.body_mut().push_back(self);
292 }
293 }
294
295 pub fn insert_before(self, before: OperationRef) {
296 assert!(
297 self.parent().is_none(),
298 "cannot insert operation that is already attached to another block"
299 );
300 let mut block = before.parent().expect("'before' block is not attached to a block");
301 {
302 let mut block = block.borrow_mut();
303 let block_body = block.body_mut();
304 let mut cursor = unsafe { block_body.cursor_mut_from_ptr(before) };
305 cursor.insert_before(self);
306 }
307 }
308
309 pub fn insert_after(self, after: OperationRef) {
310 assert!(
311 self.parent().is_none(),
312 "cannot insert operation that is already attached to another block"
313 );
314 let mut block = after.parent().expect("'after' block is not attached to a block");
315 {
316 let mut block = block.borrow_mut();
317 let block_body = block.body_mut();
318 let mut cursor = unsafe { block_body.cursor_mut_from_ptr(after) };
319 cursor.insert_after(self);
320 }
321 }
322}
323
324impl OperationRef {
326 pub fn name(&self) -> OperationName {
327 let ptr = OperationRef::as_ptr(self);
328 unsafe {
334 let name_ptr = core::ptr::addr_of!((*ptr).name);
335 OperationName::clone(&*name_ptr)
336 }
337 }
338
339 pub fn parent_op(&self) -> Option<OperationRef> {
342 self.parent_region().and_then(|region| region.parent())
343 }
344
345 pub fn parent_region(&self) -> Option<RegionRef> {
347 self.grandparent()
348 }
349
350 pub fn nearest_symbol_table(&self) -> Option<OperationRef> {
354 let mut parent = self.parent_op();
355 while let Some(parent_op) = parent.take() {
356 if parent_op.name().implements::<dyn SymbolTable>() {
357 return Some(parent_op);
358 }
359 parent = parent_op.parent_op();
360 }
361 None
362 }
363}
364
365impl Operation {
367 pub fn name(&self) -> OperationName {
371 self.name.clone()
372 }
373
374 pub fn dialect(&self) -> Rc<dyn Dialect> {
376 self.context().get_registered_dialect(self.name.dialect())
377 }
378
379 #[inline]
381 pub fn set_span(&mut self, span: SourceSpan) {
382 self.span = span;
383 }
384
385 #[inline(always)]
387 pub fn context(&self) -> &Context {
388 unsafe { self.context.as_ref() }
391 }
392
393 pub fn context_rc(&self) -> Rc<Context> {
395 unsafe {
404 let ptr = self.context.as_ptr().cast_const();
405 Rc::increment_strong_count(ptr);
406 Rc::from_raw(ptr)
407 }
408 }
409}
410
411impl Operation {
413 pub fn verify(&self) -> Result<(), Report> {
415 let dyn_op: &dyn Op = self.as_ref();
416 dyn_op.verify(self.context())
417 }
418
419 pub fn recursively_verify(&self) -> Result<(), Report> {
424 self.postwalk(|op: &Operation| op.verify().into()).into_result()
425 }
426}
427
428impl Operation {
430 pub(super) const fn container(&self) -> *const () {
431 unsafe {
432 let ptr = self as *const Self;
433 ptr.byte_sub(self.offset).cast()
434 }
435 }
436
437 #[inline(always)]
438 pub fn as_operation_ref(&self) -> OperationRef {
439 unsafe { OperationRef::from_raw(self) }
445 }
446
447 #[inline]
449 pub fn is<T: 'static>(&self) -> bool {
450 self.name.is::<T>()
451 }
452
453 #[inline]
455 pub fn implements<Trait>(&self) -> bool
456 where
457 Trait: ?Sized + Pointee<Metadata = DynMetadata<Trait>> + 'static,
458 {
459 self.name.implements::<Trait>()
460 }
461
462 pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
464 self.name.downcast_ref::<T>(self.container())
465 }
466
467 pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
469 self.name.downcast_mut::<T>(self.container().cast_mut())
470 }
471
472 pub fn as_trait<Trait>(&self) -> Option<&Trait>
474 where
475 Trait: ?Sized + Pointee<Metadata = DynMetadata<Trait>> + 'static,
476 {
477 self.name.upcast(self.container())
478 }
479
480 pub fn as_trait_mut<Trait>(&mut self) -> Option<&mut Trait>
482 where
483 Trait: ?Sized + Pointee<Metadata = DynMetadata<Trait>> + 'static,
484 {
485 self.name.upcast_mut(self.container().cast_mut())
486 }
487}
488
489impl Operation {
491 #[inline(always)]
493 pub fn attributes(&self) -> &AttributeSet {
494 &self.attrs
495 }
496
497 #[inline(always)]
499 pub fn attributes_mut(&mut self) -> &mut AttributeSet {
500 &mut self.attrs
501 }
502
503 pub fn get_attribute(&self, name: impl Into<interner::Symbol>) -> Option<&dyn AttributeValue> {
505 self.attrs.get_any(name.into())
506 }
507
508 pub fn get_attribute_mut(
510 &mut self,
511 name: impl Into<interner::Symbol>,
512 ) -> Option<&mut dyn AttributeValue> {
513 self.attrs.get_any_mut(name.into())
514 }
515
516 pub fn get_typed_attribute<T>(&self, name: impl Into<interner::Symbol>) -> Option<&T>
519 where
520 T: AttributeValue,
521 {
522 self.attrs.get(name.into())
523 }
524
525 pub fn get_typed_attribute_mut<T>(
528 &mut self,
529 name: impl Into<interner::Symbol>,
530 ) -> Option<&mut T>
531 where
532 T: AttributeValue,
533 {
534 self.attrs.get_mut(name.into())
535 }
536
537 pub fn has_attribute(&self, name: impl Into<interner::Symbol>) -> bool {
539 self.attrs.has(name.into())
540 }
541
542 pub fn set_attribute(
544 &mut self,
545 name: impl Into<interner::Symbol>,
546 value: Option<impl AttributeValue>,
547 ) {
548 self.attrs.insert(name, value);
549 }
550
551 pub fn set_intrinsic_attribute(
553 &mut self,
554 name: impl Into<interner::Symbol>,
555 value: Option<impl AttributeValue>,
556 ) {
557 self.attrs.set(crate::Attribute {
558 name: name.into(),
559 value: value.map(|v| Box::new(v) as Box<dyn AttributeValue>),
560 intrinsic: true,
561 });
562 }
563
564 pub fn remove_attribute(&mut self, name: impl Into<interner::Symbol>) {
566 self.attrs.remove(name.into());
567 }
568}
569
570impl Operation {
572 pub fn set_symbol_attribute(
573 &mut self,
574 attr_name: impl Into<interner::Symbol>,
575 symbol: impl AsSymbolRef,
576 ) {
577 let attr_name = attr_name.into();
578 let mut symbol = symbol.as_symbol_ref();
579
580 let (data_ptr, _) = SymbolRef::as_ptr(&symbol).to_raw_parts();
587 assert!(
588 !core::ptr::addr_eq(data_ptr, self.container()),
589 "a symbol cannot use itself, except via nested operations"
590 );
591
592 let user = self.context().alloc_tracked(SymbolUse {
594 owner: self.as_operation_ref(),
595 attr: attr_name,
596 });
597
598 if self.has_attribute(attr_name) {
600 let attr = self.get_typed_attribute_mut::<SymbolPathAttr>(attr_name).unwrap();
601 let symbol = symbol.borrow();
602 assert!(
603 !attr.user.is_linked(),
604 "attempted to replace symbol use without unlinking the previously used symbol \
605 first"
606 );
607 attr.user = user;
608 attr.path = symbol.path();
609 } else {
610 let attr = {
611 let symbol = symbol.borrow();
612 SymbolPathAttr {
613 user,
614 path: symbol.path(),
615 }
616 };
617 self.set_attribute(attr_name, Some(attr));
618 }
619
620 symbol.borrow_mut().insert_use(user);
621 }
622}
623
624impl Operation {
626 #[inline]
628 pub fn parent(&self) -> Option<BlockRef> {
629 self.as_operation_ref().parent()
630 }
631
632 pub fn parent_region(&self) -> Option<RegionRef> {
634 self.as_operation_ref().parent_region()
635 }
636
637 pub fn parent_op(&self) -> Option<OperationRef> {
640 self.as_operation_ref().parent_op()
641 }
642
643 pub fn nearest_parent_op<T: Op>(&self) -> Option<UnsafeIntrusiveEntityRef<T>> {
646 let mut parent = self.parent_op();
647 while let Some(op) = parent.take() {
648 parent =
649 op.parent().and_then(|block| block.parent()).and_then(|region| region.parent());
650 let op = op.borrow();
651 if let Some(t_ref) = op.downcast_ref::<T>() {
652 return Some(unsafe { UnsafeIntrusiveEntityRef::from_raw(t_ref) });
653 }
654 }
655 None
656 }
657}
658
659impl Operation {
661 pub fn prewalk_all<F>(&self, callback: F)
662 where
663 F: FnMut(&Operation),
664 {
665 Walk::<Operation>::prewalk_all::<Forward, _>(self, callback);
666 }
667
668 pub fn prewalk<F, B>(&self, callback: F) -> WalkResult<B>
669 where
670 F: FnMut(&Operation) -> WalkResult<B>,
671 {
672 Walk::<Operation>::prewalk::<Forward, _, _>(self, callback)
673 }
674
675 pub fn postwalk_all<F>(&self, callback: F)
676 where
677 F: FnMut(&Operation),
678 {
679 Walk::<Operation>::postwalk_all::<Forward, _>(self, callback);
680 }
681
682 pub fn postwalk<F, B>(&self, callback: F) -> WalkResult<B>
683 where
684 F: FnMut(&Operation) -> WalkResult<B>,
685 {
686 Walk::<Operation>::postwalk::<Forward, _, _>(self, callback)
687 }
688}
689
690impl Operation {
692 #[inline]
694 pub fn has_regions(&self) -> bool {
695 !self.regions.is_empty()
696 }
697
698 #[inline]
703 pub fn num_regions(&self) -> usize {
704 self.regions.len()
705 }
706
707 #[inline(always)]
709 pub fn regions(&self) -> &RegionList {
710 &self.regions
711 }
712
713 #[inline(always)]
715 pub fn regions_mut(&mut self) -> &mut RegionList {
716 &mut self.regions
717 }
718
719 pub fn region(&self, index: usize) -> EntityRef<'_, Region> {
723 let mut cursor = self.regions.front();
724 let mut count = 0;
725 while !cursor.is_null() {
726 if index == count {
727 return cursor.into_borrow().unwrap();
728 }
729 cursor.move_next();
730 count += 1;
731 }
732 panic!("invalid region index {index}: out of bounds");
733 }
734
735 pub fn region_mut(&mut self, index: usize) -> EntityMut<'_, Region> {
739 let mut cursor = self.regions.front_mut();
740 let mut count = 0;
741 while !cursor.is_null() {
742 if index == count {
743 return cursor.into_borrow_mut().unwrap();
744 }
745 cursor.move_next();
746 count += 1;
747 }
748 panic!("invalid region index {index}: out of bounds");
749 }
750}
751
752impl Operation {
754 #[inline]
756 pub fn has_successors(&self) -> bool {
757 !self.successors.is_empty()
758 }
759
760 #[inline]
762 pub fn num_successors(&self) -> usize {
763 self.successors.len()
764 }
765
766 #[inline(always)]
768 pub fn successors(&self) -> &OpSuccessorStorage {
769 &self.successors
770 }
771
772 #[inline(always)]
774 pub fn successors_mut(&mut self) -> &mut OpSuccessorStorage {
775 &mut self.successors
776 }
777
778 #[inline]
780 pub fn successor_group(&self, index: usize) -> OpSuccessorRange<'_> {
781 self.successors.group(index)
782 }
783
784 #[inline]
786 pub fn successor_group_mut(&mut self, index: usize) -> OpSuccessorRangeMut<'_> {
787 self.successors.group_mut(index)
788 }
789
790 #[inline]
792 pub fn keyed_successor_group<T>(&self, index: usize) -> KeyedSuccessorRange<'_, T>
793 where
794 T: KeyedSuccessor,
795 {
796 let range = self.successors.group(index);
797 KeyedSuccessorRange::new(range, &self.operands)
798 }
799
800 #[inline]
802 pub fn keyed_successor_group_mut<T>(&mut self, index: usize) -> KeyedSuccessorRangeMut<'_, T>
803 where
804 T: KeyedSuccessor,
805 {
806 let range = self.successors.group_mut(index);
807 KeyedSuccessorRangeMut::new(range, &mut self.operands)
808 }
809
810 #[inline]
812 pub fn successor_in_group(&self, group_index: usize, index: usize) -> OpSuccessor<'_> {
813 let info = &self.successors.group(group_index)[index];
814 OpSuccessor {
815 dest: info.block,
816 arguments: self.operands.group(info.operand_group as usize),
817 }
818 }
819
820 #[inline]
822 pub fn successor_in_group_mut(
823 &mut self,
824 group_index: usize,
825 index: usize,
826 ) -> OpSuccessorMut<'_> {
827 let info = &self.successors.group(group_index)[index];
828 OpSuccessorMut {
829 dest: info.block,
830 arguments: self.operands.group_mut(info.operand_group as usize),
831 }
832 }
833
834 #[inline]
836 #[track_caller]
837 pub fn successor(&self, index: usize) -> OpSuccessor<'_> {
838 let info = &self.successors[index];
839 OpSuccessor {
840 dest: info.block,
841 arguments: self.operands.group(info.operand_group as usize),
842 }
843 }
844
845 #[inline]
847 #[track_caller]
848 pub fn successor_mut(&mut self, index: usize) -> OpSuccessorMut<'_> {
849 let info = self.successors[index];
850 OpSuccessorMut {
851 dest: info.block,
852 arguments: self.operands.group_mut(info.operand_group as usize),
853 }
854 }
855
856 pub fn successor_iter(&self) -> impl DoubleEndedIterator<Item = OpSuccessor<'_>> + '_ {
858 self.successors.iter().map(|info| OpSuccessor {
859 dest: info.block,
860 arguments: self.operands.group(info.operand_group as usize),
861 })
862 }
863}
864
865impl Operation {
867 #[inline]
869 pub fn has_operands(&self) -> bool {
870 !self.operands.is_empty()
871 }
872
873 #[inline]
875 pub fn num_operands(&self) -> usize {
876 self.operands.len()
877 }
878
879 #[inline]
881 pub fn operands(&self) -> &OpOperandStorage {
882 &self.operands
883 }
884
885 #[inline]
887 pub fn operands_mut(&mut self) -> &mut OpOperandStorage {
888 &mut self.operands
889 }
890
891 pub fn set_operands(&mut self, operands: impl IntoIterator<Item = ValueRef>) {
893 self.operands.clear();
894 let context = self.context_rc();
895 let owner = self.as_operation_ref();
896 self.operands.extend(
897 operands
898 .into_iter()
899 .enumerate()
900 .map(|(index, value)| context.make_operand(value, owner, index as u8)),
901 );
902 }
903
904 pub fn replaces_uses_of_with(&mut self, from: ValueRef, to: ValueRef) {
906 if ValueRef::ptr_eq(&from, &to) {
907 return;
908 }
909
910 for operand in self.operands.iter_mut() {
911 debug_assert!(operand.is_linked());
912 if ValueRef::ptr_eq(&from, &operand.borrow().value.unwrap()) {
913 operand.borrow_mut().set(to);
914 }
915 }
916 }
917
918 pub fn replace_all_uses_with(&mut self, values: impl ExactSizeIterator<Item = ValueRef>) {
923 assert_eq!(self.num_results(), values.len());
924 for (result, replacement) in self.results.iter_mut().zip(values) {
925 if (*result as ValueRef) == replacement {
926 continue;
927 }
928 result.borrow_mut().replace_all_uses_with(replacement);
929 }
930 }
931
932 pub fn replace_uses_with_if<F, V>(&mut self, values: V, should_replace: F)
938 where
939 V: ExactSizeIterator<Item = ValueRef>,
940 F: Fn(&OpOperandImpl) -> bool,
941 {
942 assert_eq!(self.num_results(), values.len());
943 for (result, replacement) in self.results.iter_mut().zip(values) {
944 let mut result = *result as ValueRef;
945 if result == replacement {
946 continue;
947 }
948 result.borrow_mut().replace_uses_with_if(replacement, &should_replace);
949 }
950 }
951}
952
953impl Operation {
955 #[inline]
957 pub fn has_results(&self) -> bool {
958 !self.results.is_empty()
959 }
960
961 #[inline]
963 pub fn num_results(&self) -> usize {
964 self.results.len()
965 }
966
967 #[inline]
969 pub fn results(&self) -> &OpResultStorage {
970 &self.results
971 }
972
973 #[inline]
975 pub fn results_mut(&mut self) -> &mut OpResultStorage {
976 &mut self.results
977 }
978
979 #[inline]
981 pub fn get_result(&self, index: usize) -> &OpResultRef {
982 &self.results[index]
983 }
984
985 pub fn is_used(&self) -> bool {
987 self.results.iter().any(|result| result.borrow().is_used())
988 }
989
990 pub fn has_exactly_one_use(&self) -> bool {
992 let mut used_by = None;
993 for result in self.results.iter() {
994 let result = result.borrow();
995 if !result.is_used() {
996 continue;
997 }
998
999 for used in result.iter_uses() {
1000 if used_by.as_ref().is_some_and(|user| !OperationRef::eq(user, &used.owner)) {
1001 return false;
1003 } else if used_by.is_none() {
1004 used_by = Some(used.owner);
1005 }
1006 }
1007 }
1008
1009 used_by.is_some()
1011 }
1012
1013 pub fn is_used_outside_of_block(&self, block: &BlockRef) -> bool {
1015 self.results
1016 .iter()
1017 .any(|result| result.borrow().is_used_outside_of_block(block))
1018 }
1019
1020 pub fn is_trivially_dead(&self) -> bool {
1022 !self.is_used() && self.would_be_trivially_dead()
1023 }
1024
1025 pub fn would_be_trivially_dead(&self) -> bool {
1030 if self.implements::<dyn crate::traits::Terminator>() || self.implements::<dyn Symbol>() {
1031 false
1032 } else {
1033 self.would_be_trivially_dead_even_if_terminator()
1034 }
1035 }
1036
1037 pub fn would_be_trivially_dead_even_if_terminator(&self) -> bool {
1041 let mut effecting_ops = SmallVec::<[OperationRef; 4]>::from_iter([self.as_operation_ref()]);
1043 while let Some(op) = effecting_ops.pop() {
1044 let op = op.borrow();
1045
1046 let has_recursive_effects = op.implements::<dyn HasRecursiveMemoryEffects>();
1049 if has_recursive_effects {
1050 for region in op.regions() {
1051 for block in region.body() {
1052 for op in block.body() {
1053 effecting_ops.push(op.as_operation_ref());
1054 }
1055 }
1056 }
1057 }
1058
1059 if let Some(effect_interface) = op.as_trait::<dyn MemoryEffectOpInterface>() {
1062 let mut effects = effect_interface.effects();
1063
1064 let mut alloc_results = SmallSet::<ValueRef, 4>::default();
1066 for effect in effects.as_slice() {
1067 let allocates = matches!(effect.effect(), MemoryEffect::Allocate);
1068 if let Some(value) = effect.value() {
1069 let is_defined_by_op = value
1070 .borrow()
1071 .get_defining_op()
1072 .is_some_and(|op| self.as_operation_ref() == op);
1073 if allocates && is_defined_by_op {
1074 alloc_results.insert(value);
1075 }
1076 }
1077 }
1078
1079 if !effects.all(|effect| {
1080 if effect.value().is_some_and(|v| alloc_results.contains(&v)) {
1083 true
1084 } else {
1085 matches!(effect.effect(), MemoryEffect::Read)
1087 }
1088 }) {
1089 return false;
1090 }
1091 continue;
1092 }
1093
1094 if has_recursive_effects {
1097 continue;
1098 }
1099
1100 return false;
1103 }
1104
1105 true
1108 }
1109
1110 pub fn is_memory_effect_free(&self) -> bool {
1120 if let Some(mem_interface) = self.as_trait::<dyn MemoryEffectOpInterface>() {
1121 if !mem_interface.has_no_effect() {
1122 return false;
1123 }
1124
1125 if !self.implements::<dyn HasRecursiveMemoryEffects>() {
1127 return true;
1128 }
1129 } else if !self.implements::<dyn HasRecursiveMemoryEffects>() {
1130 return false;
1133 }
1134
1135 for region in self.regions() {
1137 let walk_result = region.prewalk(|op| {
1138 if !op.is_memory_effect_free() {
1139 WalkResult::Break(())
1140 } else {
1141 WalkResult::Continue(())
1142 }
1143 });
1144 if walk_result.was_interrupted() {
1145 return false;
1146 }
1147 }
1148
1149 true
1150 }
1151}
1152
1153impl Operation {
1155 #[inline]
1157 pub fn erase(&mut self) {
1158 self.remove();
1160
1161 self.successors.clear();
1162 self.operands.clear();
1163 }
1164
1165 pub fn remove(&mut self) {
1167 if let Some(mut parent) = self.parent() {
1168 let mut block = parent.borrow_mut();
1169 let body = block.body_mut();
1170 let mut cursor = unsafe { body.cursor_mut_from_ptr(self.as_operation_ref()) };
1171 cursor.remove();
1172 }
1173 }
1174
1175 pub fn move_to(&mut self, mut ip: ProgramPoint) {
1183 let this = self.as_operation_ref();
1184 if let Some(op) = ip.operation() {
1185 if op == this {
1186 return;
1188 }
1189
1190 assert!(ip.block().is_some(), "cannot insert an operation relative to an orphaned op");
1191 }
1192
1193 self.remove();
1195
1196 {
1197 let mut cursor = ip.cursor_mut().expect("insertion point is invalid/unset");
1199 cursor.insert_after(self.as_operation_ref());
1202 }
1203 }
1204
1205 pub fn drop_all_references(&mut self) {
1208 self.operands.clear();
1209
1210 {
1211 let mut region_cursor = self.regions.front_mut();
1212 while let Some(mut region) = region_cursor.as_pointer() {
1213 region.borrow_mut().drop_all_references();
1214 region_cursor.move_next();
1215 }
1216 }
1217
1218 self.successors.clear();
1219 }
1220
1221 pub fn drop_all_defined_value_uses(&mut self) {
1224 for result in self.results.iter_mut() {
1225 let mut res = result.borrow_mut();
1226 res.uses_mut().clear();
1227 }
1228
1229 let mut regions = self.regions.front_mut();
1230 while let Some(mut region) = regions.as_pointer() {
1231 let mut region = region.borrow_mut();
1232 let blocks = region.body_mut();
1233 let mut cursor = blocks.front_mut();
1234 while let Some(mut block) = cursor.as_pointer() {
1235 block.borrow_mut().drop_all_defined_value_uses();
1236 cursor.move_next();
1237 }
1238 regions.move_next();
1239 }
1240 }
1241
1242 pub fn drop_all_uses(&mut self) {
1244 for result in self.results.iter_mut() {
1245 result.borrow_mut().uses_mut().clear();
1246 }
1247 }
1248}
1249
1250impl Operation {
1252 const INVALID_ORDER: u32 = u32::MAX;
1254 const ORDER_STRIDE: u32 = 5;
1256
1257 pub fn is_ancestor_of(&self, other: &Self) -> bool {
1262 core::ptr::addr_eq(self, other) || Self::is_a_proper_ancestor_of_b(self, other)
1263 }
1264
1265 pub fn is_proper_ancestor_of(&self, other: &Self) -> bool {
1267 Self::is_a_proper_ancestor_of_b(self, other)
1268 }
1269
1270 fn is_a_proper_ancestor_of_b(a: &Self, b: &Self) -> bool {
1272 let a = a.as_operation_ref();
1273 let mut next = b.parent_op();
1274 while let Some(b) = next.take() {
1275 if OperationRef::ptr_eq(&a, &b) {
1276 return true;
1277 }
1278 }
1279 false
1280 }
1281
1282 pub fn is_before_in_block(&self, other: &OperationRef) -> bool {
1288 use core::sync::atomic::Ordering;
1289
1290 let block = self.parent().expect("operations without parent blocks have no order");
1291 let other = other.borrow();
1292 assert!(
1293 other
1294 .parent()
1295 .as_ref()
1296 .is_some_and(|other_block| BlockRef::ptr_eq(&block, other_block)),
1297 "expected both operations to have the same parent block"
1298 );
1299
1300 if !block.borrow().is_op_order_valid() {
1302 Self::recompute_block_order(block);
1303 } else {
1304 self.update_order_if_necessary();
1306 other.update_order_if_necessary();
1307 }
1308
1309 self.order.load(Ordering::Relaxed) < other.order.load(Ordering::Relaxed)
1310 }
1311
1312 fn update_order_if_necessary(&self) {
1315 use core::sync::atomic::Ordering;
1316
1317 assert!(self.parent().is_some(), "expected valid parent");
1318
1319 let block = self.parent().unwrap();
1321 if self.has_valid_order() || block.borrow().body().iter().count() == 1 {
1322 return;
1323 }
1324
1325 let this = self.as_operation_ref();
1326 let prev = this.prev();
1327 let next = this.next();
1328 assert!(prev.is_some() || next.is_some(), "expected more than one operation in block");
1329
1330 if next.is_none() {
1332 let prev = prev.unwrap();
1333 let prev = prev.borrow();
1334 let prev_order = prev.order.load(Ordering::Acquire);
1335 if prev_order == Self::INVALID_ORDER {
1336 return Self::recompute_block_order(block);
1337 }
1338
1339 self.order.store(prev_order + Self::ORDER_STRIDE, Ordering::Release);
1341 return;
1342 }
1343
1344 if prev.is_none() {
1347 let next = next.unwrap();
1348 let next = next.borrow();
1349 let next_order = next.order.load(Ordering::Acquire);
1350 match next_order {
1351 Self::INVALID_ORDER | 0 => {
1352 return Self::recompute_block_order(block);
1353 }
1354 order if order <= Self::ORDER_STRIDE => {
1357 self.order.store(order / 2, Ordering::Release);
1358 }
1359 _ => {
1360 self.order.store(Self::ORDER_STRIDE, Ordering::Release);
1361 }
1362 }
1363 return;
1364 }
1365
1366 let prev = prev.unwrap().borrow().order.load(Ordering::Acquire);
1369 let next = next.unwrap().borrow().order.load(Ordering::Acquire);
1370 if prev == Self::INVALID_ORDER || next == Self::INVALID_ORDER {
1371 return Self::recompute_block_order(block);
1372 }
1373
1374 if prev + 1 == next {
1376 return Self::recompute_block_order(block);
1377 }
1378 self.order.store(prev + ((next - prev) / 2), Ordering::Release);
1379 }
1380
1381 fn recompute_block_order(block: BlockRef) {
1382 use core::sync::atomic::Ordering;
1383
1384 let block = block.borrow();
1385 let mut cursor = block.body().front();
1386 let mut index = 0;
1387 while let Some(op) = cursor.as_pointer() {
1388 index += Self::ORDER_STRIDE;
1389 cursor.move_next();
1390 let ptr = OperationRef::as_ptr(&op);
1391 unsafe {
1392 let order_addr = core::ptr::addr_of!((*ptr).order);
1393 (*order_addr).store(index, Ordering::Release);
1394 }
1395 }
1396
1397 block.mark_op_order_valid();
1398 }
1399
1400 #[inline]
1402 pub(crate) fn order(&self) -> Option<u32> {
1403 use core::sync::atomic::Ordering;
1404 match self.order.load(Ordering::Acquire) {
1405 Self::INVALID_ORDER => None,
1406 order => Some(order),
1407 }
1408 }
1409
1410 #[inline]
1412 #[allow(unused)]
1413 pub(crate) fn get_or_compute_order(&self) -> u32 {
1414 use core::sync::atomic::Ordering;
1415
1416 if let Some(order) = self.order() {
1417 return order;
1418 }
1419
1420 Self::recompute_block_order(
1421 self.parent().expect("cannot compute block ordering for orphaned operation"),
1422 );
1423
1424 self.order.load(Ordering::Acquire)
1425 }
1426
1427 #[inline(always)]
1429 pub(super) fn has_valid_order(&self) -> bool {
1430 self.order().is_some()
1431 }
1432}
1433
1434impl Operation {
1436 #[inline]
1438 pub fn populate_canonicalization_patterns(
1439 &self,
1440 rewrites: &mut RewritePatternSet,
1441 context: Rc<Context>,
1442 ) {
1443 self.name.populate_canonicalization_patterns(rewrites, context);
1444 }
1445}
1446
1447impl crate::traits::Foldable for Operation {
1448 fn fold(&self, results: &mut smallvec::SmallVec<[OpFoldResult; 1]>) -> FoldResult {
1449 use crate::traits::Foldable;
1450
1451 if let Some(foldable) = self.as_trait::<dyn Foldable>() {
1452 foldable.fold(results)
1453 } else {
1454 FoldResult::Failed
1455 }
1456 }
1457
1458 fn fold_with<'operands>(
1459 &self,
1460 operands: &[Option<Box<dyn AttributeValue>>],
1461 results: &mut smallvec::SmallVec<[OpFoldResult; 1]>,
1462 ) -> FoldResult {
1463 use crate::traits::Foldable;
1464
1465 if let Some(foldable) = self.as_trait::<dyn Foldable>() {
1466 foldable.fold_with(operands, results)
1467 } else {
1468 FoldResult::Failed
1469 }
1470 }
1471}