1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
use midenc_session::Session;

use super::{AnalysisKey, AnalysisManager, PassInfo};
use crate::diagnostics::Report;

/// A convenient type alias for `Result<(), Report>`
pub type RewriteResult = Result<(), Report>;

/// A convenient type alias for closures which can be used as rewrite passes
pub type RewriteFn<T> = dyn FnMut(&mut T, &mut AnalysisManager, &Session) -> RewriteResult;

/// This is a marker trait for [RewritePass] impls which also implement [PassInfo]
///
/// It is automatically implemented for you.
pub trait RewritePassInfo: PassInfo + RewritePass {}
impl<P> RewritePassInfo for P where P: PassInfo + RewritePass {}

/// A [RewritePass] is a pass which transforms/rewrites an entity without converting it to a
/// new representation. For conversions, see [crate::ConversionPass].
///
/// For example, a rewrite rule which applies a mangling scheme to function names, does not
/// change the representation of a function, it simply changes things about the existing
/// representation (e.g. the name in this example).
///
/// A rewrite is given access to the current [AnalysisManager], which can be used to obtain
/// the results of some [Analysis] needed to perform the rewrite, as well as indicate to the
/// [AnalysisManager] which analyses are preserved by the rewrite, if any.
///
/// Additionally, the current [midenc_session::Session] is provided, which can be used as a
/// source of configuration for the rewrite, if needed.
pub trait RewritePass {
    /// The entity type to which this rewrite applies
    type Entity: AnalysisKey;

    /// Returns true if this rewrite should be applied to `entity`
    fn should_apply(&self, _entity: &Self::Entity, _session: &Session) -> bool {
        true
    }

    /// Apply this rewrite to `entity`
    fn apply(
        &mut self,
        entity: &mut Self::Entity,
        analyses: &mut AnalysisManager,
        session: &Session,
    ) -> RewriteResult;

    /// Apply this rewrite, then `next` as a pipeline of rewrites
    fn chain<R>(self, next: R) -> RewriteSet<Self::Entity>
    where
        Self: Sized + 'static,
        R: RewritePass<Entity = Self::Entity> + 'static,
    {
        RewriteSet::pair(self, next)
    }
}

impl<P, T> RewritePass for Box<P>
where
    T: AnalysisKey,
    P: RewritePass<Entity = T>,
{
    type Entity = T;

    fn should_apply(&self, entity: &Self::Entity, session: &Session) -> bool {
        (**self).should_apply(entity, session)
    }

    fn apply(
        &mut self,
        entity: &mut Self::Entity,
        analyses: &mut AnalysisManager,
        session: &Session,
    ) -> RewriteResult {
        (**self).apply(entity, analyses, session)
    }

    fn chain<R>(self, next: R) -> RewriteSet<Self::Entity>
    where
        Self: Sized + 'static,
        R: RewritePass<Entity = Self::Entity> + 'static,
    {
        let mut rewrites = RewriteSet::from(self);
        rewrites.push(next);
        rewrites
    }
}
impl<T> RewritePass for Box<dyn RewritePass<Entity = T>>
where
    T: AnalysisKey,
{
    type Entity = T;

    #[inline]
    fn apply(
        &mut self,
        entity: &mut Self::Entity,
        analyses: &mut AnalysisManager,
        session: &Session,
    ) -> RewriteResult {
        (**self).apply(entity, analyses, session)
    }
}
impl<T> RewritePass for Box<dyn FnMut(&mut T, &mut AnalysisManager, &Session) -> RewriteResult>
where
    T: AnalysisKey,
{
    type Entity = T;

    #[inline]
    fn apply(
        &mut self,
        entity: &mut Self::Entity,
        analyses: &mut AnalysisManager,
        session: &Session,
    ) -> RewriteResult {
        self(entity, analyses, session)
    }
}
impl<T> RewritePass for dyn FnMut(&mut T, &mut AnalysisManager, &Session) -> RewriteResult
where
    T: AnalysisKey,
{
    type Entity = T;

    #[inline]
    fn apply(
        &mut self,
        entity: &mut Self::Entity,
        analyses: &mut AnalysisManager,
        session: &Session,
    ) -> RewriteResult {
        self(entity, analyses, session)
    }
}

/// This type is used to adapt function [RewritePass] to apply against a module.
///
/// When this is applied to a module, all functions in the module will be rewritten.
pub struct ModuleRewritePassAdapter<R>(R);
impl<R> Default for ModuleRewritePassAdapter<R>
where
    R: RewritePass<Entity = crate::Function> + Default,
{
    fn default() -> Self {
        Self(R::default())
    }
}
impl<R> ModuleRewritePassAdapter<R>
where
    R: RewritePass<Entity = crate::Function>,
{
    /// Adapt `R` to run against all functions in a [crate::Module]
    pub const fn new(pass: R) -> Self {
        Self(pass)
    }
}
impl<R: PassInfo> PassInfo for ModuleRewritePassAdapter<R> {
    const DESCRIPTION: &'static str = <R as PassInfo>::DESCRIPTION;
    const FLAG: &'static str = <R as PassInfo>::FLAG;
    const SUMMARY: &'static str = <R as PassInfo>::SUMMARY;
}
impl<R> RewritePass for ModuleRewritePassAdapter<R>
where
    R: RewritePass<Entity = crate::Function>,
{
    type Entity = crate::Module;

    fn apply(
        &mut self,
        module: &mut Self::Entity,
        analyses: &mut AnalysisManager,
        session: &Session,
    ) -> RewriteResult {
        // Removing a function via this cursor will move the cursor to
        // the next function in the module. Once the end of the module
        // is reached, the cursor will point to the null object, and
        // `remove` will return `None`.
        let mut cursor = module.cursor_mut();
        let mut dirty = false;
        while let Some(mut function) = cursor.remove() {
            // Apply rewrite
            if self.0.should_apply(&function, session) {
                dirty = true;
                self.0.apply(&mut function, analyses, session)?;
                analyses.invalidate::<crate::Function>(&function.id);
            }

            // Add the function back to the module
            //
            // We add it before the current position of the cursor
            // to ensure that we don't interfere with our traversal
            // of the module top to bottom
            cursor.insert_before(function);
        }

        if !dirty {
            analyses.mark_all_preserved::<crate::Module>(&module.name);
        }

        Ok(())
    }
}

/// A [RewriteSet] is used to compose two or more [RewritePass] impls for the same entity type,
/// to be applied as a single, fused [RewritePass].
pub struct RewriteSet<T> {
    rewrites: Vec<Box<dyn RewritePass<Entity = T>>>,
}
impl<T> Default for RewriteSet<T> {
    fn default() -> Self {
        Self { rewrites: vec![] }
    }
}
impl<T> RewriteSet<T>
where
    T: AnalysisKey,
{
    /// Create a new [RewriteSet] from a pair of [RewritePass]
    pub fn pair<A, B>(a: A, b: B) -> Self
    where
        A: RewritePass<Entity = T> + 'static,
        B: RewritePass<Entity = T> + 'static,
    {
        Self {
            rewrites: vec![Box::new(a), Box::new(b)],
        }
    }

    /// Append a new [RewritePass] to this set
    pub fn push<R>(&mut self, rewrite: R)
    where
        R: RewritePass<Entity = T> + 'static,
    {
        self.rewrites.push(Box::new(rewrite));
    }

    /// Take all rewrites out of another [RewriteSet], and append them to this set
    pub fn append(&mut self, other: &mut Self) {
        self.rewrites.append(&mut other.rewrites);
    }

    /// Extend this rewrite set with rewrites from `iter`
    pub fn extend(&mut self, iter: impl IntoIterator<Item = Box<dyn RewritePass<Entity = T>>>) {
        self.rewrites.extend(iter);
    }
}
impl<T> IntoIterator for RewriteSet<T>
where
    T: AnalysisKey,
{
    type IntoIter = alloc::vec::IntoIter<Self::Item>;
    type Item = Box<dyn RewritePass<Entity = T>>;

    #[inline]
    fn into_iter(self) -> Self::IntoIter {
        self.rewrites.into_iter()
    }
}
impl<T> From<Box<dyn RewritePass<Entity = T>>> for RewriteSet<T>
where
    T: AnalysisKey,
{
    fn from(rewrite: Box<dyn RewritePass<Entity = T>>) -> Self {
        Self {
            rewrites: vec![rewrite],
        }
    }
}
impl<T, R: RewritePass<Entity = T> + 'static> From<Box<R>> for RewriteSet<T>
where
    T: AnalysisKey,
{
    fn from(rewrite: Box<R>) -> Self {
        Self {
            rewrites: vec![rewrite],
        }
    }
}
impl<T> RewritePass for RewriteSet<T>
where
    T: AnalysisKey,
{
    type Entity = T;

    fn apply(
        &mut self,
        entity: &mut Self::Entity,
        analyses: &mut AnalysisManager,
        session: &Session,
    ) -> RewriteResult {
        for pass in self.rewrites.iter_mut() {
            // Skip the rewrite if it shouldn't be applied
            if !pass.should_apply(entity, session) {
                continue;
            }

            // Apply the rewrite
            pass.apply(entity, analyses, session)?;
            // Invalidate all analyses that were not marked preserved by `pass`
            analyses.invalidate::<T>(&entity.key());
        }

        Ok(())
    }

    fn chain<R>(mut self, next: R) -> RewriteSet<Self::Entity>
    where
        Self: Sized + 'static,
        R: RewritePass<Entity = Self::Entity> + 'static,
    {
        self.push(next);
        self
    }
}

#[doc(hidden)]
pub struct RewritePassRegistration<T> {
    pub name: &'static str,
    pub summary: &'static str,
    pub description: &'static str,
    ctor: fn() -> Box<dyn RewritePass<Entity = T>>,
}
impl<T> RewritePassRegistration<T> {
    pub const fn new<P>() -> Self
    where
        P: RewritePass<Entity = T> + PassInfo + Default + 'static,
    {
        Self {
            name: <P as PassInfo>::FLAG,
            summary: <P as PassInfo>::SUMMARY,
            description: <P as PassInfo>::DESCRIPTION,
            ctor: dyn_rewrite_pass_ctor::<P>,
        }
    }

    /// Get the name of the registered pass
    #[inline]
    pub const fn name(&self) -> &'static str {
        self.name
    }

    /// Get a summary of the registered pass
    #[inline]
    pub const fn summary(&self) -> &'static str {
        self.summary
    }

    /// Get a rich description of the registered pass
    #[inline]
    pub const fn description(&self) -> &'static str {
        self.description
    }

    /// Get an instance of the registered pass
    #[inline]
    pub fn get(&self) -> Box<dyn RewritePass<Entity = T>> {
        (self.ctor)()
    }
}

fn dyn_rewrite_pass_ctor<P>() -> Box<dyn RewritePass<Entity = <P as RewritePass>::Entity>>
where
    P: RewritePass + Default + 'static,
{
    Box::<P>::default()
}

// Register rewrite passes for modules
inventory::collect!(RewritePassRegistration<crate::Module>);

// Register rewrite passes for functions
inventory::collect!(RewritePassRegistration<crate::Function>);