///|
pub type NodeId = Int32
///|
pub type TypeId = Int32
///|
pub type LocalId = Int32
///|
pub type LabelId = Int32
///|
pub type SideTableId = Int32
///|
pub type RootId = Int32
///|
pub enum HotOp {
Nop
Unreachable
Block
Loop
If
Try
TryTable
Br
BrIf
BrTable
BrOnNull
BrOnNonNull
BrOnCast
BrOnCastFail
Return
Call
CallIndirect
CallRef
ReturnCall
ReturnCallIndirect
ReturnCallRef
LocalGet
LocalSet
LocalTee
GlobalGet
GlobalSet
Load
Store
MemorySize
MemoryGrow
MemoryCopy
MemoryFill
MemoryInit
DataDrop
TableGet
TableSet
TableSize
TableGrow
TableFill
TableCopy
TableInit
ElemDrop
Const
Drop
Select
RefNull
RefIsNull
RefFunc
RefGetDesc
RefTest
RefCast
RefTestDesc
RefCastDescEq
Heap
Continuation
Unary
Binary
Compare
Convert
TupleMake
TupleExtract
StringOp
Throw
Rethrow
ThrowRef
Delegate
Catch
CatchAll
Atomic
Simd
} derive(Eq, Debug)
///|
pub impl Show for HotOp with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub fn HotOp::br_on_null() -> HotOp {
BrOnNull
}
///|
pub fn HotOp::br_on_non_null() -> HotOp {
BrOnNonNull
}
///|
pub fn HotOp::br_on_cast() -> HotOp {
BrOnCast
}
///|
pub fn HotOp::br_on_cast_fail() -> HotOp {
BrOnCastFail
}
///|
pub fn HotOp::ref_get_desc() -> HotOp {
RefGetDesc
}
///|
pub fn HotOp::rethrow() -> HotOp {
Rethrow
}
///|
pub fn HotOp::catch_() -> HotOp {
Catch
}
///|
pub fn HotOp::catch_all() -> HotOp {
CatchAll
}
///|
pub fn HotOp::atomic_op() -> HotOp {
Atomic
}
///|
#valtype
pub struct HotNode {
op : HotOp
ty : TypeId
flags : UInt32
// Allocation order follows copied replacements; a storage slot does not.
order : Int32
imm0 : Int32
imm1 : Int32
child_base : Int32
child_count : Int32
} derive(Eq, Debug)
///|
pub impl Show for HotNode with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub struct HotLocals {
param_count : Int32
params : Array[ValType]
locals : Array[ValType]
} derive(Eq, Debug)
///|
pub impl Show for HotLocals with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub fn HotLocals::new(
param_count~ : Int32 = 0,
params~ : Array[ValType] = [],
locals~ : Array[ValType] = [],
) -> HotLocals {
if param_count != 0 && params.length() > 0 && param_count != params.length() {
abort(
"hot locals param_count must match params length when both are provided",
)
}
{
param_count: if params.length() > 0 {
params.length()
} else {
param_count
},
params,
locals,
}
}
///|
pub enum HotTypeInfo {
VoidType
BlockResult(BlockType)
BlockResultResolved(BlockType, Array[ValType], Array[ValType])
Value(ValType)
Results(Array[ValType])
} derive(Eq, Debug)
///|
pub enum HotLabelKind {
BlockLabel
LoopLabel
IfLabel
RegionLabel
TryLabel
TryTableLabel
} derive(Eq, Debug)
///|
pub struct HotLabelInfo {
kind : HotLabelKind
result_type : TypeId
owner : NodeId
branch_arity : Int32
} derive(Eq, Debug)
///|
pub impl Show for HotLabelInfo with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub enum HotConstPayload {
I32Const(I32)
I64Const(I64)
F32Const(F32)
F64Const(F64)
RefNullConst(RefType)
StringConst(Bytes)
} derive(Eq, Debug)
///|
pub impl Show for HotConstPayload with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub struct HotMemArg {
memarg : MemArg
} derive(Eq, Debug)
///|
pub impl Show for HotMemArg with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub struct HotBranchTable {
targets : Array[LabelId]
default_target : LabelId
} derive(Eq, Debug)
///|
pub impl Show for HotBranchTable with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub enum HotCatchArm {
CatchArm(TagIdx, LabelId)
CatchRefArm(TagIdx, LabelId)
CatchAllArm(LabelId)
CatchAllRefArm(LabelId)
} derive(Eq, Debug)
///|
pub impl Show for HotCatchArm with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub struct HotCatchInfo {
catches : Array[HotCatchArm]
} derive(Eq, Debug)
///|
pub impl Show for HotCatchInfo with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub struct HotCallSig {
type_idx : TypeIdx
table_idx : TableIdx?
} derive(Eq, Debug)
///|
pub impl Show for HotCallSig with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub struct HotInstrPayload {
instr : Instruction
} derive(Eq, Debug)
///|
pub impl Show for HotInstrPayload with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub enum HotStringBuiltin {
NewWtf16Array
} derive(Eq, Debug)
///|
pub impl Show for HotStringBuiltin with fn output(self, logger) {
logger.write_string(@debug.to_string(self))
}
///|
pub fn HotStringBuiltin::new_wtf16_array() -> HotStringBuiltin {
NewWtf16Array
}
///|
pub struct HotFunc {
nodes : Array[HotNode]
children : Array[NodeId]
roots : Array[NodeId]
locals : HotLocals
types : Array[HotTypeInfo]
type_ids : @hashmap.HashMap[String, TypeId]
value_types : Array[ValType]
value_type_ids : Array[TypeId]
block_types : Array[BlockType]
block_type_ids : Array[TypeId]
labels : Array[HotLabelInfo]
consts : Array[HotConstPayload]
memargs : Array[HotMemArg]
branch_tables : Array[HotBranchTable]
catches : Array[HotCatchInfo]
call_sigs : Array[HotCallSig]
instr_payloads : Array[HotInstrPayload]
mut body_result_type : TypeId
mut func_type_idx : TypeIdx?
free_nodes : Array[NodeId]
deleted_nodes : Array[Bool]
mut revision : Int32
// Shared module type context, retained once per lifted function for verification.
mut validation_env : @validate.Env?
mut lifted_local_accesses : HotLiftLocalAccesses?
}
///|
const HOT_NONE_NODE : NodeId = -1
///|
pub const HOT_IMPLICIT_FUNCTION_LABEL : LabelId = -2
///|
pub fn HotFunc::new(
locals~ : HotLocals = HotLocals::new(),
body_result_type~ : TypeId = 0,
) -> HotFunc {
let type_ids = @hashmap.new(capacity=16)
type_ids.set(HotTypeInfo::VoidType.to_string(), 0)
{
nodes: [],
children: [],
roots: [],
locals,
types: [HotTypeInfo::VoidType],
type_ids,
value_types: [],
value_type_ids: [],
block_types: [],
block_type_ids: [],
labels: [],
consts: [],
memargs: [],
branch_tables: [],
catches: [],
call_sigs: [],
instr_payloads: [],
body_result_type,
func_type_idx: None,
free_nodes: [],
deleted_nodes: [],
revision: 0,
validation_env: None,
lifted_local_accesses: None,
}
}
///|
pub fn hot_node_count(func : HotFunc) -> Int32 {
func.nodes.length()
}
///|
pub fn hot_child_storage_count(func : HotFunc) -> Int32 {
func.children.length()
}
///|
pub fn hot_root_storage_count(func : HotFunc) -> Int32 {
func.roots.length()
}
///|
pub fn hot_body_result_type(func : HotFunc) -> TypeId {
func.body_result_type
}
///|
fn hot_core_type_id_is_valid(func : HotFunc, ty : TypeId) -> Bool {
ty >= 0 && ty < func.types.length()
}
///|
pub fn hot_body_result_type_set(func : HotFunc, ty : TypeId) -> Unit {
if !hot_core_type_id_is_valid(func, ty) {
abort("hot body result type must reference a valid interned TypeId")
}
func.body_result_type = ty
func.revision += 1
}
///|
pub fn hot_node_is_live(func : HotFunc, node_id : NodeId) -> Bool {
// Keep normal getters on a direct path: the optional work probe belongs to
// the incomplete-bitmap fallback, not every read of a maintained HOT arena.
if node_id < 0 || node_id >= func.nodes.length() {
return false
}
if func.free_nodes.is_empty() {
return true
}
if node_id < func.deleted_nodes.length() {
return !func.deleted_nodes[node_id]
}
hot_node_is_live_impl(func, node_id)
}
///|
fn hot_node_is_live_impl(
func : HotFunc,
node_id : NodeId,
free_list_checks~ : Array[Int32]? = None,
) -> Bool {
if node_id < 0 || node_id >= func.nodes.length() {
return false
}
if func.free_nodes.is_empty() {
return true
}
if node_id < func.deleted_nodes.length() {
// Allocation and deletion already maintain this bitmap, including small
// free lists. Repeated getters can therefore answer without rescanning
// every tombstone. Keep the fallback for incomplete internal fixtures.
return !func.deleted_nodes[node_id]
}
if func.free_nodes.length() <= 32 {
for free_id in func.free_nodes {
match free_list_checks {
Some(checks) => checks[0] += 1
None => ()
}
if free_id == node_id {
return false
}
}
return true
}
true
}
///|
pub fn hot_debug_dump_core(func : HotFunc) -> String {
"nodes=\{hot_node_count(func)} children=\{hot_child_storage_count(func)} roots=\{hot_root_storage_count(func)} body_result_type=\{func.body_result_type} revision=\{func.revision} free_nodes=\{func.free_nodes.length()}"
}
///|
pub fn hot_make_simple_node(
op : HotOp,
ty : TypeId,
canonicalized_flags : UInt32,
imm0 : Int32,
imm1 : Int32,
children : Array[NodeId],
) -> HotNode {
// Flags are intentionally canonicalized from the opcode so callers cannot
// construct stale flag metadata through this convenience helper.
let _ = canonicalized_flags
{
op,
ty,
flags: hot_default_flags_for_op(op),
order: -1,
imm0,
imm1,
child_base: 0,
child_count: children.length(),
}
}