Compare commits

..

10 commits

Author SHA1 Message Date
Graham Kelly 8fff4913c0 table64 2024-08-10 15:02:05 -04:00
Graham Kelly 8db8a02347 serde 2024-07-07 14:59:55 -04:00
Graham Kelly 87e06c1242 mutable module interp 2024-07-01 07:37:03 -04:00
Chris Fallin 123bc5f4f9 Update to newly published wasm-tools crates. 2024-06-30 10:20:57 -04:00
Graham Kelly cefe7a48ac full merge 2024-06-23 14:30:46 -04:00
Chris Fallin 598e004a39 Switch to git dep on BA main branch now that PR is merged 2024-06-23 14:29:24 -04:00
Chris Fallin c22215b103 switch to branch of wasm-tools for now 2024-06-23 14:28:57 -04:00
Chris Fallin eab940024b Switch over to raw Vec<u8> for compiled funcs. 2024-06-23 14:28:46 -04:00
Chris Fallin efa07f16cf Upgrade to latest wasmparser/wasmencoder. 2024-06-23 14:27:59 -04:00
Graham Kelly 4bdc602ef9 better fusing 2024-06-23 13:31:20 -04:00
16 changed files with 282 additions and 159 deletions

View file

@ -7,8 +7,8 @@ license = "Apache-2.0 WITH LLVM-exception"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
wasmparser = "0.202" wasmparser = "0.212"
wasm-encoder = "0.202" wasm-encoder = "0.212"
anyhow = "1.0" anyhow = "1.0"
structopt = "0.3" structopt = "0.3"
log = "0.4" log = "0.4"
@ -26,6 +26,7 @@ indexmap = "2.2.2"
stacker = "0.1.15" stacker = "0.1.15"
wasm-smith = { version = "0.202", optional = true } wasm-smith = { version = "0.202", optional = true }
paste = "1.0.15" paste = "1.0.15"
serde = { version = "1.0.204", features = ["derive"] }
[features] [features]
default = [] default = []

View file

@ -8,6 +8,7 @@ use anyhow::Result;
use rayon::prelude::*; use rayon::prelude::*;
use std::borrow::Cow; use std::borrow::Cow;
use wasm_encoder::CustomSection; use wasm_encoder::CustomSection;
use wasm_encoder::Encode;
pub mod stackify; pub mod stackify;
use stackify::{Context as StackifyContext, WasmBlock}; use stackify::{Context as StackifyContext, WasmBlock};
@ -210,8 +211,8 @@ impl<'a> WasmFuncBackend<'a> {
self.lower_value(value, func); self.lower_value(value, func);
} }
func.instruction(&wasm_encoder::Instruction::ReturnCallIndirect { func.instruction(&wasm_encoder::Instruction::ReturnCallIndirect {
ty: sig.index() as u32, type_index: sig.index() as u32,
table: table.index() as u32, table_index: table.index() as u32,
}); });
} }
WasmBlock::ReturnCallRef { sig, values } => { WasmBlock::ReturnCallRef { sig, values } => {
@ -299,8 +300,8 @@ impl<'a> WasmFuncBackend<'a> {
sig_index, sig_index,
table_index, table_index,
} => Some(wasm_encoder::Instruction::CallIndirect { } => Some(wasm_encoder::Instruction::CallIndirect {
ty: sig_index.index() as u32, type_index: sig_index.index() as u32,
table: table_index.index() as u32, table_index: table_index.index() as u32,
}), }),
Operator::Select => Some(wasm_encoder::Instruction::Select), Operator::Select => Some(wasm_encoder::Instruction::Select),
Operator::TypedSelect { ty } => Some(wasm_encoder::Instruction::TypedSelect( Operator::TypedSelect { ty } => Some(wasm_encoder::Instruction::TypedSelect(
@ -1141,9 +1142,11 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result<wasm_encoder::Module> {
minimum: table minimum: table
.func_elements .func_elements
.as_ref() .as_ref()
.map(|elts| elts.len() as u32) .map(|elts| elts.len() as u64)
.unwrap_or(table.initial), .unwrap_or(table.initial),
maximum: table.max, maximum: table.max,
table64: table.table64,
}) })
} }
&ImportKind::Global(global) => { &ImportKind::Global(global) => {
@ -1152,6 +1155,7 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result<wasm_encoder::Module> {
wasm_encoder::EntityType::Global(wasm_encoder::GlobalType { wasm_encoder::EntityType::Global(wasm_encoder::GlobalType {
val_type: wasm_encoder::ValType::from(global.ty), val_type: wasm_encoder::ValType::from(global.ty),
mutable: global.mutable, mutable: global.mutable,
shared: false,
}) })
} }
&ImportKind::Memory(mem) => { &ImportKind::Memory(mem) => {
@ -1162,6 +1166,7 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result<wasm_encoder::Module> {
shared: mem.shared, shared: mem.shared,
minimum: mem.initial_pages as u64, minimum: mem.initial_pages as u64,
maximum: mem.maximum_pages.map(|val| val as u64), maximum: mem.maximum_pages.map(|val| val as u64),
page_size_log2: None,
}) })
} }
}; };
@ -1192,8 +1197,9 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result<wasm_encoder::Module> {
.func_elements .func_elements
.as_ref() .as_ref()
.map(|elt| elt.len()) .map(|elt| elt.len())
.unwrap_or(0) as u32, .unwrap_or(0) as u64,
maximum: table_data.max, maximum: table_data.max,
table64: table_data.table64,
}); });
} }
into_mod.section(&tables); into_mod.section(&tables);
@ -1205,6 +1211,7 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result<wasm_encoder::Module> {
maximum: mem_data.maximum_pages.map(|val| val as u64), maximum: mem_data.maximum_pages.map(|val| val as u64),
memory64: mem_data.memory64, memory64: mem_data.memory64,
shared: mem_data.shared, shared: mem_data.shared,
page_size_log2: None,
}); });
} }
into_mod.section(&memories); into_mod.section(&memories);
@ -1215,6 +1222,7 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result<wasm_encoder::Module> {
wasm_encoder::GlobalType { wasm_encoder::GlobalType {
val_type: wasm_encoder::ValType::from(global_data.ty), val_type: wasm_encoder::ValType::from(global_data.ty),
mutable: global_data.mutable, mutable: global_data.mutable,
shared: false,
}, },
&const_init(global_data.ty, global_data.value), &const_init(global_data.ty, global_data.value),
); );
@ -1296,11 +1304,6 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result<wasm_encoder::Module> {
let mut code = wasm_encoder::CodeSection::new(); let mut code = wasm_encoder::CodeSection::new();
enum FuncOrRawBytes<'a> {
Raw(&'a [u8]),
Func(Cow<'a, wasm_encoder::Function>),
}
let bodies = module let bodies = module
.funcs .funcs
.entries() .entries()
@ -1311,16 +1314,14 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result<wasm_encoder::Module> {
match func_decl { match func_decl {
FuncDecl::Lazy(_, _name, reader) => { FuncDecl::Lazy(_, _name, reader) => {
let data = &module.orig_bytes[reader.range()]; let data = &module.orig_bytes[reader.range()];
Ok(FuncOrRawBytes::Raw(data)) Ok(Cow::Borrowed(data))
}
FuncDecl::Compiled(_, _name, encoder) => {
Ok(FuncOrRawBytes::Func(Cow::Borrowed(encoder)))
} }
FuncDecl::Compiled(_, _name, bytes) => Ok(Cow::Borrowed(&bytes[..])),
FuncDecl::Body(_, name, body) => { FuncDecl::Body(_, name, body) => {
log::debug!("Compiling {} \"{}\"", func, name); log::debug!("Compiling {} \"{}\"", func, name);
WasmFuncBackend::new(body)? WasmFuncBackend::new(body)?
.compile() .compile()
.map(|func| FuncOrRawBytes::Func(Cow::Owned(func))) .map(|func| Cow::Owned(func.into_raw_body()))
} }
FuncDecl::Import(_, _) => unreachable!("Should have skipped imports"), FuncDecl::Import(_, _) => unreachable!("Should have skipped imports"),
FuncDecl::None => panic!("FuncDecl::None at compilation time"), FuncDecl::None => panic!("FuncDecl::None at compilation time"),
@ -1329,14 +1330,7 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result<wasm_encoder::Module> {
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
for body in bodies { for body in bodies {
match body { code.raw(&body);
FuncOrRawBytes::Raw(bytes) => {
code.raw(bytes);
}
FuncOrRawBytes::Func(func) => {
code.function(&*func);
}
}
} }
into_mod.section(&code); into_mod.section(&code);

View file

@ -124,7 +124,7 @@ fn main() -> Result<()> {
let mut ctx = InterpContext::new(&module)?; let mut ctx = InterpContext::new(&module)?;
debug!("Calling start function"); debug!("Calling start function");
if let Some(start) = module.start_func { if let Some(start) = module.start_func {
ctx.call(&module, start, &[]).ok().unwrap(); ctx.call(&mut module, start, &[]).ok().unwrap();
} }
// Find a function called `_start`, if any. // Find a function called `_start`, if any.
if let Some(waffle::Export { if let Some(waffle::Export {
@ -133,7 +133,7 @@ fn main() -> Result<()> {
}) = module.exports.iter().find(|e| &e.name == "_start") }) = module.exports.iter().find(|e| &e.name == "_start")
{ {
debug!("Calling _start"); debug!("Calling _start");
ctx.call(&module, *func, &[]).ok().unwrap(); ctx.call(&mut module, *func, &[]).ok().unwrap();
} }
} }
} }

View file

@ -22,7 +22,7 @@ pub trait EntityRef: Clone + Copy + PartialEq + Eq + PartialOrd + Ord + Hash {
#[macro_export] #[macro_export]
macro_rules! declare_entity { macro_rules! declare_entity {
($name:tt, $prefix:tt) => { ($name:tt, $prefix:tt) => {
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
pub struct $name(u32); pub struct $name(u32);
impl $crate::entity::EntityRef for $name { impl $crate::entity::EntityRef for $name {
@ -73,7 +73,7 @@ macro_rules! declare_entity {
}; };
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct EntityVec<Idx: EntityRef, T: Clone + Debug>(Vec<T>, PhantomData<Idx>); pub struct EntityVec<Idx: EntityRef, T: Clone + Debug>(Vec<T>, PhantomData<Idx>);
impl<Idx: EntityRef, T: Clone + Debug> std::default::Default for EntityVec<Idx, T> { impl<Idx: EntityRef, T: Clone + Debug> std::default::Default for EntityVec<Idx, T> {
@ -151,7 +151,7 @@ impl<Idx: EntityRef, T: Clone + Debug> IndexMut<Idx> for EntityVec<Idx, T> {
} }
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct PerEntity<Idx: EntityRef, T: Clone + Debug + Default>(Vec<T>, PhantomData<Idx>, T); pub struct PerEntity<Idx: EntityRef, T: Clone + Debug + Default>(Vec<T>, PhantomData<Idx>, T);
impl<Idx: EntityRef, T: Clone + Debug + Default> Index<Idx> for PerEntity<Idx, T> { impl<Idx: EntityRef, T: Clone + Debug + Default> Index<Idx> for PerEntity<Idx, T> {

View file

@ -13,9 +13,7 @@ use anyhow::{bail, Result};
use fxhash::{FxHashMap, FxHashSet}; use fxhash::{FxHashMap, FxHashSet};
use log::trace; use log::trace;
use std::convert::TryFrom; use std::convert::TryFrom;
use wasmparser::{ use wasmparser::{BlockType, DataKind, ExternalKind, KnownCustom, Name, Parser, Payload, TypeRef};
BlockType, DataKind, ExternalKind, Name, NameSectionReader, Parser, Payload, TypeRef,
};
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub struct FrontendOptions { pub struct FrontendOptions {
@ -137,6 +135,7 @@ fn handle_payload<'a>(
ty.element_type.into(), ty.element_type.into(),
ty.initial, ty.initial,
ty.maximum, ty.maximum,
ty.table64,
); );
ImportKind::Table(table) ImportKind::Table(table)
} }
@ -184,6 +183,7 @@ fn handle_payload<'a>(
table.ty.element_type.into(), table.ty.element_type.into(),
table.ty.initial, table.ty.initial,
table.ty.maximum, table.ty.maximum,
table.ty.table64,
); );
} }
} }
@ -255,8 +255,8 @@ fn handle_payload<'a>(
} }
} }
} }
Payload::CustomSection(reader) if reader.name() == "name" => { Payload::CustomSection(reader) => match reader.as_known() {
let name_reader = NameSectionReader::new(reader.data(), reader.data_offset()); KnownCustom::Name(name_reader) => {
for subsection in name_reader { for subsection in name_reader {
let subsection = subsection?; let subsection = subsection?;
match subsection { match subsection {
@ -270,57 +270,53 @@ fn handle_payload<'a>(
} }
} }
} }
Payload::CustomSection(reader) if reader.name() == ".debug_info" => { KnownCustom::Unknown => {
if reader.name() == ".debug_info" {
dwarf.debug_info = gimli::DebugInfo::new(reader.data(), gimli::LittleEndian); dwarf.debug_info = gimli::DebugInfo::new(reader.data(), gimli::LittleEndian);
} } else if reader.name() == ".debug_abbrev" {
Payload::CustomSection(reader) if reader.name() == ".debug_abbrev" => { dwarf.debug_abbrev =
dwarf.debug_abbrev = gimli::DebugAbbrev::new(reader.data(), gimli::LittleEndian); gimli::DebugAbbrev::new(reader.data(), gimli::LittleEndian);
} } else if reader.name() == ".debug_addr" {
Payload::CustomSection(reader) if reader.name() == ".debug_addr" => { dwarf.debug_addr = gimli::DebugAddr::from(gimli::EndianSlice::new(
dwarf.debug_addr =
gimli::DebugAddr::from(gimli::EndianSlice::new(reader.data(), gimli::LittleEndian));
}
Payload::CustomSection(reader) if reader.name() == ".debug_aranges" => {
dwarf.debug_aranges = gimli::DebugAranges::new(reader.data(), gimli::LittleEndian);
}
Payload::CustomSection(reader) if reader.name() == ".debug_line" => {
dwarf.debug_line = gimli::DebugLine::new(reader.data(), gimli::LittleEndian);
}
Payload::CustomSection(reader) if reader.name() == ".debug_line_str" => {
dwarf.debug_line_str = gimli::DebugLineStr::new(reader.data(), gimli::LittleEndian);
}
Payload::CustomSection(reader) if reader.name() == ".debug_str" => {
dwarf.debug_str = gimli::DebugStr::new(reader.data(), gimli::LittleEndian);
}
Payload::CustomSection(reader) if reader.name() == ".debug_str_offsets" => {
dwarf.debug_str_offsets = gimli::DebugStrOffsets::from(gimli::EndianSlice::new(
reader.data(), reader.data(),
gimli::LittleEndian, gimli::LittleEndian,
)); ));
} } else if reader.name() == ".debug_aranges" {
Payload::CustomSection(reader) if reader.name() == ".debug_types" => { dwarf.debug_aranges =
gimli::DebugAranges::new(reader.data(), gimli::LittleEndian);
} else if reader.name() == ".debug_line" {
dwarf.debug_line = gimli::DebugLine::new(reader.data(), gimli::LittleEndian);
} else if reader.name() == ".debug_line_str" {
dwarf.debug_line_str =
gimli::DebugLineStr::new(reader.data(), gimli::LittleEndian);
} else if reader.name() == ".debug_str" {
dwarf.debug_str = gimli::DebugStr::new(reader.data(), gimli::LittleEndian);
} else if reader.name() == ".debug_str_offsets" {
dwarf.debug_str_offsets = gimli::DebugStrOffsets::from(
gimli::EndianSlice::new(reader.data(), gimli::LittleEndian),
);
} else if reader.name() == ".debug_types" {
dwarf.debug_types = gimli::DebugTypes::new(reader.data(), gimli::LittleEndian); dwarf.debug_types = gimli::DebugTypes::new(reader.data(), gimli::LittleEndian);
} } else if reader.name() == ".debug_loc" {
Payload::CustomSection(reader) if reader.name() == ".debug_loc" => { extra_sections.debug_loc =
extra_sections.debug_loc = gimli::DebugLoc::new(reader.data(), gimli::LittleEndian); gimli::DebugLoc::new(reader.data(), gimli::LittleEndian);
} } else if reader.name() == ".debug_loclists" {
Payload::CustomSection(reader) if reader.name() == ".debug_loclists" => {
extra_sections.debug_loclists = extra_sections.debug_loclists =
gimli::DebugLocLists::new(reader.data(), gimli::LittleEndian); gimli::DebugLocLists::new(reader.data(), gimli::LittleEndian);
} } else if reader.name() == ".debug_ranges" {
Payload::CustomSection(reader) if reader.name() == ".debug_ranges" => {
extra_sections.debug_ranges = extra_sections.debug_ranges =
gimli::DebugRanges::new(reader.data(), gimli::LittleEndian); gimli::DebugRanges::new(reader.data(), gimli::LittleEndian);
} } else if reader.name() == ".debug_rnglists" {
Payload::CustomSection(reader) if reader.name() == ".debug_rnglists" => {
extra_sections.debug_rnglists = extra_sections.debug_rnglists =
gimli::DebugRngLists::new(reader.data(), gimli::LittleEndian); gimli::DebugRngLists::new(reader.data(), gimli::LittleEndian);
} }else{
Payload::CustomSection(reader) => {
module module
.custom_sections .custom_sections
.insert(reader.name().to_owned(), reader.data().to_owned()); .insert(reader.name().to_owned(), reader.data().to_owned());
} }
}
_ => {}
},
Payload::Version { .. } => {} Payload::Version { .. } => {}
Payload::ElementSection(reader) => { Payload::ElementSection(reader) => {
for element in reader { for element in reader {

View file

@ -6,6 +6,7 @@ use crate::ops::Operator;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc;
mod wasi; mod wasi;
@ -18,8 +19,7 @@ pub struct InterpContext {
pub globals: PerEntity<Global, ConstVal>, pub globals: PerEntity<Global, ConstVal>,
pub fuel: u64, pub fuel: u64,
pub trace_handler: Option<Box<dyn Fn(usize, Vec<ConstVal>) -> bool + Send>>, pub trace_handler: Option<Box<dyn Fn(usize, Vec<ConstVal>) -> bool + Send>>,
pub import_hander: pub import_hander: Arc<dyn Fn(&mut InterpContext,&mut Module<'_>, &str, &[ConstVal]) -> InterpResult>,
Option<Box<dyn FnMut(&mut InterpContext, &str, &[ConstVal]) -> InterpResult>>,
} }
type MultiVal = SmallVec<[ConstVal; 2]>; type MultiVal = SmallVec<[ConstVal; 2]>;
@ -88,11 +88,11 @@ impl InterpContext {
globals, globals,
fuel: u64::MAX, fuel: u64::MAX,
trace_handler: None, trace_handler: None,
import_hander: None, import_hander: Arc::new(|_, _, _,_| InterpResult::TraceHandlerQuit),
}) })
} }
pub fn call(&mut self, module: &Module<'_>, mut func: Func, args: &[ConstVal]) -> InterpResult { pub fn call(&mut self, module: &mut Module<'_>, mut func: Func, args: &[ConstVal]) -> InterpResult {
let mut args = args.to_vec(); let mut args = args.to_vec();
'redo: loop { 'redo: loop {
let body = match &module.funcs[func] { let body = match &module.funcs[func] {
@ -101,9 +101,9 @@ impl InterpContext {
FuncDecl::Import(..) => { FuncDecl::Import(..) => {
let import = &module.imports[func.index()]; let import = &module.imports[func.index()];
assert_eq!(import.kind, ImportKind::Func(func)); assert_eq!(import.kind, ImportKind::Func(func));
return self.call_import(&import.name[..], &args); return self.call_import(module,&import.name[..].to_owned(), &args);
} }
FuncDecl::Body(_, _, body) => body, FuncDecl::Body(_, _, body) => body.clone(),
FuncDecl::None => panic!("FuncDecl::None in call()"), FuncDecl::None => panic!("FuncDecl::None in call()"),
}; };
@ -178,6 +178,25 @@ impl InterpContext {
_ => return result, _ => return result,
} }
} }
&ValueDef::Operator(Operator::CallRef { .. }, args, _) => {
let args = body.arg_pool[args]
.iter()
.map(|&arg| {
let arg = body.resolve_alias(arg);
let multivalue = frame.values.get(&arg).unwrap();
assert_eq!(multivalue.len(), 1);
multivalue[0]
})
.collect::<Vec<_>>();
let ConstVal::Ref(Some(func)) = args.last().unwrap() else {
return InterpResult::TraceHandlerQuit;
};
let result = self.call(module, *func, &args[..args.len() - 1]);
match result {
InterpResult::Ok(vals) => vals,
_ => return result,
}
}
&ValueDef::Operator(ref op, args, _) => { &ValueDef::Operator(ref op, args, _) => {
let args = body.arg_pool[args] let args = body.arg_pool[args]
.iter() .iter()
@ -284,7 +303,7 @@ impl InterpContext {
return InterpResult::Trap(frame.func, frame.cur_block, u32::MAX) return InterpResult::Trap(frame.func, frame.cur_block, u32::MAX)
} }
&Terminator::Br { ref target } => { &Terminator::Br { ref target } => {
frame.apply_target(body, target); frame.apply_target(&body, target);
} }
&Terminator::CondBr { &Terminator::CondBr {
cond, cond,
@ -295,9 +314,9 @@ impl InterpContext {
let cond = frame.values.get(&cond).unwrap(); let cond = frame.values.get(&cond).unwrap();
let cond = cond[0].as_u32().unwrap() != 0; let cond = cond[0].as_u32().unwrap() != 0;
if cond { if cond {
frame.apply_target(body, if_true); frame.apply_target(&body, if_true);
} else { } else {
frame.apply_target(body, if_false); frame.apply_target(&body, if_false);
} }
} }
&Terminator::Select { &Terminator::Select {
@ -309,9 +328,9 @@ impl InterpContext {
let value = frame.values.get(&value).unwrap(); let value = frame.values.get(&value).unwrap();
let value = value[0].as_u32().unwrap() as usize; let value = value[0].as_u32().unwrap() as usize;
if value < targets.len() { if value < targets.len() {
frame.apply_target(body, &targets[value]); frame.apply_target(&body, &targets[value]);
} else { } else {
frame.apply_target(body, default); frame.apply_target(&body, default);
} }
} }
&Terminator::Return { ref values } => { &Terminator::Return { ref values } => {
@ -325,16 +344,35 @@ impl InterpContext {
log::trace!("returning from {}: {:?}", func, values); log::trace!("returning from {}: {:?}", func, values);
return InterpResult::Ok(values); return InterpResult::Ok(values);
} }
Terminator::ReturnCallRef { sig, args } => todo!(), Terminator::ReturnCallRef {
sig,
args: ref args2,
} => {
let args2 = args2
.iter()
.map(|&arg| {
let arg = body.resolve_alias(arg);
let multivalue = frame.values.get(&arg).unwrap();
assert_eq!(multivalue.len(), 1);
multivalue[0]
})
.collect::<Vec<_>>();
let ConstVal::Ref(Some(fu)) = args.last().unwrap() else {
return InterpResult::TraceHandlerQuit;
};
func = *fu;
args = args2[..args2.len() - 1].to_vec();
continue 'redo;
}
} }
} }
} }
} }
fn call_import(&mut self, name: &str, args: &[ConstVal]) -> InterpResult { fn call_import(&mut self,module: &mut Module<'_>, name: &str, args: &[ConstVal]) -> InterpResult {
let mut r = self.import_hander.take().unwrap(); let mut r = self.import_hander.clone();
let rs = r(self, name, args); let rs = r(self, module,name, args);
self.import_hander = Some(r); // self.import_hander = Some(r);
return rs; return rs;
} }
} }
@ -388,6 +426,7 @@ pub enum ConstVal {
I64(u64), I64(u64),
F32(u32), F32(u32),
F64(u64), F64(u64),
Ref(Option<Func>),
#[default] #[default]
None, None,
} }
@ -995,9 +1034,32 @@ pub fn const_eval(
ConstVal::None ConstVal::None
}), }),
(Operator::TableGet { .. }, _) (Operator::TableGet { table_index }, [ConstVal::I32(i)]) => ctx.and_then(|global| {
| (Operator::TableSet { .. }, _) Some(ConstVal::Ref(
| (Operator::TableGrow { .. }, _) => None, global.tables[*table_index]
.elements
.get(*i as usize)
.and_then(|x| {
if *x == Func::invalid() {
None
} else {
Some(*x)
}
}),
))
}),
(Operator::TableSet { table_index }, [ConstVal::I32(i), ConstVal::Ref(r)]) => {
ctx.and_then(|global| {
global.tables[*table_index].elements[*i as usize] = r.unwrap_or_default();
Some(ConstVal::I32(0))
})
}
(Operator::TableGrow { table_index }, [ConstVal::I32(i)]) => ctx.and_then(|global| {
global.tables[*table_index]
.elements
.extend((0..*i).map(|a| Func::default()));
Some(ConstVal::I32(0))
}),
(Operator::TableSize { table_index }, []) => { (Operator::TableSize { table_index }, []) => {
ctx.map(|global| ConstVal::I32(global.tables[*table_index].elements.len() as u32)) ctx.map(|global| ConstVal::I32(global.tables[*table_index].elements.len() as u32))
@ -1232,6 +1294,16 @@ pub fn const_eval(
write_u64(&mut global.memories[memory.memory], addr, *data); write_u64(&mut global.memories[memory.memory], addr, *data);
Some(ConstVal::None) Some(ConstVal::None)
}), }),
(Operator::RefFunc { func_index }, []) => Some(ConstVal::Ref(Some(*func_index))),
(
Operator::RefNull {
ty: Type::FuncRef | Type::TypedFuncRef { .. },
},
[],
) => Some(ConstVal::Ref(None)),
(Operator::RefIsNull, [ConstVal::Ref(r)]) => {
Some(ConstVal::I32(if r.is_none() { 1 } else { 0 }))
}
(_, args) if args.iter().any(|&arg| arg == ConstVal::None) => None, (_, args) if args.iter().any(|&arg| arg == ConstVal::None) => None,
_ => None, _ => None,
} }

View file

@ -2,7 +2,7 @@
use crate::{declare_entity, entity::EntityRef}; use crate::{declare_entity, entity::EntityRef};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
pub enum Type { pub enum Type {
I32, I32,
I64, I64,

View file

@ -9,7 +9,7 @@ use std::collections::HashMap;
declare_entity!(SourceFile, "file"); declare_entity!(SourceFile, "file");
declare_entity!(SourceLoc, "loc"); declare_entity!(SourceLoc, "loc");
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct Debug { pub struct Debug {
pub source_files: EntityVec<SourceFile, String>, pub source_files: EntityVec<SourceFile, String>,
source_file_dedup: HashMap<String, SourceFile>, source_file_dedup: HashMap<String, SourceFile>,
@ -17,7 +17,7 @@ pub struct Debug {
source_loc_dedup: HashMap<SourceLocData, SourceLoc>, source_loc_dedup: HashMap<SourceLocData, SourceLoc>,
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
pub struct SourceLocData { pub struct SourceLocData {
pub file: SourceFile, pub file: SourceFile,
pub line: u32, pub line: u32,
@ -46,7 +46,7 @@ impl Debug {
} }
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct DebugMap { pub struct DebugMap {
/// Offset of code section relative to the Wasm file start. /// Offset of code section relative to the Wasm file start.
pub code_offset: u32, pub code_offset: u32,

View file

@ -13,16 +13,16 @@ use std::collections::HashSet;
/// A declaration of a function: there is one `FuncDecl` per `Func` /// A declaration of a function: there is one `FuncDecl` per `Func`
/// index. /// index.
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
pub enum FuncDecl<'a> { pub enum FuncDecl<'a> {
/// An imported function. /// An imported function.
Import(Signature, String), Import(Signature, String),
/// An un-expanded body that can be lazily expanded if needed. /// An un-expanded body that can be lazily expanded if needed.
Lazy(Signature, String, wasmparser::FunctionBody<'a>), #[serde(skip)]Lazy(Signature, String, wasmparser::FunctionBody<'a>),
/// A modified or new function body that requires compilation. /// A modified or new function body that requires compilation.
Body(Signature, String, FunctionBody), Body(Signature, String, FunctionBody),
/// A compiled function body (was IR, has been collapsed back to bytecode). /// A compiled function body (was IR, has been collapsed back to bytecode).
Compiled(Signature, String, wasm_encoder::Function), Compiled(Signature, String, Vec<u8>),
/// A placeholder. /// A placeholder.
#[default] #[default]
None, None,
@ -113,7 +113,7 @@ impl<'a> FuncDecl<'a> {
} }
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct FunctionBody { pub struct FunctionBody {
/// How many parameters the function has. (Their types are the /// How many parameters the function has. (Their types are the
/// first `n_params` values in `locals`.) /// first `n_params` values in `locals`.)
@ -481,7 +481,7 @@ impl FunctionBody {
} }
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct BlockDef { pub struct BlockDef {
/// Instructions in this block. /// Instructions in this block.
pub insts: Vec<Value>, pub insts: Vec<Value>,
@ -501,7 +501,7 @@ pub struct BlockDef {
pub desc: String, pub desc: String,
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct BlockTarget { pub struct BlockTarget {
pub block: Block, pub block: Block,
pub args: Vec<Value>, pub args: Vec<Value>,
@ -518,7 +518,7 @@ impl std::fmt::Display for BlockTarget {
} }
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum Terminator { pub enum Terminator {
Br { Br {
target: BlockTarget, target: BlockTarget,

View file

@ -7,7 +7,7 @@ use indexmap::IndexMap;
pub use crate::frontend::FrontendOptions; pub use crate::frontend::FrontendOptions;
#[derive(Clone, Debug)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct Module<'a> { pub struct Module<'a> {
pub orig_bytes: &'a [u8], pub orig_bytes: &'a [u8],
pub funcs: EntityVec<Func, FuncDecl<'a>>, pub funcs: EntityVec<Func, FuncDecl<'a>>,
@ -23,13 +23,13 @@ pub struct Module<'a> {
pub custom_sections: IndexMap<String, Vec<u8>>, pub custom_sections: IndexMap<String, Vec<u8>>,
} }
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
pub struct SignatureData { pub struct SignatureData {
pub params: Vec<Type>, pub params: Vec<Type>,
pub returns: Vec<Type>, pub returns: Vec<Type>,
} }
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
pub struct MemoryData { pub struct MemoryData {
pub initial_pages: usize, pub initial_pages: usize,
pub maximum_pages: Option<usize>, pub maximum_pages: Option<usize>,
@ -38,21 +38,22 @@ pub struct MemoryData {
pub shared: bool, pub shared: bool,
} }
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
pub struct MemorySegment { pub struct MemorySegment {
pub offset: usize, pub offset: usize,
pub data: Vec<u8>, pub data: Vec<u8>,
} }
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
pub struct TableData { pub struct TableData {
pub ty: Type, pub ty: Type,
pub initial: u32, pub table64: bool,
pub max: Option<u32>, pub initial: u64,
pub max: Option<u64>,
pub func_elements: Option<Vec<Func>>, pub func_elements: Option<Vec<Func>>,
} }
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
pub struct GlobalData { pub struct GlobalData {
pub ty: Type, pub ty: Type,
pub value: Option<u64>, pub value: Option<u64>,
@ -81,14 +82,14 @@ impl From<wasmparser::FuncType> for SignatureData {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct Import { pub struct Import {
pub module: String, pub module: String,
pub name: String, pub name: String,
pub kind: ImportKind, pub kind: ImportKind,
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum ImportKind { pub enum ImportKind {
Table(Table), Table(Table),
Func(Func), Func(Func),
@ -108,13 +109,13 @@ impl std::fmt::Display for ImportKind {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct Export { pub struct Export {
pub name: String, pub name: String,
pub kind: ExportKind, pub kind: ExportKind,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum ExportKind { pub enum ExportKind {
Table(Table), Table(Table),
Func(Func), Func(Func),
@ -177,13 +178,14 @@ impl<'a> Module<'a> {
} }
impl<'a> Module<'a> { impl<'a> Module<'a> {
pub(crate) fn frontend_add_table(&mut self, ty: Type, initial: u32, max: Option<u32>) -> Table { pub(crate) fn frontend_add_table(&mut self, ty: Type, initial: u64, max: Option<u64>, table64: bool) -> Table {
let func_elements = Some(vec![]); let func_elements = Some(vec![]);
self.tables.push(TableData { self.tables.push(TableData {
ty, ty,
func_elements, func_elements,
initial, initial,
max, max,
table64
}) })
} }

View file

@ -2,7 +2,7 @@ use super::{Block, Type, Value};
use crate::pool::{ListPool, ListRef}; use crate::pool::{ListPool, ListRef};
use crate::Operator; use crate::Operator;
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] #[derive(Clone, Debug, Default, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum ValueDef { pub enum ValueDef {
BlockParam(Block, u32, Type), BlockParam(Block, u32, Type),
Operator(Operator, ListRef<Value>, ListRef<Type>), Operator(Operator, ListRef<Value>, ListRef<Type>),

View file

@ -4,6 +4,8 @@
// Re-export wasmparser for easier use of the right version by our embedders. // Re-export wasmparser for easier use of the right version by our embedders.
pub use wasmparser; pub use wasmparser;
// Likewise for wasm-encoder.
pub use wasm_encoder;
mod backend; mod backend;
pub mod cfg; pub mod cfg;

View file

@ -257,12 +257,24 @@ pub fn op_inputs(
Operator::F64ReinterpretI64 => Ok(Cow::Borrowed(&[Type::I64])), Operator::F64ReinterpretI64 => Ok(Cow::Borrowed(&[Type::I64])),
Operator::I32ReinterpretF32 => Ok(Cow::Borrowed(&[Type::F32])), Operator::I32ReinterpretF32 => Ok(Cow::Borrowed(&[Type::F32])),
Operator::I64ReinterpretF64 => Ok(Cow::Borrowed(&[Type::F64])), Operator::I64ReinterpretF64 => Ok(Cow::Borrowed(&[Type::F64])),
Operator::TableGet { .. } => Ok(Cow::Borrowed(&[Type::I32])), Operator::TableGet { table_index } => if module.tables[*table_index].table64{
Ok(Cow::Borrowed(&[Type::I64]))
}else{
Ok(Cow::Borrowed(&[Type::I32]))
},
Operator::TableSet { table_index } => { Operator::TableSet { table_index } => {
Ok(vec![Type::I32, module.tables[*table_index].ty].into()) Ok(vec![if module.tables[*table_index].table64{
Type::I64
}else{
Type::I32
}, module.tables[*table_index].ty].into())
} }
Operator::TableGrow { table_index } => { Operator::TableGrow { table_index } => {
Ok(vec![Type::I32, module.tables[*table_index].ty].into()) Ok(vec![if module.tables[*table_index].table64{
Type::I64
}else{
Type::I32
}, module.tables[*table_index].ty].into())
} }
Operator::TableSize { .. } => Ok(Cow::Borrowed(&[])), Operator::TableSize { .. } => Ok(Cow::Borrowed(&[])),
Operator::MemorySize { .. } => Ok(Cow::Borrowed(&[])), Operator::MemorySize { .. } => Ok(Cow::Borrowed(&[])),
@ -1302,7 +1314,11 @@ pub fn op_outputs(
Operator::TableGet { table_index } => Ok(vec![module.tables[*table_index].ty].into()), Operator::TableGet { table_index } => Ok(vec![module.tables[*table_index].ty].into()),
Operator::TableSet { .. } => Ok(Cow::Borrowed(&[])), Operator::TableSet { .. } => Ok(Cow::Borrowed(&[])),
Operator::TableGrow { .. } => Ok(Cow::Borrowed(&[])), Operator::TableGrow { .. } => Ok(Cow::Borrowed(&[])),
Operator::TableSize { .. } => Ok(Cow::Borrowed(&[Type::I32])), Operator::TableSize { table_index } => Ok(Cow::Borrowed(if module.tables[*table_index].table64{
&[Type::I64]
}else{
&[Type::I32]
})),
Operator::MemorySize { mem } => Ok(if module.memories[*mem].memory64 { Operator::MemorySize { mem } => Ok(if module.memories[*mem].memory64 {
Cow::Borrowed(&[Type::I64]) Cow::Borrowed(&[Type::I64])
} else { } else {

View file

@ -5,7 +5,7 @@ use anyhow::Context;
use std::convert::TryFrom; use std::convert::TryFrom;
pub use wasmparser::{Ieee32, Ieee64}; pub use wasmparser::{Ieee32, Ieee64};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct MemoryArg { pub struct MemoryArg {
pub align: u32, pub align: u32,
pub offset: u64 , pub offset: u64 ,
@ -23,7 +23,7 @@ impl std::fmt::Display for MemoryArg {
} }
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum Operator { pub enum Operator {
Unreachable, Unreachable,
Nop, Nop,

View file

@ -1,4 +1,4 @@
use std::{collections::BTreeMap, convert::Infallible, iter::empty}; use std::{collections::BTreeMap, convert::Infallible, iter::{empty, once}};
use anyhow::Context; use anyhow::Context;
// use libc::name_t; // use libc::name_t;
@ -110,7 +110,7 @@ impl Fuse {
// } // }
return new; return new;
} }
pub fn process(&self, f: &mut FunctionBody) { pub fn process(&self, m: &mut Module, f: &mut FunctionBody) {
let vz = f.arg_pool.from_iter(empty()); let vz = f.arg_pool.from_iter(empty());
let tz = f.type_pool.from_iter(empty()); let tz = f.type_pool.from_iter(empty());
let ti = f.type_pool.from_iter(vec![Type::I32].into_iter()); let ti = f.type_pool.from_iter(vec![Type::I32].into_iter());
@ -123,6 +123,42 @@ impl Fuse {
// let vi = v; // let vi = v;
if let ValueDef::Operator(a, b, c) = &mut w { if let ValueDef::Operator(a, b, c) = &mut w {
let mut bp = f.arg_pool[*b].to_vec(); let mut bp = f.arg_pool[*b].to_vec();
fn g(
a: impl for<'a> FnMut(&mut Module,&mut FunctionBody, Memory, &'a mut crate::Value),
) -> impl for<'a> FnMut(&mut Module,&mut FunctionBody, Memory, &'a mut crate::Value)
{
return a;
}
let mut p = g(|m: &mut Module,f, mem, v| {
match (m.memories[mem].memory64, m.memories[self.target].memory64) {
(true, true) => {}
(true, false) => {
let ti = f.type_pool.from_iter(once(Type::I32));
let w = f.arg_pool.from_iter(vec![*v].into_iter());
let x = f.add_value(ValueDef::Operator(
Operator::I32WrapI64,
w,
ti,
));
f.append_to_block(k, x);
// crate::append_before(f, x, vi, k);
*v = x;
}
(false, true) => {
let ti = f.type_pool.from_iter(once(Type::I64));
let w = f.arg_pool.from_iter(vec![*v].into_iter());
let x = f.add_value(ValueDef::Operator(
Operator::I64ExtendI32U,
w,
ti,
));
f.append_to_block(k, x);
// crate::append_before(f, x, vi, k);
*v = x;
},
(false, false) => {}
}
});
match a.clone() { match a.clone() {
Operator::MemorySize { mem } => { Operator::MemorySize { mem } => {
if mem != self.target { if mem != self.target {
@ -139,6 +175,7 @@ impl Fuse {
function_index: self.size, function_index: self.size,
}; };
bp.push(ia); bp.push(ia);
p(m,f, mem, &mut bp[0]);
} }
} }
Operator::MemoryGrow { mem } => { Operator::MemoryGrow { mem } => {
@ -156,13 +193,14 @@ impl Fuse {
function_index: self.grow, function_index: self.grow,
}; };
bp.push(ia); bp.push(ia);
p(m, f,mem, &mut bp[0]);
} }
} }
_ => crate::op_traits::rewrite_mem(a, &mut bp, |m, v| { _ => crate::op_traits::rewrite_mem(a, &mut bp, |mem, v| {
if *m != self.target{ if *mem != self.target {
let ia = f.add_value(ValueDef::Operator( let ia = f.add_value(ValueDef::Operator(
Operator::I32Const { Operator::I32Const {
value: m.index() as u32, value: mem.index() as u32,
}, },
vz, vz,
ti, ti,
@ -170,6 +208,7 @@ impl Fuse {
f.append_to_block(k, ia); f.append_to_block(k, ia);
// append_before(f, ia, vi, k); // append_before(f, ia, vi, k);
if let Some(v) = v { if let Some(v) = v {
p(m,f, *mem, &mut *v);
let w = f.arg_pool.from_iter(vec![*v, ia].into_iter()); let w = f.arg_pool.from_iter(vec![*v, ia].into_iter());
let x = f.add_value(ValueDef::Operator( let x = f.add_value(ValueDef::Operator(
Operator::Call { Operator::Call {
@ -182,10 +221,11 @@ impl Fuse {
// crate::append_before(f, x, vi, k); // crate::append_before(f, x, vi, k);
*v = x; *v = x;
} }
*m = self.target; *mem = self.target;
} }
Ok::<(), Infallible>(()) Ok::<(), Infallible>(())
}).unwrap(), })
.unwrap(),
} }
*b = *ka *b = *ka
.entry(bp.clone()) .entry(bp.clone())
@ -201,7 +241,7 @@ pub fn fuse(m: &mut Module) -> anyhow::Result<()> {
let f = Fuse::new(m).context("in getting the fuse funcs")?; let f = Fuse::new(m).context("in getting the fuse funcs")?;
crate::passes::unmem::metafuse_all(m, &mut crate::passes::unmem::All {}); crate::passes::unmem::metafuse_all(m, &mut crate::passes::unmem::All {});
// crate::passes::splice::splice_module(m)?; // crate::passes::splice::splice_module(m)?;
m.per_func_body(|b| f.process(b)); m.take_per_func_body(|m, b| f.process(m, b));
f.finalize(m); f.finalize(m);
return Ok(()); return Ok(());
} }

View file

@ -5,7 +5,7 @@ use std::fmt::Debug;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};
#[derive(Clone, Debug)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct ListPool<T: Clone + Debug> { pub struct ListPool<T: Clone + Debug> {
pub storage: Vec<T>, pub storage: Vec<T>,
} }
@ -16,7 +16,7 @@ impl<T: Clone + Debug> Default for ListPool<T> {
} }
} }
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
pub struct ListRef<T>(u32, u32, PhantomData<T>); pub struct ListRef<T>(u32, u32, PhantomData<T>);
impl<T> Default for ListRef<T> { impl<T> Default for ListRef<T> {