1use std::{collections::HashSet, num::NonZero, time::Duration};
2
3use clap::Parser;
4use device_driver_common::{
5 identifier::{IdentifierRef, Namespace},
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, Id, LendingIterator, Manifest, Object, ObjectId};
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: Namespace>(
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![device.address_offset.value];
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(d.address_offset.value);
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(
160 manifest: &mut Manifest,
161 mut removals: HashSet<ObjectId>,
162) -> Result<(), DynError> {
163 fn try_remove_from_vec(objects: &mut Vec<Object>, removals: &mut HashSet<ObjectId>) {
164 removals.retain(|removal| {
165 if let Some((index, _)) = objects
166 .iter()
167 .enumerate()
168 .find(|(_, obj)| obj.has_id(removal))
169 {
170 objects.remove(index);
171 false
172 } else {
173 for fs in objects.iter_mut().filter_map(|o| o.as_field_set_mut()) {
175 for index in 0..fs.fields.len() {
176 if fs.fields[index].has_id(removal) {
177 fs.fields.remove(index);
178 return false;
179 }
180 }
181 }
182 for enum_value in objects.iter_mut().filter_map(|o| o.as_enum_mut()) {
184 for index in 0..enum_value.variants.len() {
185 if enum_value.variants[index].has_id(removal) {
186 enum_value.variants.remove(index);
187 return false;
188 }
189 }
190 }
191
192 true
193 }
194 });
195 }
196
197 if removals.is_empty() {
198 return Ok(());
199 }
200
201 for removal in removals.iter() {
202 if !removal.identifier().is_valid() {
203 return Err(DynError::new(format!("removal {} is invalid", removal)));
204 }
205 }
206
207 try_remove_from_vec(&mut manifest.objects, &mut removals);
208
209 if removals.is_empty() {
210 return Ok(());
211 }
212
213 let mut iter = manifest.iter_objects_with_config_mut();
214 while let Some((object, _)) = iter.next() {
215 let Some(child_objects) = object.child_objects_vec() else {
216 continue;
217 };
218
219 try_remove_from_vec(child_objects, &mut removals);
220
221 if removals.is_empty() {
222 return Ok(());
223 }
224 }
225
226 Ok(())
227}
228
229#[derive(Debug)]
230pub struct PassTiming {
231 pub name: String,
232 pub duration: Duration,
233}