From 1e0ad0c1d4fdca7a37221e05fd5e39739d9084f5 Mon Sep 17 00:00:00 2001
From: Patrick Walton <pcwalton@fb.com>
Date: Mon, 20 Jun 2022 16:26:51 -0700
Subject: [PATCH] Implement support for DWARF version 5.

DWARF version 5 brings a number of improvements over version 4. Quoting from
the announcement [1]:

> Version 5 incorporates improvements in many areas: better data compression,
> separation of debugging data from executable files, improved description of
> macros and source files, faster searching for symbols, improved debugging
> optimized code, as well as numerous improvements in functionality and
> performance.

On platforms where DWARF version 5 is supported (Linux, primarily), this commit
adds support for it behind a new `-Z dwarf-version=5` flag.

[1]: https://dwarfstd.org/Public_Review.php
---
 .../rustc_codegen_llvm/src/debuginfo/mod.rs   | 16 +++++++--------
 compiler/rustc_interface/src/tests.rs         |  1 +
 compiler/rustc_session/src/options.rs         |  2 ++
 compiler/rustc_session/src/session.rs         |  6 ++++++
 .../rustc_target/src/spec/android_base.rs     |  2 +-
 compiler/rustc_target/src/spec/apple_base.rs  |  2 +-
 .../rustc_target/src/spec/dragonfly_base.rs   |  2 +-
 .../rustc_target/src/spec/freebsd_base.rs     |  2 +-
 compiler/rustc_target/src/spec/mod.rs         | 14 ++++++-------
 compiler/rustc_target/src/spec/netbsd_base.rs |  2 +-
 .../rustc_target/src/spec/openbsd_base.rs     |  2 +-
 .../src/compiler-flags/dwarf-version.md       |  9 +++++++++
 src/test/assembly/dwarf5.rs                   | 20 +++++++++++++++++++
 13 files changed, 59 insertions(+), 21 deletions(-)
 create mode 100644 src/doc/unstable-book/src/compiler-flags/dwarf-version.md
 create mode 100644 src/test/assembly/dwarf5.rs

diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index 64ecbc82c560..730048d061b5 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -103,14 +103,14 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
             // for macOS to understand. For more info see #11352
             // This can be overridden using --llvm-opts -dwarf-version,N.
             // Android has the same issue (#22398)
-            if let Some(version) = sess.target.dwarf_version {
-                llvm::LLVMRustAddModuleFlag(
-                    self.llmod,
-                    llvm::LLVMModFlagBehavior::Warning,
-                    "Dwarf Version\0".as_ptr().cast(),
-                    version,
-                )
-            }
+            let dwarf_version =
+                sess.opts.debugging_opts.dwarf_version.unwrap_or(sess.target.default_dwarf_version);
+            llvm::LLVMRustAddModuleFlag(
+                self.llmod,
+                llvm::LLVMModFlagBehavior::Warning,
+                "Dwarf Version\0".as_ptr().cast(),
+                dwarf_version,
+            );
 
             // Indicate that we want CodeView debug information on MSVC
             if sess.target.is_like_msvc {
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 1bb79a0264dd..55827ff583af 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -733,6 +733,7 @@ fn test_debugging_options_tracking_hash() {
     tracked!(dep_info_omit_d_target, true);
     tracked!(drop_tracking, true);
     tracked!(dual_proc_macros, true);
+    tracked!(dwarf_version, Some(5));
     tracked!(fewer_names, Some(true));
     tracked!(force_unstable_if_unmarked, true);
     tracked!(fuel, Some(("abc".to_string(), 99)));
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index b617eb02eb6e..8f1057b793fa 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -1272,6 +1272,8 @@ options! {
         computed `block` spans (one span encompassing a block's terminator and \
         all statements). If `-Z instrument-coverage` is also enabled, create \
         an additional `.html` file showing the computed coverage spans."),
+    dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED],
+        "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
     emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
         "emit a section containing stack size metadata (default: no)"),
     fewer_names: Option<bool> = (None, parse_opt_bool, [TRACKED],
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 1eee0c3163d4..1cccef2f64fe 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -1498,6 +1498,12 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
             ))
         }
     }
+
+    if let Some(dwarf_version) = sess.opts.debugging_opts.dwarf_version {
+        if dwarf_version > 5 {
+            sess.err(&format!("requested DWARF version {} is greater than 5", dwarf_version));
+        }
+    }
 }
 
 /// Holds data on the current incremental compilation session, if there is one.
diff --git a/compiler/rustc_target/src/spec/android_base.rs b/compiler/rustc_target/src/spec/android_base.rs
index c2b9d696776f..dc06597db6fd 100644
--- a/compiler/rustc_target/src/spec/android_base.rs
+++ b/compiler/rustc_target/src/spec/android_base.rs
@@ -3,7 +3,7 @@ use crate::spec::TargetOptions;
 pub fn opts() -> TargetOptions {
     let mut base = super::linux_base::opts();
     base.os = "android".into();
-    base.dwarf_version = Some(2);
+    base.default_dwarf_version = 2;
     base.position_independent_executables = true;
     base.has_thread_local = false;
     // This is for backward compatibility, see https://github.com/rust-lang/rust/issues/49867
diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs
index e8460a509e2b..713dc9a1f0ea 100644
--- a/compiler/rustc_target/src/spec/apple_base.rs
+++ b/compiler/rustc_target/src/spec/apple_base.rs
@@ -28,7 +28,7 @@ pub fn opts(os: &'static str) -> TargetOptions {
         executables: true,
         families: cvs!["unix"],
         is_like_osx: true,
-        dwarf_version: Some(2),
+        default_dwarf_version: 2,
         frame_pointer: FramePointer::Always,
         has_rpath: true,
         dll_suffix: ".dylib".into(),
diff --git a/compiler/rustc_target/src/spec/dragonfly_base.rs b/compiler/rustc_target/src/spec/dragonfly_base.rs
index b59322d07f57..c1e469746cb4 100644
--- a/compiler/rustc_target/src/spec/dragonfly_base.rs
+++ b/compiler/rustc_target/src/spec/dragonfly_base.rs
@@ -9,7 +9,7 @@ pub fn opts() -> TargetOptions {
         has_rpath: true,
         position_independent_executables: true,
         relro_level: RelroLevel::Full,
-        dwarf_version: Some(2),
+        default_dwarf_version: 2,
         ..Default::default()
     }
 }
diff --git a/compiler/rustc_target/src/spec/freebsd_base.rs b/compiler/rustc_target/src/spec/freebsd_base.rs
index a7e0f9f70412..36312d26e370 100644
--- a/compiler/rustc_target/src/spec/freebsd_base.rs
+++ b/compiler/rustc_target/src/spec/freebsd_base.rs
@@ -10,7 +10,7 @@ pub fn opts() -> TargetOptions {
         position_independent_executables: true,
         relro_level: RelroLevel::Full,
         abi_return_struct_as_int: true,
-        dwarf_version: Some(2),
+        default_dwarf_version: 2,
         ..Default::default()
     }
 }
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index a08603da0409..48ccb10f2142 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -1275,9 +1275,9 @@ pub struct TargetOptions {
     pub is_like_msvc: bool,
     /// Whether a target toolchain is like WASM.
     pub is_like_wasm: bool,
-    /// Version of DWARF to use if not using the default.
+    /// Default supported version of DWARF on this platform.
     /// Useful because some platforms (osx, bsd) only want up to DWARF2.
-    pub dwarf_version: Option<u32>,
+    pub default_dwarf_version: u32,
     /// Whether the linker support GNU-like arguments such as -O. Defaults to true.
     pub linker_is_gnu: bool,
     /// The MinGW toolchain has a known issue that prevents it from correctly
@@ -1539,7 +1539,7 @@ impl Default for TargetOptions {
             is_like_windows: false,
             is_like_msvc: false,
             is_like_wasm: false,
-            dwarf_version: None,
+            default_dwarf_version: 4,
             linker_is_gnu: true,
             allows_weak_linkage: true,
             has_rpath: false,
@@ -1778,13 +1778,13 @@ impl Target {
                     base.$key_name = s;
                 }
             } );
-            ($key_name:ident, Option<u32>) => ( {
+            ($key_name:ident, u32) => ( {
                 let name = (stringify!($key_name)).replace("_", "-");
                 if let Some(s) = obj.remove(&name).and_then(|b| b.as_u64()) {
                     if s < 1 || s > 5 {
                         return Err("Not a valid DWARF version number".into());
                     }
-                    base.$key_name = Some(s as u32);
+                    base.$key_name = s as u32;
                 }
             } );
             ($key_name:ident, Option<u64>) => ( {
@@ -2143,7 +2143,7 @@ impl Target {
         key!(is_like_windows, bool);
         key!(is_like_msvc, bool);
         key!(is_like_wasm, bool);
-        key!(dwarf_version, Option<u32>);
+        key!(default_dwarf_version, u32);
         key!(linker_is_gnu, bool);
         key!(allows_weak_linkage, bool);
         key!(has_rpath, bool);
@@ -2387,7 +2387,7 @@ impl ToJson for Target {
         target_option_val!(is_like_windows);
         target_option_val!(is_like_msvc);
         target_option_val!(is_like_wasm);
-        target_option_val!(dwarf_version);
+        target_option_val!(default_dwarf_version);
         target_option_val!(linker_is_gnu);
         target_option_val!(allows_weak_linkage);
         target_option_val!(has_rpath);
diff --git a/compiler/rustc_target/src/spec/netbsd_base.rs b/compiler/rustc_target/src/spec/netbsd_base.rs
index 69016d77cf97..40ef04ba0438 100644
--- a/compiler/rustc_target/src/spec/netbsd_base.rs
+++ b/compiler/rustc_target/src/spec/netbsd_base.rs
@@ -11,7 +11,7 @@ pub fn opts() -> TargetOptions {
         position_independent_executables: true,
         relro_level: RelroLevel::Full,
         use_ctors_section: true,
-        dwarf_version: Some(2),
+        default_dwarf_version: 2,
         ..Default::default()
     }
 }
diff --git a/compiler/rustc_target/src/spec/openbsd_base.rs b/compiler/rustc_target/src/spec/openbsd_base.rs
index bbd322bb6ce2..51cecdd47eab 100644
--- a/compiler/rustc_target/src/spec/openbsd_base.rs
+++ b/compiler/rustc_target/src/spec/openbsd_base.rs
@@ -11,7 +11,7 @@ pub fn opts() -> TargetOptions {
         position_independent_executables: true,
         frame_pointer: FramePointer::Always, // FIXME 43575: should be MayOmit...
         relro_level: RelroLevel::Full,
-        dwarf_version: Some(2),
+        default_dwarf_version: 2,
         ..Default::default()
     }
 }
diff --git a/src/doc/unstable-book/src/compiler-flags/dwarf-version.md b/src/doc/unstable-book/src/compiler-flags/dwarf-version.md
new file mode 100644
index 000000000000..c5e86f17df74
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/dwarf-version.md
@@ -0,0 +1,9 @@
+## `dwarf-version`
+
+This option controls the version of DWARF that the compiler emits, on platforms
+that use DWARF to encode debug information. It takes one of the following
+values:
+
+* `2`: DWARF version 2 (the default on certain platforms, like macOS).
+* `4`: DWARF version 4 (the default on certain platforms, like Linux).
+* `5`: DWARF version 5.
diff --git a/src/test/assembly/dwarf5.rs b/src/test/assembly/dwarf5.rs
new file mode 100644
index 000000000000..f41e6bd55be7
--- /dev/null
+++ b/src/test/assembly/dwarf5.rs
@@ -0,0 +1,20 @@
+// Makes sure that `-Z dwarf-version=5` causes `rustc` to emit DWARF version 5.
+// assembly-output: emit-asm
+// compile-flags: -g --target x86_64-unknown-linux-gnu -Z dwarf-version=5
+// needs-llvm-components: x86
+
+#![feature(no_core, lang_items)]
+#![crate_type = "rlib"]
+#![no_core]
+
+#[lang = "sized"]
+trait Sized {}
+#[lang = "copy"]
+trait Copy {}
+
+pub fn wibble() {}
+
+// CHECK: .section .debug_info
+// CHECK-NOT: .short 2
+// CHECK-NOT: .short 4
+// CHECK: .short 5