midenc_hir/pass/
pass.rs

1use alloc::{boxed::Box, rc::Rc};
2use core::{any::Any, fmt};
3
4use super::*;
5use crate::{Context, EntityMut, OperationName, OperationRef, Report};
6
7/// A type-erased [Pass].
8///
9/// This is used to allow heterogenous passes to be operated on uniformly.
10///
11/// Semantically, an [OperationPass] behaves like a `Pass<Target = Operation>`.
12#[allow(unused_variables)]
13pub trait OperationPass {
14    fn as_any(&self) -> &dyn Any;
15    fn as_any_mut(&mut self) -> &mut dyn Any;
16    fn into_any(self: Box<Self>) -> Box<dyn Any>;
17    fn name(&self) -> &'static str;
18
19    fn argument(&self) -> &'static str {
20        // NOTE: Could we compute an argument string from the type name?
21        ""
22    }
23    fn description(&self) -> &'static str {
24        ""
25    }
26    fn info(&self) -> PassInfo {
27        PassInfo::lookup(self.argument()).expect("could not find pass information")
28    }
29    /// The name of the operation that this pass operates on, or `None` if this is a generic pass.
30    fn target_name(&self, context: &Context) -> Option<OperationName>;
31    fn initialize_options(&mut self, options: &str) -> Result<(), Report> {
32        Ok(())
33    }
34    fn print_as_textual_pipeline(&self, f: &mut fmt::Formatter) -> fmt::Result;
35    fn has_statistics(&self) -> bool {
36        !self.statistics().is_empty()
37    }
38    fn statistics(&self) -> &[Box<dyn Statistic>];
39    fn statistics_mut(&mut self) -> &mut [Box<dyn Statistic>];
40    fn initialize(&mut self, context: Rc<Context>) -> Result<(), Report> {
41        Ok(())
42    }
43    fn can_schedule_on(&self, name: &OperationName) -> bool;
44    fn run_on_operation(
45        &mut self,
46        op: OperationRef,
47        state: &mut PassExecutionState,
48    ) -> Result<(), Report>;
49    fn run_pipeline(
50        &mut self,
51        pipeline: &mut OpPassManager,
52        op: OperationRef,
53        state: &mut PassExecutionState,
54    ) -> Result<(), Report>;
55}
56
57impl<P> OperationPass for P
58where
59    P: Pass + 'static,
60{
61    fn as_any(&self) -> &dyn Any {
62        <P as Pass>::as_any(self)
63    }
64
65    fn as_any_mut(&mut self) -> &mut dyn Any {
66        <P as Pass>::as_any_mut(self)
67    }
68
69    fn into_any(self: Box<Self>) -> Box<dyn Any> {
70        <P as Pass>::into_any(self)
71    }
72
73    fn name(&self) -> &'static str {
74        <P as Pass>::name(self)
75    }
76
77    fn argument(&self) -> &'static str {
78        <P as Pass>::argument(self)
79    }
80
81    fn description(&self) -> &'static str {
82        <P as Pass>::description(self)
83    }
84
85    fn info(&self) -> PassInfo {
86        <P as Pass>::info(self)
87    }
88
89    fn target_name(&self, context: &Context) -> Option<OperationName> {
90        <P as Pass>::target_name(self, context)
91    }
92
93    fn initialize_options(&mut self, options: &str) -> Result<(), Report> {
94        <P as Pass>::initialize_options(self, options)
95    }
96
97    fn print_as_textual_pipeline(&self, f: &mut fmt::Formatter) -> fmt::Result {
98        <P as Pass>::print_as_textual_pipeline(self, f)
99    }
100
101    fn has_statistics(&self) -> bool {
102        <P as Pass>::has_statistics(self)
103    }
104
105    fn statistics(&self) -> &[Box<dyn Statistic>] {
106        <P as Pass>::statistics(self)
107    }
108
109    fn statistics_mut(&mut self) -> &mut [Box<dyn Statistic>] {
110        <P as Pass>::statistics_mut(self)
111    }
112
113    fn initialize(&mut self, context: Rc<Context>) -> Result<(), Report> {
114        <P as Pass>::initialize(self, context)
115    }
116
117    fn can_schedule_on(&self, name: &OperationName) -> bool {
118        <P as Pass>::can_schedule_on(self, name)
119    }
120
121    fn run_on_operation(
122        &mut self,
123        mut op: OperationRef,
124        state: &mut PassExecutionState,
125    ) -> Result<(), Report> {
126        let op = <<P as Pass>::Target as PassTarget>::into_target_mut(&mut op);
127        <P as Pass>::run_on_operation(self, op, state)
128    }
129
130    fn run_pipeline(
131        &mut self,
132        pipeline: &mut OpPassManager,
133        op: OperationRef,
134        state: &mut PassExecutionState,
135    ) -> Result<(), Report> {
136        <P as Pass>::run_pipeline(self, pipeline, op, state)
137    }
138}
139
140#[derive(Debug, PartialEq, Clone, Copy)]
141pub enum PostPassStatus {
142    Unchanged,
143    Changed,
144}
145
146impl From<bool> for PostPassStatus {
147    fn from(ir_was_changed: bool) -> Self {
148        if ir_was_changed {
149            PostPassStatus::Changed
150        } else {
151            PostPassStatus::Unchanged
152        }
153    }
154}
155
156/// A compiler pass which operates on an [Operation] of some kind.
157#[allow(unused_variables)]
158pub trait Pass: Sized + Any {
159    /// The concrete/trait type targeted by this pass.
160    ///
161    /// Calls to `get_operation` will return a reference of this type.
162    type Target: ?Sized + PassTarget;
163
164    /// Used for downcasting
165    #[inline(always)]
166    fn as_any(&self) -> &dyn Any {
167        self as &dyn Any
168    }
169
170    /// Used for downcasting
171    #[inline(always)]
172    fn as_any_mut(&mut self) -> &mut dyn Any {
173        self as &mut dyn Any
174    }
175
176    /// Used for downcasting
177    #[inline(always)]
178    fn into_any(self: Box<Self>) -> Box<dyn Any> {
179        self as Box<dyn Any>
180    }
181
182    /// The display name of this pass
183    fn name(&self) -> &'static str;
184    /// The command line option name used to control this pass
185    fn argument(&self) -> &'static str {
186        // NOTE: Could we compute an argument string from the type name or `self.name()`?
187        ""
188    }
189    /// A description of what this pass does.
190    fn description(&self) -> &'static str {
191        ""
192    }
193    /// Obtain the underlying [PassInfo] object for this pass.
194    fn info(&self) -> PassInfo {
195        PassInfo::lookup(self.argument()).expect("pass is not currently registered")
196    }
197    /// The name of the operation that this pass operates on, or `None` if this is a generic pass.
198    fn target_name(&self, context: &Context) -> Option<OperationName> {
199        <<Self as Pass>::Target as PassTarget>::target_name(context)
200    }
201    /// If command-line options are provided for this pass, implementations must parse the raw
202    /// options here, returning `Err` if parsing fails for some reason.
203    ///
204    /// By default, this is a no-op.
205    fn initialize_options(&mut self, options: &str) -> Result<(), Report> {
206        Ok(())
207    }
208    /// Prints out the pass in the textual representation of pipelines.
209    ///
210    /// If this is an adaptor pass, print its pass managers.
211    fn print_as_textual_pipeline(&self, f: &mut fmt::Formatter) -> fmt::Result {
212        let argument = self.argument();
213        if !argument.is_empty() {
214            write!(f, "{argument}")
215        } else {
216            write!(f, "unknown<{}>", self.name())
217        }
218    }
219    /// Returns true if this pass has associated statistics
220    fn has_statistics(&self) -> bool {
221        !self.statistics().is_empty()
222    }
223    /// Get pass statistics associated with this pass
224    fn statistics(&self) -> &[Box<dyn Statistic>] {
225        &[]
226    }
227    /// Get mutable access to the pass statistics associated with this pass
228    fn statistics_mut(&mut self) -> &mut [Box<dyn Statistic>] {
229        &mut []
230    }
231    /// Initialize any complex state necessary for running this pass.
232    ///
233    /// This hook should not rely on any state accessible during the execution of a pass. For
234    /// example, `context`/`get_operation`/`get_analysis`/etc. should not be invoked within this
235    /// hook.
236    ///
237    /// This method is invoked after all dependent dialects for the pipeline are loaded, and is not
238    /// allowed to load any further dialects (override the `get_dependent_dialects()` hook for this
239    /// purpose instead). Returns `Err` with a diagnostic if initialization fails, in which case the
240    /// pass pipeline won't execute.
241    fn initialize(&mut self, context: Rc<Context>) -> Result<(), Report> {
242        Ok(())
243    }
244    /// Query if this pass can be scheduled to run on the given operation type.
245    fn can_schedule_on(&self, name: &OperationName) -> bool;
246    /// Run this pass on the current operation
247    fn run_on_operation(
248        &mut self,
249        op: EntityMut<'_, Self::Target>,
250        state: &mut PassExecutionState,
251    ) -> Result<(), Report>;
252    /// Schedule an arbitrary pass pipeline on the provided operation.
253    ///
254    /// This can be invoke any time in a pass to dynamic schedule more passes. The provided
255    /// operation must be the current one or one nested below.
256    fn run_pipeline(
257        &mut self,
258        pipeline: &mut OpPassManager,
259        op: OperationRef,
260        state: &mut PassExecutionState,
261    ) -> Result<(), Report> {
262        state.run_pipeline(pipeline, op)
263    }
264}
265
266impl<P> Pass for Box<P>
267where
268    P: Pass,
269{
270    type Target = <P as Pass>::Target;
271
272    fn as_any(&self) -> &dyn Any {
273        (**self).as_any()
274    }
275
276    fn as_any_mut(&mut self) -> &mut dyn Any {
277        (**self).as_any_mut()
278    }
279
280    fn into_any(self: Box<Self>) -> Box<dyn Any> {
281        let pass = Box::into_inner(self);
282        <P as Pass>::into_any(pass)
283    }
284
285    #[inline]
286    fn name(&self) -> &'static str {
287        (**self).name()
288    }
289
290    #[inline]
291    fn argument(&self) -> &'static str {
292        (**self).argument()
293    }
294
295    #[inline]
296    fn description(&self) -> &'static str {
297        (**self).description()
298    }
299
300    #[inline]
301    fn info(&self) -> PassInfo {
302        (**self).info()
303    }
304
305    #[inline]
306    fn target_name(&self, context: &Context) -> Option<OperationName> {
307        (**self).target_name(context)
308    }
309
310    #[inline]
311    fn initialize_options(&mut self, options: &str) -> Result<(), Report> {
312        (**self).initialize_options(options)
313    }
314
315    #[inline]
316    fn print_as_textual_pipeline(&self, f: &mut fmt::Formatter) -> fmt::Result {
317        (**self).print_as_textual_pipeline(f)
318    }
319
320    #[inline]
321    fn has_statistics(&self) -> bool {
322        (**self).has_statistics()
323    }
324
325    #[inline]
326    fn statistics(&self) -> &[Box<dyn Statistic>] {
327        (**self).statistics()
328    }
329
330    #[inline]
331    fn statistics_mut(&mut self) -> &mut [Box<dyn Statistic>] {
332        (**self).statistics_mut()
333    }
334
335    #[inline]
336    fn initialize(&mut self, context: Rc<Context>) -> Result<(), Report> {
337        (**self).initialize(context)
338    }
339
340    #[inline]
341    fn can_schedule_on(&self, name: &OperationName) -> bool {
342        (**self).can_schedule_on(name)
343    }
344
345    #[inline]
346    fn run_on_operation(
347        &mut self,
348        op: EntityMut<'_, Self::Target>,
349        state: &mut PassExecutionState,
350    ) -> Result<(), Report> {
351        (**self).run_on_operation(op, state)
352    }
353
354    #[inline]
355    fn run_pipeline(
356        &mut self,
357        pipeline: &mut OpPassManager,
358        op: OperationRef,
359        state: &mut PassExecutionState,
360    ) -> Result<(), Report> {
361        (**self).run_pipeline(pipeline, op, state)
362    }
363}
364
365pub type DynamicPipelineExecutor =
366    dyn FnMut(&mut OpPassManager, OperationRef) -> Result<(), Report>;
367
368/// The state for a single execution of a pass. This provides a unified
369/// interface for accessing and initializing necessary state for pass execution.
370pub struct PassExecutionState {
371    /// The operation being transformed
372    op: OperationRef,
373    context: Rc<Context>,
374    analysis_manager: AnalysisManager,
375    /// The set of preserved analyses for the current execution
376    preserved_analyses: PreservedAnalyses,
377    // Callback in the pass manager that allows one to schedule dynamic pipelines that will be
378    // rooted at the provided operation.
379    #[allow(unused)]
380    pipeline_executor: Option<Box<DynamicPipelineExecutor>>,
381    post_pass_status: PostPassStatus,
382}
383impl PassExecutionState {
384    pub fn new(
385        op: OperationRef,
386        context: Rc<Context>,
387        analysis_manager: AnalysisManager,
388        pipeline_executor: Option<Box<DynamicPipelineExecutor>>,
389    ) -> Self {
390        Self {
391            op,
392            context,
393            analysis_manager,
394            preserved_analyses: Default::default(),
395            pipeline_executor,
396            post_pass_status: PostPassStatus::Unchanged,
397        }
398    }
399
400    #[inline(always)]
401    pub fn context(&self) -> Rc<Context> {
402        self.context.clone()
403    }
404
405    #[inline(always)]
406    pub const fn current_operation(&self) -> &OperationRef {
407        &self.op
408    }
409
410    #[inline(always)]
411    pub const fn analysis_manager(&self) -> &AnalysisManager {
412        &self.analysis_manager
413    }
414
415    #[inline(always)]
416    pub const fn preserved_analyses(&self) -> &PreservedAnalyses {
417        &self.preserved_analyses
418    }
419
420    #[inline(always)]
421    pub fn preserved_analyses_mut(&mut self) -> &mut PreservedAnalyses {
422        &mut self.preserved_analyses
423    }
424
425    #[inline(always)]
426    pub fn post_pass_status(&self) -> &PostPassStatus {
427        &self.post_pass_status
428    }
429
430    #[inline(always)]
431    pub fn set_post_pass_status(&mut self, post_pass_status: PostPassStatus) {
432        self.post_pass_status = post_pass_status;
433    }
434
435    pub fn run_pipeline(
436        &mut self,
437        pipeline: &mut OpPassManager,
438        op: OperationRef,
439    ) -> Result<(), Report> {
440        if let Some(pipeline_executor) = self.pipeline_executor.as_deref_mut() {
441            pipeline_executor(pipeline, op)
442        } else {
443            Ok(())
444        }
445    }
446}