diff --git a/constants.lua b/constants.lua index ad81149..9f311f6 100644 --- a/constants.lua +++ b/constants.lua @@ -1,5 +1,7 @@ local constants = {} +constants.I32_MAX = (1 << 32) - 1 + constants.opcodes = { -- Control OP_UNREACHABLE = 0x00, @@ -190,4 +192,20 @@ constants.opcodes = { OP_F64_REINTERPRET_I64 = 0xBF, } +constants.valtypes = { + VTY_I32 = 0x7F, + VTY_I64 = 0x7E, + VTY_F32 = 0x7D, + VTY_F64 = 0x7C, +} + +constants.blocktypes = { + BTY_NONE = 0x40, + BTY_I32 = constants.valtypes.VTY_I32, + BTY_I64 = constants.valtypes.VTY_I64, + BTY_F32 = constants.valtypes.VTY_F32, + BTY_F64 = constants.valtypes.VTY_F64, + +} + return constants \ No newline at end of file diff --git a/frame.lua b/frame.lua new file mode 100644 index 0000000..4d227e7 --- /dev/null +++ b/frame.lua @@ -0,0 +1,16 @@ +local frame = {} + +frame.StackFrame = { + funcIndex = 0, + locals = {}, + pc = 1, -- program counter +} + +function frame.StackFrame:new(funcIndex) + local f = {} + setmetatable(f, {__index = self}) + f.funcIndex = funcIndex + return f +end + +return frame \ No newline at end of file diff --git a/intutil.lua b/intutil.lua new file mode 100644 index 0000000..1864174 --- /dev/null +++ b/intutil.lua @@ -0,0 +1,48 @@ +local intutil = {} + +function intutil.fromle32(tab, idx) + local n = tab[idx] + n = n | tab[idx+1] << 8 + n = n | tab[idx+2] << 16 + n = n | tab[idx+3] << 24 + return n +end + +function intutil.tole32(tab, idx, n) + tab[idx] = n & 0xFF + tab[idx+1] = (n >> 8) & 0xFF + tab[idx+2] = (n >> 16) & 0xFF + tab[idx+3] = (n >> 24) & 0xFF +end + +function intutil.fromle64(tab, idx) + local n = tab[idx] + n = n | tab[idx+1] << 8 + n = n | tab[idx+2] << 16 + n = n | tab[idx+3] << 24 + n = n | tab[idx+4] << 32 + n = n | tab[idx+5] << 40 + n = n | tab[idx+6] << 48 + n = n | tab[idx+7] << 56 + return n +end + +function intutil.fromle64(tab, idx, n) + tab[idx] = n & 0xFF + tab[idx+1] = (n >> 8) & 0xFF + tab[idx+2] = (n >> 16) & 0xFF + tab[idx+3] = (n >> 24) & 0xFF + tab[idx+4] = (n >> 32) & 0xFF + tab[idx+5] = (n >> 40) & 0xFF + tab[idx+6] = (n >> 48) & 0xFF + tab[idx+7] = (n >> 56) & 0xFF +end + +function intutil.signexti32(n) + if (n & 0x80000000) ~= 0 then + n = n | 0xFFFFFFFF00000000 + end + return n +end + +return intutil \ No newline at end of file diff --git a/ops.lua b/ops.lua index 96e1dca..85bb4f2 100644 --- a/ops.lua +++ b/ops.lua @@ -1,5 +1,14 @@ local ops = {} +local constants = require("constants") +local intutil = require("intutil") + +function ops.select(a, b, c) + return (c ~= 0) and a or b +end + +--[[ i64 ]] + function ops.i64_eqz(a) return (a == 0) and 1 or 0 end @@ -57,7 +66,6 @@ function ops.i64_clz(a) if (a & 0x8000000000000000) == 0 then n = n + 1 end - return n end @@ -117,7 +125,7 @@ function ops.i64_mul(a, b) end function ops.i64_div_s(a, b) - return a / b + return a // b end function ops.i64_rem_s(a, b) @@ -137,11 +145,11 @@ function ops.i64_xor(a, b) end function ops.i64_shl(a, b) - return a >> b + return a << b end function ops.i64_shr_s(a, b) - return a << b + return a >> b end function ops.i64_rotl(a, b) @@ -154,4 +162,199 @@ function ops.i64_rotr(a, b) return (a >> c) | (a << (64 - c)) end +--[[ i32 ]] + +function ops.i32_eqz(a) + return (a == 0) and 1 or 0 +end + +function ops.i32_eq(a, b) + return (a == b) and 1 or 0 +end + +function ops.i32_ne(a, b) + return (a ~= b) and 1 or 0 +end + +function ops.i32_lt_s(a, b) + a = intutil.signexti32(a) + b = intutil.signexti32(b) + return (a < b) and 1 or 0 +end + +function ops.i32_lt_s(a, b) + a = intutil.signexti32(a) + b = intutil.signexti32(b) + return (a < b) and 1 or 0 +end + +function ops.i32_lt_u(a, b) + return (a < b) and 1 or 0 +end + +function ops.i32_gt_s(a, b) + a = intutil.signexti32(a) + b = intutil.signexti32(b) + return (a > b) and 1 or 0 +end + +function ops.i32_gt_u(a, b) + return (a > b) and 1 or 0 +end + +function ops.i32_le_s(a, b) + a = intutil.signexti32(a) + b = intutil.signexti32(b) + return (a <= b) and 1 or 0 +end + +function ops.i32_le_u(a, b) + return (a <= b) and 1 or 0 +end + +function ops.i32_ge_s(a, b) + a = intutil.signexti32(a) + b = intutil.signexti32(b) + return (a >= b) and 1 or 0 +end + +function ops.i32_ge_u(a, b) + return (a >= b) and 1 or 0 +end + +function ops.i32_clz(a) + if a == 0 then + return 32 + end + + local n = 0 + if (a & 0xFFFF0000) == 0 then + n = n + 16 + a = a << 16 + end + if (a & 0xFF000000) == 0 then + n = n + 8 + a = a << 8 + end + if (a & 0xF0000000) == 0 then + n = n + 4 + a = a << 4 + end + if (a & 0xC0000000) == 0 then + n = n + 2 + a = a << 2 + end + if (a & 0x80000000) == 0 then + n = n + 1 + end + return n +end + +function ops.i32_ctz(a) + if a == 0 then + return 64 + end + + local n = 0 + if (a & 0x0000FFFF) == 0 then + n = n + 16 + a = a >> 16 + end + if (a & 0x000000FF) == 0 then + n = n + 8 + a = a >> 8 + end + if (a & 0x0000000F) == 0 then + n = n + 4 + a = a >> 4 + end + if (a & 0x00000003) == 0 then + n = n + 2 + a = a >> 2 + end + if (a & 0x00000001) == 0 then + n = n + 1 + end + return n +end + +function ops.i32_popcnt(a) + local n = 0 + for _ = 1, 32 do + if (a & 1) == 1 then + n = n + 1 + end + a = a >> 1 + end + return n +end + +function ops.i32_add(a, b) + return (a + b) & constants.I32_MAX +end + +function ops.i32_sub(a, b) + return (a - b) & constants.I32_MAX +end + +function ops.i32_mul(a, b) + return (a * b) & constants.I32_MAX +end + +function ops.i32_div_s(a, b) + a = intutil.signexti32(a) + b = intutil.signexti32(b) + return (a // b) & constants.I32_MAX +end + +function ops.i32_div_u(a, b) + return (a // b) & constants.I32_MAX +end + +function ops.i32_rem_s(a, b) + a = intutil.signexti32(a) + b = intutil.signexti32(b) + return (a % b) & constants.I32_MAX +end + +function ops.i32_rem_u(a, b) + return (a % b) & constants.I32_MAX +end + +function ops.i32_and(a, b) + return a & b +end + +function ops.i32_or(a, b) + return a | b +end + +function ops.i32_xor(a, b) + return a ~ b +end + +function ops.i32_shl(a, b) + return (a << b) & constants.I32_MAX +end + +function ops.i32_shr_s(a, b) + a = intutil.signexti32(a) + b = intutil.signexti32(b) + return (a >> b) & constants.I32_MAX +end + +function ops.i32_shr_u(a, b) + return (a >> b) & constants.I32_MAX +end + +function ops.i32_rotl(a, b) + local c = b % 32 + return ((a << c) | (a >> (32 - c))) & constants.I32_MAX +end + +function ops.i32_rotr(a, b) + local c = b % 32 + return ((a >> c) | (a << (32 - c))) & constants.I32_MAX +end + return ops \ No newline at end of file diff --git a/wasmlib.lua b/wasmlib.lua index f215c3c..f34f483 100644 --- a/wasmlib.lua +++ b/wasmlib.lua @@ -2,20 +2,36 @@ local wasmlib = {} local constants = require("constants") local ops = require("ops") +local frame = require("frame") +local intutil = require("intutil") wasmlib.VM = { stack = {}, + stackFrames = {}, + functions = {}, + types = {}, } +function wasmlib.VM:topFrame() + return self.stackFrames[#self.stackFrames] +end + function wasmlib.VM:new() local vm = {} setmetatable(vm, {__index = self}) return vm end -function wasmlib.VM:binop(f) +function wasmlib.VM:triop(f) + local c = table.remove(self.stack) + local b = table.remove(self.stack) local a = table.remove(self.stack) + table.insert(self.stack, f(a, b, c)) +end + +function wasmlib.VM:binop(f) local b = table.remove(self.stack) + local a = table.remove(self.stack) table.insert(self.stack, f(a, b)) end @@ -24,32 +40,166 @@ function wasmlib.VM:unop(f) table.insert(self.stack, f(a)) end -function wasmlib.VM:doOperation(opcode) +function wasmlib.VM:readarg8() + local curFrame = self.topFrame() + local body = self.functions[curFrame.funcIndex].body + local result = body[curFrame.pc + 1] + curFrame.pc = curFrame.pc + 1 + return result +end + +function wasmlib.VM:readarg32() + local curFrame = self:topFrame() + local body = self.functions[curFrame.funcIndex].body + local result = intutil.fromle32(body, curFrame.pc + 1) + curFrame.pc = curFrame.pc + 4 + return result +end + +function wasmlib.VM:local_get() + local curFrame = self:topFrame() + local localIdx = self:readarg32() + local localVal = curFrame.locals[localIdx] + if localVal == nil then + error("read uninitialised or out of bounds local") + end + table.insert(self.stack, localVal) +end + +function wasmlib.VM:local_set() + local curFrame = self:topFrame() + local localIdx = self:readarg32() + local localVal = table.remove(self.stack) + curFrame.locals[localIdx] = localVal +end + +function wasmlib.VM:call() + local funcIdx = self:readarg32() + self:invoke(funcIdx) +end + +function wasmlib.VM:step() + local curFrame = self:topFrame() + local body = self.functions[curFrame.funcIndex].body + local opcode = body[curFrame.pc] + local c = constants.opcodes local optable = { - [c.OP_NOP] = function() end, - [c.OP_I64_EQZ] = function() self:unop (ops.i64_eqz) end, - [c.OP_I64_EQ] = function() self:binop(ops.i64_eq) end, - [c.OP_I64_NE] = function() self:binop(ops.i64_ne) end, - [c.OP_I64_LT_S] = function() self:binop(ops.i64_lt_s) end, - [c.OP_I64_GT_S] = function() self:binop(ops.i64_gt_s) end, - [c.OP_I64_LE_S] = function() self:binop(ops.i64_le_s) end, - [c.OP_I64_GE_S] = function() self:binop(ops.i64_ge_s) end, - [c.OP_I64_CLZ] = function() self:unop (ops.i64_clz) end, - [c.OP_I64_CTZ] = function() self:unop (ops.i64_ctz) end, + -- control + [c.OP_UNREACHABLE] = function() error("unreachable") end, + [c.OP_NOP] = function() end, + -- BLOCK + -- LOOP + -- IF + -- ELSE + -- END + -- BR + -- BR_IF + -- BR_TABLE + [c.OP_RETURN] = function() self:ret() end, + [c.OP_CALL] = function() self:call() end, + -- CALL_INDIRECT + -- parametric + [c.OP_DROP] = function() table.remove(self.stack) end, + [c.OP_SELECT] = function() self:triop(ops.select) end, + -- variable + [c.OP_LOCAL_GET] = function() self:local_get() end, + [c.OP_LOCAL_SET] = function() self:local_set() end, + -- LOCAL_TEE + -- GLOBAL_GET + -- GLOBAL_SET + -- memory + -- I32_LOAD + -- I64_LOAD + -- [float instrs] + -- I32_LOAD8_S + -- I32_LOAD8_U + -- I32_LOAD16_S + -- I32_LOAD16_U + -- I64_LOAD8_S + -- I64_LOAD8_U + -- I64_LOAD16_S + -- I64_LOAD16_U + -- I64_LOAD32_S + -- I64_LOAD32_U + -- I32_STORE + -- I64_STORE + -- [floart instrs] + -- I32_STORE8 + -- I32_STORE16 + -- I64_STORE8 + -- I64_STORE16 + -- I64_STORE32 + -- MEMORY_SIZE + -- MEMORY_GROW + -- i32 comparisons + [c.OP_I32_EQZ] = function() self:unop (ops.i32_eqz) end, + [c.OP_I32_EQ] = function() self:binop(ops.i32_eq) end, + [c.OP_I32_NE] = function() self:binop(ops.i32_ne) end, + [c.OP_I32_LT_S] = function() self:binop(ops.i32_lt_s) end, + [c.OP_I32_LT_U] = function() self:binop(ops.i32_lt_u) end, + [c.OP_I32_GT_S] = function() self:binop(ops.i32_gt_s) end, + [c.OP_I32_GT_U] = function() self:binop(ops.i32_gt_u) end, + [c.OP_I32_LE_S] = function() self:binop(ops.i32_le_s) end, + [c.OP_I32_LE_U] = function() self:binop(ops.i32_le_u) end, + -- i64 comparisons + [c.OP_I64_EQZ] = function() self:unop (ops.i64_eqz) end, + [c.OP_I64_EQ] = function() self:binop(ops.i64_eq) end, + [c.OP_I64_NE] = function() self:binop(ops.i64_ne) end, + [c.OP_I64_LT_S] = function() self:binop(ops.i64_lt_s) end, + -- I64_LT_U + [c.OP_I64_GT_S] = function() self:binop(ops.i64_gt_s) end, + -- I64_GT_U + [c.OP_I64_LE_S] = function() self:binop(ops.i64_le_s) end, + -- I64_LE_U + [c.OP_I64_GE_S] = function() self:binop(ops.i64_ge_s) end, + -- I64_GE_U + -- [float comparisons] + -- i32 operations + [c.OP_I32_CLZ] = function() self:unop (ops.i32_clz) end, + [c.OP_I32_CTZ] = function() self:unop (ops.i32_ctz) end, + [c.OP_I32_POPCNT] = function() self:unop (ops.i32_popcnt) end, + [c.OP_I32_ADD] = function() self:binop(ops.i32_add) end, + [c.OP_I32_SUB] = function() self:binop(ops.i32_sub) end, + [c.OP_I32_MUL] = function() self:binop(ops.i32_mul) end, + [c.OP_I32_DIV_S] = function() self:binop(ops.i32_div_s) end, + [c.OP_I32_DIV_U] = function() self:binop(ops.i32_div_u) end, + [c.OP_I32_REM_S] = function() self:binop(ops.i32_rem_s) end, + [c.OP_I32_REM_U] = function() self:binop(ops.i32_rem_u) end, + [c.OP_I32_AND] = function() self:binop(ops.i32_and) end, + [c.OP_I32_OR] = function() self:binop(ops.i32_or) end, + [c.OP_I32_XOR] = function() self:binop(ops.i32_xor) end, + [c.OP_I32_SHL] = function() self:binop(ops.i32_shl) end, + [c.OP_I32_SHR_S] = function() self:binop(ops.i32_shr_s) end, + [c.OP_I32_SHR_U] = function() self:binop(ops.i32_shr_u) end, + [c.OP_I32_ROTL] = function() self:binop(ops.i32_rotl) end, + [c.OP_I32_ROTR] = function() self:binop(ops.i32_rotr) end, + -- i64 operations + [c.OP_I64_CLZ] = function() self:unop (ops.i64_clz) end, + [c.OP_I64_CTZ] = function() self:unop (ops.i64_ctz) end, [c.OP_I64_POPCNT] = function() self:unop (ops.i64_popcnt) end, - [c.OP_I64_ADD] = function() self:binop(ops.i64_add) end, - [c.OP_I64_SUB] = function() self:binop(ops.i64_sub) end, - [c.OP_I64_MUL] = function() self:binop(ops.i64_mul) end, - [c.OP_I64_DIV_S] = function() self:binop(ops.i64_div_s) end, - [c.OP_I64_REM_S] = function() self:binop(ops.i64_rem_s) end, - [c.OP_I64_AND] = function() self:binop(ops.i64_and) end, - [c.OP_I64_OR] = function() self:binop(ops.i64_or) end, - [c.OP_I64_XOR] = function() self:binop(ops.i64_xor) end, - [c.OP_I64_SHL] = function() self:binop(ops.i64_shl) end, - [c.OP_I64_SHR_S] = function() self:binop(ops.i64_shr_s) end, - [c.OP_I64_ROTL] = function() self:binop(ops.i64_rotl) end, - [c.OP_I64_ROTR] = function() self:binop(ops.i64_rotr) end, + [c.OP_I64_ADD] = function() self:binop(ops.i64_add) end, + [c.OP_I64_SUB] = function() self:binop(ops.i64_sub) end, + [c.OP_I64_MUL] = function() self:binop(ops.i64_mul) end, + [c.OP_I64_DIV_S] = function() self:binop(ops.i64_div_s) end, + -- I64_DIV_U + [c.OP_I64_REM_S] = function() self:binop(ops.i64_rem_s) end, + -- I64_REM_U + [c.OP_I64_AND] = function() self:binop(ops.i64_and) end, + [c.OP_I64_OR] = function() self:binop(ops.i64_or) end, + [c.OP_I64_XOR] = function() self:binop(ops.i64_xor) end, + [c.OP_I64_SHL] = function() self:binop(ops.i64_shl) end, + [c.OP_I64_SHR_S] = function() self:binop(ops.i64_shr_s) end, + -- I64_SHR_U + [c.OP_I64_ROTL] = function() self:binop(ops.i64_rotl) end, + [c.OP_I64_ROTR] = function() self:binop(ops.i64_rotr) end, + -- [float operations] + -- conversions + -- I32_WRAP_I64 + -- [float stuff] + -- I64_EXTEND_I32_S + -- I64_EXTEND_I32_U + -- [float stuff] } local opfunc = optable[opcode] @@ -57,6 +207,22 @@ function wasmlib.VM:doOperation(opcode) error("unimplemented") end opfunc() + + curFrame.pc = curFrame.pc + 1 +end + +function wasmlib.VM:invoke(funcIndex) + local fr = frame.StackFrame:new(funcIndex) + local f = self.functions[funcIndex] + local sig = self.types[f.typeidx] + for i = 1, #sig.arguments do + fr.locals[#sig.arguments - i] = table.remove(self.stack) + end + table.insert(self.stackFrames, fr) +end + +function wasmlib.VM:ret() + table.remove(self.stackFrames) end return wasmlib \ No newline at end of file