Source added

This commit is contained in:
Rerumu
2021-10-12 01:33:54 -04:00
commit b6297463a7
18 changed files with 2178 additions and 0 deletions
+153
View File
@@ -0,0 +1,153 @@
use super::writer::Writer;
use std::{fmt::Display, io::Result};
pub struct Infix<T> {
rhs: &'static str,
inner: T,
}
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: Writer) -> Result<()>;
fn start_loop(&self, level: usize, w: Writer) -> Result<()>;
fn start_if(&self, cond: &str, w: Writer) -> Result<()>;
fn end_block(&self, level: usize, w: Writer) -> Result<()>;
fn end_loop(&self, w: Writer) -> Result<()>;
fn end_if(&self, level: usize, w: Writer) -> Result<()>;
fn br_target(&self, level: usize, in_loop: bool, w: Writer) -> Result<()>;
fn br_to_level(&self, level: usize, up: usize, is_loop: bool, w: Writer) -> Result<()>;
fn i64(&self, i: i64) -> Infix<i64>;
}
pub struct LuaJIT;
impl Edition for LuaJIT {
fn runtime(&self) -> &'static str {
"luajit"
}
fn start_block(&self, w: Writer) -> Result<()> {
writeln!(w, "do")
}
fn start_loop(&self, level: usize, w: Writer) -> Result<()> {
writeln!(w, "do")?;
writeln!(w, "::continue_at_{}::", level)
}
fn start_if(&self, cond: &str, w: Writer) -> Result<()> {
writeln!(w, "if {} ~= 0 then", cond)
}
fn end_block(&self, level: usize, w: Writer) -> Result<()> {
writeln!(w, "::continue_at_{}::", level)?;
writeln!(w, "end")
}
fn end_loop(&self, w: Writer) -> Result<()> {
writeln!(w, "end")
}
fn end_if(&self, level: usize, w: Writer) -> Result<()> {
writeln!(w, "::continue_at_{}::", level)?;
writeln!(w, "end")
}
fn br_target(&self, _level: usize, _in_loop: bool, _w: Writer) -> Result<()> {
Ok(())
}
fn br_to_level(&self, level: usize, up: usize, _is_loop: bool, w: Writer) -> Result<()> {
writeln!(w, "goto continue_at_{}", level - up)
}
fn i64(&self, i: i64) -> Infix<i64> {
Infix {
rhs: "LL",
inner: i,
}
}
}
pub struct Luau;
impl Edition for Luau {
fn runtime(&self) -> &'static str {
"luau"
}
fn start_block(&self, w: Writer) -> Result<()> {
writeln!(w, "while true do")
}
fn start_loop(&self, _level: usize, w: Writer) -> Result<()> {
writeln!(w, "while true do")
}
fn start_if(&self, cond: &str, w: Writer) -> Result<()> {
writeln!(w, "while true do")?;
writeln!(w, "if {} ~= 0 then", cond)
}
fn end_block(&self, _level: usize, w: Writer) -> Result<()> {
writeln!(w, "break")?;
writeln!(w, "end")
}
fn end_loop(&self, w: Writer) -> Result<()> {
writeln!(w, "break")?;
writeln!(w, "end")
}
fn end_if(&self, _level: usize, w: Writer) -> Result<()> {
writeln!(w, "end")?;
writeln!(w, "break")?;
writeln!(w, "end")
}
fn br_target(&self, level: usize, in_loop: bool, w: Writer) -> Result<()> {
writeln!(w, "if desired then")?;
writeln!(w, "if desired == {} then", level)?;
writeln!(w, "desired = nil")?;
if in_loop {
writeln!(w, "continue")?;
}
writeln!(w, "end")?;
writeln!(w, "break")?;
writeln!(w, "end")
}
fn br_to_level(&self, level: usize, up: usize, is_loop: bool, w: Writer) -> Result<()> {
if up == 0 {
if is_loop {
writeln!(w, "continue")?;
} else {
writeln!(w, "break")?;
}
} else {
writeln!(w, "desired = {}", level - up)?;
writeln!(w, "break")?;
}
Ok(())
}
fn i64(&self, i: i64) -> Infix<i64> {
Infix { rhs: "", inner: i }
}
}
+3
View File
@@ -0,0 +1,3 @@
pub mod edition;
pub mod register;
pub mod writer;
+41
View File
@@ -0,0 +1,41 @@
pub struct Register {
pub last: u32,
pub inner: u32,
saved: Vec<u32>,
}
impl Register {
pub fn new() -> Self {
Self {
last: 0,
inner: 0,
saved: vec![0],
}
}
fn extend(&mut self) {
self.last = self.last.max(self.inner);
}
pub fn save(&mut self) {
self.saved.push(self.inner);
}
pub fn load(&mut self) {
self.inner = self.saved.pop().unwrap();
}
pub fn push(&mut self, n: u32) -> u32 {
let prev = self.inner;
self.inner = self.inner.checked_add(n).unwrap();
self.extend();
prev
}
pub fn pop(&mut self, n: u32) -> u32 {
self.inner = self.inner.checked_sub(n).unwrap();
self.inner
}
}
+17
View File
@@ -0,0 +1,17 @@
use std::io::{Result, Write};
pub type Writer<'a> = &'a mut dyn Write;
pub fn ordered_iter(prefix: &'static str, end: u32) -> impl Iterator<Item = String> {
(1..=end).map(move |i| format!("{}_{}", prefix, i))
}
pub fn write_ordered(prefix: &'static str, end: u32, w: Writer) -> Result<()> {
let mut iter = ordered_iter(prefix, end);
if let Some(s) = iter.next() {
write!(w, "{}", s)?;
}
iter.try_for_each(|s| write!(w, ", {}", s))
}
+2
View File
@@ -0,0 +1,2 @@
pub mod helper;
pub mod translation;
+458
View File
@@ -0,0 +1,458 @@
use super::level_2::list_to_range;
use crate::{
backend::helper::{edition::Edition, register::Register, writer::Writer},
data::{Arity, Code, Module},
};
use parity_wasm::elements::{BrTableData, Instruction};
use std::{fmt::Display, io::Result};
#[derive(PartialEq)]
pub enum Label {
Block,
If,
Loop,
}
pub struct Body<'a> {
spec: &'a dyn Edition,
label_list: Vec<Label>,
pub table_list: Vec<BrTableData>,
pub reg: Register,
}
impl<'a> Body<'a> {
pub fn new(spec: &'a dyn Edition) -> Self {
Self {
spec,
label_list: vec![],
table_list: Vec::new(),
reg: Register::new(),
}
}
pub fn gen(&mut self, index: usize, module: &Module) -> Result<Vec<u8>> {
let mut w = Vec::new();
module.code[index]
.inst_list
.iter()
.try_for_each(|v| self.gen_inst(index, v, module, &mut w))?;
Ok(w)
}
fn gen_jump(&mut self, up: u32, w: Writer) -> Result<()> {
let up = up as usize;
let level = self.label_list.len() - 1;
let is_loop = self.label_list[level - up] == Label::Loop;
self.spec.br_to_level(level, up, is_loop, w)
}
fn gen_br_if(&mut self, i: u32, f: &Code, w: Writer) -> Result<()> {
let cond = f.var_name_of(self.reg.pop(1));
writeln!(w, "if {} ~= 0 then", cond)?;
self.gen_jump(i, w)?;
writeln!(w, "end")
}
fn gen_br_table(&mut self, data: &BrTableData, f: &Code, w: Writer) -> Result<()> {
let case = f.var_name_of(self.reg.pop(1));
for (r, t) in list_to_range(&data.table) {
if r.len() == 1 {
writeln!(w, "if {} == {} then", case, r.start)?;
} else {
writeln!(w, "if {0} >= {1} and {0} <= {2} then", case, r.start, r.end)?;
}
self.gen_jump(t, w)?;
write!(w, "else")?;
}
writeln!(w)?;
self.gen_jump(data.default, w)?;
writeln!(w, "end")
}
fn gen_load(&mut self, t: &str, o: u32, f: &Code, w: Writer) -> Result<()> {
let reg = f.var_name_of(self.reg.pop(1));
self.reg.push(1);
writeln!(w, "{0} = load.{1}(MEMORY_LIST[0], {0} + {2})", reg, t, o)
}
fn gen_store(&mut self, t: &str, o: u32, f: &Code, w: Writer) -> Result<()> {
let val = f.var_name_of(self.reg.pop(1));
let reg = f.var_name_of(self.reg.pop(1));
writeln!(w, "store.{}(MEMORY_LIST[0], {} + {}, {})", t, reg, o, val)
}
fn gen_const<T: Display>(&mut self, val: T, f: &Code, w: Writer) -> Result<()> {
let reg = f.var_name_of(self.reg.push(1));
writeln!(w, "{} = {}", reg, val)
}
fn gen_compare(&mut self, op: &str, f: &Code, w: Writer) -> Result<()> {
let rhs = f.var_name_of(self.reg.pop(1));
let lhs = f.var_name_of(self.reg.pop(1));
self.reg.push(1);
writeln!(w, "{1} = {1} {0} {2} and 1 or 0", op, lhs, rhs)
}
fn gen_unop_ex(&mut self, op: &str, f: &Code, w: Writer) -> Result<()> {
let reg = f.var_name_of(self.reg.pop(1));
self.reg.push(1);
writeln!(w, "{1} = {0}({1})", op, reg)
}
fn gen_binop(&mut self, op: &str, f: &Code, w: Writer) -> Result<()> {
let rhs = f.var_name_of(self.reg.pop(1));
let lhs = f.var_name_of(self.reg.pop(1));
self.reg.push(1);
writeln!(w, "{1} = {1} {0} {2}", op, lhs, rhs)
}
fn gen_binop_ex(&mut self, op: &str, f: &Code, w: Writer) -> Result<()> {
let rhs = f.var_name_of(self.reg.pop(1));
let lhs = f.var_name_of(self.reg.pop(1));
self.reg.push(1);
writeln!(w, "{1} = {0}({1}, {2})", op, lhs, rhs)
}
fn gen_call(&mut self, name: &str, f: &Code, a: &Arity, w: Writer) -> Result<()> {
let bottom = self.reg.pop(a.num_param);
self.reg.push(a.num_result);
if a.num_result != 0 {
let result = f.var_range_of(bottom, a.num_result).join(", ");
writeln!(w, "{} =", result)?;
}
if a.num_param == 0 {
writeln!(w, "{}()", name)
} else {
let param = f.var_range_of(bottom, a.num_param).join(", ");
writeln!(w, "{}({})", name, param)
}
}
fn gen_return(&mut self, num: u32, f: &Code, w: Writer) -> Result<()> {
let top = self.reg.inner;
let list = f.var_range_of(top - num, num).join(", ");
self.reg.pop(num); // technically a no-op
writeln!(w, "do return {} end", list)
}
fn gen_inst(&mut self, index: usize, i: &Instruction, m: &Module, w: Writer) -> Result<()> {
let func = &m.code[index];
match i {
Instruction::Unreachable => writeln!(w, "error('unreachable code entered')"),
Instruction::Nop => {
// no code
Ok(())
}
Instruction::Block(_) => {
self.reg.save();
self.label_list.push(Label::Block);
self.spec.start_block(w)
}
Instruction::Loop(_) => {
self.reg.save();
self.label_list.push(Label::Loop);
self.spec.start_loop(self.label_list.len() - 1, w)
}
Instruction::If(_) => {
let cond = func.var_name_of(self.reg.pop(1));
self.reg.save();
self.label_list.push(Label::If);
self.spec.start_if(&cond, w)
}
Instruction::Else => {
self.reg.load();
self.reg.save();
writeln!(w, "else")
}
Instruction::End => {
let rem = self.label_list.len().saturating_sub(1);
match self.label_list.pop() {
Some(Label::Block) => self.spec.end_block(rem, w)?,
Some(Label::If) => self.spec.end_if(rem, w)?,
Some(Label::Loop) => self.spec.end_loop(w)?,
None => {
let num = m.in_arity[index].num_result;
if num != 0 {
self.gen_return(num, func, w)?;
}
writeln!(w, "end")?;
}
}
self.reg.load();
match self.label_list.last() {
Some(Label::Block | Label::If) => self.spec.br_target(rem, false, w),
Some(Label::Loop) => self.spec.br_target(rem, true, w),
None => Ok(()),
}
}
Instruction::Br(i) => self.gen_jump(*i, w),
Instruction::BrIf(i) => self.gen_br_if(*i, func, w),
Instruction::BrTable(data) => self.gen_br_table(data, func, w),
Instruction::Return => {
let num = m.in_arity[index].num_result;
self.gen_return(num, func, w)
}
Instruction::Call(i) => {
let name = format!("FUNC_LIST[{}]", i);
let arity = m.arity_of(*i as usize);
self.gen_call(&name, func, arity, w)
}
Instruction::CallIndirect(i, t) => {
let index = func.var_name_of(self.reg.pop(1));
let name = format!("TABLE_LIST[{}][{}]", t, index);
let arity = m.arity_of(*i as usize);
self.gen_call(&name, func, arity, w)
}
Instruction::Drop => {
self.reg.pop(1);
Ok(())
}
Instruction::Select => {
let cond = func.var_name_of(self.reg.pop(1));
let v2 = func.var_name_of(self.reg.pop(1));
let v1 = func.var_name_of(self.reg.pop(1));
self.reg.push(1);
writeln!(w, "if {} == 0 then", cond)?;
writeln!(w, "{} = {}", v1, v2)?;
writeln!(w, "end")
}
Instruction::GetLocal(i) => {
let reg = func.var_name_of(self.reg.push(1));
let var = func.var_name_of(*i);
writeln!(w, "{} = {}", reg, var)
}
Instruction::SetLocal(i) => {
let var = func.var_name_of(*i);
let reg = func.var_name_of(self.reg.pop(1));
writeln!(w, "{} = {}", var, reg)
}
Instruction::TeeLocal(i) => {
let var = func.var_name_of(*i);
let reg = func.var_name_of(self.reg.pop(1));
self.reg.push(1);
writeln!(w, "{} = {}", var, reg)
}
Instruction::GetGlobal(i) => {
let reg = func.var_name_of(self.reg.push(1));
writeln!(w, "{} = GLOBAL_LIST[{}].value", reg, i)
}
Instruction::SetGlobal(i) => {
let reg = func.var_name_of(self.reg.pop(1));
writeln!(w, "GLOBAL_LIST[{}].value = {}", i, reg)
}
Instruction::I32Load(_, o) => self.gen_load("i32", *o, func, w),
Instruction::I64Load(_, o) => self.gen_load("i64", *o, func, w),
Instruction::F32Load(_, o) => self.gen_load("f32", *o, func, w),
Instruction::F64Load(_, o) => self.gen_load("f64", *o, func, w),
Instruction::I32Load8S(_, o) => self.gen_load("i32_i8", *o, func, w),
Instruction::I32Load8U(_, o) => self.gen_load("i32_u8", *o, func, w),
Instruction::I32Load16S(_, o) => self.gen_load("i32_i16", *o, func, w),
Instruction::I32Load16U(_, o) => self.gen_load("i32_u16", *o, func, w),
Instruction::I64Load8S(_, o) => self.gen_load("i64_i8", *o, func, w),
Instruction::I64Load8U(_, o) => self.gen_load("i64_u8", *o, func, w),
Instruction::I64Load16S(_, o) => self.gen_load("i64_i16", *o, func, w),
Instruction::I64Load16U(_, o) => self.gen_load("i64_u16", *o, func, w),
Instruction::I64Load32S(_, o) => self.gen_load("i64_i32", *o, func, w),
Instruction::I64Load32U(_, o) => self.gen_load("i64_u32", *o, func, w),
Instruction::I32Store(_, o) => self.gen_store("i32", *o, func, w),
Instruction::I64Store(_, o) => self.gen_store("i64", *o, func, w),
Instruction::F32Store(_, o) => self.gen_store("f32", *o, func, w),
Instruction::F64Store(_, o) => self.gen_store("f64", *o, func, w),
Instruction::I32Store8(_, o) => self.gen_store("i32_n8", *o, func, w),
Instruction::I32Store16(_, o) => self.gen_store("i32_n16", *o, func, w),
Instruction::I64Store8(_, o) => self.gen_store("i64_n8", *o, func, w),
Instruction::I64Store16(_, o) => self.gen_store("i64_n16", *o, func, w),
Instruction::I64Store32(_, o) => self.gen_store("i64_n32", *o, func, w),
Instruction::CurrentMemory(index) => {
let reg = func.var_name_of(self.reg.push(1));
writeln!(w, "{} = read_page_num(MEMORY_LIST[{}])", reg, index)
}
Instruction::GrowMemory(index) => {
let reg = func.var_name_of(self.reg.pop(1));
self.reg.push(1);
writeln!(w, "{0} = grow_page_num(MEMORY_LIST[{1}], {0})", reg, index)
}
Instruction::I32Const(v) => self.gen_const(v, func, w),
Instruction::I64Const(v) => self.gen_const(self.spec.i64(*v), func, w),
Instruction::F32Const(v) => self.gen_const(f32::from_bits(*v), func, w),
Instruction::F64Const(v) => self.gen_const(f64::from_bits(*v), func, w),
Instruction::I32Eqz | Instruction::I64Eqz => {
let reg = func.var_name_of(self.reg.pop(1));
self.reg.push(1);
writeln!(w, "{} = {} == 0 and 1 or 0", reg, reg)
}
Instruction::I32Eq | Instruction::I64Eq | Instruction::F32Eq | Instruction::F64Eq => {
self.gen_compare("==", func, w)
}
Instruction::I32Ne | Instruction::I64Ne | Instruction::F32Ne | Instruction::F64Ne => {
self.gen_compare("~=", func, w)
}
// note that signed comparisons of all types behave the same so
// they can be condensed using Lua's operators
Instruction::I32LtU => self.gen_binop_ex("lt.u32", func, w),
Instruction::I32LtS | Instruction::I64LtS | Instruction::F32Lt | Instruction::F64Lt => {
self.gen_compare("<", func, w)
}
Instruction::I32GtU => self.gen_binop_ex("gt.u32", func, w),
Instruction::I32GtS | Instruction::I64GtS | Instruction::F32Gt | Instruction::F64Gt => {
self.gen_compare(">", func, w)
}
Instruction::I32LeU => self.gen_binop_ex("le.u32", func, w),
Instruction::I32LeS | Instruction::I64LeS | Instruction::F32Le | Instruction::F64Le => {
self.gen_compare("<=", func, w)
}
Instruction::I32GeU => self.gen_binop_ex("ge.u32", func, w),
Instruction::I32GeS | Instruction::I64GeS | Instruction::F32Ge | Instruction::F64Ge => {
self.gen_compare(">=", func, w)
}
Instruction::I64LtU => self.gen_binop_ex("lt.u64", func, w),
Instruction::I64GtU => self.gen_binop_ex("gt.u64", func, w),
Instruction::I64LeU => self.gen_binop_ex("le.u64", func, w),
Instruction::I64GeU => self.gen_binop_ex("ge.u64", func, w),
Instruction::I32Clz => self.gen_unop_ex("clz.i32", func, w),
Instruction::I32Ctz => self.gen_unop_ex("ctz.i32", func, w),
Instruction::I32Popcnt => self.gen_unop_ex("popcnt.i32", func, w),
Instruction::I32DivS => self.gen_binop_ex("div.i32", func, w),
Instruction::I32DivU => self.gen_binop_ex("div.u32", func, w),
Instruction::I32RemS => self.gen_binop_ex("rem.i32", func, w),
Instruction::I32RemU => self.gen_binop_ex("rem.u32", func, w),
Instruction::I32And => self.gen_binop_ex("band.i32", func, w),
Instruction::I32Or => self.gen_binop_ex("bor.i32", func, w),
Instruction::I32Xor => self.gen_binop_ex("bxor.i32", func, w),
Instruction::I32Shl => self.gen_binop_ex("shl.i32", func, w),
Instruction::I32ShrS => self.gen_binop_ex("shr.i32", func, w),
Instruction::I32ShrU => self.gen_binop_ex("shr.u32", func, w),
Instruction::I32Rotl => self.gen_binop_ex("rotl.i32", func, w),
Instruction::I32Rotr => self.gen_binop_ex("rotr.i32", func, w),
Instruction::I64Clz => self.gen_unop_ex("clz.i64", func, w),
Instruction::I64Ctz => self.gen_unop_ex("ctz.i64", func, w),
Instruction::I64Popcnt => self.gen_unop_ex("popcnt.i64", func, w),
Instruction::I64DivS => self.gen_binop_ex("div.i64", func, w),
Instruction::I64DivU => self.gen_binop_ex("div.u64", func, w),
Instruction::I64RemS => self.gen_binop_ex("rem.i64", func, w),
Instruction::I64RemU => self.gen_binop_ex("rem.u64", func, w),
Instruction::I64And => self.gen_binop_ex("band.i64", func, w),
Instruction::I64Or => self.gen_binop_ex("bor.i64", func, w),
Instruction::I64Xor => self.gen_binop_ex("bxor.i64", func, w),
Instruction::I64Shl => self.gen_binop_ex("shl.i64", func, w),
Instruction::I64ShrS => self.gen_binop_ex("shr.i64", func, w),
Instruction::I64ShrU => self.gen_binop_ex("shr.u64", func, w),
Instruction::I64Rotl => self.gen_binop_ex("rotl.i64", func, w),
Instruction::I64Rotr => self.gen_binop_ex("rotr.i64", func, w),
Instruction::F32Abs | Instruction::F64Abs => self.gen_unop_ex("math.abs", func, w),
Instruction::F32Neg | Instruction::F64Neg => {
let reg = func.var_name_of(self.reg.pop(1));
self.reg.push(1);
writeln!(w, "{} = -{}", reg, reg)
}
Instruction::F32Ceil | Instruction::F64Ceil => self.gen_unop_ex("math.ceil", func, w),
Instruction::F32Floor | Instruction::F64Floor => {
self.gen_unop_ex("math.floor", func, w)
}
Instruction::F32Trunc | Instruction::F64Trunc => self.gen_unop_ex("trunc.f", func, w),
Instruction::F32Nearest | Instruction::F64Nearest => {
self.gen_unop_ex("nearest.f", func, w)
}
Instruction::F32Sqrt | Instruction::F64Sqrt => self.gen_unop_ex("math.sqrt", func, w),
Instruction::I32Add
| Instruction::I64Add
| Instruction::F32Add
| Instruction::F64Add => self.gen_binop("+", func, w),
Instruction::I32Sub
| Instruction::I64Sub
| Instruction::F32Sub
| Instruction::F64Sub => self.gen_binop("-", func, w),
Instruction::I32Mul
| Instruction::I64Mul
| Instruction::F32Mul
| Instruction::F64Mul => self.gen_binop("*", func, w),
Instruction::F32Div | Instruction::F64Div => self.gen_binop("/", func, w),
Instruction::F32Min | Instruction::F64Min => self.gen_binop_ex("math.min", func, w),
Instruction::F32Max | Instruction::F64Max => self.gen_binop_ex("math.max", func, w),
Instruction::F32Copysign | Instruction::F64Copysign => {
self.gen_unop_ex("math.sign", func, w)
}
Instruction::I32WrapI64 => self.gen_unop_ex("wrap.i64_i32", func, w),
Instruction::I32TruncSF32 => self.gen_unop_ex("trunc.f32_i32", func, w),
Instruction::I32TruncUF32 => self.gen_unop_ex("trunc.f32_u32", func, w),
Instruction::I32TruncSF64 => self.gen_unop_ex("trunc.f64_i32", func, w),
Instruction::I32TruncUF64 => self.gen_unop_ex("trunc.f64_u32", func, w),
Instruction::I64ExtendSI32 => self.gen_unop_ex("extend.i32_i64", func, w),
Instruction::I64ExtendUI32 => self.gen_unop_ex("extend.i32_u64", func, w),
Instruction::I64TruncSF32 => self.gen_unop_ex("trunc.f32_i64", func, w),
Instruction::I64TruncUF32 => self.gen_unop_ex("trunc.f32_u64", func, w),
Instruction::I64TruncSF64 => self.gen_unop_ex("trunc.f64_i64", func, w),
Instruction::I64TruncUF64 => self.gen_unop_ex("trunc.f64_u64", func, w),
Instruction::F32ConvertSI32 => self.gen_unop_ex("convert.i32_f32", func, w),
Instruction::F32ConvertUI32 => self.gen_unop_ex("convert.u32_f32", func, w),
Instruction::F32ConvertSI64 => self.gen_unop_ex("convert.i64_f32", func, w),
Instruction::F32ConvertUI64 => self.gen_unop_ex("convert.u64_f32", func, w),
Instruction::F32DemoteF64 => self.gen_unop_ex("demote.f64_f32", func, w),
Instruction::F64ConvertSI32 => self.gen_unop_ex("convert.f64_i32", func, w),
Instruction::F64ConvertUI32 => self.gen_unop_ex("convert.f64_u32", func, w),
Instruction::F64ConvertSI64 => self.gen_unop_ex("convert.f64_i64", func, w),
Instruction::F64ConvertUI64 => self.gen_unop_ex("convert.f64_u64", func, w),
Instruction::F64PromoteF32 => self.gen_unop_ex("promote.f32_f64", func, w),
Instruction::I32ReinterpretF32 => self.gen_unop_ex("reinterpret.f32_i32", func, w),
Instruction::I64ReinterpretF64 => self.gen_unop_ex("reinterpret.f64_i64", func, w),
Instruction::F32ReinterpretI32 => self.gen_unop_ex("reinterpret.i32_f32", func, w),
Instruction::F64ReinterpretI64 => self.gen_unop_ex("reinterpret.i64_f64", func, w),
}
}
}
+97
View File
@@ -0,0 +1,97 @@
use super::level_1::Body;
use crate::{
backend::helper::{
edition::Edition,
writer::{write_ordered, Writer},
},
data::Module,
};
use parity_wasm::elements::Instruction;
use std::{
io::{Result, Write},
ops::Range,
};
pub fn list_to_range(list: &[u32]) -> Vec<(Range<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, list[start]));
}
result
}
pub fn gen_init_expression(code: &[Instruction], w: Writer) -> Result<()> {
assert!(code.len() == 2);
let inst = code.first().unwrap();
match *inst {
Instruction::I32Const(v) => writeln!(w, "{}", v),
Instruction::I64Const(v) => writeln!(w, "{}", v),
Instruction::F32Const(v) => writeln!(w, "{}", f32::from_bits(v)),
Instruction::F64Const(v) => writeln!(w, "{}", f64::from_bits(v)),
Instruction::GetGlobal(i) => writeln!(w, "GLOBAL_LIST[{}].value", i),
_ => unreachable!(),
}
}
fn gen_prelude(num_param: u32, num_local: u32) -> Result<Vec<u8>> {
let mut w = Vec::new();
writeln!(w, "function(")?;
write_ordered("param", num_param, &mut w)?;
writeln!(w, ")")?;
if num_local != 0 {
let zero = vec!["0"; num_local as usize].join(", ");
writeln!(w, "local")?;
write_ordered("var", num_local, &mut w)?;
writeln!(w, "= {}", zero)?;
}
Ok(w)
}
fn gen_reg_list(last: u32, num_param: u32, num_local: u32) -> Result<Vec<u8>> {
let mut w = Vec::new();
let num = last - num_local - num_param;
if num != 0 {
writeln!(w, "local")?;
write_ordered("reg", num, &mut w)?;
writeln!(w)?;
}
Ok(w)
}
pub fn gen_function(spec: &dyn Edition, index: usize, m: &Module, w: Writer) -> Result<()> {
let mut inner = Body::new(spec);
let num_param = m.in_arity[index].num_param;
let num_local = m.code[index].num_local;
inner.reg.push(num_param + num_local);
let prelude = gen_prelude(num_param, num_local)?;
let body = inner.gen(index, m)?;
let reg = gen_reg_list(inner.reg.last, num_param, num_local)?;
w.write_all(&prelude)?;
w.write_all(&reg)?;
w.write_all(&body)
}
+266
View File
@@ -0,0 +1,266 @@
use super::level_2::{gen_function, gen_init_expression};
use crate::{
backend::helper::{edition::Edition, writer::Writer},
data::Module,
};
use parity_wasm::elements::{External, ImportCountType, Internal, ResizableLimits};
use std::io::Result;
const RUNTIME_DATA: &str = "
local grow_page_num = runtime.grow_page_num
local add = runtime.add
local sub = runtime.sub
local mul = runtime.mul
local div = runtime.div
local le = runtime.le
local lt = runtime.lt
local ge = runtime.ge
local gt = runtime.gt
local band = runtime.band
local bor = runtime.bor
local bxor = runtime.bxor
local bnot = runtime.bnot
local shl = runtime.shl
local shr = runtime.shr
local extend = runtime.extend
local wrap = runtime.wrap
local load = runtime.load
local store = runtime.store
";
fn gen_import_of<T>(m: &Module, w: Writer, lower: &str, cond: T) -> Result<()>
where
T: Fn(&External) -> bool,
{
let import = match m.parent.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();
writeln!(w, "{}[{}] = wasm.{}.{}.{}", upper, i, module, lower, field)?;
}
Ok(())
}
fn aux_internal_index(internal: Internal) -> u32 {
match internal {
Internal::Function(v) | Internal::Table(v) | Internal::Memory(v) | Internal::Global(v) => v,
}
}
fn gen_export_of<T>(m: &Module, w: Writer, lower: &str, cond: T) -> Result<()>
where
T: Fn(&Internal) -> bool,
{
let export = match m.parent.export_section() {
Some(v) => v.entries(),
None => return Ok(()),
};
let upper = lower.to_uppercase();
writeln!(w, "{} = {{", lower)?;
for v in export.iter().filter(|v| cond(v.internal())) {
let field = v.field();
let index = aux_internal_index(*v.internal());
writeln!(w, "{} = {}[{}],", field, upper, index)?;
}
writeln!(w, "}},")
}
fn gen_import_list(m: &Module, w: Writer) -> Result<()> {
gen_import_of(m, w, "func_list", |v| matches!(v, External::Function(_)))?;
gen_import_of(m, w, "table_list", |v| matches!(v, External::Table(_)))?;
gen_import_of(m, w, "memory_list", |v| matches!(v, External::Memory(_)))?;
gen_import_of(m, w, "global_list", |v| matches!(v, External::Global(_)))
}
fn gen_export_list(m: &Module, w: Writer) -> Result<()> {
gen_export_of(m, w, "func_list", |v| matches!(v, Internal::Function(_)))?;
gen_export_of(m, w, "table_list", |v| matches!(v, Internal::Table(_)))?;
gen_export_of(m, w, "memory_list", |v| matches!(v, Internal::Memory(_)))?;
gen_export_of(m, w, "global_list", |v| matches!(v, Internal::Global(_)))
}
fn gen_limit_data(limit: &ResizableLimits, w: Writer) -> Result<()> {
writeln!(w, "{{ min = {}", limit.initial())?;
if let Some(max) = limit.maximum() {
writeln!(w, ", max = {}", max)?;
}
writeln!(w, ", data = {{}} }}")
}
fn gen_table_list(m: &Module, w: Writer) -> Result<()> {
let table = match m.parent.table_section() {
Some(v) => v.entries(),
None => return Ok(()),
};
let offset = m.parent.import_count(ImportCountType::Table);
for (i, v) in table.iter().enumerate() {
let index = i + offset;
writeln!(w, "TABLE_LIST[{}] =", index)?;
gen_limit_data(v.limits(), w)?;
}
Ok(())
}
fn gen_memory_list(m: &Module, w: Writer) -> Result<()> {
let memory = match m.parent.memory_section() {
Some(v) => v.entries(),
None => return Ok(()),
};
let offset = m.parent.import_count(ImportCountType::Memory);
for (i, v) in memory.iter().enumerate() {
let index = i + offset;
writeln!(w, "MEMORY_LIST[{}] =", index)?;
gen_limit_data(v.limits(), w)?;
}
Ok(())
}
fn gen_global_list(m: &Module, w: Writer) -> Result<()> {
let global = match m.parent.global_section() {
Some(v) => v,
None => return Ok(()),
};
let offset = m.parent.import_count(ImportCountType::Global);
for (i, v) in global.entries().iter().enumerate() {
let index = i + offset;
writeln!(w, "GLOBAL_LIST[{}] = {{ value =", index)?;
gen_init_expression(v.init_expr().code(), w)?;
writeln!(w, "}}")?;
}
Ok(())
}
fn gen_element_list(m: &Module, w: Writer) -> Result<()> {
let element = match m.parent.elements_section() {
Some(v) => v.entries(),
None => return Ok(()),
};
for v in element {
writeln!(w, "do")?;
writeln!(w, "local target = TABLE_LIST[{}]", v.index())?;
writeln!(w, "local offset =")?;
gen_init_expression(v.offset().as_ref().unwrap().code(), w)?;
for (i, f) in v.members().iter().enumerate() {
writeln!(w, "target[offset + {}] = FUNC_LIST[{}]", i, f)?;
}
writeln!(w, "end")?;
}
Ok(())
}
fn gen_data_list(m: &Module, w: Writer) -> Result<()> {
let data = match m.parent.data_section() {
Some(v) => v.entries(),
None => return Ok(()),
};
for v in data {
writeln!(w, "do")?;
writeln!(w, "local target = MEMORY_LIST[{}].data", v.index())?;
writeln!(w, "local offset =")?;
gen_init_expression(v.offset().as_ref().unwrap().code(), w)?;
writeln!(w, "/ 4")?;
for (i, b) in v.value().chunks(4).enumerate() {
let mut temp = [0; 4];
temp.iter_mut().zip(b).for_each(|(l, r)| *l = *r);
let value = u32::from_le_bytes(temp);
writeln!(w, "target[offset + {}] = 0x{:X}", i, value)?;
}
writeln!(w, "end")?;
}
Ok(())
}
fn gen_start_point(m: &Module, w: Writer) -> Result<()> {
writeln!(w, "local function run_init_code()")?;
gen_table_list(m, w)?;
gen_memory_list(m, w)?;
gen_global_list(m, w)?;
gen_element_list(m, w)?;
gen_data_list(m, w)?;
writeln!(w, "end")?;
writeln!(w, "return function(wasm)")?;
gen_import_list(m, w)?;
writeln!(w, "run_init_code()")?;
if let Some(start) = m.parent.start_section() {
writeln!(w, "FUNC_LIST[{}]()", start)?;
}
writeln!(w, "return {{")?;
gen_export_list(m, w)?;
writeln!(w, "}} end")
}
fn gen_nil_array(name: &str, len: usize, w: Writer) -> Result<()> {
if len == 0 {
return Ok(());
}
let list = vec!["nil"; len].join(", ");
writeln!(w, "local {} = {{[0] = {}}}", name, list)
}
pub fn translate(spec: &dyn Edition, m: &Module, w: Writer) -> Result<()> {
writeln!(w, "local runtime = require('{}')", spec.runtime())?;
writeln!(w, "{}", RUNTIME_DATA)?;
gen_nil_array("FUNC_LIST", m.in_arity.len(), w)?;
gen_nil_array("TABLE_LIST", m.parent.table_space(), w)?;
gen_nil_array("MEMORY_LIST", m.parent.memory_space(), w)?;
gen_nil_array("GLOBAL_LIST", m.parent.globals_space(), w)?;
let offset = m.ex_arity.len();
for i in 0..m.in_arity.len() {
writeln!(w, "FUNC_LIST[{}] =", i + offset)?;
gen_function(spec, i, m, w)?;
}
gen_start_point(m, w)
}
+8
View File
@@ -0,0 +1,8 @@
// Translation is done in levels.
// Level 1 handles user logic and WASM instructions.
// Level 2 handles setup for functions.
// Level 3 handles initialization of the module.
mod level_1;
mod level_2;
pub mod level_3;
Executable
+147
View File
@@ -0,0 +1,147 @@
use crate::backend::helper::writer::ordered_iter;
use parity_wasm::elements::{
External, FunctionType, ImportEntry, Instruction, Local, Module as WasmModule, Type,
};
use std::{borrow::Cow, convert::TryInto};
pub struct Code<'a> {
pub num_local: u32,
pub inst_list: &'a [Instruction],
var_list: Vec<String>,
}
impl<'a> Code<'a> {
pub fn new(inst_list: &'a [Instruction], num_local: u32) -> Self {
Self {
num_local,
inst_list,
var_list: Vec::new(),
}
}
pub fn local_sum(list: &[Local]) -> u32 {
list.iter().map(Local::count).sum()
}
pub fn var_name_of(&self, index: u32) -> Cow<'_, str> {
let index: usize = index.try_into().unwrap();
let offset = self.var_list.len();
self.var_list
.get(index)
.map_or_else(|| format!("reg_{}", index - offset + 1).into(), Cow::from)
}
pub fn var_range_of(&self, start: u32, len: u32) -> Vec<Cow<'_, str>> {
(start..start + len).map(|i| self.var_name_of(i)).collect()
}
}
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 Module<'a> {
pub ex_arity: Vec<Arity>,
pub in_arity: Vec<Arity>,
pub code: Vec<Code<'a>>,
pub parent: &'a WasmModule,
}
impl<'a> Module<'a> {
pub fn new(parent: &'a WasmModule) -> Self {
let mut module = Module {
in_arity: Self::new_arity_in_list(parent),
ex_arity: Self::new_arity_ex_list(parent),
code: Self::new_function_list(parent),
parent,
};
module.fill_cache();
module
}
fn fill_cache(&mut self) {
for (a, c) in self.in_arity.iter().zip(self.code.iter_mut()) {
c.var_list = ordered_iter("param", a.num_param)
.chain(ordered_iter("var", c.num_local))
.collect();
}
}
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: &WasmModule) -> 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: &WasmModule) -> 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()
}
fn new_function_list(wasm: &WasmModule) -> Vec<Code> {
let bodies = match wasm.code_section() {
Some(b) => b.bodies(),
None => return Vec::new(),
};
bodies
.iter()
.map(|v| {
let num_local = Code::local_sum(v.locals());
Code::new(v.code().elements(), num_local)
})
.collect()
}
}
Executable
+30
View File
@@ -0,0 +1,30 @@
use backend::{
helper::edition::{Edition, LuaJIT, Luau},
translation::level_3,
};
use data::Module;
use parity_wasm::elements::deserialize_file;
mod backend;
mod data;
fn main() {
let mut args = std::env::args().skip(1);
let spec: Box<dyn Edition> = match args.next().as_deref().map(str::to_lowercase).as_deref() {
Some("luau") => Box::new(Luau),
Some("luajit") => Box::new(LuaJIT),
_ => {
println!("expected either 'luau' or 'luajit' option");
return;
}
};
let output = std::io::stdout();
for v in args {
let wasm = deserialize_file(v).unwrap();
let module = Module::new(&wasm);
level_3::translate(spec.as_ref(), &module, &mut output.lock()).unwrap();
}
}