Skip to content

Commit

Permalink
Allow custom sections to specify gc roots
Browse files Browse the repository at this point in the history
This commit adds APIs necessary to allow custom sections to reference
core wasm functions/memories/etc and provide gc roots into them. This is
intended to be used, for example, with wasm interface types.
  • Loading branch information
alexcrichton committed Nov 19, 2019
1 parent c50e1fd commit f8cdb67
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 75 deletions.
11 changes: 11 additions & 0 deletions src/module/custom.rs
@@ -1,5 +1,6 @@
//! Working with custom sections.

use crate::passes::Roots;
use crate::tombstone_arena::{Id, Tombstone, TombstoneArena};
use crate::CodeTransform;
use crate::IdsToIndices;
Expand Down Expand Up @@ -29,6 +30,16 @@ pub trait CustomSection: WalrusAny + Debug + Send + Sync {
/// payload. `walrus` will handle these for you.
fn data(&self, ids_to_indices: &IdsToIndices) -> Cow<[u8]>;

/// Add any core wasm roots to the provided `roots` argument.
///
/// This function will add any referenced core wasm items into the `Roots`
/// array provided.
///
/// The default provided method does nothing.
fn gc_add_roots(&self, roots: &mut Roots) {
drop(roots);
}

/// Apply the given code transformations to this custom section.
///
/// If the module was not configured with `preserve_code_transform = true`,
Expand Down
4 changes: 2 additions & 2 deletions src/passes/gc.rs
Expand Up @@ -4,13 +4,13 @@
//! internally and can be safely removed.

use crate::map::IdHashSet;
use crate::passes::Used;
use crate::passes::used::Used;
use crate::{ImportKind, Module};
use id_arena::Id;

/// Run GC passes over the module specified.
pub fn run(m: &mut Module) {
let used = Used::new(m, m.exports.iter().map(|e| e.id()));
let used = Used::new(m);

let mut unused_imports = Vec::new();
for import in m.imports.iter() {
Expand Down
2 changes: 1 addition & 1 deletion src/passes/mod.rs
Expand Up @@ -3,4 +3,4 @@
pub mod gc;
mod used;
pub mod validate;
pub use self::used::Used;
pub use self::used::Roots;
156 changes: 84 additions & 72 deletions src/passes/used.rs
@@ -1,12 +1,72 @@
use crate::ir::*;
use crate::map::IdHashSet;
use crate::{
ActiveDataLocation, Data, DataId, DataKind, Element, ExportId, ExportItem, Function, InitExpr,
};
use crate::{ActiveDataLocation, Data, DataId, DataKind, Element, ExportItem, Function, InitExpr};
use crate::{FunctionId, FunctionKind, Global, GlobalId};
use crate::{GlobalKind, ImportKind, Memory, MemoryId, Table, TableId};
use crate::{Module, TableKind, Type, TypeId};

/// Set of all root used items in a wasm module.
#[derive(Debug, Default)]
pub struct Roots {
tables: Vec<TableId>,
funcs: Vec<FunctionId>,
globals: Vec<GlobalId>,
memories: Vec<MemoryId>,
datas: Vec<DataId>,
used: Used,
}

impl Roots {
/// Creates a new set of empty roots.
pub fn new() -> Roots {
Roots::default()
}

/// Adds a new function to the set of roots
pub fn push_func(&mut self, func: FunctionId) -> &mut Roots {
if self.used.funcs.insert(func) {
log::trace!("function is used: {:?}", func);
self.funcs.push(func);
}
self
}

/// Adds a new table to the set of roots
pub fn push_table(&mut self, table: TableId) -> &mut Roots {
if self.used.tables.insert(table) {
log::trace!("table is used: {:?}", table);
self.tables.push(table);
}
self
}

/// Adds a new memory to the set of roots
pub fn push_memory(&mut self, memory: MemoryId) -> &mut Roots {
if self.used.memories.insert(memory) {
log::trace!("memory is used: {:?}", memory);
self.memories.push(memory);
}
self
}

/// Adds a new global to the set of roots
pub fn push_global(&mut self, global: GlobalId) -> &mut Roots {
if self.used.globals.insert(global) {
log::trace!("global is used: {:?}", global);
self.globals.push(global);
}
self
}

fn push_data(&mut self, data: DataId) -> &mut Roots {
if self.used.data.insert(data) {
log::trace!("data is used: {:?}", data);
self.datas.push(data);
}
self
}
}

/// Finds the things within a module that are used.
///
/// This is useful for implementing something like a linker's `--gc-sections` so
Expand All @@ -32,29 +92,21 @@ pub struct Used {

impl Used {
/// Construct a new `Used` set for the given module.
pub fn new<R>(module: &Module, roots: R) -> Used
where
R: IntoIterator<Item = ExportId>,
{
pub fn new(module: &Module) -> Used {
log::debug!("starting to calculate used set");
let mut used = Used::default();
let mut stack = UsedStack {
used: &mut used,
functions: Vec::new(),
tables: Vec::new(),
globals: Vec::new(),
memories: Vec::new(),
datas: Vec::new(),
};

for r in roots {
match module.exports.get(r).item {
let mut stack = Roots::default();

// All exports are roots
for export in module.exports.iter() {
match export.item {
ExportItem::Function(f) => stack.push_func(f),
ExportItem::Table(t) => stack.push_table(t),
ExportItem::Memory(m) => stack.push_memory(m),
ExportItem::Global(g) => stack.push_global(g),
}
};
}

// The start function is an implicit root as well
if let Some(f) = module.start {
stack.push_func(f);
}
Expand Down Expand Up @@ -85,13 +137,19 @@ impl Used {
}
}

// And finally ask custom sections for their roots
for (_id, section) in module.customs.iter() {
section.gc_add_roots(&mut stack);
}

// Iteratively visit all items until our stack is empty
while stack.functions.len() > 0
while stack.funcs.len() > 0
|| stack.tables.len() > 0
|| stack.memories.len() > 0
|| stack.globals.len() > 0
|| stack.datas.len() > 0
{
while let Some(f) = stack.functions.pop() {
while let Some(f) = stack.funcs.pop() {
let func = module.funcs.get(f);
stack.used.types.insert(func.ty());

Expand Down Expand Up @@ -151,61 +209,15 @@ impl Used {
}
}

used
}
}

struct UsedStack<'a> {
used: &'a mut Used,
functions: Vec<FunctionId>,
tables: Vec<TableId>,
memories: Vec<MemoryId>,
globals: Vec<GlobalId>,
datas: Vec<DataId>,
}

impl UsedStack<'_> {
fn push_func(&mut self, f: FunctionId) {
log::trace!("function is used: {:?}", f);
if self.used.funcs.insert(f) {
self.functions.push(f);
}
}

fn push_table(&mut self, f: TableId) {
log::trace!("table is used: {:?}", f);
if self.used.tables.insert(f) {
self.tables.push(f);
}
}

fn push_global(&mut self, f: GlobalId) {
log::trace!("global is used: {:?}", f);
if self.used.globals.insert(f) {
self.globals.push(f);
}
}

fn push_memory(&mut self, f: MemoryId) {
log::trace!("memory is used: {:?}", f);
if self.used.memories.insert(f) {
self.memories.push(f);
}
}

fn push_data(&mut self, d: DataId) {
log::trace!("data is used: {:?}", d);
if self.used.data.insert(d) {
self.datas.push(d);
}
stack.used
}
}

struct UsedVisitor<'a, 'b> {
stack: &'a mut UsedStack<'b>,
struct UsedVisitor<'a> {
stack: &'a mut Roots,
}

impl<'expr> Visitor<'expr> for UsedVisitor<'_, '_> {
impl<'expr> Visitor<'expr> for UsedVisitor<'_> {
fn visit_function_id(&mut self, &func: &FunctionId) {
self.stack.push_func(func);
}
Expand Down

0 comments on commit f8cdb67

Please sign in to comment.