Refactor as Rust workspace

This commit is contained in:
Rerumu
2021-11-28 03:33:00 -05:00
parent 185799eecc
commit a1894e492c
22 changed files with 11 additions and 9 deletions
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "wasm"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
parity-wasm = "0.42.2"
+393
View File
@@ -0,0 +1,393 @@
local module = {}
local bit = require('bit')
local ffi = require('ffi')
local jit = require('jit')
local u32 = ffi.typeof('uint32_t')
local u64 = ffi.typeof('uint64_t')
local i64 = ffi.typeof('int64_t')
ffi.cdef [[
typedef union {
int32_t i32;
int64_t i64;
float f32;
double f64;
} Reinterpret;
]]
if jit and jit.opt then jit.opt.start("maxsnap=1000", "loopunroll=500", "maxmcode=2048") end
local function truncate(num)
if num >= 0 then
return math.floor(num)
else
return math.ceil(num)
end
end
do
local add = {}
local sub = {}
local mul = {}
local div = {}
local to_signed = bit.tobit
module.add = add
module.sub = sub
module.mul = mul
module.div = div
function add.i32(a, b) return to_signed(a + b) end
function add.i64(a, b) return a + b end
function sub.i32(a, b) return to_signed(a - b) end
function sub.i64(a, b) return a - b end
function mul.i32(a, b) return to_signed(a * b) end
function mul.i64(a, b) return a * b end
function div.i32(lhs, rhs)
if rhs == 0 then error('division by zero') end
return truncate(lhs / rhs)
end
function div.u32(lhs, rhs)
if rhs == 0 then error('division by zero') end
lhs = tonumber(u32(lhs))
rhs = tonumber(u32(rhs))
return to_signed(math.floor(lhs / rhs))
end
function div.u64(lhs, rhs)
if rhs == 0 then error('division by zero') end
return i64(u64(lhs) / u64(rhs))
end
end
do
local clz = {}
local ctz = {}
local popcnt = {}
local lj_band = bit.band
local lj_lshift = bit.lshift
module.clz = clz
module.ctz = ctz
module.popcnt = popcnt
function clz.i32(num)
for i = 0, 31 do
local mask = lj_lshift(1, 31 - i)
if lj_band(num, mask) ~= 0 then return i end
end
return 32
end
function ctz.i32(num)
for i = 0, 31 do
local mask = lj_lshift(1, i)
if lj_band(num, mask) ~= 0 then return i end
end
return 32
end
function popcnt.i32(num)
local count = 0
while num ~= 0 do
num = lj_band(num, num - 1)
count = count + 1
end
return count
end
end
do
local eqz = {}
local eq = {}
local ne = {}
local le = {}
local lt = {}
local ge = {}
local gt = {}
module.eqz = eqz
module.eq = eq
module.ne = ne
module.le = le
module.lt = lt
module.ge = ge
module.gt = gt
local function to_boolean(cond)
if cond then
return 1
else
return 0
end
end
function eq.i32(lhs, rhs) return to_boolean(lhs == rhs) end
function eq.i64(lhs, rhs) return to_boolean(lhs == rhs) end
function eqz.i32(lhs) return to_boolean(lhs == 0) end
function eqz.i64(lhs) return to_boolean(lhs == 0) end
function ne.i32(lhs, rhs) return to_boolean(lhs ~= rhs) end
function ne.i64(lhs, rhs) return to_boolean(lhs ~= rhs) end
function ge.i32(lhs, rhs) return to_boolean(lhs >= rhs) end
function ge.i64(lhs, rhs) return to_boolean(lhs >= rhs) end
function ge.u32(lhs, rhs) return to_boolean(u32(lhs) >= u32(rhs)) end
function ge.u64(lhs, rhs) return to_boolean(u64(lhs) >= u64(rhs)) end
function gt.i32(lhs, rhs) return to_boolean(lhs > rhs) end
function gt.i64(lhs, rhs) return to_boolean(lhs > rhs) end
function gt.u32(lhs, rhs) return to_boolean(u32(lhs) > u32(rhs)) end
function gt.u64(lhs, rhs) return to_boolean(u64(lhs) > u64(rhs)) end
function le.i32(lhs, rhs) return to_boolean(lhs <= rhs) end
function le.i64(lhs, rhs) return to_boolean(lhs <= rhs) end
function le.u32(lhs, rhs) return to_boolean(u32(lhs) <= u32(rhs)) end
function le.u64(lhs, rhs) return to_boolean(u64(lhs) <= u64(rhs)) end
function lt.i32(lhs, rhs) return to_boolean(lhs < rhs) end
function lt.i64(lhs, rhs) return to_boolean(lhs < rhs) end
function lt.u32(lhs, rhs) return to_boolean(u32(lhs) < u32(rhs)) end
function lt.u64(lhs, rhs) return to_boolean(u64(lhs) < u64(rhs)) end
end
do
local band = {}
local bor = {}
local bxor = {}
local bnot = {}
module.band = band
module.bor = bor
module.bxor = bxor
module.bnot = bnot
band.i32 = bit.band
band.i64 = bit.band
bnot.i32 = bit.bnot
bnot.i64 = bit.bnot
bor.i32 = bit.bor
bor.i64 = bit.bor
bxor.i32 = bit.bxor
bxor.i64 = bit.bxor
end
do
local shl = {}
local shr = {}
local rotl = {}
local rotr = {}
local lj_lshift = bit.lshift
local lj_rshift = bit.rshift
local lj_arshift = bit.arshift
module.shl = shl
module.shr = shr
module.rotl = rotl
module.rotr = rotr
rotl.i32 = bit.rol
rotl.i64 = bit.rol
rotr.i32 = bit.ror
rotr.i64 = bit.ror
shl.i32 = lj_lshift
shl.i64 = lj_lshift
shl.u32 = lj_lshift
shl.u64 = lj_lshift
shr.i32 = lj_arshift
shr.i64 = lj_arshift
shr.u32 = lj_rshift
shr.u64 = lj_rshift
end
do
local wrap = {}
local trunc = {}
local extend = {}
local convert = {}
local reinterpret = {}
-- This would surely be an issue in a multi-thread environment...
-- ... thankfully this isn't one.
local RE_INSTANCE = ffi.new('Reinterpret')
local function truncate_i64(num) return i64(truncate(num)) end
module.wrap = wrap
module.trunc = trunc
module.extend = extend
module.convert = convert
module.reinterpret = reinterpret
function wrap.i32_i64(num)
RE_INSTANCE.i64 = num
return RE_INSTANCE.i32
end
trunc.i32_f32 = truncate
trunc.i32_f64 = truncate
trunc.u32_f32 = truncate
trunc.u32_f64 = truncate
trunc.i64_f32 = truncate_i64
trunc.i64_f64 = truncate_i64
trunc.u64_f32 = truncate_i64
trunc.u64_f64 = truncate_i64
function extend.u64_i32(num)
RE_INSTANCE.i64 = 0
RE_INSTANCE.i32 = num
return RE_INSTANCE.i64
end
function convert.f32_i32(num) return num end
function convert.f32_u32(num) return tonumber(u32(num)) end
function convert.f32_i64(num) return tonumber(num) end
function convert.f32_u64(num) return tonumber(u64(num)) end
function convert.f64_i32(num) return num end
function convert.f64_u32(num) return tonumber(u32(num)) end
function convert.f64_i64(num) return tonumber(num) end
function convert.f64_u64(num) return tonumber(u64(num)) end
function reinterpret.i32_f32(num)
RE_INSTANCE.f32 = num
return RE_INSTANCE.i32
end
function reinterpret.i64_f64(num)
RE_INSTANCE.f64 = num
return RE_INSTANCE.i64
end
function reinterpret.f32_i32(num)
RE_INSTANCE.i32 = num
return RE_INSTANCE.f32
end
function reinterpret.f64_i64(num)
RE_INSTANCE.i64 = num
return RE_INSTANCE.f64
end
end
do
local load = {}
local store = {}
local cast = ffi.cast
local ptr_i8 = ffi.typeof('int8_t *')
local ptr_i16 = ffi.typeof('int16_t *')
local ptr_i32 = ffi.typeof('int32_t *')
local ptr_i64 = ffi.typeof('int64_t *')
local ptr_u16 = ffi.typeof('uint16_t *')
local ptr_u32 = ffi.typeof('uint32_t *')
local ptr_f32 = ffi.typeof('float *')
local ptr_f64 = ffi.typeof('double *')
module.load = load
module.store = store
function load.i32_i8(memory, addr) return cast(ptr_i8, memory.data)[addr] end
function load.i32_u8(memory, addr) return memory.data[addr] end
function load.i32_i16(memory, addr) return cast(ptr_i16, memory.data + addr)[0] end
function load.i32_u16(memory, addr) return cast(ptr_u16, memory.data + addr)[0] end
function load.i32(memory, addr) return cast(ptr_i32, memory.data + addr)[0] end
function load.i64_i8(memory, addr) return i64(cast(ptr_i8, memory.data)[addr]) end
function load.i64_u8(memory, addr) return i64(memory.data[addr]) end
function load.i64_i16(memory, addr) return i64(cast(ptr_i16, memory.data + addr)[0]) end
function load.i64_u16(memory, addr) return i64(cast(ptr_u16, memory.data + addr)[0]) end
function load.i64_i32(memory, addr) return i64(cast(ptr_i32, memory.data + addr)[0]) end
function load.i64_u32(memory, addr) return i64(cast(ptr_u32, memory.data + addr)[0]) end
function load.i64(memory, addr) return cast(ptr_i64, memory.data + addr)[0] end
function load.f32(memory, addr) return cast(ptr_f32, memory.data + addr)[0] end
function load.f64(memory, addr) return cast(ptr_f64, memory.data + addr)[0] end
function store.i32_n8(memory, addr, value) memory.data[addr] = value end
function store.i32_n16(memory, addr, value) cast(ptr_i16, memory.data + addr)[0] = value end
function store.i32(memory, addr, value) cast(ptr_i32, memory.data + addr)[0] = value end
function store.i64_n8(memory, addr, value) memory.data[addr] = value end
function store.i64_n16(memory, addr, value) cast(ptr_i16, memory.data + addr)[0] = value end
function store.i64_n32(memory, addr, value) cast(ptr_i32, memory.data + addr)[0] = value end
function store.i64(memory, addr, value) cast(ptr_i64, memory.data + addr)[0] = value end
function store.f32(memory, addr, value) cast(ptr_f32, memory.data + addr)[0] = value end
function store.f64(memory, addr, value) cast(ptr_f64, memory.data + addr)[0] = value end
end
do
local memory = {}
local vla_u8 = ffi.typeof('uint8_t[?]')
local WASM_PAGE_SIZE = 65536
module.memory = memory
local function grow_unchecked(memory, old, new)
local data = vla_u8(new * WASM_PAGE_SIZE, 0)
ffi.copy(data, memory.data, old * WASM_PAGE_SIZE)
memory.min = new
memory.data = data
end
function memory.new(min, max)
local memory = {}
memory.min = min
memory.max = max
memory.data = vla_u8(min * WASM_PAGE_SIZE, 0)
return memory
end
function memory.init(memory, offset, data) ffi.copy(memory.data + offset, data) end
function memory.size(memory) return memory.min end
function memory.grow(memory, num)
local old = memory.min
local new = old + num
if new > memory.max then
return -1
else
grow_unchecked(memory, old, new)
return old
end
end
end
return module
+286
View File
@@ -0,0 +1,286 @@
local module = {}
local function no_op(x) return x end
do
local div = {}
module.div = div
function div.i32(lhs, rhs)
if rhs == 0 then error('division by zero') end
return math.floor(lhs / rhs)
end
end
do
local clz = {}
local ctz = {}
local popcnt = {}
module.clz = clz
module.ctz = ctz
module.popcnt = popcnt
clz.i32 = bit32.countlz
ctz.i32 = bit32.countrz
function popcnt.i32(num)
local count = 0
while num ~= 0 do
num = bit.band(num, num - 1)
count = count + 1
end
return count
end
end
do
local eqz = {}
local eq = {}
local ne = {}
local le = {}
local lt = {}
local ge = {}
local gt = {}
module.eqz = eqz
module.eq = eq
module.ne = ne
module.le = le
module.lt = lt
module.ge = ge
module.gt = gt
local function unsign_i32(x)
if x < 0 then x = x + 0x100000000 end
return x
end
local function unsign_i64(x)
if x < 0 then x = x + 0x10000000000000000 end
return x
end
function eq.i32(lhs, rhs) return lhs == rhs and 1 or 0 end
function eq.i64(lhs, rhs) return lhs == rhs and 1 or 0 end
function eqz.i32(lhs) return lhs == 0 and 1 or 0 end
function eqz.i64(lhs) return lhs == 0 and 1 or 0 end
function ne.i32(lhs, rhs) return lhs ~= rhs and 1 or 0 end
function ne.i64(lhs, rhs) return lhs ~= rhs and 1 or 0 end
function ge.i32(lhs, rhs) return lhs >= rhs and 1 or 0 end
function ge.i64(lhs, rhs) return lhs >= rhs and 1 or 0 end
function ge.u32(lhs, rhs) return unsign_i32(lhs) >= unsign_i32(rhs) and 1 or 0 end
function ge.u64(lhs, rhs) return unsign_i64(lhs) >= unsign_i64(rhs) and 1 or 0 end
function gt.i32(lhs, rhs) return lhs > rhs and 1 or 0 end
function gt.i64(lhs, rhs) return lhs > rhs and 1 or 0 end
function gt.u32(lhs, rhs) return unsign_i32(lhs) > unsign_i32(rhs) and 1 or 0 end
function gt.u64(lhs, rhs) return unsign_i64(lhs) > unsign_i64(rhs) and 1 or 0 end
function le.i32(lhs, rhs) return lhs <= rhs and 1 or 0 end
function le.i64(lhs, rhs) return lhs <= rhs and 1 or 0 end
function le.u32(lhs, rhs) return unsign_i32(lhs) <= unsign_i32(rhs) and 1 or 0 end
function le.u64(lhs, rhs) return unsign_i64(lhs) <= unsign_i64(rhs) and 1 or 0 end
function lt.i32(lhs, rhs) return lhs < rhs and 1 or 0 end
function lt.i64(lhs, rhs) return lhs < rhs and 1 or 0 end
function lt.u32(lhs, rhs) return unsign_i32(lhs) < unsign_i32(rhs) and 1 or 0 end
function lt.u64(lhs, rhs) return unsign_i64(lhs) < unsign_i64(rhs) and 1 or 0 end
end
do
local band = {}
local bor = {}
local bxor = {}
local bnot = {}
module.band = band
module.bor = bor
module.bxor = bxor
module.bnot = bnot
band.i32 = bit32.band
band.i64 = bit32.band
bnot.i32 = bit32.bnot
bnot.i64 = bit32.bnot
bor.i32 = bit32.bor
bor.i64 = bit32.bor
bxor.i32 = bit32.bxor
bxor.i64 = bit32.bxor
end
do
local shl = {}
local shr = {}
local rotl = {}
local rotr = {}
module.shl = shl
module.shr = shr
module.rotl = rotl
module.rotr = rotr
rotl.i32 = bit32.lrotate
rotr.i32 = bit32.rrotate
shl.i32 = bit32.lshift
shl.i64 = bit32.lshift
shl.u32 = bit32.lshift
shl.u64 = bit32.lshift
shr.i32 = bit32.rshift
shr.i64 = bit32.rshift
shr.u32 = bit32.rshift
shr.u64 = bit32.rshift
end
do
local extend = {}
local wrap = {}
module.extend = extend
module.wrap = wrap
extend.i32_u64 = no_op
function wrap.i64_i32(i) return i % 2 ^ 32 end
end
do
local load = {}
local store = {}
module.load = load
module.store = store
local function rip_u64(x) return math.floor(x / 0x100000000), x % 0x100000000 end
local function merge_u64(hi, lo) return hi * 0x100000000 + lo end
local function black_mask_byte(value, offset)
local mask = bit32.lshift(0xFF, offset * 8)
return bit32.band(value, bit32.bnot(mask))
end
local function load_byte(memory, addr)
local offset = addr % 4
local value = memory.data[(addr - offset) / 4] or 0
return bit32.band(bit32.rshift(value, offset * 8), 0xFF)
end
local function store_byte(memory, addr, value)
local offset = addr % 4
local adjust = (addr - offset) / 4
local lhs = bit32.lshift(bit32.band(value, 0xFF), offset * 8)
local rhs = black_mask_byte(memory.data[adjust] or 0, offset)
memory.data[adjust] = bit32.bor(lhs, rhs)
end
function load.i32_i8(memory, addr)
local b = load_byte(memory, addr)
if b > 0x7F then b = b - 0x100 end
return b
end
load.i32_u8 = load_byte
function load.i32(memory, addr)
if addr % 4 == 0 then
-- aligned read
return memory.data[addr / 4] or 0
else
-- unaligned read
local b1 = load_byte(memory, addr)
local b2 = bit32.lshift(load_byte(memory, addr + 1), 8)
local b3 = bit32.lshift(load_byte(memory, addr + 2), 16)
local b4 = bit32.lshift(load_byte(memory, addr + 3), 24)
return bit32.bor(b1, b2, b3, b4)
end
end
function load.i64(memory, addr)
local hi = load.i32(memory, addr + 4)
local lo = load.i32(memory, addr)
return merge_u64(hi, lo)
end
store.i32_n8 = store_byte
function store.i32(memory, addr, value)
if addr % 4 == 0 then
-- aligned write
memory.data[addr / 4] = value
else
-- unaligned write
store_byte(memory, addr, value)
store_byte(memory, addr + 1, bit32.rshift(value, 8))
store_byte(memory, addr + 2, bit32.rshift(value, 16))
store_byte(memory, addr + 3, bit32.rshift(value, 24))
end
end
function store.i64(memory, addr, value)
local hi, lo = rip_u64(value)
store.i32(memory, addr, lo)
store.i32(memory, addr + 4, hi)
end
end
do
local memory = {}
module.memory = memory
function memory.new(min, max) return {min = min, max = max, data = {}} end
function memory.init(memory, offset, data)
local store_i8 = module.store.i32_n8
local store_i32 = module.store.i32
local len = #data
local rem = len % 4
for i = 1, len - rem, 4 do
local v = string.unpack('<I4', data, i)
store_i32(memory, offset + i - 1, v)
end
for i = len - rem + 1, len do
local v = string.byte(data, i)
store_i8(memory, offset + i - 1, v)
end
end
function memory.size(memory) return memory.min end
function memory.grow(memory, num)
local old = memory.min
local new = old + num
if new > memory.max then
return -1
else
memory.min = new
return old
end
end
end
return module
+413
View File
@@ -0,0 +1,413 @@
use std::ops::Range;
use parity_wasm::elements::BrTableData;
use crate::backend::visitor::data::Visitor;
use super::operation::{BinOp, Load, Store, UnOp};
#[derive(Clone)]
pub struct Select {
pub cond: Box<Expression>,
pub a: Box<Expression>,
pub b: Box<Expression>,
}
impl Select {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_select(self);
self.cond.accept(visitor);
self.a.accept(visitor);
self.b.accept(visitor);
}
}
#[derive(Clone)]
pub struct GetLocal {
pub var: u32,
}
impl GetLocal {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_get_local(self);
}
}
#[derive(Clone)]
pub struct GetGlobal {
pub var: u32,
}
impl GetGlobal {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_get_global(self);
}
}
#[derive(Clone)]
pub struct AnyLoad {
pub op: Load,
pub offset: u32,
pub pointer: Box<Expression>,
}
impl AnyLoad {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_any_load(self);
self.pointer.accept(visitor);
}
}
#[derive(Clone)]
pub struct MemorySize {
pub memory: u8,
}
impl MemorySize {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_memory_size(self);
}
}
#[derive(Clone)]
pub struct MemoryGrow {
pub memory: u8,
pub value: Box<Expression>,
}
impl MemoryGrow {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_memory_grow(self);
self.value.accept(visitor);
}
}
#[derive(Clone, Copy)]
pub enum Value {
I32(i32),
I64(i64),
F32(f32),
F64(f64),
}
impl Value {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_value(self);
}
}
#[derive(Clone)]
pub struct AnyUnOp {
pub op: UnOp,
pub rhs: Box<Expression>,
}
impl AnyUnOp {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_any_unop(self);
self.rhs.accept(visitor);
}
}
#[derive(Clone)]
pub struct AnyBinOp {
pub op: BinOp,
pub lhs: Box<Expression>,
pub rhs: Box<Expression>,
}
impl AnyBinOp {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_any_binop(self);
self.lhs.accept(visitor);
self.rhs.accept(visitor);
}
}
#[derive(Clone)]
pub enum Expression {
Recall(usize),
Select(Select),
GetLocal(GetLocal),
GetGlobal(GetGlobal),
AnyLoad(AnyLoad),
MemorySize(MemorySize),
MemoryGrow(MemoryGrow),
Value(Value),
AnyUnOp(AnyUnOp),
AnyBinOp(AnyBinOp),
}
impl Expression {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_expression(self);
match self {
Expression::Recall(v) => visitor.visit_recall(*v),
Expression::Select(v) => v.accept(visitor),
Expression::GetLocal(v) => v.accept(visitor),
Expression::GetGlobal(v) => v.accept(visitor),
Expression::AnyLoad(v) => v.accept(visitor),
Expression::MemorySize(v) => v.accept(visitor),
Expression::MemoryGrow(v) => v.accept(visitor),
Expression::Value(v) => v.accept(visitor),
Expression::AnyUnOp(v) => v.accept(visitor),
Expression::AnyBinOp(v) => v.accept(visitor),
}
}
pub fn is_recalling(&self, wanted: usize) -> bool {
match *self {
Expression::Recall(v) => v == wanted,
_ => false,
}
}
}
pub struct Memorize {
pub var: usize,
pub value: Expression,
}
impl Memorize {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_memorize(self);
self.value.accept(visitor);
}
}
pub struct Forward {
pub body: Vec<Statement>,
}
impl Forward {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_forward(self);
for v in &self.body {
v.accept(visitor);
}
}
}
pub struct Backward {
pub body: Vec<Statement>,
}
impl Backward {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_backward(self);
for v in &self.body {
v.accept(visitor);
}
}
}
pub struct If {
pub cond: Expression,
pub truthy: Vec<Statement>,
pub falsey: Option<Vec<Statement>>,
}
impl If {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_if(self);
self.cond.accept(visitor);
for v in &self.truthy {
v.accept(visitor);
}
if let Some(v) = &self.falsey {
for v in v {
v.accept(visitor);
}
}
}
}
pub struct Br {
pub target: u32,
}
impl Br {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_br(self);
}
}
pub struct BrIf {
pub cond: Expression,
pub target: u32,
}
impl BrIf {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_br_if(self);
self.cond.accept(visitor);
}
}
pub struct BrTable {
pub cond: Expression,
pub data: BrTableData,
}
impl BrTable {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_br_table(self);
self.cond.accept(visitor);
}
}
pub struct Return {
pub list: Vec<Expression>,
}
impl Return {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_return(self);
for v in &self.list {
v.accept(visitor);
}
}
}
pub struct Call {
pub func: u32,
pub result: Range<u32>,
pub param_list: Vec<Expression>,
}
impl Call {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_call(self);
for v in &self.param_list {
v.accept(visitor);
}
}
}
pub struct CallIndirect {
pub table: u8,
pub index: Expression,
pub result: Range<u32>,
pub param_list: Vec<Expression>,
}
impl CallIndirect {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_call_indirect(self);
self.index.accept(visitor);
for v in &self.param_list {
v.accept(visitor);
}
}
}
pub struct SetLocal {
pub var: u32,
pub value: Expression,
}
impl SetLocal {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_set_local(self);
self.value.accept(visitor);
}
}
pub struct SetGlobal {
pub var: u32,
pub value: Expression,
}
impl SetGlobal {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_set_global(self);
self.value.accept(visitor);
}
}
pub struct AnyStore {
pub op: Store,
pub offset: u32,
pub pointer: Expression,
pub value: Expression,
}
impl AnyStore {
fn accept<V: Visitor>(&self, visitor: &mut V) {
visitor.visit_any_store(self);
self.pointer.accept(visitor);
self.value.accept(visitor);
}
}
pub enum Statement {
Unreachable,
Memorize(Memorize),
Forward(Forward),
Backward(Backward),
If(If),
Br(Br),
BrIf(BrIf),
BrTable(BrTable),
Return(Return),
Call(Call),
CallIndirect(CallIndirect),
SetLocal(SetLocal),
SetGlobal(SetGlobal),
AnyStore(AnyStore),
}
impl Statement {
fn accept<V: Visitor>(&self, visitor: &mut V) {
match self {
Statement::Unreachable => visitor.visit_unreachable(),
Statement::Memorize(v) => v.accept(visitor),
Statement::Forward(v) => v.accept(visitor),
Statement::Backward(v) => v.accept(visitor),
Statement::If(v) => v.accept(visitor),
Statement::Br(v) => v.accept(visitor),
Statement::BrIf(v) => v.accept(visitor),
Statement::BrTable(v) => v.accept(visitor),
Statement::Return(v) => v.accept(visitor),
Statement::Call(v) => v.accept(visitor),
Statement::CallIndirect(v) => v.accept(visitor),
Statement::SetLocal(v) => v.accept(visitor),
Statement::SetGlobal(v) => v.accept(visitor),
Statement::AnyStore(v) => v.accept(visitor),
}
}
}
pub struct Function {
pub num_param: u32,
pub num_local: u32,
pub num_stack: u32,
pub body: Forward,
}
impl Function {
pub fn accept<V: Visitor>(&self, visitor: &mut V) {
self.body.accept(visitor);
}
}
+3
View File
@@ -0,0 +1,3 @@
pub mod data;
mod operation;
pub mod transformer;
+503
View File
@@ -0,0 +1,503 @@
use std::convert::TryFrom;
use parity_wasm::elements::Instruction;
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
pub enum Load {
I32,
I64,
F32,
F64,
I32_I8,
I32_U8,
I32_I16,
I32_U16,
I64_I8,
I64_U8,
I64_I16,
I64_U16,
I64_I32,
I64_U32,
}
impl Load {
pub fn as_name(self) -> &'static str {
match self {
Self::I32 => "i32",
Self::I64 => "i64",
Self::F32 => "f32",
Self::F64 => "f64",
Self::I32_I8 => "i32_i8",
Self::I32_U8 => "i32_u8",
Self::I32_I16 => "i32_i16",
Self::I32_U16 => "i32_u16",
Self::I64_I8 => "i64_i8",
Self::I64_U8 => "i64_u8",
Self::I64_I16 => "i64_i16",
Self::I64_U16 => "i64_u16",
Self::I64_I32 => "i64_i32",
Self::I64_U32 => "i64_u32",
}
}
}
impl TryFrom<&Instruction> for Load {
type Error = ();
fn try_from(inst: &Instruction) -> Result<Self, Self::Error> {
let result = match inst {
Instruction::I32Load(_, _) => Self::I32,
Instruction::I64Load(_, _) => Self::I64,
Instruction::F32Load(_, _) => Self::F32,
Instruction::F64Load(_, _) => Self::F64,
Instruction::I32Load8S(_, _) => Self::I32_I8,
Instruction::I32Load8U(_, _) => Self::I32_U8,
Instruction::I32Load16S(_, _) => Self::I32_I16,
Instruction::I32Load16U(_, _) => Self::I32_U16,
Instruction::I64Load8S(_, _) => Self::I64_I8,
Instruction::I64Load8U(_, _) => Self::I64_U8,
Instruction::I64Load16S(_, _) => Self::I64_I16,
Instruction::I64Load16U(_, _) => Self::I64_U16,
Instruction::I64Load32S(_, _) => Self::I64_I32,
Instruction::I64Load32U(_, _) => Self::I64_U32,
_ => return Err(()),
};
Ok(result)
}
}
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
pub enum Store {
I32,
I64,
F32,
F64,
I32_N8,
I32_N16,
I64_N8,
I64_N16,
I64_N32,
}
impl Store {
pub fn as_name(self) -> &'static str {
match self {
Self::I32 => "i32",
Self::I64 => "i64",
Self::F32 => "f32",
Self::F64 => "f64",
Self::I32_N8 => "i32_n8",
Self::I32_N16 => "i32_n16",
Self::I64_N8 => "i64_n8",
Self::I64_N16 => "i64_n16",
Self::I64_N32 => "i64_n32",
}
}
}
impl TryFrom<&Instruction> for Store {
type Error = ();
fn try_from(inst: &Instruction) -> Result<Self, Self::Error> {
let result = match inst {
Instruction::I32Store(_, _) => Self::I32,
Instruction::I64Store(_, _) => Self::I64,
Instruction::F32Store(_, _) => Self::F32,
Instruction::F64Store(_, _) => Self::F64,
Instruction::I32Store8(_, _) => Self::I32_N8,
Instruction::I32Store16(_, _) => Self::I32_N16,
Instruction::I64Store8(_, _) => Self::I64_N8,
Instruction::I64Store16(_, _) => Self::I64_N16,
Instruction::I64Store32(_, _) => Self::I64_N32,
_ => return Err(()),
};
Ok(result)
}
}
// Order of mnemonics is:
// operation_result_parameter
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
pub enum UnOp {
Eqz_I32,
Eqz_I64,
Clz_I32,
Ctz_I32,
Popcnt_I32,
Clz_I64,
Ctz_I64,
Popcnt_I64,
Abs_FN,
Neg_FN,
Ceil_FN,
Floor_FN,
Trunc_FN,
Nearest_FN,
Sqrt_FN,
Wrap_I32_I64,
Trunc_I32_F32,
Trunc_U32_F32,
Trunc_I32_F64,
Trunc_U32_F64,
Extend_I64_I32,
Extend_U64_I32,
Trunc_I64_F32,
Trunc_U64_F32,
Trunc_I64_F64,
Trunc_U64_F64,
Convert_F32_I32,
Convert_F32_U32,
Convert_F32_I64,
Convert_F32_U64,
Demote_F32_F64,
Convert_F64_I32,
Convert_F64_U32,
Convert_F64_I64,
Convert_F64_U64,
Promote_F64_F32,
Reinterpret_I32_F32,
Reinterpret_I64_F64,
Reinterpret_F32_I32,
Reinterpret_F64_I64,
}
impl UnOp {
pub fn as_operator(self) -> Option<&'static str> {
let op = match self {
Self::Neg_FN => "-",
_ => return None,
};
Some(op)
}
pub fn as_name(self) -> (&'static str, &'static str) {
match self {
Self::Eqz_I32 => ("eqz", "i32"),
Self::Eqz_I64 => ("eqz", "i64"),
Self::Clz_I32 => ("clz", "i32"),
Self::Ctz_I32 => ("ctz", "i32"),
Self::Popcnt_I32 => ("popcnt", "i32"),
Self::Clz_I64 => ("clz", "i64"),
Self::Ctz_I64 => ("ctz", "i64"),
Self::Popcnt_I64 => ("popcnt", "i64"),
Self::Abs_FN => ("math", "abs"),
Self::Neg_FN => ("neg", "num"),
Self::Ceil_FN => ("math", "ceil"),
Self::Floor_FN => ("math", "floor"),
Self::Trunc_FN => ("trunc", "num"),
Self::Nearest_FN => ("nearest", "num"),
Self::Sqrt_FN => ("math", "sqrt"),
Self::Wrap_I32_I64 => ("wrap", "i32_i64"),
Self::Trunc_I32_F32 => ("trunc", "i32_f32"),
Self::Trunc_U32_F32 => ("trunc", "u32_f32"),
Self::Trunc_I32_F64 => ("trunc", "i32_f64"),
Self::Trunc_U32_F64 => ("trunc", "u32_f64"),
Self::Extend_I64_I32 => ("extend", "i64_i32"),
Self::Extend_U64_I32 => ("extend", "u64_i32"),
Self::Trunc_I64_F32 => ("trunc", "i64_f32"),
Self::Trunc_U64_F32 => ("trunc", "u64_f32"),
Self::Trunc_I64_F64 => ("trunc", "i64_f64"),
Self::Trunc_U64_F64 => ("trunc", "u64_f64"),
Self::Convert_F32_I32 => ("convert", "f32_i32"),
Self::Convert_F32_U32 => ("convert", "f32_u32"),
Self::Convert_F32_I64 => ("convert", "f32_i64"),
Self::Convert_F32_U64 => ("convert", "f32_u64"),
Self::Demote_F32_F64 => ("demote", "f32_f64"),
Self::Convert_F64_I32 => ("convert", "f64_i32"),
Self::Convert_F64_U32 => ("convert", "f64_u32"),
Self::Convert_F64_I64 => ("convert", "f64_i64"),
Self::Convert_F64_U64 => ("convert", "f64_u64"),
Self::Promote_F64_F32 => ("promote", "f64_f32"),
Self::Reinterpret_I32_F32 => ("reinterpret", "i32_f32"),
Self::Reinterpret_I64_F64 => ("reinterpret", "i64_f64"),
Self::Reinterpret_F32_I32 => ("reinterpret", "f32_i32"),
Self::Reinterpret_F64_I64 => ("reinterpret", "f64_i64"),
}
}
}
impl TryFrom<&Instruction> for UnOp {
type Error = ();
fn try_from(inst: &Instruction) -> Result<Self, Self::Error> {
let result = match inst {
Instruction::I32Eqz => Self::Eqz_I32,
Instruction::I64Eqz => Self::Eqz_I64,
Instruction::I32Clz => Self::Clz_I32,
Instruction::I32Ctz => Self::Ctz_I32,
Instruction::I32Popcnt => Self::Popcnt_I32,
Instruction::I64Clz => Self::Clz_I64,
Instruction::I64Ctz => Self::Ctz_I64,
Instruction::I64Popcnt => Self::Popcnt_I64,
Instruction::F32Abs | Instruction::F64Abs => Self::Abs_FN,
Instruction::F32Neg | Instruction::F64Neg => Self::Neg_FN,
Instruction::F32Ceil | Instruction::F64Ceil => Self::Ceil_FN,
Instruction::F32Floor | Instruction::F64Floor => Self::Floor_FN,
Instruction::F32Trunc | Instruction::F64Trunc => Self::Trunc_FN,
Instruction::F32Nearest | Instruction::F64Nearest => Self::Nearest_FN,
Instruction::F32Sqrt | Instruction::F64Sqrt => Self::Sqrt_FN,
Instruction::I32WrapI64 => Self::Wrap_I32_I64,
Instruction::I32TruncSF32 => Self::Trunc_I32_F32,
Instruction::I32TruncUF32 => Self::Trunc_U32_F32,
Instruction::I32TruncSF64 => Self::Trunc_I32_F64,
Instruction::I32TruncUF64 => Self::Trunc_U32_F64,
Instruction::I64ExtendSI32 => Self::Extend_I64_I32,
Instruction::I64ExtendUI32 => Self::Extend_U64_I32,
Instruction::I64TruncSF32 => Self::Trunc_I64_F32,
Instruction::I64TruncUF32 => Self::Trunc_U64_F32,
Instruction::I64TruncSF64 => Self::Trunc_I64_F64,
Instruction::I64TruncUF64 => Self::Trunc_U64_F64,
Instruction::F32ConvertSI32 => Self::Convert_F32_I32,
Instruction::F32ConvertUI32 => Self::Convert_F32_U32,
Instruction::F32ConvertSI64 => Self::Convert_F32_I64,
Instruction::F32ConvertUI64 => Self::Convert_F32_U64,
Instruction::F32DemoteF64 => Self::Demote_F32_F64,
Instruction::F64ConvertSI32 => Self::Convert_F64_I32,
Instruction::F64ConvertUI32 => Self::Convert_F64_U32,
Instruction::F64ConvertSI64 => Self::Convert_F64_I64,
Instruction::F64ConvertUI64 => Self::Convert_F64_U64,
Instruction::F64PromoteF32 => Self::Promote_F64_F32,
Instruction::I32ReinterpretF32 => Self::Reinterpret_I32_F32,
Instruction::I64ReinterpretF64 => Self::Reinterpret_I64_F64,
Instruction::F32ReinterpretI32 => Self::Reinterpret_F32_I32,
Instruction::F64ReinterpretI64 => Self::Reinterpret_F64_I64,
_ => return Err(()),
};
Ok(result)
}
}
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
pub enum BinOp {
Eq_I32,
Ne_I32,
LtS_I32,
LtU_I32,
GtS_I32,
GtU_I32,
LeS_I32,
LeU_I32,
GeS_I32,
GeU_I32,
Eq_I64,
Ne_I64,
LtS_I64,
LtU_I64,
GtS_I64,
GtU_I64,
LeS_I64,
LeU_I64,
GeS_I64,
GeU_I64,
Add_I32,
Sub_I32,
Mul_I32,
DivS_I32,
DivU_I32,
RemS_I32,
RemU_I32,
And_I32,
Or_I32,
Xor_I32,
Shl_I32,
ShrS_I32,
ShrU_I32,
Rotl_I32,
Rotr_I32,
Add_I64,
Sub_I64,
Mul_I64,
DivS_I64,
DivU_I64,
RemS_I64,
RemU_I64,
And_I64,
Or_I64,
Xor_I64,
Shl_I64,
ShrS_I64,
ShrU_I64,
Rotl_I64,
Rotr_I64,
Eq_FN,
Ne_FN,
Lt_FN,
Gt_FN,
Le_FN,
Ge_FN,
Add_FN,
Sub_FN,
Mul_FN,
Div_FN,
Min_FN,
Max_FN,
Copysign_FN,
}
impl BinOp {
pub fn as_operator(self) -> Option<&'static str> {
let op = match self {
Self::Add_FN => "+",
Self::Sub_FN => "-",
Self::Mul_FN => "*",
Self::Div_FN => "/",
Self::RemS_I32 | Self::RemU_I32 | Self::RemS_I64 | Self::RemU_I64 => "%",
_ => return None,
};
Some(op)
}
pub fn as_name(self) -> (&'static str, &'static str) {
match self {
Self::Eq_I32 => ("eq", "i32"),
Self::Ne_I32 => ("ne", "i32"),
Self::LtS_I32 => ("lt", "i32"),
Self::LtU_I32 => ("lt", "u32"),
Self::GtS_I32 => ("gt", "i32"),
Self::GtU_I32 => ("gt", "u32"),
Self::LeS_I32 => ("le", "i32"),
Self::LeU_I32 => ("le", "u32"),
Self::GeS_I32 => ("ge", "i32"),
Self::GeU_I32 => ("ge", "u32"),
Self::Eq_I64 => ("eq", "i64"),
Self::Ne_I64 => ("ne", "i64"),
Self::LtS_I64 => ("lt", "i64"),
Self::LtU_I64 => ("lt", "u64"),
Self::GtS_I64 => ("gt", "i64"),
Self::GtU_I64 => ("gt", "u64"),
Self::LeS_I64 => ("le", "i64"),
Self::LeU_I64 => ("le", "u64"),
Self::GeS_I64 => ("ge", "i64"),
Self::GeU_I64 => ("ge", "u64"),
Self::Add_I32 => ("add", "i32"),
Self::Sub_I32 => ("sub", "i32"),
Self::Mul_I32 => ("mul", "i32"),
Self::DivS_I32 => ("div", "i32"),
Self::DivU_I32 => ("div", "u32"),
Self::RemS_I32 => ("rem", "i32"),
Self::RemU_I32 => ("rem", "u32"),
Self::And_I32 => ("band", "i32"),
Self::Or_I32 => ("bor", "i32"),
Self::Xor_I32 => ("bxor", "i32"),
Self::Shl_I32 => ("shl", "i32"),
Self::ShrS_I32 => ("shr", "i32"),
Self::ShrU_I32 => ("shr", "u32"),
Self::Rotl_I32 => ("rotl", "i32"),
Self::Rotr_I32 => ("rotr", "i32"),
Self::Add_I64 => ("add", "i64"),
Self::Sub_I64 => ("sub", "i64"),
Self::Mul_I64 => ("mul", "i64"),
Self::DivS_I64 => ("div", "i64"),
Self::DivU_I64 => ("div", "u64"),
Self::RemS_I64 => ("rem", "i64"),
Self::RemU_I64 => ("rem", "u64"),
Self::And_I64 => ("band", "i64"),
Self::Or_I64 => ("bor", "i64"),
Self::Xor_I64 => ("bxor", "i64"),
Self::Shl_I64 => ("shl", "i64"),
Self::ShrS_I64 => ("shr", "i64"),
Self::ShrU_I64 => ("shr", "u64"),
Self::Rotl_I64 => ("rotl", "i64"),
Self::Rotr_I64 => ("rotr", "i64"),
Self::Eq_FN => ("eq", "num"),
Self::Ne_FN => ("ne", "num"),
Self::Lt_FN => ("lt", "num"),
Self::Gt_FN => ("gt", "num"),
Self::Le_FN => ("le", "num"),
Self::Ge_FN => ("ge", "num"),
Self::Add_FN => ("add", "num"),
Self::Sub_FN => ("sub", "num"),
Self::Mul_FN => ("mul", "num"),
Self::Div_FN => ("div", "num"),
Self::Min_FN => ("math", "min"),
Self::Max_FN => ("math", "max"),
Self::Copysign_FN => ("copysign", "num"),
}
}
}
impl TryFrom<&Instruction> for BinOp {
type Error = ();
fn try_from(inst: &Instruction) -> Result<Self, Self::Error> {
let result = match inst {
Instruction::I32Eq => Self::Eq_I32,
Instruction::I32Ne => Self::Ne_I32,
Instruction::I32LtS => Self::LtS_I32,
Instruction::I32LtU => Self::LtU_I32,
Instruction::I32GtS => Self::GtS_I32,
Instruction::I32GtU => Self::GtU_I32,
Instruction::I32LeS => Self::LeS_I32,
Instruction::I32LeU => Self::LeU_I32,
Instruction::I32GeS => Self::GeS_I32,
Instruction::I32GeU => Self::GeU_I32,
Instruction::I64Eq => Self::Eq_I64,
Instruction::I64Ne => Self::Ne_I64,
Instruction::I64LtS => Self::LtS_I64,
Instruction::I64LtU => Self::LtU_I64,
Instruction::I64GtS => Self::GtS_I64,
Instruction::I64GtU => Self::GtU_I64,
Instruction::I64LeS => Self::LeS_I64,
Instruction::I64LeU => Self::LeU_I64,
Instruction::I64GeS => Self::GeS_I64,
Instruction::I64GeU => Self::GeU_I64,
Instruction::I32Add => Self::Add_I32,
Instruction::I32Sub => Self::Sub_I32,
Instruction::I32Mul => Self::Mul_I32,
Instruction::I32DivS => Self::DivS_I32,
Instruction::I32DivU => Self::DivU_I32,
Instruction::I32RemS => Self::RemS_I32,
Instruction::I32RemU => Self::RemU_I32,
Instruction::I32And => Self::And_I32,
Instruction::I32Or => Self::Or_I32,
Instruction::I32Xor => Self::Xor_I32,
Instruction::I32Shl => Self::Shl_I32,
Instruction::I32ShrS => Self::ShrS_I32,
Instruction::I32ShrU => Self::ShrU_I32,
Instruction::I32Rotl => Self::Rotl_I32,
Instruction::I32Rotr => Self::Rotr_I32,
Instruction::I64Add => Self::Add_I64,
Instruction::I64Sub => Self::Sub_I64,
Instruction::I64Mul => Self::Mul_I64,
Instruction::I64DivS => Self::DivS_I64,
Instruction::I64DivU => Self::DivU_I64,
Instruction::I64RemS => Self::RemS_I64,
Instruction::I64RemU => Self::RemU_I64,
Instruction::I64And => Self::And_I64,
Instruction::I64Or => Self::Or_I64,
Instruction::I64Xor => Self::Xor_I64,
Instruction::I64Shl => Self::Shl_I64,
Instruction::I64ShrS => Self::ShrS_I64,
Instruction::I64ShrU => Self::ShrU_I64,
Instruction::I64Rotl => Self::Rotl_I64,
Instruction::I64Rotr => Self::Rotr_I64,
Instruction::F32Eq | Instruction::F64Eq => Self::Eq_FN,
Instruction::F32Ne | Instruction::F64Ne => Self::Ne_FN,
Instruction::F32Lt | Instruction::F64Lt => Self::Lt_FN,
Instruction::F32Gt | Instruction::F64Gt => Self::Gt_FN,
Instruction::F32Le | Instruction::F64Le => Self::Le_FN,
Instruction::F32Ge | Instruction::F64Ge => Self::Ge_FN,
Instruction::F32Add | Instruction::F64Add => Self::Add_FN,
Instruction::F32Sub | Instruction::F64Sub => Self::Sub_FN,
Instruction::F32Mul | Instruction::F64Mul => Self::Mul_FN,
Instruction::F32Div | Instruction::F64Div => Self::Div_FN,
Instruction::F32Min | Instruction::F64Min => Self::Min_FN,
Instruction::F32Max | Instruction::F64Max => Self::Max_FN,
Instruction::F32Copysign | Instruction::F64Copysign => Self::Copysign_FN,
_ => {
return Err(());
}
};
Ok(result)
}
}
+414
View File
@@ -0,0 +1,414 @@
use parity_wasm::elements::{BlockType, Instruction, Local, Module};
use crate::backend::translator::arity::{Arity, List as ArityList};
use super::{
data::Memorize,
operation::{BinOp, UnOp},
{
data::{
AnyBinOp, AnyLoad, AnyStore, AnyUnOp, Backward, Br, BrIf, BrTable, Call, CallIndirect,
Expression, Forward, Function, GetGlobal, GetLocal, If, MemoryGrow, MemorySize, Return,
Select, SetGlobal, SetLocal, Statement, Value,
},
operation::{Load, Store},
},
};
pub struct Transformer<'a> {
// target state
wasm: &'a Module,
arity: &'a ArityList,
name: usize,
// translation state
pending: Vec<Vec<Expression>>,
stack: Vec<Expression>,
last_stack: usize,
}
fn local_sum(list: &[Local]) -> u32 {
list.iter().map(Local::count).sum()
}
fn is_else_stat(inst: &Instruction) -> bool {
inst == &Instruction::Else
}
impl<'a> Transformer<'a> {
pub fn new(wasm: &'a Module, arity: &'a ArityList, name: usize) -> Transformer<'a> {
Transformer {
wasm,
arity,
name,
pending: Vec::new(),
stack: Vec::new(),
last_stack: 0,
}
}
pub fn consume(mut self) -> Function {
debug_assert!(self.name != usize::MAX, "Not an indexed value");
let func = &self.wasm.code_section().unwrap().bodies()[self.name];
let body = self.new_forward(&mut func.code().elements());
Function {
num_param: self.arity.in_arity[self.name].num_param,
num_local: local_sum(func.locals()),
num_stack: u32::try_from(self.last_stack).unwrap(),
body,
}
}
fn push_recall(&mut self, num: u32) {
let len = self.stack.len();
(len..len + num as usize)
.map(Expression::Recall)
.for_each(|v| self.stack.push(v));
}
fn push_block_result(&mut self, typ: BlockType) {
if matches!(typ, BlockType::NoResult) {
return;
}
self.push_recall(1);
}
// If any expressions are still pending at the start of
// statement, we leak them into variables.
// Since expressions do not have set ordering rules, this is
// safe and condenses code.
fn gen_leak_pending(&mut self, stat: &mut Vec<Statement>) {
self.last_stack = self.last_stack.max(self.stack.len());
for (i, v) in self
.stack
.iter_mut()
.enumerate()
.filter(|v| !v.1.is_recalling(v.0))
{
let new = Expression::Recall(i);
let mem = Memorize {
var: i,
value: std::mem::replace(v, new),
};
stat.push(Statement::Memorize(mem));
}
}
// Pending expressions are put to sleep before entering
// a control structure so that they are not lost.
fn save_pending(&mut self) {
self.pending.push(self.stack.clone());
}
fn load_pending(&mut self) {
self.stack = self.pending.pop().unwrap();
}
fn gen_return(&mut self, stat: &mut Vec<Statement>) {
let num = self.arity.in_arity[self.name].num_result as usize;
let list = self.stack.split_off(self.stack.len() - num);
self.gen_leak_pending(stat);
stat.push(Statement::Return(Return { list }));
}
fn gen_call(&mut self, func: u32, stat: &mut Vec<Statement>) {
let arity = self.arity.arity_of(func as usize);
let param_list = self
.stack
.split_off(self.stack.len() - arity.num_param as usize);
let len = u32::try_from(self.stack.len()).unwrap();
let result = len..len + arity.num_result;
self.push_recall(arity.num_result);
self.gen_leak_pending(stat);
stat.push(Statement::Call(Call {
func,
result,
param_list,
}));
}
fn gen_call_indirect(&mut self, typ: u32, table: u8, stat: &mut Vec<Statement>) {
let types = self.wasm.type_section().unwrap().types();
let arity = Arity::from_index(types, typ);
let index = self.stack.pop().unwrap();
let param_list = self
.stack
.split_off(self.stack.len() - arity.num_param as usize);
let len = u32::try_from(self.stack.len()).unwrap();
let result = len..len + arity.num_result;
self.push_recall(arity.num_result);
self.gen_leak_pending(stat);
stat.push(Statement::CallIndirect(CallIndirect {
table,
index,
result,
param_list,
}));
}
fn push_load(&mut self, op: Load, offset: u32) {
let pointer = Box::new(self.stack.pop().unwrap());
self.stack.push(Expression::AnyLoad(AnyLoad {
op,
offset,
pointer,
}));
}
fn gen_store(&mut self, op: Store, offset: u32, stat: &mut Vec<Statement>) {
let value = self.stack.pop().unwrap();
let pointer = self.stack.pop().unwrap();
self.gen_leak_pending(stat);
stat.push(Statement::AnyStore(AnyStore {
op,
offset,
pointer,
value,
}));
}
fn push_constant(&mut self, value: Value) {
self.stack.push(Expression::Value(value));
}
fn push_un_op(&mut self, op: UnOp) {
let rhs = Box::new(self.stack.pop().unwrap());
self.stack.push(Expression::AnyUnOp(AnyUnOp { op, rhs }));
}
fn push_bin_op(&mut self, op: BinOp) {
let rhs = Box::new(self.stack.pop().unwrap());
let lhs = Box::new(self.stack.pop().unwrap());
self.stack
.push(Expression::AnyBinOp(AnyBinOp { op, lhs, rhs }));
}
fn new_body(&mut self, list: &mut &[Instruction]) -> Vec<Statement> {
use Instruction as Inst;
let mut stat = Vec::new();
loop {
let inst = &list[0];
*list = &list[1..];
if let Ok(op) = UnOp::try_from(inst) {
self.push_un_op(op);
continue;
} else if let Ok(op) = BinOp::try_from(inst) {
self.push_bin_op(op);
continue;
}
match inst {
Inst::Nop => {}
Inst::Unreachable => {
self.gen_leak_pending(&mut stat);
stat.push(Statement::Unreachable);
}
Inst::Block(t) => {
self.gen_leak_pending(&mut stat);
let data = self.new_forward(list);
self.push_block_result(*t);
stat.push(Statement::Forward(data));
}
Inst::Loop(t) => {
self.gen_leak_pending(&mut stat);
let data = self.new_backward(list);
self.push_block_result(*t);
stat.push(Statement::Backward(data));
}
Inst::If(t) => {
let cond = self.stack.pop().unwrap();
self.gen_leak_pending(&mut stat);
let data = self.new_if(cond, list);
self.push_block_result(*t);
stat.push(Statement::If(data));
}
Inst::Else => {
self.gen_leak_pending(&mut stat);
break;
}
Inst::End => {
if list.is_empty() && !self.stack.is_empty() {
self.gen_return(&mut stat);
} else {
self.gen_leak_pending(&mut stat);
}
break;
}
Inst::Br(i) => {
self.gen_leak_pending(&mut stat);
stat.push(Statement::Br(Br { target: *i }));
}
Inst::BrIf(i) => {
let cond = self.stack.pop().unwrap();
self.gen_leak_pending(&mut stat);
stat.push(Statement::BrIf(BrIf { cond, target: *i }));
}
Inst::BrTable(t) => {
let cond = self.stack.pop().unwrap();
self.gen_leak_pending(&mut stat);
stat.push(Statement::BrTable(BrTable {
cond,
data: *t.clone(),
}));
}
Inst::Return => {
self.gen_return(&mut stat);
}
Inst::Call(i) => {
self.gen_call(*i, &mut stat);
}
Inst::CallIndirect(i, t) => {
self.gen_call_indirect(*i, *t, &mut stat);
}
Inst::Drop => {
self.stack.pop().unwrap();
}
Inst::Select => {
let cond = Box::new(self.stack.pop().unwrap());
let b = Box::new(self.stack.pop().unwrap());
let a = Box::new(self.stack.pop().unwrap());
self.stack.push(Expression::Select(Select { cond, a, b }));
}
Inst::GetLocal(i) => {
self.stack.push(Expression::GetLocal(GetLocal { var: *i }));
}
Inst::SetLocal(i) => {
let value = self.stack.pop().unwrap();
self.gen_leak_pending(&mut stat);
stat.push(Statement::SetLocal(SetLocal { var: *i, value }));
}
Inst::TeeLocal(i) => {
self.gen_leak_pending(&mut stat);
let value = self.stack.last().unwrap().clone();
stat.push(Statement::SetLocal(SetLocal { var: *i, value }));
}
Inst::GetGlobal(i) => {
self.stack
.push(Expression::GetGlobal(GetGlobal { var: *i }));
}
Inst::SetGlobal(i) => {
let value = self.stack.pop().unwrap();
stat.push(Statement::SetGlobal(SetGlobal { var: *i, value }));
}
Inst::I32Load(_, o) => self.push_load(Load::I32, *o),
Inst::I64Load(_, o) => self.push_load(Load::I64, *o),
Inst::F32Load(_, o) => self.push_load(Load::F32, *o),
Inst::F64Load(_, o) => self.push_load(Load::F64, *o),
Inst::I32Load8S(_, o) => self.push_load(Load::I32_I8, *o),
Inst::I32Load8U(_, o) => self.push_load(Load::I32_U8, *o),
Inst::I32Load16S(_, o) => self.push_load(Load::I32_I16, *o),
Inst::I32Load16U(_, o) => self.push_load(Load::I32_U16, *o),
Inst::I64Load8S(_, o) => self.push_load(Load::I64_I8, *o),
Inst::I64Load8U(_, o) => self.push_load(Load::I64_U8, *o),
Inst::I64Load16S(_, o) => self.push_load(Load::I64_I16, *o),
Inst::I64Load16U(_, o) => self.push_load(Load::I64_U16, *o),
Inst::I64Load32S(_, o) => self.push_load(Load::I64_I32, *o),
Inst::I64Load32U(_, o) => self.push_load(Load::I64_U32, *o),
Inst::I32Store(_, o) => self.gen_store(Store::I32, *o, &mut stat),
Inst::I64Store(_, o) => self.gen_store(Store::I64, *o, &mut stat),
Inst::F32Store(_, o) => self.gen_store(Store::F32, *o, &mut stat),
Inst::F64Store(_, o) => self.gen_store(Store::F64, *o, &mut stat),
Inst::I32Store8(_, o) => self.gen_store(Store::I32_N8, *o, &mut stat),
Inst::I32Store16(_, o) => self.gen_store(Store::I32_N16, *o, &mut stat),
Inst::I64Store8(_, o) => self.gen_store(Store::I64_N8, *o, &mut stat),
Inst::I64Store16(_, o) => self.gen_store(Store::I64_N16, *o, &mut stat),
Inst::I64Store32(_, o) => self.gen_store(Store::I64_N32, *o, &mut stat),
Inst::CurrentMemory(i) => {
self.stack
.push(Expression::MemorySize(MemorySize { memory: *i }));
}
Inst::GrowMemory(i) => {
let value = Box::new(self.stack.pop().unwrap());
// `MemoryGrow` is an expression *but* it has side effects
self.stack
.push(Expression::MemoryGrow(MemoryGrow { memory: *i, value }));
self.gen_leak_pending(&mut stat);
}
Inst::I32Const(v) => self.push_constant(Value::I32(*v)),
Inst::I64Const(v) => self.push_constant(Value::I64(*v)),
Inst::F32Const(v) => self.push_constant(Value::F32(f32::from_bits(*v))),
Inst::F64Const(v) => self.push_constant(Value::F64(f64::from_bits(*v))),
_ => unreachable!(),
}
}
stat
}
fn new_stored_body(&mut self, list: &mut &[Instruction]) -> Vec<Statement> {
self.save_pending();
let body = self.new_body(list);
self.load_pending();
body
}
fn new_if(&mut self, cond: Expression, list: &mut &[Instruction]) -> If {
let copied = <&[Instruction]>::clone(list);
let truthy = self.new_stored_body(list);
let end = copied.len() - list.len() - 1;
let falsey = is_else_stat(&copied[end]).then(|| self.new_stored_body(list));
If {
cond,
truthy,
falsey,
}
}
fn new_backward(&mut self, list: &mut &[Instruction]) -> Backward {
Backward {
body: self.new_stored_body(list),
}
}
fn new_forward(&mut self, list: &mut &[Instruction]) -> Forward {
Forward {
body: self.new_stored_body(list),
}
}
}
+51
View File
@@ -0,0 +1,51 @@
use std::{
fmt::Display,
io::{Result, Write},
};
use super::{luajit::LuaJIT, luau::Luau};
pub struct Infix<T> {
rhs: &'static str,
inner: T,
}
impl<T> Infix<T> {
pub fn new(rhs: &'static str, inner: T) -> Self {
Infix { rhs, inner }
}
}
impl<T> Display for Infix<T>
where
T: Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)?;
self.rhs.fmt(f)
}
}
pub trait Edition {
fn runtime(&self) -> &'static str;
fn start_block(&self, w: &mut dyn Write) -> Result<()>;
fn start_loop(&self, level: usize, w: &mut dyn Write) -> Result<()>;
fn start_if(&self, cond: &str, w: &mut dyn Write) -> Result<()>;
fn end_block(&self, level: usize, w: &mut dyn Write) -> Result<()>;
fn end_loop(&self, w: &mut dyn Write) -> Result<()>;
fn end_if(&self, level: usize, w: &mut dyn Write) -> Result<()>;
fn br_target(&self, level: usize, in_loop: bool, w: &mut dyn Write) -> Result<()>;
fn br_to_level(&self, level: usize, up: usize, is_loop: bool, w: &mut dyn Write) -> Result<()>;
fn i64(&self, i: i64) -> Infix<i64>;
}
pub fn from_string(name: &str) -> Option<&'static dyn Edition> {
match name.to_ascii_lowercase().as_str() {
"luau" => Some(&Luau),
"luajit" => Some(&LuaJIT),
_ => None,
}
}
+56
View File
@@ -0,0 +1,56 @@
use std::io::{Result, Write};
use super::data::{Edition, Infix};
pub struct LuaJIT;
impl Edition for LuaJIT {
fn runtime(&self) -> &'static str {
"'luajit'"
}
fn start_block(&self, w: &mut dyn Write) -> Result<()> {
write!(w, "do ")
}
fn start_loop(&self, level: usize, w: &mut dyn Write) -> Result<()> {
write!(w, "do ")?;
write!(w, "::continue_at_{}::", level)
}
fn start_if(&self, cond: &str, w: &mut dyn Write) -> Result<()> {
write!(w, "if {} ~= 0 then ", cond)
}
fn end_block(&self, level: usize, w: &mut dyn Write) -> Result<()> {
write!(w, "::continue_at_{}::", level)?;
write!(w, "end ")
}
fn end_loop(&self, w: &mut dyn Write) -> Result<()> {
write!(w, "end ")
}
fn end_if(&self, level: usize, w: &mut dyn Write) -> Result<()> {
write!(w, "::continue_at_{}::", level)?;
write!(w, "end ")
}
fn br_target(&self, _level: usize, _in_loop: bool, _w: &mut dyn Write) -> Result<()> {
Ok(())
}
fn br_to_level(
&self,
level: usize,
up: usize,
_is_loop: bool,
w: &mut dyn Write,
) -> Result<()> {
write!(w, "goto continue_at_{} ", level - up)
}
fn i64(&self, i: i64) -> Infix<i64> {
Infix::new("LL", i)
}
}
+75
View File
@@ -0,0 +1,75 @@
use std::io::{Result, Write};
use super::data::{Edition, Infix};
pub struct Luau;
impl Edition for Luau {
fn runtime(&self) -> &'static str {
"script.Runtime"
}
fn start_block(&self, w: &mut dyn Write) -> Result<()> {
write!(w, "while true do ")
}
fn start_loop(&self, _level: usize, w: &mut dyn Write) -> Result<()> {
write!(w, "while true do ")
}
fn start_if(&self, cond: &str, w: &mut dyn Write) -> Result<()> {
write!(w, "while true do ")?;
write!(w, "if {} ~= 0 then ", cond)
}
fn end_block(&self, _level: usize, w: &mut dyn Write) -> Result<()> {
write!(w, "break ")?;
write!(w, "end ")
}
fn end_loop(&self, w: &mut dyn Write) -> Result<()> {
write!(w, "break ")?;
write!(w, "end ")
}
fn end_if(&self, _level: usize, w: &mut dyn Write) -> Result<()> {
write!(w, "end ")?;
write!(w, "break ")?;
write!(w, "end ")
}
fn br_target(&self, level: usize, in_loop: bool, w: &mut dyn Write) -> Result<()> {
write!(w, "if desired then ")?;
write!(w, "if desired == {} then ", level)?;
write!(w, "desired = nil ")?;
if in_loop {
write!(w, "continue ")?;
}
write!(w, "end ")?;
write!(w, "break ")?;
write!(w, "end ")
}
fn br_to_level(&self, level: usize, up: usize, is_loop: bool, w: &mut dyn Write) -> Result<()> {
write!(w, "do ")?;
if up == 0 {
if is_loop {
write!(w, "continue ")?;
} else {
write!(w, "break ")?;
}
} else {
write!(w, "desired = {} ", level - up)?;
write!(w, "break ")?;
}
write!(w, "end ")
}
fn i64(&self, i: i64) -> Infix<i64> {
Infix::new("", i)
}
}
+3
View File
@@ -0,0 +1,3 @@
pub mod data;
pub mod luajit;
pub mod luau;
+4
View File
@@ -0,0 +1,4 @@
mod ast;
pub mod edition;
pub mod translator;
mod visitor;
+81
View File
@@ -0,0 +1,81 @@
use std::convert::TryInto;
use parity_wasm::elements::{External, FunctionType, ImportEntry, Module, Type};
pub struct Arity {
pub num_param: u32,
pub num_result: u32,
}
impl Arity {
fn from_type(typ: &FunctionType) -> Self {
let num_param = typ.params().len().try_into().unwrap();
let num_result = typ.results().len().try_into().unwrap();
Self {
num_param,
num_result,
}
}
pub fn from_index(types: &[Type], index: u32) -> Self {
let Type::Function(typ) = &types[index as usize];
Self::from_type(typ)
}
}
pub struct List {
pub ex_arity: Vec<Arity>,
pub in_arity: Vec<Arity>,
}
impl List {
pub fn new(parent: &Module) -> Self {
Self {
ex_arity: Self::new_arity_ex_list(parent),
in_arity: Self::new_arity_in_list(parent),
}
}
pub fn arity_of(&self, index: usize) -> &Arity {
let offset = self.ex_arity.len();
self.ex_arity
.get(index)
.or_else(|| self.in_arity.get(index - offset))
.unwrap()
}
fn new_arity_ext(types: &[Type], import: &ImportEntry) -> Option<Arity> {
if let External::Function(i) = import.external() {
Some(Arity::from_index(types, *i))
} else {
None
}
}
fn new_arity_in_list(wasm: &Module) -> Vec<Arity> {
let (types, funcs) = match (wasm.type_section(), wasm.function_section()) {
(Some(t), Some(f)) => (t.types(), f.entries()),
_ => return Vec::new(),
};
funcs
.iter()
.map(|i| Arity::from_index(types, i.type_ref()))
.collect()
}
fn new_arity_ex_list(wasm: &Module) -> Vec<Arity> {
let (types, imports) = match (wasm.type_section(), wasm.import_section()) {
(Some(t), Some(i)) => (t.types(), i.entries()),
_ => return Vec::new(),
};
imports
.iter()
.filter_map(|i| Self::new_arity_ext(types, i))
.collect()
}
}
+306
View File
@@ -0,0 +1,306 @@
use std::{
collections::BTreeSet,
io::{Result, Write},
};
use parity_wasm::elements::{
External, ImportCountType, Instruction, Internal, Module as WasmModule, ResizableLimits,
};
use crate::backend::{
ast::{data::Function, transformer::Transformer},
edition::data::Edition,
visitor::localize,
};
use super::{arity::List as ArityList, writer::Data};
fn aux_internal_index(internal: Internal) -> u32 {
match internal {
Internal::Function(v) | Internal::Table(v) | Internal::Memory(v) | Internal::Global(v) => v,
}
}
fn new_limit_max(limits: &ResizableLimits) -> String {
match limits.maximum() {
Some(v) => v.to_string(),
None => "math.huge".to_string(),
}
}
fn gen_table_init(limit: &ResizableLimits, w: &mut dyn Write) -> Result<()> {
let a = limit.initial();
let b = new_limit_max(limit);
write!(w, "{{ min = {}, max = {}, data = {{}} }}", a, b)
}
fn gen_memory_init(limit: &ResizableLimits, w: &mut dyn Write) -> Result<()> {
let a = limit.initial();
let b = new_limit_max(limit);
write!(w, "rt.memory.new({}, {})", a, b)
}
fn gen_nil_array(name: &str, len: usize, w: &mut dyn Write) -> Result<()> {
if len == 0 {
return Ok(());
}
write!(w, "local {} = {{[0] = {}}}", name, "nil, ".repeat(len))
}
pub fn gen_expression(code: &[Instruction], w: &mut dyn Write) -> Result<()> {
assert!(code.len() == 2);
let inst = code.first().unwrap();
match *inst {
Instruction::I32Const(v) => write!(w, "{} ", v),
Instruction::I64Const(v) => write!(w, "{} ", v),
Instruction::F32Const(v) => write!(w, "{} ", f32::from_bits(v)),
Instruction::F64Const(v) => write!(w, "{} ", f64::from_bits(v)),
Instruction::GetGlobal(i) => write!(w, "GLOBAL_LIST[{}].value ", i),
_ => unreachable!(),
}
}
pub struct Module<'a> {
wasm: &'a WasmModule,
arity: ArityList,
}
impl<'a> Module<'a> {
pub fn new(wasm: &'a WasmModule) -> Self {
let arity = ArityList::new(wasm);
Self { wasm, arity }
}
fn gen_import_of<T>(&self, w: &mut dyn Write, lower: &str, cond: T) -> Result<()>
where
T: Fn(&External) -> bool,
{
let import = match self.wasm.import_section() {
Some(v) => v.entries(),
None => return Ok(()),
};
let upper = lower.to_uppercase();
for (i, v) in import.iter().filter(|v| cond(v.external())).enumerate() {
let field = v.field();
let module = v.module();
write!(w, "{}[{}] = wasm.{}.{}.{} ", upper, i, module, lower, field)?;
}
Ok(())
}
fn gen_export_of<T>(&self, w: &mut dyn Write, lower: &str, cond: T) -> Result<()>
where
T: Fn(&Internal) -> bool,
{
let export = match self.wasm.export_section() {
Some(v) => v.entries(),
None => return Ok(()),
};
let upper = lower.to_uppercase();
write!(w, "{} = {{", lower)?;
for v in export.iter().filter(|v| cond(v.internal())) {
let field = v.field();
let index = aux_internal_index(*v.internal());
write!(w, "{} = {}[{}],", field, upper, index)?;
}
write!(w, "}},")
}
fn gen_import_list(&self, w: &mut dyn Write) -> Result<()> {
self.gen_import_of(w, "func_list", |v| matches!(v, External::Function(_)))?;
self.gen_import_of(w, "table_list", |v| matches!(v, External::Table(_)))?;
self.gen_import_of(w, "memory_list", |v| matches!(v, External::Memory(_)))?;
self.gen_import_of(w, "global_list", |v| matches!(v, External::Global(_)))
}
fn gen_export_list(&self, w: &mut dyn Write) -> Result<()> {
self.gen_export_of(w, "func_list", |v| matches!(v, Internal::Function(_)))?;
self.gen_export_of(w, "table_list", |v| matches!(v, Internal::Table(_)))?;
self.gen_export_of(w, "memory_list", |v| matches!(v, Internal::Memory(_)))?;
self.gen_export_of(w, "global_list", |v| matches!(v, Internal::Global(_)))
}
fn gen_table_list(&self, w: &mut dyn Write) -> Result<()> {
let table = match self.wasm.table_section() {
Some(v) => v.entries(),
None => return Ok(()),
};
let offset = self.wasm.import_count(ImportCountType::Table);
for (i, v) in table.iter().enumerate() {
let index = i + offset;
write!(w, "TABLE_LIST[{}] =", index)?;
gen_table_init(v.limits(), w)?;
}
Ok(())
}
fn gen_memory_list(&self, w: &mut dyn Write) -> Result<()> {
let memory = match self.wasm.memory_section() {
Some(v) => v.entries(),
None => return Ok(()),
};
let offset = self.wasm.import_count(ImportCountType::Memory);
for (i, v) in memory.iter().enumerate() {
let index = i + offset;
write!(w, "MEMORY_LIST[{}] =", index)?;
gen_memory_init(v.limits(), w)?;
}
Ok(())
}
fn gen_global_list(&self, w: &mut dyn Write) -> Result<()> {
let global = match self.wasm.global_section() {
Some(v) => v,
None => return Ok(()),
};
let offset = self.wasm.import_count(ImportCountType::Global);
for (i, v) in global.entries().iter().enumerate() {
let index = i + offset;
write!(w, "GLOBAL_LIST[{}] = {{ value =", index)?;
gen_expression(v.init_expr().code(), w)?;
write!(w, "}}")?;
}
Ok(())
}
fn gen_element_list(&self, w: &mut dyn Write) -> Result<()> {
let element = match self.wasm.elements_section() {
Some(v) => v.entries(),
None => return Ok(()),
};
for v in element {
write!(w, "do ")?;
write!(w, "local target = TABLE_LIST[{}].data ", v.index())?;
write!(w, "local offset =")?;
gen_expression(v.offset().as_ref().unwrap().code(), w)?;
write!(w, "local data = {{")?;
v.members()
.iter()
.try_for_each(|v| write!(w, "FUNC_LIST[{}],", v))?;
write!(w, "}}")?;
write!(w, "table.move(data, 1, #data, offset, target)")?;
write!(w, "end ")?;
}
Ok(())
}
fn gen_data_list(&self, w: &mut dyn Write) -> Result<()> {
let data = match self.wasm.data_section() {
Some(v) => v.entries(),
None => return Ok(()),
};
for v in data {
write!(w, "do ")?;
write!(w, "local target = MEMORY_LIST[{}]", v.index())?;
write!(w, "local offset =")?;
gen_expression(v.offset().as_ref().unwrap().code(), w)?;
write!(w, "local data = \"")?;
v.value()
.iter()
.try_for_each(|v| write!(w, "\\x{:02X}", v))?;
write!(w, "\"")?;
write!(w, "rt.memory.init(target, offset, data)")?;
write!(w, "end ")?;
}
Ok(())
}
fn gen_start_point(&self, w: &mut dyn Write) -> Result<()> {
write!(w, "local function run_init_code()")?;
self.gen_table_list(w)?;
self.gen_memory_list(w)?;
self.gen_global_list(w)?;
self.gen_element_list(w)?;
self.gen_data_list(w)?;
write!(w, "end ")?;
write!(w, "return function(wasm)")?;
self.gen_import_list(w)?;
write!(w, "run_init_code()")?;
if let Some(start) = self.wasm.start_section() {
write!(w, "FUNC_LIST[{}]()", start)?;
}
write!(w, "return {{")?;
self.gen_export_list(w)?;
write!(w, "}} end ")
}
fn gen_localize(func_list: &[Function], w: &mut dyn Write) -> Result<()> {
let mut loc_set = BTreeSet::new();
for func in func_list {
loc_set.extend(localize::visit(func));
}
loc_set
.into_iter()
.try_for_each(|(a, b)| write!(w, "local {0}_{1} = rt.{0}.{1} ", a, b))
}
pub fn translate(&self, ed: &dyn Edition, w: &mut dyn Write) -> Result<()> {
write!(w, "local rt = require({})", ed.runtime())?;
let func_list: Vec<_> = (0..self.arity.in_arity.len())
.map(|i| Transformer::new(self.wasm, &self.arity, i).consume())
.collect();
Self::gen_localize(&func_list, w)?;
gen_nil_array("FUNC_LIST", self.wasm.functions_space(), w)?;
gen_nil_array("TABLE_LIST", self.wasm.table_space(), w)?;
gen_nil_array("MEMORY_LIST", self.wasm.memory_space(), w)?;
gen_nil_array("GLOBAL_LIST", self.wasm.globals_space(), w)?;
let offset = self.arity.ex_arity.len();
for (i, v) in func_list.into_iter().enumerate() {
write!(w, "FUNC_LIST[{}] =", i + offset)?;
v.output(&mut Data::new(v.num_param, ed), w)?;
}
self.gen_start_point(w)
}
}
+3
View File
@@ -0,0 +1,3 @@
pub mod arity;
pub mod data;
mod writer;
+503
View File
@@ -0,0 +1,503 @@
use std::{
io::{Result, Write},
ops::Range,
};
use crate::backend::{
ast::data::{
AnyBinOp, AnyLoad, AnyStore, AnyUnOp, Backward, Br, BrIf, BrTable, Call, CallIndirect,
Expression, Forward, Function, GetGlobal, GetLocal, If, Memorize, MemoryGrow, MemorySize,
Return, Select, SetGlobal, SetLocal, Statement, Value,
},
edition::data::Edition,
visitor::memory,
};
fn write_in_order(prefix: &'static str, len: u32, w: &mut dyn Write) -> Result<()> {
if len == 0 {
return Ok(());
}
write!(w, "{}_{}", prefix, 0)?;
(1..len).try_for_each(|i| write!(w, ", {}_{}", prefix, i))
}
fn write_br_gadget(rem: usize, d: &mut Data, w: &mut dyn Write) -> Result<()> {
match d.label_list.last() {
Some(Label::Forward | Label::If) => d.edition.br_target(rem, false, w),
Some(Label::Backward) => d.edition.br_target(rem, true, w),
None => Ok(()),
}
}
pub fn condense_jump_table(list: &[u32]) -> Vec<(usize, usize, u32)> {
let mut result = Vec::new();
let mut index = 0;
while index < list.len() {
let start = index;
loop {
index += 1;
// if end of list or next value is not equal, break
if index == list.len() || list[index - 1] != list[index] {
break;
}
}
result.push((start, index - 1, list[start]));
}
result
}
#[derive(PartialEq, Eq)]
enum Label {
Forward,
Backward,
If,
}
pub struct Data<'a> {
label_list: Vec<Label>,
num_param: u32,
edition: &'a dyn Edition,
}
impl<'a> Data<'a> {
pub fn new(num_param: u32, edition: &'a dyn Edition) -> Self {
Self {
label_list: Vec::new(),
num_param,
edition,
}
}
}
impl Select {
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
write!(w, "(")?;
self.cond.output(d, w)?;
write!(w, " ~= 0 and ")?;
self.a.output(d, w)?;
write!(w, " or ")?;
self.b.output(d, w)?;
write!(w, ")")
}
}
impl GetLocal {
fn write_variable(var: u32, d: &Data, w: &mut dyn Write) -> Result<()> {
if let Some(rem) = var.checked_sub(d.num_param) {
write!(w, "loc_{} ", rem)
} else {
write!(w, "param_{} ", var)
}
}
fn output(&self, d: &Data, w: &mut dyn Write) -> Result<()> {
Self::write_variable(self.var, d, w)
}
}
impl GetGlobal {
fn output(&self, w: &mut dyn Write) -> Result<()> {
write!(w, "GLOBAL_LIST[{}].value ", self.var)
}
}
impl AnyLoad {
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
write!(w, "load_{}(memory_at_0, ", self.op.as_name())?;
self.pointer.output(d, w)?;
write!(w, "+ {})", self.offset)
}
}
impl MemorySize {
fn output(&self, w: &mut dyn Write) -> Result<()> {
write!(w, "rt.memory.size(memory_at_{})", self.memory)
}
}
impl MemoryGrow {
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
write!(w, "rt.memory.grow(memory_at_{}, ", self.memory)?;
self.value.output(d, w)?;
write!(w, ")")
}
}
impl Value {
fn write_f32(f: f32, w: &mut dyn Write) -> Result<()> {
let sign = if f.is_sign_negative() { "-" } else { "" };
if f.is_infinite() {
write!(w, "{}math.huge", sign)
} else if f.is_nan() {
write!(w, "{}0/0", sign)
} else {
write!(w, "{:e}", f)
}
}
fn write_f64(f: f64, w: &mut dyn Write) -> Result<()> {
let sign = if f.is_sign_negative() { "-" } else { "" };
if f.is_infinite() {
write!(w, "{}math.huge", sign)
} else if f.is_nan() {
write!(w, "{}0/0", sign)
} else {
write!(w, "{:e}", f)
}
}
fn output(&self, d: &Data, w: &mut dyn Write) -> Result<()> {
match self {
Self::I32(i) => write!(w, "{} ", i),
Self::I64(i) => write!(w, "{} ", d.edition.i64(*i)),
Self::F32(f) => Self::write_f32(*f, w),
Self::F64(f) => Self::write_f64(*f, w),
}
}
}
impl AnyUnOp {
fn write_as_call(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
let (a, b) = self.op.as_name();
write!(w, "{}_{}(", a, b)?;
self.rhs.output(d, w)?;
write!(w, ")")
}
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
if let Some(op) = self.op.as_operator() {
write!(w, "{}", op)?;
self.rhs.output(d, w)
} else {
self.write_as_call(d, w)
}
}
}
impl AnyBinOp {
fn write_as_op(&self, op: &'static str, d: &mut Data, w: &mut dyn Write) -> Result<()> {
write!(w, "(")?;
self.lhs.output(d, w)?;
write!(w, "{} ", op)?;
self.rhs.output(d, w)?;
write!(w, ")")
}
fn write_as_call(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
let (a, b) = self.op.as_name();
write!(w, "{}_{}(", a, b)?;
self.lhs.output(d, w)?;
write!(w, ", ")?;
self.rhs.output(d, w)?;
write!(w, ")")
}
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
if let Some(op) = self.op.as_operator() {
self.write_as_op(op, d, w)
} else {
self.write_as_call(d, w)
}
}
}
impl Expression {
fn write_list(list: &[Self], d: &mut Data, w: &mut dyn Write) -> Result<()> {
list.iter().enumerate().try_for_each(|(i, v)| {
if i != 0 {
write!(w, ", ")?;
}
v.output(d, w)
})
}
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
match self {
Self::Recall(i) => write!(w, "reg_{} ", i),
Self::Select(s) => s.output(d, w),
Self::GetLocal(g) => g.output(d, w),
Self::GetGlobal(g) => g.output(w),
Self::AnyLoad(a) => a.output(d, w),
Self::MemorySize(m) => m.output(w),
Self::MemoryGrow(m) => m.output(d, w),
Self::Value(v) => v.output(d, w),
Self::AnyUnOp(a) => a.output(d, w),
Self::AnyBinOp(a) => a.output(d, w),
}
}
fn to_buffer(&self, d: &mut Data) -> Result<String> {
let mut buf = Vec::new();
self.output(d, &mut buf)?;
Ok(String::from_utf8(buf).unwrap())
}
}
impl Memorize {
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
write!(w, "reg_{} = ", self.var)?;
self.value.output(d, w)
}
}
impl Forward {
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
let rem = d.label_list.len();
d.label_list.push(Label::Forward);
d.edition.start_block(w)?;
self.body.iter().try_for_each(|s| s.output(d, w))?;
d.edition.end_block(rem, w)?;
d.label_list.pop().unwrap();
write_br_gadget(rem, d, w)
}
}
impl Backward {
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
let rem = d.label_list.len();
d.label_list.push(Label::Backward);
d.edition.start_loop(rem, w)?;
self.body.iter().try_for_each(|s| s.output(d, w))?;
d.edition.end_loop(w)?;
d.label_list.pop().unwrap();
write_br_gadget(rem, d, w)
}
}
impl If {
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
let rem = d.label_list.len();
d.label_list.push(Label::If);
let var = self.cond.to_buffer(d)?;
d.edition.start_if(&var, w)?;
self.truthy.iter().try_for_each(|s| s.output(d, w))?;
if let Some(v) = &self.falsey {
write!(w, "else ")?;
v.iter().try_for_each(|s| s.output(d, w))?;
}
d.edition.end_if(rem, w)?;
d.label_list.pop().unwrap();
write_br_gadget(rem, d, w)
}
}
impl Br {
fn write_at(up: u32, d: &Data, w: &mut dyn Write) -> Result<()> {
let up = up as usize;
let level = d.label_list.len() - 1;
let is_loop = d.label_list[level - up] == Label::Backward;
d.edition.br_to_level(level, up, is_loop, w)
}
fn output(&self, d: &Data, w: &mut dyn Write) -> Result<()> {
Self::write_at(self.target, d, w)
}
}
impl BrIf {
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
write!(w, "if ")?;
self.cond.output(d, w)?;
write!(w, "~= 0 then ")?;
Br::write_at(self.target, d, w)?;
write!(w, "end ")
}
}
impl BrTable {
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
write!(w, "local temp = ")?;
self.cond.output(d, w)?;
write!(w, " ")?;
for (start, end, dest) in condense_jump_table(&self.data.table) {
if start == end {
write!(w, "if temp == {} then ", start)?;
} else {
write!(w, "if temp >= {} and temp <= {} then ", start, end)?;
}
Br::write_at(dest, d, w)?;
write!(w, "else")?;
}
write!(w, " ")?;
Br::write_at(self.data.default, d, w)?;
write!(w, "end ")
}
}
impl Return {
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
write!(w, "do return ")?;
self.list.iter().enumerate().try_for_each(|(i, v)| {
if i > 0 {
write!(w, ", ")?;
}
v.output(d, w)
})?;
write!(w, "end ")
}
}
impl Call {
fn write_result_list(range: Range<u32>, w: &mut dyn Write) -> Result<()> {
if range.is_empty() {
return Ok(());
}
range.clone().try_for_each(|i| {
if i != range.start {
write!(w, ", ")?;
}
write!(w, "reg_{}", i)
})?;
write!(w, " = ")
}
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
Self::write_result_list(self.result.clone(), w)?;
write!(w, "FUNC_LIST[{}](", self.func)?;
Expression::write_list(&self.param_list, d, w)?;
write!(w, ")")
}
}
impl CallIndirect {
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
Call::write_result_list(self.result.clone(), w)?;
write!(w, "TABLE_LIST[{}].data[", self.table)?;
self.index.output(d, w)?;
write!(w, "](")?;
Expression::write_list(&self.param_list, d, w)?;
write!(w, ")")
}
}
impl SetLocal {
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
GetLocal::write_variable(self.var, d, w)?;
write!(w, "= ")?;
self.value.output(d, w)
}
}
impl SetGlobal {
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
write!(w, "GLOBAL_LIST[{}].value = ", self.var)?;
self.value.output(d, w)
}
}
impl AnyStore {
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
write!(w, "store_{}(memory_at_0, ", self.op.as_name())?;
self.pointer.output(d, w)?;
write!(w, "+ {}, ", self.offset)?;
self.value.output(d, w)?;
write!(w, ")")
}
}
impl Statement {
fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
match self {
Statement::Unreachable => write!(w, "error(\"out of code bounds\")"),
Statement::Memorize(v) => v.output(d, w),
Statement::Forward(v) => v.output(d, w),
Statement::Backward(v) => v.output(d, w),
Statement::If(v) => v.output(d, w),
Statement::Br(v) => v.output(d, w),
Statement::BrIf(v) => v.output(d, w),
Statement::BrTable(v) => v.output(d, w),
Statement::Return(v) => v.output(d, w),
Statement::Call(v) => v.output(d, w),
Statement::CallIndirect(v) => v.output(d, w),
Statement::SetLocal(v) => v.output(d, w),
Statement::SetGlobal(v) => v.output(d, w),
Statement::AnyStore(v) => v.output(d, w),
}
}
}
impl Function {
fn write_variable_list(&self, w: &mut dyn Write) -> Result<()> {
if self.num_local != 0 {
let list = vec!["0"; self.num_local as usize].join(", ");
write!(w, "local ")?;
write_in_order("loc", self.num_local, w)?;
write!(w, " = {} ", list)?;
}
if self.num_stack != 0 {
write!(w, "local ")?;
write_in_order("reg", self.num_stack, w)?;
write!(w, " ")?;
}
Ok(())
}
pub fn output(&self, d: &mut Data, w: &mut dyn Write) -> Result<()> {
write!(w, "function(")?;
write_in_order("param", self.num_param, w)?;
write!(w, ")")?;
for v in memory::visit(self) {
write!(w, "local memory_at_{0} = MEMORY_LIST[{0}]", v)?;
}
self.write_variable_list(w)?;
self.body.output(d, w)?;
write!(w, "end ")
}
}
+59
View File
@@ -0,0 +1,59 @@
use crate::backend::ast::data::{
AnyBinOp, AnyLoad, AnyStore, AnyUnOp, Backward, Br, BrIf, BrTable, Call, CallIndirect,
Expression, Forward, GetGlobal, GetLocal, If, Memorize, MemoryGrow, MemorySize, Return, Select,
SetGlobal, SetLocal, Statement, Value,
};
pub trait Visitor {
fn visit_recall(&mut self, _: usize) {}
fn visit_select(&mut self, _: &Select) {}
fn visit_get_local(&mut self, _: &GetLocal) {}
fn visit_get_global(&mut self, _: &GetGlobal) {}
fn visit_any_load(&mut self, _: &AnyLoad) {}
fn visit_memory_size(&mut self, _: &MemorySize) {}
fn visit_memory_grow(&mut self, _: &MemoryGrow) {}
fn visit_value(&mut self, _: &Value) {}
fn visit_any_unop(&mut self, _: &AnyUnOp) {}
fn visit_any_binop(&mut self, _: &AnyBinOp) {}
fn visit_expression(&mut self, _: &Expression) {}
fn visit_unreachable(&mut self) {}
fn visit_memorize(&mut self, _: &Memorize) {}
fn visit_forward(&mut self, _: &Forward) {}
fn visit_backward(&mut self, _: &Backward) {}
fn visit_if(&mut self, _: &If) {}
fn visit_br(&mut self, _: &Br) {}
fn visit_br_if(&mut self, _: &BrIf) {}
fn visit_br_table(&mut self, _: &BrTable) {}
fn visit_return(&mut self, _: &Return) {}
fn visit_call(&mut self, _: &Call) {}
fn visit_call_indirect(&mut self, _: &CallIndirect) {}
fn visit_set_local(&mut self, _: &SetLocal) {}
fn visit_set_global(&mut self, _: &SetGlobal) {}
fn visit_any_store(&mut self, _: &AnyStore) {}
fn visit_statement(&mut self, _: &Statement) {}
}
+53
View File
@@ -0,0 +1,53 @@
use std::collections::BTreeSet;
use crate::backend::ast::data::{AnyBinOp, AnyLoad, AnyStore, AnyUnOp, Function};
use super::data::Visitor;
struct Visit {
result: BTreeSet<(&'static str, &'static str)>,
}
impl Visitor for Visit {
fn visit_any_load(&mut self, v: &AnyLoad) {
let name = v.op.as_name();
self.result.insert(("load", name));
}
fn visit_any_store(&mut self, v: &AnyStore) {
let name = v.op.as_name();
self.result.insert(("store", name));
}
fn visit_any_unop(&mut self, v: &AnyUnOp) {
if v.op.as_operator().is_some() {
return;
}
let name = v.op.as_name();
self.result.insert(name);
}
fn visit_any_binop(&mut self, v: &AnyBinOp) {
if v.op.as_operator().is_some() {
return;
}
let name = v.op.as_name();
self.result.insert(name);
}
}
pub fn visit(func: &Function) -> BTreeSet<(&'static str, &'static str)> {
let mut visit = Visit {
result: BTreeSet::new(),
};
func.accept(&mut visit);
visit.result
}
+37
View File
@@ -0,0 +1,37 @@
use std::collections::BTreeSet;
use crate::backend::ast::data::{AnyLoad, AnyStore, Function, MemoryGrow, MemorySize};
use super::data::Visitor;
struct Visit {
result: BTreeSet<u8>,
}
impl Visitor for Visit {
fn visit_any_store(&mut self, _: &AnyStore) {
self.result.insert(0);
}
fn visit_any_load(&mut self, _: &AnyLoad) {
self.result.insert(0);
}
fn visit_memory_size(&mut self, m: &MemorySize) {
self.result.insert(m.memory);
}
fn visit_memory_grow(&mut self, m: &MemoryGrow) {
self.result.insert(m.memory);
}
}
pub fn visit(func: &Function) -> BTreeSet<u8> {
let mut visit = Visit {
result: BTreeSet::new(),
};
func.accept(&mut visit);
visit.result
}
+3
View File
@@ -0,0 +1,3 @@
pub mod data;
pub mod localize;
pub mod memory;
+22
View File
@@ -0,0 +1,22 @@
use backend::{edition::data::from_string, translator::data::Module};
use parity_wasm::deserialize_file;
mod backend;
fn main() {
let mut args = std::env::args().skip(1);
let ed = args
.next()
.as_deref()
.and_then(from_string)
.expect("No language argument provided");
let output = std::io::stdout();
for v in args {
let wasm = deserialize_file(v).unwrap();
let module = Module::new(&wasm);
module.translate(ed, &mut output.lock()).unwrap();
}
}