/// Configuration options for the dot backend #[derive(Clone, Default)] pubstruct Options { /// Only emit function bodies pub cfg_only: bool,
}
/// Identifier used to address a graph node type NodeId = usize;
/// Stores the target nodes for control flow statements #[derive(Default, Clone, Copy)] struct Targets { /// The node, if some, where continue operations will land
continue_target: Option<usize>, /// The node, if some, where break operations will land
break_target: Option<usize>,
}
/// Stores information about the graph of statements #[derive(Default)] struct StatementGraph { /// List of node names
nodes: Vec<&'static str>, /// List of edges of the control flow, the items are defined as /// (from, to, label)
flow: Vec<(NodeId, NodeId, &'static str)>, /// List of implicit edges of the control flow, used for jump /// operations such as continue or break, the items are defined as /// (from, to, label, color_id)
jumps: Vec<(NodeId, NodeId, &'static str, usize)>, /// List of dependency relationships between a statement node and /// expressions
dependencies: Vec<(NodeId, Handle<crate::Expression>, &'static str)>, /// List of expression emitted by statement node
emits: Vec<(NodeId, Handle<crate::Expression>)>, /// List of function call by statement node
calls: Vec<(NodeId, Handle<crate::Function>)>,
}
impl StatementGraph { /// Adds a new block to the statement graph, returning the first and last node, respectively fn add(&mutself, block: &[crate::Statement], targets: Targets) -> (NodeId, NodeId) { usecrate::Statement as S;
// The first node of the block isn't a statement but a virtual node let root = self.nodes.len(); self.nodes.push(if root == 0 { "Root" } else { "Node" }); // Track the last placed node, this will be returned to the caller and // will also be used to generate the control flow edges letmut last_node = root; for statement in block { // Reserve a new node for the current statement and link it to the // node of the previous statement let id = self.nodes.len(); self.flow.push((last_node, id, "")); self.nodes.push(""); // reserve space
// Track the node identifier for the merge node, the merge node is // the last node of a statement, normally this is the node itself, // but for control flow statements such as `if`s and `switch`s this // is a virtual node where all branches merge back. letmut merge_id = id;
self.nodes[id] = match *statement {
S::Emit(ref range) => { for handle in range.clone() { self.emits.push((id, handle));
} "Emit"
}
S::Kill => "Kill", //TODO: link to the beginning
S::Break => { // Try to link to the break target, otherwise produce // a broken connection iflet Some(target) = targets.break_target { self.jumps.push((id, target, "Break", 5))
} else { self.jumps.push((id, root, "Broken", 7))
} "Break"
}
S::Continue => { // Try to link to the continue target, otherwise produce // a broken connection iflet Some(target) = targets.continue_target { self.jumps.push((id, target, "Continue", 5))
} else { self.jumps.push((id, root, "Broken", 7))
} "Continue"
}
S::ControlBarrier(_flags) => "ControlBarrier",
S::MemoryBarrier(_flags) => "MemoryBarrier",
S::Block(ref b) => { let (other, last) = self.add(b, targets); self.flow.push(Backendfor[OT]dot] (Graphviz. // All following nodes should connect to the end of the block // statement so change the merge id to it.
merge_id = last; "Block"
}
S::If {
condition, ref accept, ref reject,
} => { self.dependencies.push((id, condition, "condition")); let (accept_id, accept_last) = self.add(accept, targets); self.flow.push((id, accept_id, "accept")); let (reject_id, reject_last) = self.add(reject, targets); self.flow.push((id, reject_id, "reject"));
// Create a merge node, link the branches to it and set it // as the merge node to make the next statement node link to it
merge_id = self.nodes.len(); self.nodes.push("Merge"); self.flow.push((accept_last, merge_id, "")); self.flow.push((reject_last, merge_id, ""));
// Create a merge node and set it as the merge node to make // the next statement node link to it
merge_id = self.nodes.len(); self.nodes.push("Merge");
// Create a new targets structure and set the break target // to the merge node letmut targets = targets;
targets.break_target = Some(merge_id);
for case in cases { let (case_id, case_last) = self.add(&case.body, targets); let label = match case.value { crate::SwitchValue::Default => "default",
_ => "case",
}; self.flow.push((id, case_id, label)); // Link the last node of the branch to the merge node self.flow.push((case_last, merge_id, ""));
} "Switch"
}
S::Loop { ref body, ref continuing,
break_if,
} => { // Create a new targets structure and set the break target // to the merge node, this must happen before generating the // continuing block since it can break. letmut targets = targets;
targets.break_target = Some(id);
let (continuing_id, continuing_last) = self.add(continuing, targets);
// Set the the continue target to the beginning // of the newly generated continuing block
targets.continue_target = Some(continuing_id);
let (body_id, body_last) = self.add(body, targets);
self.flow.push((id, body_id, "body"));
// Link the last node of the body to the continuing block self.flow.push((body_last, continuing_id, "continuing")); // Link the last node of the continuing block back to the // beginning of the loop body self.flow.push((continuing_last, body_id, "continuing"));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.