midenc_hir/ir/
operation.rs

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/// The [Operation] struct provides the common foundation for all [Op] implementations.
29///
30/// It provides:
31///
32/// * Support for casting between the concrete operation type `T`, `dyn Op`, the underlying
33///   `Operation`, and any of the operation traits that the op implements. Not only can the casts
34///   be performed, but an [Operation] can be queried to see if it implements a specific trait at
35///   runtime to conditionally perform some behavior. This makes working with operations in the IR
36///   very flexible and allows for adding or modifying operations without needing to change most of
37///   the compiler, which predominately works on operation traits rather than concrete ops.
38/// * Storage for all IR entities attached to an operation, e.g. operands, results, nested regions,
39///   attributes, etc.
40/// * Navigation of the IR graph; navigate up to the containing block/region/op, down to nested
41///   regions/blocks/ops, or next/previous sibling operations in the same block. Additionally, you
42///   can navigate directly to the definitions of operands used, to users of results produced, and
43///   to successor blocks.
44/// * Many utility functions related to working with operations, many of which are also accessible
45///   via the [Op] trait, so that working with an [Op] or an [Operation] are largely
46///   indistinguishable.
47///
48/// All [Op] implementations can be cast to the underlying [Operation], but most of the
49/// fucntionality is re-exported via default implementations of methods on the [Op] trait. The main
50/// benefit is avoiding any potential overhead of casting when going through the trait, rather than
51/// calling the underlying [Operation] method directly.
52///
53/// # Safety
54///
55/// [Operation] is implemented as part of a larger structure that relies on assumptions which depend
56/// on IR entities being allocated via [Context], i.e. the arena. Those allocations produce an
57/// [UnsafeIntrusiveEntityRef] or [UnsafeEntityRef], which allocate the pointee type inside a struct
58/// that provides metadata about the pointee that can be accessed without aliasing the pointee
59/// itself - in particular, links for intrusive collections. This is important, because while these
60/// pointer types are a bit like raw pointers in that they lack any lifetime information, and are
61/// thus unsafe to dereference in general, they _do_ ensure that the pointee can be safely reified
62/// as a reference without violating Rust's borrow checking rules, i.e. they are dynamically borrow-
63/// checked.
64///
65/// The reason why we are able to generally treat these "unsafe" references as safe, is because we
66/// require that all IR entities be allocated via [Context]. This makes it essential to keep the
67/// context around in order to work with the IR, and effectively guarantees that no [RawEntityRef]
68/// will be dereferenced after the context is dropped. This is not a guarantee provided by the
69/// compiler however, but one that is imposed in practice, as attempting to work with the IR in
70/// any capacity without a [Context] is almost impossible. We must ensure however, that we work
71/// within this set of rules to uphold the safety guarantees.
72///
73/// This "fragility" is a tradeoff - we get the performance characteristics of an arena-allocated
74/// IR, with the flexibility and power of using pointers rather than indexes as handles, while also
75/// maintaining the safety guarantees of Rust's borrowing system. The downside is that we can't just
76/// allocate IR entities wherever we want and use them the same way.
77#[derive(Spanned)]
78pub struct Operation {
79    /// The [Context] in which this [Operation] was allocated.
80    context: NonNull<Context>,
81    /// The dialect and opcode name for this operation, as well as trait implementation metadata
82    name: OperationName,
83    /// The offset of the field containing this struct inside the concrete [Op] it represents.
84    ///
85    /// This is required in order to be able to perform casts from [Operation]. An [Operation]
86    /// cannot be constructed without providing it to the `uninit` function, and callers of that
87    /// function are required to ensure that it is correct.
88    offset: usize,
89    /// The order of this operation in its containing block
90    ///
91    /// This is atomic to ensure that even if a mutable reference to this operation is held, loads
92    /// of this field cannot be elided, as the value can still be mutated at any time. In practice,
93    /// the only time this is ever written, is when all operations in a block have their orders
94    /// recomputed, or when a single operation is updating its own order.
95    order: AtomicU32,
96    #[span]
97    pub span: SourceSpan,
98    /// Attributes that apply to this operation
99    pub attrs: AttributeSet,
100    /// The set of operands for this operation
101    ///
102    /// NOTE: If the op supports immediate operands, the storage for the immediates is handled
103    /// by the op, rather than here. Additionally, the semantics of the immediate operands are
104    /// determined by the op, e.g. whether the immediate operands are always applied first, or
105    /// what they are used for.
106    pub operands: OpOperandStorage,
107    /// The set of values produced by this operation.
108    pub results: OpResultStorage,
109    /// If this operation represents control flow, this field stores the set of successors,
110    /// and successor operands.
111    pub successors: OpSuccessorStorage,
112    /// The set of regions belonging to this operation, if any
113    pub regions: RegionList,
114}
115
116/// Equality over operations is determined by reference identity, i.e. two operations are only equal
117/// if they refer to the same address in memory, regardless of the content of the operation itself.
118impl Eq for Operation {}
119impl PartialEq for Operation {
120    fn eq(&self, other: &Self) -> bool {
121        core::ptr::addr_eq(self, other)
122    }
123}
124
125/// The Hash implementation for operations is defined to match the equality implementation, i.e.
126/// the hash of an operation is the hash of its address in memory.
127impl 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        // NOTE: We use OperationName, instead of the Operation itself, to avoid borrowing.
185        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                    // NOTE: We call `unwrap()` here because we are confident that these function calls
190                    // are valid thanks to the `implements` check above.
191                    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        // Invalidate the ordering of the new parent block
220        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        // NOTE: We use OperationName, instead of the Operation itself, to avoid borrowing.
226        if this.name().implements::<dyn Symbol>() {
227            let parent = this.nearest_symbol_table();
228            if let Some(mut parent) = parent {
229                // NOTE: We use OperationName, instead of the Operation itself, to avoid borrowing.
230                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
250/// Construction
251impl 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
271/// Insertion
272impl 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
324/// Read-only Metadata
325impl OperationRef {
326    pub fn name(&self) -> OperationName {
327        let ptr = OperationRef::as_ptr(self);
328        // SAFETY: The `name` field of Operation is read-only after an op is allocated, and the
329        // safety guarantees of OperationRef require that the allocation never moves for the
330        // lifetime of the ref. So it is always safe to read this field via direct pointer, even
331        // if a mutable borrow of the containing op exists, because the field is never written to
332        // after allocation.
333        unsafe {
334            let name_ptr = core::ptr::addr_of!((*ptr).name);
335            OperationName::clone(&*name_ptr)
336        }
337    }
338
339    /// Returns a handle to the nearest containing [Operation] of this operation, if it is attached
340    /// to one
341    pub fn parent_op(&self) -> Option<OperationRef> {
342        self.parent_region().and_then(|region| region.parent())
343    }
344
345    /// Returns a handle to the containing [Region] of this operation, if it is attached to one
346    pub fn parent_region(&self) -> Option<RegionRef> {
347        self.grandparent()
348    }
349
350    /// Returns the nearest [SymbolTable] from this operation.
351    ///
352    /// Returns `None` if no parent of this operation is a valid symbol table.
353    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
365/// Metadata
366impl Operation {
367    /// Get the name of this operation
368    ///
369    /// An operation name consists of both its dialect, and its opcode.
370    pub fn name(&self) -> OperationName {
371        self.name.clone()
372    }
373
374    /// Get the dialect associated with this operation
375    pub fn dialect(&self) -> Rc<dyn Dialect> {
376        self.context().get_registered_dialect(self.name.dialect())
377    }
378
379    /// Set the source location associated with this operation
380    #[inline]
381    pub fn set_span(&mut self, span: SourceSpan) {
382        self.span = span;
383    }
384
385    /// Get a borrowed reference to the owning [Context] of this operation
386    #[inline(always)]
387    pub fn context(&self) -> &Context {
388        // SAFETY: This is safe so long as this operation is allocated in a Context, since the
389        // Context by definition outlives the allocation.
390        unsafe { self.context.as_ref() }
391    }
392
393    /// Get a owned reference to the owning [Context] of this operation
394    pub fn context_rc(&self) -> Rc<Context> {
395        // SAFETY: This is safe so long as this operation is allocated in a Context, since the
396        // Context by definition outlives the allocation.
397        //
398        // Additionally, constructing the Rc from a raw pointer is safe here, as the pointer was
399        // obtained using `Rc::as_ptr`, so the only requirement to call `Rc::from_raw` is to
400        // increment the strong count, as `as_ptr` does not preserve the count for the reference
401        // held by this operation. Incrementing the count first is required to manufacture new
402        // clones of the `Rc` safely.
403        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
411/// Verification
412impl Operation {
413    /// Run any verifiers for this operation
414    pub fn verify(&self) -> Result<(), Report> {
415        let dyn_op: &dyn Op = self.as_ref();
416        dyn_op.verify(self.context())
417    }
418
419    /// Run any verifiers for this operation, and all of its nested operations, recursively.
420    ///
421    /// The verification is performed in post-order, so that when the verifier(s) for `self` are
422    /// run, it is known that all of its children have successfully verified.
423    pub fn recursively_verify(&self) -> Result<(), Report> {
424        self.postwalk(|op: &Operation| op.verify().into()).into_result()
425    }
426}
427
428/// Traits/Casts
429impl 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        // SAFETY: This is safe under the assumption that we always allocate Operations using the
440        // arena, i.e. it is a child of a RawEntityMetadata structure.
441        //
442        // Additionally, this relies on the fact that Op implementations are #[repr(C)] and ensure
443        // that their Operation field is always first in the generated struct
444        unsafe { OperationRef::from_raw(self) }
445    }
446
447    /// Returns true if the concrete type of this operation is `T`
448    #[inline]
449    pub fn is<T: 'static>(&self) -> bool {
450        self.name.is::<T>()
451    }
452
453    /// Returns true if this operation implements `Trait`
454    #[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    /// Attempt to downcast to the concrete [Op] type of this operation
463    pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
464        self.name.downcast_ref::<T>(self.container())
465    }
466
467    /// Attempt to downcast to the concrete [Op] type of this operation
468    pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
469        self.name.downcast_mut::<T>(self.container().cast_mut())
470    }
471
472    /// Attempt to cast this operation reference to an implementation of `Trait`
473    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    /// Attempt to cast this operation reference to an implementation of `Trait`
481    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
489/// Attributes
490impl Operation {
491    /// Get the underlying attribute set for this operation
492    #[inline(always)]
493    pub fn attributes(&self) -> &AttributeSet {
494        &self.attrs
495    }
496
497    /// Get a mutable reference to the underlying attribute set for this operation
498    #[inline(always)]
499    pub fn attributes_mut(&mut self) -> &mut AttributeSet {
500        &mut self.attrs
501    }
502
503    /// Return the value associated with attribute `name` for this function
504    pub fn get_attribute(&self, name: impl Into<interner::Symbol>) -> Option<&dyn AttributeValue> {
505        self.attrs.get_any(name.into())
506    }
507
508    /// Return the value associated with attribute `name` for this function
509    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    /// Return the value associated with attribute `name` for this function, as its concrete type
517    /// `T`, _if_ the attribute by that name, is of that type.
518    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    /// Return the value associated with attribute `name` for this function, as its concrete type
526    /// `T`, _if_ the attribute by that name, is of that type.
527    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    /// Return true if this function has an attributed named `name`
538    pub fn has_attribute(&self, name: impl Into<interner::Symbol>) -> bool {
539        self.attrs.has(name.into())
540    }
541
542    /// Set the attribute `name` with `value` for this function.
543    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    /// Set the intrinsic attribute `name` with `value` for this function.
552    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    /// Remove any attribute with the given name from this function
565    pub fn remove_attribute(&mut self, name: impl Into<interner::Symbol>) {
566        self.attrs.remove(name.into());
567    }
568}
569
570/// Symbol Attributes
571impl 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        // Do not allow self-references
581        //
582        // NOTE: We are using this somewhat convoluted way to check identity of the symbol,
583        // so that we do not attempt to borrow `self` again if `symbol` and `self` are the
584        // same operation. That would fail due to the mutable reference to `self` we are
585        // already holding.
586        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        // Track the usage of `symbol` by `self`
593        let user = self.context().alloc_tracked(SymbolUse {
594            owner: self.as_operation_ref(),
595            attr: attr_name,
596        });
597
598        // Store the underlying attribute value
599        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
624/// Navigation
625impl Operation {
626    /// Returns a handle to the containing [Block] of this operation, if it is attached to one
627    #[inline]
628    pub fn parent(&self) -> Option<BlockRef> {
629        self.as_operation_ref().parent()
630    }
631
632    /// Returns a handle to the containing [Region] of this operation, if it is attached to one
633    pub fn parent_region(&self) -> Option<RegionRef> {
634        self.as_operation_ref().parent_region()
635    }
636
637    /// Returns a handle to the nearest containing [Operation] of this operation, if it is attached
638    /// to one
639    pub fn parent_op(&self) -> Option<OperationRef> {
640        self.as_operation_ref().parent_op()
641    }
642
643    /// Returns a handle to the nearest containing [Operation] of type `T` for this operation, if it
644    /// is attached to one
645    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
659/// Traversal
660impl 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
690/// Regions
691impl Operation {
692    /// Returns true if this operation has any regions
693    #[inline]
694    pub fn has_regions(&self) -> bool {
695        !self.regions.is_empty()
696    }
697
698    /// Returns the number of regions owned by this operation.
699    ///
700    /// NOTE: This does not include regions of nested operations, just those directly attached
701    /// to this operation.
702    #[inline]
703    pub fn num_regions(&self) -> usize {
704        self.regions.len()
705    }
706
707    /// Get a reference to the region list for this operation
708    #[inline(always)]
709    pub fn regions(&self) -> &RegionList {
710        &self.regions
711    }
712
713    /// Get a mutable reference to the region list for this operation
714    #[inline(always)]
715    pub fn regions_mut(&mut self) -> &mut RegionList {
716        &mut self.regions
717    }
718
719    /// Get a reference to a specific region, given its index.
720    ///
721    /// This function will panic if the index is invalid.
722    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    /// Get a mutable reference to a specific region, given its index.
736    ///
737    /// This function will panic if the index is invalid.
738    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
752/// Successors
753impl Operation {
754    /// Returns true if this operation has any successor blocks
755    #[inline]
756    pub fn has_successors(&self) -> bool {
757        !self.successors.is_empty()
758    }
759
760    /// Returns the number of successor blocks this operation may transfer control to
761    #[inline]
762    pub fn num_successors(&self) -> usize {
763        self.successors.len()
764    }
765
766    /// Get a reference to the successors of this operation
767    #[inline(always)]
768    pub fn successors(&self) -> &OpSuccessorStorage {
769        &self.successors
770    }
771
772    /// Get a mutable reference to the successors of this operation
773    #[inline(always)]
774    pub fn successors_mut(&mut self) -> &mut OpSuccessorStorage {
775        &mut self.successors
776    }
777
778    /// Get a reference to the successor group at `index`
779    #[inline]
780    pub fn successor_group(&self, index: usize) -> OpSuccessorRange<'_> {
781        self.successors.group(index)
782    }
783
784    /// Get a mutable reference to the successor group at `index`
785    #[inline]
786    pub fn successor_group_mut(&mut self, index: usize) -> OpSuccessorRangeMut<'_> {
787        self.successors.group_mut(index)
788    }
789
790    /// Get a reference to the keyed successor group at `index`
791    #[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    /// Get a mutable reference to the keyed successor group at `index`
801    #[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    /// Get a reference to the successor at `index` in the group at `group_index`
811    #[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    /// Get a mutable reference to the successor at `index` in the group at `group_index`
821    #[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    /// Get a reference to the successor at `index`
835    #[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    /// Get a mutable reference to the successor at `index`
846    #[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    /// Get an iterator over the successors of this operation
857    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
865/// Operands
866impl Operation {
867    /// Returns true if this operation has at least one operand
868    #[inline]
869    pub fn has_operands(&self) -> bool {
870        !self.operands.is_empty()
871    }
872
873    /// Returns the number of operands given to this operation
874    #[inline]
875    pub fn num_operands(&self) -> usize {
876        self.operands.len()
877    }
878
879    /// Get a reference to the operand storage for this operation
880    #[inline]
881    pub fn operands(&self) -> &OpOperandStorage {
882        &self.operands
883    }
884
885    /// Get a mutable reference to the operand storage for this operation
886    #[inline]
887    pub fn operands_mut(&mut self) -> &mut OpOperandStorage {
888        &mut self.operands
889    }
890
891    /// Replace the current operands of this operation with the ones provided in `operands`.
892    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    /// Replace any uses of `from` with `to` within this operation
905    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    /// Replace all uses of this operation's results with `values`
919    ///
920    /// The number of results and the number of values in `values` must be exactly the same,
921    /// otherwise this function will panic.
922    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    /// Replace uses of this operation's results with `values`, for each use which, when provided
933    /// to the given callback, returns true.
934    ///
935    /// The number of results and the number of values in `values` must be exactly the same,
936    /// otherwise this function will panic.
937    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
953/// Results
954impl Operation {
955    /// Returns true if this operation produces any results
956    #[inline]
957    pub fn has_results(&self) -> bool {
958        !self.results.is_empty()
959    }
960
961    /// Returns the number of results produced by this operation
962    #[inline]
963    pub fn num_results(&self) -> usize {
964        self.results.len()
965    }
966
967    /// Get a reference to the result set of this operation
968    #[inline]
969    pub fn results(&self) -> &OpResultStorage {
970        &self.results
971    }
972
973    /// Get a mutable reference to the result set of this operation
974    #[inline]
975    pub fn results_mut(&mut self) -> &mut OpResultStorage {
976        &mut self.results
977    }
978
979    /// Get a reference to the result at `index` among all results of this operation
980    #[inline]
981    pub fn get_result(&self, index: usize) -> &OpResultRef {
982        &self.results[index]
983    }
984
985    /// Returns true if the results of this operation are used
986    pub fn is_used(&self) -> bool {
987        self.results.iter().any(|result| result.borrow().is_used())
988    }
989
990    /// Returns true if the results of this operation have exactly one user
991    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                    // We found more than one user
1002                    return false;
1003                } else if used_by.is_none() {
1004                    used_by = Some(used.owner);
1005                }
1006            }
1007        }
1008
1009        // If we reach here, and we have a `used_by` set, we have exactly one user
1010        used_by.is_some()
1011    }
1012
1013    /// Returns true if the results of this operation are used outside of the given block
1014    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    /// Returns true if this operation is unused and has no side effects that prevent it being erased
1021    pub fn is_trivially_dead(&self) -> bool {
1022        !self.is_used() && self.would_be_trivially_dead()
1023    }
1024
1025    /// Returns true if this operation would be dead if unused, and has no side effects that would
1026    /// prevent erasing it. This is equivalent to checking `is_trivially_dead` if `self` is unused.
1027    ///
1028    /// NOTE: Terminators and symbols are never considered to be trivially dead by this function.
1029    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    /// Implementation of `would_be_trivially_dead` that also considers terminator operations as
1038    /// dead if they have no side effects. This allows for marking region operations as trivially
1039    /// dead without always being conservative about terminators.
1040    pub fn would_be_trivially_dead_even_if_terminator(&self) -> bool {
1041        // The set of operations to consider when checking for side effects
1042        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            // If the operation has recursive effects, push all of the nested operations on to the
1047            // stack to consider
1048            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 the op has memory effects, try to characterize them to see if the op is trivially
1060            // dead here.
1061            if let Some(effect_interface) = op.as_trait::<dyn MemoryEffectOpInterface>() {
1062                let mut effects = effect_interface.effects();
1063
1064                // Gather all results of this op that are allocated
1065                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                    // We can drop effects if the value is an allocation and is a result of
1081                    // the operation
1082                    if effect.value().is_some_and(|v| alloc_results.contains(&v)) {
1083                        true
1084                    } else {
1085                        // Otherwise, the effect must be a read
1086                        matches!(effect.effect(), MemoryEffect::Read)
1087                    }
1088                }) {
1089                    return false;
1090                }
1091                continue;
1092            }
1093
1094            // Otherwise, if the op has recursive side effects we can treat the operation itself
1095            // as having no effects
1096            if has_recursive_effects {
1097                continue;
1098            }
1099
1100            // If there were no effect interfaces, we treat this op as conservatively having
1101            // effects
1102            return false;
1103        }
1104
1105        // If we get here, none of the operations had effects that prevented marking this operation
1106        // as dead.
1107        true
1108    }
1109
1110    /// Returns true if the given operation is free of memory effects.
1111    ///
1112    /// An operation is free of memory effects if its implementation of `MemoryEffectOpInterface`
1113    /// indicates that it has no memory effects. For example, it may implement `NoMemoryEffect`.
1114    /// Alternatively, if the operation has the `HasRecursiveMemoryEffects` trait, then it is free
1115    /// of memory effects if all of its nested operations are free of memory effects.
1116    ///
1117    /// If the operation has both, then it is free of memory effects if both conditions are
1118    /// satisfied.
1119    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 the op does not have recursive side effects, then it is memory effect free
1126            if !self.implements::<dyn HasRecursiveMemoryEffects>() {
1127                return true;
1128            }
1129        } else if !self.implements::<dyn HasRecursiveMemoryEffects>() {
1130            // Otherwise, if the op does not implement the memory effect interface and it does not
1131            // have recursive side effects, then it cannot be known that the op is moveable.
1132            return false;
1133        }
1134
1135        // Recurse into the regions and ensure that all nested ops are memory effect free
1136        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
1153/// Movement
1154impl Operation {
1155    /// Remove this operation (and its descendants) from its containing block, and delete them
1156    #[inline]
1157    pub fn erase(&mut self) {
1158        // We don't delete entities currently, so for now this is just an alias for `remove`
1159        self.remove();
1160
1161        self.successors.clear();
1162        self.operands.clear();
1163    }
1164
1165    /// Remove the operation from its parent block, but don't delete it.
1166    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    /// Unlink this operation from its current block and insert it at `ip`, which may be in the same
1176    /// or another block in the same function.
1177    ///
1178    /// # Panics
1179    ///
1180    /// This function will panic if the given program point is unset, or refers to an orphaned op,
1181    /// i.e. an op that has no parent block.
1182    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                // The move is a no-op
1187                return;
1188            }
1189
1190            assert!(ip.block().is_some(), "cannot insert an operation relative to an orphaned op");
1191        }
1192
1193        // Detach `self`
1194        self.remove();
1195
1196        {
1197            // Move `self` to `ip`
1198            let mut cursor = ip.cursor_mut().expect("insertion point is invalid/unset");
1199            // NOTE: We use `insert_after` here because the cursor we get is positioned such that
1200            // insert_after will always insert at the precise point specified.
1201            cursor.insert_after(self.as_operation_ref());
1202        }
1203    }
1204
1205    /// This drops all operand uses from this operation, which is used to break cyclic dependencies
1206    /// between references when they are to be deleted
1207    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    /// This drops all uses of any values defined by this operation or its nested regions,
1222    /// wherever they are located.
1223    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    /// Drop all uses of results of this operation
1243    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
1250/// Ordering
1251impl Operation {
1252    /// This value represents an invalid index ordering for an operation within its containing block
1253    const INVALID_ORDER: u32 = u32::MAX;
1254    /// This value represents the stride to use when computing a new order for an operation
1255    const ORDER_STRIDE: u32 = 5;
1256
1257    /// Returns true if this operation is an ancestor of `other`.
1258    ///
1259    /// An operation is considered its own ancestor, use [Self::is_proper_ancestor_of] if you do not
1260    /// want this behavior.
1261    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    /// Returns true if this operation is a proper ancestor of `other`
1266    pub fn is_proper_ancestor_of(&self, other: &Self) -> bool {
1267        Self::is_a_proper_ancestor_of_b(self, other)
1268    }
1269
1270    /// Returns true if operation `a` is a proper ancestor of operation `b`
1271    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    /// Given an operation `other` that is within the same parent block, return whether the current
1283    /// operation is before it in the operation list.
1284    ///
1285    /// NOTE: This function has an average complexity of O(1), but worst case may take O(N) where
1286    /// N is the number of operations within the parent block.
1287    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 the order of the block is already invalid, directly recompute the parent
1301        if !block.borrow().is_op_order_valid() {
1302            Self::recompute_block_order(block);
1303        } else {
1304            // Update the order of either operation if necessary.
1305            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    /// Update the order index of this operation of this operation if necessary,
1313    /// potentially recomputing the order of the parent block.
1314    fn update_order_if_necessary(&self) {
1315        use core::sync::atomic::Ordering;
1316
1317        assert!(self.parent().is_some(), "expected valid parent");
1318
1319        // If the order is valid for this operation there is nothing to do.
1320        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 the operation is at the end of the block.
1331        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            // Add the stride to the previous operation.
1340            self.order.store(prev_order + Self::ORDER_STRIDE, Ordering::Release);
1341            return;
1342        }
1343
1344        // If this is the first operation try to use the next operation to compute the
1345        // ordering.
1346        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                // If we can't use the stride, just take the middle value left. This is safe
1355                // because we know there is at least one valid index to assign to.
1356                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        // Otherwise, this operation is between two others. Place this operation in
1367        // the middle of the previous and next if possible.
1368        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        // Check to see if there is a valid order between the two.
1375        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    /// Returns `None` if this operation has invalid ordering
1401    #[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    /// Returns `None` if this operation has invalid ordering
1411    #[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    /// Returns true if this operation has a valid order
1428    #[inline(always)]
1429    pub(super) fn has_valid_order(&self) -> bool {
1430        self.order().is_some()
1431    }
1432}
1433
1434/// Canonicalization
1435impl Operation {
1436    /// Populates `rewrites` with the set of canonicalization patterns registered for this operation
1437    #[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}