///|
fn hot_make_node_with_children(
func : HotFunc,
op : HotOp,
ty : TypeId,
canonicalized_flags : UInt32,
imm0 : Int32,
imm1 : Int32,
children : Array[NodeId],
) -> NodeId {
// Flags are intentionally canonicalized from the opcode, matching
// hot_make_simple_node and hot_replace_node.
let _ = canonicalized_flags
let id = hot_alloc_node(func, {
op,
ty,
flags: hot_default_flags_for_op(op),
order: -1,
imm0,
imm1,
child_base: 0,
child_count: 0,
})
if children.length() > 0 {
ignore(hot_replace_child_span(func, id, children))
}
id
}
///|
fn hot_replace_children(
func : HotFunc,
id : NodeId,
children : Array[NodeId],
) -> Unit {
ignore(hot_replace_child_span(func, id, children))
}
///|
fn hot_pop_value(values : Array[NodeId]) -> NodeId {
match values.pop() {
Some(id) => id
None => abort("hot lift stack underflow")
}
}
///|
fn hot_pop_values(values : Array[NodeId], count : Int32) -> Array[NodeId] {
let out = Array::make(count, HOT_NONE_NODE)
for slot = count - 1; slot >= 0; slot = slot - 1 {
out[slot] = hot_pop_value(values)
}
out
}
///|
fn hot_region_body(
func : HotFunc,
expr : Expr,
label_stack : Array[LabelId],
result_arity : Int32,
) -> Array[NodeId] {
let Expr(instrs) = expr
let roots : Array[NodeId] = []
let values : Array[NodeId] = []
for instr in instrs {
hot_lift_instruction(func, instr, label_stack, roots, values)
}
let keep = if result_arity > values.length() {
values.length()
} else {
result_arity
}
let start = values.length() - keep
for i = start; i < values.length(); i = i + 1 {
roots.push(values[i])
}
if keep == 0 {
for value in values {
roots.push(value)
}
}
roots
}
///|
fn hot_wrap_region(
func : HotFunc,
expr : Expr,
label_stack : Array[LabelId],
result_arity : Int32,
) -> NodeId {
let id = hot_make_node_with_children(
func,
HotOp::Block,
hot_type_void(func),
hot_default_flags_for_op(HotOp::Block),
-1,
0,
[],
)
let label_id = hot_label_alloc(
func,
HotLabelKind::RegionLabel,
hot_type_void(func),
id,
result_arity,
)
hot_replace_children(
func,
id,
hot_region_body(func, expr, label_stack, result_arity),
)
hot_node_set(func, id, { ..hot_node_get(func, id), imm0: label_id, })
id
}
///|
fn hot_lift_instruction(
func : HotFunc,
instr : Instruction,
label_stack : Array[LabelId],
roots : Array[NodeId],
values : Array[NodeId],
) -> Unit {
match instr {
I32Const(v) => {
let const_id = hot_alloc_const(func, HotConstPayload::I32Const(v))
values.push(
hot_alloc_node(
func,
hot_make_simple_node(
HotOp::Const,
hot_type_void(func),
hot_default_flags_for_op(HotOp::Const),
const_id,
0,
[],
),
),
)
}
I64Const(v) => {
let const_id = hot_alloc_const(func, HotConstPayload::I64Const(v))
values.push(
hot_alloc_node(
func,
hot_make_simple_node(
HotOp::Const,
hot_type_void(func),
hot_default_flags_for_op(HotOp::Const),
const_id,
0,
[],
),
),
)
}
RefNull(rt) => {
let const_id = hot_alloc_const(func, HotConstPayload::RefNullConst(rt))
values.push(
hot_alloc_node(
func,
hot_make_simple_node(
HotOp::RefNull,
hot_type_void(func),
hot_default_flags_for_op(HotOp::RefNull),
const_id,
0,
[],
),
),
)
}
StringConst(bytes) => {
let const_id = hot_alloc_const(func, HotConstPayload::StringConst(bytes))
values.push(
hot_alloc_node(
func,
hot_make_simple_node(
HotOp::Const,
hot_type_void(func),
hot_default_flags_for_op(HotOp::Const),
const_id,
0,
[],
),
),
)
}
LocalGet(LocalIdx(raw)) =>
values.push(
hot_alloc_node(
func,
hot_make_simple_node(
HotOp::LocalGet,
hot_type_void(func),
hot_default_flags_for_op(HotOp::LocalGet),
raw.reinterpret_as_int(),
0,
[],
),
),
)
GlobalGet(GlobalIdx(raw)) =>
values.push(
hot_alloc_node(
func,
hot_make_simple_node(
HotOp::GlobalGet,
hot_type_void(func),
hot_default_flags_for_op(HotOp::GlobalGet),
raw.reinterpret_as_int(),
0,
[],
),
),
)
Nop =>
roots.push(
hot_alloc_node(
func,
hot_make_simple_node(
HotOp::Nop,
hot_type_void(func),
hot_default_flags_for_op(HotOp::Nop),
0,
0,
[],
),
),
)
Unreachable =>
roots.push(
hot_alloc_node(
func,
hot_make_simple_node(
HotOp::Unreachable,
hot_type_void(func),
hot_default_flags_for_op(HotOp::Unreachable),
0,
0,
[],
),
),
)
Drop =>
roots.push(
hot_make_node_with_children(
func,
HotOp::Drop,
hot_type_void(func),
hot_default_flags_for_op(HotOp::Drop),
0,
0,
[hot_pop_value(values)],
),
)
LocalSet(LocalIdx(raw)) =>
roots.push(
hot_make_node_with_children(
func,
HotOp::LocalSet,
hot_type_void(func),
hot_default_flags_for_op(HotOp::LocalSet),
raw.reinterpret_as_int(),
0,
[hot_pop_value(values)],
),
)
LocalTee(LocalIdx(raw)) => {
let id = hot_make_node_with_children(
func,
HotOp::LocalTee,
hot_type_void(func),
hot_default_flags_for_op(HotOp::LocalTee),
raw.reinterpret_as_int(),
0,
[hot_pop_value(values)],
)
roots.push(id)
values.push(id)
}
GlobalSet(GlobalIdx(raw)) =>
roots.push(
hot_make_node_with_children(
func,
HotOp::GlobalSet,
hot_type_void(func),
hot_default_flags_for_op(HotOp::GlobalSet),
raw.reinterpret_as_int(),
0,
[hot_pop_value(values)],
),
)
Block(block_type, expr) => {
let ty = hot_type_intern_block_result(func, block_type)
let id = hot_make_node_with_children(
func,
HotOp::Block,
ty,
hot_default_flags_for_op(HotOp::Block),
-1,
0,
[],
)
let result_arity = hot_type_result_arity(func, ty)
let label_id = hot_label_alloc(
func,
HotLabelKind::BlockLabel,
ty,
id,
result_arity,
)
let next_stack = label_stack.copy()
next_stack.push(label_id)
hot_replace_children(
func,
id,
hot_region_body(func, expr, next_stack, result_arity),
)
hot_node_set(func, id, { ..hot_node_get(func, id), imm0: label_id, })
if result_arity > 0 {
values.push(id)
} else {
roots.push(id)
}
}
Loop(block_type, expr) => {
let ty = hot_type_intern_block_result(func, block_type)
let id = hot_make_node_with_children(
func,
HotOp::Loop,
ty,
hot_default_flags_for_op(HotOp::Loop),
-1,
0,
[],
)
let result_arity = hot_type_result_arity(func, ty)
let label_id = hot_label_alloc(
func,
HotLabelKind::LoopLabel,
ty,
id,
result_arity,
)
let next_stack = label_stack.copy()
next_stack.push(label_id)
hot_replace_children(
func,
id,
hot_region_body(func, expr, next_stack, result_arity),
)
hot_node_set(func, id, { ..hot_node_get(func, id), imm0: label_id, })
if result_arity > 0 {
values.push(id)
} else {
roots.push(id)
}
}
If(block_type, then_instrs, else_instrs) => {
let ty = hot_type_intern_block_result(func, block_type)
let id = hot_make_node_with_children(
func,
HotOp::If,
ty,
hot_default_flags_for_op(HotOp::If),
-1,
0,
[],
)
let result_arity = hot_type_result_arity(func, ty)
let label_id = hot_label_alloc(
func,
HotLabelKind::IfLabel,
ty,
id,
result_arity,
)
let next_stack = label_stack.copy()
next_stack.push(label_id)
let then_region = hot_wrap_region(
func,
Expr::new(then_instrs),
next_stack,
result_arity,
)
let else_region = match else_instrs {
Some(instrs) =>
hot_wrap_region(func, Expr::new(instrs), next_stack, result_arity)
None => HOT_NONE_NODE
}
hot_replace_children(func, id, [
hot_pop_value(values),
then_region,
else_region,
])
hot_node_set(func, id, { ..hot_node_get(func, id), imm0: label_id, })
if result_arity > 0 {
values.push(id)
} else {
roots.push(id)
}
}
Return =>
roots.push(
hot_make_node_with_children(
func,
HotOp::Return,
hot_type_void(func),
hot_default_flags_for_op(HotOp::Return),
0,
0,
hot_pop_values(
values,
hot_type_result_arity(func, func.body_result_type),
),
),
)
_ => abort("unsupported instruction for hot lift")
}
}
///|
pub fn hot_lift_body(
body : Expr,
locals : HotLocals,
body_result_type~ : BlockType = BlockType::void_(),
) -> HotFunc {
match hot_lift_body_result(body, locals, body_result_type~) {
Ok(func) => func
Err(err) => abort(err.to_string())
}
}