From 12bfbdfaca3ecd3da95b30670df1b71fdba01be0 Mon Sep 17 00:00:00 2001 From: Adam Bratschi-Kaye Date: Mon, 8 Nov 2021 16:31:04 +0100 Subject: [PATCH] Skip generating DWARF info for dead code (#3498) 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. --- crates/cranelift/src/debug/transform/unit.rs | 20 ++++++++++++++++ tests/all/debug/testsuite/dead_code.c | 19 +++++++++++++++ tests/all/debug/testsuite/dead_code.wasm | Bin 0 -> 1174 bytes tests/all/debug/translate.rs | 24 +++++++++++++++++++ 4 files changed, 63 insertions(+) create mode 100644 tests/all/debug/testsuite/dead_code.c create mode 100755 tests/all/debug/testsuite/dead_code.wasm diff --git a/crates/cranelift/src/debug/transform/unit.rs b/crates/cranelift/src/debug/transform/unit.rs index e4164942cb..4ef08b66f2 100644 --- a/crates/cranelift/src/debug/transform/unit.rs +++ b/crates/cranelift/src/debug/transform/unit.rs @@ -244,6 +244,15 @@ where Ok(wrapper_die_id) } +fn is_dead_code(entry: &DebuggingInformationEntry) -> 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, unit: Unit, @@ -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 { @@ -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; } diff --git a/tests/all/debug/testsuite/dead_code.c b/tests/all/debug/testsuite/dead_code.c new file mode 100644 index 0000000000..61067a78da --- /dev/null +++ b/tests/all/debug/testsuite/dead_code.c @@ -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; +} diff --git a/tests/all/debug/testsuite/dead_code.wasm b/tests/all/debug/testsuite/dead_code.wasm new file mode 100755 index 0000000000000000000000000000000000000000..b8c28a1f63d432907ef2838b3b7fe91cc7818bb7 GIT binary patch literal 1174 zcmcIjOK;Oa5T5bcNouzyX%20Lgi@&p5Nh0prfG{>2&hQ8q4E&r;`*^kUFswl*CR{Tk1R#wOWQ82Weot%6{}DUYA^V1G zikuV1fzJdLP(RU>BmWEPL0Nw2h;)CDkSVQB2a)8Syw~Z^f=@?z=)ftm+ASUnA0Z;= zQ4*0_iR5U!#6t(mwjFk+ zsuL2UAVi2RF%1<}T~t`6DPf-$O=Edf2Gwz*@ATGegS?}6qCkzbV@AYA_6NMYxNlI>I&T7`2+3Jit=h{>D?3^=QtGag8 z;g#u%TbpUxRX%S!=ir~kQL)Fh8S7!d4a>CxgFoo-$P0LzdUBsWWkPGPtq8n!0^)bs z))rV`gK5@RR_d$fjmF(&^XBUHWzz(>CbzAC<9QhhrYYP_hZ<`-Nf^cW`&X63Pi2gP zWrqs&Wx?P95BjrS+x>k!LyE;D)gJf}d9CLTl-b5vB` FzF)0q4O;*J literal 0 HcmV?d00001 diff --git a/tests/all/debug/translate.rs b/tests/all/debug/translate.rs index db5e2af935..4e72dcd39f 100644 --- a/tests/all/debug/translate.rs +++ b/tests/all/debug/translate.rs @@ -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(