use dfwasm_template::{Args, Template};
use wasmparser::Operator;
use crate::{
DFWasmCompiler, DFWasmResult,
compiler::ControlStackEntry,
df_helper::{
DF_FUNC_CONTROL_LOOP_CHECK, DF_FUNC_TRAP, DF_VAR_BRANCH_COUNTER, TemplateExt,
format_df_number_usize, generate_block_name, generate_conditional_name, generate_loop_name,
num, string, var,
},
};
pub fn compile_control_flow_operator(
compiler: &mut DFWasmCompiler,
operator: Operator,
location: usize,
) -> DFWasmResult<()> {
let templates_len = compiler.templates.len();
let template = compiler.get_current_template();
match operator {
Operator::Block { .. } => {
let block_id = templates_len;
let block_func_name =
generate_block_name(block_id, compiler.options.module_name.as_deref());
let template = compiler.get_current_template();
template
.repeat("Multiple", Args::with(vec![num("1")]))
.open_bracket_repeat()
.call_function(DF_FUNC_CONTROL_LOOP_CHECK, Args::default())
.call_function(block_func_name.clone(), Args::default())
.close_bracket_repeat()
.call_function(DF_FUNC_CONTROL_LOOP_CHECK, Args::default());
compiler
.templates
.push(Template::start_function_hidden(block_func_name));
compiler
.control_stack
.push(ControlStackEntry::Block(block_id));
}
Operator::Loop { .. } => {
let loop_id = templates_len;
let loop_func_name =
generate_loop_name(loop_id, compiler.options.module_name.as_deref());
let template = compiler.get_current_template();
template
.repeat("Forever", Args::default())
.open_bracket_repeat()
.call_function(DF_FUNC_CONTROL_LOOP_CHECK, Args::default())
.call_function(loop_func_name.clone(), Args::default())
.control("StopRepeat", Args::default())
.close_bracket_repeat()
.call_function(DF_FUNC_CONTROL_LOOP_CHECK, Args::default());
compiler
.templates
.push(Template::start_function_hidden(loop_func_name));
compiler
.control_stack
.push(ControlStackEntry::Loop(loop_id));
}
Operator::If { .. } => {
let if_id = templates_len;
let if_func_name =
generate_conditional_name(if_id, compiler.options.module_name.as_deref());
let template = compiler.get_current_template();
template
.pop_op_stack(var("$a"))
.if_var("!=", Args::with(vec![var("$a"), num("0")]))
.open_bracket()
.call_function(if_func_name.clone(), Args::default())
.close_bracket();
compiler
.templates
.push(Template::start_function_hidden(if_func_name));
compiler.control_stack.push(ControlStackEntry::If(if_id));
}
Operator::Else => {
compiler
.control_stack
.pop()
.expect("Control stack is empty");
let if_id = templates_len;
let if_func_name =
generate_conditional_name(if_id, compiler.options.module_name.as_deref());
let template = compiler.get_current_template();
template
.else_block()
.open_bracket()
.call_function(if_func_name.clone(), Args::default())
.close_bracket();
compiler
.templates
.push(Template::start_function_hidden(if_func_name));
compiler.control_stack.push(ControlStackEntry::Else(if_id));
}
Operator::End => {
compiler
.control_stack
.pop()
.expect("Control stack is empty");
}
Operator::Br { relative_depth } => compile_branch_operator(compiler, relative_depth),
Operator::BrIf { relative_depth } => {
template
.pop_op_stack(var("$cond"))
.if_var("!=", Args::with(vec![var("$cond"), num("0")]))
.open_bracket();
compile_branch_operator(compiler, relative_depth);
let template = compiler.get_current_template();
template.close_bracket();
}
Operator::BrTable { targets } => {
template.pop_op_stack(var("$index"));
for (i, target) in targets.targets().enumerate() {
let target = target?;
let template = compiler.get_current_template();
template
.if_var(
"=",
Args::with(vec![var("$index"), num(format_df_number_usize(i))]),
)
.open_bracket();
compile_branch_operator(compiler, target);
let template = compiler.get_current_template();
template.close_bracket();
}
let default_target = targets.default();
compile_branch_operator(compiler, default_target);
}
Operator::Return => {
let loop_depth = compiler
.control_stack
.iter()
.filter(|entry| {
matches!(
entry,
ControlStackEntry::Loop(_) | ControlStackEntry::Block(_)
)
})
.count();
let template = compiler.get_current_template();
if loop_depth == 0 {
template.control("Skip", Args::default());
} else {
template
.set_var(
"=",
Args::with(vec![var(DF_VAR_BRANCH_COUNTER), num(loop_depth)]),
)
.control("StopRepeat", Args::default());
}
}
Operator::Unreachable => {
template.call_function(
DF_FUNC_TRAP,
Args::with(vec![
string("unreachable"),
string(format!("{location:#x}")),
]),
);
}
_ => unreachable!(),
}
Ok(())
}
fn compile_branch_operator(compiler: &mut DFWasmCompiler, relative_control_stack_depth: u32) {
let control_stack_idx =
compiler.control_stack.len() - 1 - relative_control_stack_depth as usize;
let control_stack_entry = compiler
.control_stack
.get(control_stack_idx)
.expect("Control stack entry not found");
let loop_depth = compiler.control_stack[(control_stack_idx + 1)..]
.iter()
.filter(|entry| {
matches!(
entry,
ControlStackEntry::Block(_) | ControlStackEntry::Loop(_)
)
})
.count();
match control_stack_entry {
ControlStackEntry::Block(_) | ControlStackEntry::Loop(_) => {
let template = compiler.get_current_template();
if loop_depth == 0 {
template.control("Skip", Args::default());
} else {
template
.set_var(
"=",
Args::with(vec![var(DF_VAR_BRANCH_COUNTER), num(loop_depth)]),
)
.control("StopRepeat", Args::default());
}
}
_ => panic!(
"Expected a block or loop at the relative depth, but got {control_stack_entry:?}"
),
}
}