///|
fn hot_query_op(func : HotFunc, node_id : NodeId) -> HotOp {
hot_node_op(func, node_id)
}
///|
pub enum HotSimpleValueKey {
ConstValue(HotConstPayload)
LocalGetValue(LocalId)
} derive(Eq, Debug)
///|
pub impl Show for HotSimpleValueKey with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
fn hot_query_single_result_is_numeric(func : HotFunc, node_id : NodeId) -> Bool {
match hot_type_results(func, hot_node_type(func, node_id)) {
[@lib.NumTypeValType(_)] => true
_ => false
}
}
///|
pub fn hot_node_kind_is_control(func : HotFunc, node_id : NodeId) -> Bool {
hot_is_control_node(func, node_id)
}
///|
pub fn hot_node_kind_is_branch(func : HotFunc, node_id : NodeId) -> Bool {
hot_is_branch(func, node_id) || hot_query_op(func, node_id) == HotOp::Delegate
}
///|
pub fn hot_node_kind_is_call(func : HotFunc, node_id : NodeId) -> Bool {
match hot_query_op(func, node_id) {
HotOp::Call
| HotOp::CallIndirect
| HotOp::CallRef
| HotOp::ReturnCall
| HotOp::ReturnCallIndirect
| HotOp::ReturnCallRef => true
_ => false
}
}
///|
pub fn hot_node_kind_is_local(func : HotFunc, node_id : NodeId) -> Bool {
match hot_query_op(func, node_id) {
HotOp::LocalGet | HotOp::LocalSet | HotOp::LocalTee => true
_ => false
}
}
///|
pub fn hot_node_kind_is_global(func : HotFunc, node_id : NodeId) -> Bool {
match hot_query_op(func, node_id) {
HotOp::GlobalGet | HotOp::GlobalSet => true
_ => false
}
}
///|
pub fn hot_node_kind_is_memory(func : HotFunc, node_id : NodeId) -> Bool {
match hot_query_op(func, node_id) {
HotOp::Load
| HotOp::Store
| HotOp::MemorySize
| HotOp::MemoryGrow
| HotOp::MemoryCopy
| HotOp::MemoryFill
| HotOp::MemoryInit
| HotOp::DataDrop => true
_ => false
}
}
///|
pub fn hot_node_kind_is_table(func : HotFunc, node_id : NodeId) -> Bool {
match hot_query_op(func, node_id) {
HotOp::TableGet
| HotOp::TableSet
| HotOp::TableSize
| HotOp::TableGrow
| HotOp::TableFill
| HotOp::TableCopy
| HotOp::TableInit
| HotOp::ElemDrop => true
_ => false
}
}
///|
pub fn hot_node_kind_is_numeric(func : HotFunc, node_id : NodeId) -> Bool {
match hot_query_op(func, node_id) {
HotOp::Unary | HotOp::Binary | HotOp::Compare | HotOp::Convert => true
HotOp::Const => hot_query_single_result_is_numeric(func, node_id)
_ => false
}
}
///|
pub fn hot_node_kind_is_heap(func : HotFunc, node_id : NodeId) -> Bool {
hot_query_op(func, node_id) == HotOp::Heap
}
///|
pub fn hot_node_kind_is_tuple(func : HotFunc, node_id : NodeId) -> Bool {
match hot_query_op(func, node_id) {
HotOp::TupleMake | HotOp::TupleExtract => true
_ => false
}
}
///|
pub fn hot_node_kind_is_exception(func : HotFunc, node_id : NodeId) -> Bool {
match hot_query_op(func, node_id) {
HotOp::Throw
| HotOp::Rethrow
| HotOp::ThrowRef
| HotOp::Delegate
| HotOp::Catch
| HotOp::CatchAll => true
_ => false
}
}
///|
pub fn hot_catch_payload_tag(func : HotFunc, node_id : NodeId) -> TagIdx {
let node = hot_node_get(func, node_id)
if node.op != HotOp::Catch {
abort("hot_catch_payload_tag requires a typed catch payload node")
}
TagIdx::new(node.imm0.reinterpret_as_uint())
}
///|
pub fn hot_node_kind_is_simd(func : HotFunc, node_id : NodeId) -> Bool {
hot_query_op(func, node_id) == HotOp::Simd
}
///|
pub fn hot_node_kind_is_atomic(func : HotFunc, node_id : NodeId) -> Bool {
hot_query_op(func, node_id) == HotOp::Atomic
}
///|
pub fn hot_node_type(func : HotFunc, node_id : NodeId) -> TypeId {
hot_node_require_live_read(func, node_id)
func.nodes[node_id].ty
}
///|
pub fn hot_node_result_arity(func : HotFunc, node_id : NodeId) -> Int32 {
hot_type_result_arity(func, hot_node_type(func, node_id))
}
///|
pub fn hot_simple_value_key(
func : HotFunc,
node_id : NodeId,
) -> HotSimpleValueKey? {
if node_id < 0 ||
node_id >= hot_node_count(func) ||
!hot_node_is_live(func, node_id) {
return None
}
let node = hot_node_get(func, node_id)
if node.child_count != 0 {
return None
}
match node.op {
HotOp::Const =>
Some(HotSimpleValueKey::ConstValue(hot_const_get(func, node.imm0)))
HotOp::LocalGet => Some(HotSimpleValueKey::LocalGetValue(node.imm0))
_ => None
}
}
///|
pub fn hot_nodes_match_simple_value(
func : HotFunc,
lhs : NodeId,
rhs : NodeId,
) -> Bool {
hot_simple_value_key(func, lhs) == hot_simple_value_key(func, rhs)
}
///|
pub fn hot_region_single_live_root(
func : HotFunc,
region_ref : HotRegionRef,
) -> NodeId? {
if hot_region_root_count(func, region_ref) != 1 {
return None
}
let candidate = hot_region_get(func, region_ref, 0)
if candidate < 0 || !hot_node_is_live(func, candidate) {
return None
}
Some(candidate)
}
///|
fn hot_root_is_strict_no_work_block_chain(
func : HotFunc,
root_id : NodeId,
label_is_used : (LabelId) -> Bool,
) -> Bool {
if root_id < 0 || !hot_node_is_live(func, root_id) {
return false
}
match hot_query_op(func, root_id) {
HotOp::Nop => hot_child_count(func, root_id) == 0
HotOp::Block => {
if hot_node_result_arity(func, root_id) != 0 {
return false
}
let label_id = hot_control_node_label(func, root_id)
if label_id < 0 || label_is_used(label_id) {
return false
}
let body_ref = hot_region_body_ref(func, root_id)
for slot = 0
slot < hot_region_root_count(func, body_ref)
slot = slot + 1 {
if !hot_root_is_strict_no_work_block_chain(
func,
hot_region_get(func, body_ref, slot),
label_is_used,
) {
return false
}
}
true
}
_ => false
}
}
///|
/// Prove that a region is empty or contains only an unused-label resultless
/// single-root block chain ending in an empty body or one childless `nop`.
pub fn hot_region_is_strict_no_work_block_chain(
func : HotFunc,
region_ref : HotRegionRef,
label_is_used : (LabelId) -> Bool,
) -> Bool {
match hot_region_root_count(func, region_ref) {
0 => true
1 =>
hot_root_is_strict_no_work_block_chain(
func,
hot_region_get(func, region_ref, 0),
label_is_used,
)
_ => false
}
}
///|
/// Prove that every root in a region independently forms an unused-label
/// resultless block chain ending empty or in one childless `nop`.
pub fn hot_region_is_strict_no_work_forest(
func : HotFunc,
region_ref : HotRegionRef,
label_is_used : (LabelId) -> Bool,
) -> Bool {
for slot = 0; slot < hot_region_root_count(func, region_ref); slot = slot + 1 {
if !hot_root_is_strict_no_work_block_chain(
func,
hot_region_get(func, region_ref, slot),
label_is_used,
) {
return false
}
}
true
}
///|
pub fn hot_region_single_live_block_root(
func : HotFunc,
region_ref : HotRegionRef,
) -> NodeId? {
let candidate = match hot_region_single_live_root(func, region_ref) {
Some(candidate) => candidate
None => return None
}
match hot_query_op(func, candidate) {
HotOp::Block => Some(candidate)
_ => None
}
}
///|
pub fn hot_region_single_live_block_body(
func : HotFunc,
region_ref : HotRegionRef,
) -> HotRegionRef? {
match hot_region_single_live_block_root(func, region_ref) {
Some(block_id) => Some(hot_region_body_ref(func, block_id))
None => None
}
}
///|
pub fn hot_region_single_live_block_body_or_self(
func : HotFunc,
region_ref : HotRegionRef,
) -> HotRegionRef {
match hot_region_single_live_block_body(func, region_ref) {
Some(body_ref) => body_ref
None => region_ref
}
}
///|
pub fn hot_region_single_live_if_root(
func : HotFunc,
region_ref : HotRegionRef,
) -> NodeId? {
let candidate = match hot_region_single_live_root(func, region_ref) {
Some(candidate) => candidate
None => return None
}
match hot_query_op(func, candidate) {
HotOp::If => Some(candidate)
_ => None
}
}
///|
pub fn hot_region_single_plain_br_root(
func : HotFunc,
region_ref : HotRegionRef,
) -> NodeId? {
let candidate = match hot_region_single_live_root(func, region_ref) {
Some(candidate) => candidate
None => return None
}
let node = hot_node_get(func, candidate)
match node.op {
HotOp::Br if node.child_count == 0 => Some(candidate)
_ => None
}
}
///|
pub fn hot_region_single_plain_br_target(
func : HotFunc,
region_ref : HotRegionRef,
) -> LabelId? {
match hot_region_single_plain_br_root(func, region_ref) {
Some(br_id) => hot_branch_target(func, br_id)
None => None
}
}
///|
pub fn hot_region_split_trailing_plain_br_root(
func : HotFunc,
region_ref : HotRegionRef,
) -> (Array[NodeId], NodeId)? {
let root_count = hot_region_root_count(func, region_ref)
if root_count == 0 {
return None
}
let tail = hot_region_get(func, region_ref, root_count - 1)
let node = hot_node_get(func, tail)
match node.op {
HotOp::Br if node.child_count == 0 => ()
_ => return None
}
let prefix : Array[NodeId] = []
for idx = 0; idx < root_count - 1; idx = idx + 1 {
prefix.push(hot_region_get(func, region_ref, idx))
}
Some((prefix, tail))
}
///|
pub fn hot_region_split_trailing_plain_br_target(
func : HotFunc,
region_ref : HotRegionRef,
) -> (Array[NodeId], LabelId)? {
match hot_region_split_trailing_plain_br_root(func, region_ref) {
Some((prefix, tail)) =>
match hot_branch_target(func, tail) {
Some(target) => Some((prefix, target))
None => None
}
None => None
}
}
///|
pub fn hot_region_single_exit_root(
func : HotFunc,
region_ref : HotRegionRef,
) -> NodeId? {
let candidate = match hot_region_single_live_root(func, region_ref) {
Some(candidate) => candidate
None => return None
}
let node = hot_node_get(func, candidate)
match node.op {
HotOp::Return => Some(candidate)
HotOp::Br if node.child_count == 0 => Some(candidate)
_ => None
}
}
///|
pub fn hot_region_holder_block_root(
func : HotFunc,
region_ref : HotRegionRef,
) -> NodeId? {
let holder = region_ref.holder
if holder < 0 || !hot_node_is_live(func, holder) {
return None
}
match hot_query_op(func, holder) {
HotOp::Block => Some(holder)
_ => None
}
}
///|
pub fn hot_region_holder_block_label(
func : HotFunc,
region_ref : HotRegionRef,
) -> LabelId? {
match hot_region_holder_block_root(func, region_ref) {
Some(holder) => Some(hot_control_node_label(func, holder))
None => None
}
}
///|
pub fn hot_region_split_trailing_payload_roots(
func : HotFunc,
region_ref : HotRegionRef,
result_arity : Int32,
) -> (Array[NodeId], Array[NodeId])? {
hot_split_trailing_payload_roots(
func,
hot_region_roots(func, region_ref),
result_arity,
)
}
///|
pub fn hot_split_trailing_payload_roots(
func : HotFunc,
roots : Array[NodeId],
result_arity : Int32,
) -> (Array[NodeId], Array[NodeId])? {
if result_arity <= 0 {
return Some((roots, []))
}
let mut payload_arity = 0
let mut payload_start = roots.length()
while payload_start > 0 && payload_arity < result_arity {
let candidate = roots[payload_start - 1]
let candidate_arity = hot_node_result_arity(func, candidate)
if candidate_arity <= 0 {
return None
}
payload_arity += candidate_arity
payload_start -= 1
}
if payload_arity != result_arity {
return None
}
let prefix : Array[NodeId] = []
for idx = 0; idx < payload_start; idx = idx + 1 {
prefix.push(roots[idx])
}
let payload : Array[NodeId] = []
for idx = payload_start; idx < roots.length(); idx = idx + 1 {
payload.push(roots[idx])
}
Some((prefix, payload))
}
///|
pub fn hot_node_repeated_live_child(
func : HotFunc,
node_id : NodeId,
) -> NodeId? {
if node_id < 0 ||
node_id >= hot_node_count(func) ||
!hot_node_is_live(func, node_id) {
return None
}
let node = hot_node_get(func, node_id)
if node.child_count == 0 {
return None
}
let candidate = hot_child_get(func, node_id, 0)
if candidate < 0 || !hot_node_is_live(func, candidate) {
return None
}
for child_idx = 1; child_idx < node.child_count; child_idx = child_idx + 1 {
if hot_child_get(func, node_id, child_idx) != candidate {
return None
}
}
Some(candidate)
}
///|
pub fn hot_region_repeated_live_root(
func : HotFunc,
region_ref : HotRegionRef,
) -> NodeId? {
let root_count = hot_region_root_count(func, region_ref)
if root_count == 0 {
return None
}
let candidate = hot_region_get(func, region_ref, 0)
if candidate < 0 || !hot_node_is_live(func, candidate) {
return None
}
for idx = 1; idx < root_count; idx = idx + 1 {
if hot_region_get(func, region_ref, idx) != candidate {
return None
}
}
Some(candidate)
}
///|
pub fn hot_region_repeated_live_if_root(
func : HotFunc,
region_ref : HotRegionRef,
) -> NodeId? {
let candidate = match hot_region_repeated_live_root(func, region_ref) {
Some(candidate) => candidate
None => return None
}
match hot_query_op(func, candidate) {
HotOp::If => Some(candidate)
_ => None
}
}
///|
pub fn hot_region_repeated_live_block_root(
func : HotFunc,
region_ref : HotRegionRef,
) -> NodeId? {
let candidate = match hot_region_repeated_live_root(func, region_ref) {
Some(candidate) => candidate
None => return None
}
match hot_query_op(func, candidate) {
HotOp::Block => Some(candidate)
_ => None
}
}
///|
pub fn hot_region_repeated_live_block_body(
func : HotFunc,
region_ref : HotRegionRef,
) -> HotRegionRef? {
match hot_region_repeated_live_block_root(func, region_ref) {
Some(block_id) => Some(hot_region_body_ref(func, block_id))
None => None
}
}
///|
pub fn hot_region_repeated_live_block_body_or_self(
func : HotFunc,
region_ref : HotRegionRef,
) -> HotRegionRef {
match hot_region_repeated_live_block_body(func, region_ref) {
Some(body_ref) => body_ref
None => region_ref
}
}
///|
pub fn hot_branch_target(func : HotFunc, node_id : NodeId) -> LabelId? {
let node = hot_node_get(func, node_id)
match node.op {
HotOp::Br
| HotOp::BrIf
| HotOp::BrOnNull
| HotOp::BrOnNonNull
| HotOp::BrOnCast
| HotOp::BrOnCastFail
| HotOp::Delegate => Some(node.imm0)
HotOp::BrTable => Some(hot_branch_table_default_target(func, node.imm0))
_ => None
}
}
///|
pub fn hot_branch_table_targets_for_node(
func : HotFunc,
node_id : NodeId,
) -> Array[LabelId] {
let node = hot_node_get(func, node_id)
match node.op {
HotOp::BrTable => hot_branch_table_targets(func, node.imm0)
_ => []
}
}
///|
pub fn hot_root_span(func : HotFunc) -> Array[NodeId] {
let out : Array[NodeId] = []
for i = 0; i < hot_root_count(func); i = i + 1 {
out.push(hot_root_get(func, i))
}
out
}
///|
pub fn hot_child_span(func : HotFunc, node_id : NodeId) -> Array[NodeId] {
let out : Array[NodeId] = []
for i = 0; i < hot_child_count(func, node_id); i = i + 1 {
out.push(hot_child_get(func, node_id, i))
}
out
}
///|
pub fn hot_local_param_count(func : HotFunc) -> Int32 {
hot_param_count(func)
}
///|
pub fn hot_local_body_count(func : HotFunc) -> Int32 {
hot_local_count(func) - hot_local_param_count(func)
}
///|
pub fn hot_local_is_param(func : HotFunc, local_id : LocalId) -> Bool {
local_id >= 0 && local_id < hot_local_param_count(func)
}
///|
pub fn hot_node_is_deleted(func : HotFunc, node_id : NodeId) -> Bool {
node_id >= 0 &&
node_id < hot_node_count(func) &&
!hot_node_is_live(func, node_id)
}