Restructure internals of wasm-gc

This commit restructures some of the internals of `wasm-gc` now that
I've actually got a better grasp on the wasm format and what all the
ownership edges look like. This shouldn't actually result in any
user-facing changes, but should make us be a bit more compatible with
operations in the future.

Memories/tables/elements/segments are no longer considered automatic
roots but rather need to be rooted by something else to prevent a gc.
For example an element section is gc'd along with a table if the table
is never referenced, along with data segments as well if the memory
isn't referenced.

Additionally all index sets now don't contained offseted indices, but
rather everything is always stored relative to the "index space" to
ensure consistency.

This should make it a bit easier to add future items to gc!
This commit is contained in:
Alex Crichton 2018-10-18 12:46:59 -07:00
parent 1fa407d2f9
commit a0fc095407

View File

@ -54,37 +54,41 @@ fn run(config: &mut Config, module: &mut Module) {
let mut cx = LiveContext::new(&module); let mut cx = LiveContext::new(&module);
cx.blacklist.insert("rust_eh_personality"); cx.blacklist.insert("rust_eh_personality");
// always treat memory as a root
cx.add_memory(0);
// All exports are a root
if let Some(section) = module.export_section() { if let Some(section) = module.export_section() {
for (i, entry) in section.entries().iter().enumerate() { for (i, entry) in section.entries().iter().enumerate() {
if cx.blacklist.contains(entry.field()) {
continue
}
cx.add_export_entry(entry, i as u32); cx.add_export_entry(entry, i as u32);
} }
} }
if let Some(section) = module.import_section() {
for (i, entry) in section.entries().iter().enumerate() { // Pessimistically assume all passive data and table segments are
debug!("import {:?}", entry); // required
if let External::Memory(_) = *entry.external() {
cx.add_import_entry(entry, i as u32);
}
}
}
if let Some(section) = module.data_section() { if let Some(section) = module.data_section() {
for entry in section.entries() { for entry in section.entries() {
if entry.passive() {
cx.add_data_segment(entry); cx.add_data_segment(entry);
} }
} }
if let Some(tables) = module.table_section() {
for i in 0..tables.entries().len() as u32 {
cx.add_table(i);
}
} }
if let Some(elements) = module.elements_section() { if let Some(elements) = module.elements_section() {
for seg in elements.entries() { for seg in elements.entries() {
if seg.passive() {
cx.add_element_segment(seg); cx.add_element_segment(seg);
} }
} }
}
// The start function is also a root
if let Some(i) = module.start_section() { if let Some(i) = module.start_section() {
cx.add_function(i); cx.add_function(i);
} }
cx.analysis cx.analysis
}; };
@ -129,7 +133,6 @@ fn run(config: &mut Config, module: &mut Module) {
#[derive(Default)] #[derive(Default)]
struct Analysis { struct Analysis {
codes: BitSet,
tables: BitSet, tables: BitSet,
memories: BitSet, memories: BitSet,
globals: BitSet, globals: BitSet,
@ -137,67 +140,56 @@ struct Analysis {
imports: BitSet, imports: BitSet,
exports: BitSet, exports: BitSet,
functions: BitSet, functions: BitSet,
all_globals: BitSet, imported_functions: u32,
} imported_globals: u32,
imported_memories: u32,
enum Memories<'a> { imported_tables: u32,
Exported(&'a MemorySection),
Imported(&'a MemoryType),
}
impl<'a> Memories<'a> {
fn has_entry(&self, idx: usize) -> bool {
match *self {
Memories::Exported(memory_section) => idx < memory_section.entries().len(),
Memories::Imported(_) => idx == 0,
}
}
} }
struct LiveContext<'a> { struct LiveContext<'a> {
blacklist: HashSet<&'static str>, blacklist: HashSet<&'static str>,
function_section: Option<&'a FunctionSection>, function_section: Option<&'a FunctionSection>,
type_section: Option<&'a TypeSection>, type_section: Option<&'a TypeSection>,
data_section: Option<&'a DataSection>,
element_section: Option<&'a ElementSection>,
code_section: Option<&'a CodeSection>, code_section: Option<&'a CodeSection>,
table_section: Option<&'a TableSection>, table_section: Option<&'a TableSection>,
memories: Option<Memories<'a>>,
global_section: Option<&'a GlobalSection>, global_section: Option<&'a GlobalSection>,
import_section: Option<&'a ImportSection>, import_section: Option<&'a ImportSection>,
imported_functions: u32,
imported_globals: u32,
analysis: Analysis, analysis: Analysis,
} }
impl<'a> LiveContext<'a> { impl<'a> LiveContext<'a> {
fn new(module: &'a Module) -> LiveContext<'a> { fn new(module: &'a Module) -> LiveContext<'a> {
let memories = module.memory_section().map(Memories::Exported).or_else(|| { let (mut tables, mut memories, mut globals, mut functions) = (0, 0, 0, 0);
if let Some(import_section) = module.import_section() { if let Some(imports) = module.import_section() {
for entry in import_section.entries() { for entry in imports.entries() {
if let External::Memory(ref memory_type) = *entry.external() { match entry.external() {
return Some(Memories::Imported(memory_type)); External::Memory(_) => memories += 1,
External::Table(_) => tables += 1,
External::Function(_) => functions += 1,
External::Global(_) => globals += 1,
} }
} }
} }
None
});
LiveContext { LiveContext {
blacklist: HashSet::new(), blacklist: HashSet::new(),
function_section: module.function_section(), function_section: module.function_section(),
type_section: module.type_section(), type_section: module.type_section(),
data_section: module.data_section(),
element_section: module.elements_section(),
code_section: module.code_section(), code_section: module.code_section(),
table_section: module.table_section(), table_section: module.table_section(),
memories: memories,
global_section: module.global_section(), global_section: module.global_section(),
import_section: module.import_section(), import_section: module.import_section(),
imported_functions: module.import_section() analysis: Analysis {
.map(|s| s.functions()) imported_functions: functions,
.unwrap_or(0) as u32, imported_globals: globals,
imported_globals: module.import_section() imported_tables: tables,
.map(|s| s.globals()) imported_memories: memories,
.unwrap_or(0) as u32, ..Analysis::default()
analysis: Analysis::default(), },
} }
} }
@ -205,10 +197,10 @@ impl<'a> LiveContext<'a> {
if !self.analysis.functions.insert(idx) { if !self.analysis.functions.insert(idx) {
return return
} }
debug!("adding function: {}", idx);
if idx < self.imported_functions { if idx < self.analysis.imported_functions {
let imports = self.import_section.unwrap(); let imports = self.import_section.unwrap();
debug!("adding import: {}", idx);
let (i, import) = imports.entries() let (i, import) = imports.entries()
.iter() .iter()
.enumerate() .enumerate()
@ -222,36 +214,32 @@ impl<'a> LiveContext<'a> {
.next() .next()
.expect("expected an imported function with this index"); .expect("expected an imported function with this index");
let i = i as u32; let i = i as u32;
self.analysis.imports.insert(i);
return self.add_import_entry(import, i); return self.add_import_entry(import, i);
} }
let idx = idx - self.imported_functions; let idx = idx - self.analysis.imported_functions;
if !self.analysis.codes.insert(idx) {
return
}
debug!("adding function: {}", idx);
let functions = self.function_section.expect("no functions section"); let functions = self.function_section.expect("no functions section");
self.add_type(functions.entries()[idx as usize].type_ref()); self.add_type(functions.entries()[idx as usize].type_ref());
let codes = self.code_section.expect("no codes section"); let codes = self.code_section.expect("no codes section");
self.add_func_body(&codes.bodies()[idx as usize]); self.add_func_body(&codes.bodies()[idx as usize]);
} }
fn add_table(&mut self, mut idx: u32) { fn add_table(&mut self, idx: u32) {
if let Some(imports) = self.import_section { if !self.analysis.tables.insert(idx) {
let imported_tables = imports.entries() return
.iter()
.filter(|i| {
match *i.external() {
External::Table(_) => true,
_ => false,
} }
}) debug!("adding table: {}", idx);
.count();
let imported_tables = imported_tables as u32; // Add all element segments that initialize this table
if idx < imported_tables { if let Some(elements) = self.element_section {
debug!("adding table import: {}", idx); for entry in elements.entries().iter().filter(|d| !d.passive()) {
if entry.index() == idx {
self.add_element_segment(entry);
}
}
}
if idx < self.analysis.imported_tables {
let imports = self.import_section.unwrap();
let (i, import) = imports.entries() let (i, import) = imports.entries()
.iter() .iter()
.enumerate() .enumerate()
@ -265,14 +253,9 @@ impl<'a> LiveContext<'a> {
.next() .next()
.expect("expected an imported table with this index"); .expect("expected an imported table with this index");
let i = i as u32; let i = i as u32;
self.analysis.imports.insert(i);
return self.add_import_entry(import, i); return self.add_import_entry(import, i);
} }
idx -= imported_tables; let idx = idx - self.analysis.imported_tables;
}
if !self.analysis.tables.insert(idx) {
return
}
let tables = self.table_section.expect("no table section"); let tables = self.table_section.expect("no table section");
let table = &tables.entries()[idx as usize]; let table = &tables.entries()[idx as usize];
drop(table); drop(table);
@ -282,18 +265,47 @@ impl<'a> LiveContext<'a> {
if !self.analysis.memories.insert(idx) { if !self.analysis.memories.insert(idx) {
return return
} }
let memories = self.memories.as_ref().expect("no memory section or imported memory"); debug!("adding memory: {}", idx);
assert!(memories.has_entry(idx as usize));
// Add all data segments that initialize this memory
if let Some(data) = self.data_section {
for entry in data.entries().iter().filter(|d| !d.passive()) {
if entry.index() == idx {
self.add_data_segment(entry);
}
}
}
// ... and add the import if it's an imported memory ..
if idx < self.analysis.imported_memories {
let imports = self.import_section.unwrap();
let (i, import) = imports.entries()
.iter()
.enumerate()
.filter(|&(_, i)| {
match *i.external() {
External::Memory(_) => true,
_ => false,
}
})
.skip(idx as usize)
.next()
.expect("expected an imported memory with this index");
let i = i as u32;
return self.add_import_entry(import, i);
}
// ... and if it's not imported that's it!
} }
fn add_global(&mut self, idx: u32) { fn add_global(&mut self, idx: u32) {
if !self.analysis.all_globals.insert(idx) { if !self.analysis.globals.insert(idx) {
return return
} }
debug!("adding global: {}", idx);
if idx < self.imported_globals { if idx < self.analysis.imported_globals {
let imports = self.import_section.unwrap(); let imports = self.import_section.unwrap();
debug!("adding global import: {}", idx);
let (i, import) = imports.entries() let (i, import) = imports.entries()
.iter() .iter()
.enumerate() .enumerate()
@ -307,14 +319,10 @@ impl<'a> LiveContext<'a> {
.next() .next()
.expect("expected an imported global with this index"); .expect("expected an imported global with this index");
let i = i as u32; let i = i as u32;
self.analysis.imports.insert(i);
return self.add_import_entry(import, i); return self.add_import_entry(import, i);
} }
let idx = idx - self.imported_globals;
if !self.analysis.globals.insert(idx) { let idx = idx - self.analysis.imported_globals;
return
}
let globals = self.global_section.expect("no global section"); let globals = self.global_section.expect("no global section");
let global = &globals.entries()[idx as usize]; let global = &globals.entries()[idx as usize];
self.add_global_type(global.global_type()); self.add_global_type(global.global_type());
@ -377,7 +385,10 @@ impl<'a> LiveContext<'a> {
Instruction::Loop(ref b) | Instruction::Loop(ref b) |
Instruction::If(ref b) => self.add_block_type(b), Instruction::If(ref b) => self.add_block_type(b),
Instruction::Call(f) => self.add_function(f), Instruction::Call(f) => self.add_function(f),
Instruction::CallIndirect(t, _) => self.add_type(t), Instruction::CallIndirect(t, _) => {
self.add_type(t);
self.add_table(0);
}
Instruction::GetGlobal(i) | Instruction::GetGlobal(i) |
Instruction::SetGlobal(i) => self.add_global(i), Instruction::SetGlobal(i) => self.add_global(i),
_ => {} _ => {}
@ -392,10 +403,9 @@ impl<'a> LiveContext<'a> {
} }
fn add_export_entry(&mut self, entry: &ExportEntry, idx: u32) { fn add_export_entry(&mut self, entry: &ExportEntry, idx: u32) {
if self.blacklist.contains(entry.field()) { if !self.analysis.exports.insert(idx) {
return return
} }
self.analysis.exports.insert(idx);
match *entry.internal() { match *entry.internal() {
Internal::Function(i) => self.add_function(i), Internal::Function(i) => self.add_function(i),
Internal::Table(i) => self.add_table(i), Internal::Table(i) => self.add_table(i),
@ -405,14 +415,15 @@ impl<'a> LiveContext<'a> {
} }
fn add_import_entry(&mut self, entry: &ImportEntry, idx: u32) { fn add_import_entry(&mut self, entry: &ImportEntry, idx: u32) {
if !self.analysis.imports.insert(idx) {
return
}
debug!("adding import: {}", idx);
match *entry.external() { match *entry.external() {
External::Function(i) => self.add_type(i), External::Function(i) => self.add_type(i),
External::Table(_) => {}, External::Table(_) => {}
External::Memory(_) => { External::Memory(_) => {}
self.add_memory(0); External::Global(g) => self.add_value_type(&g.content_type()),
self.analysis.imports.insert(idx);
},
External::Global(_) => {},
} }
} }
@ -487,7 +498,7 @@ impl<'a> RemapContext<'a> {
} }
if let Some(s) = m.function_section() { if let Some(s) = m.function_section() {
for i in 0..(s.entries().len() as u32) { for i in 0..(s.entries().len() as u32) {
if analysis.codes.contains(&i) { if analysis.functions.contains(&(i + analysis.imported_functions)) {
functions.push(nfunctions); functions.push(nfunctions);
nfunctions += 1; nfunctions += 1;
} else { } else {
@ -498,7 +509,7 @@ impl<'a> RemapContext<'a> {
} }
if let Some(s) = m.global_section() { if let Some(s) = m.global_section() {
for i in 0..(s.entries().len() as u32) { for i in 0..(s.entries().len() as u32) {
if analysis.globals.contains(&i) { if analysis.globals.contains(&(i + analysis.imported_globals)) {
globals.push(nglobals); globals.push(nglobals);
nglobals += 1; nglobals += 1;
} else { } else {
@ -509,7 +520,7 @@ impl<'a> RemapContext<'a> {
} }
if let Some(s) = m.table_section() { if let Some(s) = m.table_section() {
for i in 0..(s.entries().len() as u32) { for i in 0..(s.entries().len() as u32) {
if analysis.tables.contains(&i) { if analysis.tables.contains(&(i + analysis.imported_tables)) {
tables.push(ntables); tables.push(ntables);
ntables += 1; ntables += 1;
} else { } else {
@ -520,7 +531,7 @@ impl<'a> RemapContext<'a> {
} }
if let Some(s) = m.memory_section() { if let Some(s) = m.memory_section() {
for i in 0..(s.entries().len() as u32) { for i in 0..(s.entries().len() as u32) {
if analysis.memories.contains(&i) { if analysis.memories.contains(&(i + analysis.imported_memories)) {
memories.push(nmemories); memories.push(nmemories);
nmemories += 1; nmemories += 1;
} else { } else {
@ -541,9 +552,9 @@ impl<'a> RemapContext<'a> {
} }
} }
fn retain<T>(&self, set: &BitSet, list: &mut Vec<T>, name: &str) { fn retain<T>(&self, set: &BitSet, list: &mut Vec<T>, name: &str, offset: u32) {
for i in (0..list.len()).rev().map(|x| x as u32) { for i in (0..list.len()).rev().map(|x| x as u32) {
if !set.contains(&i) { if !set.contains(&(i + offset)) {
debug!("removing {} {}", name, i); debug!("removing {} {}", name, i);
list.remove(i as usize); list.remove(i as usize);
} }
@ -551,7 +562,7 @@ impl<'a> RemapContext<'a> {
} }
fn remap_type_section(&self, s: &mut TypeSection) -> bool { fn remap_type_section(&self, s: &mut TypeSection) -> bool {
self.retain(&self.analysis.types, s.types_mut(), "type"); self.retain(&self.analysis.types, s.types_mut(), "type", 0);
for t in s.types_mut() { for t in s.types_mut() {
self.remap_type(t); self.remap_type(t);
} }
@ -574,11 +585,17 @@ impl<'a> RemapContext<'a> {
} }
fn remap_value_type(&self, t: &mut ValueType) { fn remap_value_type(&self, t: &mut ValueType) {
drop(t); match t {
ValueType::I32 => {}
ValueType::I64 => {}
ValueType::F32 => {}
ValueType::F64 => {}
ValueType::V128 => {}
}
} }
fn remap_import_section(&self, s: &mut ImportSection) -> bool { fn remap_import_section(&self, s: &mut ImportSection) -> bool {
self.retain(&self.analysis.imports, s.entries_mut(), "import"); self.retain(&self.analysis.imports, s.entries_mut(), "import", 0);
for i in s.entries_mut() { for i in s.entries_mut() {
self.remap_import_entry(i); self.remap_import_entry(i);
} }
@ -596,7 +613,12 @@ impl<'a> RemapContext<'a> {
} }
fn remap_function_section(&self, s: &mut FunctionSection) -> bool { fn remap_function_section(&self, s: &mut FunctionSection) -> bool {
self.retain(&self.analysis.codes, s.entries_mut(), "function"); self.retain(
&self.analysis.functions,
s.entries_mut(),
"function",
self.analysis.imported_functions,
);
for f in s.entries_mut() { for f in s.entries_mut() {
self.remap_func(f); self.remap_func(f);
} }
@ -608,7 +630,12 @@ impl<'a> RemapContext<'a> {
} }
fn remap_table_section(&self, s: &mut TableSection) -> bool { fn remap_table_section(&self, s: &mut TableSection) -> bool {
self.retain(&self.analysis.tables, s.entries_mut(), "table"); self.retain(
&self.analysis.tables,
s.entries_mut(),
"table",
self.analysis.imported_tables,
);
for t in s.entries_mut() { for t in s.entries_mut() {
drop(t); // TODO drop(t); // TODO
} }
@ -616,7 +643,12 @@ impl<'a> RemapContext<'a> {
} }
fn remap_memory_section(&self, s: &mut MemorySection) -> bool { fn remap_memory_section(&self, s: &mut MemorySection) -> bool {
self.retain(&self.analysis.memories, s.entries_mut(), "memory"); self.retain(
&self.analysis.memories,
s.entries_mut(),
"memory",
self.analysis.imported_memories,
);
for m in s.entries_mut() { for m in s.entries_mut() {
drop(m); // TODO drop(m); // TODO
} }
@ -624,7 +656,12 @@ impl<'a> RemapContext<'a> {
} }
fn remap_global_section(&self, s: &mut GlobalSection) -> bool { fn remap_global_section(&self, s: &mut GlobalSection) -> bool {
self.retain(&self.analysis.globals, s.entries_mut(), "global"); self.retain(
&self.analysis.globals,
s.entries_mut(),
"global",
self.analysis.imported_globals,
);
for g in s.entries_mut() { for g in s.entries_mut() {
self.remap_global_entry(g); self.remap_global_entry(g);
} }
@ -647,7 +684,7 @@ impl<'a> RemapContext<'a> {
} }
fn remap_export_section(&self, s: &mut ExportSection) -> bool { fn remap_export_section(&self, s: &mut ExportSection) -> bool {
self.retain(&self.analysis.exports, s.entries_mut(), "export"); self.retain(&self.analysis.exports, s.entries_mut(), "export", 0);
for s in s.entries_mut() { for s in s.entries_mut() {
self.remap_export_entry(s); self.remap_export_entry(s);
} }
@ -683,7 +720,12 @@ impl<'a> RemapContext<'a> {
} }
fn remap_code_section(&self, s: &mut CodeSection) -> bool { fn remap_code_section(&self, s: &mut CodeSection) -> bool {
self.retain(&self.analysis.codes, s.bodies_mut(), "code"); self.retain(
&self.analysis.functions,
s.bodies_mut(),
"code",
self.analysis.imported_functions,
);
for s in s.bodies_mut() { for s in s.bodies_mut() {
self.remap_func_body(s); self.remap_func_body(s);
} }