import {
Stack = "src.utils:Stack";
+ pprint = "lualib.pprint";
}
--call_indirect is dynamic
["drop"] = { 1, 0 };
- ["select"] = { 3, 0 };
+ ["select"] = { 3, 1 };
["local.get"] = { 0, 1 };
["local.set"] = { 1, 0 };
["f64.reinterpret_i64"] = { 1, 1 };
}
+local build_instr_list
+
+function binop(op)
+ return function(stack, instr)
+ local right = stack:pop()
+ local left = stack:pop()
+ stack:push("(" .. left .. " " .. op .. " " .. right .. ")")
+ end
+end
+
+function cast(to)
+ return function(stack, instr)
+ local thing = stack:pop()
+ stack:push("<" .. to .. ">" .. thing)
+ end
+end
+
+function func_call(name, nparams)
+ nparams = nparams or 1
+ return function(stack, instr)
+ local str = name .. "("
+ local args = ""
+ for i=1,nparams do
+ local val = stack:pop()
+ args = ", " .. val .. args
+ end
+ str = str .. args:sub(3) .. ")"
+ stack:push(str)
+ end
+end
+
+function const(t)
+ return function(stack, instr)
+ stack:push(tostring(instr.x) .. t)
+ end
+end
+
+function expr_return(stack, instr)
+ if stack:size() >= 1 then
+ return "return " .. stack:pop()
+ else
+ return "return"
+ end
+end
+
+function expr_br(stack, instr)
+ return "br <" .. instr.x.block.label .. ">"
+end
+
+function expr_br_if(stack, instr)
+ return "br_if <" .. instr.x.block.label .. "> (" .. stack:pop() .. ")"
+end
+
+function expr_br_table(stack, instr)
+ error "Cannot handle br_table"
+end
+
+function expr_drop(stack, instr)
+ return "drop (" .. stack:pop() .. ")"
+end
+
+function expr_select(stack, instr)
+ stack:push("select (" .. stack:pop() .. [[) {
+ 0: ]] .. stack:pop() .. [[
+ 1: ]] .. stack:pop() .. [[
+}]])
+end
+
+function expr_local_get(stack, instr, func)
+ stack:push(func.locals[instr.x + 1].name)
+end
+
+function expr_local_set(stack, instr, func)
+ local varname = func.locals[instr.x + 1].name
+ return varname .. " = " .. stack:pop()
+end
+
+function expr_local_tee(stack, instr, func)
+ local varname = func.locals[instr.x + 1].name
+ stack:push(varname .. " = " .. stack:pop())
+end
+
+function expr_global_get(stack, instr)
+ stack:push("global" .. instr.x)
+end
+
+function expr_global_set(stack, instr)
+ return "global" .. instr.x .. " = " .. stack:pop()
+end
+
+function expr_load(stack, instr)
+ stack:push("[" .. stack:pop() .. "]")
+end
+
+function expr_store(stack, instr)
+ local value = stack:pop()
+ local location = stack:pop()
+ return "[" .. location .. "] <- " .. value
+end
+
+function indent(lines, prefix)
+ local newlines = {}
+ for _, line in ipairs(lines) do
+ table.insert(newlines, prefix .. line)
+ end
+ return newlines
+end
+
+function expr_block(stack, instr, func, mod)
+ local lines = indent(
+ build_instr_list(instr.instrs, func, mod),
+ " "
+ )
+ table.insert(lines, 1, "block <" .. instr.label .. "> {")
+ table.insert(lines, "}")
+ return lines
+end
+
+function expr_loop(stack, instr, func, mod)
+ local lines = indent(
+ build_instr_list(instr.instrs, func, mod),
+ " "
+ )
+ table.insert(lines, 1, "loop <" .. instr.label .. "> {")
+ table.insert(lines, "}")
+ return lines
+end
+
+function expr_if(stack, instr, func, mod)
+ local lines = indent(
+ build_instr_list(instr.instrs, func, mod),
+ " "
+ )
+ table.insert(lines, 1, "if <" .. instr.label .. "> (" .. stack:pop() .. ") {")
+ if #instr.else_instrs > 0 then
+ table.insert(lines, "} else {")
+ local else_lines = indent(
+ build_instr_list(instr.else_instrs, func, mod),
+ " "
+ )
+ for _, line in ipairs(else_lines) do
+ table.insert(lines, line)
+ end
+ end
+ table.insert(lines, "}")
+ return lines
+end
+
+function expr_call(stack, instr, func, mod)
+ local nparams = #mod.funcs[instr.x].type_.param_types
+ local nreturns = #mod.funcs[instr.x].type_.result_types
+ local name = mod.funcs[instr.x].name
+
+ local res = func_call(name, nparams)(stack, instr, func, mod)
+ if nreturns >= 1 then
+ stack:push(res)
+ else
+ return res
+ end
+end
+
+function call_indirect(stack, instr)
+ error "Cannot handle call indirect"
+end
+
+local expr_generators = {
+-- Name Inputs, Outputs
+ ["unreachable"] = { 0, 0 };
+ ["nop"] = { 0, 0 };
+
+ ["block"] = expr_block;
+ ["loop"] = expr_loop;
+ ["if"] = expr_if;
+
+ ["br"] = expr_br;
+ ["br_if"] = expr_br_if;
+ ["br_table"] = expr_br_table;
+ ["return"] = expr_return;
+ ["call"] = expr_call;
+ --call_indirect is dynamic
+
+ ["drop"] = expr_drop;
+ ["select"] = expr_select;
+
+ ["local.get"] = expr_local_get;
+ ["local.set"] = expr_local_set;
+ ["local.tee"] = expr_local_tee;
+ ["global.get"] = expr_global_get;
+ ["global.set"] = expr_global_set;
+
+ ["i32.load"] = expr_load;
+ ["i64.load"] = expr_load;
+ ["f32.load"] = expr_load;
+ ["f64.load"] = expr_load;
+ ["i32.load8_s"] = expr_load;
+ ["i32.load8_u"] = expr_load;
+ ["i32.load16_s"] = expr_load;
+ ["i32.load16_u"] = expr_load;
+ ["i64.load8_s"] = expr_load;
+ ["i64.load8_u"] = expr_load;
+ ["i64.load16_s"] = expr_load;
+ ["i64.load16_u"] = expr_load;
+ ["i64.load32_s"] = expr_load;
+ ["i64.load32_u"] = expr_load;
+ ["i32.store"] = expr_store;
+ ["i64.store"] = expr_store;
+ ["f32.store"] = expr_store;
+ ["f64.store"] = expr_store;
+ ["i32.store8"] = expr_store;
+ ["i32.store16"] = expr_store;
+ ["i64.store8"] = expr_store;
+ ["i64.store16"] = expr_store;
+ ["i64.store32"] = expr_store;
+
+ ["memory.size"] = func_call("memory.size", 0);
+ ["memory.grow"] = func_call "memory.grow";
+
+ ["i32.const"] = const "i32";
+ ["i64.const"] = const "i64";
+ ["f32.const"] = const "f32";
+ ["f64.const"] = const "f64";
+
+ ["i32.eqz"] = func_call "eqz";
+ ["i32.eq"] = binop "==";
+ ["i32.ne"] = binop "!=";
+ ["i32.lt_s"] = binop "<";
+ ["i32.lt_u"] = binop "<";
+ ["i32.gt_s"] = binop ">";
+ ["i32.gt_u"] = binop ">";
+ ["i32.le_s"] = binop "<=";
+ ["i32.le_u"] = binop "<=";
+ ["i32.ge_s"] = binop ">=";
+ ["i32.ge_u"] = binop ">=";
+ ["i64.eqz"] = func_call "eqz";
+ ["i64.eq"] = binop "==";
+ ["i64.ne"] = binop "!=";
+ ["i64.lt_s"] = binop "<";
+ ["i64.lt_u"] = binop "<";
+ ["i64.gt_s"] = binop ">";
+ ["i64.gt_u"] = binop ">";
+ ["i64.le_s"] = binop "<=";
+ ["i64.le_u"] = binop "<=";
+ ["i64.ge_s"] = binop ">=";
+ ["i64.ge_u"] = binop ">=";
+ ["f32.eq"] = binop "==";
+ ["f32.ne"] = binop "!=";
+ ["f32.lt"] = binop "<";
+ ["f32.gt"] = binop ">";
+ ["f32.le"] = binop "<=";
+ ["f32.ge"] = binop ">=";
+ ["f64.eq"] = binop "==";
+ ["f64.ne"] = binop "!=";
+ ["f64.lt"] = binop "<";
+ ["f64.gt"] = binop ">";
+ ["f64.le"] = binop "<=";
+ ["f64.ge"] = binop ">=";
+ ["i32.clz"] = func_call "clz";
+ ["i32.ctz"] = func_call "ctz";
+ ["i32.popcnt"] = func_call "popcnt";
+ ["i32.add"] = binop "+";
+ ["i32.sub"] = binop "-";
+ ["i32.mul"] = binop "*";
+ ["i32.div_s"] = binop "/";
+ ["i32.div_u"] = binop "/";
+ ["i32.rem_s"] = binop "%";
+ ["i32.rem_u"] = binop "%";
+ ["i32.and"] = binop "&";
+ ["i32.or"] = binop "|";
+ ["i32.xor"] = binop "^";
+ ["i32.shl"] = binop "<<";
+ ["i32.shr_s"] = binop ">>";
+ ["i32.shr_u"] = binop ">>>";
+ ["i32.rotl"] = binop "rotl";
+ ["i32.rotr"] = binop "rotr";
+ ["i64.clz"] = func_call "clz";
+ ["i64.ctz"] = func_call "ctz";
+ ["i64.popcnt"] = func_call "popcnt";
+ ["i64.add"] = binop "+";
+ ["i64.sub"] = binop "-";
+ ["i64.mul"] = binop "*";
+ ["i64.div_s"] = binop "/";
+ ["i64.div_u"] = binop "/";
+ ["i64.rem_s"] = binop "%";
+ ["i64.rem_u"] = binop "%";
+ ["i64.and"] = binop "&";
+ ["i64.or"] = binop "|";
+ ["i64.xor"] = binop "^";
+ ["i64.shl"] = binop "<<";
+ ["i64.shr_s"] = binop ">>";
+ ["i64.shr_u"] = binop ">>>";
+ ["i64.rotl"] = binop "rotl";
+ ["i64.rotr"] = binop "rotr";
+ ["f32.abs"] = func_call "abs";
+ ["f32.neg"] = func_call "neg"; -- TODO: make this better
+ ["f32.ceil"] = func_call "ceil";
+ ["f32.floor"] = func_call "floor";
+ ["f32.trunc"] = func_call "trunc";
+ ["f32.nearest"] = func_call "nearest";
+ ["f32.sqrt"] = func_call "sqrt";
+ ["f32.add"] = binop "+";
+ ["f32.sub"] = binop "-";
+ ["f32.mul"] = binop "*";
+ ["f32.div"] = binop "/";
+ ["f32.min"] = func_call("min", 2);
+ ["f32.max"] = func_call("max", 2);
+ ["f32.copysign"] = func_call("copysign", 2);
+ ["f64.abs"] = func_call "abs";
+ ["f64.neg"] = func_call "neg";
+ ["f64.ceil"] = func_call "ceil";
+ ["f64.floor"] = func_call "floor";
+ ["f64.trunc"] = func_call "trunc";
+ ["f64.nearest"] = func_call "nearest";
+ ["f64.sqrt"] = func_call "sqrt";
+ ["f64.add"] = binop "+";
+ ["f64.sub"] = binop "-";
+ ["f64.mul"] = binop "*";
+ ["f64.div"] = binop "/";
+ ["f64.min"] = func_call("min", 2);
+ ["f64.max"] = func_call("max", 2);
+ ["f64.copysign"] = func_call("copysign", 2);
+
+ ["i32.wrap_i64"] = cast "i32";
+ ["i32.trunc_f32_s"] = cast "i32";
+ ["i32.trunc_f32_u"] = cast "i32";
+ ["i32.trunc_f64_s"] = cast "i32";
+ ["i32.trunc_f64_u"] = cast "i32";
+ ["i64.extend_i32_s"] = cast "i64";
+ ["i64.extend_i32_u"] = cast "i64";
+ ["i64.trunc_f32_s"] = cast "i64";
+ ["i64.trunc_f32_u"] = cast "i64";
+ ["i64.trunc_f64_s"] = cast "i64";
+ ["i64.trunc_f64_u"] = cast "i64";
+ ["f32.convert_i32_s"] = cast "f32";
+ ["f32.convert_i32_u"] = cast "f32";
+ ["f32.convert_i64_s"] = cast "f32";
+ ["f32.convert_i64_u"] = cast "f32";
+ ["f32.demote_f64"] = cast "f32";
+ ["f64.convert_i32_s"] = cast "f64";
+ ["f64.convert_i32_u"] = cast "f64";
+ ["f64.convert_i64_s"] = cast "f64";
+ ["f64.convert_i64_u"] = cast "f64";
+ ["f64.promote_f32"] = cast "f64";
+ ["i32.reinterpret_f32"] = cast "i32";
+ ["i64.reinterpret_f64"] = cast "i64";
+ ["f32.reinterpret_i32"] = cast "f32";
+ ["f64.reinterpret_i64"] = cast "f64";
+}
+
-- This works... ish but we should use proper symbols (+-*/) and other things
-function build_instr_list(instrs)
+function build_instr_list(instrs, func, mod)
local lines = {}
local stack = Stack()
for _, instr in ipairs(instrs) do
- local instr_name = instr[1]
- if stack_opts[instr_name] then
- -- For now, everything will be built in a function calling syntax
- local str = ""
- for i=1, stack_opts[instr_name][1] do
- str = str .. ", "
- str = str .. stack:pop()
- end
-
- str = instr_name .. "(" .. str:sub(3) .. ")"
-
- if stack_opts[instr_name][2] == 1 then
- stack:push(str)
- else
- table.insert(lines, str)
- end
- end
-
- if instr_name == "block" or instr_name == "loop" then
- -- have a .instrs which is the instruction list
- table.insert(lines, instr_name .. " { [" .. instr.label .. "]")
- local instr_lines = build_instr_list(instr.instrs)
- for _, line in ipairs(instr_lines) do
- table.insert(lines, " " .. line)
- end
- table.insert(lines, "}")
- end
- if instr_name == "if" then
- local str = instr_name .. " ("
- str = str .. stack:pop()
- str = str .. " ) { [" .. instr.label .. "]"
- table.insert(lines, str)
-
- local instr_lines = build_instr_list(instr.instrs)
- for _, line in ipairs(instr_lines) do
- table.insert(lines, " " .. line)
- end
-
- if instr_name == "if" and #instr.else_instrs > 0 then
- table.insert(lines, "} else {")
- instr_lines = build_instr_list(instr.else_instrs)
- for _, line in ipairs(instr_lines) do
- table.insert(lines, " " .. line)
- end
- end
-
- table.insert(lines, "}")
+ local res = expr_generators[instr[1]](stack, instr, func, mod)
+ if type(res) == "string" then
+ table.insert(lines, res)
+ elseif type(res) == "table" then
+ for _, line in ipairs(res) do table.insert(lines, line) end
end
+ end
- if instr_name == "call" then
+ if stack:size() ~= 0 then
+ while not stack:empty() do
+ table.insert(lines, stack:pop())
end
end
end
function build_expr(wasm_func, wasm_mod)
- local lines = build_instr_list(wasm_func.body)
+ local lines = build_instr_list(wasm_func.body, wasm_func, wasm_mod)
return lines
end