use crate::bytecode::{AbilitySet, FunctionDef, MoveModule};
use anyhow::{bail, Context, Result};
use std::collections::HashMap;
use wasm_encoder::{BlockType, CodeSection, Function, FunctionSection, Instruction, ValType};
use super::super::analysis::{
analyze_stack, derive_slot_types, effective_locals, stack_effect, val_type_from_tag,
};
use super::super::resources::build_struct_lookup;
use super::instructions::emit_case_body;
use super::ImportLayout;
pub fn build_functions(
module: &MoveModule,
func_type_indices: &[u32],
imported_functions: u32,
import_layout: &ImportLayout,
needs_storage: bool,
) -> Result<(FunctionSection, CodeSection)> {
let mut functions = FunctionSection::new();
let mut code = CodeSection::new();
let struct_lookup = build_struct_lookup(&module.structs);
for (idx, func) in module.functions.iter().enumerate() {
functions.function(func_type_indices[idx]);
let wasm_func = lower_function(
module,
func,
imported_functions,
import_layout,
needs_storage,
&struct_lookup,
)
.with_context(|| format!("lowering function {}", func.name))?;
code.function(&wasm_func);
}
Ok((functions, code))
}
fn lower_function(
module: &MoveModule,
func: &FunctionDef,
import_offset: u32,
imports: &ImportLayout,
needs_storage: bool,
struct_lookup: &HashMap<String, AbilitySet>,
) -> Result<Function> {
let locals = effective_locals(func);
if locals.len() < func.parameters.len() {
bail!(
"function {} has {} parameters but only {} locals",
func.name,
func.parameters.len(),
locals.len()
);
}
let stack_states = analyze_stack(func, module, struct_lookup)?;
let slot_types = derive_slot_types(&stack_states);
let mut local_types: Vec<ValType> = locals[func.parameters.len()..]
.iter()
.map(val_type_from_tag)
.collect();
let base_local_index = func.parameters.len() as u32;
let pc_local = base_local_index + local_types.len() as u32;
local_types.push(ValType::I32);
let tmp_a_local = base_local_index + local_types.len() as u32;
local_types.push(ValType::I64);
let tmp_b_local = base_local_index + local_types.len() as u32;
local_types.push(ValType::I64);
let mut stack_slots = Vec::new();
for kind in &slot_types {
let idx = base_local_index + local_types.len() as u32;
stack_slots.push(idx);
local_types.push(kind.val_type());
}
let mut wasm_locals = Vec::new();
for ty in local_types {
wasm_locals.push((1, ty));
}
let case_count = func.code.len();
let mut f = Function::new(wasm_locals);
f.instruction(&Instruction::I32Const(0));
f.instruction(&Instruction::LocalSet(pc_local));
f.instruction(&Instruction::Loop(BlockType::Empty)); f.instruction(&Instruction::Block(BlockType::Empty));
for _ in 0..case_count {
f.instruction(&Instruction::Block(BlockType::Empty));
}
f.instruction(&Instruction::LocalGet(pc_local));
let targets: Vec<_> = (0..case_count as u32).collect();
f.instruction(&Instruction::BrTable(targets.into(), case_count as u32));
for (idx, opcode) in func.code.iter().enumerate() {
f.instruction(&Instruction::End); let dispatch_depth = (case_count - idx) as u32;
let entry_state = stack_states.get(idx).cloned().flatten();
if entry_state.is_none() {
f.instruction(&Instruction::Unreachable);
f.instruction(&Instruction::Br(dispatch_depth));
continue;
}
let entry_stack = entry_state.unwrap();
let (stack_after, _) = stack_effect(
opcode,
func,
module,
&locals,
struct_lookup,
entry_stack.clone(),
)?;
emit_case_body(
module,
opcode,
idx,
dispatch_depth,
pc_local,
tmp_a_local,
tmp_b_local,
import_offset,
imports,
needs_storage,
struct_lookup,
&entry_stack,
&stack_after,
&stack_slots,
&slot_types,
&mut f,
)?;
}
f.instruction(&Instruction::End); f.instruction(&Instruction::Unreachable);
f.instruction(&Instruction::End); f.instruction(&Instruction::Unreachable);
f.instruction(&Instruction::End);
Ok(f)
}