more stuff

This commit is contained in:
Graham Kelly 2024-06-18 23:21:09 -04:00
parent ad1a16d150
commit 6c87f4f9b8
17 changed files with 2055 additions and 203 deletions

913
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
[workspace]
members=[ "wars", "wars-macro","wars-rt", "wars-test"]
members=[ "waffle-func-reloop", "wars", "wars-component", "wars-macro","wars-rt", "wars-test"]
resolver="2"
[workspace.dependencies]

View file

@ -0,0 +1,10 @@
[package]
name = "waffle-func-reloop"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
portal-pc-waffle.workspace = true
relooper = "0.1.0"

View file

@ -0,0 +1,40 @@
use relooper::ShapedBlock;
use waffle::{cfg::CFGInfo, Block, FunctionBody};
pub fn go(b: &FunctionBody) -> Box<ShapedBlock<Block>> {
let cfg = CFGInfo::new(b);
let reloop = std::panic::catch_unwind(|| {
relooper::reloop(
b.blocks
.entries()
.filter(|k| cfg.dominates(b.entry, k.0))
.map(|(k, l)| {
(
k,
l.succs
.iter()
.cloned()
.chain(b.blocks.iter().filter(|x| cfg.dominates(*x, k)))
.collect(),
)
})
// .chain(once((Block::invalid(), vec![b.entry])))
.collect(),
// Block::invalid(),
b.entry,
)
});
let reloop = match reloop {
Ok(a) => a,
Err(e) => {
panic!(
"reloop failure ({}) in {}",
e.downcast_ref::<&str>()
.map(|a| *a)
.unwrap_or("unknown panic"),
b.display("", None)
);
}
};
reloop
}

14
wars-component/Cargo.toml Normal file
View file

@ -0,0 +1,14 @@
[package]
name = "wars-component"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
golem-wasm-ast = "0.2.2"
proc-macro2 = "1.0.85"
quasiquote = "0.1.1"
quote = "1.0.36"
syn = "2.0.66"
wars = { version = "0.1.0", path = "../wars" }

View file

@ -0,0 +1,6 @@
pub trait Component{
}
impl Component for Opts<golem_wasm_ast::Component>{
}

View file

@ -11,9 +11,9 @@ proc-macro2 = "1.0.85"
quote = "1.0.36"
syn = "2.0.66"
wars = { version = "0.1.0", path = "../wars" }
wars-rt = { version = "0.1.0", path = "../wars-rt" }
wat = "1.209.1"
[dev-dependencies]
wars-rt = { version = "0.1.0", path = "../wars-rt" }
[lib]
proc-macro = true

View file

@ -1,17 +1,19 @@
use std::sync::Arc;
use std::{collections::BTreeMap, sync::Arc};
use expander::{Edition, Expander};
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::{parse::Parse, parse_macro_input, Ident, Path, Token};
use wars::Opts;
use syn::{parse::Parse, parse_macro_input, Ident, LitBool, Path, Token};
use wars::{Flags, Opts};
struct O {
pub crate_path: syn::Path,
pub module: Vec<u8>,
pub name: Ident,
// pub cfg: Arc<dyn ImportCfg>,
pub flags: Flags,
pub embed: proc_macro2::TokenStream,
pub data: BTreeMap<Ident, proc_macro2::TokenStream>, // pub cfg: Arc<dyn ImportCfg>,
}
// struct NoopCfg {}
// impl ImportCfg for NoopCfg {
@ -27,6 +29,9 @@ impl Parse for O {
crate_path: syn::parse(quote! {::wars_rt}.into())?,
module: vec![],
name: Ident::new("Wars", Span::call_site()),
flags: Default::default(),
embed: Default::default(),
data: BTreeMap::new(),
// cfg: Arc::new(NoopCfg {}),
};
while input.lookahead1().peek(Ident) {
@ -59,6 +64,22 @@ impl Parse for O {
"name" => {
o.name = input.parse()?;
}
"r#async" => {
let b: LitBool = input.parse()?;
if b.value {
o.flags |= Flags::ASYNC
} else {
o.flags &= Flags::ASYNC.complement();
}
}
"legacy_host" => {
let b: LitBool = input.parse()?;
if b.value {
o.flags |= Flags::LEGACY
} else {
o.flags &= Flags::LEGACY.complement();
}
}
_ => return Err(syn::Error::new(i.span(), "unexpected type")),
};
let _comma: Token![,] = input.parse()?;
@ -73,12 +94,18 @@ impl Parse for O {
#[proc_macro]
pub fn wars(a: TokenStream) -> TokenStream {
let o = parse_macro_input!(a as O);
let x = wars::go(&Opts {
crate_path: o.crate_path,
module: o.module,
name: o.name,
// cfg: o.cfg,
});
let x = wars::go(
&Opts {
crate_path: o.crate_path,
module: o.module,
name: o.name,
flags: o.flags,
embed: o.embed,
data: o.data,
// cfg: o.cfg,
}
.to_mod(),
);
let expanded = Expander::new("wars")
.add_comment("This is generated code!".to_owned())
.fmt(Edition::_2021)
@ -92,3 +119,20 @@ pub fn wars(a: TokenStream) -> TokenStream {
});
expanded.into()
}
// #[proc_macro]
// pub fn wasix(a: TokenStream) -> TokenStream {
// let x = wars::wasix::wasix();
// let expanded = Expander::new("wars_wasix")
// .add_comment("This is generated code!".to_owned())
// .fmt(Edition::_2021)
// .verbose(true)
// // common way of gating this, by making it part of the default feature set
// .dry(cfg!(feature = "no-file-expansion"))
// .write_to_out_dir(x.clone())
// .unwrap_or_else(|e| {
// eprintln!("Failed to write to file: {:?}", e);
// x
// });
// expanded.into()
// }

View file

@ -1,7 +1,9 @@
wars_macro::wars!(
file = "target/wasm32-unknown-unknown/debug/wars_test.wasm",
r#async = true,
);
fn x<A: Wars>(){
}

View file

@ -12,3 +12,5 @@ derive_more = "0.99.17"
paste = "1.0.15"
tramp = "0.3.0"
tuple_list = "0.1.3"
wars-macro = { version = "0.1.0", path = "../wars-macro" }
wasm_runtime_layer = "0.4.0"

View file

@ -2,6 +2,10 @@ use std::sync::Arc;
use anyhow::Context;
use tramp::{tramp, BorrowRec, Thunk};
pub mod unsync;
pub fn ret<'a,T>(a: T) -> BorrowRec<'a,T>{
BorrowRec::Ret(a)
}
pub trait CtxSpec: Sized {
type ExternRef: Clone;
}
@ -16,7 +20,7 @@ pub enum Value<C: CtxSpec> {
&'a mut C,
Vec<Value<C>>,
) -> tramp::BorrowRec<'a, anyhow::Result<Vec<Value<C>>>>
+ 'static,
+ Send + Sync + 'static,
>,
),
Null,
@ -140,13 +144,13 @@ pub fn map_rec<'a, T: 'a, U>(
}
}
pub type Df<A, B, C> =
Arc<dyn for<'a> Fn(&'a mut C, A) -> tramp::BorrowRec<'a, anyhow::Result<B>> + 'static>;
Arc<dyn for<'a> Fn(&'a mut C, A) -> tramp::BorrowRec<'a, anyhow::Result<B>> + Send + Sync + 'static>;
pub fn da<
A,
B,
C,
F: for<'a> Fn(&'a mut C, A) -> tramp::BorrowRec<'a, anyhow::Result<B>> + 'static,
F: for<'a> Fn(&'a mut C, A) -> tramp::BorrowRec<'a, anyhow::Result<B>> + Send + Sync + 'static,
>(
f: F,
) -> Df<A,B,C> {

207
wars-rt/src/func/unsync.rs Normal file
View file

@ -0,0 +1,207 @@
use std::{future::Future, pin::Pin, sync::Arc};
use anyhow::Context;
// use tramp::{tramp, BorrowRec, Thunk};
pub fn ret<'a,T>(a: T) -> AsyncRec<'a,T>{
AsyncRec::Ret(a)
}
pub enum AsyncRec<'a,T>{
Ret(T),
Async(Pin<Box<dyn Future<Output = AsyncRec<'a,T>> + Send + Sync + 'a>>)
}
impl<'a,T> AsyncRec<'a,T>{
pub async fn go(mut self) -> T{
loop{
self = match self{
AsyncRec::Ret(r) => return r,
AsyncRec::Async(a) => a.await,
}
}
}
}
pub trait CtxSpec: Sized {
type ExternRef: Clone;
}
pub enum Value<C: CtxSpec> {
I32(u32),
I64(u64),
F32(f32),
F64(f64),
FunRef(
Arc<
dyn for<'a> Fn(
&'a mut C,
Vec<Value<C>>,
) -> AsyncRec<'a, anyhow::Result<Vec<Value<C>>>>
+ Send + Sync + 'static,
>,
),
Null,
ExRef(C::ExternRef),
}
pub fn call_ref<'a, A: CoeVec<C> + 'static, B: CoeVec<C> + 'static, C: CtxSpec + 'static>(
ctx: &'a mut C,
go: Df<A,B,C>,
a: A,
) -> AsyncRec<'a, anyhow::Result<B>> {
// let go: Df<A, B, C> = cast(go);
go(ctx, a)
}
impl<C: CtxSpec> Clone for Value<C> {
fn clone(&self) -> Self {
match self {
Self::I32(arg0) => Self::I32(arg0.clone()),
Self::I64(arg0) => Self::I64(arg0.clone()),
Self::F32(arg0) => Self::F32(arg0.clone()),
Self::F64(arg0) => Self::F64(arg0.clone()),
Self::FunRef(arg0) => Self::FunRef(arg0.clone()),
Self::Null => Self::Null,
Self::ExRef(e) => Self::ExRef(e.clone()),
}
}
}
pub trait Coe<C: CtxSpec>: Sized {
fn coe(self) -> Value<C>;
fn uncoe(x: Value<C>) -> anyhow::Result<Self>;
}
pub fn cast<A: Coe<C> + 'static, B: Coe<C> + 'static, C: CtxSpec>(a: A) -> B {
let a = match castaway::cast!(a, B) {
Ok(b) => return b,
Err(a) => a,
};
B::uncoe(A::coe(a)).unwrap()
}
impl<C: CtxSpec> Coe<C> for Value<C> {
fn coe(self) -> Value<C> {
self
}
fn uncoe(x: Value<C>) -> anyhow::Result<Self> {
Ok(x)
}
}
impl<C: CtxSpec, D: Coe<C>> Coe<C> for Option<D> {
fn coe(self) -> Value<C> {
match self {
None => Value::Null,
Some(d) => d.coe(),
}
}
fn uncoe(x: Value<C>) -> anyhow::Result<Self> {
if let Value::Null = x {
return Ok(None);
}
return Ok(Some(D::uncoe(x)?));
}
}
macro_rules! coe_impl_prim {
($a:tt in $b:ident) => {
impl<C: CtxSpec> Coe<C> for $a {
fn coe(self) -> Value<C> {
Value::$b(self)
}
fn uncoe(x: Value<C>) -> anyhow::Result<Self> {
match x {
Value::$b(a) => Ok(a),
_ => anyhow::bail!("invalid type"),
}
}
}
};
}
coe_impl_prim!(u32 in I32);
coe_impl_prim!(u64 in I64);
coe_impl_prim!(f32 in F32);
coe_impl_prim!(f64 in F64);
pub trait CoeVec<C: CtxSpec>: Sized {
fn coe(self) -> Vec<Value<C>>;
fn uncoe(a: Vec<Value<C>>) -> anyhow::Result<Self>;
}
impl<C: CtxSpec> CoeVec<C> for () {
fn coe(self) -> Vec<Value<C>> {
vec![]
}
fn uncoe(a: Vec<Value<C>>) -> anyhow::Result<Self> {
Ok(())
}
}
impl<C: CtxSpec, A: Coe<C>, B: CoeVec<C>> CoeVec<C> for (A, B) {
fn coe(self) -> Vec<Value<C>> {
let mut a = self.1.coe();
a.push(self.0.coe());
return a;
}
fn uncoe(mut a: Vec<Value<C>>) -> anyhow::Result<Self> {
let Some(x) = a.pop() else {
anyhow::bail!("list too small")
};
let y = A::uncoe(x).context("invalid item (note coe lists are REVERSED)")?;
let z = B::uncoe(a)?;
Ok((y, z))
}
}
pub fn map_rec<'a, T: 'a, U>(
r: AsyncRec<'a, T>,
go: impl FnOnce(T) -> U + Send + Sync + 'a,
) -> AsyncRec<'a, U> {
match r {
AsyncRec::Ret(x) => AsyncRec::Ret(go(x)),
AsyncRec::Async(a) => AsyncRec::Async(Box::pin(async move{
let v = a.await;
map_rec(v, go)
})),
}
}
pub type Df<A, B, C> =
Arc<dyn for<'a> Fn(&'a mut C, A) -> AsyncRec<'a, anyhow::Result<B>> + Send + Sync + 'static>;
pub fn da<
A,
B,
C,
F: for<'a> Fn(&'a mut C, A) -> AsyncRec<'a, anyhow::Result<B>> + Send + Sync + 'static,
>(
f: F,
) -> Df<A,B,C> {
Arc::new(f)
}
impl<C: CtxSpec + 'static, A: CoeVec<C> + 'static, B: CoeVec<C> + 'static> Coe<C> for Df<A, B, C> {
fn coe(self) -> Value<C> {
pub fn x<
C: CtxSpec,
T: for<'a> Fn(
&'a mut C,
Vec<Value<C>>,
) -> AsyncRec<'a, anyhow::Result<Vec<Value<C>>>>
+ 'static,
>(
a: T,
) -> T {
return a;
}
Value::FunRef(Arc::new(x(move |ctx, x| {
let x = match A::uncoe(x) {
Ok(x) => x,
Err(e) => return AsyncRec::Ret(Err(e)),
};
let x = self(ctx, x);
map_rec(x, |a| a.map(|b| b.coe()))
})))
}
fn uncoe(x: Value<C>) -> anyhow::Result<Self> {
let Value::FunRef(x) = x else {
anyhow::bail!("invalid value")
};
Ok(Arc::new(move |ctx, a| {
let v = a.coe();
let v = x(ctx, v);
map_rec(v, |a| a.and_then(B::uncoe))
}))
}
}

View file

@ -1,7 +1,85 @@
pub mod func;
pub mod wrl;
pub mod wasix;
use std::sync::{Arc, Mutex};
// use as_ref::AsSlice;
use func::CtxSpec;
pub use func::Value;
pub trait Memory {
fn read<'a>(&'a self, a: usize, s: usize) -> anyhow::Result<Box<dyn AsRef<[u8]> + 'a>>;
fn write(&mut self, a: usize, x: &[u8]) -> anyhow::Result<()>;
fn size(&self) -> anyhow::Result<usize>;
fn grow(&mut self, x: usize) -> anyhow::Result<()>;
}
impl Memory for Vec<u8> {
fn read<'a>(&'a self, a: usize, s: usize) -> anyhow::Result<Box<dyn AsRef<[u8]> + 'a>>{
Ok(Box::new(&self[a..][..s]))
}
fn write(&mut self, a: usize, x: &[u8]) -> anyhow::Result<()> {
self[a..][..x.len()].copy_from_slice(x);
Ok(())
}
fn size(&self) -> anyhow::Result<usize> {
Ok(self.len())
}
fn grow(&mut self, x: usize) -> anyhow::Result<()> {
self.extend((0..x).map(|a| 0u8));
Ok(())
}
}
impl<T: Memory> Memory for Arc<Mutex<T>>{
fn read<'a>(&'a self, a: usize, s: usize) -> anyhow::Result<Box<dyn AsRef<[u8]> + 'a>> {
let l = self.lock().unwrap();
let r = l.read(a, s)?;
return Ok(Box::new(r.as_ref().as_ref().to_vec()));
}
fn write(&mut self, a: usize, x: &[u8]) -> anyhow::Result<()> {
let mut l = self.lock().unwrap();
return l.write(a, x);
}
fn size(&self) -> anyhow::Result<usize> {
let l = self.lock().unwrap();
return l.size();
}
fn grow(&mut self, x: usize) -> anyhow::Result<()> {
let mut l = self.lock().unwrap();
return l.grow(x)
}
}
pub unsafe fn host_memory() -> impl Memory{
struct W{}
impl Memory for W{
fn read<'a>(&'a self, a: usize, s: usize) -> anyhow::Result<Box<dyn AsRef<[u8]> + 'a>> {
return Ok(Box::new(unsafe{
std::slice::from_raw_parts(a as *const u8, s)
}))
}
fn write(&mut self, a: usize, x: &[u8]) -> anyhow::Result<()> {
let n = unsafe{
std::slice::from_raw_parts_mut(a as *mut u8, x.len())
};
n.copy_from_slice(x);
return Ok(());
}
fn size(&self) -> anyhow::Result<usize> {
anyhow::bail!("host memory cannot use size")
}
fn grow(&mut self, x: usize) -> anyhow::Result<()> {
anyhow::bail!("host memory cannot use grow")
}
}
return W{};
}
pub mod _rexport {
pub use anyhow;
pub use tramp;
@ -135,59 +213,61 @@ macro_rules! int_ty{
Ok(tuple_list::tuple_list!(a.wrapping_sub(b)))
}
//LOADS and STORES
pub fn [<$p load>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = &a[b.try_into()?..][..std::mem::size_of::<$int>()];
Ok(tuple_list::tuple_list!($int::from_ne_bytes(r.try_into()?)))
pub fn [<$p load>]<T: TryInto<usize>,M: Memory + ?Sized>(a: &mut M, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = a.read(b.try_into()?,std::mem::size_of::<$int>())?;
Ok(tuple_list::tuple_list!($int::from_ne_bytes(r.as_ref().as_ref().try_into()?)))
}
pub fn [<$p store>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T, c: $int) -> anyhow::Result<()> where T::Error: std::error::Error + Send + Sync + 'static{
let mut r = &mut a[b.try_into()?..][..std::mem::size_of::<$int>()];
r.copy_from_slice(&c.to_ne_bytes());
pub fn [<$p store>]<T: TryInto<usize>,M: Memory + ?Sized>(a: &mut M, b: T, c: $int) -> anyhow::Result<()> where T::Error: std::error::Error + Send + Sync + 'static{
// let mut r = &mut a[b.try_into()?..][..std::mem::size_of::<$int>()];
// r.copy_from_slice(&c.to_ne_bytes());
a.write(b.try_into()?,&c.to_ne_bytes())?;
Ok(())
}
//8 BIT
pub fn [<$p load8u>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = a[b.try_into()?..][0];
pub fn [<$p load8u>]<T: TryInto<usize>,M: Memory + ?Sized>(a: &mut M, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = a.read(b.try_into()?,1)?.as_ref().as_ref()[0];
Ok(tuple_list::tuple_list!(r as $int))
}
pub fn [<$p load8s>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = a[b.try_into()?..][0];
pub fn [<$p load8s>]<T: TryInto<usize>,M: Memory + ?Sized>(a: &mut M, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = a.read(b.try_into()?,1)?.as_ref().as_ref()[0];
Ok(tuple_list::tuple_list!(r as i8 as $p as $int))
}
pub fn [<$p store8>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T, c: $int) -> anyhow::Result<()> where T::Error: std::error::Error + Send + Sync + 'static{
let mut r = &mut a[b.try_into()?..][..1];
r[0] = (c & 0xff) as u8;
pub fn [<$p store8>]<T: TryInto<usize>,M: Memory + ?Sized>(a: &mut M, b: T, c: $int) -> anyhow::Result<()> where T::Error: std::error::Error + Send + Sync + 'static{
// let mut r = &mut a[b.try_into()?..][..1];
// r[0] = (c & 0xff) as u8;
a.write(b.try_into()?,&[(c & 0xff) as u8])?;
Ok(())
}
//16 BIT
pub fn [<$p load16u>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = &a[b.try_into()?..][..2];
let r = u16::from_ne_bytes(r.try_into()?);
pub fn [<$p load16u>]<T: TryInto<usize>,M: Memory + ?Sized>(a: &mut M, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = a.read(b.try_into()?,2)?;
let r = u16::from_ne_bytes(r.as_ref().as_ref().try_into()?);
Ok(tuple_list::tuple_list!(r as $int))
}
pub fn [<$p load16s>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = &a[b.try_into()?..][..2];
let r = u16::from_ne_bytes(r.try_into()?);
pub fn [<$p load16s>]<T: TryInto<usize>,M: Memory + ?Sized>(a: &mut M, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = a.read(b.try_into()?,2)?;
let r = u16::from_ne_bytes(r.as_ref().as_ref().try_into()?);
Ok(tuple_list::tuple_list!(r as i16 as $p as $int))
}
pub fn [<$p store16>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T, c: $int) -> anyhow::Result<()> where T::Error: std::error::Error + Send + Sync + 'static{
let mut r = &mut a[b.try_into()?..][..2];
r.copy_from_slice(&((c & 0xff) as u16).to_ne_bytes());
pub fn [<$p store16>]<T: TryInto<usize>,M: Memory + ?Sized>(a: &mut M, b: T, c: $int) -> anyhow::Result<()> where T::Error: std::error::Error + Send + Sync + 'static{
// let mut r = &mut a[b.try_into()?..][..2];
a.write(b.try_into()?,&((c & 0xffff) as u16).to_ne_bytes())?;
Ok(())
}
//32 BIT
pub fn [<$p load32u>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = &a[b.try_into()?..][..4];
let r = u32::from_ne_bytes(r.try_into()?);
pub fn [<$p load32u>]<T: TryInto<usize>,M: Memory + ?Sized>(a: &mut M, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = a.read(b.try_into()?,4)?;
let r = u32::from_ne_bytes(r.as_ref().as_ref().try_into()?);
Ok(tuple_list::tuple_list!(r as $int))
}
pub fn [<$p load32s>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = &a[b.try_into()?..][..4];
let r = u32::from_ne_bytes(r.try_into()?);
pub fn [<$p load32s>]<T: TryInto<usize>,M: Memory + ?Sized>(a: &mut M, b: T) -> anyhow::Result<tuple_list::tuple_list_type!($int)> where T::Error: std::error::Error + Send + Sync + 'static{
let r = a.read(b.try_into()?,4)?;
let r = u32::from_ne_bytes(r.as_ref().as_ref().try_into()?);
Ok(tuple_list::tuple_list!(r as i32 as $p as $int))
}
pub fn [<$p store32>]<T: TryInto<usize>>(a: &mut Vec<u8>, b: T, c: $int) -> anyhow::Result<()> where T::Error: std::error::Error + Send + Sync + 'static{
let mut r = &mut a[b.try_into()?..][..4];
r.copy_from_slice(&((c & 0xffffff) as u32).to_ne_bytes());
pub fn [<$p store32>]<T: TryInto<usize>,M: Memory + ?Sized>(a: &mut M, b: T, c: $int) -> anyhow::Result<()> where T::Error: std::error::Error + Send + Sync + 'static{
// let mut r = &mut a[b.try_into()?..][..4];
a.write(b.try_into()?,&((c & 0xffffffff) as u32).to_ne_bytes())?;
Ok(())
}
}
@ -200,4 +280,4 @@ pub fn select<T>(u: u32, t: T, t2: T) -> anyhow::Result<tuple_list::tuple_list_t
}
pub fn i32wrapi64(a: u64) -> anyhow::Result<tuple_list::tuple_list_type!(u32)> {
return Ok(tuple_list::tuple_list!((a & 0xffffffff) as u32));
}
}

0
wars-rt/src/wasix.rs Normal file
View file

135
wars-rt/src/wrl.rs Normal file
View file

@ -0,0 +1,135 @@
use std::sync::Arc;
use wasm_runtime_layer::{AsContextMut, ExternRef, FuncType};
use crate::func::CtxSpec;
#[derive(Clone)]
pub enum MetaType {
I32,
I64,
F32,
F64,
ExternRef,
FunRef {
params: Vec<MetaType>,
returns: Vec<MetaType>,
},
}
impl MetaType {
fn wrl(&self) -> wasm_runtime_layer::ValueType {
match self {
MetaType::I32 => wasm_runtime_layer::ValueType::I32,
MetaType::I64 => wasm_runtime_layer::ValueType::I64,
MetaType::F32 => wasm_runtime_layer::ValueType::F32,
MetaType::F64 => wasm_runtime_layer::ValueType::F64,
MetaType::ExternRef => wasm_runtime_layer::ValueType::ExternRef,
MetaType::FunRef { params, returns } => wasm_runtime_layer::ValueType::FuncRef,
}
}
}
pub fn translate_in<C: CtxSpec + AsContextMut<UserState = D> + 'static, D: AsMut<C>>(
val: &crate::func::Value<C>,
ctx: &mut C,
wrl_ty: &MetaType,
) -> anyhow::Result<wasm_runtime_layer::Value>
where
C::ExternRef: Send + Sync + 'static,
{
Ok(match val {
crate::Value::I32(a) => wasm_runtime_layer::Value::I32(*a as i32),
crate::Value::I64(a) => wasm_runtime_layer::Value::I64(*a as i64),
crate::Value::F32(a) => wasm_runtime_layer::Value::F32(*a),
crate::Value::F64(a) => wasm_runtime_layer::Value::F64(*a),
crate::Value::FunRef(f) => {
// let wasm_runtime_layer::ValueType::FuncRef()
let MetaType::FunRef { params, returns } = wrl_ty.clone() else {
unreachable!()
};
let ty = FuncType::new(
params.iter().map(|a| a.wrl()),
returns.iter().map(|a| a.wrl()),
);
let f = f.clone();
wasm_runtime_layer::Value::FuncRef(Some(wasm_runtime_layer::Func::new(
ctx,
ty,
move |mut ctx, args, rets| {
let args2 = args
.iter()
.zip(params.iter())
.rev()
.map(|(x, y)| translate_out(x, ctx.data_mut().as_mut(), y))
.collect::<anyhow::Result<Vec<_>>>()?;
let v = tramp::tramp(f(ctx.data_mut().as_mut(), args2))?;
for ((w, v), t) in v.iter().rev().zip(rets.iter_mut()).zip(returns.iter()) {
*v = translate_in(w, ctx.data_mut().as_mut(), t)?;
}
Ok(())
},
)))
}
crate::Value::Null => match wrl_ty{
MetaType::ExternRef => wasm_runtime_layer::Value::ExternRef(None),
MetaType::FunRef { params, returns } => wasm_runtime_layer::Value::FuncRef(None),
_ => anyhow::bail!("invalid null")
},
crate::Value::ExRef(e) => {
wasm_runtime_layer::Value::ExternRef(Some(ExternRef::new(ctx, e.clone())))
}
})
}
pub fn translate_out<C: CtxSpec + AsContextMut<UserState = D> + 'static, D: AsMut<C>>(
val: &wasm_runtime_layer::Value,
ctx: &mut C,
wrl_ty: &MetaType,
) -> anyhow::Result<crate::func::Value<C>>
where
C::ExternRef: Send + Sync + 'static,
{
Ok(match val {
wasm_runtime_layer::Value::I32(i) => crate::Value::I32(*i as u32),
wasm_runtime_layer::Value::I64(i) => crate::Value::I64(*i as u64),
wasm_runtime_layer::Value::F32(f) => crate::Value::F32(*f),
wasm_runtime_layer::Value::F64(f) => crate::Value::F64(*f),
wasm_runtime_layer::Value::FuncRef(f) => match f {
None => crate::Value::Null,
Some(a) => {
let a = a.clone();
let MetaType::FunRef { params, returns } = wrl_ty.clone() else {
unreachable!()
};
crate::Value::FunRef(Arc::new(move |ctx, args| {
let args_in: anyhow::Result<Vec<_>> = args
.iter()
.rev()
.zip(params.iter())
.map(|(x, y)| translate_in(x, &mut *ctx, y))
.collect();
let mut results =
vec![wasm_runtime_layer::Value::I32(0); a.ty(&mut *ctx).results().len()];
let args_in = match args_in {
Ok(a) => a,
Err(e) => return tramp::BorrowRec::Ret(Err(e)),
};
tramp::BorrowRec::Ret(match a.call(&mut *ctx, &args_in, &mut results) {
Err(e) => Err(e),
Ok(_) => results
.iter()
.zip(returns.iter())
.rev()
.map(|(x, y)| translate_out(x, ctx, y))
.collect(),
})
}))
}
},
wasm_runtime_layer::Value::ExternRef(x) => match x
.as_ref()
.and_then(|a| a.downcast::<C::ExternRef, _, _>(ctx.as_context()).ok())
{
None => crate::Value::Null,
Some(x) => crate::Value::ExRef(x.clone()),
},
})
}

View file

@ -6,9 +6,13 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bitflags = "2.5.0"
portal-pc-waffle.workspace = true
proc-macro2 = "1.0.85"
quasiquote = "0.1.1"
quote = "1.0.36"
relooper = "0.1.0"
syn = "2.0.66"
waffle-func-reloop = { version = "0.1.0", path = "../waffle-func-reloop" }
witx = {git="https://github.com/wasix-org/wasix-witx.git",branch="main"}

View file

@ -1,14 +1,25 @@
use std::{convert::Infallible, iter::once, sync::Arc};
use std::{collections::BTreeMap, convert::Infallible, iter::once, sync::Arc};
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use quasiquote::quasiquote;
use quote::{format_ident, quote, ToTokens};
use relooper::{reloop, BranchMode, ShapedBlock};
use syn::{Ident, Lifetime};
use waffle::{
cfg::CFGInfo, entity::EntityRef, Block, BlockTarget, ExportKind, Func, ImportKind, Memory,
Module, Operator, Signature, SignatureData, Type, Value,
cfg::CFGInfo, entity::EntityRef, Block, BlockTarget, Export, ExportKind, Func, ImportKind, Memory, Module, Operator, Signature, SignatureData, Type, Value
};
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Flags: u32{
const HOST_MEMORY = 0x1;
const ASYNC = 0x2;
const LEGACY = 0x4;
// const WASIX = 0x8;
// const UNSANDBOXED = 0x2;
}
}
pub mod unswitch;
// pub mod wasix;
pub fn mangle_value(a: Value, b: usize) -> Ident {
if b == 0 {
format_ident!("{a}")
@ -32,20 +43,123 @@ pub fn bindname(a: &str) -> String {
// }
pub const INTRINSIC: &'static str = "wars_intrinsic/";
impl Opts<Module<'static>> {
pub fn fp(&self) -> TokenStream {
let root = self.crate_path.clone();
if self.flags.contains(Flags::ASYNC) {
quote! {
#root::func::unsync
}
} else {
quote! {
#root::func
}
}
}
pub fn mem(&self, m: Memory) -> TokenStream {
if let Some(i) = self
.module
.imports
.iter()
.find(|x| x.kind == ImportKind::Memory(m))
{
if i.module == "!!unsafe" && i.name == "host" && self.flags.contains(Flags::HOST_MEMORY)
{
return quote! {
unsafe{
::std::slice::from_raw_parts_mut(::std::ptr::null(),usize::MAX)
}
};
}
}
let m2 = format_ident!("{m}");
quote! {
ctx.#m2()
}
}
pub fn import(
&self,
module: &str,
name: &str,
params: impl Iterator<Item = TokenStream>,
mut params: impl Iterator<Item = TokenStream>,
) -> TokenStream {
let root = self.crate_path.clone();
let Some(a) = module.strip_prefix(INTRINSIC) else {
let id = format_ident!("{}_{}", bindname(module), bindname(name));
return quote! {
ctx.#id(#(#params),*)
};
// if self.flags.contains(Flags::UNSANDBOXED) {
if self.flags.contains(Flags::HOST_MEMORY) {
if module == "wars/memory/host" {
match name {
_ => {}
}
}
}
// if a == "fs" {
// match name {
// "open" => {
// let p0 = params.next().unwrap();
// let l = params.next().unwrap();
// return quote! {
// #root::_rexport::tramp::BorrowRec::Ret({
// let f = #p0;
// let l = #l;
// let f = ::std::str::from_utf8(&ctx.memory()[(f as usize)..][..(l as usize)]);
// let f = match f{
// Ok(a) => a,
// Err(e) => return #root::_rexport::tramp::BorrowRec::Ret(Er(e.into()));
// };
// let f = ::std::fs::open(f);
// let f = match f{
// Ok(a) => alloc(&mut ctx.data().files,::std::sync::Arc::new(a)) * 2,
// Err(e) => alloc(&mut ctx.data().io_errors,::std::sync::Arc::new(e)) * 2 + 1;
// };
// f
// })
// };
// }
// "read" => {
// let fd = params.next().unwrap();
// let p0 = params.next().unwrap();
// let l = params.next().unwrap();
// return quote! {
// {
// let f = #p0;
// let l = #l;
// let fd = #fd;
// let fd = ctx.data().files.get(&fd).unwrap().clone();
// let f = &mut ctx.memory()[(f as usize)..][..(l as usize)];
// let f = ::std::io::Read::read(&mut fd.as_ref(),f);
// match f{
// Ok(a) => #root::_rexport::tuple_list::tuple_list!(a as u64 * 2),
// Err(e) => #root::_rexport::tuple_list::tuple_list!(alloc(&mut ctx.data().io_errors,::std::sync::Arc::new(e)) as u64 * 2 + 1);
// }
// }
// };
// }
// "write" => {
// let fd = params.next().unwrap();
// let p0 = params.next().unwrap();
// let l = params.next().unwrap();
// return quote! {
// {
// let f = #p0;
// let l = #l;
// let fd = #fd;
// let fd = ctx.data().files.get(&fd).unwrap().clone();
// let f = &ctx.memory()[(f as usize)..][..(l as usize)];
// let f = ::std::io::Write::write(&mut fd.as_ref(),f);
// match f{
// Ok(a) => #root::_rexport::tuple_list::tuple_list!(a as u64 * 2),
// Err(e) => #root::_rexport::tuple_list::tuple_list!(alloc(&mut ctx.data().io_errors,::std::sync::Arc::new(e)) as u64 * 2 + 1);
// }
// }
// };
// }
// _ => {}
// }
// }
// }
let id = format_ident!("{}_{}", bindname(module), bindname(name));
return quote! {
ctx.#id(#(#params),*)
};
quote! {}
}
pub fn render_ty(&self, ctx: &TokenStream, ty: Type) -> TokenStream {
let root = self.crate_path.clone();
@ -62,8 +176,14 @@ impl Opts<Module<'static>> {
let data = &self.module.signatures[sig_index];
let params = data.params.iter().map(|x| self.render_ty(ctx, *x));
let returns = data.returns.iter().map(|x| self.render_ty(ctx, *x));
let mut x = quote! {
#root::func::Df<#root::_rexport::tuple_list::tuple_list_type!(#(#params),*),#root::_rexport::tuple_list::tuple_list_type!(#(#returns),*),#ctx>
let mut x = if self.flags.contains(Flags::ASYNC) {
quote! {
#root::func::unsync::Df<#root::_rexport::tuple_list::tuple_list_type!(#(#params),*),#root::_rexport::tuple_list::tuple_list_type!(#(#returns),*),#ctx>
}
} else {
quote! {
#root::func::Df<#root::_rexport::tuple_list::tuple_list_type!(#(#params),*),#root::_rexport::tuple_list::tuple_list_type!(#(#returns),*),#ctx>
}
};
if nullable {
x = quote! {
@ -72,7 +192,7 @@ impl Opts<Module<'static>> {
}
x
}
_ => quote! {#root::func::Value<#ctx>},
_ => quasiquote! {#{self.fp()}::Value<#ctx>},
}
}
pub fn render_generics(&self, ctx: &TokenStream, data: &SignatureData) -> TokenStream {
@ -100,23 +220,28 @@ impl Opts<Module<'static>> {
.enumerate()
.map(|(a, _)| format_ident!("p{a}"));
let returns = data.returns.iter().map(|x| self.render_ty(&ctx, *x));
quote! {
fn #name<'a,C: #base + 'static>(ctx: &'a mut C, #root::_rexport::tuple_list::tuple_list!(#(#param_ids),*): #root::_rexport::tuple_list::tuple_list_type!(#(#params),*)) -> #root::_rexport::tramp::BorrowRec<'a,#root::_rexport::anyhow::Result<#root::_rexport::tuple_list::tuple_list_type!(#(#returns),*)>>
if self.flags.contains(Flags::ASYNC) {
quote! {
fn #name<'a,C: #base + 'static>(ctx: &'a mut C, #root::_rexport::tuple_list::tuple_list!(#(#param_ids),*): #root::_rexport::tuple_list::tuple_list_type!(#(#params),*)) -> #root::func::unsync::AsyncRec<'a,#root::_rexport::anyhow::Result<#root::_rexport::tuple_list::tuple_list_type!(#(#returns),*)>>
}
} else {
quote! {
fn #name<'a,C: #base + 'static>(ctx: &'a mut C, #root::_rexport::tuple_list::tuple_list!(#(#param_ids),*): #root::_rexport::tuple_list::tuple_list_type!(#(#params),*)) -> #root::_rexport::tramp::BorrowRec<'a,#root::_rexport::anyhow::Result<#root::_rexport::tuple_list::tuple_list_type!(#(#returns),*)>>
}
}
}
pub fn render_fun_ref(&self, ctx: &TokenStream, x: Func) -> TokenStream {
let root = self.crate_path.clone();
if x.is_invalid() {
return quote! {
#root::func::da::<(),(),C,_>(|ctx,arg|panic!("invalid func"))
return quasiquote! {
#{self.fp()}::da::<(),(),C,_>(|ctx,arg|panic!("invalid func"))
};
}
let generics =
self.render_generics(ctx, &self.module.signatures[self.module.funcs[x].sig()]);
let x = format_ident!("{x}");
quote! {
#root::func::da::<#generics,C,_>(|ctx,arg|#x(ctx,arg))
quasiquote! {
#{self.fp()}::da::<#generics,C,_>(|ctx,arg|#x(ctx,arg))
}
}
pub fn render_self_sig(
@ -138,10 +263,17 @@ impl Opts<Module<'static>> {
.collect::<Vec<_>>();
let returns = data.returns.iter().map(|x| self.render_ty(&ctx, *x));
quote! {
fn #name<'a>(self: &'a mut Self, #root::_rexport::tuple_list::tuple_list!(#(#param_ids),*): #root::_rexport::tuple_list::tuple_list_type!(#(#params),*)) -> #root::_rexport::tramp::BorrowRec<'a,#root::_rexport::anyhow::Result<#root::_rexport::tuple_list::tuple_list_type!(#(#returns),*)>> where Self: 'static{
return #wrapped(self,#root::_rexport::tuple_list::tuple_list!(#(#param_ids),*));
if self.flags.contains(Flags::ASYNC) {
quote! {
fn #name<'a>(self: &'a mut Self, #root::_rexport::tuple_list::tuple_list!(#(#param_ids),*): #root::_rexport::tuple_list::tuple_list_type!(#(#params),*)) -> #root::func::unsync::AsyncRec<'a,#root::_rexport::anyhow::Result<#root::_rexport::tuple_list::tuple_list_type!(#(#returns),*)>> where Self: 'static{
return #wrapped(self,#root::_rexport::tuple_list::tuple_list!(#(#param_ids),*));
}
}
} else {
quote! {
fn #name<'a>(self: &'a mut Self, #root::_rexport::tuple_list::tuple_list!(#(#param_ids),*): #root::_rexport::tuple_list::tuple_list_type!(#(#params),*)) -> #root::_rexport::tramp::BorrowRec<'a,#root::_rexport::anyhow::Result<#root::_rexport::tuple_list::tuple_list_type!(#(#returns),*)>> where Self: 'static{
return #wrapped(self,#root::_rexport::tuple_list::tuple_list!(#(#param_ids),*));
}
}
}
}
@ -159,9 +291,14 @@ impl Opts<Module<'static>> {
.collect::<Vec<_>>();
let returns = data.returns.iter().map(|x| self.render_ty(&ctx, *x));
quote! {
fn #name<'a>(self: &'a mut Self, #root::_rexport::tuple_list::tuple_list!(#(#param_ids),*): #root::_rexport::tuple_list::tuple_list_type!(#(#params),*)) -> #root::_rexport::tramp::BorrowRec<'a,#root::_rexport::anyhow::Result<#root::_rexport::tuple_list::tuple_list_type!(#(#returns),*)>>;
if self.flags.contains(Flags::ASYNC) {
quote! {
fn #name<'a>(self: &'a mut Self, #root::_rexport::tuple_list::tuple_list!(#(#param_ids),*): #root::_rexport::tuple_list::tuple_list_type!(#(#params),*)) -> #root::func::unsync::AsyncRec<'a,#root::_rexport::anyhow::Result<#root::_rexport::tuple_list::tuple_list_type!(#(#returns),*)>>;
}
} else {
quote! {
fn #name<'a>(self: &'a mut Self, #root::_rexport::tuple_list::tuple_list!(#(#param_ids),*): #root::_rexport::tuple_list::tuple_list_type!(#(#params),*)) -> #root::_rexport::tramp::BorrowRec<'a,#root::_rexport::anyhow::Result<#root::_rexport::tuple_list::tuple_list_type!(#(#returns),*)>>;
}
}
}
pub fn render_relooped_block(&self, f: Func, x: &ShapedBlock<Block>) -> TokenStream {
@ -228,6 +365,7 @@ impl Opts<Module<'static>> {
#next;
};
}
let fp = self.fp();
let stmts = b.blocks[stmts].params.iter().map(|a|&a.1).chain(b.blocks[stmts].insts.iter()).map(|a|{
let av = b.values[*a].tys(&b.type_pool).iter().enumerate().map(|b|mangle_value(*a,b.0));
let b = match &b.values[*a]{
@ -251,10 +389,21 @@ impl Opts<Module<'static>> {
Some(_) => {
let func = format_ident!("{function_index}");
let vals = vals.iter().map(|a|format_ident!("{a}"));
quote! {
match #root::_rexport::tramp::tramp(#func(ctx,#root::_rexport::tuple_list::tuple_list!(#(#root::func::cast::<_,_,C>(#vals .clone())),*))){
Ok(a) => a,
Err(e) => return #root::_rexport::tramp::BorrowRec::Ret(Err(e))
quasiquote! {
{
let x = #func(ctx,#root::_rexport::tuple_list::tuple_list!(#(#fp::cast::<_,_,C>(#vals .clone())),*));
match #{if self.flags.contains(Flags::ASYNC){
quote!{
x.go().await
}
}else{
quote!{
#root::_rexport::tramp::tramp(x)
}
}}{
Ok(a) => a,
Err(e) => return #{self.fp()}::ret(Err(e))
}
}
}
},
@ -270,10 +419,18 @@ impl Opts<Module<'static>> {
i.name.as_str(),
vals.iter().map(|a|format_ident!("{a}")).map(|a| quote! {#a}),
);
quote!{
match #root::_rexport::tramp::tramp(x){
quasiquote!{
match #{if self.flags.contains(Flags::ASYNC){
quote!{
#x.go().await
}
}else{
quote!{
#root::_rexport::tramp::tramp(#x)
}
}}{
Ok(a) => a,
Err(e) => return #root::_rexport::tramp::BorrowRec::Ret(Err(e))
Err(e) => return #{self.fp()}::ret(Err(e))
}
}
}
@ -286,12 +443,23 @@ impl Opts<Module<'static>> {
let vals = vals.iter().map(|a|format_ident!("{a}"));
let r = format_ident!("{r}");
let g = self.render_generics(&quote! {c}, &self.module.signatures[*sig_index]);
quote! {
match #root::_rexport::tramp::tramp(#root::func::call_ref::<#g,C>(ctx,#root::func::cast(#r.clone()),#root::_rexport::tuple_list::tuple_list!(#(#root::func::cast::<_,_,C>(#vals .clone())),*))){
quasiquote! {
{
let x = #{self.fp()}::call_ref::<#g,C>(ctx,#{self.fp()}(#r.clone()),#root::_rexport::tuple_list::tuple_list!(#(#fp::cast::<_,_,C>(#vals .clone())),*));
match #{if self.flags.contains(Flags::ASYNC){
quote!{
x.go().await
}
}else{
quote!{
#root::_rexport::tramp::tramp(x)
}
}}{
Ok(a) => a,
Err(e) => return #root::_rexport::tramp::BorrowRec::Ret(Err(e))
Err(e) => return #{self.fp()}::ret(Err(e))
}
}
}
},
Operator::CallIndirect { sig_index, table_index } => {
let t = format_ident!("{table_index}");
@ -304,12 +472,21 @@ impl Opts<Module<'static>> {
ctx.#t()[#r as usize]
};
let g = self.render_generics(&quote! {c}, &self.module.signatures[*sig_index]);
quote! {
quasiquote! {
{
let r = #r.clone();
match #root::_rexport::tramp::tramp(#root::func::call_ref::<#g,C>(ctx,#root::func::cast(r),#root::_rexport::tuple_list::tuple_list!(#(#root::func::cast::<_,_,C>(#vals .clone())),*))){
let x = #{self.fp()}::call_ref::<#g,C>(ctx,#{self.fp()}::cast(r),#root::_rexport::tuple_list::tuple_list!(#(#fp::cast::<_,_,C>(#vals .clone())),*));
match #{if self.flags.contains(Flags::ASYNC){
quote!{
x.go().await
}
}else{
quote!{
#root::_rexport::tramp::tramp(x)
}
}}{
Ok(a) => a,
Err(e) => return #root::_rexport::tramp::BorrowRec::Ret(Err(e))
Err(e) => return #{self.fp()}::ret(Err(e))
}
}
}
@ -324,8 +501,11 @@ impl Opts<Module<'static>> {
quote! {u32}
};
let m = Ident::new(&mem.to_string(), Span::call_site());
quote! {
#root::_rexport::tuple_list::tuple_list!((ctx.#m().len() / 65536) as #rt)
quasiquote! {
#root::_rexport::tuple_list::tuple_list!(((match #root::Memory::size(ctx.#m()){
Ok(a) => a,
Err(e) => return #{self.fp()}::ret(Err(e))
}) / 65536) as #rt)
}
}
waffle::Operator::MemoryGrow { mem } => {
@ -337,25 +517,30 @@ impl Opts<Module<'static>> {
}else{
quote! {u32}
};
quote! {
quasiquote! {
{
let vn = ctx.#m().len() / 65536;
let l = ctx.#m().len();
ctx.#m().resize(l + (#a .clone() as usize) * 65536,0);
let vn = (match #root::Memory::size(ctx.#m()){
Ok(a) => a,
Err(e) => return #{self.fp()}::ret(Err(e))
}) / 65536;
match #root::Memory::grow(ctx.#m(),(#a .clone() as usize) * 65536){
Ok(a) => a,
Err(e) => return #{self.fp()}::ret(Err(e))
};
#root::_rexport::tuple_list::tuple_list!(vn as #rt)
}
}
},
waffle::Operator::MemoryCopy { dst_mem, src_mem } => {
let dst = format_ident!("{dst_mem}");
let src = format_ident!("{src_mem}");
let dst = self.mem(*dst_mem);
let src = self.mem(*src_mem);
let dst_ptr = format_ident!("{}",vals[0].to_string());
let src_ptr = format_ident!("{}",vals[1].to_string());
let len = format_ident!("{}",vals[2].to_string());
quote!{
{
let m = ctx.#src()[(#src_ptr as usize)..][..(#len as usize)].to_owned();
ctx.#dst()[(#dst_ptr as usize)..][..(#len as usize)].copy_from_slice(&m);
let m = #src[(#src_ptr as usize)..][..(#len as usize)].to_owned();
#dst[(#dst_ptr as usize)..][..(#len as usize)].copy_from_slice(&m);
()
}
}
@ -377,6 +562,55 @@ impl Opts<Module<'static>> {
}
}
}
Operator::TableGet { table_index } => {
let table = format_ident!("{table_index}");
let [i,..] = vals else{
unreachable!()
};
let i = format_ident!("{i}");
// let j = format_ident!("{j}");
quasiquote!{
{
(ctx.#table()[#i as usize].clone(),())
}
}
},
Operator::TableSet { table_index } => {
let table = format_ident!("{table_index}");
let [i,j,..] = vals else{
unreachable!()
};
let i = format_ident!("{i}");
let j = format_ident!("{j}");
quasiquote!{
{
ctx.#table()[#i as usize] = #{self.fp()}::cast::<_,_,C>(#j.clone());
()
}
}
},
Operator::TableSize { table_index } => {
let table = format_ident!("{table_index}");
quote!{
(ctx.#table().len() as u32,())
}
},
Operator::TableGrow { table_index } => {
let table = format_ident!("{table_index}");
let [i,j,..] = vals else{
unreachable!()
};
let i = format_ident!("{i}");
let j = format_ident!("{j}");
quasiquote!{
{
for _ in 0..#i{
ctx.#table().push(#{self.fp()}::cast::<_,_,C>(#j.clone()));
}
()
}
}
},
_ if waffle::op_traits::mem_count(o) == 1 => {
let mut mem = Memory::invalid();
waffle::op_traits::rewrite_mem(&mut o.clone(), &mut [();4], |m,_|{
@ -386,7 +620,7 @@ impl Opts<Module<'static>> {
// let clean = o.to_string();
let clean = format_ident!("{}",o.to_string().split_once("<").unwrap().0);
let m2 = mem;
let mem = format_ident!("{mem}");
let mem = self.mem(m2);
let mut vals = vals.iter().map(|a|format_ident!("{a}"));
let rt = if self.module.memories[m2].memory64{
quote! {u64}
@ -402,10 +636,10 @@ impl Opts<Module<'static>> {
};
let val = vals.next().unwrap();
let vals = once(quote! {(#val.clone() + #offset)}).chain(vals.map(|w|quote!{#w}));
quote! {
match #root::#clean::<#rt>(ctx.#mem(),#(#root::func::cast::<_,_,C>(#vals .clone())),*){
quasiquote! {
match #root::#clean::<#rt,_>(#mem,#(#fp::cast::<_,_,C>(#vals .clone())),*){
Ok(a) => a,
Err(e) => return #root::_rexport::tramp::BorrowRec::Ret(Err(e))
Err(e) => return #{self.fp()}::ret(Err(e))
}
}
},
@ -426,10 +660,10 @@ impl Opts<Module<'static>> {
// let clean = o.to_string();
let clean = format_ident!("{o}");
let vals = vals.iter().map(|a|format_ident!("{a}"));
quote! {
match #root::#clean(#(#root::func::cast::<_,_,C>(#vals .clone())),*){
quasiquote! {
match #root::#clean(#(#fp::cast::<_,_,C>(#vals .clone())),*){
Ok(a) => a,
Err(e) => return #root::_rexport::tramp::BorrowRec::Ret(Err(e))
Err(e) => return #{self.fp()}::ret(Err(e))
}
}
}
@ -459,8 +693,8 @@ impl Opts<Module<'static>> {
let vars = k.args.iter().enumerate().map(|(i, a)| {
let a = format_ident!("{a}");
let i = format_ident!("{}param{i}", k.block.to_string());
quote! {
#i = #a;
quasiquote! {
#i = #{self.fp()}::cast::<_,_,C>(#a);
}
});
let br = term(
@ -523,8 +757,8 @@ impl Opts<Module<'static>> {
let values = b.rets.iter().enumerate().map(|(a, _)| match values.get(a) {
Some(v) => {
let v = format_ident!("{v}");
quote! {
#v
quasiquote! {
#{self.fp()}::cast::<_,_,C>(#v)
}
}
None => {
@ -533,19 +767,29 @@ impl Opts<Module<'static>> {
}
}
});
quote! {
return #root::_rexport::tramp::BorrowRec::Ret(Ok(#root::_rexport::tuple_list::tuple_list!(#(#values),*)))
quasiquote! {
return #{self.fp()}::ret(Ok(#root::_rexport::tuple_list::tuple_list!(#(#values),*)))
}
}
waffle::Terminator::ReturnCall { func, args } => {
match self.module.funcs[*func].body() {
Some(_) => {
let values = args.iter().map(|v| format_ident!("{v}"));
let values = args.iter().map(|v| format_ident!("{v}")).map(|a| {
quasiquote! {
#{self.fp()}::cast::<_,_,C>(#a)
}
});
let func = format_ident!("{func}");
quote! {
return #root::_rexport::tramp::BorrowRec::Call(#root::_rexport::tramp::Thunk::new(move||{
if self.flags.contains(Flags::ASYNC) {
quote! {
#func(ctx,#root::_rexport::tuple_list::tuple_list!(#(#values),*))
}))
}
} else {
quote! {
return #root::_rexport::tramp::BorrowRec::Call(#root::_rexport::tramp::Thunk::new(move||{
#func(ctx,#root::_rexport::tuple_list::tuple_list!(#(#values),*))
}))
}
}
}
None => {
@ -562,8 +806,12 @@ impl Opts<Module<'static>> {
.map(|a| format_ident!("{a}"))
.map(|a| quote! {#a}),
);
quote! {
return #root::_rexport::tramp::BorrowRec::Call(#root::_rexport::tramp::Thunk::new(move||{#x}))
if self.flags.contains(Flags::ASYNC) {
x
} else {
quote! {
return #root::_rexport::tramp::BorrowRec::Call(#root::_rexport::tramp::Thunk::new(move||{#x}))
}
}
}
}
@ -573,30 +821,50 @@ impl Opts<Module<'static>> {
let mut vals = args.to_owned();
let r = vals.pop().unwrap();
// let func = format_ident!("{function_index}");
let vals = vals.iter().map(|a| format_ident!("{a}"));
let vals = vals.iter().map(|a| format_ident!("{a}")).map(|a| {
quasiquote! {
#{self.fp()}::cast::<_,_,C>(#a)
}
});
let r = format_ident!("{r}");
let r = quote! {
ctx.#t()[#r as usize]
};
let g = self.render_generics(&quote! {c}, &self.module.signatures[*sig]);
quote! {
let r = #r.clone();
return #root::_rexport::tramp::BorrowRec::Call(#root::_rexport::tramp::Thunk::new(move||{
#root::func::call_ref::<#g,C>(ctx,#root::func::cast(r),#root::_rexport::tuple_list::tuple_list!(#(#root::func::cast::<_,_,C>(#vals .clone())),*))
}))
if self.flags.contains(Flags::ASYNC) {
quasiquote! {
return #{self.fp()}::call_ref::<#g,C>(ctx,#{self.fp()}::cast(r),#root::_rexport::tuple_list::tuple_list!(#(#{self.fp()}::cast::<_,_,C>(#vals .clone())),*))
}
} else {
quasiquote! {
let r = #r.clone();
return #root::_rexport::tramp::BorrowRec::Call(#root::_rexport::tramp::Thunk::new(move||{
#{self.fp()}::call_ref::<#g,C>(ctx,#{self.fp()}::cast(r),#root::_rexport::tuple_list::tuple_list!(#(#{self.fp()}::cast::<_,_,C>(#vals .clone())),*))
}))
}
}
}
waffle::Terminator::ReturnCallRef { sig, args } => {
let mut vals = args.clone();
let r = vals.pop().unwrap();
// let func = format_ident!("{function_index}");
let vals = vals.iter().map(|a| format_ident!("{a}"));
let vals = vals.iter().map(|a| format_ident!("{a}")).map(|a| {
quasiquote! {
#{self.fp()}::cast::<_,_,C>(#a)
}
});
let r = format_ident!("{r}");
let g = self.render_generics(&quote! {c}, &self.module.signatures[*sig]);
quote! {
return #root::_rexport::tramp::BorrowRec::Call(#root::_rexport::tramp::Thunk::new(move||{
#root::func::call_ref::<#g,C>(ctx,#root::func::cast(#r.clone()),#root::_rexport::tuple_list::tuple_list!(#(#root::func::cast::<_,_,C>(#vals .clone())),*))
}))
if self.flags.contains(Flags::ASYNC) {
quasiquote! {
return #{self.fp()}::call_ref::<#g,C>(ctx,#root::func::cast(#r.clone()),#root::_rexport::tuple_list::tuple_list!(#(#root::func::cast::<_,_,C>(#vals .clone())),*))
}
} else {
quasiquote! {
return #root::_rexport::tramp::BorrowRec::Call(#root::_rexport::tramp::Thunk::new(move||{
#{self.fp()}::call_ref::<#g,C>(ctx,#root::func::cast(#r.clone()),#root::_rexport::tuple_list::tuple_list!(#(#root::func::cast::<_,_,C>(#vals .clone())),*))
}))
}
}
}
waffle::Terminator::Unreachable => quote! {
@ -753,45 +1021,56 @@ impl Opts<Module<'static>> {
}
})
});
let reloop = std::panic::catch_unwind(|| {
reloop(
b.blocks
.entries()
.filter(|k| cfg.dominates(b.entry, k.0))
.map(|(k, l)| {
(
k,
l.succs
.iter()
.cloned()
.chain(b.blocks.iter().filter(|x| cfg.dominates(*x, k)))
.collect(),
)
})
.chain(once((Block::invalid(), vec![b.entry])))
.collect(),
Block::invalid(),
)
});
let reloop = match reloop {
Ok(a) => a,
Err(e) => {
panic!(
"reloop failure ({}) in {}",
e.downcast_ref::<&str>()
.map(|a| *a)
.unwrap_or("unknown panic"),
b.display("", None)
);
}
};
let reloop = waffle_func_reloop::go(b);
// let reloop = std::panic::catch_unwind(|| {
// reloop(
// b.blocks
// .entries()
// .filter(|k| cfg.dominates(b.entry, k.0))
// .map(|(k, l)| {
// (
// k,
// l.succs
// .iter()
// .cloned()
// .chain(b.blocks.iter().filter(|x| cfg.dominates(*x, k)))
// .collect(),
// )
// })
// .chain(once((Block::invalid(), vec![b.entry])))
// .collect(),
// Block::invalid(),
// )
// });
// let reloop = match reloop {
// Ok(a) => a,
// Err(e) => {
// panic!(
// "reloop failure ({}) in {}",
// e.downcast_ref::<&str>()
// .map(|a| *a)
// .unwrap_or("unknown panic"),
// b.display("", None)
// );
// }
// };
let x = self.render_relooped_block(f, reloop.as_ref());
let mut b = quote! {
let mut cff: usize = 0;
#(let mut #bpvalues);*;
#x;
panic!("should have returned");
};
if self.flags.contains(Flags::ASYNC) {
b = quasiquote! {
return #{self.fp()}::AsyncRec::Async(Box::pin(async move{
#b
}))
}
}
quote! {
#sig {
let mut cff: usize = 0;
#(let mut #bpvalues);*;
#x;
panic!("should have returned");
#b
}
}
}
@ -800,45 +1079,92 @@ pub struct Opts<B> {
pub crate_path: syn::Path,
pub module: B,
pub name: Ident,
pub flags: Flags,
pub embed: TokenStream,
pub data: BTreeMap<Ident, TokenStream>,
// pub cfg: Arc<dyn ImportCfg>,
}
pub fn go(opts: &Opts<Vec<u8>>) -> proc_macro2::TokenStream {
let mut module = waffle::Module::from_wasm_bytes(&opts.module, &Default::default()).unwrap();
module.expand_all_funcs().unwrap();
let mut module = module.without_orig_bytes();
// module.per_func_body(|b|unswitch::go(b)); //TODO: reloop better and make it not needed
// eprintln!("{}",module.display());
module.per_func_body(|f| f.convert_to_max_ssa(None));
impl<X: AsRef<[u8]>> Opts<X> {
pub fn to_mod(&self) -> Opts<Module<'static>> {
let opts = self;
let mut module =
waffle::Module::from_wasm_bytes(opts.module.as_ref(), &Default::default()).unwrap();
module.expand_all_funcs().unwrap();
let mut module = module.without_orig_bytes();
// module.per_func_body(|b|unswitch::go(b)); //TODO: reloop better and make it not needed
// eprintln!("{}",module.display());
module.per_func_body(|f| f.convert_to_max_ssa(None));
let internal_path = format_ident!("_{}_internal", opts.name);
let data = format_ident!("{}Data", opts.name);
let name = opts.name.clone();
let opts = Opts {
crate_path: opts.crate_path.clone(),
module,
name: name.clone(),
flags: opts.flags,
embed: opts.embed.clone(),
data: opts.data.clone(),
// cfg: opts.cfg.clone(),
};
return opts;
}
}
impl ToTokens for Opts<Module<'static>> {
fn to_tokens(&self, tokens: &mut TokenStream) {
go(self).to_tokens(tokens)
}
}
pub fn go(opts: &Opts<Module<'static>>) -> proc_macro2::TokenStream {
// let mut module = waffle::Module::from_wasm_bytes(&opts.module, &Default::default()).unwrap();
// module.expand_all_funcs().unwrap();
// let mut module = module.without_orig_bytes();
// // module.per_func_body(|b|unswitch::go(b)); //TODO: reloop better and make it not needed
// // eprintln!("{}",module.display());
// module.per_func_body(|f| f.convert_to_max_ssa(None));
let internal_path = format_ident!("_{}_internal", opts.name);
let data = format_ident!("{}Data", opts.name);
let name = opts.name.clone();
let opts = Opts {
crate_path: opts.crate_path.clone(),
module,
name: name.clone(),
// cfg: opts.cfg.clone(),
};
// let opts = Opts {
// crate_path: opts.crate_path.clone(),
// module,
// name: name.clone(),
// flags: opts.flags,
// // cfg: opts.cfg.clone(),
// };
let root = opts.crate_path.clone();
let funcs = opts.module.funcs.iter().map(|a| opts.render_fn(a));
let mut z = vec![];
let mut fields = vec![];
let mut fs = vec![];
fs.push(opts.embed.clone());
for (k, v) in opts.data.iter() {
fields.push(k.clone());
z.push(quote! {
#k : #v
});
}
let mut init = vec![];
for (t, d) in opts.module.tables.entries() {
// let dty = opts.render_ty(&quote! {Target}, d.ty.clone());
let n = Ident::new(&t.to_string(), Span::call_site());
z.push(quote! {
#n: Vec<#root::func::Value<Target>>
z.push(quasiquote! {
#n: Vec<#{opts.fp()}::Value<Target>>
});
fields.push(n.clone());
if let Some(e) = d.func_elements.as_ref() {
let e = e.iter().map(|x| opts.render_fun_ref(&quote! {C}, *x));
init.push(quote! {
#(ctx.data().#n.push(#root::func::Coe::coe(#e)));*;
init.push(if opts.flags.contains(Flags::ASYNC) {
quote! {
#(ctx.data().#n.push(#root::func::unsync::Coe::coe(#e)));*;
}
} else {
quote! {
#(ctx.data().#n.push(#root::func::Coe::coe(#e)));*;
}
})
}
fs.push(quote! {
fn #n(&mut self) -> &mut Vec<#root::func::Value<Self>>{
fs.push(quasiquote! {
fn #n(&mut self) -> &mut Vec<#{opts.fp()}::Value<Self>>{
&mut self.data().#n
}
})
@ -893,15 +1219,25 @@ pub fn go(opts: &Opts<Vec<u8>>) -> proc_macro2::TokenStream {
});
}
Some((a, b)) => {
// let a = bindname(&a);
// let b = bindname(&b);
let m = Ident::new(&format!("{a}_{b}"), Span::call_site());
fs.push(quote! {
fn #m<'a>(&'a mut self) -> &'a mut Vec<u8>;
fn #n<'a>(&'a mut self) -> &'a mut Vec<u8>{
return self.#m();
}
});
if a == "!!unsafe" && b == "host" && opts.flags.contains(Flags::HOST_MEMORY) {
} else {
// let a = bindname(&a);
// let b = bindname(&b);
let m = Ident::new(&format!("{a}_{b}"), Span::call_site());
let p = if opts.flags.contains(Flags::LEGACY) {
quote! {dyn #root::Memory}
} else {
quote! {
impl #root::Memory
}
};
fs.push(quote! {
fn #m<'a>(&'a mut self) -> &'a mut #p;
fn #n<'a>(&'a mut self) -> &'a mut #p{
return self.#m();
}
});
}
}
}
let pk = d.initial_pages * 65536;
@ -930,6 +1266,10 @@ pub fn go(opts: &Opts<Vec<u8>>) -> proc_macro2::TokenStream {
}
}
for xp in opts.module.exports.iter() {
let xp = Export{
name: bindname(&xp.name),
kind: xp.kind.clone()
};
match &xp.kind {
ExportKind::Func(f) => {
let f = *f;
@ -969,8 +1309,14 @@ pub fn go(opts: &Opts<Vec<u8>>) -> proc_macro2::TokenStream {
ExportKind::Memory(m) => {
let x = Ident::new(&m.to_string(), Span::call_site());
let mn = Ident::new(&xp.name, Span::call_site());
let i = quote! {
fn #mn(&mut self) -> &mut Vec<u8>{
let i = quasiquote! {
fn #mn(&mut self) -> &mut #{if opts.flags.contains(Flags::LEGACY) {
quote! {dyn #root::Memory}
} else {
quote! {
impl #root::Memory
}
}}{
return self.#x()
}
};
@ -990,13 +1336,56 @@ pub fn go(opts: &Opts<Vec<u8>>) -> proc_macro2::TokenStream {
));
}
}
quote! {
// if opts.flags.contains(Flags::UNSANDBOXED) {
// for (x, y) in [
// (
// format_ident!("files"),
// quote! {
// ::std::collections::BTreeMap<u32,::std::sync::Arc<::std::fs::File>>
// },
// ),
// (
// format_ident!("io_errors"),
// quote! {
// ::std::collections::BTreeMap<u32,::std::sync::Arc<::std::io::Error>>
// },
// ),
// ] {
// fields.push(x.clone());
// z.push(quote! {
// #x: #y
// })
// }
// }
let defaults = fields.iter().map(|a| {
quote! {
#a: Default::default()
}
});
let clones = fields.iter().map(|a| {
quote! {
#a: self.#a.clone()
}
});
quasiquote! {
mod #internal_path{
#(#funcs)*
pub fn alloc<T>(m: &mut ::std::collections::BTreeMap<u32,T>, x: T) -> u32{
let mut u = 0;
while m.contains_key(&u){
u += 1;
};
m.insert(u,x);
return u;
}
pub struct #data<Target: #name + ?Sized>{
#(#z),*
}
pub trait #name: #root::func::CtxSpec{
pub trait #name: #{opts.fp()}::CtxSpec #{if opts.flags.contains(Flags::ASYNC){
quote! {+ Send + Sync}
}else{
quote! {}
}}{
fn data(&mut self) -> &mut #data<Self>;
#(#fs)*
@ -1005,6 +1394,20 @@ pub fn go(opts: &Opts<Vec<u8>>) -> proc_macro2::TokenStream {
#(#init);*;
return Ok(())
}
impl<Target: #name + ?Sized> Default for #data<Target>{
fn default() -> Self{
Self{
#(#defaults),*
}
}
}
impl<Target: #name + ?Sized> Clone for #data<Target>{
fn clone(&self) -> Self{
Self{
#(#clones),*
}
}
}
}
use #internal_path::{#name,#data};
}