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 #[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 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}