diff --git a/constants.lua b/constants.lua index 9f311f6..ff6df92 100644 --- a/constants.lua +++ b/constants.lua @@ -2,6 +2,8 @@ local constants = {} constants.I32_MAX = (1 << 32) - 1 +constants.WASM_PAGE_SIZE = 65536 + constants.opcodes = { -- Control OP_UNREACHABLE = 0x00, diff --git a/intutil.lua b/intutil.lua index 1864174..e05f732 100644 --- a/intutil.lua +++ b/intutil.lua @@ -1,5 +1,16 @@ local intutil = {} +function intutil.fromle16(tab, idx) + local n = tab[idx] + n = n | tab[idx+1] << 8 + return n +end + +function intutil.tole16(tab, idx, n) + tab[idx] = n & 0xFF + tab[idx+1] = (n >> 8) & 0xFF +end + function intutil.fromle32(tab, idx) local n = tab[idx] n = n | tab[idx+1] << 8 @@ -45,4 +56,18 @@ function intutil.signexti32(n) return n end +function intutil.signexti16(n) + if (n & 0x8000) ~= 0 then + n = n | 0xFFFFFFFFFFFF0000 + end + return n +end + +function intutil.signexti8(n) + if (n & 0x80) ~= 0 then + n = n | 0xFFFFFFFFFFFFFF00 + end + return n +end + return intutil \ No newline at end of file diff --git a/memory.lua b/memory.lua new file mode 100644 index 0000000..f4a6dc7 --- /dev/null +++ b/memory.lua @@ -0,0 +1,31 @@ +local memory = {} + +local constants = require("constants") + +function memory.new(pages) + local m = { + mem = {}, + pages = pages, + } + setmetatable(m, { + __index = function(self, key) + if key >= (self.pages * constants.WASM_PAGE_SIZE) then + error("out of bounds memory access") + end + return self.mem[key] or 0 + end, + __newindex = function(self, key, value) + if key < (self.pages * constants.WASM_PAGE_SIZE) then + self.mem[key] = value + else + error("out of bounds memory access") + end + end, + __len = function(self) + return self.pages + end, + }) + return m +end + +return memory \ No newline at end of file diff --git a/ops.lua b/ops.lua index 85bb4f2..4436dd7 100644 --- a/ops.lua +++ b/ops.lua @@ -357,4 +357,18 @@ function ops.i32_rotr(a, b) return ((a >> c) | (a << (32 - c))) & constants.I32_MAX end +--[[ int conversions ]] + +function ops.i32_wrap_i64(a) + return a & constants.I32_MAX +end + +function ops.i64_extend_i32_s(a) + return intutil.signexti32(a) +end + +function ops.i64_extend_i32_u(a) + return a +end + return ops \ No newline at end of file diff --git a/wasmlib.lua b/wasmlib.lua index f34f483..81f9137 100644 --- a/wasmlib.lua +++ b/wasmlib.lua @@ -10,18 +10,20 @@ wasmlib.VM = { stackFrames = {}, functions = {}, types = {}, + memory = {}, + globals = {}, } -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:topFrame() + return self.stackFrames[#self.stackFrames] +end + function wasmlib.VM:triop(f) local c = table.remove(self.stack) local b = table.remove(self.stack) @@ -43,7 +45,7 @@ end function wasmlib.VM:readarg8() local curFrame = self.topFrame() local body = self.functions[curFrame.funcIndex].body - local result = body[curFrame.pc + 1] + local result = body[curFrame.pc] curFrame.pc = curFrame.pc + 1 return result end @@ -51,11 +53,19 @@ end function wasmlib.VM:readarg32() local curFrame = self:topFrame() local body = self.functions[curFrame.funcIndex].body - local result = intutil.fromle32(body, curFrame.pc + 1) + local result = intutil.fromle32(body, curFrame.pc) curFrame.pc = curFrame.pc + 4 return result end +function wasmlib.VM:readarg64() + local curFrame = self:topFrame() + local body = self.functions[curFrame.funcIndex].body + local result = intutil.fromle64(body, curFrame.pc) + curFrame.pc = curFrame.pc + 8 + return result +end + function wasmlib.VM:local_get() local curFrame = self:topFrame() local localIdx = self:readarg32() @@ -73,15 +83,205 @@ function wasmlib.VM:local_set() curFrame.locals[localIdx] = localVal end +function wasmlib.VM:local_tee() + local curFrame = self:topFrame() + local localIdx = self:readarg32() + local localVal = self.stack[#self.stack] + curFrame.locals[localIdx] = localVal +end + +function wasmlib.VM:global_get() + local globalIdx = self:readarg32() + local globalVal = self.globals[globalIdx] + if globalVal == nil then + error("read invalid global") + end + table.insert(self.stack, globalVal) +end + +function wasmlib.VM:global_set() + local globalIdx = self:readarg32() + local globalVal = table.remove(self.stack) + self.globals[globalIdx] = globalVal +end + function wasmlib.VM:call() local funcIdx = self:readarg32() self:invoke(funcIdx) end +function wasmlib.VM:memory_size() + self:readarg8() -- always zero in WASM 1.0 + return #self.memory +end + +function wasmlib.VM:memory_grow() + self:readarg8() -- always zero in WASM 1.0 + local amount = table.remove(self.stack) + if ( + (self.memory.maxpages ~= nil) and (#self.memory + amount > self.memory.maxpages) + ) or (#self.memory + amount > constants.I32_MAX) + then + table.insert(self.stack, constants.I32_MAX) -- i32 -1 + else + self.memory.pages = #self.memory + amount + table.insert(self.stack, #self.memory) + end +end + +function wasmlib.VM:i32_load() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = intutil.fromle32(self.memory, offset) + table.insert(self.stack, value) +end + +function wasmlib.VM:i64_load() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = intutil.fromle64(self.memory, offset) + table.insert(self.stack, value) +end + +function wasmlib.VM:i32_load8_s() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = intutil.signexti8(self.memory[offset]) & constants.I32_MAX + table.insert(self.stack, value) +end + +function wasmlib.VM:i32_load8_u() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = self.memory[offset] + table.insert(self.stack, value) +end + +function wasmlib.VM:i32_load16_s() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = intutil.signexti16(intutil.fromle16(self.memory, offset)) & constants.I32_MAX + table.insert(self.stack, value) +end + +function wasmlib.VM:i32_load16_u() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = intutil.fromle16(self.memory, offset) + table.insert(self.stack, value) +end + +function wasmlib.VM:i64_load8_s() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = intutil.signexti8(self.memory[offset]) + table.insert(self.stack, value) +end + +function wasmlib.VM:i64_load8_u() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = self.memory[offset] + table.insert(self.stack, value) +end + +function wasmlib.VM:i64_load16_s() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = intutil.signexti16(intutil.fromle16(self.memory, offset)) + table.insert(self.stack, value) +end + +function wasmlib.VM:i64_load16_u() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = intutil.fromle16(self.memory, offset) + table.insert(self.stack, value) +end + +function wasmlib.VM:i64_load32_s() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = intutil.signexti32(intutil.fromle32(self.memory, offset)) + table.insert(self.stack, value) +end + +function wasmlib.VM:i64_load32_u() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = intutil.fromle32(self.memory, offset) + table.insert(self.stack, value) +end + +function wasmlib.VM:i64_load() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = intutil.signexti16(intutil.fromle16(self.memory, offset)) + table.insert(self.stack, value) +end + +function wasmlib.VM:i32_store() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = table.remove(self.stack) + intutil.tole32(self.memory, offset, value) +end + +function wasmlib.VM:i64_store() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = table.remove(self.stack) + intutil.tole64(self.memory, offset, value) +end + +function wasmlib.VM:i32_store8() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = table.remove(self.stack) + self.memory[offset] = value & 0xFF +end + +function wasmlib.VM:i32_store16() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = table.remove(self.stack) + intutil.tole16(self.memory, offset, value) +end + +function wasmlib.VM:i64_store8() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = table.remove(self.stack) + self.memory[offset] = value & 0xFF +end + +function wasmlib.VM:i64_store16() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = table.remove(self.stack) + intutil.tole16(self.memory, offset, value) +end + +function wasmlib.VM:i64_store32() + self:readarg32() -- alignment, ignored for now + local offset = self:readarg32() + local value = table.remove(self.stack) + intutil.tole32(self.memory, offset, value) +end + +function wasmlib.VM:i32_const() + table.insert(self.stack, self:readarg32()) +end + +function wasmlib.VM:i64_const() + table.insert(self.stack, self:readarg64()) +end + function wasmlib.VM:step() local curFrame = self:topFrame() local body = self.functions[curFrame.funcIndex].body local opcode = body[curFrame.pc] + curFrame.pc = curFrame.pc + 1 local c = constants.opcodes local optable = { @@ -105,33 +305,37 @@ function wasmlib.VM:step() -- 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 + [c.OP_LOCAL_TEE] = function() self:local_tee() end, + [c.OP_GLOBAL_GET] = function() self:global_get() end, + [c.OP_GLOBAL_SET] = function() self:global_set() end, -- memory - -- I32_LOAD - -- I64_LOAD + [c.OP_I32_LOAD] = function() self:i32_load() end, + [c.OP_I64_LOAD] = function() self:i64_load() end, -- [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 + [c.OP_I32_LOAD8_S] = function() self:i32_load8_s() end, + [c.OP_I32_LOAD8_U] = function() self:i32_load8_u() end, + [c.OP_I32_LOAD16_S] = function() self:i32_load16_s() end, + [c.OP_I32_LOAD16_U] = function() self:i32_load16_u() end, + [c.OP_I64_LOAD8_S] = function() self:i64_load8_s() end, + [c.OP_I64_LOAD8_U] = function() self:i64_load8_u() end, + [c.OP_I64_LOAD16_S] = function() self:i64_load16_s() end, + [c.OP_I64_LOAD16_U] = function() self:i64_load16_u() end, + [c.OP_I64_LOAD32_S] = function() self:i64_load32_s() end, + [c.OP_I64_LOAD32_U] = function() self:i64_load32_u() end, + [c.OP_I32_STORE] = function() self:i32_store() end, + [c.OP_I64_STORE] = function() self:i64_store() end, -- [floart instrs] - -- I32_STORE8 - -- I32_STORE16 - -- I64_STORE8 - -- I64_STORE16 - -- I64_STORE32 - -- MEMORY_SIZE - -- MEMORY_GROW + [c.OP_I32_STORE8] = function() self:i32_store8() end, + [c.OP_I32_STORE16] = function() self:i32_store16() end, + [c.OP_I64_STORE8] = function() self:i64_store8() end, + [c.OP_I64_STORE16] = function() self:i64_store16() end, + [c.OP_I64_STORE32] = function() self:i64_store32() end, + [c.OP_MEMORY_SIZE] = function() self:memory_size() end, + [c.OP_MEMORY_GROW] = function() self:memory_grow() end, + -- constants + [c.OP_I32_CONST] = function() self:i32_const() end, + [c.OP_I64_CONST] = function() self:i64_const() end, + -- [float consts] -- i32 comparisons [c.OP_I32_EQZ] = function() self:unop (ops.i32_eqz) end, [c.OP_I32_EQ] = function() self:binop(ops.i32_eq) end, @@ -195,10 +399,10 @@ function wasmlib.VM:step() [c.OP_I64_ROTR] = function() self:binop(ops.i64_rotr) end, -- [float operations] -- conversions - -- I32_WRAP_I64 + [c.OP_I32_WRAP_I64] = function() self:unop(ops.i32_wrap_i64) end, -- [float stuff] - -- I64_EXTEND_I32_S - -- I64_EXTEND_I32_U + [c.OP_I64_EXTEND_I32_S] = function() self:unop(ops.i64_extend_i32_s) end, + [c.OP_I64_EXTEND_I32_U] = function() self:unop(ops.i64_extend_i32_u) end, -- [float stuff] } @@ -207,8 +411,6 @@ function wasmlib.VM:step() error("unimplemented") end opfunc() - - curFrame.pc = curFrame.pc + 1 end function wasmlib.VM:invoke(funcIndex)