///|
pub const HOT_FLAG_TERMINATOR : UInt32 = 1U

///|
pub const HOT_FLAG_CONTROL : UInt32 = 2U

///|
pub const HOT_FLAG_SIDE_EFFECT : UInt32 = 4U

///|
pub const HOT_FLAG_VALUE : UInt32 = 8U

///|
pub const HOT_FLAG_MAY_TRAP : UInt32 = 16U

///|
pub const HOT_FLAG_BRANCH : UInt32 = 32U

///|
pub const HOT_FLAG_CALL : UInt32 = 64U

///|
pub const HOT_FLAG_LOAD : UInt32 = 128U

///|
pub const HOT_FLAG_STORE : UInt32 = 256U

///|
pub const HOT_FLAG_THROWS : UInt32 = 512U

///|
pub const HOT_FLAG_HAS_EXCEPTIONAL_SUCC : UInt32 = 1024U

///|
fn hot_flags_include(bits : UInt32, flag : UInt32) -> Bool {
  (bits & flag) == flag
}

///|
pub fn hot_default_flags_for_op(op : HotOp) -> UInt32 {
  match op {
    HotOp::Nop => 0U
    HotOp::Unreachable => HOT_FLAG_TERMINATOR
    HotOp::Block | HotOp::Loop | HotOp::If | HotOp::Try | HotOp::TryTable =>
      HOT_FLAG_CONTROL
    HotOp::Br | HotOp::BrTable => HOT_FLAG_BRANCH | HOT_FLAG_TERMINATOR
    HotOp::BrIf
    | HotOp::BrOnNull
    | HotOp::BrOnNonNull
    | HotOp::BrOnCast
    | HotOp::BrOnCastFail => HOT_FLAG_BRANCH
    HotOp::Return => HOT_FLAG_TERMINATOR | HOT_FLAG_SIDE_EFFECT
    HotOp::Continuation =>
      HOT_FLAG_CALL |
      HOT_FLAG_BRANCH |
      HOT_FLAG_SIDE_EFFECT |
      HOT_FLAG_MAY_TRAP |
      HOT_FLAG_HAS_EXCEPTIONAL_SUCC
    HotOp::Call | HotOp::CallIndirect | HotOp::CallRef =>
      HOT_FLAG_CALL |
      HOT_FLAG_SIDE_EFFECT |
      HOT_FLAG_MAY_TRAP |
      HOT_FLAG_HAS_EXCEPTIONAL_SUCC
    HotOp::ReturnCall | HotOp::ReturnCallIndirect | HotOp::ReturnCallRef =>
      HOT_FLAG_CALL |
      HOT_FLAG_SIDE_EFFECT |
      HOT_FLAG_MAY_TRAP |
      HOT_FLAG_HAS_EXCEPTIONAL_SUCC |
      HOT_FLAG_TERMINATOR
    HotOp::LocalGet
    | HotOp::GlobalGet
    | HotOp::Const
    | HotOp::Select
    | HotOp::RefNull
    | HotOp::RefIsNull
    | HotOp::RefFunc
    | HotOp::RefGetDesc
    | HotOp::RefTest
    | HotOp::RefTestDesc
    | HotOp::Unary
    | HotOp::Binary
    | HotOp::Compare
    | HotOp::Convert
    | HotOp::TupleMake
    | HotOp::TupleExtract
    | HotOp::StringOp => HOT_FLAG_VALUE
    HotOp::RefCast | HotOp::RefCastDescEq => HOT_FLAG_VALUE | HOT_FLAG_MAY_TRAP
    HotOp::Heap => HOT_FLAG_VALUE | HOT_FLAG_SIDE_EFFECT | HOT_FLAG_MAY_TRAP
    HotOp::LocalSet | HotOp::GlobalSet | HotOp::Drop => HOT_FLAG_SIDE_EFFECT
    HotOp::LocalTee => HOT_FLAG_SIDE_EFFECT | HOT_FLAG_VALUE
    HotOp::Load => HOT_FLAG_VALUE | HOT_FLAG_MAY_TRAP | HOT_FLAG_LOAD
    HotOp::Store => HOT_FLAG_SIDE_EFFECT | HOT_FLAG_MAY_TRAP | HOT_FLAG_STORE
    HotOp::MemorySize => HOT_FLAG_VALUE
    HotOp::MemoryGrow => HOT_FLAG_SIDE_EFFECT | HOT_FLAG_VALUE
    HotOp::MemoryCopy
    | HotOp::MemoryFill
    | HotOp::MemoryInit
    | HotOp::TableSet
    | HotOp::TableFill
    | HotOp::TableCopy
    | HotOp::TableInit =>
      HOT_FLAG_SIDE_EFFECT | HOT_FLAG_MAY_TRAP | HOT_FLAG_STORE
    HotOp::DataDrop | HotOp::ElemDrop => HOT_FLAG_SIDE_EFFECT
    HotOp::TableGet => HOT_FLAG_VALUE | HOT_FLAG_MAY_TRAP | HOT_FLAG_LOAD
    HotOp::TableSize => HOT_FLAG_VALUE
    HotOp::TableGrow => HOT_FLAG_SIDE_EFFECT | HOT_FLAG_VALUE
    HotOp::Throw | HotOp::Rethrow | HotOp::ThrowRef =>
      HOT_FLAG_SIDE_EFFECT |
      HOT_FLAG_THROWS |
      HOT_FLAG_HAS_EXCEPTIONAL_SUCC |
      HOT_FLAG_TERMINATOR
    HotOp::Catch => HOT_FLAG_VALUE
    HotOp::Delegate | HotOp::CatchAll => 0U
    HotOp::Atomic | HotOp::Simd => 0U
  }
}

///|
fn hot_flags_for_exact_payload(
  func : HotFunc,
  op : HotOp,
  payload_id : SideTableId,
) -> UInt32 {
  let base = hot_default_flags_for_op(op)
  if payload_id < 0 || payload_id >= func.instr_payloads.length() {
    return base
  }
  let mask = match op {
    HotOp::Unary =>
      effects_mask_for_numeric_exact(
        Some(hot_instr_payload_get(func, payload_id).instr),
      )
    HotOp::Simd =>
      effects_mask_for_simd_exact(
        Some(hot_instr_payload_get(func, payload_id).instr),
      )
    HotOp::StringOp =>
      effects_mask_for_string_exact(
        Some(hot_instr_payload_get(func, payload_id).instr),
      )
    _ => return base
  }
  let mut flags = if op == HotOp::Simd &&
    (mask & EFFECT_MASK_MEMORY_WRITE) != 0U {
    base
  } else {
    base | HOT_FLAG_VALUE
  }
  if (mask & EFFECT_MASK_MEMORY_READ) != 0U {
    flags = flags | HOT_FLAG_LOAD
  }
  if (mask & EFFECT_MASK_MEMORY_WRITE) != 0U {
    flags = flags | HOT_FLAG_STORE | HOT_FLAG_SIDE_EFFECT
  }
  if (mask & EFFECT_MASK_TRAP) != 0U {
    flags = flags | HOT_FLAG_MAY_TRAP
  }
  flags
}

///|
pub fn hot_node_flags(func : HotFunc, node_id : NodeId) -> UInt32 {
  func.nodes[node_id].flags
}

///|
pub fn hot_node_has_flag(
  func : HotFunc,
  node_id : NodeId,
  flag : UInt32,
) -> Bool {
  hot_flags_include(hot_node_flags(func, node_id), flag)
}

///|
pub fn hot_is_control_node(func : HotFunc, node_id : NodeId) -> Bool {
  hot_node_has_flag(func, node_id, HOT_FLAG_CONTROL)
}

///|
pub fn hot_is_terminator_node(func : HotFunc, node_id : NodeId) -> Bool {
  hot_node_has_flag(func, node_id, HOT_FLAG_TERMINATOR)
}

///|
pub fn hot_has_side_effect(func : HotFunc, node_id : NodeId) -> Bool {
  hot_node_has_flag(func, node_id, HOT_FLAG_SIDE_EFFECT)
}

///|
pub fn hot_may_trap(func : HotFunc, node_id : NodeId) -> Bool {
  hot_node_has_flag(func, node_id, HOT_FLAG_MAY_TRAP)
}

///|
pub fn hot_is_branch(func : HotFunc, node_id : NodeId) -> Bool {
  hot_node_has_flag(func, node_id, HOT_FLAG_BRANCH)
}

///|
pub fn hot_has_exceptional_succ(func : HotFunc, node_id : NodeId) -> Bool {
  hot_node_has_flag(func, node_id, HOT_FLAG_HAS_EXCEPTIONAL_SUCC)
}

///|
pub fn hot_node_may_throw_exception(func : HotFunc, node_id : NodeId) -> Bool {
  hot_node_has_flag(func, node_id, HOT_FLAG_THROWS) ||
  hot_has_exceptional_succ(func, node_id)
}