///|
pub(all) struct ControlFlowSummary {
assigned : Array[String]
conditionally_assigned : Array[String]
read : Array[String]
sensitivity_names : Array[String]
mut has_clock_edge : Bool
} derive(Eq, Debug)
///|
fn append_name(names : Array[String], expression : @syntax.Expression) -> Unit {
match expression_name(expression) {
Some(name) => if !names.contains(name) { names.push(name) }
None => ()
}
}
///|
fn collect_reads(
expression : @syntax.Expression,
names : Array[String],
) -> Unit {
match expression {
@syntax.Name(_, _) => append_name(names, expression)
@syntax.Index(base, index, _) => {
collect_reads(base, names)
collect_reads(index, names)
}
@syntax.Unary(_, operand, _) => collect_reads(operand, names)
@syntax.Binary(left, _, right, _) => {
collect_reads(left, names)
collect_reads(right, names)
}
@syntax.Number(_, _) | @syntax.StringLiteral(_, _) | @syntax.Unknown(_) =>
()
}
}
///|
fn collect_statement_flow(
statement : @syntax.Statement,
assigned : Array[String],
conditional : Array[String],
reads : Array[String],
) -> Unit {
match statement {
@syntax.Assignment(target, value, _, _) => {
append_name(assigned, target)
collect_reads(value, reads)
}
@syntax.Block(statements, _) =>
for child in statements {
collect_statement_flow(child, assigned, conditional, reads)
}
@syntax.If(condition, then_branch, else_branch, _) => {
collect_reads(condition, reads)
let then_assigned : Array[String] = []
let else_assigned : Array[String] = []
collect_statement_flow(then_branch, then_assigned, [], reads)
match else_branch {
None =>
for name in then_assigned {
if !conditional.contains(name) {
conditional.push(name)
}
}
Some(branch) => {
collect_statement_flow(branch, else_assigned, [], reads)
for name in then_assigned {
if else_assigned.contains(name) {
if !assigned.contains(name) {
assigned.push(name)
}
} else if !conditional.contains(name) {
conditional.push(name)
}
}
for name in else_assigned {
if !then_assigned.contains(name) && !conditional.contains(name) {
conditional.push(name)
}
}
}
}
}
@syntax.Case(expression, arms, _) => {
collect_reads(expression, reads)
let branch_names : Array[String] = []
for arm in arms {
let names : Array[String] = []
collect_statement_flow(arm.body, names, [], reads)
for name in names {
if !branch_names.contains(name) {
branch_names.push(name)
}
}
}
for name in branch_names {
if !conditional.contains(name) {
conditional.push(name)
}
}
}
@syntax.Empty(_) | @syntax.Unknown(_) => ()
}
}
///|
fn merge_flow(target : ControlFlowSummary, source : ControlFlowSummary) -> Unit {
for name in source.assigned {
if !target.assigned.contains(name) {
target.assigned.push(name)
}
}
for name in source.conditionally_assigned {
if !target.conditionally_assigned.contains(name) {
target.conditionally_assigned.push(name)
}
}
for name in source.read {
if !target.read.contains(name) {
target.read.push(name)
}
}
for name in source.sensitivity_names {
if !target.sensitivity_names.contains(name) {
target.sensitivity_names.push(name)
}
}
target.has_clock_edge = target.has_clock_edge || source.has_clock_edge
}
///|
/// Summarize procedural reads, writes, and sensitivity edges for one module.
pub fn SemanticIndex::control_flow(
self : SemanticIndex,
module_name : String,
) -> ControlFlowSummary? {
let summary : ControlFlowSummary = {
assigned: [],
conditionally_assigned: [],
read: [],
sensitivity_names: [],
has_clock_edge: false,
}
let mut found = false
for mod in self.syntax_modules {
if mod.name == module_name {
found = true
for item in mod.items {
match item {
@syntax.Always(always) => {
let assigned : Array[String] = []
let conditional : Array[String] = []
let reads : Array[String] = []
collect_statement_flow(always.body, assigned, conditional, reads)
let flow : ControlFlowSummary = {
assigned,
conditionally_assigned: conditional,
read: reads,
sensitivity_names: [],
has_clock_edge: false,
}
for sensitivity in always.sensitivity {
collect_reads(sensitivity.expression, flow.sensitivity_names)
if sensitivity.edge is @syntax.Posedge ||
sensitivity.edge is @syntax.Negedge {
flow.has_clock_edge = true
}
}
merge_flow(summary, flow)
}
_ => ()
}
}
}
}
if found {
Some(summary)
} else {
None
}
}