defmodule WaspVM.Executor do alias WaspVM.Stack alias WaspVM.Frame alias WaspVM.Memory use Bitwise require Logger require IEx @moduledoc false # Reference for tests being used: https://github.com/WebAssembly/wabt/tree/master/test def create_frame_and_execute(vm, addr) do {{inputs, _outputs}, module_ref, instr, locals} = Enum.at(vm.store.funcs, addr) {args, stack} = Stack.pop_multiple(vm.stack, tuple_size(inputs)) if tuple_size(inputs) != length(args) do {{:error, :param_mismatch, tuple_size(inputs), length(args)}, vm} else module = Enum.find(vm.modules, & &1.ref == module_ref) vm = Map.put(vm, :stack, stack) frame = %Frame{ module: module, instructions: instr, locals: args ++ Enum.map(locals, fn _ -> 0 end), next_instr: 0 } execute(frame, vm) end end def execute(%{next_instr: n, instructions: i}, vm) when n == length(i), do: vm def execute(frame, vm) do {frame, vm} = frame.instructions |> Enum.at(frame.next_instr) |> instruction({frame, vm}) frame = Map.put(frame, :next_instr, frame.next_instr + 1) execute(frame, vm) end def instruction(opcode, ctx) when is_atom(opcode), do: exec_inst(ctx, opcode) def instruction(opcode, ctx) when is_tuple(opcode), do: exec_inst(ctx, opcode) defp exec_inst({frame, vm}, {:i32_const, i32}) do {frame, Map.put(vm, :stack, Stack.push(vm.stack, i32))} end defp exec_inst({frame, vm}, {:i64_const, i64}) do {frame, Map.put(vm, :stack, Stack.push(vm.stack, i64))} end defp exec_inst({frame, vm}, {:f32_const, f32}) do {frame, Map.put(vm, :stack, Stack.push(vm.stack, f32))} end defp exec_inst({frame, vm}, {:f64_const, f64}) do {frame, Map.put(vm, :stack, Stack.push(vm.stack, f64))} end defp exec_inst({frame, vm}, {:i32_store, alignment, offset}) do {[value, address], stack} = Stack.pop_multiple(vm.stack) # Will only work while each module can only have 1 mem mem_addr = hd(frame.module.memaddrs) mem = vm.store.mems |> Enum.at(mem_addr) |> Memory.put_at(address + offset, <>) store_mems = List.replace_at(vm.store.mems, mem_addr, mem) store = Map.put(vm.store, :mems, store_mems) {frame, Map.merge(vm, %{store: store, stack: stack})} end defp exec_inst({frame, vm}, {:i64_store, alignment, offset}) do {[value, address], stack} = Stack.pop_multiple(vm.stack) # Will only work while each module can only have 1 mem mem_addr = hd(frame.module.memaddrs) mem = vm.store.mems |> Enum.at(mem_addr) |> Memory.put_at(address + offset, <>) store_mems = List.replace_at(vm.store.mems, mem_addr, mem) store = Map.put(vm.store, :mems, store_mems) {frame, Map.merge(vm, %{store: store, stack: stack})} end defp exec_inst({frame, vm}, {:f32_store, alignment, offset}) do {[value, address], stack} = Stack.pop_multiple(vm.stack) # Will only work while each module can only have 1 mem mem_addr = hd(frame.module.memaddrs) mem = vm.store.mems |> Enum.at(mem_addr) |> Memory.put_at(address + offset, <>) store_mems = List.replace_at(vm.store.mems, mem_addr, mem) store = Map.put(vm.store, :mems, store_mems) {frame, Map.merge(vm, %{store: store, stack: stack})} end defp exec_inst({frame, vm}, {:f64_store, alignment, offset}) do {[value, address], stack} = Stack.pop_multiple(vm.stack) # Will only work while each module can only have 1 mem mem_addr = hd(frame.module.memaddrs) mem = vm.store.mems |> Enum.at(mem_addr) |> Memory.put_at(address + offset, <>) store_mems = List.replace_at(vm.store.mems, mem_addr, mem) store = Map.put(vm.store, :mems, store_mems) {frame, Map.merge(vm, %{store: store, stack: stack})} end defp exec_inst({frame, vm}, {:i32_load, alignment, offset}) do {address, stack} = Stack.pop(vm.stack) # Will only work while each module can only have 1 mem mem_addr = hd(frame.module.memaddrs) <> = vm.store.mems |> Enum.at(mem_addr) |> Memory.get_at(address + offset, 4) {frame, Map.put(vm, :stack, Stack.push(stack, i32))} end defp exec_inst({frame, vm}, {:i64_load, alignment, offset}) do {address, stack} = Stack.pop(vm.stack) # Will only work while each module can only have 1 mem mem_addr = hd(frame.module.memaddrs) <> = vm.store.mems |> Enum.at(mem_addr) |> Memory.get_at(address + offset, 8) {frame, Map.put(vm, :stack, Stack.push(stack, i64))} end defp exec_inst({frame, vm}, {:f32_load, alignment, offset}) do {address, stack} = Stack.pop(vm.stack) # Will only work while each module can only have 1 mem mem_addr = hd(frame.module.memaddrs) <> = vm.store.mems |> Enum.at(mem_addr) |> Memory.get_at(address + offset, 4) {frame, Map.put(vm, :stack, Stack.push(stack, f32))} end defp exec_inst({frame, vm}, {:f64_load, alignment, offset}) do {address, stack} = Stack.pop(vm.stack) # Will only work while each module can only have 1 mem mem_addr = hd(frame.module.memaddrs) <> = vm.store.mems |> Enum.at(mem_addr) |> Memory.get_at(address + offset, 8) {frame, Map.put(vm, :stack, Stack.push(stack, f64))} end defp exec_inst({frame, vm}, {:get_local, idx}) do local = Enum.at(frame.locals, idx) {frame, Map.put(vm, :stack, Stack.push(vm.stack, local))} end # Needs revisit defp exec_inst({frame, vm}, {:get_global, idx}) do global = Enum.at(vm.globals, idx) {frame, Map.put(vm, :stack, Stack.push(vm.stack, global))} end # Needs revisit defp exec_inst({frame, vm}, {:set_global, idx}) do {value, stack} = Stack.pop(vm.stack) globals = List.replace_at(vm.globals, idx, value) {frame, Map.merge(vm, %{globals: globals, stack: stack})} end defp exec_inst({frame, vm}, {:set_local, idx}) do {value, stack} = Stack.pop(vm.stack) locals = List.replace_at(frame.locals, idx, value) {Map.put(frame, :locals, locals), Map.put(vm, :stack, stack)} end defp exec_inst({frame, vm}, {:tee_local, idx}) do value = Stack.read(vm.stack) locals = List.replace_at(frame.locals, idx, value) {Map.put(frame, :locals, locals), vm} end defp exec_inst({frame, vm}, :i32_add) do {[a, b], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, a + b))} end defp exec_inst({frame, vm}, :i32_sub) do {[b, a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, a - b))} end defp exec_inst({frame, vm}, :i32_mul) do {[a, b], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, a * b))} end defp exec_inst({frame, vm}, :f32_add) do {[a, b], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, a + b))} end defp exec_inst({frame, vm}, :f32_sub) do {[b, a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, a - b))} end defp exec_inst({frame, vm}, :f32_mul) do {[a, b], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, a * b))} end defp exec_inst({frame, vm}, :f64_add) do {[a, b], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, a + b))} end defp exec_inst({frame, vm}, :f64_sub) do {[b, a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, a - b))} end defp exec_inst({frame, vm}, :f64_mul) do {[a, b], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, a * b))} end defp exec_inst({frame, vm}, :i32_rem_u) do {[b, a], stack} = Stack.pop_multiple(vm.stack) if b == 0 do {:error, :undefined} else {frame, Map.put(vm, :stack, Stack.push(stack, a - (b*trunc(a/b))))} end end defp exec_inst({frame, vm}, :i64_rem_u) do {[b, a], stack} = Stack.pop_multiple(vm.stack) if b == 0 do {:error, :undefined} else {frame, Map.put(vm, :stack, Stack.push(stack, a - (b*trunc(a/b))))} end end defp exec_inst({frame, vm}, :f32_min) do {[a, b], stack} = Stack.pop_multiple(vm.stack) min = Enum.min([a, b]) {frame, Map.put(vm, :stack, Stack.push(stack, min))} end defp exec_inst({frame, vm}, :f32_max) do {[a, b], stack} = Stack.pop_multiple(vm.stack) max = Enum.max([a, b]) {frame, Map.put(vm, :stack, Stack.push(stack, max))} end defp exec_inst({frame, vm}, :f64_min) do {[a, b], stack} = Stack.pop_multiple(vm.stack) min = Enum.min([a, b]) {frame, Map.put(vm, :stack, Stack.push(stack, min))} end defp exec_inst({frame, vm}, :f32_nearest) do {[a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, Kernel.round(a)))} end defp exec_inst({frame, vm}, :f64_nearest) do {[a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, Kernel.round(a)))} end defp exec_inst({frame, vm}, :f32_trunc) do {[a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, Kernel.trunc(a)))} end defp exec_inst({frame, vm}, :f64_trunc) do {[a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, Kernel.trunc(a)))} end defp exec_inst({frame, vm}, :f32_floor) do {[a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, Float.floor(a)))} end defp exec_inst({frame, vm}, :f64_floor) do {[a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, Float.floor(a)))} end defp exec_inst({frame, vm}, :f32_neg) do {[a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, a*-1))} end defp exec_inst({frame, vm}, :f32_ceil) do {[a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, Float.ceil(a)))} end defp exec_inst({frame, vm}, :f64_ceil) do {[a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, Float.ceil(a)))} end defp exec_inst({frame, vm}, :f64_neg) do {[a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, a*-1))} end defp exec_inst({frame, vm}, :f32_abs) do {[a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, abs(a)))} end defp exec_inst({frame, vm}, :f64_abs) do {[a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, abs(a)))} end defp exec_inst({frame, vm}, :f64_max) do {[a, b], stack} = Stack.pop_multiple(vm.stack) max = Enum.max([a, b]) {frame, Map.put(vm, :stack, Stack.push(stack, max))} end defp exec_inst({frame, vm}, :f32_sqrt) do {[a], stack} = Stack.pop(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, :math.sqrt(a)))} end defp exec_inst({frame, vm}, :f64_sqrt) do {[a], stack} = Stack.pop(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, :math.sqrt(a)))} end defp exec_inst({frame, vm}, :f32_div) do {[b, a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, a / b))} end defp exec_inst({frame, vm}, :i32_div_s) do {[b, a], stack} = Stack.pop_multiple(vm.stack) j1 = sign_value(a, 32) j2 = sign_value(b, 32) if j2 == 0 do {:error, :undefined} else if j1/j2 == :math.pow(2, 31) do {:error, :undefined} else res = trunc(j1/j2) n = :math.pow(2, 31) s_1 = n + res ans = s_1 - :math.pow(2, 32) {frame, Map.put(vm, :stack, Stack.push(stack, ans))} end end end defp exec_inst({frame, vm}, :i64_div_s) do {[b, a], stack} = Stack.pop_multiple(vm.stack) j1 = sign_value(a, 64) j2 = sign_value(b, 64) if j2 == 0 do {:error, :undefined} else if j1/j2 == :math.pow(2, 63) do {:error, :undefined} else res = trunc(j1/j2) n = :math.pow(2, 63) s_1 = n + res ans = s_1 - :math.pow(2, 64) {frame, Map.put(vm, :stack, Stack.push(stack, ans))} end end end defp exec_inst({frame, vm}, :i32_div_u) do {[b, a], stack} = Stack.pop_multiple(vm.stack) if b == 0 do {:error, :undefined} else rem = a - (b*trunc(a/b)) result = Integer.floor_div((a - rem), b) {frame, Map.put(vm, :stack, Stack.push(stack, result))} end end defp exec_inst({frame, vm}, :i32_rem_s) do {[b, a], stack} = Stack.pop_multiple(vm.stack) if b == 0 do {:error, :undefined} else j1 = sign_value(a, 32) j2 = sign_value(b, 32) rem = j1 - (j2*trunc(j1/j2)) n = :math.pow(2, 32) res = n - rem {frame, Map.put(vm, :stack, Stack.push(stack, res))} end end defp exec_inst({frame, vm}, :i64_rem_s) do {[b, a], stack} = Stack.pop_multiple(vm.stack) if b == 0 do {:error, :undefined} else j1 = sign_value(a, 64) j2 = sign_value(b, 64) rem = j1 - (j2*trunc(j1/j2)) n = :math.pow(2, 64) res = n - rem {frame, Map.put(vm, :stack, Stack.push(stack, res))} end end defp exec_inst({frame, vm}, :i64_div_u) do {[b, a], stack} = Stack.pop_multiple(vm.stack) if b == 0 do {:error, :undefined} else rem = a - (b*trunc(a/b)) result = Integer.floor_div((a - rem), b) {frame, Map.put(vm, :stack, Stack.push(stack, result))} end end defp exec_inst({frame, vm}, :i32_popcnt) do {a, stack} = Stack.pop(vm.stack) result = popcnt(a, 32) {frame, Map.put(vm, :stack, Stack.push(stack, result))} end defp exec_inst({frame, vm}, :i64_popcnt) do {a, stack} = Stack.pop(vm.stack) result = popcnt(a, 64) {frame, Map.put(vm, :stack, Stack.push(stack, result))} end defp exec_inst({frame, vm}, :i32_rotl) do {[b, a], stack} = Stack.pop_multiple(vm.stack) j2 = b - (32 * Integer.floor_div(b, 32)) answer = rotl(a, j2) {frame, Map.put(vm, :stack, Stack.push(stack, answer))} end defp exec_inst({frame, vm}, :i32_rotr) do {[b, a], stack} = Stack.pop_multiple(vm.stack) j2 = Integer.mod(b, 32) answer = rotr(a, j2) {frame, Map.put(vm, :stack, Stack.push(stack, answer))} end defp exec_inst({frame, vm}, :i32_and) do {[b, a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, band(a, b)))} end defp exec_inst({frame, vm}, :i32_or) do {[b, a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, bor(a, b)))} end defp exec_inst({frame, vm}, :i32_xor) do {[b, a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, bxor(a, b)))} end defp exec_inst({frame, vm}, :i32_shr_s) do {[b, a], stack} = Stack.pop_multiple(vm.stack) j2 = Integer.mod(b, 32) {frame, Map.put(vm, :stack, Stack.push(stack, bsr(a, j2)))} end defp exec_inst({frame, vm}, :i32_eq) do {[a, b], stack} = Stack.pop_multiple(vm.stack) val = if a === b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :f32_eq) do {[a, b], stack} = Stack.pop_multiple(vm.stack) val = if a === b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :f64_eq) do {[a, b], stack} = Stack.pop_multiple(vm.stack) val = if a === b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :i32_ne) do {[a, b], stack} = Stack.pop_multiple(vm.stack) val = if a !== b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :f32_lt) do {[a, b], stack} = Stack.pop_multiple(vm.stack) val = if a !== b && a < b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :f64_lt) do {[a, b], stack} = Stack.pop_multiple(vm.stack) val = if a !== b && a < b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :f32_le) do {[a, b], stack} = Stack.pop_multiple(vm.stack) val = if a !== b && a <= b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :f64_le) do {[a, b], stack} = Stack.pop_multiple(vm.stack) val = if a !== b && a <= b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :f32_ge) do {[a, b], stack} = Stack.pop_multiple(vm.stack) val = if a !== b && a <= b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :f64_ge) do {[a, b], stack} = Stack.pop_multiple(vm.stack) val = if a !== b && a <= b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :f32_gt) do {[a, b], stack} = Stack.pop_multiple(vm.stack) val = if a !== b && a > b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :f64_gt) do {[a, b], stack} = Stack.pop_multiple(vm.stack) val = if a !== b && a > b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :f32_ne) do {[a, b], stack} = Stack.pop_multiple(vm.stack) val = if a !== b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :f64_ne) do {[a, b], stack} = Stack.pop_multiple(vm.stack) val = if a !== b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :i32_lt_s) do {[b, a], stack} = Stack.pop_multiple(vm.stack) val = if a < b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :i32_le_s) do {[b, a], stack} = Stack.pop_multiple(vm.stack) val = if a <= b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :i32_gt_s) do {[b, a], stack} = Stack.pop_multiple(vm.stack) val = if a > b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :i32_ge_s) do {[b, a], stack} = Stack.pop_multiple(vm.stack) val = if a >= b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :i32_eqz) do {a, stack} = Stack.pop(vm.stack) val = if a === 0, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :i64_add) do {[a, b], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, a + b))} end defp exec_inst({frame, vm}, :i64_sub) do {[b, a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, a - b))} end defp exec_inst({frame, vm}, :i64_mul) do {[a, b], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, a * b))} end defp exec_inst({frame, vm}, :i64_and) do {[b, a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, band(a, b)))} end defp exec_inst({frame, vm}, :i64_or) do {[b, a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, bor(a, b)))} end defp exec_inst({frame, vm}, :i64_xor) do {[b, a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, bxor(a, b)))} end defp exec_inst({frame, vm}, :i64_shr_s) do {[b, a], stack} = Stack.pop_multiple(vm.stack) j2 = Integer.mod(b, 64) {frame, Map.put(vm, :stack, Stack.push(stack, bsr(a, j2)))} end defp exec_inst({frame, vm}, :i32_shl) do {[b, a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, bsl(a, b)))} end defp exec_inst({frame, vm}, :i64_shl) do {[b, a], stack} = Stack.pop_multiple(vm.stack) {frame, Map.put(vm, :stack, Stack.push(stack, bsl(a, b)))} end defp exec_inst({frame, vm}, :i64_shr_u) do {[b, a], stack} = Stack.pop_multiple(vm.stack) j2 = Integer.mod(b, 64) {frame, Map.put(vm, :stack, Stack.push(stack, bsr(a, j2)))} end defp exec_inst({frame, vm}, :i32_shr_u) do {[a, b], stack} = Stack.pop_multiple(vm.stack) j2 = b - (32 * Integer.floor_div(b, 32)) |> IO.inspect Bitwise.band(bsr(a, j2), 0xFFFFFFFF) |> IO.inspect {frame, Map.put(vm, :stack, Stack.push(stack, bsr(a, j2)))} end defp exec_inst({frame, vm}, :i64_eq) do {[a, b], stack} = Stack.pop_multiple(vm.stack) val = if a === b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :i64_ne) do {[a, b], stack} = Stack.pop_multiple(vm.stack) val = if a !== b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :i64_lt_s) do {[b, a], stack} = Stack.pop_multiple(vm.stack) val = if a < b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :i64_le_s) do {[b, a], stack} = Stack.pop_multiple(vm.stack) val = if a <= b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :i64_gt_s) do {[b, a], stack} = Stack.pop_multiple(vm.stack) val = if a > b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :i64_ge_s) do {[b, a], stack} = Stack.pop_multiple(vm.stack) val = if a >= b, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :i64_eqz) do {a, stack} = Stack.pop(vm.stack) val = if a === 0, do: 1, else: 0 {frame, Map.put(vm, :stack, Stack.push(stack, val))} end defp exec_inst({frame, vm}, :current_memory) do size = length(vm.memory.pages) {frame, Map.put(vm, :stack, Stack.push(vm.stack, size))} end defp exec_inst({frame, vm}, :grow_memory) do {pages, stack} = Stack.pop(vm.stack) {frame, Map.merge(vm, %{memory: Memory.grow(vm.memory, pages), stack: Stack.push(stack, length(vm.memory))})} end defp exec_inst({frame, vm}, {:call, funcidx}) do func_addr = Enum.at(frame.module.funcaddrs, funcidx) vm = create_frame_and_execute(vm, func_addr) {frame, vm} end defp exec_inst({frame, vm}, :unreachable), do: {frame, vm} defp exec_inst({frame, vm}, :nop), do: {frame, vm} defp exec_inst({frame, vm}, :end), do: {frame, vm} defp exec_inst({frame, vm}, op) do IEx.pry end # Reference https://lemire.me/blog/2017/05/29/unsigned-vs-signed-integer-arithmetic/ defp sign_value(integer, n), do: sign_value(integer, n, :math.pow(-2, 31), :math.pow(2, 31)) defp sign_value(integer, n, upper, lower) when integer >= 0 and integer < lower, do: integer defp sign_value(integer, n, upper, lower) when integer > upper and integer < -1, do: :math.pow(2, 32) + integer defp popcnt(integer, 32) do <> |> Binary.to_list() |> Enum.reject(& &1 == 0) |> Enum.count() end defp popcnt(integer, 64) do <> |> Binary.to_list() |> Enum.reject(& &1 == 0) |> Enum.count() end defp rotl(number, shift) do number <<< shift ||| number >>> (32 - shift) end defp rotr(number, shift) do (32 - number) <<< shift ||| number >>> shift end end