Skip to main content

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 PostPassStatus {
147    pub const fn ir_changed(&self) -> bool {
148        matches!(self, Self::Changed)
149    }
150}
151
152impl From<bool> for PostPassStatus {
153    fn from(ir_was_changed: bool) -> Self {
154        if ir_was_changed {
155            PostPassStatus::Changed
156        } else {
157            PostPassStatus::Unchanged
158        }
159    }
160}
161
162impl core::ops::BitOrAssign for PostPassStatus {
163    fn bitor_assign(&mut self, rhs: Self) {
164        if rhs.ir_changed() {
165            *self = PostPassStatus::Changed;
166        }
167    }
168}
169
170/// A compiler pass which operates on an [Operation] of some kind.
171#[allow(unused_variables)]
172pub trait Pass: Sized + Any {
173    /// The concrete/trait type targeted by this pass.
174    ///
175    /// Calls to `get_operation` will return a reference of this type.
176    type Target: ?Sized + PassTarget;
177
178    /// Used for downcasting
179    #[inline(always)]
180    fn as_any(&self) -> &dyn Any {
181        self as &dyn Any
182    }
183
184    /// Used for downcasting
185    #[inline(always)]
186    fn as_any_mut(&mut self) -> &mut dyn Any {
187        self as &mut dyn Any
188    }
189
190    /// Used for downcasting
191    #[inline(always)]
192    fn into_any(self: Box<Self>) -> Box<dyn Any> {
193        self as Box<dyn Any>
194    }
195
196    /// The display name of this pass
197    fn name(&self) -> &'static str;
198    /// The command line option name used to control this pass
199    fn argument(&self) -> &'static str {
200        // NOTE: Could we compute an argument string from the type name or `self.name()`?
201        ""
202    }
203    /// A description of what this pass does.
204    fn description(&self) -> &'static str {
205        ""
206    }
207    /// Obtain the underlying [PassInfo] object for this pass.
208    fn info(&self) -> PassInfo {
209        PassInfo::lookup(self.argument()).expect("pass is not currently registered")
210    }
211    /// The name of the operation that this pass operates on, or `None` if this is a generic pass.
212    fn target_name(&self, context: &Context) -> Option<OperationName> {
213        <<Self as Pass>::Target as PassTarget>::target_name(context)
214    }
215    /// If command-line options are provided for this pass, implementations must parse the raw
216    /// options here, returning `Err` if parsing fails for some reason.
217    ///
218    /// By default, this is a no-op.
219    fn initialize_options(&mut self, options: &str) -> Result<(), Report> {
220        Ok(())
221    }
222    /// Prints out the pass in the textual representation of pipelines.
223    ///
224    /// If this is an adaptor pass, print its pass managers.
225    fn print_as_textual_pipeline(&self, f: &mut fmt::Formatter) -> fmt::Result {
226        let argument = self.argument();
227        if !argument.is_empty() {
228            write!(f, "{argument}")
229        } else {
230            write!(f, "unknown<{}>", self.name())
231        }
232    }
233    /// Returns true if this pass has associated statistics
234    fn has_statistics(&self) -> bool {
235        !self.statistics().is_empty()
236    }
237    /// Get pass statistics associated with this pass
238    fn statistics(&self) -> &[Box<dyn Statistic>] {
239        &[]
240    }
241    /// Get mutable access to the pass statistics associated with this pass
242    fn statistics_mut(&mut self) -> &mut [Box<dyn Statistic>] {
243        &mut []
244    }
245    /// Initialize any complex state necessary for running this pass.
246    ///
247    /// This hook should not rely on any state accessible during the execution of a pass. For
248    /// example, `context`/`get_operation`/`get_analysis`/etc. should not be invoked within this
249    /// hook.
250    ///
251    /// This method is invoked after all dependent dialects for the pipeline are loaded, and is not
252    /// allowed to load any further dialects (override the `get_dependent_dialects()` hook for this
253    /// purpose instead). Returns `Err` with a diagnostic if initialization fails, in which case the
254    /// pass pipeline won't execute.
255    fn initialize(&mut self, context: Rc<Context>) -> Result<(), Report> {
256        Ok(())
257    }
258    /// Query if this pass can be scheduled to run on the given operation type.
259    fn can_schedule_on(&self, name: &OperationName) -> bool;
260    /// Run this pass on the current operation
261    fn run_on_operation(
262        &mut self,
263        op: EntityMut<'_, Self::Target>,
264        state: &mut PassExecutionState,
265    ) -> Result<(), Report>;
266    /// Schedule an arbitrary pass pipeline on the provided operation.
267    ///
268    /// This can be invoke any time in a pass to dynamic schedule more passes. The provided
269    /// operation must be the current one or one nested below.
270    fn run_pipeline(
271        &mut self,
272        pipeline: &mut OpPassManager,
273        op: OperationRef,
274        state: &mut PassExecutionState,
275    ) -> Result<(), Report> {
276        state.run_pipeline(pipeline, op)
277    }
278}
279
280impl<P> Pass for Box<P>
281where
282    P: Pass,
283{
284    type Target = <P as Pass>::Target;
285
286    fn as_any(&self) -> &dyn Any {
287        <P as Pass>::as_any(self)
288    }
289
290    fn as_any_mut(&mut self) -> &mut dyn Any {
291        <P as Pass>::as_any_mut(self)
292    }
293
294    fn into_any(self: Box<Self>) -> Box<dyn Any> {
295        let pass = Box::into_inner(self);
296        <P as Pass>::into_any(pass)
297    }
298
299    #[inline]
300    fn name(&self) -> &'static str {
301        <P as Pass>::name(self)
302    }
303
304    #[inline]
305    fn argument(&self) -> &'static str {
306        (**self).argument()
307    }
308
309    #[inline]
310    fn description(&self) -> &'static str {
311        (**self).description()
312    }
313
314    #[inline]
315    fn info(&self) -> PassInfo {
316        (**self).info()
317    }
318
319    #[inline]
320    fn target_name(&self, context: &Context) -> Option<OperationName> {
321        (**self).target_name(context)
322    }
323
324    #[inline]
325    fn initialize_options(&mut self, options: &str) -> Result<(), Report> {
326        (**self).initialize_options(options)
327    }
328
329    #[inline]
330    fn print_as_textual_pipeline(&self, f: &mut fmt::Formatter) -> fmt::Result {
331        (**self).print_as_textual_pipeline(f)
332    }
333
334    #[inline]
335    fn has_statistics(&self) -> bool {
336        (**self).has_statistics()
337    }
338
339    #[inline]
340    fn statistics(&self) -> &[Box<dyn Statistic>] {
341        (**self).statistics()
342    }
343
344    #[inline]
345    fn statistics_mut(&mut self) -> &mut [Box<dyn Statistic>] {
346        (**self).statistics_mut()
347    }
348
349    #[inline]
350    fn initialize(&mut self, context: Rc<Context>) -> Result<(), Report> {
351        (**self).initialize(context)
352    }
353
354    #[inline]
355    fn can_schedule_on(&self, name: &OperationName) -> bool {
356        (**self).can_schedule_on(name)
357    }
358
359    #[inline]
360    fn run_on_operation(
361        &mut self,
362        op: EntityMut<'_, Self::Target>,
363        state: &mut PassExecutionState,
364    ) -> Result<(), Report> {
365        (**self).run_on_operation(op, state)
366    }
367
368    #[inline]
369    fn run_pipeline(
370        &mut self,
371        pipeline: &mut OpPassManager,
372        op: OperationRef,
373        state: &mut PassExecutionState,
374    ) -> Result<(), Report> {
375        (**self).run_pipeline(pipeline, op, state)
376    }
377}
378
379pub type DynamicPipelineExecutor =
380    dyn FnMut(&mut OpPassManager, OperationRef) -> Result<(), Report>;
381
382/// The state for a single execution of a pass. This provides a unified
383/// interface for accessing and initializing necessary state for pass execution.
384pub struct PassExecutionState {
385    /// The operation being transformed
386    op: OperationRef,
387    context: Rc<Context>,
388    analysis_manager: AnalysisManager,
389    /// The set of preserved analyses for the current execution
390    preserved_analyses: PreservedAnalyses,
391    // Callback in the pass manager that allows one to schedule dynamic pipelines that will be
392    // rooted at the provided operation.
393    #[allow(unused)]
394    pipeline_executor: Option<Box<DynamicPipelineExecutor>>,
395    post_pass_status: PostPassStatus,
396}
397impl PassExecutionState {
398    pub fn new(
399        op: OperationRef,
400        context: Rc<Context>,
401        analysis_manager: AnalysisManager,
402        pipeline_executor: Option<Box<DynamicPipelineExecutor>>,
403    ) -> Self {
404        Self {
405            op,
406            context,
407            analysis_manager,
408            preserved_analyses: Default::default(),
409            pipeline_executor,
410            post_pass_status: PostPassStatus::Unchanged,
411        }
412    }
413
414    #[inline(always)]
415    pub fn context(&self) -> Rc<Context> {
416        self.context.clone()
417    }
418
419    #[inline(always)]
420    pub const fn current_operation(&self) -> &OperationRef {
421        &self.op
422    }
423
424    #[inline(always)]
425    pub const fn analysis_manager(&self) -> &AnalysisManager {
426        &self.analysis_manager
427    }
428
429    #[inline(always)]
430    pub const fn preserved_analyses(&self) -> &PreservedAnalyses {
431        &self.preserved_analyses
432    }
433
434    #[inline(always)]
435    pub fn preserved_analyses_mut(&mut self) -> &mut PreservedAnalyses {
436        &mut self.preserved_analyses
437    }
438
439    #[inline(always)]
440    pub fn post_pass_status(&self) -> &PostPassStatus {
441        &self.post_pass_status
442    }
443
444    #[inline(always)]
445    pub fn set_post_pass_status(&mut self, post_pass_status: PostPassStatus) {
446        self.post_pass_status = post_pass_status;
447    }
448
449    pub fn run_pipeline(
450        &mut self,
451        pipeline: &mut OpPassManager,
452        op: OperationRef,
453    ) -> Result<(), Report> {
454        if let Some(pipeline_executor) = self.pipeline_executor.as_deref_mut() {
455            pipeline_executor(pipeline, op)
456        } else {
457            Ok(())
458        }
459    }
460}