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 #[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 #[arg(long = "unstable-mir-randomize-passes", global = true)]
34 pub randomize_mir_passes: bool,
35 #[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
52pub 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#[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 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}