1use std::{
4 collections::{HashMap, HashSet},
5 hash::Hash,
6 time::Duration,
7};
8
9use expressions::Expr;
10pub use lowering::livetime_equivalences::LivetimeEquivalences;
11use memory::{Memory, StreamMemory};
12use rtlola_frontend::mir::{self};
13use windows::Window;
14mod display;
15pub mod expressions;
16mod lowering;
17pub mod memory;
18#[cfg(test)]
19pub(crate) mod parse;
20mod print;
21mod schedule;
22pub mod windows;
23pub use lowering::LoweringError;
24pub use print::DebugFormatter;
25pub use schedule::{Deadline, StaticSchedule, Task};
26
27#[derive(Debug, Clone)]
28pub struct StreamIr {
30 pub stmt: Stmt,
32 pub sr2memory: HashMap<StreamReference, Memory>,
34 pub wref2window: HashMap<WindowReference, Window>,
36 pub lref2lfreq: HashMap<LocalFreqRef, LocalFreq>,
38 pub livetime_equivalences: LivetimeEquivalences,
40 pub static_schedule: Option<StaticSchedule>,
42 pub triggers: HashMap<OutputReference, usize>,
44 pub accesses: HashMap<StreamReference, Accesses>,
46 pub accessed_by: HashMap<StreamReference, Accesses>,
48}
49
50impl StreamIr {
51 pub fn stream_memory(&self, sr: StreamReference) -> &Memory {
53 &self.sr2memory[&sr]
54 }
55
56 pub fn name(&self, sr: StreamReference) -> &str {
58 &self.stream_memory(sr).name
59 }
60
61 pub fn stream_by_name(&self, name: &str) -> Option<StreamReference> {
63 self.sr2memory
64 .iter()
65 .find(|(_, m)| m.name == name)
66 .map(|(sr, _)| sr)
67 .copied()
68 }
69
70 pub fn streams(&self) -> impl Iterator<Item = StreamReference> + '_ {
72 self.sr2memory.keys().copied()
73 }
74
75 pub fn inputs(&self) -> impl Iterator<Item = InputReference> + '_ {
77 self.sr2memory
78 .keys()
79 .filter(|sr| matches!(sr, StreamReference::In(_)))
80 .map(|sr| sr.in_idx())
81 }
82
83 pub fn num_inputs(&self) -> usize {
85 self.inputs().count()
86 }
87
88 pub fn outputs(&self) -> impl Iterator<Item = OutputReference> + '_ {
90 self.sr2memory
91 .keys()
92 .filter(|sr| matches!(sr, StreamReference::Out(_)))
93 .map(|sr| sr.out_idx())
94 }
95
96 pub fn triggers(&self) -> impl Iterator<Item = OutputReference> + '_ {
98 self.outputs().filter(|o| self.triggers.contains_key(o))
99 }
100
101 pub fn num_outputs(&self) -> usize {
103 self.outputs().count()
104 }
105
106 pub fn static_outputs(&self) -> impl Iterator<Item = OutputReference> + '_ {
108 self.outputs().filter(|sr| {
109 matches!(
110 self.stream_memory(sr.sr()).buffer,
111 StreamMemory::Static(_) | StreamMemory::NoMemory
112 )
113 })
114 }
115
116 pub fn dynamic_outputs(&self) -> impl Iterator<Item = OutputReference> + '_ {
118 self.outputs().filter(|sr| {
119 matches!(
120 self.stream_memory(sr.sr()).buffer,
121 StreamMemory::Dynamic { .. }
122 )
123 })
124 }
125
126 pub fn parameterized_outputs(&self) -> impl Iterator<Item = OutputReference> + '_ {
128 self.outputs().filter(|sr| {
129 matches!(
130 self.stream_memory(sr.sr()).buffer,
131 StreamMemory::Instances { .. }
132 )
133 })
134 }
135}
136
137#[derive(Debug, Clone, PartialEq, Eq)]
138pub enum Stmt {
140 Skip,
142 Seq(Vec<Stmt>),
144 Parallel(Vec<Stmt>),
146 Shift(StreamReference),
148 Input(InputReference),
150 Spawn {
152 sr: OutputReference,
154 with: Option<Vec<Expr>>,
156 local_frequencies: Vec<LocalFreqRef>,
158 windows: Vec<WindowReference>,
160 },
161 Eval {
163 sr: OutputReference,
165 with: Expr,
167 idx: usize,
169 },
170 Close {
173 sr: OutputReference,
175 local_frequencies: Vec<LocalFreqRef>,
177 windows: Vec<WindowReference>,
179 },
180 If(IfStmt),
182 Iterate {
184 sr: Vec<OutputReference>,
186 stmt: Box<Stmt>,
188 },
189 Assign {
191 parameter_expr: Vec<Expr>,
193 sr: Vec<OutputReference>,
195 stmt: Box<Stmt>,
197 },
198}
199
200#[derive(Debug, Clone, PartialEq, Eq)]
201pub struct IfStmt {
203 pub(crate) guard: Guard,
205 pub(crate) cons: Box<Stmt>,
207 pub(crate) alt: Box<Stmt>,
209}
210
211impl IfStmt {
212 pub fn guard(&self) -> &Guard {
214 &self.guard
215 }
216
217 pub fn cons(&self) -> &Stmt {
219 &self.cons
220 }
221
222 pub fn alt(&self) -> Option<&Stmt> {
224 (!matches!(*self.alt, Stmt::Skip)).then_some(self.alt.as_ref())
225 }
226
227 pub fn destruct(self) -> (Guard, Stmt, Option<Stmt>) {
229 let IfStmt { guard, cons, alt } = self;
230 (guard, *cons, (!matches!(*alt, Stmt::Skip)).then_some(*alt))
231 }
232}
233
234#[derive(Debug, Clone, PartialEq, Eq)]
235pub enum Guard {
237 Stream(StreamReference),
239 Alive(StreamReference),
241 Dynamic(Expr),
243 GlobalFreq(Duration),
245 LocalFreq(LocalFreqRef),
247 And {
249 lhs: Box<Guard>,
251 rhs: Box<Guard>,
253 },
254 Or {
256 lhs: Box<Guard>,
258 rhs: Box<Guard>,
260 },
261 Constant(bool),
263 FastAnd(Vec<StreamReference>),
265 FastOr(Vec<StreamReference>),
267}
268
269impl Guard {
270 pub(crate) fn eq_liveness(
271 &self,
272 other: &Self,
273 livetime_equivalences: &LivetimeEquivalences,
274 ) -> bool {
275 match (self, other) {
276 (Self::Stream(l0), Self::Stream(r0)) => l0 == r0,
277 (Self::Alive(l0), Self::Alive(r0)) => livetime_equivalences.is_equivalent(*l0, *r0),
278 (Self::Dynamic(l0), Self::Dynamic(r0)) => l0 == r0,
279 (Self::GlobalFreq(l0), Self::GlobalFreq(r0)) => l0 == r0,
280 (Self::LocalFreq(l0), Self::LocalFreq(r0)) => l0 == r0,
281 (
282 Self::And {
283 lhs: l_lhs,
284 rhs: l_rhs,
285 },
286 Self::And {
287 lhs: r_lhs,
288 rhs: r_rhs,
289 },
290 ) => {
291 l_lhs.eq_liveness(r_lhs, livetime_equivalences)
292 && l_rhs.eq_liveness(r_rhs, livetime_equivalences)
293 }
294 (
295 Self::Or {
296 lhs: l_lhs,
297 rhs: l_rhs,
298 },
299 Self::Or {
300 lhs: r_lhs,
301 rhs: r_rhs,
302 },
303 ) => {
304 l_lhs.eq_liveness(r_lhs, livetime_equivalences)
305 && l_rhs.eq_liveness(r_rhs, livetime_equivalences)
306 }
307 (Self::Constant(l0), Self::Constant(r0)) => l0 == r0,
308 _ => false,
309 }
310 }
311}
312
313impl Hash for Guard {
314 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
315 match self {
316 Guard::Stream(sr) => {
317 state.write_u8(1);
318 sr.hash(state);
319 }
320 Guard::Alive(sr) => {
321 state.write_u8(2);
322 sr.hash(state);
323 }
324 Guard::Dynamic(_) => {
325 state.write_u8(3);
326 }
327 Guard::GlobalFreq(duration) => {
328 state.write_u8(4);
329 duration.hash(state);
330 }
331 Guard::LocalFreq(f) => {
332 state.write_u8(5);
333 f.hash(state);
334 }
335 Guard::And { lhs, rhs } => {
336 state.write_u8(6);
337 lhs.hash(state);
338 rhs.hash(state);
339 }
340 Guard::Or { lhs, rhs } => {
341 state.write_u8(7);
342 lhs.hash(state);
343 rhs.hash(state);
344 }
345 Guard::Constant(c) => {
346 state.write_u8(8);
347 c.hash(state);
348 }
349 Guard::FastAnd(sr) => {
350 state.write_u8(9);
351 sr.hash(state);
352 }
353 Guard::FastOr(sr) => {
354 state.write_u8(10);
355 sr.hash(state);
356 }
357 }
358 }
359}
360
361pub type LocalFreqRef = usize;
363
364#[derive(Debug, Clone, Copy)]
365pub struct LocalFreq {
367 pub dur: Duration,
369 pub sr: OutputReference,
371 pub reference: LocalFreqRef,
373}
374
375impl PartialEq for LocalFreq {
376 fn eq(&self, other: &Self) -> bool {
377 self.dur == other.dur && self.sr == other.sr
378 }
379}
380
381impl Eq for LocalFreq {}
382
383pub type InputReference = usize;
385
386#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
388pub enum OutputReference {
389 Unparameterized(usize),
391 Parameterized(usize),
393}
394
395impl OutputReference {
396 pub fn unparameterized_idx(self) -> usize {
398 match self {
399 OutputReference::Parameterized(_) => unreachable!(),
400 OutputReference::Unparameterized(i) => i,
401 }
402 }
403
404 pub fn parameterized_idx(self) -> usize {
406 match self {
407 OutputReference::Unparameterized(_) => unreachable!(),
408 OutputReference::Parameterized(i) => i,
409 }
410 }
411
412 pub fn sr(self) -> StreamReference {
414 StreamReference::Out(self)
415 }
416}
417
418#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
419pub enum StreamReference {
421 In(InputReference),
423 Out(OutputReference),
425}
426
427impl StreamReference {
428 pub fn in_idx(self) -> InputReference {
430 match self {
431 StreamReference::In(i) => i,
432 StreamReference::Out(_) => unreachable!("Called in_idx on an Outputstream"),
433 }
434 }
435
436 pub fn out_idx(self) -> OutputReference {
438 match self {
439 StreamReference::In(_) => unreachable!("Called out_idx on an Inputstream"),
440 StreamReference::Out(o) => o,
441 }
442 }
443}
444
445#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
446pub enum Origin {
448 Spawn,
450 EvalWhen(usize),
452 EvalWith(usize),
454 Close,
456}
457
458impl From<mir::Origin> for Origin {
459 fn from(value: mir::Origin) -> Self {
460 match value {
461 mir::Origin::Spawn => Origin::Spawn,
462 mir::Origin::Filter(clause) => Origin::EvalWhen(clause),
463 mir::Origin::Eval(clause) => Origin::EvalWith(clause),
464 mir::Origin::Close => Origin::Close,
465 }
466 }
467}
468
469#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
470pub enum WindowReference {
472 Sliding(usize),
474 Discrete(usize),
476 Instance(usize),
478}
479
480impl WindowReference {
481 pub fn idx(self) -> usize {
483 match self {
484 WindowReference::Sliding(i)
485 | WindowReference::Discrete(i)
486 | WindowReference::Instance(i) => i,
487 }
488 }
489}
490
491#[derive(Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
493pub enum Type {
494 Int(u16),
496 UInt(u16),
498 Bool,
500 String,
502 Float32,
504 Float64,
506 Fixed(u16),
508 UFixed(u16),
510 Option(Box<Type>),
512 Tuple(Vec<Type>),
514 Bytes,
516}
517
518impl Type {
519 pub fn inner_ty(&self) -> &Type {
521 if let Type::Option(inner) = self {
522 inner
523 } else {
524 self
525 }
526 }
527}
528
529impl StreamIr {
530 pub fn all_periodic_pacings(&self) -> (Vec<Duration>, Vec<&LocalFreq>) {
535 let global_freqs = self.stmt.all_global_freqs().into_iter().collect();
536 let local_freqs = self.lref2lfreq.values().collect();
537 (global_freqs, local_freqs)
538 }
539}
540
541impl Stmt {
542 fn all_global_freqs(&self) -> HashSet<Duration> {
543 match self {
544 Stmt::Skip
545 | Stmt::Shift(_)
546 | Stmt::Input(_)
547 | Stmt::Spawn { .. }
548 | Stmt::Eval { .. }
549 | Stmt::Close { .. } => HashSet::new(),
550 Stmt::Parallel(stmts) | Stmt::Seq(stmts) => {
551 stmts.iter().flat_map(|s| s.all_global_freqs()).collect()
552 }
553 Stmt::Iterate { stmt, .. } | Stmt::Assign { stmt, .. } => stmt.all_global_freqs(),
554 Stmt::If(IfStmt { guard, cons, alt }) => cons
555 .all_global_freqs()
556 .into_iter()
557 .chain(alt.all_global_freqs())
558 .chain(guard.all_global_freqs())
559 .collect(),
560 }
561 }
562}
563
564impl Guard {
565 fn all_global_freqs(&self) -> HashSet<Duration> {
566 match self {
567 Guard::GlobalFreq(duration) => vec![*duration].into_iter().collect(),
568 Guard::And { lhs, rhs } | Guard::Or { lhs, rhs } => lhs
569 .all_global_freqs()
570 .into_iter()
571 .chain(rhs.all_global_freqs())
572 .collect(),
573 Guard::Constant(_)
574 | Guard::Stream(_)
575 | Guard::LocalFreq(_)
576 | Guard::Alive(_)
577 | Guard::Dynamic(_)
578 | Guard::FastAnd(_)
579 | Guard::FastOr(_) => HashSet::new(),
580 }
581 }
582}
583
584pub type Accesses = Vec<(StreamReference, Vec<(Origin, StreamAccessKind)>)>;
586
587#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
589pub enum StreamAccessKind {
590 Sync,
592 DiscreteWindow(WindowReference),
596 SlidingWindow(WindowReference),
600 InstanceAggregation(WindowReference),
604 Hold,
606 Offset(Offset),
610 Get,
612 Fresh,
614}
615
616#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
618pub enum Offset {
619 Future(u32),
621 Past(u32),
623}
624
625impl PartialOrd for Offset {
626 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
627 Some(self.cmp(other))
628 }
629}
630
631impl Ord for Offset {
632 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
633 use std::cmp::Ordering;
634
635 use Offset::*;
636 match (self, other) {
637 (Past(_), Future(_)) => Ordering::Less,
638 (Future(_), Past(_)) => Ordering::Greater,
639 (Future(a), Future(b)) => a.cmp(b),
640 (Past(a), Past(b)) => b.cmp(a),
641 }
642 }
643}
644
645impl Stmt {
646 pub fn contains_interate(&self, sr: OutputReference) -> bool {
648 match self {
649 Stmt::Skip
650 | Stmt::Shift(_)
651 | Stmt::Input(_)
652 | Stmt::Spawn { .. }
653 | Stmt::Close { .. }
654 | Stmt::Eval { .. } => false,
655 Stmt::Parallel(stmts) | Stmt::Seq(stmts) => {
656 stmts.iter().any(|s| s.contains_interate(sr))
657 }
658 Stmt::Iterate { sr: srs, stmt } => srs.contains(&sr) || stmt.contains_interate(sr),
659 Stmt::If(IfStmt {
660 guard: _,
661 cons,
662 alt,
663 }) => cons.contains_interate(sr) || alt.contains_interate(sr),
664 Stmt::Assign { stmt, .. } => stmt.contains_interate(sr),
665 }
666 }
667}