diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ecbf039 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +test.lua \ No newline at end of file diff --git a/constants.lua b/constants.lua index aad2914..ad81149 100644 --- a/constants.lua +++ b/constants.lua @@ -1,189 +1,193 @@ -opcodes = { +local constants = {} + +constants.opcodes = { -- Control - OP_UNREACHABLE = 0x00 - OP_NOP = 0x01 - OP_BLOCK = 0x02 - OP_LOOP = 0x03 - OP_IF = 0x04 - OP_ELSE = 0x05 - OP_END = 0x0B - OP_BR = 0x0C - OP_BR_IF = 0x0D - OP_BR_TABLE = 0x0E - OP_RETURN = 0x0F - OP_CALL = 0x10 - OP_CALL_INDIRECT = 0x11 + OP_UNREACHABLE = 0x00, + OP_NOP = 0x01, + OP_BLOCK = 0x02, + OP_LOOP = 0x03, + OP_IF = 0x04, + OP_ELSE = 0x05, + OP_END = 0x0B, + OP_BR = 0x0C, + OP_BR_IF = 0x0D, + OP_BR_TABLE = 0x0E, + OP_RETURN = 0x0F, + OP_CALL = 0x10, + OP_CALL_INDIRECT = 0x11, -- Parametric - OP_DROP = 0x1A - OP_SELECT = 0x1B + OP_DROP = 0x1A, + OP_SELECT = 0x1B, -- Variable - OP_LOCAL_GET = 0x20 - OP_LOCAL_SET = 0x21 - OP_LOCAL_TEE = 0x22 - OP_GLOBAL_GET = 0x23 - OP_GLOBAL_SET = 0x24 + OP_LOCAL_GET = 0x20, + OP_LOCAL_SET = 0x21, + OP_LOCAL_TEE = 0x22, + OP_GLOBAL_GET = 0x23, + OP_GLOBAL_SET = 0x24, -- Memory - OP_I32_LOAD = 0x28 - OP_I64_LOAD = 0x29 - OP_F32_LOAD = 0x2A - OP_F64_LOAD = 0x2B - OP_I32_LOAD8_S = 0x2C - OP_I32_LOAD8_U = 0x2D - OP_I32_LOAD16_S = 0x2E - OP_I32_LOAD16_U = 0x2F - OP_I64_LOAD8_S = 0x30 - OP_I64_LOAD8_U = 0x31 - OP_I64_LOAD16_S = 0x32 - OP_I64_LOAD16_U = 0x33 - OP_I64_LOAD32_S = 0x34 - OP_I64_LOAD32_U = 0x35 - OP_I32_STORE = 0x36 - OP_I64_STORE = 0x37 - OP_F32_STORE = 0x38 - OP_F64_STORE = 0x39 - OP_I32_STORE8 = 0x3A - OP_I32_STORE16 = 0x3B - OP_I64_STORE8 = 0x3C - OP_I64_STORE16 = 0x3D - OP_I64_STORE32 = 0x3E - OP_MEMORY_SIZE = 0x3F - OP_MEMORY_GROW = 0x40 + OP_I32_LOAD = 0x28, + OP_I64_LOAD = 0x29, + OP_F32_LOAD = 0x2A, + OP_F64_LOAD = 0x2B, + OP_I32_LOAD8_S = 0x2C, + OP_I32_LOAD8_U = 0x2D, + OP_I32_LOAD16_S = 0x2E, + OP_I32_LOAD16_U = 0x2F, + OP_I64_LOAD8_S = 0x30, + OP_I64_LOAD8_U = 0x31, + OP_I64_LOAD16_S = 0x32, + OP_I64_LOAD16_U = 0x33, + OP_I64_LOAD32_S = 0x34, + OP_I64_LOAD32_U = 0x35, + OP_I32_STORE = 0x36, + OP_I64_STORE = 0x37, + OP_F32_STORE = 0x38, + OP_F64_STORE = 0x39, + OP_I32_STORE8 = 0x3A, + OP_I32_STORE16 = 0x3B, + OP_I64_STORE8 = 0x3C, + OP_I64_STORE16 = 0x3D, + OP_I64_STORE32 = 0x3E, + OP_MEMORY_SIZE = 0x3F, + OP_MEMORY_GROW = 0x40, -- Numeric Constants - OP_I32_CONST = 0x41 - OP_I64_CONST = 0x42 - OP_F32_CONST = 0x43 - OP_F64_CONST = 0x44 + OP_I32_CONST = 0x41, + OP_I64_CONST = 0x42, + OP_F32_CONST = 0x43, + OP_F64_CONST = 0x44, -- Numeric Operations ---- i32 comparisons - OP_I32_EQZ = 0x45 - OP_I32_EQ = 0x46 - OP_I32_NE = 0x47 - OP_I32_LT_S = 0x48 - OP_I32_LT_U = 0x49 - OP_I32_GT_S = 0x50 - OP_I32_GT_U = 0x51 - OP_I32_LE_S = 0x52 - OP_I32_LE_U = 0x53 - OP_I32_GE_S = 0x54 - OP_I32_GE_U = 0x55 ---- i64 comparisons - OP_I64_EQZ = 0x50 - OP_I64_EQ = 0x51 - OP_I64_NE = 0x52 - OP_I64_LT_S = 0x53 - OP_I64_LT_U = 0x54 - OP_I64_GT_S = 0x55 - OP_I64_GT_U = 0x56 - OP_I64_LE_S = 0x57 - OP_I64_LE_U = 0x58 - OP_I64_GE_S = 0x59 - OP_I64_GE_U = 0x5A ---- f32 comparisons - OP_F32_EQ = 0x5B - OP_F32_NE = 0x5C - OP_F32_LT = 0x5D - OP_F32_GT = 0x5E - OP_F32_LE = 0x5F - OP_F32_GE = 0x60 ---- f64 comparisons - OP_F64_EQ = 0x61 - OP_F64_NE = 0x62 - OP_F64_LT = 0x63 - OP_F64_GT = 0x64 - OP_F64_LE = 0x65 - OP_F64_GE = 0x66 ---- i32 operations - OP_I32_CLZ = 0x67 - OP_I32_CTZ = 0x68 - OP_I32_POPCNT = 0x69 - OP_I32_ADD = 0x6A - OP_I32_SUB = 0x6B - OP_I32_MUL = 0x6C - OP_I32_DIV_S = 0x6D - OP_I32_DIV_U = 0x6E - OP_I32_REM_S = 0x6F - OP_I32_REM_U = 0x70 - OP_I32_AND = 0x71 - OP_I32_OR = 0x72 - OP_I32_XOR = 0x73 - OP_I32_SHL = 0x74 - OP_I32_SHR_S = 0x75 - OP_I32_SHR_U = 0x76 - OP_I32_ROTL = 0x77 - OP_I32_ROTR = 0x78 ---- i64 operations - OP_I64_CLZ = 0x79 - OP_I64_CTZ = 0x7A - OP_I64_POPCNT = 0x7B - OP_I64_ADD = 0x7C - OP_I64_SUB = 0x7D - OP_I64_MUL = 0x7E - OP_I64_DIV_S = 0x7F - OP_I64_DIV_U = 0x80 - OP_I64_REM_S = 0x81 - OP_I64_REM_U = 0x82 - OP_I64_AND = 0x83 - OP_I64_OR = 0x84 - OP_I64_XOR = 0x85 - OP_I64_SHL = 0x86 - OP_I64_SHR_S = 0x87 - OP_I64_SHR_U = 0x88 - OP_I64_ROTL = 0x89 - OP_I64_ROTR = 0x8A ---- f32 operations - OP_F32_ABS = 0x8B - OP_F32_NEG = 0x8C - OP_F32_CEIL = 0x8D - OP_F32_FLOOR = 0x8E - OP_F32_TRUNC = 0x8F - OP_F32_NEAREST = 0x90 - OP_F32_SQRT = 0x91 - OP_F32_ADD = 0x92 - OP_F32_SUB = 0x93 - OP_F32_MUL = 0x94 - OP_F32_DIV = 0x95 - OP_F32_MIN = 0x96 - OP_F32_MAX = 0x97 - OP_F32_COPYSIGN = 0x98 ---- f64 operations - OP_F64_ABS = 0x99 - OP_F64_NEG = 0x9A - OP_F64_CEIL = 0x9B - OP_F64_FLOOR = 0x9C - OP_F64_TRUNC = 0x9D - OP_F64_NEAREST = 0x9E - OP_F64_SQRT = 0x9F - OP_F64_ADD = 0xA0 - OP_F64_SUB = 0xA1 - OP_F64_MUL = 0xA2 - OP_F64_DIV = 0xA3 - OP_F64_MIN = 0xA4 - OP_F64_MAX = 0xA5 - OP_F64_COPYSIGN = 0xA6 ---- conversions - OP_I32_WRAP_I64 = 0xA7 - OP_I32_TRUNC_F32_S = 0xA8 - OP_I32_TRUNC_F32_U = 0xA9 - OP_I32_TRUNC_F64_S = 0xAA - OP_I32_TRUNC_F64_U = 0xAB - OP_I64_EXTEND_I32_S = 0xAC - OP_I64_EXTEND_I32_U = 0xAD - OP_I64_TRUNC_F32_S = 0xAE - OP_I64_TRUNC_F32_U = 0xAF - OP_I64_TRUNC_F64_S = 0xB0 - OP_I64_TRUNC_F64_U = 0xB1 - OP_F32_CONVERT_I32_S = 0xB2 - OP_F32_CONVERT_I32_U = 0xB3 - OP_F32_CONVERT_I64_S = 0xB4 - OP_F32_CONVERT_I64_U = 0xB5 - OP_F32_DEMOTE_F64 = 0xB6 - OP_F64_CONVERT_I32_S = 0xB7 - OP_F64_CONVERT_I32_U = 0xB8 - OP_F64_CONVERT_I64_S = 0xB9 - OP_F64_CONVERT_I64_U = 0xBA - OP_F64_PROMOTE_F32 = 0xBB - OP_I32_REINTERPRET_F32 = 0xBC - OP_I64_REINTERPRET_F64 = 0xBD - OP_F32_REINTERPRET_I32 = 0xBE - OP_F64_REINTERPRET_I64 = 0xBF -} \ No newline at end of file +-- i32 comparisons + OP_I32_EQZ = 0x45, + OP_I32_EQ = 0x46, + OP_I32_NE = 0x47, + OP_I32_LT_S = 0x48, + OP_I32_LT_U = 0x49, + OP_I32_GT_S = 0x50, + OP_I32_GT_U = 0x51, + OP_I32_LE_S = 0x52, + OP_I32_LE_U = 0x53, + OP_I32_GE_S = 0x54, + OP_I32_GE_U = 0x55, +-- i64 comparisons + OP_I64_EQZ = 0x50, + OP_I64_EQ = 0x51, + OP_I64_NE = 0x52, + OP_I64_LT_S = 0x53, + OP_I64_LT_U = 0x54, + OP_I64_GT_S = 0x55, + OP_I64_GT_U = 0x56, + OP_I64_LE_S = 0x57, + OP_I64_LE_U = 0x58, + OP_I64_GE_S = 0x59, + OP_I64_GE_U = 0x5A, +-- f32 comparisons + OP_F32_EQ = 0x5B, + OP_F32_NE = 0x5C, + OP_F32_LT = 0x5D, + OP_F32_GT = 0x5E, + OP_F32_LE = 0x5F, + OP_F32_GE = 0x60, +-- f64 comparisons + OP_F64_EQ = 0x61, + OP_F64_NE = 0x62, + OP_F64_LT = 0x63, + OP_F64_GT = 0x64, + OP_F64_LE = 0x65, + OP_F64_GE = 0x66, +-- i32 operations + OP_I32_CLZ = 0x67, + OP_I32_CTZ = 0x68, + OP_I32_POPCNT = 0x69, + OP_I32_ADD = 0x6A, + OP_I32_SUB = 0x6B, + OP_I32_MUL = 0x6C, + OP_I32_DIV_S = 0x6D, + OP_I32_DIV_U = 0x6E, + OP_I32_REM_S = 0x6F, + OP_I32_REM_U = 0x70, + OP_I32_AND = 0x71, + OP_I32_OR = 0x72, + OP_I32_XOR = 0x73, + OP_I32_SHL = 0x74, + OP_I32_SHR_S = 0x75, + OP_I32_SHR_U = 0x76, + OP_I32_ROTL = 0x77, + OP_I32_ROTR = 0x78, +-- i64 operations + OP_I64_CLZ = 0x79, + OP_I64_CTZ = 0x7A, + OP_I64_POPCNT = 0x7B, + OP_I64_ADD = 0x7C, + OP_I64_SUB = 0x7D, + OP_I64_MUL = 0x7E, + OP_I64_DIV_S = 0x7F, + OP_I64_DIV_U = 0x80, + OP_I64_REM_S = 0x81, + OP_I64_REM_U = 0x82, + OP_I64_AND = 0x83, + OP_I64_OR = 0x84, + OP_I64_XOR = 0x85, + OP_I64_SHL = 0x86, + OP_I64_SHR_S = 0x87, + OP_I64_SHR_U = 0x88, + OP_I64_ROTL = 0x89, + OP_I64_ROTR = 0x8A, +-- f32 operations + OP_F32_ABS = 0x8B, + OP_F32_NEG = 0x8C, + OP_F32_CEIL = 0x8D, + OP_F32_FLOOR = 0x8E, + OP_F32_TRUNC = 0x8F, + OP_F32_NEAREST = 0x90, + OP_F32_SQRT = 0x91, + OP_F32_ADD = 0x92, + OP_F32_SUB = 0x93, + OP_F32_MUL = 0x94, + OP_F32_DIV = 0x95, + OP_F32_MIN = 0x96, + OP_F32_MAX = 0x97, + OP_F32_COPYSIGN = 0x98, +-- f64 operations + OP_F64_ABS = 0x99, + OP_F64_NEG = 0x9A, + OP_F64_CEIL = 0x9B, + OP_F64_FLOOR = 0x9C, + OP_F64_TRUNC = 0x9D, + OP_F64_NEAREST = 0x9E, + OP_F64_SQRT = 0x9F, + OP_F64_ADD = 0xA0, + OP_F64_SUB = 0xA1, + OP_F64_MUL = 0xA2, + OP_F64_DIV = 0xA3, + OP_F64_MIN = 0xA4, + OP_F64_MAX = 0xA5, + OP_F64_COPYSIGN = 0xA6, +-- conversions + OP_I32_WRAP_I64 = 0xA7, + OP_I32_TRUNC_F32_S = 0xA8, + OP_I32_TRUNC_F32_U = 0xA9, + OP_I32_TRUNC_F64_S = 0xAA, + OP_I32_TRUNC_F64_U = 0xAB, + OP_I64_EXTEND_I32_S = 0xAC, + OP_I64_EXTEND_I32_U = 0xAD, + OP_I64_TRUNC_F32_S = 0xAE, + OP_I64_TRUNC_F32_U = 0xAF, + OP_I64_TRUNC_F64_S = 0xB0, + OP_I64_TRUNC_F64_U = 0xB1, + OP_F32_CONVERT_I32_S = 0xB2, + OP_F32_CONVERT_I32_U = 0xB3, + OP_F32_CONVERT_I64_S = 0xB4, + OP_F32_CONVERT_I64_U = 0xB5, + OP_F32_DEMOTE_F64 = 0xB6, + OP_F64_CONVERT_I32_S = 0xB7, + OP_F64_CONVERT_I32_U = 0xB8, + OP_F64_CONVERT_I64_S = 0xB9, + OP_F64_CONVERT_I64_U = 0xBA, + OP_F64_PROMOTE_F32 = 0xBB, + OP_I32_REINTERPRET_F32 = 0xBC, + OP_I64_REINTERPRET_F64 = 0xBD, + OP_F32_REINTERPRET_I32 = 0xBE, + OP_F64_REINTERPRET_I64 = 0xBF, +} + +return constants \ No newline at end of file diff --git a/ops.lua b/ops.lua new file mode 100644 index 0000000..96e1dca --- /dev/null +++ b/ops.lua @@ -0,0 +1,157 @@ +local ops = {} + +function ops.i64_eqz(a) + return (a == 0) and 1 or 0 +end + +function ops.i64_eq(a, b) + return (a == b) and 1 or 0 +end + +function ops.i64_ne(a, b) + return (a ~= b) and 1 or 0 +end + +function ops.i64_lt_s(a, b) + return (a < b) and 1 or 0 +end + +function ops.i64_gt_s(a, b) + return (a > b) and 1 or 0 +end + +function ops.i64_le_s(a, b) + return (a <= b) and 1 or 0 +end + +function ops.i64_ge_s(a, b) + return (a >= b) and 1 or 0 +end + +function ops.i64_clz(a) + if a == 0 then + return 64 + end + + local n = 0 + if (a & 0xFFFFFFFF00000000) == 0 then + n = n + 32 + a = a << 32 + end + if (a & 0xFFFF000000000000) == 0 then + n = n + 16 + a = a << 16 + end + if (a & 0xFF00000000000000) == 0 then + n = n + 8 + a = a << 8 + end + if (a & 0xF000000000000000) == 0 then + n = n + 4 + a = a << 4 + end + if (a & 0xC000000000000000) == 0 then + n = n + 2 + a = a << 2 + end + if (a & 0x8000000000000000) == 0 then + n = n + 1 + end + + return n +end + +function ops.i64_ctz(a) + if a == 0 then + return 64 + end + + local n = 0 + if (a & 0x00000000FFFFFFFF) == 0 then + n = n + 32 + a = a >> 32 + end + if (a & 0x000000000000FFFF) == 0 then + n = n + 16 + a = a >> 16 + end + if (a & 0x00000000000000FF) == 0 then + n = n + 8 + a = a >> 8 + end + if (a & 0x000000000000000F) == 0 then + n = n + 4 + a = a >> 4 + end + if (a & 0x0000000000000003) == 0 then + n = n + 2 + a = a >> 2 + end + if (a & 0x0000000000000001) == 0 then + n = n + 1 + end + return n +end + +function ops.i64_popcnt(a) + local n = 0 + for _ = 1, 64 do + if (a & 1) == 1 then + n = n + 1 + end + a = a >> 1 + end + return n +end + +function ops.i64_add(a, b) + return a + b +end + +function ops.i64_sub(a, b) + return a - b +end + +function ops.i64_mul(a, b) + return a * b +end + +function ops.i64_div_s(a, b) + return a / b +end + +function ops.i64_rem_s(a, b) + return a % b +end + +function ops.i64_and(a, b) + return a & b +end + +function ops.i64_or(a, b) + return a | b +end + +function ops.i64_xor(a, b) + return a ~ b +end + +function ops.i64_shl(a, b) + return a >> b +end + +function ops.i64_shr_s(a, b) + return a << b +end + +function ops.i64_rotl(a, b) + local c = b % 64 + return (a << c) | (a >> (64 - c)) +end + +function ops.i64_rotr(a, b) + local c = b % 64 + return (a >> c) | (a << (64 - c)) +end + +return ops \ No newline at end of file diff --git a/wasmlib.lua b/wasmlib.lua new file mode 100644 index 0000000..f215c3c --- /dev/null +++ b/wasmlib.lua @@ -0,0 +1,62 @@ +local wasmlib = {} + +local constants = require("constants") +local ops = require("ops") + +wasmlib.VM = { + stack = {}, +} + +function wasmlib.VM:new() + local vm = {} + setmetatable(vm, {__index = self}) + return vm +end + +function wasmlib.VM:binop(f) + local a = table.remove(self.stack) + local b = table.remove(self.stack) + table.insert(self.stack, f(a, b)) +end + +function wasmlib.VM:unop(f) + local a = table.remove(self.stack) + table.insert(self.stack, f(a)) +end + +function wasmlib.VM:doOperation(opcode) + 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, + [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, + } + + local opfunc = optable[opcode] + if opfunc == nil then + error("unimplemented") + end + opfunc() +end + +return wasmlib \ No newline at end of file