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