Skip to main content

device_driver_mir/
lib.rs

1use std::{collections::HashSet, num::NonZero, time::Duration};
2
3use clap::Parser;
4use device_driver_common::{
5    identifier::{IdentifierRef, IdentifierType},
6    span::{Span, SpanExt},
7    specifiers::{Repeat, RepeatSource},
8};
9use device_driver_diagnostics::{Diagnostics, DynError};
10use device_driver_parser::Ast;
11
12use crate::model::{Device, LendingIterator, Manifest, Object, Unique, UniqueId};
13
14mod lowering;
15pub mod model;
16pub(crate) mod passes;
17
18#[cfg(feature = "gen-docs")]
19pub use lowering::gen_docs::gen_docs;
20
21#[derive(Parser, Debug, Clone, Default)]
22#[command(no_binary_name = true)]
23pub struct MirOptions {
24    /// The seed to use for randomization. If not specified, a random seed is used
25    #[arg(
26        long = "unstable-mir-randomize-seed",
27        require_equals = true,
28        global = true,
29        value_name = "SEED"
30    )]
31    pub randomize_mir_passes_seed: Option<u64>,
32    /// Randomize the order of the mir passes
33    #[arg(long = "unstable-mir-randomize-passes", global = true)]
34    pub randomize_mir_passes: bool,
35    /// Run assumption checks for the passes
36    #[arg(long = "unstable-mir-check-assumptions", global = true)]
37    pub check_assumptions: bool,
38}
39
40pub fn lower_ast(
41    ast: Ast,
42    options: &MirOptions,
43    diagnostics: &mut Diagnostics,
44) -> Result<(model::Manifest, Vec<PassTiming>), DynError> {
45    let mut mir = lowering::lower(ast, diagnostics);
46
47    let pass_timings = passes::run_passes(&mut mir, options, diagnostics)?;
48
49    Ok((mir, pass_timings))
50}
51
52/// This assumes [passes::Assumption::NamesUnique]
53pub fn search_object<'o, T: IdentifierType>(
54    manifest: &'o Manifest,
55    name: &IdentifierRef<T>,
56) -> Option<&'o Object> {
57    manifest.iter_objects().find(|o| name.is_ref_to(o.name()))
58}
59
60/// Returns None if device has no objects that pass the filter
61///
62/// This assumes [passes::Assumption::RepeatStrideNonZero], [passes::Assumption::NamesUnique] & [passes::Assumption::RepeatEnumRefValid]
63#[expect(clippy::type_complexity, reason = "I disagree")]
64pub fn find_min_max_addresses<'m>(
65    manifest: &'m Manifest,
66    device: &'m Device,
67    filter: impl Fn(&'m Object) -> bool,
68) -> Option<((i128, &'m Object), (i128, &'m Object))> {
69    let mut min_address_found = i128::MAX;
70    let mut min_obj_found = None;
71    let mut max_address_found = i128::MIN;
72    let mut max_obj_found = None;
73
74    let mut children_left = vec![device.objects.len()];
75    let mut address_offsets = vec![0];
76
77    for object in device.iter_objects() {
78        while children_left.last() == Some(&0) {
79            children_left.pop();
80            address_offsets.pop();
81        }
82
83        *children_left.last_mut().unwrap() -= 1;
84
85        if !filter(object) {
86            continue;
87        }
88
89        if let Some(address) = object.address() {
90            let repeat = object.repeat().cloned().unwrap_or(Repeat {
91                source: RepeatSource::Count(NonZero::new(1).unwrap()).with_dummy_span(),
92                stride: 0.with_dummy_span(),
93                span: Span::empty(),
94            });
95
96            let total_address_offsets = address_offsets.iter().sum::<i128>();
97
98            match repeat.source.value {
99                RepeatSource::Count(count) => {
100                    let count_0_address = total_address_offsets + address.value;
101                    let count_max_address = count_0_address
102                        + (i128::from(count.get().saturating_sub(1)) * repeat.stride.value);
103                    let min_address = count_0_address.min(count_max_address);
104                    let max_address = count_0_address.max(count_max_address);
105
106                    if min_address < min_address_found {
107                        min_address_found = min_address;
108                        min_obj_found = Some(object);
109                    }
110
111                    if max_address > max_address_found {
112                        max_address_found = max_address;
113                        max_obj_found = Some(object);
114                    }
115                }
116                RepeatSource::Enum(enum_name) => {
117                    let enum_value = search_object(manifest, &enum_name)
118                        .expect("A mir pass checked this enum exists")
119                        .as_enum()
120                        .expect("A mir pass checked this is an enum");
121
122                    for (discriminant, _) in enum_value.iter_variants_with_discriminant() {
123                        let address = total_address_offsets
124                            + address.value
125                            + (discriminant * repeat.stride.value);
126                        if address < min_address_found {
127                            min_address_found = address;
128                            min_obj_found = Some(object);
129                        }
130
131                        if address > max_address_found {
132                            max_address_found = address;
133                            max_obj_found = Some(object);
134                        }
135                    }
136                }
137            }
138        }
139
140        match object {
141            Object::Device(d) => {
142                address_offsets.push(0);
143                children_left.push(d.objects.len());
144            }
145            Object::Block(b) => {
146                address_offsets.push(b.address_offset.value);
147                children_left.push(b.objects.len());
148            }
149            _ => (),
150        }
151    }
152
153    Some((
154        (min_address_found, min_obj_found?),
155        (max_address_found, max_obj_found?),
156    ))
157}
158
159fn remove_objects(manifest: &mut Manifest, mut removals: HashSet<UniqueId>) {
160    fn try_remove_from_vec(objects: &mut Vec<Object>, removals: &mut HashSet<UniqueId>) {
161        removals.retain(|removal| {
162            if let Some((index, _)) = objects
163                .iter()
164                .enumerate()
165                .find(|(_, obj)| obj.has_id(removal))
166            {
167                objects.remove(index);
168                false
169            } else {
170                // Find a field
171                for fs in objects.iter_mut().filter_map(|o| o.as_field_set_mut()) {
172                    let fs_id = fs.id();
173                    for field_index in 0..fs.fields.len() {
174                        if fs.fields[field_index].has_id_with(fs_id.clone(), removal) {
175                            fs.fields.remove(field_index);
176                            return false;
177                        }
178                    }
179                }
180
181                true
182            }
183        });
184    }
185
186    if removals.is_empty() {
187        return;
188    }
189
190    try_remove_from_vec(&mut manifest.objects, &mut removals);
191
192    if removals.is_empty() {
193        return;
194    }
195
196    let mut iter = manifest.iter_objects_with_config_mut();
197    while let Some((object, _)) = iter.next() {
198        let Some(child_objects) = object.child_objects_vec() else {
199            continue;
200        };
201
202        try_remove_from_vec(child_objects, &mut removals);
203
204        if removals.is_empty() {
205            return;
206        }
207    }
208}
209
210#[derive(Debug)]
211pub struct PassTiming {
212    pub name: String,
213    pub duration: Duration,
214}