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 main(): parser = argparse.ArgumentParser(description='CodeGenC') parser.add_argument('--trace', action='store_const', const=True, default=False, help='show info after every step') parser.add_argument('--debug_parser', action='store_const', const=True, default=False, help='dump module before starting execution') parser.add_argument('filename', type=str, help='file to execute') args = parser.parse_args() if args.filename == "-": fin = sys.stdin else: fin = open(args.filename) unit = serialize.UnitParseFromAsm(fin, args.debug_parser) assert "main" in unit.fun_syms print(PROLOG) for fun in unit.funs: if fun.kind is o.FUN_KIND.BUILTIN: continue sanity.FunCheck(fun, unit, check_push_pop=True, check_cfg=False) EmitFunctionProto(fun, True) print(";") for mem in unit.mems: EmitMemory(mem) for fun in unit.funs: if fun.kind is o.FUN_KIND.BUILTIN: continue EmitFunction(fun) print(EPILOG)
def FunCfgInit(fun: ir.Fun, unit: ir.Unit): cfg.FunSplitBbls(fun) cfg.FunInitCFG(fun) cfg.FunRemoveUnconditionalBranches(fun) cfg.FunRemoveEmptyBbls(fun) sanity.FunCheck(fun, unit, check_cfg=True, check_push_pop=True, check_fallthroughs=False)
def LegalizeAll(unit, opt_stats, fout, verbose=False): for fun in unit.funs: sanity.FunCheck(fun, unit, check_cfg=False, check_push_pop=True) if fun.kind is o.FUN_KIND.NORMAL: legalize.PhaseOptimize(fun, unit, opt_stats, fout) for fun in unit.funs: legalize.PhaseLegalization(fun, unit, opt_stats, fout)
def PhaseLegalization(fun: ir.Fun, unit: ir.Unit, _opt_stats: Dict[str, int], fout): """ Does a lot of the heavily lifting so that the instruction selector can remain simple and table driven. * lift almost all regs to 32bit width * rewrite Ins that cannot be expanded * rewrite immediates that cannot be expanded except stack offsets which are dealt with in another pass TODO: missing is a function to change calling signature so that """ lowering.FunRegWidthWidening(fun, o.DK.U8, o.DK.U32) lowering.FunRegWidthWidening(fun, o.DK.S8, o.DK.S32) lowering.FunRegWidthWidening(fun, o.DK.S16, o.DK.S32) lowering.FunRegWidthWidening(fun, o.DK.U16, o.DK.U32) fun.cpu_live_in = regs.GetCpuRegsForSignature(fun.input_types) fun.cpu_live_out = regs.GetCpuRegsForSignature(fun.output_types) if fun.kind is not o.FUN_KIND.NORMAL: return # ARM has no mod instruction lowering.FunEliminateRem(fun) # ARM has not support for these addressing modes lowering.FunEliminateStkLoadStoreWithRegOffset(fun, base_kind=o.DK.A32, offset_kind=o.DK.S32) # No floating point immediates lowering.FunMoveImmediatesToMemory(fun, unit, o.DK.F32) lowering.FunMoveImmediatesToMemory(fun, unit, o.DK.F64) # also handles ld_mem from two transformations above lowering.FunEliminateMemLoadStore(fun, base_kind=o.DK.A32, offset_kind=o.DK.S32) canonicalize.FunCanonicalize(fun) # TODO: add a cfg linearization pass to improve control flow optimize.FunCfgExit( fun, unit) # not this may affect immediates as it flips branches # Handle most overflowing immediates. # This excludes immediates related to stack offsets which have not been determined yet lowering.FunEliminateImmediateStores(fun) # handles st_stk immediates _FunRewriteOutOfBoundsImmediates(fun) # hack: some of the code expansion templates need a scratch reg # we do not want to reserve registers for this globally, so instead # we inject some nop instructions that reserve a register that we # use as a scratch for the instruction immediately following the nop isel_tab.FunAddNop1ForCodeSel(fun) sanity.FunCheck(fun, None)
def PhaseLegalization(fun: ir.Fun, unit: ir.Unit, _opt_stats: Dict[str, int], fout): """ Does a lot of the heavily lifting so that the instruction selector can remain simple and table driven. * lift almost all regs to 32bit width * rewrite Ins that cannot be expanded * rewrite immediates that cannot be expanded except stack offsets which are dealt with in another pass TODO: missing is a function to change calling signature so that """ lowering.FunRegWidthWidening(fun, o.DK.U8, o.DK.U32) lowering.FunRegWidthWidening(fun, o.DK.S8, o.DK.S32) lowering.FunRegWidthWidening(fun, o.DK.S16, o.DK.S32) lowering.FunRegWidthWidening(fun, o.DK.U16, o.DK.U32) fun.cpu_live_in = regs.PushPopInterface.GetCpuRegsForInSignature(fun.input_types) fun.cpu_live_out = regs.PushPopInterface.GetCpuRegsForOutSignature(fun.output_types) if fun.kind is not o.FUN_KIND.NORMAL: return # Getting rid of the pusharg/poparg now relieves us form having to pay to attention to the # invariant that pushargs/popargs must be adjacent. lowering.FunPushargConversion(fun, regs.PushPopInterface) lowering.FunPopargConversion(fun, regs.PushPopInterface) # ARM has no mod instruction lowering.FunEliminateRem(fun) # A64 has not support for these addressing modes lowering.FunEliminateStkLoadStoreWithRegOffset(fun, base_kind=o.DK.A64, offset_kind=o.DK.S32) # we cannot load/store directly from mem so expand the instruction to simpler # sequences lowering.FunEliminateMemLoadStore(fun, base_kind=o.DK.A64, offset_kind=o.DK.S32) canonicalize.FunCanonicalize(fun) # TODO: add a cfg linearization pass to improve control flow optimize.FunCfgExit(fun, unit) # not this may affect immediates as it flips branches # Handle most overflowing immediates. # This excludes immediates related to stack offsets which have not been determined yet _FunRewriteOutOfBoundsImmediates(fun, unit) sanity.FunCheck(fun, None)
def RegAllocGlobal(unit, opt_stats, fout, verbose=False): for fun in unit.funs: sanity.FunCheck(fun, unit, check_cfg=False, check_push_pop=False) legalize.PhaseGlobalRegAlloc(fun, opt_stats, fout) if verbose: legalize.DumpFun("after global_reg_alloc", fun)
def FunCfgExit(fun: ir.Fun, unit: ir.Unit): cfg.FunAddUnconditionalBranches(fun) sanity.FunCheck(fun, unit, check_fallthroughs=True)
def PhaseLegalization(fun: ir.Fun, unit: ir.Unit, _opt_stats: Dict[str, int], fout): """ Does a lot of the heavily lifting so that the instruction selector can remain simple and table driven. * lift almost all regs to 32bit width * rewrite Ins that cannot be expanded * rewrite immediates that cannot be expanded except stack offsets which are dealt with in another pass TODO: missing is a function to change calling signature so that """ fun.cpu_live_in = regs.PushPopInterface.GetCpuRegsForInSignature( fun.input_types) fun.cpu_live_out = regs.PushPopInterface.GetCpuRegsForOutSignature( fun.output_types) if fun.kind is not o.FUN_KIND.NORMAL: return # Getting rid of the pusharg/poparg now relieves us form having to pay to attention to the # invariant that pushargs/popargs must be adjacent. lowering.FunPushargConversion(fun, regs.PushPopInterface) lowering.FunPopargConversion(fun, regs.PushPopInterface) # We did not bother with this addressing mode # TODO: we like can avoid this by adding more cases to isel_tab.py lowering.FunEliminateStkLoadStoreWithRegOffset(fun, base_kind=o.DK.A64, offset_kind=o.DK.S32) # TODO: switch this to a WithRegOffset flavor lowering.FunEliminateMemLoadStore(fun, base_kind=o.DK.A64, offset_kind=o.DK.S32) lowering.FunEliminateCopySign(fun) # TODO: support a few special cases in the isel, e.g. cmpXX a 0, 1, x, y lowering.FunEliminateCmp(fun) canonicalize.FunCanonicalize(fun) # TODO: add a cfg linearization pass to improve control flow optimize.FunCfgExit( fun, unit) # not this may affect immediates as it flips branches # Handle most overflowing immediates. # This excludes immediates related to stack offsets which have not been determined yet _FunRewriteOutOfBoundsImmediates(fun, unit) # mul/div/rem need special treatment _FunRewriteDivRem(fun) _FunRewriteIntoAABForm(fun, unit) # Recompute Everything (TODO: make this more selective to reduce work) reg_stats.FunComputeRegStatsExceptLAC(fun) reg_stats.FunDropUnreferencedRegs(fun) liveness.FunComputeLivenessInfo(fun) reg_stats.FunComputeRegStatsLAC(fun) reg_stats.FunSeparateLocalRegUsage( fun ) # this has special hacks to avoid undoing _FunRewriteIntoAABForm() # DumpRegStats(fun, local_reg_stats) # if fun.name == "fibonacci": DumpFun("end of legal", fun) # if fun.name == "write_s": exit(1) sanity.FunCheck(fun, None)
def PhaseLegalization(fun: ir.Fun, unit: ir.Unit, _opt_stats: Dict[str, int], fout): """ Does a lot of the heavily lifting so that the instruction selector can remain simple and table driven. * lift almost all regs to 32bit width * rewrite Ins that cannot be expanded * rewrite immediates that cannot be expanded (stack offsets which are not known yet are ignored and we rely on being able to select all (reasonable) stack offsets) TODO: missing is a function to change calling signature so that """ # shifts on A32 are saturating but Cwerg requires (mod <bitwidth>) lowering.FunLimitShiftAmounts(fun, 32) # lift everything to 32 bit lowering.FunRegWidthWidening(fun, o.DK.U8, o.DK.U32) lowering.FunRegWidthWidening(fun, o.DK.S8, o.DK.S32) lowering.FunRegWidthWidening(fun, o.DK.S16, o.DK.S32) lowering.FunRegWidthWidening(fun, o.DK.U16, o.DK.U32) fun.cpu_live_in = regs.PushPopInterface.GetCpuRegsForInSignature( fun.input_types) fun.cpu_live_out = regs.PushPopInterface.GetCpuRegsForOutSignature( fun.output_types) if fun.kind is not o.FUN_KIND.NORMAL: return # replaces pusharg and poparg instructions and replace them with moves # The moves will use pre-allocated regs (the once use for argument/result passing) # We eliminate pusharg/poparg early because out immediate elimination code may # introduce addtional instructions which make it difficult to perserve the invariant # that all poparg/pusharg related to a call be adjecent. lowering.FunPushargConversion(fun, regs.PushPopInterface) lowering.FunPopargConversion(fun, regs.PushPopInterface) # ARM has no mod instruction lowering.FunEliminateRem(fun) # A32 has not support for these addressing modes. # They will be rewritten using lea.stk lowering.FunEliminateStkLoadStoreWithRegOffset(fun, base_kind=o.DK.A32, offset_kind=o.DK.S32) # we cannot load/store directly from mem so expand the instruction to simpler # sequences lowering.FunEliminateMemLoadStore(fun, base_kind=o.DK.A32, offset_kind=o.DK.S32) canonicalize.FunCanonicalize(fun) # TODO: add a cfg linearization pass to improve control flow optimize.FunCfgExit( fun, unit) # not this may affect immediates as it flips branches # Handle most overflowing immediates. _FunRewriteOutOfBoundsImmediates(fun, unit) # hack: some of the code expansion templates need a scratch reg # we do not want to reserve registers for this globally, so instead # we inject some nop instructions that reserve a register that we # use as a scratch for the instruction immediately following the nop isel_tab.FunAddNop1ForCodeSel(fun) sanity.FunCheck(fun, None)