cmtir/ir/
rule.rs

1use std::collections::HashMap;
2
3use super::*;
4
5/// Rule signature in `cmtir`.
6/// `always` rules have inputs and outputs
7/// `method` rules have inputs, outputs, and side effect
8#[derive(Debug, Clone, SExpr)]
9pub enum RuleSignature {
10  #[pp(surrounded)]
11  Always {
12    inputs: Vec<ValueId>,
13    outputs: Vec<ValueId>,
14  },
15  Method {
16    inputs: Vec<ValueId>,
17    outputs: Vec<ValueId>,
18    side_effect: Option<bool>,
19  },
20}
21
22impl RuleSignature {
23  /// Get all inputs of the rule.
24  pub fn inputs(&self) -> impl Iterator<Item = ValueId> {
25    match self {
26      RuleSignature::Always { inputs, .. } => inputs.clone().into_iter(),
27      RuleSignature::Method { inputs, .. } => inputs.clone().into_iter(),
28    }
29  }
30  /// Get all inputs of the rule (mut).
31  pub fn inputs_mut(&mut self) -> &mut Vec<ValueId> {
32    match self {
33      RuleSignature::Always { inputs, .. } => inputs,
34      RuleSignature::Method { inputs, .. } => inputs,
35    }
36  }
37  /// Get all outputs of the rule.
38  pub fn outputs(&self) -> impl Iterator<Item = ValueId> {
39    match self {
40      RuleSignature::Always { outputs, .. } => outputs.clone().into_iter(),
41      RuleSignature::Method { outputs, .. } => outputs.clone().into_iter(),
42    }
43  }
44  /// Get all outputs of the rule (mut).
45  pub fn outputs_mut(&mut self) -> &mut Vec<ValueId> {
46    match self {
47      RuleSignature::Always { outputs, .. } => outputs,
48      RuleSignature::Method { outputs, .. } => outputs,
49    }
50  }
51
52  pub fn is_method(&self) -> bool {
53    matches!(self, RuleSignature::Method { .. })
54  }
55}
56
57/// Rule timing in `cmtir`.
58/// Currently, only single-cycle/FSM/pipeline rules are supported.
59/// **multi-cycle rules are not supported yet.**
60#[derive(Debug, Clone, SExpr)]
61pub enum RuleTiming {
62  #[pp(surrounded)]
63  SingleCycle,
64  MultiCycle {
65    // Some(x) means latency-sensitive (x
66    // cycles), None means latency-insensitive
67    num_cycles: Option<u32>,
68    intv: TimingIntv,
69  },
70  FSM,
71  Pipeline,
72}
73
74impl RuleTiming {
75  pub fn interval(&self) -> TimingIntv {
76    match self {
77      RuleTiming::SingleCycle => TimingIntv::none(),
78      RuleTiming::MultiCycle {
79        num_cycles: _,
80        intv,
81      } => intv.clone(),
82      _ => unimplemented!(),
83    }
84  }
85}
86
87/// Rule in `cmtir`.
88/// ((ext? <is_ext>) (private? <is_private>) rule "<name>" (<signature>)
89/// (<timing>) <enable> <ready> (<guard_ops>) (<ops>)) <annotations>
90#[derive(Debug, Clone, SExpr)]
91pub struct Rule {
92  #[pp(open)]
93  #[pp(surrounded = "ext?")]
94  pub is_ext: bool,
95  #[pp(surrounded = "private?")]
96  pub is_private: bool,
97  #[pp(kw = "rule")]
98  pub name: String,
99  pub signature: RuleSignature,
100  pub timing: RuleTiming,
101  pub enable: Option<String>,
102  pub ready: Option<String>,
103  #[pp(list_ml)]
104  pub guard_ops: Vec<Op>,
105  #[pp(list_ml)]
106  #[pp(close)]
107  pub ops: Vec<Op>,
108  pub annotations: json::object::Object,
109}
110
111impl Rule {
112  /// Check if the rule is external.
113  pub fn is_ext(&self) -> bool {
114    self.is_ext
115  }
116
117  /// Check if the rule is private.
118  pub fn is_private(&self) -> bool {
119    self.is_private
120  }
121
122  /// Set the rule to private.
123  pub fn set_private(&mut self, is_private: bool) {
124    self.is_private = is_private;
125  }
126
127  /// Check if the rule is always.
128  pub fn is_always(&self) -> bool {
129    matches!(self.signature, RuleSignature::Always { .. })
130  }
131
132  /// Check if the rule is method.
133  pub fn is_method(&self) -> bool {
134    matches!(self.signature, RuleSignature::Method { .. })
135  }
136
137  /// Check if the rule has side effect.
138  pub fn has_side_effect(&self) -> bool {
139    matches!(self.signature, RuleSignature::Method { side_effect, .. } if side_effect.unwrap_or(false))
140  }
141
142  /// Set the rule to have side effect.
143  pub fn set_side_effect(&mut self, se: bool) {
144    if let RuleSignature::Method { side_effect, .. } = &mut self.signature {
145      *side_effect = Some(se);
146    }
147  }
148
149  /// Check if the rule is single-cycle.
150  pub fn is_single_cycle(&self) -> bool {
151    matches!(self.timing, RuleTiming::SingleCycle)
152  }
153
154  /// Get all guard operations of the rule.
155  pub fn guard(&self) -> impl Iterator<Item = &Op> {
156    self.guard_ops.iter()
157  }
158
159  /// Get all guard operations of the rule (mut).
160  pub fn guard_mut(&mut self) -> impl Iterator<Item = &mut Op> {
161    self.guard_ops.iter_mut()
162  }
163
164  /// Get all operations of the rule.
165  pub fn ops(&self) -> impl Iterator<Item = &Op> {
166    self.ops.iter()
167  }
168
169  /// Get all operations of the rule (mut).
170  pub fn ops_mut(&mut self) -> impl Iterator<Item = &mut Op> {
171    self.ops.iter_mut()
172  }
173
174  /// Get the name of the rule.
175  pub fn name(&self) -> &str {
176    &self.name
177  }
178
179  /// Create a new rule.
180  pub fn new(name: String) -> Rule {
181    Rule {
182      is_ext: false,
183      is_private: false,
184      name,
185      signature: RuleSignature::Always {
186        inputs: vec![],
187        outputs: vec![],
188      },
189      timing: RuleTiming::SingleCycle,
190      guard_ops: vec![],
191      ops: vec![],
192      enable: None,
193      ready: None,
194      annotations: json::object::Object::new(),
195    }
196  }
197
198  /// Create a new always rule.
199  pub fn always(
200    name: String,
201    inputs: Vec<ValueId>,
202    outputs: Vec<ValueId>,
203    guard_ops: Vec<Op>,
204    ops: Vec<Op>,
205    timing: RuleTiming,
206  ) -> Rule {
207    Rule {
208      is_ext: false,
209      is_private: false,
210      name,
211      signature: RuleSignature::Always { inputs, outputs },
212      guard_ops,
213      timing,
214      ops,
215      enable: None,
216      ready: None,
217      annotations: json::object::Object::new(),
218    }
219  }
220
221  /// Create a new method rule.
222  pub fn method(
223    name: String,
224    inputs: Vec<ValueId>,
225    outputs: Vec<ValueId>,
226    side_effect: Option<bool>,
227    guard_ops: Vec<Op>,
228    ops: Vec<Op>,
229    timing: RuleTiming,
230  ) -> Rule {
231    Rule {
232      is_ext: false,
233      is_private: false,
234      name,
235      signature: RuleSignature::Method {
236        inputs,
237        outputs,
238        side_effect,
239      },
240      timing,
241      guard_ops,
242      ops,
243      enable: None,
244      ready: None,
245      annotations: json::object::Object::new(),
246    }
247  }
248
249  /// Create a new external rule.
250  pub fn ext(
251    name: String,
252    inputs: Vec<ValueId>,
253    outputs: Vec<ValueId>,
254    enable: Option<String>,
255    ready: Option<String>,
256    side_effect: Option<bool>,
257  ) -> Rule {
258    Rule {
259      is_ext: true,
260      is_private: false,
261      name,
262      signature: RuleSignature::Method {
263        inputs,
264        outputs,
265        side_effect,
266      },
267      guard_ops: vec![],
268      timing: RuleTiming::SingleCycle,
269      ops: vec![],
270      enable,
271      ready,
272      annotations: json::object::Object::new(),
273    }
274  }
275
276  /// Set the rule to private.
277  pub fn to_be_private(self) -> Rule {
278    Rule {
279      is_private: true,
280      ..self
281    }
282  }
283
284  /// Get all spans of the rule from annotations.
285  pub fn span(&self) -> impl Iterator<Item = MySpan> + '_ {
286    self.annotations.iter().filter_map(|(k, v)| {
287      if k.ends_with("span") {
288        MySpan::from_json(v)
289      } else {
290        None
291      }
292    })
293  }
294
295  /// Get all inputs of the rule.
296  pub fn inputs(&self) -> Vec<ValueId> {
297    self.signature.inputs().collect()
298  }
299
300  /// Get all inputs of the rule (mut).
301  pub fn inputs_mut(&mut self) -> &mut Vec<ValueId> {
302    self.signature.inputs_mut()
303  }
304
305  /// Get all outputs of the rule.
306  pub fn outputs(&self) -> Vec<ValueId> {
307    self.signature.outputs().collect()
308  }
309
310  /// Get all outputs of the rule (mut).
311  pub fn outputs_mut(&mut self) -> &mut Vec<ValueId> {
312    self.signature.outputs_mut()
313  }
314
315  /// Get all return values of the rule.
316  pub fn return_values(&self) -> Vec<ValueId> {
317    // last op's outputs are the return values
318    self
319      .ops
320      .last()
321      .map(|op| op.outputs().collect())
322      .unwrap_or_default()
323  }
324
325  /// Replace the guard operations of the rule.
326  pub fn replace_guard_op(&mut self, index: usize, new_ops: Vec<Op>) {
327    if index < self.guard_ops.len() {
328      self.guard_ops.splice(index..=index, new_ops);
329    } else {
330      panic!("Index out of bounds for guard_ops");
331    }
332  }
333
334  /// Replace the operations of the rule.
335  pub fn replace_op(&mut self, index: usize, new_ops: Vec<Op>) {
336    if index < self.ops.len() {
337      self.ops.splice(index + 1..=index + 1, new_ops);
338    } else {
339      panic!("Index out of bounds for ops");
340    }
341  }
342
343  /// Replace all operations of the rule with the given map.
344  pub fn replace_all_op_with_map(
345    &mut self,
346    replacements: &HashMap<ir::ValueId, ir::ValueId>,
347  ) {
348    for op in self.guard_mut() {
349      op.replace_value_with_map(replacements);
350    }
351
352    for op in self.ops_mut() {
353      op.replace_value_with_map(replacements);
354    }
355  }
356
357  /// Remove unused operations from the rule.
358  pub fn remove_unused_op(&mut self) {
359    let mut indices_to_remove = vec![];
360
361    for (index, op) in self.guard_mut().enumerate() {
362      if let OpEnum::Assign(AssignOp { res, value }) = op.inner_mut() {
363        if res == value {
364          indices_to_remove.push(index);
365        }
366      } else {
367        op.remove_unused_op();
368      }
369    }
370
371    indices_to_remove.sort_unstable_by(|a, b| b.cmp(a));
372    for &index in &indices_to_remove {
373      self.guard_ops.remove(index);
374    }
375
376    indices_to_remove.clear();
377
378    for (index, op) in self.ops_mut().enumerate() {
379      if let OpEnum::Assign(AssignOp { res, value }) = op.inner_mut() {
380        if res == value {
381          indices_to_remove.push(index);
382        }
383      } else {
384        op.remove_unused_op();
385      }
386    }
387
388    indices_to_remove.sort_unstable_by(|a, b| b.cmp(a));
389
390    for &index in &indices_to_remove {
391      self.ops.remove(index);
392    }
393  }
394}
395
396/// Rule relation in `cmtir`.
397/// `Method` relations specify constraints on the method rules, including
398/// C(conflict), CF(conflict-free), SA(sequence-ahead), and SB(sequence-behind).
399/// `Schedule` relations specify the order of the rules for execution.
400#[derive(Debug, Clone, SExpr)]
401pub enum RuleRel {
402  #[pp(surrounded)]
403  Method {
404    rel: MethodRel,
405    lhs: Vec<InstRule>,
406    rhs: Vec<InstRule>,
407  },
408  Schedule(Vec<InstRule>),
409}
410
411impl RuleRel {
412  /// Create a new method relation.
413  pub fn method(
414    rel: MethodRel,
415    lhs: Vec<InstRule>,
416    rhs: Vec<InstRule>,
417  ) -> RuleRel {
418    RuleRel::Method { rel, lhs, rhs }
419  }
420
421  /// Create a new schedule relation.
422  pub fn schedule(inst_rules: Vec<InstRule>) -> RuleRel {
423    RuleRel::Schedule(inst_rules)
424  }
425}