Skip to main content

device_driver_mir/
lib.rs

1use std::{collections::VecDeque, num::NonZero, time::Duration};
2
3use clap::Parser;
4use device_driver_common::{
5    span::{Span, SpanExt},
6    specifiers::{Repeat, RepeatSource},
7};
8use device_driver_diagnostics::{Diagnostics, DynError};
9use device_driver_parser::Ast;
10
11use crate::model::{Device, Manifest, Object};
12
13mod lowering;
14pub mod model;
15pub(crate) mod passes;
16pub mod runtime;
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/// Returns None if device has no objects that pass the filter
53///
54/// This assumes [passes::Assumption::RepeatStrideNonZero], [passes::Assumption::NamesUnique] & [passes::Assumption::RepeatEnumRefValid]
55pub fn find_min_max_addresses<'m>(
56    manifest: &'m Manifest,
57    device: &'m Device,
58    filter: impl Fn(Object<'m>) -> bool,
59) -> Option<((i128, Object<'m>), (i128, Object<'m>))> {
60    let mut min_address_found = i128::MAX;
61    let mut min_obj_found = None;
62    let mut max_address_found = i128::MIN;
63    let mut max_obj_found = None;
64
65    let mut children_left = vec![device.children.len()];
66    let mut address_offsets = vec![device.address_offset.value];
67
68    let mut children_queue = VecDeque::from(device.children.clone());
69
70    while let Some(object) = children_queue.pop_front() {
71        let object = manifest.object(object).unwrap();
72
73        while children_left.last() == Some(&0) {
74            children_left.pop();
75            address_offsets.pop();
76        }
77
78        *children_left.last_mut().unwrap() -= 1;
79
80        if !filter(object) {
81            continue;
82        }
83
84        if let Some(address) = object.address() {
85            let repeat = object.repeat().cloned().unwrap_or(Repeat {
86                source: RepeatSource::Count(NonZero::new(1).unwrap()).with_dummy_span(),
87                stride: 0.with_dummy_span(),
88                span: Span::empty(),
89            });
90
91            let total_address_offsets = address_offsets.iter().sum::<i128>();
92
93            match repeat.source.value {
94                RepeatSource::Count(count) => {
95                    let count_0_address = total_address_offsets + address.value;
96                    let count_max_address = count_0_address
97                        + (i128::from(count.get().saturating_sub(1)) * repeat.stride.value);
98                    let min_address = count_0_address.min(count_max_address);
99                    let max_address = count_0_address.max(count_max_address);
100
101                    if min_address < min_address_found {
102                        min_address_found = min_address;
103                        min_obj_found = Some(object);
104                    }
105
106                    if max_address > max_address_found {
107                        max_address_found = max_address;
108                        max_obj_found = Some(object);
109                    }
110                }
111                RepeatSource::Enum(enum_name) => {
112                    let enum_value = manifest
113                        .search_object(&enum_name)
114                        .expect("A mir pass checked this enum exists")
115                        .as_enum()
116                        .expect("A mir pass checked this is an enum");
117
118                    for (discriminant, _) in
119                        enum_value.iter_variants_with_discriminant(&manifest.enum_variants)
120                    {
121                        let address = total_address_offsets
122                            + address.value
123                            + (discriminant * repeat.stride.value);
124                        if address < min_address_found {
125                            min_address_found = address;
126                            min_obj_found = Some(object);
127                        }
128
129                        if address > max_address_found {
130                            max_address_found = address;
131                            max_obj_found = Some(object);
132                        }
133                    }
134                }
135            }
136        }
137
138        match object {
139            Object::Device(d) => {
140                address_offsets.push(d.address_offset.value);
141                children_left.push(d.children.len());
142                for sub_child in d.children.iter().rev() {
143                    children_queue.push_front(*sub_child);
144                }
145            }
146            Object::Block(b) => {
147                address_offsets.push(b.address_offset.value);
148                children_left.push(b.children.len());
149                for sub_child in b.children.iter().rev() {
150                    children_queue.push_front(*sub_child);
151                }
152            }
153            _ => (),
154        }
155    }
156
157    Some((
158        (min_address_found, min_obj_found?),
159        (max_address_found, max_obj_found?),
160    ))
161}
162
163#[derive(Debug)]
164pub struct PassTiming {
165    pub name: String,
166    pub duration: Duration,
167}