def Translate(mod: wasm.Module, addr_type: o.DK) -> ir.Unit: table_import = mod.sections.get(wasm.SECTION_ID.IMPORT) for i in table_import.items: assert isinstance(i, wasm.Import) assert isinstance(i.desc, wasm.TypeIdx), f"cannot handle strange imports: {i}" bit_width = addr_type.bitwidth() unit = ir.Unit("unit") global_argv = unit.AddMem( ir.Mem("global_argv", 2 * bit_width // 8, o.MEM_KIND.RW)) global_argv.AddData(ir.DataBytes(bit_width // 8, b"\0")) global_argc = unit.AddMem(ir.Mem("global_argc", 4, o.MEM_KIND.RW)) global_argc.AddData(ir.DataBytes(4, b"\0")) memcpy = GenerateMemcpyFun(unit, addr_type) init_global = GenerateInitGlobalVarsFun(mod, unit, addr_type) init_data = GenerateInitDataFun(mod, unit, memcpy, addr_type) unit.AddFun(ir.Fun("__wasi_init", o.FUN_KIND.EXTERN, [], [])) unit.AddFun( ir.Fun("__memory_grow", o.FUN_KIND.EXTERN, [o.DK.S32], [o.DK.S32])) main = None for wasm_fun in mod.functions: # forward declare everything since we cannot rely on a topological sort of the funs if isinstance(wasm_fun.impl, wasm.Import): assert wasm_fun.name in WASI_FUNCTIONS, f"unimplemented external function: {wasm_fun.name}" arguments = [addr_type] + TranslateTypeList(wasm_fun.func_type.args) returns = TranslateTypeList(wasm_fun.func_type.rets) # assert len(returns) <= 1 unit.AddFun( ir.Fun(wasm_fun.name, o.FUN_KIND.EXTERN, returns, arguments)) global_table = MaybeMakeGlobalTable(mod, unit, addr_type) for wasm_fun in mod.functions: if isinstance(wasm_fun.impl, wasm.Import): continue fun = unit.GetFun(wasm_fun.name) fun.kind = o.FUN_KIND.NORMAL if fun.name == "_start": fun.name = "$main" main = fun GenerateFun(unit, mod, wasm_fun, fun, global_table, addr_type) # print ("\n".join(serialize.FunRenderToAsm(fun))) sanity.FunCheck(fun, unit, check_cfg=False) initial_heap_size_pages = 0 sec_memory = mod.sections.get(wasm.SECTION_ID.MEMORY) if sec_memory: assert len(sec_memory.items) == 1 heap: wasm.Mem = sec_memory.items[0] initial_heap_size_pages = heap.mem_type.limits.min assert main, f"missing main function" GenerateStartup(unit, global_argc, global_argv, main, init_global, init_data, initial_heap_size_pages, addr_type) return unit
def BuildExample() -> ir.Unit: unit = ir.Unit("fib") fun_fib = unit.AddFun( ir.Fun("fib", o.FUN_KIND.NORMAL, [o.DK.U32], [o.DK.U32])) bbl_start = fun_fib.AddBbl(ir.Bbl("start")) bbl_difficult = fun_fib.AddBbl(ir.Bbl("difficult")) reg_in = fun_fib.AddReg(ir.Reg("in", o.DK.U32)) reg_x = fun_fib.AddReg(ir.Reg("x", o.DK.U32)) reg_out = fun_fib.AddReg(ir.Reg("out", o.DK.U32)) bbl_start.AddIns(ir.Ins(o.POPARG, [reg_in])) bbl_start.AddIns( ir.Ins(o.BLT, [ir.Const(o.DK.U32, 1), reg_in, bbl_difficult])) bbl_start.AddIns(ir.Ins(o.PUSHARG, [reg_in])) bbl_start.AddIns(ir.Ins(o.RET, [])) bbl_difficult.AddIns(ir.Ins(o.MOV, [reg_out, ir.Const(o.DK.U32, 0)])) bbl_difficult.AddIns(ir.Ins(o.SUB, [reg_x, reg_in, ir.Const(o.DK.U32, 1)])) bbl_difficult.AddIns(ir.Ins(o.PUSHARG, [reg_x])) bbl_difficult.AddIns(ir.Ins(o.BSR, [fun_fib])) bbl_difficult.AddIns(ir.Ins(o.POPARG, [reg_x])) bbl_difficult.AddIns(ir.Ins(o.ADD, [reg_out, reg_out, reg_x])) bbl_difficult.AddIns(ir.Ins(o.SUB, [reg_x, reg_in, ir.Const(o.DK.U32, 2)])) bbl_difficult.AddIns(ir.Ins(o.PUSHARG, [reg_x])) bbl_difficult.AddIns(ir.Ins(o.BSR, [fun_fib])) bbl_difficult.AddIns(ir.Ins(o.POPARG, [reg_x])) bbl_difficult.AddIns(ir.Ins(o.ADD, [reg_out, reg_out, reg_x])) bbl_difficult.AddIns(ir.Ins(o.PUSHARG, [reg_out])) bbl_difficult.AddIns(ir.Ins(o.RET, [])) return unit
def GenerateMemcpyFun(unit: ir.Unit, addr_type: o.DK) -> ir.Fun: fun = unit.AddFun( ir.Fun("$memcpy", o.FUN_KIND.NORMAL, [], [addr_type, addr_type, o.DK.U32])) dst = fun.AddReg(ir.Reg("dst", addr_type)) src = fun.AddReg(ir.Reg("src", addr_type)) cnt = fun.AddReg(ir.Reg("cnt", o.DK.U32)) data = fun.AddReg(ir.Reg("data", o.DK.U8)) prolog = fun.AddBbl(ir.Bbl("prolog")) loop = fun.AddBbl(ir.Bbl("loop")) epilog = fun.AddBbl(ir.Bbl("epilog")) prolog.AddIns(ir.Ins(o.POPARG, [dst])) prolog.AddIns(ir.Ins(o.POPARG, [src])) prolog.AddIns(ir.Ins(o.POPARG, [cnt])) prolog.AddIns(ir.Ins(o.BRA, [epilog])) loop.AddIns(ir.Ins(o.SUB, [cnt, cnt, ONE])) loop.AddIns(ir.Ins(o.LD, [data, src, cnt])) loop.AddIns(ir.Ins(o.ST, [dst, cnt, data])) epilog.AddIns(ir.Ins(o.BLT, [ZERO, cnt, loop])) epilog.AddIns(ir.Ins(o.RET, [])) return fun
def GenerateInitGlobalVarsFun(mod: wasm.Module, unit: ir.Unit, addr_type: o.DK) -> ir.Fun: fun = unit.AddFun(ir.Fun("init_global_vars_fun", o.FUN_KIND.NORMAL, [], [])) bbl = fun.AddBbl(ir.Bbl("start")) epilog = fun.AddBbl(ir.Bbl("end")) epilog.AddIns(ir.Ins(o.RET, [])) section = mod.sections.get(wasm.SECTION_ID.GLOBAL) if not section: return fun val32 = fun.AddReg(ir.Reg("val32", o.DK.U32)) val64 = fun.AddReg(ir.Reg("val64", o.DK.U64)) for n, data in enumerate(section.items): kind = o.MEM_KIND.RO if data.global_type.mut is wasm.MUT.CONST else o.MEM_KIND.RW mem = unit.AddMem(ir.Mem(f"global_vars_{n}", 16, kind)) ins = GetInsFromInitializerExpression(data.expr) var_type = data.global_type.value_type if ins.opcode is wasm_opc.GLOBAL_GET: mem.AddData( ir.DataBytes(1, b"\0" * (4 if var_type.is_32bit() else 8))) src_mem = unit.GetMem(f"global_vars_{int(ins.args[0])}") reg = val32 if var_type.is_32bit() else val64 bbl.AddIns(ir.Ins(o.LD_MEM, [reg, src_mem, ZERO])) bbl.AddIns(ir.Ins(o.ST_MEM, [mem, ZERO, reg])) elif ins.opcode.kind is wasm_opc.OPC_KIND.CONST: mem.AddData( ir.DataBytes(1, ExtractBytesFromConstIns(ins, var_type))) else: assert False, f"unsupported init instructions {ins}" return fun
def DirFun(unit: ir.Unit, operands: List): name, kind, output_types, input_types = operands if len(input_types) > o.MAX_PARAMETERS or len( output_types) > o.MAX_PARAMETERS: raise ParseError(f"parameter list too long {name}") fun = unit.GetFun(name) if fun is None: fun = ir.Fun(name, kind, output_types, input_types) unit.AddFun(fun) elif fun.forward_declared: unit.InitForwardDeclaredFun(fun, kind, output_types, input_types) else: raise ParseError(f"duplicate Fun {name}")
def GenerateStartup(unit: ir.Unit, global_argc, global_argv, main: ir.Fun, init_global: ir.Fun, init_data: ir.Fun, initial_heap_size_pages: int, addr_type: o.DK) -> ir.Fun: bit_width = addr_type.bitwidth() global_mem_base = unit.AddMem(ir.Mem("__memory_base", 0, o.MEM_KIND.EXTERN)) fun = unit.AddFun( ir.Fun("main", o.FUN_KIND.NORMAL, [o.DK.U32], [o.DK.U32, addr_type])) argc = fun.AddReg(ir.Reg("argc", o.DK.U32)) argv = fun.AddReg(ir.Reg("argv", addr_type)) bbl = fun.AddBbl(ir.Bbl("start")) bbl.AddIns(ir.Ins(o.POPARG, [argc])) bbl.AddIns(ir.Ins(o.POPARG, [argv])) bbl.AddIns(ir.Ins(o.ST_MEM, [global_argc, ZERO, argc])) bbl.AddIns(ir.Ins(o.ST_MEM, [global_argv, ZERO, argv])) bbl.AddIns(ir.Ins(o.BSR, [unit.GetFun("__wasi_init")])) if initial_heap_size_pages: bbl.AddIns( ir.Ins(o.PUSHARG, [ir.Const(o.DK.S32, initial_heap_size_pages)])) bbl.AddIns(ir.Ins(o.BSR, [unit.GetFun("__memory_grow")])) bbl.AddIns(ir.Ins(o.POPARG, [fun.AddReg(ir.Reg("dummy", o.DK.S32))])) mem_base = fun.AddReg(ir.Reg("mem_base", addr_type)) bbl.AddIns(ir.Ins(o.LD_MEM, [mem_base, global_mem_base, ZERO])) if init_global: bbl.AddIns(ir.Ins(o.BSR, [init_global])) if init_data: bbl.AddIns(ir.Ins(o.PUSHARG, [mem_base])) bbl.AddIns(ir.Ins(o.BSR, [init_data])) bbl.AddIns(ir.Ins(o.PUSHARG, [mem_base])) bbl.AddIns(ir.Ins(o.BSR, [main])) bbl.AddIns(ir.Ins(o.PUSHARG, [ir.Const(o.DK.U32, 0)])) bbl.AddIns(ir.Ins(o.RET, [])) return fun
def GenerateInitDataFun(mod: wasm.Module, unit: ir.Unit, memcpy: ir.Fun, addr_type: o.DK) -> typing.Optional[ir.Fun]: fun = unit.AddFun( ir.Fun("init_data_fun", o.FUN_KIND.NORMAL, [], [addr_type])) bbl = fun.AddBbl(ir.Bbl("start")) epilog = fun.AddBbl(ir.Bbl("end")) epilog.AddIns(ir.Ins(o.RET, [])) section = mod.sections.get(wasm.SECTION_ID.DATA) mem_base = fun.AddReg(ir.Reg("mem_base", addr_type)) bbl.AddIns(ir.Ins(o.POPARG, [mem_base])) if not section: return None offset = fun.AddReg(ir.Reg("offset", o.DK.S32)) src = fun.AddReg(ir.Reg("src", addr_type)) dst = fun.AddReg(ir.Reg("dst", addr_type)) for n, data in enumerate(section.items): assert data.memory_index == 0 assert isinstance(data.offset, wasm.Expression) ins = GetInsFromInitializerExpression(data.offset) init = unit.AddMem(ir.Mem(f"global_init_mem_{n}", 16, o.MEM_KIND.RO)) init.AddData(ir.DataBytes(1, data.init)) if ins.opcode is wasm_opc.GLOBAL_GET: src_mem = unit.GetMem(f"global_vars_{int(ins.args[0])}") bbl.AddIns(ir.Ins(o.LD_MEM, [offset, src_mem, ZERO])) elif ins.opcode is wasm_opc.I32_CONST: bbl.AddIns(ir.Ins(o.MOV, [offset, ir.Const(o.DK.S32, ins.args[0])])) else: assert False, f"unsupported init instructions {ins}" bbl.AddIns(ir.Ins(o.LEA, [dst, mem_base, offset])) bbl.AddIns(ir.Ins(o.LEA_MEM, [src, init, ZERO])) bbl.AddIns(ir.Ins(o.PUSHARG, [ir.Const(o.DK.U32, len(data.init))])) bbl.AddIns(ir.Ins(o.PUSHARG, [src])) bbl.AddIns(ir.Ins(o.PUSHARG, [dst])) bbl.AddIns(ir.Ins(o.BSR, [memcpy])) return fun
def DirFun(unit: ir.Unit, operands: List): name, kind, output_types, input_types = operands if len(input_types) > o.MAX_PARAMETERS or len( output_types) > o.MAX_PARAMETERS: raise ParseError(f"parameter list too long {name}") fun = unit.GetFun(name) if fun is None: fun = ir.Fun(name, kind, output_types, input_types) unit.AddFun(fun) elif fun.kind is o.FUN_KIND.INVALID: # forward_declared unit.InitForwardDeclaredFun(fun, kind, output_types, input_types) elif fun.kind is o.FUN_KIND.EXTERN or kind is o.FUN_KIND.EXTERN: assert output_types == fun.output_types assert input_types == fun.input_types if kind is o.FUN_KIND.EXTERN: # we already have a proper function return # definition of a formerly extern functions fun.kind = kind # move fun to make it current unit.funs.remove(fun) unit.funs.append(fun) else: raise ParseError(f"duplicate Fun {name}")