2020-04-01 21:33:36 +13:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require 'spec_helper'
|
|
|
|
|
|
|
|
# Simple examples taken from: https://blog.scottlogic.com/2018/04/26/webassembly-by-hand.html
|
|
|
|
|
|
|
|
RSpec.describe 'WAT code generation' do
|
|
|
|
let(:mod) { WAG::Module.new }
|
|
|
|
subject { mod }
|
|
|
|
|
|
|
|
context 'Basic example' do
|
|
|
|
let(:mod) do
|
2020-04-02 10:05:22 +13:00
|
|
|
super().build do
|
|
|
|
func do
|
2020-04-01 21:33:36 +13:00
|
|
|
result(:i32)
|
|
|
|
i32.const(42)
|
|
|
|
end
|
2020-04-02 10:05:22 +13:00
|
|
|
export('helloWorld') do
|
2020-04-01 21:33:36 +13:00
|
|
|
func(0)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#to_wat' do
|
|
|
|
subject { mod.to_wat.to_s }
|
|
|
|
it { is_expected.to eq '(module (export "helloWorld" (func 0)) (func (result i32) (i32.const 42)))' }
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#to_wasm' do
|
|
|
|
subject { mod.to_wasm }
|
|
|
|
|
|
|
|
it { is_expected.to be_valid }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'Game of life example' do
|
|
|
|
let(:mod) do
|
2020-04-02 10:05:22 +13:00
|
|
|
super().build do
|
|
|
|
import('console', 'log') do
|
2020-04-01 21:33:36 +13:00
|
|
|
func(:log) do
|
|
|
|
param(:i32)
|
|
|
|
param(:i32)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
memory(:mem, 1)
|
2020-04-01 21:33:36 +13:00
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
table(16, :anyfunc)
|
2020-04-01 21:33:36 +13:00
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
elem(0, :dead, :dead, :dead, :alive, :dead, :dead, :dead, :dead,
|
|
|
|
:dead, :dead, :dead, :alive, :alive, :dead, :dead, :dead,
|
|
|
|
:dead, :dead)
|
2020-04-01 21:33:36 +13:00
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
func(:alive) do
|
2020-04-01 21:33:36 +13:00
|
|
|
result(:i32)
|
|
|
|
i32.const(1)
|
|
|
|
end
|
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
func(:dead) do
|
2020-04-01 21:33:36 +13:00
|
|
|
result(:i32)
|
|
|
|
i32.const(0)
|
|
|
|
end
|
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
func(:offsetFromCoordinate) do
|
2020-04-01 21:33:36 +13:00
|
|
|
param(:x, :i32)
|
|
|
|
param(:y, :i32)
|
|
|
|
result(:i32)
|
|
|
|
|
|
|
|
i32.add do
|
|
|
|
i32.mul do
|
|
|
|
i32.const(200)
|
|
|
|
local.get(:y)
|
|
|
|
end
|
|
|
|
i32.mul do
|
|
|
|
i32.const(4)
|
|
|
|
local.get(:x)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
func(:setCell) do
|
2020-04-01 21:33:36 +13:00
|
|
|
param(:x, :i32)
|
|
|
|
param(:y, :i32)
|
|
|
|
param(:value, :i32)
|
|
|
|
|
|
|
|
i32.store do
|
|
|
|
call(:offsetFromCoordinate) do
|
|
|
|
local.get(:x)
|
|
|
|
local.get(:y)
|
|
|
|
end
|
|
|
|
local.get(:value)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
func(:getCell) do
|
2020-04-01 21:33:36 +13:00
|
|
|
param(:x, :i32)
|
|
|
|
param(:y, :i32)
|
|
|
|
result(:i32)
|
|
|
|
|
|
|
|
if_ do
|
|
|
|
result(:i32)
|
|
|
|
block do
|
|
|
|
result(:i32)
|
|
|
|
i32.and do
|
|
|
|
call(:inRange) do
|
|
|
|
i32.const(0)
|
|
|
|
i32.const(50)
|
|
|
|
local.get(:x)
|
|
|
|
end
|
|
|
|
call(:inRange) do
|
|
|
|
i32.const(0)
|
|
|
|
i32.const(50)
|
|
|
|
local.get(:y)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
then_ do
|
|
|
|
i32.load8_u do
|
|
|
|
call(:offsetFromCoordinate) do
|
|
|
|
local.get(:x)
|
|
|
|
local.get(:y)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else_ do
|
|
|
|
i32.const(0)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
func(:liveNeighbourCount) do
|
2020-04-01 21:33:36 +13:00
|
|
|
param(:x, :i32)
|
|
|
|
param(:y, :i32)
|
|
|
|
result(:i32)
|
|
|
|
|
|
|
|
i32.const(0)
|
|
|
|
|
|
|
|
# add the cell value from x + 1, y
|
|
|
|
call(:isCellAlive) do
|
|
|
|
i32.add do
|
|
|
|
local.get(:x)
|
|
|
|
i32.const(1)
|
|
|
|
end
|
|
|
|
local.get(:y)
|
|
|
|
end
|
|
|
|
i32.add
|
|
|
|
|
|
|
|
# add the cell value from x - 1, y
|
|
|
|
call(:isCellAlive) do
|
|
|
|
i32.add do
|
|
|
|
local.get(:x)
|
|
|
|
i32.const(-1)
|
|
|
|
end
|
|
|
|
local.get(:y)
|
|
|
|
end
|
|
|
|
i32.add
|
|
|
|
|
|
|
|
# add the cell value from x, y - 1
|
|
|
|
call(:isCellAlive) do
|
|
|
|
local.get(:x)
|
|
|
|
i32.add do
|
|
|
|
local.get(:y)
|
|
|
|
i32.const(-1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
i32.add
|
|
|
|
|
|
|
|
# add the cell value from x - 1, y - 1
|
|
|
|
call(:isCellAlive) do
|
|
|
|
i32.add do
|
|
|
|
local.get(:x)
|
|
|
|
i32.const(-1)
|
|
|
|
end
|
|
|
|
i32.add do
|
|
|
|
local.get(:y)
|
|
|
|
i32.const(-1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
i32.add
|
|
|
|
|
|
|
|
# add the cell value from x + 1, y - 1
|
|
|
|
call(:isCellAlive) do
|
|
|
|
i32.add do
|
|
|
|
local.get(:x)
|
|
|
|
i32.const(1)
|
|
|
|
end
|
|
|
|
i32.add do
|
|
|
|
local.get(:y)
|
|
|
|
i32.const(-1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
i32.add
|
|
|
|
|
|
|
|
# add the cell value from x, y + 1
|
|
|
|
call(:isCellAlive) do
|
|
|
|
local.get(:x)
|
|
|
|
i32.add do
|
|
|
|
local.get(:y)
|
|
|
|
i32.const(1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
i32.add
|
|
|
|
|
|
|
|
# add the cell value from x - 1, y + 1
|
|
|
|
call(:isCellAlive) do
|
|
|
|
i32.add do
|
|
|
|
local.get(:x)
|
|
|
|
i32.const(-1)
|
|
|
|
end
|
|
|
|
i32.add do
|
|
|
|
local.get(:y)
|
|
|
|
i32.const(1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
i32.add
|
|
|
|
|
|
|
|
# add the cell value from x + 1, y + 1
|
|
|
|
call(:isCellAlive) do
|
|
|
|
i32.add do
|
|
|
|
local.get(:x)
|
|
|
|
i32.const(1)
|
|
|
|
end
|
|
|
|
i32.add do
|
|
|
|
local.get(:y)
|
|
|
|
i32.const(1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
i32.add
|
|
|
|
end
|
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
func(:inRange) do
|
2020-04-01 21:33:36 +13:00
|
|
|
param(:low, :i32)
|
|
|
|
param(:high, :i32)
|
|
|
|
param(:value, :i32)
|
|
|
|
result(:i32)
|
|
|
|
|
|
|
|
i32.and do
|
|
|
|
i32.ge_s do
|
|
|
|
local.get(:value)
|
|
|
|
local.get(:low)
|
|
|
|
end
|
|
|
|
i32.lt_s do
|
|
|
|
local.get(:value)
|
|
|
|
local.get(:high)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
func(:isCellAlive) do
|
2020-04-01 21:33:36 +13:00
|
|
|
param(:x, :i32)
|
|
|
|
param(:y, :i32)
|
|
|
|
result(:i32)
|
|
|
|
|
|
|
|
i32.and do
|
|
|
|
call(:getCell) do
|
|
|
|
local.get(:x)
|
|
|
|
local.get(:y)
|
|
|
|
end
|
|
|
|
i32.const(1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
func(:setCellStateForNextGeneration) do
|
2020-04-01 21:33:36 +13:00
|
|
|
param(:x, :i32)
|
|
|
|
param(:y, :i32)
|
|
|
|
param(:value, :i32)
|
|
|
|
|
|
|
|
call(:setCell) do
|
|
|
|
local.get(:x)
|
|
|
|
local.get(:y)
|
|
|
|
i32.or do
|
|
|
|
call(:isCellAlive) do
|
|
|
|
local.get(:x)
|
|
|
|
local.get(:y)
|
|
|
|
end
|
|
|
|
i32.shl do
|
|
|
|
local.get(:value)
|
|
|
|
i32.const(1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
func(:evolveCellToNextGeneration) do
|
2020-04-01 21:33:36 +13:00
|
|
|
param(:x, :i32)
|
|
|
|
param(:y, :i32)
|
|
|
|
|
|
|
|
call(:setCellStateForNextGeneration) do
|
|
|
|
local.get(:x)
|
|
|
|
local.get(:y)
|
|
|
|
call_indirect do
|
|
|
|
result(:i32)
|
|
|
|
i32.or do
|
|
|
|
i32.mul do
|
|
|
|
i32.const(9)
|
|
|
|
call(:isCellAlive) do
|
|
|
|
local.get(:x)
|
|
|
|
local.get(:y)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
call(:liveNeighbourCount) do
|
|
|
|
local.get(:x)
|
|
|
|
local.get(:y)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
func(:increment) do
|
2020-04-01 21:33:36 +13:00
|
|
|
param(:value, :i32)
|
|
|
|
result(:i32)
|
|
|
|
|
|
|
|
i32.add do
|
|
|
|
local.get(:value)
|
|
|
|
i32.const(1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
func(:evolveAllCells) do
|
2020-04-01 21:33:36 +13:00
|
|
|
local(:x, :i32)
|
|
|
|
local(:y, :i32)
|
|
|
|
|
|
|
|
local.set(:x) do
|
|
|
|
i32.const(0)
|
|
|
|
end
|
|
|
|
local.set(:y) do
|
|
|
|
i32.const(0)
|
|
|
|
end
|
|
|
|
|
|
|
|
block do
|
|
|
|
loop_ do
|
|
|
|
local.set(:x) do
|
|
|
|
i32.const(0)
|
|
|
|
end
|
|
|
|
|
|
|
|
block do
|
|
|
|
loop_ do
|
|
|
|
call(:evolveCellToNextGeneration) do
|
|
|
|
local.get(:x)
|
|
|
|
local.get(:y)
|
|
|
|
end
|
|
|
|
local.set(:x) do
|
|
|
|
call(:increment) do
|
|
|
|
local.get(:x)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
br_if(1) do
|
|
|
|
i32.eq do
|
|
|
|
local.get(:x)
|
|
|
|
i32.const(50)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
br(0)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local.set(:y) do
|
|
|
|
call(:increment) do
|
|
|
|
local.get(:y)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
br_if(1) do
|
|
|
|
i32.eq do
|
|
|
|
local.get(:y)
|
|
|
|
i32.const(50)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
br(0)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
func(:promoteNextGeneration) do
|
2020-04-01 21:33:36 +13:00
|
|
|
local(:x, :i32)
|
|
|
|
local(:y, :i32)
|
|
|
|
|
|
|
|
local.set(:x) do
|
|
|
|
i32.const(0)
|
|
|
|
end
|
|
|
|
local.set(:y) do
|
|
|
|
i32.const(0)
|
|
|
|
end
|
|
|
|
|
|
|
|
block do
|
|
|
|
loop_ do
|
|
|
|
local.set(:x) do
|
|
|
|
i32.const(0)
|
|
|
|
end
|
|
|
|
|
|
|
|
block do
|
|
|
|
loop_ do
|
|
|
|
call(:setCell) do
|
|
|
|
local.get(:x)
|
|
|
|
local.get(:y)
|
|
|
|
i32.shr_u do
|
|
|
|
call(:getCell) do
|
|
|
|
local.get(:x)
|
|
|
|
local.get(:y)
|
|
|
|
end
|
|
|
|
i32.const(1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local.set(:x) do
|
|
|
|
call(:increment) do
|
|
|
|
local.get(:x)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
br_if(1) do
|
|
|
|
i32.eq do
|
|
|
|
local.get(:x)
|
|
|
|
i32.const(50)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
br(0)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local.set(:y) do
|
|
|
|
call(:increment) do
|
|
|
|
local.get(:y)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
br_if(1) do
|
|
|
|
i32.eq do
|
|
|
|
local.get(:y)
|
|
|
|
i32.const(50)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
br(0)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
func(:tick) do
|
2020-04-01 21:33:36 +13:00
|
|
|
call(:evolveAllCells)
|
|
|
|
call(:promoteNextGeneration)
|
|
|
|
end
|
|
|
|
|
2020-04-02 10:05:22 +13:00
|
|
|
export('tick') do
|
2020-04-01 21:33:36 +13:00
|
|
|
func(:tick)
|
|
|
|
end
|
2020-04-02 10:05:22 +13:00
|
|
|
export('promoteNextGeneration') do
|
2020-04-01 21:33:36 +13:00
|
|
|
func(:promoteNextGeneration)
|
|
|
|
end
|
2020-04-02 10:05:22 +13:00
|
|
|
export('evolveAllCells') do
|
2020-04-01 21:33:36 +13:00
|
|
|
func(:evolveAllCells)
|
|
|
|
end
|
2020-04-02 10:05:22 +13:00
|
|
|
export('evolveCellToNextGeneration') do
|
2020-04-01 21:33:36 +13:00
|
|
|
func(:evolveCellToNextGeneration)
|
|
|
|
end
|
2020-04-02 10:05:22 +13:00
|
|
|
export('setCellStateForNextGeneration') do
|
2020-04-01 21:33:36 +13:00
|
|
|
func(:setCellStateForNextGeneration)
|
|
|
|
end
|
2020-04-02 10:05:22 +13:00
|
|
|
export('isCellAlive') do
|
2020-04-01 21:33:36 +13:00
|
|
|
func(:isCellAlive)
|
|
|
|
end
|
2020-04-02 10:05:22 +13:00
|
|
|
export('inRange') do
|
2020-04-01 21:33:36 +13:00
|
|
|
func(:inRange)
|
|
|
|
end
|
2020-04-02 10:05:22 +13:00
|
|
|
export('offsetFromCoordinate') do
|
2020-04-01 21:33:36 +13:00
|
|
|
func(:offsetFromCoordinate)
|
|
|
|
end
|
2020-04-02 10:05:22 +13:00
|
|
|
export('liveNeighbourCount') do
|
2020-04-01 21:33:36 +13:00
|
|
|
func(:liveNeighbourCount)
|
|
|
|
end
|
2020-04-02 10:05:22 +13:00
|
|
|
export('getCell') do
|
2020-04-01 21:33:36 +13:00
|
|
|
func(:getCell)
|
|
|
|
end
|
2020-04-02 10:05:22 +13:00
|
|
|
export('setCell') do
|
2020-04-01 21:33:36 +13:00
|
|
|
func(:setCell)
|
|
|
|
end
|
2020-04-02 10:05:22 +13:00
|
|
|
export('memory') do
|
2020-04-01 21:33:36 +13:00
|
|
|
memory(:mem)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '#to_wasm' do
|
|
|
|
subject { mod.to_wasm }
|
|
|
|
it { is_expected.to be_valid }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|