Skip to content

Commit

Permalink
more wip
Browse files Browse the repository at this point in the history
  • Loading branch information
alexcrichton committed Nov 27, 2018
1 parent 049c2de commit 98a7a7b
Show file tree
Hide file tree
Showing 18 changed files with 520 additions and 176 deletions.
1 change: 1 addition & 0 deletions crates/anyref-xform/Cargo.toml
Expand Up @@ -12,3 +12,4 @@ Internal anyref transformations for wasm-bindgen

[dependencies]
parity-wasm = "0.35"
wasm-bindgen-wasm-utils = { path = '../wasm-utils', version = '=0.2.28' }
183 changes: 139 additions & 44 deletions crates/anyref-xform/src/lib.rs
Expand Up @@ -16,11 +16,14 @@
//! `anyref` and valid wasm modules going out which use `anyref` at the fringes.

extern crate parity_wasm;
extern crate wasm_bindgen_wasm_utils as wasm_utils;

use std::collections::{BTreeMap, HashMap};

use parity_wasm::elements::*;
use wasm_utils::Remap;

// must be kept in sync with src/lib.rs and ANYREF_HEAP_START
const DEFAULT_MIN: u32 = 32;

/// State of the anyref pass, used to collect information while bindings are
Expand Down Expand Up @@ -73,6 +76,7 @@ struct Sections<'a> {
tables: Option<&'a mut TableSection>,
globals: Option<&'a mut GlobalSection>,
elements: Option<&'a mut ElementSection>,
start: Option<&'a mut u32>,
imported_functions: u32,
}

Expand All @@ -89,24 +93,28 @@ impl Context {
if !self.enabled {
return
}
let s = match module.elements_section() {
Some(s) => s,
None => return,
};
for entry in s.entries() {
let expr = match entry.offset() {
Some(i) => i,
None => continue,
};
let start = match expr.code().get(0) {
Some(Instruction::I32Const(i)) => *i as u32,
_ => continue,
};
let end = start + entry.members().len() as u32;
if end > self.next_element {
self.next_element = end;
let mut sections = Sections::from(module);
if let Some(s) = &mut sections.elements {
for entry in s.entries() {
let expr = match entry.offset() {
Some(i) => i,
None => continue,
};
let start = match expr.code().get(0) {
Some(Instruction::I32Const(i)) => *i as u32,
_ => continue,
};
let end = start + entry.members().len() as u32;
if end > self.next_element {
self.next_element = end;
}
}
}

// Add in anyref tables/globals so we can use them in other various
// passes
self.inject_tables(&mut sections);
self.inject_globals(&mut sections);
}

/// Store information about an imported function that needs to be
Expand Down Expand Up @@ -178,44 +186,31 @@ impl Context {
}
}

pub fn anyref_table_idx(&self) -> u32 {
self.table.unwrap()
}

pub fn run(&mut self, module: &mut Module) {
if !self.enabled {
return
}
self.xform(module).remap_module(module)
}

fn xform(&mut self, module: &mut Module) -> Remap<impl FnMut(u32) -> u32> {
// Sort of a hack for now, but ensure that these two sections are
// present unconditionally as we're going to be injecting items into
// them later.
self.ensure_table_section(module);
self.ensure_global_section(module);
self.ensure_start_section(module);

// Probe for all the sections and store them all in a struct with fields
// so we can take disjoint borrows on each struct field.
let mut sections = Sections::default();
for section in module.sections_mut() {
match section {
Section::Code(s) => sections.codes = Some(s),
Section::Type(s) => sections.types = Some(s),
Section::Export(s) => sections.exports = Some(s),
Section::Function(s) => sections.functions = Some(s),
Section::Table(s) => sections.tables = Some(s),
Section::Global(s) => sections.globals = Some(s),
Section::Element(s) => sections.elements = Some(s),
Section::Import(s) => {
sections.imported_functions = s.functions() as u32;
sections.imports = Some(s);
}
Section::Name(NameSection::Function(s)) => {
sections.function_names = Some(s);
}
_ => {}
}
}
let mut sections = Sections::from(module);

// Add an anyref table, a stack pointer, and detect all the various
// intrinsics and such. This will also along the way inject an intrinsic
// for cloning an anyref.
self.inject_tables(&mut sections);
self.inject_globals(&mut sections);
// Detect all the various intrinsics and such. This will also along the
// way inject an intrinsic for cloning an anyref.
self.find_intrinsics(&mut sections);

// And with all that data prepared now actually perform transformations
Expand All @@ -230,10 +225,13 @@ impl Context {
self.process_elements(&mut sections);
assert!(self.elements.is_empty());

// Last but not least, perform all instruction transformations to
// rewrite calls between functions and make sure everything is still
// hooked up right.
// Perform all instruction transformations to rewrite calls between
// functions and make sure everything is still hooked up right.
self.rewrite_calls(&mut sections, nbodies);

// Inject initialization routine to set up default slots in the table
// (things like null/undefined/true/false)
self.inject_initialization(&mut sections)
}

fn ensure_table_section(&mut self, module: &mut Module) {
Expand Down Expand Up @@ -284,6 +282,32 @@ impl Context {
module.sections_mut().insert(pos, section);
}

fn ensure_start_section(&mut self, module: &mut Module) {
let mut pos = None;
// See this URL for section orderings:
//
// https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#high-level-structure
for i in 0..module.sections().len() {
match &mut module.sections_mut()[i] {
Section::Type(_) |
Section::Import(_) |
Section::Function(_) |
Section::Table(_) |
Section::Memory(_) |
Section::Global(_) |
Section::Export(_) => continue,
Section::Start(_) => return,
_ => {}
}
pos = Some(i);
break
}
let section = Section::Start(u32::max_value());
let len = module.sections().len();
let pos = pos.unwrap_or_else(|| len - 1);
module.sections_mut().insert(pos, section);
}

/// Inject an `anyref` table to use for the stack and the heap.
fn inject_tables(&mut self, sections: &mut Sections) {
let mut offset = 0;
Expand Down Expand Up @@ -826,4 +850,75 @@ impl Context {
}
}
}

fn inject_initialization(&mut self, sections: &mut Sections)
-> Remap<impl FnMut(u32) -> u32>
{
let start = sections.start.as_mut().unwrap();
let types = sections.types.as_mut().unwrap();
let codes = sections.codes.as_mut().unwrap();
let imports = sections.imports.as_mut().unwrap();

// If there was a preexisting start function then we update its first
// instruction to initialization of the anyref table.
if **start != u32::max_value() {
assert!(**start >= sections.imported_functions);
let code_idx = **start - sections.imported_functions;
let code = &mut codes.bodies_mut()[code_idx as usize];
code
.code_mut()
.elements_mut()
.insert(0, Instruction::Call(u32::max_value()));
}

let type_idx = types.types().len() as u32;
let ty = Type::Function(FunctionType::new(Vec::new(), None));
types.types_mut().push(ty);
let entry = ImportEntry::new(
"__wbindgen_placeholder__".to_string(),
"__wbindgen_init_anyref_table".to_string(),
External::Function(type_idx),
);
imports.entries_mut().push(entry);

let prev_function_imports = sections.imported_functions;
Remap {
remap: move |idx| {
if idx < prev_function_imports {
idx
} else if idx == u32::max_value() {
prev_function_imports
} else {
idx + 1
}
},
}
}
}

impl<'a> Sections<'a> {
fn from(module: &'a mut Module) -> Sections<'a> {
let mut sections = Sections::default();
for section in module.sections_mut() {
match section {
Section::Code(s) => sections.codes = Some(s),
Section::Type(s) => sections.types = Some(s),
Section::Export(s) => sections.exports = Some(s),
Section::Function(s) => sections.functions = Some(s),
Section::Table(s) => sections.tables = Some(s),
Section::Global(s) => sections.globals = Some(s),
Section::Element(s) => sections.elements = Some(s),
Section::Start(s) => sections.start = Some(s),
Section::Import(s) => {
sections.imported_functions = s.functions() as u32;
sections.imports = Some(s);
}
Section::Name(NameSection::Function(s)) => {
sections.function_names = Some(s);
}
_ => {}
}
}
return sections;
}
}
54 changes: 50 additions & 4 deletions crates/cli-support/src/js/mod.rs
Expand Up @@ -440,6 +440,20 @@ impl<'a> Context<'a> {
self.unexport_unused_internal_exports();
self.export_table();

// After the anyref pass has executed, if this intrinsic is needed then
// we expose a function which initializes it
self.bind("__wbindgen_init_anyref_table", &|me| {
me.expose_anyref_table();
Ok(String::from("function() {
const table = wasm.__wbg_anyref_table;
const offset = table.grow(4);
//table.set(offset + 0, undefined);
table.set(offset + 1, null);
table.set(offset + 2, true);
table.set(offset + 3, false);
}"))
})?;

self.gc();

// make sure that the anyref pass runs before binding this as anyref may
Expand Down Expand Up @@ -506,6 +520,11 @@ impl<'a> Context<'a> {
self.update_producers_section();
self.append_gc_section();

let mut has_start_function = false;
if self.config.no_modules {
has_start_function = self.unstart_start_function();
}

let mut js = if self.config.threads.is_some() {
// TODO: It's not clear right now how to best use threads with
// bundlers like webpack. We need a way to get the existing
Expand All @@ -531,6 +550,7 @@ impl<'a> Context<'a> {
}
memory.push_str("})");

assert!(has_start_function);
format!(
"\
(function() {{
Expand Down Expand Up @@ -563,6 +583,7 @@ impl<'a> Context<'a> {
init.__wbindgen_wasm_instance = instance;
init.__wbindgen_wasm_module = module;
init.__wbindgen_wasm_memory = __exports.memory;
wasm.__wbindgen_start();
}});
}};
self.{global_name} = Object.assign(init, __exports);
Expand Down Expand Up @@ -602,7 +623,8 @@ impl<'a> Context<'a> {
}}
return instantiation.then(({{instance}}) => {{
wasm = init.wasm = instance.exports;
return;
init.__wbindgen_wasm_instance = instance;
{start}
}});
}};
self.{global_name} = Object.assign(init, __exports);
Expand All @@ -613,6 +635,11 @@ impl<'a> Context<'a> {
.as_ref()
.map(|s| &**s)
.unwrap_or("wasm_bindgen"),
start = if has_start_function {
"wasm.__wbindgen_start();"
} else {
""
},
)
} else {
let import_wasm = if self.globals.len() == 0 {
Expand Down Expand Up @@ -642,8 +669,6 @@ impl<'a> Context<'a> {
)
};

self.gc();

while js.contains("\n\n\n") {
js = js.replace("\n\n\n", "\n\n");
}
Expand Down Expand Up @@ -2197,7 +2222,7 @@ impl<'a> Context<'a> {
};
let entry = ExportEntry::new(
"__wbg_anyref_table".to_string(),
Internal::Table(1), // TODO: verify this
Internal::Table(self.anyref.anyref_table_idx()),
);
exports.entries_mut().push(entry);
break;
Expand Down Expand Up @@ -2258,6 +2283,27 @@ impl<'a> Context<'a> {
};
self.module.sections_mut().insert(0, section);
}

fn unstart_start_function(&mut self) -> bool {
let mut start = None;
for (i, section) in self.module.sections().iter().enumerate() {
if let Section::Start(idx) = section {
start = Some((i, *idx));
break
}
}
let (i, idx) = match start {
Some(p) => p,
None => return false,
};
self.module.sections_mut().remove(i);
let entry = ExportEntry::new(
"__wbindgen_start".to_string(),
Internal::Function(idx),
);
self.module.export_section_mut().unwrap().entries_mut().push(entry);
true
}
}

impl<'a, 'b> SubContext<'a, 'b> {
Expand Down
3 changes: 2 additions & 1 deletion crates/cli-support/src/js/rust2js.rs
Expand Up @@ -661,9 +661,10 @@ impl<'a, 'b> Rust2Js<'a, 'b> {
let catch = if self.cx.config.anyref {
self.cx.expose_add_to_anyref_table()?;
"\
const idx = addToAnyrefTable(e);
const view = getUint32Memory();
view[exnptr / 4] = 1;
view[exnptr / 4 + 1] = addToAnyrefTable(e);
view[exnptr / 4 + 1] = idx;
"
} else {
self.cx.expose_add_heap_object();
Expand Down

0 comments on commit 98a7a7b

Please sign in to comment.