Skip to content

Commit c4a38d6

Browse files
authored
Use gimli::write::Dwarf::convert_with_filter for DWARF transform (#12428)
Replace parts of the transform code with `gimli`'s generic DWARF transformation support. Most of the remaining code is specific to the needs of Wasmtime. The overall behaviour is the same as the previous implementation. We build a graph of the DIE dependencies, and prune DIEs that don't have valid code ranges. Then we traverse the DIE tree again and transform the DIEs. However, there are some differences. Previously, unresolved references to DIEs were stored in `PendingUnitRefs` and `PendingDebugInfoRefs`, then at the end of the transformation we went back and fixed up these references. The new behaviour reserves IDs for all of the DIEs in the dependency tree before transformation, which avoids the need to fix up references later. It also allows gimli to correctly handle references in DWARF expressions, although that doesn't currently matter for Wasmtime because it doesn't handle references in expressions yet. The visible effect of this in Wasmtime is that the order of transformed attributes will now always match the original DWARF. The DIE tree pruning is slightly different. We no longer add back-edges pointing to namespace DIEs or the root DIE, which previously caused some DIEs to be reachable when they should not have been. In particular, this affects DW_TAG_variable DIEs for global variables. Wasmtime can't currently translate the location for globals, so they were added without a valid DW_AT_location. These DIEs are now omitted from the transformed DWARF. In the future when global variables can be translated correctly, they can be included by calling `ReserveUnitSection::require_entry`. Performance measurements for `wasmtime compile -D debug-info=y` show minimal change.
1 parent 0f95030 commit c4a38d6

File tree

9 files changed

+361
-1029
lines changed

9 files changed

+361
-1029
lines changed

crates/cranelift/src/debug/gc.rs

Lines changed: 14 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -1,154 +1,25 @@
11
use crate::debug::Reader;
22
use crate::debug::transform::AddressTransform;
3-
use gimli::UnitSectionOffset;
43
use gimli::constants;
54
use gimli::read;
6-
use std::collections::{HashMap, HashSet};
7-
8-
#[derive(Debug)]
9-
pub struct Dependencies {
10-
edges: HashMap<UnitSectionOffset, HashSet<UnitSectionOffset>>,
11-
roots: HashSet<UnitSectionOffset>,
12-
}
13-
14-
impl Dependencies {
15-
fn new() -> Dependencies {
16-
Dependencies {
17-
edges: HashMap::new(),
18-
roots: HashSet::new(),
19-
}
20-
}
21-
22-
fn add_edge(&mut self, a: UnitSectionOffset, b: UnitSectionOffset) {
23-
use std::collections::hash_map::Entry;
24-
match self.edges.entry(a) {
25-
Entry::Occupied(mut o) => {
26-
o.get_mut().insert(b);
27-
}
28-
Entry::Vacant(v) => {
29-
let mut set = HashSet::new();
30-
set.insert(b);
31-
v.insert(set);
32-
}
33-
}
34-
}
35-
36-
fn add_root(&mut self, root: UnitSectionOffset) {
37-
self.roots.insert(root);
38-
}
39-
40-
pub fn get_reachable(&self) -> HashSet<UnitSectionOffset> {
41-
let mut reachable = self.roots.clone();
42-
let mut queue = Vec::new();
43-
for i in self.roots.iter() {
44-
if let Some(deps) = self.edges.get(i) {
45-
for j in deps {
46-
if reachable.contains(j) {
47-
continue;
48-
}
49-
reachable.insert(*j);
50-
queue.push(*j);
51-
}
52-
}
53-
}
54-
while let Some(i) = queue.pop() {
55-
if let Some(deps) = self.edges.get(&i) {
56-
for j in deps {
57-
if reachable.contains(j) {
58-
continue;
59-
}
60-
reachable.insert(*j);
61-
queue.push(*j);
62-
}
63-
}
64-
}
65-
reachable
66-
}
67-
}
5+
use gimli::write;
686

697
pub fn build_dependencies(
70-
dwarf: &read::Dwarf<Reader<'_>>,
8+
filter: &mut write::FilterUnitSection<'_, Reader<'_>>,
719
at: &AddressTransform,
72-
) -> read::Result<Dependencies> {
73-
let mut deps = Dependencies::new();
74-
let mut units = dwarf.units();
75-
while let Some(unit) = units.next()? {
76-
build_unit_dependencies(unit, dwarf, at, &mut deps)?;
10+
) -> write::ConvertResult<()> {
11+
while let Some(mut unit) = filter.read_unit()? {
12+
build_die_dependencies(&mut unit, at)?;
7713
}
78-
Ok(deps)
79-
}
80-
81-
fn build_unit_dependencies(
82-
header: read::UnitHeader<Reader<'_>>,
83-
dwarf: &read::Dwarf<Reader<'_>>,
84-
at: &AddressTransform,
85-
deps: &mut Dependencies,
86-
) -> read::Result<()> {
87-
let unit = dwarf.unit(header)?;
88-
let unit = unit.unit_ref(dwarf);
89-
let mut tree = unit.entries_tree(None)?;
90-
let root = tree.root()?;
91-
build_die_dependencies(root, unit, at, deps)?;
9214
Ok(())
9315
}
9416

95-
fn has_die_back_edge(die: &read::DebuggingInformationEntry<Reader<'_>>) -> read::Result<bool> {
96-
// DIEs can be broadly divided into three categories:
97-
// 1. Extensions of their parents; effectively attributes: DW_TAG_variable, DW_TAG_member, etc.
98-
// 2. Standalone entities referred to by other DIEs via 'reference' class attributes: types.
99-
// 3. Structural entities that organize how the above relate to each other: namespaces.
100-
// Here, we must make sure to return 'true' for DIEs in the first category since stripping them,
101-
// provided their parent is alive, is always wrong. To be conservatively correct in the face
102-
// of new/vendor tags, we maintain a "(mostly) known good" list of tags of the latter categories.
103-
let result = match die.tag() {
104-
constants::DW_TAG_array_type
105-
| constants::DW_TAG_atomic_type
106-
| constants::DW_TAG_base_type
107-
| constants::DW_TAG_class_type
108-
| constants::DW_TAG_const_type
109-
| constants::DW_TAG_dwarf_procedure
110-
| constants::DW_TAG_entry_point
111-
| constants::DW_TAG_enumeration_type
112-
| constants::DW_TAG_pointer_type
113-
| constants::DW_TAG_ptr_to_member_type
114-
| constants::DW_TAG_reference_type
115-
| constants::DW_TAG_restrict_type
116-
| constants::DW_TAG_rvalue_reference_type
117-
| constants::DW_TAG_string_type
118-
| constants::DW_TAG_structure_type
119-
| constants::DW_TAG_typedef
120-
| constants::DW_TAG_union_type
121-
| constants::DW_TAG_unspecified_type
122-
| constants::DW_TAG_volatile_type
123-
| constants::DW_TAG_coarray_type
124-
| constants::DW_TAG_common_block
125-
| constants::DW_TAG_dynamic_type
126-
| constants::DW_TAG_file_type
127-
| constants::DW_TAG_immutable_type
128-
| constants::DW_TAG_interface_type
129-
| constants::DW_TAG_set_type
130-
| constants::DW_TAG_shared_type
131-
| constants::DW_TAG_subroutine_type
132-
| constants::DW_TAG_packed_type
133-
| constants::DW_TAG_template_alias
134-
| constants::DW_TAG_namelist
135-
| constants::DW_TAG_namespace
136-
| constants::DW_TAG_imported_unit
137-
| constants::DW_TAG_imported_declaration
138-
| constants::DW_TAG_imported_module
139-
| constants::DW_TAG_module => false,
140-
constants::DW_TAG_subprogram => die.attr(constants::DW_AT_declaration).is_some(),
141-
_ => true,
142-
};
143-
Ok(result)
144-
}
145-
14617
fn has_valid_code_range(
147-
die: &read::DebuggingInformationEntry<Reader<'_>>,
148-
unit: read::UnitRef<Reader<'_>>,
18+
die: &write::FilterUnitEntry<'_, Reader<'_>>,
14919
at: &AddressTransform,
15020
) -> read::Result<bool> {
151-
match die.tag() {
21+
let unit = die.read_unit;
22+
match die.tag {
15223
constants::DW_TAG_subprogram => {
15324
if let Some(ranges_attr) = die.attr_value(constants::DW_AT_ranges) {
15425
let offset = match ranges_attr {
@@ -215,50 +86,14 @@ fn has_valid_code_range(
21586
}
21687

21788
fn build_die_dependencies(
218-
die: read::EntriesTreeNode<Reader<'_>>,
219-
unit: read::UnitRef<Reader<'_>>,
89+
unit: &mut write::FilterUnit<'_, Reader<'_>>,
22090
at: &AddressTransform,
221-
deps: &mut Dependencies,
222-
) -> read::Result<()> {
223-
let entry = die.entry();
224-
let offset = entry.offset().to_unit_section_offset(&unit);
225-
for attr in entry.attrs() {
226-
build_attr_dependencies(attr, offset, unit, at, deps)?;
227-
}
228-
229-
let mut children = die.children();
230-
while let Some(child) = children.next()? {
231-
let child_entry = child.entry();
232-
let child_offset = child_entry.offset().to_unit_section_offset(&unit);
233-
deps.add_edge(child_offset, offset);
234-
if has_die_back_edge(child_entry)? {
235-
deps.add_edge(offset, child_offset);
236-
}
237-
if has_valid_code_range(child_entry, unit, at)? {
238-
deps.add_root(child_offset);
91+
) -> write::ConvertResult<()> {
92+
let mut die = write::FilterUnitEntry::null(unit.read_unit);
93+
while unit.read_entry(&mut die)? {
94+
if has_valid_code_range(&die, at)? {
95+
unit.require_entry(die.offset);
23996
}
240-
build_die_dependencies(child, unit, at, deps)?;
241-
}
242-
Ok(())
243-
}
244-
245-
fn build_attr_dependencies(
246-
attr: &read::Attribute<Reader<'_>>,
247-
offset: UnitSectionOffset,
248-
unit: read::UnitRef<Reader<'_>>,
249-
_at: &AddressTransform,
250-
deps: &mut Dependencies,
251-
) -> read::Result<()> {
252-
match attr.value() {
253-
read::AttributeValue::UnitRef(val) => {
254-
let ref_offset = val.to_unit_section_offset(&unit);
255-
deps.add_edge(offset, ref_offset);
256-
}
257-
read::AttributeValue::DebugInfoRef(val) => {
258-
let ref_offset = UnitSectionOffset(val.0);
259-
deps.add_edge(offset, ref_offset);
260-
}
261-
_ => (),
26297
}
26398
Ok(())
26499
}

0 commit comments

Comments
 (0)