use crate::component::dfg::CoreDef;
use crate::component::{
Adapter, AdapterOptions as AdapterOptionsDfg, ComponentTypesBuilder, FlatType, InterfaceType,
StringEncoding, TypeFuncIndex,
};
use crate::fact::transcode::Transcoder;
use crate::{EntityRef, FuncIndex, GlobalIndex, MemoryIndex, PrimaryMap};
use std::collections::HashMap;
use wasm_encoder::*;
mod core_types;
mod signature;
mod trampoline;
mod transcode;
mod traps;
pub use self::transcode::{FixedEncoding, Transcode};
pub struct Module<'a> {
debug: bool,
types: &'a ComponentTypesBuilder,
core_types: core_types::CoreTypes,
core_imports: ImportSection,
imports: Vec<Import>,
imported: HashMap<CoreDef, usize>,
imported_transcoders: HashMap<Transcoder, FuncIndex>,
imported_funcs: PrimaryMap<FuncIndex, Option<CoreDef>>,
imported_memories: PrimaryMap<MemoryIndex, CoreDef>,
imported_globals: PrimaryMap<GlobalIndex, CoreDef>,
funcs: PrimaryMap<FunctionId, Function>,
helper_funcs: HashMap<Helper, FunctionId>,
helper_worklist: Vec<(FunctionId, Helper)>,
}
struct AdapterData {
name: String,
lift: AdapterOptions,
lower: AdapterOptions,
callee: FuncIndex,
called_as_export: bool,
}
struct AdapterOptions {
ty: TypeFuncIndex,
flags: GlobalIndex,
post_return: Option<FuncIndex>,
options: Options,
}
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
struct Options {
string_encoding: StringEncoding,
memory64: bool,
memory: Option<MemoryIndex>,
realloc: Option<FuncIndex>,
}
enum Context {
Lift,
Lower,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct Helper {
src: HelperType,
dst: HelperType,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct HelperType {
ty: InterfaceType,
opts: Options,
loc: HelperLocation,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
enum HelperLocation {
Stack,
Memory,
}
impl<'a> Module<'a> {
pub fn new(types: &'a ComponentTypesBuilder, debug: bool) -> Module<'a> {
Module {
debug,
types,
core_types: Default::default(),
core_imports: Default::default(),
imported: Default::default(),
imports: Default::default(),
imported_transcoders: Default::default(),
imported_funcs: PrimaryMap::new(),
imported_memories: PrimaryMap::new(),
imported_globals: PrimaryMap::new(),
funcs: PrimaryMap::new(),
helper_funcs: HashMap::new(),
helper_worklist: Vec::new(),
}
}
pub fn adapt(&mut self, name: &str, adapter: &Adapter) {
let mut lift = self.import_options(adapter.lift_ty, &adapter.lift_options);
let lower = self.import_options(adapter.lower_ty, &adapter.lower_options);
assert!(adapter.lower_options.post_return.is_none());
let signature = self.types.signature(&lift, Context::Lift);
let ty = self
.core_types
.function(&signature.params, &signature.results);
let callee = self.import_func("callee", name, ty, adapter.func.clone());
lift.post_return = adapter.lift_options.post_return.as_ref().map(|func| {
let ty = self.core_types.function(&signature.results, &[]);
self.import_func("post_return", name, ty, func.clone())
});
trampoline::compile(
self,
&AdapterData {
name: name.to_string(),
lift,
lower,
callee,
called_as_export: true,
},
);
while let Some((result, helper)) = self.helper_worklist.pop() {
trampoline::compile_helper(self, result, helper);
}
}
fn import_options(&mut self, ty: TypeFuncIndex, options: &AdapterOptionsDfg) -> AdapterOptions {
let AdapterOptionsDfg {
instance,
string_encoding,
memory,
memory64,
realloc,
post_return: _, } = options;
let flags = self.import_global(
"flags",
&format!("instance{}", instance.as_u32()),
GlobalType {
val_type: ValType::I32,
mutable: true,
},
CoreDef::InstanceFlags(*instance),
);
let memory = memory.as_ref().map(|memory| {
self.import_memory(
"memory",
"",
MemoryType {
minimum: 0,
maximum: None,
shared: false,
memory64: *memory64,
},
memory.clone().into(),
)
});
let realloc = realloc.as_ref().map(|func| {
let ptr = if *memory64 {
ValType::I64
} else {
ValType::I32
};
let ty = self.core_types.function(&[ptr, ptr, ptr, ptr], &[ptr]);
self.import_func("realloc", "", ty, func.clone())
});
AdapterOptions {
ty,
flags,
post_return: None,
options: Options {
string_encoding: *string_encoding,
memory64: *memory64,
memory,
realloc,
},
}
}
fn import_func(&mut self, module: &str, name: &str, ty: u32, def: CoreDef) -> FuncIndex {
self.import(module, name, EntityType::Function(ty), def, |m| {
&mut m.imported_funcs
})
}
fn import_global(
&mut self,
module: &str,
name: &str,
ty: GlobalType,
def: CoreDef,
) -> GlobalIndex {
self.import(module, name, EntityType::Global(ty), def, |m| {
&mut m.imported_globals
})
}
fn import_memory(
&mut self,
module: &str,
name: &str,
ty: MemoryType,
def: CoreDef,
) -> MemoryIndex {
self.import(module, name, EntityType::Memory(ty), def, |m| {
&mut m.imported_memories
})
}
fn import<K: EntityRef, V: From<CoreDef>>(
&mut self,
module: &str,
name: &str,
ty: EntityType,
def: CoreDef,
map: impl FnOnce(&mut Self) -> &mut PrimaryMap<K, V>,
) -> K {
if let Some(prev) = self.imported.get(&def) {
return K::new(*prev);
}
let idx = map(self).push(def.clone().into());
self.core_imports.import(module, name, ty);
self.imported.insert(def.clone(), idx.index());
self.imports.push(Import::CoreDef(def));
idx
}
fn import_transcoder(&mut self, transcoder: transcode::Transcoder) -> FuncIndex {
*self
.imported_transcoders
.entry(transcoder)
.or_insert_with(|| {
let name = transcoder.name();
let ty = transcoder.ty(&mut self.core_types);
self.core_imports.import("transcode", &name, ty);
let from = self.imported_memories[transcoder.from_memory].clone();
let to = self.imported_memories[transcoder.to_memory].clone();
self.imports.push(Import::Transcode {
op: transcoder.op,
from,
from64: transcoder.from_memory64,
to,
to64: transcoder.to_memory64,
});
self.imported_funcs.push(None)
})
}
fn translate_helper(&mut self, helper: Helper) -> FunctionId {
*self.helper_funcs.entry(helper).or_insert_with(|| {
let ty = helper.core_type(self.types, &mut self.core_types);
let id = self.funcs.push(Function::new(None, ty));
self.helper_worklist.push((id, helper));
id
})
}
pub fn encode(&mut self) -> Vec<u8> {
let mut funcs = FunctionSection::new();
let mut exports = ExportSection::new();
let mut id_to_index = PrimaryMap::<FunctionId, FuncIndex>::new();
for (id, func) in self.funcs.iter() {
assert!(func.filled_in);
let idx = FuncIndex::from_u32(self.imported_funcs.next_key().as_u32() + id.as_u32());
let id2 = id_to_index.push(idx);
assert_eq!(id2, id);
funcs.function(func.ty);
if let Some(name) = &func.export {
exports.export(name, ExportKind::Func, idx.as_u32());
}
}
let mut code = CodeSection::new();
let mut traps = traps::TrapSection::default();
for (id, func) in self.funcs.iter() {
let mut func_traps = Vec::new();
let mut body = Vec::new();
func.locals.len().encode(&mut body);
for (count, ty) in func.locals.iter() {
count.encode(&mut body);
ty.encode(&mut body);
}
for chunk in func.body.iter() {
match chunk {
Body::Raw(code, traps) => {
let start = body.len();
body.extend_from_slice(code);
for (offset, trap) in traps {
func_traps.push((start + offset, *trap));
}
}
Body::Call(id) => {
Instruction::Call(id_to_index[*id].as_u32()).encode(&mut body);
}
}
}
code.raw(&body);
traps.append(id_to_index[id].as_u32(), func_traps);
}
let traps = traps.finish();
let mut result = wasm_encoder::Module::new();
result.section(&self.core_types.section);
result.section(&self.core_imports);
result.section(&funcs);
result.section(&exports);
result.section(&code);
if self.debug {
result.section(&CustomSection {
name: "wasmtime-trampoline-traps",
data: &traps,
});
}
result.finish()
}
pub fn imports(&self) -> &[Import] {
&self.imports
}
}
#[derive(Clone)]
pub enum Import {
CoreDef(CoreDef),
Transcode {
op: Transcode,
from: CoreDef,
from64: bool,
to: CoreDef,
to64: bool,
},
}
impl Options {
fn ptr(&self) -> ValType {
if self.memory64 {
ValType::I64
} else {
ValType::I32
}
}
fn ptr_size(&self) -> u8 {
if self.memory64 {
8
} else {
4
}
}
fn flat_types<'a>(
&self,
ty: &InterfaceType,
types: &'a ComponentTypesBuilder,
) -> Option<&'a [FlatType]> {
let flat = types.flat_types(ty)?;
Some(if self.memory64 {
flat.memory64
} else {
flat.memory32
})
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct FunctionId(u32);
cranelift_entity::entity_impl!(FunctionId);
struct Function {
filled_in: bool,
ty: u32,
locals: Vec<(u32, ValType)>,
export: Option<String>,
body: Vec<Body>,
}
enum Body {
Raw(Vec<u8>, Vec<(usize, traps::Trap)>),
Call(FunctionId),
}
impl Function {
fn new(export: Option<String>, ty: u32) -> Function {
Function {
filled_in: false,
ty,
locals: Vec::new(),
export,
body: Vec::new(),
}
}
}
impl Helper {
fn core_type(
&self,
types: &ComponentTypesBuilder,
core_types: &mut core_types::CoreTypes,
) -> u32 {
let mut params = Vec::new();
let mut results = Vec::new();
self.src.push_flat(&mut params, types);
match self.dst.loc {
HelperLocation::Stack => self.dst.push_flat(&mut results, types),
HelperLocation::Memory => params.push(self.dst.opts.ptr()),
}
core_types.function(¶ms, &results)
}
}
impl HelperType {
fn push_flat(&self, dst: &mut Vec<ValType>, types: &ComponentTypesBuilder) {
match self.loc {
HelperLocation::Stack => {
for ty in self.opts.flat_types(&self.ty, types).unwrap() {
dst.push((*ty).into());
}
}
HelperLocation::Memory => {
dst.push(self.opts.ptr());
}
}
}
}