///|
pub enum HotVisitCtl[S, E] {
Continue(S)
Skip(S)
Stop(S)
Error(E)
}
///|
pub fn[S, E] hot_visit_continue(state : S) -> HotVisitCtl[S, E] {
Continue(state)
}
///|
pub fn[S, E] hot_visit_skip(state : S) -> HotVisitCtl[S, E] {
Skip(state)
}
///|
pub fn[S, E] hot_visit_stop(state : S) -> HotVisitCtl[S, E] {
Stop(state)
}
///|
pub fn[S, E] hot_visit_error(err : E) -> HotVisitCtl[S, E] {
Error(err)
}
///|
pub enum HotRewriteCtl[S, E] {
Same(S)
Replace(S, NodeId)
Remove(S)
Error(E)
}
///|
pub fn[S, E] hot_rewrite_same(state : S) -> HotRewriteCtl[S, E] {
Same(state)
}
///|
pub fn[S, E] hot_rewrite_replace(
state : S,
replacement : NodeId,
) -> HotRewriteCtl[S, E] {
Replace(state, replacement)
}
///|
pub fn[S, E] hot_rewrite_remove(state : S) -> HotRewriteCtl[S, E] {
Remove(state)
}
///|
pub fn[S, E] hot_rewrite_error(err : E) -> HotRewriteCtl[S, E] {
Error(err)
}
///|
fn hot_walk_push_live_node(
func : HotFunc,
stack : Array[NodeId],
node_id : NodeId,
) -> Unit {
if node_id != HOT_NONE_NODE && hot_node_is_live(func, node_id) {
stack.push(node_id)
}
}
///|
// If keeps its historical condition/then/else slots, followed by typed entry
// operands. Execution order is entries, condition, then the selected region.
#inline
fn hot_child_evaluation_slot(node : HotNode, index : Int32) -> Int32 {
if node.op == HotOp::If && node.child_count > 3 {
let entries = node.child_count - 3
if index < entries {
index + 3
} else {
index - entries
}
} else {
index
}
}
///|
fn hot_walk_push_children_reversed(
func : HotFunc,
stack : Array[NodeId],
node_id : NodeId,
) -> Unit {
for i = hot_child_count(func, node_id) - 1; i >= 0; i = i - 1 {
hot_walk_push_live_node(func, stack, hot_child_get(func, node_id, i))
}
}
///|
pub fn[S, E] hot_for_each_root(
func : HotFunc,
initial : S,
visit : (S, Int32, NodeId) -> HotVisitCtl[S, E],
) -> Result[S, E] {
let mut state = initial
for idx = 0; idx < hot_root_count(func); idx = idx + 1 {
match visit(state, idx, hot_root_get(func, idx)) {
HotVisitCtl::Continue(next) => state = next
HotVisitCtl::Skip(next) => state = next
HotVisitCtl::Stop(next) => return Ok(next)
HotVisitCtl::Error(err) => return Err(err)
}
}
Ok(state)
}
///|
pub fn[S, E] hot_for_each_child(
func : HotFunc,
parent_id : NodeId,
initial : S,
visit : (S, Int32, NodeId) -> HotVisitCtl[S, E],
) -> Result[S, E] {
let mut state = initial
for slot = 0; slot < hot_child_count(func, parent_id); slot = slot + 1 {
match visit(state, slot, hot_child_get(func, parent_id, slot)) {
HotVisitCtl::Continue(next) => state = next
HotVisitCtl::Skip(next) => state = next
HotVisitCtl::Stop(next) => return Ok(next)
HotVisitCtl::Error(err) => return Err(err)
}
}
Ok(state)
}
///|
pub fn[S, E] hot_walk_subtree_preorder(
func : HotFunc,
root_id : NodeId,
initial : S,
visit : (S, NodeId) -> HotVisitCtl[S, E],
) -> Result[S, E] {
let mut state = initial
let stack : Array[NodeId] = []
hot_walk_push_live_node(func, stack, root_id)
while stack.length() > 0 {
let node_id = stack.pop().unwrap()
match visit(state, node_id) {
HotVisitCtl::Continue(next) => {
state = next
hot_walk_push_children_reversed(func, stack, node_id)
}
HotVisitCtl::Skip(next) => state = next
HotVisitCtl::Stop(next) => return Ok(next)
HotVisitCtl::Error(err) => return Err(err)
}
}
Ok(state)
}
///|
pub fn[S, E] hot_walk_subtree_postorder(
func : HotFunc,
root_id : NodeId,
initial : S,
visit : (S, NodeId) -> HotVisitCtl[S, E],
) -> Result[S, E] {
let mut state = initial
let stack : Array[(NodeId, Bool)] = []
if root_id != HOT_NONE_NODE && hot_node_is_live(func, root_id) {
stack.push((root_id, false))
}
while stack.length() > 0 {
let (node_id, expanded) = stack.pop().unwrap()
if expanded {
match visit(state, node_id) {
HotVisitCtl::Continue(next) => state = next
HotVisitCtl::Skip(next) => state = next
HotVisitCtl::Stop(next) => return Ok(next)
HotVisitCtl::Error(err) => return Err(err)
}
continue
}
stack.push((node_id, true))
for i = hot_child_count(func, node_id) - 1; i >= 0; i = i - 1 {
let child_id = hot_child_get(func, node_id, i)
if child_id != HOT_NONE_NODE && hot_node_is_live(func, child_id) {
stack.push((child_id, false))
}
}
}
Ok(state)
}
///|
pub fn[S, E] hot_walk_region(
func : HotFunc,
region_id : NodeId,
initial : S,
visit : (S, Int32, NodeId) -> HotVisitCtl[S, E],
) -> Result[S, E] {
hot_for_each_child(func, region_id, initial, visit)
}
///|
pub fn[S, E] hot_rewrite_children_by_slot(
func : HotFunc,
parent_id : NodeId,
initial : S,
rewrite : (S, NodeId, Int32, NodeId) -> HotRewriteCtl[S, E],
) -> Result[S, E] {
let mut state = initial
for slot = 0; slot < hot_child_count(func, parent_id); slot = slot + 1 {
let child_id = hot_child_get(func, parent_id, slot)
match rewrite(state, parent_id, slot, child_id) {
HotRewriteCtl::Same(next) => state = next
HotRewriteCtl::Replace(next, replacement) => {
hot_child_set(func, parent_id, slot, replacement)
state = next
}
HotRewriteCtl::Remove(next) => {
hot_child_set(func, parent_id, slot, HOT_NONE_NODE)
state = next
}
HotRewriteCtl::Error(err) => return Err(err)
}
}
Ok(state)
}
///|
pub fn[S, E] hot_worklist_walk(
func : HotFunc,
seeds : Array[NodeId],
initial : S,
visit : (S, NodeId) -> HotVisitCtl[S, E],
) -> Result[S, E] {
let mut state = initial
let stack : Array[NodeId] = []
for i = seeds.length() - 1; i >= 0; i = i - 1 {
hot_walk_push_live_node(func, stack, seeds[i])
}
while stack.length() > 0 {
let node_id = stack.pop().unwrap()
match visit(state, node_id) {
HotVisitCtl::Continue(next) => {
state = next
hot_walk_push_children_reversed(func, stack, node_id)
}
HotVisitCtl::Skip(next) => state = next
HotVisitCtl::Stop(next) => return Ok(next)
HotVisitCtl::Error(err) => return Err(err)
}
}
Ok(state)
}
///|
pub fn[S, E] hot_walk_control_regions(
func : HotFunc,
control_id : NodeId,
initial : S,
visit : (S, NodeId, HotControlRegionSlotInfo, NodeId) -> HotVisitCtl[S, E],
) -> Result[S, E] {
let mut state = initial
let slot_infos = hot_control_region_slot_info(
hot_node_get(func, control_id).op,
)
for slot_info in slot_infos {
if slot_info.slot >= hot_child_count(func, control_id) {
continue
}
let region_id = hot_child_get(func, control_id, slot_info.slot)
if region_id == HOT_NONE_NODE || !hot_node_is_live(func, region_id) {
continue
}
match visit(state, control_id, slot_info, region_id) {
HotVisitCtl::Continue(next) => state = next
HotVisitCtl::Skip(next) => state = next
HotVisitCtl::Stop(next) => return Ok(next)
HotVisitCtl::Error(err) => return Err(err)
}
}
Ok(state)
}