GC passive segments

We statically know which passive segments are actually used, so let's be
sure to gc them!
This commit is contained in:
Alex Crichton 2018-10-29 15:54:32 -07:00
parent 91d68a0012
commit 31c11f0781
8 changed files with 242 additions and 40 deletions

View File

@ -58,10 +58,11 @@ fn run(config: &mut Config, module: &mut Module) {
cx.blacklist.insert("__heap_base"); cx.blacklist.insert("__heap_base");
cx.blacklist.insert("__data_end"); cx.blacklist.insert("__data_end");
// always treat memory as a root // Always treat memory as a root. In theory we could actually gc this
// away in some circumstances, but it's probably not worth the effort.
cx.add_memory(0); cx.add_memory(0);
// All exports are a root // All non-blacklisted exports are roots
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()) { if cx.blacklist.contains(entry.field()) {
@ -71,24 +72,7 @@ fn run(config: &mut Config, module: &mut Module) {
} }
} }
// Pessimistically assume all passive data and table segments are // ... and finally, the start function
// required
if let Some(section) = module.data_section() {
for entry in section.entries() {
if entry.passive() {
cx.add_data_segment(entry);
}
}
}
if let Some(elements) = module.elements_section() {
for (i, seg) in elements.entries().iter().enumerate() {
if seg.passive() {
cx.add_element_segment(i as u32, 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);
} }
@ -147,6 +131,7 @@ struct Analysis {
exports: BitSet, exports: BitSet,
functions: BitSet, functions: BitSet,
elements: BitSet, elements: BitSet,
data_segments: BitSet,
imported_functions: u32, imported_functions: u32,
imported_globals: u32, imported_globals: u32,
imported_memories: u32, imported_memories: u32,
@ -241,11 +226,9 @@ impl<'a> LiveContext<'a> {
let iter = elements.entries() let iter = elements.entries()
.iter() .iter()
.enumerate() .enumerate()
.filter(|(_, d)| !d.passive()); .filter(|(_, d)| !d.passive() && d.index() == idx);
for (i, entry) in iter { for (i, _) in iter {
if entry.index() == idx { self.add_element_segment(i as u32);
self.add_element_segment(i as u32, entry);
}
} }
} }
@ -280,10 +263,12 @@ impl<'a> LiveContext<'a> {
// Add all data segments that initialize this memory // Add all data segments that initialize this memory
if let Some(data) = self.data_section { if let Some(data) = self.data_section {
for entry in data.entries().iter().filter(|d| !d.passive()) { let iter = data.entries()
if entry.index() == idx { .iter()
self.add_data_segment(entry); .enumerate()
} .filter(|(_, d)| !d.passive() && d.index() == idx);
for (i, _) in iter {
self.add_data_segment(i as u32);
} }
} }
@ -402,6 +387,16 @@ impl<'a> LiveContext<'a> {
} }
Instruction::GetGlobal(i) | Instruction::GetGlobal(i) |
Instruction::SetGlobal(i) => self.add_global(i), Instruction::SetGlobal(i) => self.add_global(i),
Instruction::MemoryInit(i) |
Instruction::MemoryDrop(i) => {
self.add_memory(0);
self.add_data_segment(i);
}
Instruction::TableInit(i) |
Instruction::TableDrop(i) => {
self.add_table(0);
self.add_element_segment(i);
}
_ => {} _ => {}
} }
} }
@ -438,23 +433,32 @@ impl<'a> LiveContext<'a> {
} }
} }
fn add_data_segment(&mut self, data: &DataSegment) { fn add_data_segment(&mut self, idx: u32) {
if let Some(offset) = data.offset() { if !self.analysis.data_segments.insert(idx) {
self.add_memory(data.index()); return
self.add_init_expr(offset); }
let data = &self.data_section.unwrap().entries()[idx as usize];
if !data.passive() {
if let Some(offset) = data.offset() {
self.add_memory(data.index());
self.add_init_expr(offset);
}
} }
} }
fn add_element_segment(&mut self, idx: u32, seg: &ElementSegment) { fn add_element_segment(&mut self, idx: u32) {
if !self.analysis.elements.insert(idx) { if !self.analysis.elements.insert(idx) {
return return
} }
let seg = &self.element_section.unwrap().entries()[idx as usize];
for member in seg.members() { for member in seg.members() {
self.add_function(*member); self.add_function(*member);
} }
if let Some(offset) = seg.offset() { if !seg.passive() {
self.add_table(seg.index()); if let Some(offset) = seg.offset() {
self.add_init_expr(offset); self.add_table(seg.index());
self.add_init_expr(offset);
}
} }
} }
} }
@ -467,6 +471,8 @@ struct RemapContext<'a> {
types: Vec<u32>, types: Vec<u32>,
tables: Vec<u32>, tables: Vec<u32>,
memories: Vec<u32>, memories: Vec<u32>,
elements: Vec<u32>,
data_segments: Vec<u32>,
} }
impl<'a> RemapContext<'a> { impl<'a> RemapContext<'a> {
@ -561,6 +567,34 @@ impl<'a> RemapContext<'a> {
} }
} }
let mut elements = Vec::new();
if let Some(s) = m.elements_section() {
let mut nelements = 0;
for i in 0..(s.entries().len() as u32) {
if analysis.elements.contains(&i) {
elements.push(nelements);
nelements += 1;
} else {
debug!("gc element segment {}", i);
elements.push(u32::max_value());
}
}
}
let mut data_segments = Vec::new();
if let Some(s) = m.data_section() {
let mut ndata_segments = 0;
for i in 0..(s.entries().len() as u32) {
if analysis.data_segments.contains(&i) {
data_segments.push(ndata_segments);
ndata_segments += 1;
} else {
debug!("gc data segment {}", i);
data_segments.push(u32::max_value());
}
}
}
RemapContext { RemapContext {
analysis, analysis,
functions, functions,
@ -569,6 +603,8 @@ impl<'a> RemapContext<'a> {
tables, tables,
types, types,
config, config,
elements,
data_segments,
} }
} }
@ -729,9 +765,11 @@ impl<'a> RemapContext<'a> {
} }
fn remap_element_segment(&self, s: &mut ElementSegment) { fn remap_element_segment(&self, s: &mut ElementSegment) {
let mut i = s.index(); if !s.passive() {
self.remap_table_idx(&mut i); let mut i = s.index();
assert_eq!(s.index(), i); self.remap_table_idx(&mut i);
assert_eq!(s.index(), i);
}
for m in s.members_mut() { for m in s.members_mut() {
self.remap_function_idx(m); self.remap_function_idx(m);
} }
@ -772,6 +810,10 @@ impl<'a> RemapContext<'a> {
Instruction::CallIndirect(ref mut t, _) => self.remap_type_idx(t), Instruction::CallIndirect(ref mut t, _) => self.remap_type_idx(t),
Instruction::GetGlobal(ref mut i) | Instruction::GetGlobal(ref mut i) |
Instruction::SetGlobal(ref mut i) => self.remap_global_idx(i), Instruction::SetGlobal(ref mut i) => self.remap_global_idx(i),
Instruction::TableInit(ref mut i) |
Instruction::TableDrop(ref mut i) => self.remap_element_idx(i),
Instruction::MemoryInit(ref mut i) |
Instruction::MemoryDrop(ref mut i) => self.remap_data_idx(i),
_ => {} _ => {}
} }
} }
@ -784,6 +826,7 @@ impl<'a> RemapContext<'a> {
} }
fn remap_data_section(&self, s: &mut DataSection) -> bool { fn remap_data_section(&self, s: &mut DataSection) -> bool {
self.retain(&self.analysis.data_segments, s.entries_mut(), "data", 0);
for data in s.entries_mut() { for data in s.entries_mut() {
self.remap_data_segment(data); self.remap_data_segment(data);
} }
@ -825,6 +868,16 @@ impl<'a> RemapContext<'a> {
assert!(*i != u32::max_value()); assert!(*i != u32::max_value());
} }
fn remap_element_idx(&self, i: &mut u32) {
*i = self.elements[*i as usize];
assert!(*i != u32::max_value());
}
fn remap_data_idx(&self, i: &mut u32) {
*i = self.data_segments[*i as usize];
assert!(*i != u32::max_value());
}
fn remap_name_section(&self, s: &mut NameSection) { fn remap_name_section(&self, s: &mut NameSection) {
match *s { match *s {
NameSection::Module(_) => {} NameSection::Module(_) => {}

View File

@ -75,6 +75,7 @@ fn run_test(test: &Test) -> Result<(), Box<Error>> {
let expected = extract_expected(&input); let expected = extract_expected(&input);
let status = Command::new("wat2wasm") let status = Command::new("wat2wasm")
.arg("--debug-names") .arg("--debug-names")
.arg("--enable-bulk-memory")
.arg(&test.input) .arg(&test.input)
.arg("-o") .arg("-o")
.arg(f.path()) .arg(f.path())
@ -94,6 +95,7 @@ fn run_test(test: &Test) -> Result<(), Box<Error>> {
fs::write(f.path(), wasm)?; fs::write(f.path(), wasm)?;
let status = Command::new("wasm2wat") let status = Command::new("wasm2wat")
.arg("--enable-bulk-memory")
.arg(&f.path()) .arg(&f.path())
.stderr(Stdio::inherit()) .stderr(Stdio::inherit())
.output()?; .output()?;

View File

@ -0,0 +1,27 @@
(module
(memory 0 10)
(func $foo
i32.const 0
i32.const 0
i32.const 0
memory.init 0
)
(data passive "wut")
(start $foo)
)
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
;; (module
;; (type (;0;) (func))
;; (func $foo (type 0)
;; i32.const 0
;; i32.const 0
;; i32.const 0
;; memory.init 0)
;; (memory (;0;) 0 10)
;; (start 0)
;; (data (;0;) passive "wut"))
;; STDOUT

View File

@ -0,0 +1,30 @@
(module
(import "" "" (table 0 1 anyfunc))
(func $foo
i32.const 0
i32.const 0
i32.const 0
table.init 0
)
(func $bar)
(elem passive $bar)
(start $foo)
)
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
;; (module
;; (type (;0;) (func))
;; (import "" "" (table (;0;) 0 1 anyfunc))
;; (func $foo (type 0)
;; i32.const 0
;; i32.const 0
;; i32.const 0
;; table.init 0)
;; (func $bar (type 0))
;; (start 0)
;; (elem (;0;) passive $bar))
;; STDOUT

View File

@ -0,0 +1,18 @@
(module
(memory 0 10)
(func $foo
i32.const 0
i32.const 0
i32.const 0
memory.init 0
)
(data passive "wut")
)
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
;; (module
;; (memory (;0;) 0 10))
;; STDOUT

View File

@ -0,0 +1,11 @@
(module
(import "" "" (table 0 1 anyfunc))
(func $foo)
(elem passive $foo)
)
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
;; (module)
;; STDOUT

View File

@ -0,0 +1,29 @@
(module
(memory 0 10)
(func $foo
i32.const 0
i32.const 0
i32.const 0
memory.init 1
)
(data passive "wut")
(data passive "wut2")
(data passive "wut3")
(start $foo)
)
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
;; (module
;; (type (;0;) (func))
;; (func $foo (type 0)
;; i32.const 0
;; i32.const 0
;; i32.const 0
;; memory.init 0)
;; (memory (;0;) 0 10)
;; (start 0)
;; (data (;0;) passive "wut2"))
;; STDOUT

View File

@ -0,0 +1,32 @@
(module
(import "" "" (table 0 1 anyfunc))
(func $foo
i32.const 0
i32.const 0
i32.const 0
table.init 1
)
(func $bar)
(func $bar2)
(elem passive $bar)
(elem passive $bar2)
(start $foo)
)
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
;; (module
;; (type (;0;) (func))
;; (import "" "" (table (;0;) 0 1 anyfunc))
;; (func $foo (type 0)
;; i32.const 0
;; i32.const 0
;; i32.const 0
;; table.init 0)
;; (func $bar2 (type 0))
;; (start 0)
;; (elem (;0;) passive $bar2))
;; STDOUT