Skip to content

Commit

Permalink
Skip generating DWARF info for dead code (#3498)
Browse files Browse the repository at this point in the history
When encountering a subprogram that is dead code (as indicated by the
dead code proposal
https://dwarfstd.org/ShowIssue.php?issue=200609.1), don't generate debug
output for the subprogram or any of its children.
  • Loading branch information
adamrk authored Nov 8, 2021
1 parent 2053e97 commit 12bfbdf
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 0 deletions.
20 changes: 20 additions & 0 deletions crates/cranelift/src/debug/transform/unit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,15 @@ where
Ok(wrapper_die_id)
}

fn is_dead_code<R: Reader>(entry: &DebuggingInformationEntry<R>) -> bool {
const TOMBSTONE: u64 = u32::MAX as u64;

match entry.attr_value(gimli::DW_AT_low_pc) {
Ok(Some(AttributeValue::Addr(addr))) => addr == TOMBSTONE,
_ => false,
}
}

pub(crate) fn clone_unit<'a, R>(
dwarf: &gimli::Dwarf<R>,
unit: Unit<R, R::Offset>,
Expand Down Expand Up @@ -346,12 +355,20 @@ where
let mut current_value_range = InheritedAttr::new();
let mut current_scope_ranges = InheritedAttr::new();
while let Some((depth_delta, entry)) = entries.next_dfs()? {
// If `skip_at_depth` is `Some` then we previously decided to skip over
// a node and all it's children. Let A be the last node processed, B be
// the first node skipped, C be previous node, and D the current node.
// Then `cached` is the difference from A to B, `depth` is the diffence
// from B to C, and `depth_delta` is the differenc from C to D.
let depth_delta = if let Some((depth, cached)) = skip_at_depth {
// `new_depth` = B to D
let new_depth = depth + depth_delta;
// if D is below B continue to skip
if new_depth > 0 {
skip_at_depth = Some((new_depth, cached));
continue;
}
// otherwise process D with `depth_delta` being the difference from A to D
skip_at_depth = None;
new_depth + cached
} else {
Expand All @@ -361,8 +378,11 @@ where
if !context
.reachable
.contains(&entry.offset().to_unit_section_offset(&unit))
|| is_dead_code(&entry)
{
// entry is not reachable: discarding all its info.
// Here B = C so `depth` is 0. A is the previous node so `cached` =
// `depth_delta`.
skip_at_depth = Some((0, depth_delta));
continue;
}
Expand Down
19 changes: 19 additions & 0 deletions tests/all/debug/testsuite/dead_code.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
int bar(int a)
{
int b[50];
b[0] = a;
b[29] = a;
return a;
}

int baz(int a);

__attribute__((export_name("foo"))) int foo()
{
return baz(10);
}

__attribute__((noinline)) int baz(int a)
{
return a + 5;
}
Binary file added tests/all/debug/testsuite/dead_code.wasm
Binary file not shown.
24 changes: 24 additions & 0 deletions tests/all/debug/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,30 @@ fn check_line_program(wasm_path: &str, directives: &str) -> Result<()> {
Ok(())
}

#[test]
#[ignore]
#[cfg(all(
any(target_os = "linux", target_os = "macos"),
target_pointer_width = "64"
))]
fn test_debug_dwarf_translate_dead_code() -> Result<()> {
check_wasm(
"tests/all/debug/testsuite/dead_code.wasm",
r##"
check: DW_TAG_compile_unit
# We don't have "bar" function because it is dead code
not: DW_AT_name ("bar")
# We have "foo" function
check: DW_TAG_subprogram
check: DW_AT_name ("foo")
# We have "baz" function
# it was marked `noinline` so isn't dead code
check: DW_TAG_subprogram
check: DW_AT_name ("baz")
"##,
)
}

#[test]
#[ignore]
#[cfg(all(
Expand Down

0 comments on commit 12bfbdf

Please sign in to comment.