midenc_hir/pass/
analysis.rs

1use alloc::rc::Rc;
2use core::{
3    any::{Any, TypeId},
4    cell::RefCell,
5};
6
7use smallvec::SmallVec;
8
9use super::{PassInstrumentor, PassTarget};
10use crate::{FxHashMap, Op, Operation, OperationRef, Report};
11
12/// The [Analysis] trait is used to define an analysis over some operation.
13///
14/// Analyses must be default-constructible, and `Sized + 'static` to support downcasting.
15///
16/// An analysis, when requested, is first constructed via its `Default` implementation, and then
17/// [Analysis::analyze] is called on the target type in order to compute the analysis results.
18/// The analysis type also acts as storage for the analysis results.
19///
20/// When the IR is changed, analyses are invalidated by default, unless they are specifically
21/// preserved via the [PreservedAnalyses] set. When an analysis is being asked if it should be
22/// invalidated, via [Analysis::invalidate], it has the opportunity to identify if it actually
23/// needs to be invalidated based on what analyses were preserved. If dependent analyses of this
24/// analysis haven't been invalidated, then this analysis may be able preserve itself as well,
25/// and avoid redundant recomputation.
26pub trait Analysis: Default + Any {
27    /// The specific type on which this analysis is performed.
28    ///
29    /// The analysis will only be run when an operation is of this type.
30    type Target: ?Sized + PassTarget;
31
32    /// The [TypeId] associated with the concrete underlying [Analysis] implementation
33    ///
34    /// This is automatically implemented for you, but in some cases, such as wrapping an
35    /// analysis in another type, you may want to implement this so that queries against the
36    /// type return the expected [TypeId]
37    #[inline]
38    fn analysis_id(&self) -> TypeId {
39        TypeId::of::<Self>()
40    }
41
42    /// Get a `dyn Any` reference to the underlying [Analysis] implementation
43    ///
44    /// This is automatically implemented for you, but in some cases, such as wrapping an
45    /// analysis in another type, you may want to implement this so that queries against the
46    /// type return the expected [TypeId]
47    #[inline(always)]
48    fn as_any(&self) -> &dyn Any {
49        self as &dyn Any
50    }
51
52    /// Same as [Analysis::as_any], but used specifically for getting a reference-counted handle,
53    /// rather than a raw reference.
54    #[inline(always)]
55    fn as_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
56        self as Rc<dyn Any>
57    }
58
59    /// Returns the display name for this analysis
60    ///
61    /// By default this simply returns the name of the concrete implementation type.
62    fn name(&self) -> &'static str {
63        core::any::type_name::<Self>()
64    }
65
66    /// Analyze `op` using the provided [AnalysisManager].
67    fn analyze(
68        &mut self,
69        op: &Self::Target,
70        analysis_manager: AnalysisManager,
71    ) -> Result<(), Report>;
72
73    /// Query this analysis for invalidation.
74    ///
75    /// Given a preserved analysis set, returns true if it should truly be invalidated. This allows
76    /// for more fine-tuned invalidation in cases where an analysis wasn't explicitly marked
77    /// preserved, but may be preserved(or invalidated) based upon other properties such as analyses
78    /// sets.
79    fn invalidate(&self, preserved_analyses: &mut PreservedAnalyses) -> bool;
80}
81
82/// A type-erased [Analysis].
83///
84/// This is automatically derived for all [Analysis] implementations, and is the means by which
85/// one can abstract over sets of analyses using dynamic dispatch.
86///
87/// This essentially just delegates to the underlying [Analysis] implementation, but it also handles
88/// converting a raw [OperationRef] to the appropriate target type expected by the underlying
89/// [Analysis].
90pub trait OperationAnalysis {
91    /// The unique type id of this analysis
92    fn analysis_id(&self) -> TypeId;
93
94    /// Used for dynamic casting to the underlying [Analysis] type
95    fn as_any(&self) -> &dyn Any;
96
97    /// Used for dynamic casting to the underlying [Analysis] type
98    fn as_any_rc(self: Rc<Self>) -> Rc<dyn Any>;
99
100    /// The name of this analysis
101    fn name(&self) -> &'static str;
102
103    /// Runs this analysis over `op`.
104    ///
105    /// NOTE: This is only ever called once per instantiation of the analysis, but in theory can
106    /// support multiple calls to re-analyze `op`. Each call should reset any internal state to
107    /// ensure that if an analysis is reused in this way, that each analysis gets a clean slate.
108    fn analyze(&mut self, op: &OperationRef, am: AnalysisManager) -> Result<(), Report>;
109
110    /// Query this analysis for invalidation.
111    ///
112    /// Given a preserved analysis set, returns true if it should truly be invalidated. This allows
113    /// for more fine-tuned invalidation in cases where an analysis wasn't explicitly marked
114    /// preserved, but may be preserved(or invalidated) based upon other properties such as analyses
115    /// sets.
116    ///
117    /// Invalidated analyses must be removed from `preserved_analyses`.
118    fn invalidate(&self, preserved_analyses: &mut PreservedAnalyses) -> bool;
119}
120
121impl dyn OperationAnalysis {
122    /// Cast an reference-counted handle to this analysis to its concrete implementation type.
123    ///
124    /// Returns `None` if the underlying analysis is not of type `T`
125    #[inline]
126    pub fn downcast<T: 'static>(self: Rc<Self>) -> Option<Rc<T>> {
127        self.as_any_rc().downcast::<T>().ok()
128    }
129}
130
131impl<A> OperationAnalysis for A
132where
133    A: Analysis,
134{
135    #[inline]
136    fn analysis_id(&self) -> TypeId {
137        <A as Analysis>::analysis_id(self)
138    }
139
140    #[inline]
141    fn as_any(&self) -> &dyn Any {
142        <A as Analysis>::as_any(self)
143    }
144
145    #[inline]
146    fn as_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
147        <A as Analysis>::as_any_rc(self)
148    }
149
150    #[inline]
151    fn name(&self) -> &'static str {
152        <A as Analysis>::name(self)
153    }
154
155    #[inline]
156    fn analyze(&mut self, op: &OperationRef, am: AnalysisManager) -> Result<(), Report> {
157        let op = <<A as Analysis>::Target as PassTarget>::into_target(op);
158        <A as Analysis>::analyze(self, &op, am)
159    }
160
161    #[inline]
162    fn invalidate(&self, preserved_analyses: &mut PreservedAnalyses) -> bool {
163        <A as Analysis>::invalidate(self, preserved_analyses)
164    }
165}
166
167/// Represents a set of analyses that are known to be preserved after a rewrite has been applied.
168#[derive(Default)]
169pub struct PreservedAnalyses {
170    /// The set of preserved analysis type ids
171    preserved: SmallVec<[TypeId; 8]>,
172}
173impl PreservedAnalyses {
174    /// Mark all analyses as preserved.
175    ///
176    /// This is generally only useful when the IR is known not to have changed.
177    pub fn preserve_all(&mut self) {
178        self.insert(AllAnalyses::TYPE_ID);
179    }
180
181    /// Mark the specified [Analysis] type as preserved.
182    pub fn preserve<A: 'static>(&mut self) {
183        self.insert(TypeId::of::<A>());
184    }
185
186    /// Mark a type as preserved using its raw [TypeId].
187    ///
188    /// Typically it is best to use [Self::preserve] instead, but this can be useful in cases
189    /// where you can't express the type in Rust directly.
190    pub fn preserve_raw(&mut self, id: TypeId) {
191        self.insert(id);
192    }
193
194    /// Returns true if the specified type is preserved.
195    ///
196    /// This will return true if all analyses are marked preserved, even if the specified type was
197    /// not explicitly preserved.
198    pub fn is_preserved<A: 'static>(&self) -> bool {
199        self.preserved.contains(&TypeId::of::<A>()) || self.is_all()
200    }
201
202    /// Returns true if the specified [TypeId] is marked preserved.
203    ///
204    /// This will return true if all analyses are marked preserved, even if the specified type was
205    /// not explicitly preserved.
206    pub fn is_preserved_raw(&self, ty: &TypeId) -> bool {
207        self.preserved.contains(ty) || self.is_all()
208    }
209
210    /// Mark a previously preserved type as invalidated.
211    ///
212    /// This will also remove the "all preserved" flag, if it had been set.
213    pub fn unpreserve<A: 'static>(&mut self) {
214        // We must also remove the `all` marker, as we have invalidated one of the analyses
215        self.remove(&AllAnalyses::TYPE_ID);
216        self.remove(&TypeId::of::<A>());
217    }
218
219    /// Mark a previously preserved [TypeId] as invalidated.
220    ///
221    /// This will also remove the "all preserved" flag, if it had been set.
222    pub fn unpreserve_raw(&mut self, ty: &TypeId) {
223        // We must also remove the `all` marker, as we have invalidated one of the analyses
224        self.remove(&AllAnalyses::TYPE_ID);
225        self.remove(ty)
226    }
227
228    /// Returns true if all analyses are preserved
229    pub fn is_all(&self) -> bool {
230        self.preserved.contains(&AllAnalyses::TYPE_ID)
231    }
232
233    /// Returns true if no analyses are being preserved
234    pub fn is_none(&self) -> bool {
235        self.preserved.is_empty()
236    }
237
238    fn insert(&mut self, id: TypeId) {
239        match self.preserved.binary_search_by_key(&id, |probe| *probe) {
240            Ok(index) => self.preserved.insert(index, id),
241            Err(index) => self.preserved.insert(index, id),
242        }
243    }
244
245    fn remove(&mut self, id: &TypeId) {
246        if let Ok(index) = self.preserved.binary_search_by_key(&id, |probe| probe) {
247            self.preserved.remove(index);
248        }
249    }
250}
251
252/// A marker type that is used to represent all possible [Analysis] types
253pub struct AllAnalyses;
254impl AllAnalyses {
255    const TYPE_ID: TypeId = TypeId::of::<AllAnalyses>();
256}
257
258/// This type wraps all analyses stored in an [AnalysisMap], and handles some of the boilerplate
259/// details around invalidation by intercepting calls to [Analysis::invalidate] and wrapping it
260/// with extra logic. Notably, ensuring that invalidated analyses are removed from the
261/// [PreservedAnalyses] set is handled by this wrapper.
262///
263/// It is a transparent wrapper around `A`, and otherwise acts as a simple proxy to `A`'s
264/// implementation of the [Analysis] trait.
265#[repr(transparent)]
266struct AnalysisWrapper<A> {
267    analysis: A,
268}
269impl<A: Analysis> AnalysisWrapper<A> {
270    fn new(op: &<A as Analysis>::Target, am: AnalysisManager) -> Result<Self, Report> {
271        let mut analysis = A::default();
272        analysis.analyze(op, am)?;
273
274        Ok(Self { analysis })
275    }
276}
277impl<A: Default> Default for AnalysisWrapper<A> {
278    fn default() -> Self {
279        Self {
280            analysis: Default::default(),
281        }
282    }
283}
284impl<A: Analysis> Analysis for AnalysisWrapper<A> {
285    type Target = <A as Analysis>::Target;
286
287    #[inline]
288    fn analysis_id(&self) -> TypeId {
289        self.analysis.analysis_id()
290    }
291
292    #[inline]
293    fn as_any(&self) -> &dyn Any {
294        self.analysis.as_any()
295    }
296
297    #[inline]
298    fn as_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
299        // SAFETY: This transmute is safe because AnalysisWrapper is a transparent wrapper
300        // around A, so a pointer to the former is a pointer to the latter
301        let ptr = Rc::into_raw(self);
302        unsafe { Rc::<A>::from_raw(ptr.cast()) as Rc<dyn Any> }
303    }
304
305    #[inline]
306    fn name(&self) -> &'static str {
307        self.analysis.name()
308    }
309
310    #[inline]
311    fn analyze(&mut self, op: &Self::Target, am: AnalysisManager) -> Result<(), Report> {
312        self.analysis.analyze(op, am)
313    }
314
315    fn invalidate(&self, preserved_analyses: &mut PreservedAnalyses) -> bool {
316        let invalidated = self.analysis.invalidate(preserved_analyses);
317        if invalidated {
318            preserved_analyses.unpreserve::<A>();
319        }
320        invalidated
321    }
322}
323
324/// An [AnalysisManager] is the primary entrypoint for performing analysis on a specific operation
325/// instance that it is constructed for.
326///
327/// It is used to manage and cache analyses for the operation, as well as those of child operations,
328/// via nested [AnalysisManager] instances.
329///
330/// This type is a thin wrapper around a pointer, and is meant to be passed by value. It can be
331/// cheaply cloned.
332#[derive(Clone)]
333#[repr(transparent)]
334pub struct AnalysisManager {
335    analyses: Rc<NestedAnalysisMap>,
336}
337impl AnalysisManager {
338    /// Create a new top-level [AnalysisManager] for `op`
339    pub fn new(op: OperationRef, instrumentor: Option<Rc<PassInstrumentor>>) -> Self {
340        Self {
341            analyses: Rc::new(NestedAnalysisMap::new(op, instrumentor)),
342        }
343    }
344
345    /// Query for a cached analysis on the given parent operation. The analysis may not exist and if
346    /// it does it may be out-of-date.
347    pub fn get_cached_parent_analysis<A>(&self, parent: OperationRef) -> Option<Rc<A>>
348    where
349        A: Analysis,
350    {
351        let mut current_parent = self.analyses.parent();
352        while let Some(parent_am) = current_parent.take() {
353            if parent_am.get_operation() == parent {
354                return parent_am.analyses().get_cached::<A>();
355            }
356            current_parent = parent_am.parent();
357        }
358        None
359    }
360
361    /// Query for the given analysis for the current operation.
362    pub fn get_analysis<A>(&self) -> Result<Rc<A>, Report>
363    where
364        A: Analysis<Target = Operation>,
365    {
366        self.get_analysis_for::<A, Operation>()
367    }
368
369    /// Query for the given analysis for the current operation of a specific derived operation type.
370    ///
371    /// NOTE: This will panic if the current operation is not of type `O`.
372    pub fn get_analysis_for<A, O>(&self) -> Result<Rc<A>, Report>
373    where
374        A: Analysis<Target = O>,
375        O: 'static,
376    {
377        let op = {
378            let analysis_map = self.analyses.analyses.borrow();
379            let cached = analysis_map.get_cached_for::<A, O>();
380            if let Some(cached) = cached {
381                return Ok(cached);
382            }
383            analysis_map.ir
384        };
385
386        // We have to construct the analysis without borrowing the AnalysisMap, otherwise we might
387        // try to re-borrow the map to construct a dependent analysis while holding a mutable ref.
388        let pi = self.pass_instrumentor();
389        let am = self.clone();
390        let ir = <O as PassTarget>::into_target(&op);
391        let analysis = AnalysisMap::compute_analysis_for::<A, O>(pi, &*ir, &op, am)?;
392
393        // Once the analysis is constructed, we can add it to the map
394        self.analyses
395            .analyses
396            .borrow_mut()
397            .analyses
398            .insert(TypeId::of::<A>(), Rc::clone(&analysis) as Rc<dyn OperationAnalysis>);
399
400        Ok(analysis)
401    }
402
403    /// Query for a cached entry of the given analysis on the current operation.
404    pub fn get_cached_analysis<A>(&self) -> Option<Rc<A>>
405    where
406        A: Analysis,
407    {
408        self.analyses.analyses().get_cached::<A>()
409    }
410
411    /// Query for an analysis of a child operation, constructing it if necessary.
412    pub fn get_child_analysis<A>(&self, op: OperationRef) -> Result<Rc<A>, Report>
413    where
414        A: Analysis<Target = Operation>,
415    {
416        self.clone().nest(op).get_analysis::<A>()
417    }
418
419    /// Query for an analysis of a child operation of a specific derived operation type,
420    /// constructing it if necessary.
421    ///
422    /// NOTE: This will panic if `op` is not of type `O`.
423    pub fn get_child_analysis_for<A, O>(&self, op: &O) -> Result<Rc<A>, Report>
424    where
425        A: Analysis<Target = O>,
426        O: Op,
427    {
428        self.clone().nest(op.as_operation_ref()).get_analysis_for::<A, O>()
429    }
430
431    /// Query for a cached analysis of a child operation, or return `None`.
432    pub fn get_cached_child_analysis<A>(&self, child: &OperationRef) -> Option<Rc<A>>
433    where
434        A: Analysis,
435    {
436        assert!(child.borrow().parent_op().unwrap() == self.analyses.get_operation());
437        let child_analyses = self.analyses.child_analyses.borrow();
438        let child_analyses = child_analyses.get(child)?;
439        let child_analyses = child_analyses.analyses.borrow();
440        child_analyses.get_cached::<A>()
441    }
442
443    /// Get an analysis manager for the given operation, which must be a proper descendant of the
444    /// current operation represented by this analysis manager.
445    pub fn nest(&self, op: OperationRef) -> AnalysisManager {
446        let current_op = self.analyses.get_operation();
447        assert!(
448            current_op.borrow().is_proper_ancestor_of(&op.borrow()),
449            "expected valid descendant op"
450        );
451
452        // Check for the base case where the provided operation is immediately nested
453        if current_op == op.borrow().parent_op().expect("expected `op` to have a parent") {
454            return self.nest_immediate(op);
455        }
456
457        // Otherwise, we need to collect all ancestors up to the current operation
458        let mut ancestors = SmallVec::<[OperationRef; 4]>::default();
459        let mut next_op = op;
460        while next_op != current_op {
461            ancestors.push(next_op);
462            next_op = next_op.borrow().parent_op().unwrap();
463        }
464
465        let mut manager = self.clone();
466        while let Some(op) = ancestors.pop() {
467            manager = manager.nest_immediate(op);
468        }
469        manager
470    }
471
472    fn nest_immediate(&self, op: OperationRef) -> AnalysisManager {
473        use hashbrown::hash_map::Entry;
474
475        assert!(
476            Some(self.analyses.get_operation()) == op.borrow().parent_op(),
477            "expected immediate child operation"
478        );
479        let parent = self.analyses.clone();
480        let mut child_analyses = self.analyses.child_analyses.borrow_mut();
481        match child_analyses.entry(op) {
482            Entry::Vacant(entry) => {
483                let analyses = entry.insert(Rc::new(parent.nest(op)));
484                AnalysisManager {
485                    analyses: Rc::clone(analyses),
486                }
487            }
488            Entry::Occupied(entry) => AnalysisManager {
489                analyses: Rc::clone(entry.get()),
490            },
491        }
492    }
493
494    /// Invalidate any non preserved analyses.
495    #[inline]
496    pub fn invalidate(&self, preserved_analyses: &mut PreservedAnalyses) {
497        Rc::clone(&self.analyses).invalidate(preserved_analyses)
498    }
499
500    /// Clear any held analyses.
501    #[inline]
502    pub fn clear(&mut self) {
503        self.analyses.clear();
504    }
505
506    /// Clear any held analyses when the returned guard is dropped.
507    #[inline]
508    pub fn defer_clear(&self) -> ResetAnalysesOnDrop {
509        ResetAnalysesOnDrop {
510            analyses: self.analyses.clone(),
511        }
512    }
513
514    /// Returns a [PassInstrumentor] for the current operation, if one was installed.
515    #[inline]
516    pub fn pass_instrumentor(&self) -> Option<Rc<PassInstrumentor>> {
517        self.analyses.pass_instrumentor()
518    }
519}
520
521#[must_use]
522#[doc(hidden)]
523pub struct ResetAnalysesOnDrop {
524    analyses: Rc<NestedAnalysisMap>,
525}
526impl Drop for ResetAnalysesOnDrop {
527    fn drop(&mut self) {
528        self.analyses.clear()
529    }
530}
531
532/// An analysis map that contains a map for the current operation, and a set of maps for any child
533/// operations.
534struct NestedAnalysisMap {
535    parent: Option<Rc<NestedAnalysisMap>>,
536    instrumentor: Option<Rc<PassInstrumentor>>,
537    analyses: RefCell<AnalysisMap>,
538    child_analyses: RefCell<FxHashMap<OperationRef, Rc<NestedAnalysisMap>>>,
539}
540impl NestedAnalysisMap {
541    /// Create a new top-level [NestedAnalysisMap] for `op`, with the given optional pass
542    /// instrumentor.
543    pub fn new(op: OperationRef, instrumentor: Option<Rc<PassInstrumentor>>) -> Self {
544        Self {
545            parent: None,
546            instrumentor,
547            analyses: RefCell::new(AnalysisMap::new(op)),
548            child_analyses: Default::default(),
549        }
550    }
551
552    /// Create a new [NestedAnalysisMap] for `op` nested under `self`.
553    pub fn nest(self: Rc<Self>, op: OperationRef) -> Self {
554        let instrumentor = self.instrumentor.clone();
555        Self {
556            parent: Some(self),
557            instrumentor,
558            analyses: RefCell::new(AnalysisMap::new(op)),
559            child_analyses: Default::default(),
560        }
561    }
562
563    /// Get the parent [NestedAnalysisMap], or `None` if this is a top-level map.
564    pub fn parent(&self) -> Option<Rc<NestedAnalysisMap>> {
565        self.parent.clone()
566    }
567
568    /// Return a [PassInstrumentor] for the current operation, if one was installed.
569    pub fn pass_instrumentor(&self) -> Option<Rc<PassInstrumentor>> {
570        self.instrumentor.clone()
571    }
572
573    /// Get the operation for this analysis map.
574    #[inline]
575    pub fn get_operation(&self) -> OperationRef {
576        self.analyses.borrow().get_operation()
577    }
578
579    fn analyses(&self) -> core::cell::Ref<'_, AnalysisMap> {
580        self.analyses.borrow()
581    }
582
583    /// Invalidate any non preserved analyses.
584    pub fn invalidate(self: Rc<Self>, preserved_analyses: &mut PreservedAnalyses) {
585        // If all analyses were preserved, then there is nothing to do
586        if preserved_analyses.is_all() {
587            return;
588        }
589
590        // Invalidate the analyses for the current operation directly
591        self.analyses.borrow_mut().invalidate(preserved_analyses);
592
593        // If no analyses were preserved, then just simply clear out the child analysis results
594        if preserved_analyses.is_none() {
595            self.child_analyses.borrow_mut().clear();
596        }
597
598        // Otherwise, invalidate each child analysis map
599        let mut to_invalidate = SmallVec::<[Rc<NestedAnalysisMap>; 8]>::from_iter([self]);
600        while let Some(map) = to_invalidate.pop() {
601            map.child_analyses.borrow_mut().retain(|_op, nested_analysis_map| {
602                Rc::clone(nested_analysis_map).invalidate(preserved_analyses);
603                if nested_analysis_map.child_analyses.borrow().is_empty() {
604                    false
605                } else {
606                    to_invalidate.push(Rc::clone(nested_analysis_map));
607                    true
608                }
609            });
610        }
611    }
612
613    pub fn clear(&self) {
614        self.child_analyses.borrow_mut().clear();
615        self.analyses.borrow_mut().clear();
616    }
617}
618
619/// This class represents a cache of analyses for a single operation.
620///
621/// All computation, caching, and invalidation of analyses takes place here.
622struct AnalysisMap {
623    analyses: FxHashMap<TypeId, Rc<dyn OperationAnalysis>>,
624    ir: OperationRef,
625}
626impl AnalysisMap {
627    pub fn new(ir: OperationRef) -> Self {
628        Self {
629            analyses: Default::default(),
630            ir,
631        }
632    }
633
634    /// Get a cached analysis instance if one exists, otherwise return `None`.
635    pub fn get_cached<A>(&self) -> Option<Rc<A>>
636    where
637        A: Analysis,
638    {
639        self.analyses.get(&TypeId::of::<A>()).cloned().and_then(|a| a.downcast::<A>())
640    }
641
642    /// Get a cached analysis instance if one exists, otherwise return `None`.
643    pub fn get_cached_for<A, O>(&self) -> Option<Rc<A>>
644    where
645        A: Analysis<Target = O>,
646        O: 'static,
647    {
648        self.analyses.get(&TypeId::of::<A>()).cloned().and_then(|a| a.downcast::<A>())
649    }
650
651    fn compute_analysis_for<A, O>(
652        pi: Option<Rc<PassInstrumentor>>,
653        ir: &O,
654        op: &OperationRef,
655        am: AnalysisManager,
656    ) -> Result<Rc<A>, Report>
657    where
658        A: Analysis<Target = O>,
659    {
660        let id = TypeId::of::<A>();
661
662        // We don't have a cached analysis for the operation, compute it directly and
663        // add it to the cache.
664        if let Some(pi) = pi.as_deref() {
665            pi.run_before_analysis(core::any::type_name::<A>(), &id, op);
666        }
667
668        let analysis = Self::construct_analysis::<A, O>(am, ir)?;
669
670        if let Some(pi) = pi.as_deref() {
671            pi.run_after_analysis(core::any::type_name::<A>(), &id, op);
672        }
673
674        Ok(analysis.downcast::<A>().unwrap())
675    }
676
677    fn construct_analysis<A, O>(
678        am: AnalysisManager,
679        op: &O,
680    ) -> Result<Rc<dyn OperationAnalysis>, Report>
681    where
682        A: Analysis<Target = O>,
683    {
684        AnalysisWrapper::<A>::new(op, am)
685            .map(|analysis| Rc::new(analysis) as Rc<dyn OperationAnalysis>)
686    }
687
688    /// Returns the operation that this analysis map represents.
689    pub fn get_operation(&self) -> OperationRef {
690        self.ir
691    }
692
693    /// Clear any held analyses.
694    pub fn clear(&mut self) {
695        self.analyses.clear();
696    }
697
698    /// Invalidate any cached analyses based upon the given set of preserved analyses.
699    pub fn invalidate(&mut self, preserved_analyses: &mut PreservedAnalyses) {
700        // Remove any analyses that were invalidated.
701        //
702        // Using `retain`, we preserve the original insertion order, and dependencies always go
703        // before users, so we need only a single pass through.
704        self.analyses.retain(|_, a| !a.invalidate(preserved_analyses));
705    }
706}