diff --git a/Cargo.lock b/Cargo.lock
index ab33f6f030a7e..c9d5f1744f3bb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4456,6 +4456,7 @@ dependencies = [
 name = "rustc_session"
 version = "0.0.0"
 dependencies = [
+ "bitflags 1.3.2",
  "getopts",
  "libc",
  "rustc_ast",
diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs
index 953d957a4ac79..f7bafa2856e4d 100644
--- a/compiler/rustc_builtin_macros/src/source_util.rs
+++ b/compiler/rustc_builtin_macros/src/source_util.rs
@@ -61,9 +61,14 @@ pub fn expand_file(
 
     let topmost = cx.expansion_cause().unwrap_or(sp);
     let loc = cx.source_map().lookup_char_pos(topmost.lo());
-    base::MacEager::expr(
-        cx.expr_str(topmost, Symbol::intern(&loc.file.name.prefer_remapped().to_string_lossy())),
-    )
+
+    use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt};
+    base::MacEager::expr(cx.expr_str(
+        topmost,
+        Symbol::intern(
+            &loc.file.name.for_scope(cx.sess, RemapPathScopeComponents::MACRO).to_string_lossy(),
+        ),
+    ))
 }
 
 pub fn expand_stringify(
diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs
index 8958369267e5d..7a3ae6ebf52fa 100644
--- a/compiler/rustc_codegen_cranelift/src/common.rs
+++ b/compiler/rustc_codegen_cranelift/src/common.rs
@@ -414,11 +414,12 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
     // Note: must be kept in sync with get_caller_location from cg_ssa
     pub(crate) fn get_caller_location(&mut self, mut source_info: mir::SourceInfo) -> CValue<'tcx> {
         let span_to_caller_location = |fx: &mut FunctionCx<'_, '_, 'tcx>, span: Span| {
+            use rustc_session::RemapFileNameExt;
             let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
             let caller = fx.tcx.sess.source_map().lookup_char_pos(topmost.lo());
             let const_loc = fx.tcx.const_caller_location((
                 rustc_span::symbol::Symbol::intern(
-                    &caller.file.name.prefer_remapped().to_string_lossy(),
+                    &caller.file.name.for_codegen(&fx.tcx.sess).to_string_lossy(),
                 ),
                 caller.line as u32,
                 caller.col_display as u32 + 1,
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
index d00d19f9a80c4..6230ca15d6e10 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
@@ -95,7 +95,11 @@ impl DebugContext {
         match &source_file.name {
             FileName::Real(path) => {
                 let (dir_path, file_name) =
-                    split_path_dir_and_file(path.remapped_path_if_available());
+                    split_path_dir_and_file(if self.should_remap_filepaths {
+                        path.remapped_path_if_available()
+                    } else {
+                        path.local_path_if_available()
+                    });
                 let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str());
                 let file_name = osstr_as_utf8_bytes(file_name);
 
@@ -116,7 +120,14 @@ impl DebugContext {
             filename => {
                 let dir_id = line_program.default_directory();
                 let dummy_file_name = LineString::new(
-                    filename.prefer_remapped().to_string().into_bytes(),
+                    filename
+                        .display(if self.should_remap_filepaths {
+                            FileNameDisplayPreference::Remapped
+                        } else {
+                            FileNameDisplayPreference::Local
+                        })
+                        .to_string()
+                        .into_bytes(),
                     line_program.encoding(),
                     line_strings,
                 );
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
index 9e78cc259ce10..84bfe15c13f65 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs
@@ -31,6 +31,8 @@ pub(crate) struct DebugContext {
 
     dwarf: DwarfUnit,
     unit_range_list: RangeList,
+
+    should_remap_filepaths: bool,
 }
 
 pub(crate) struct FunctionDebugContext {
@@ -63,12 +65,18 @@ impl DebugContext {
 
         let mut dwarf = DwarfUnit::new(encoding);
 
+        let should_remap_filepaths = tcx.sess.should_prefer_remapped_for_codegen();
+
         let producer = producer();
         let comp_dir = tcx
             .sess
             .opts
             .working_dir
-            .to_string_lossy(FileNameDisplayPreference::Remapped)
+            .to_string_lossy(if should_remap_filepaths {
+                FileNameDisplayPreference::Remapped
+            } else {
+                FileNameDisplayPreference::Local
+            })
             .into_owned();
         let (name, file_info) = match tcx.sess.local_crate_source_file() {
             Some(path) => {
@@ -102,7 +110,12 @@ impl DebugContext {
             root.set(gimli::DW_AT_low_pc, AttributeValue::Address(Address::Constant(0)));
         }
 
-        DebugContext { endian, dwarf, unit_range_list: RangeList(Vec::new()) }
+        DebugContext {
+            endian,
+            dwarf,
+            unit_range_list: RangeList(Vec::new()),
+            should_remap_filepaths,
+        }
     }
 
     pub(crate) fn define_function(
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index c778a6e017fac..9d5204034def0 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -259,9 +259,17 @@ pub fn target_machine_factory(
     };
     let debuginfo_compression = SmallCStr::new(&debuginfo_compression);
 
+    let should_prefer_remapped_for_split_debuginfo_paths =
+        sess.should_prefer_remapped_for_split_debuginfo_paths();
+
     Arc::new(move |config: TargetMachineFactoryConfig| {
         let path_to_cstring_helper = |path: Option<PathBuf>| -> CString {
-            let path = path_mapping.map_prefix(path.unwrap_or_default()).0;
+            let path = path.unwrap_or_default();
+            let path = if should_prefer_remapped_for_split_debuginfo_paths {
+                path_mapping.map_prefix(path).0
+            } else {
+                path.into()
+            };
             CString::new(path.to_str().unwrap()).unwrap()
         };
 
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
index d4e775256985c..d58e95fcd363b 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
@@ -126,9 +126,9 @@ impl GlobalFileTable {
         // Since rustc generates coverage maps with relative paths, the
         // compilation directory can be combined with the relative paths
         // to get absolute paths, if needed.
-        let working_dir = Symbol::intern(
-            &tcx.sess.opts.working_dir.remapped_path_if_available().to_string_lossy(),
-        );
+        use rustc_session::RemapFileNameExt;
+        let working_dir =
+            Symbol::intern(&tcx.sess.opts.working_dir.for_codegen(&tcx.sess).to_string_lossy());
         global_file_table.insert(working_dir);
         Self { global_file_table }
     }
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 11874898a5adb..4f8ae2ddb8f9e 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -547,48 +547,77 @@ pub fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) ->
     ) -> &'ll DIFile {
         debug!(?source_file.name);
 
+        use rustc_session::RemapFileNameExt;
         let (directory, file_name) = match &source_file.name {
             FileName::Real(filename) => {
                 let working_directory = &cx.sess().opts.working_dir;
                 debug!(?working_directory);
 
-                let filename = cx
-                    .sess()
-                    .source_map()
-                    .path_mapping()
-                    .to_embeddable_absolute_path(filename.clone(), working_directory);
-
-                // Construct the absolute path of the file
-                let abs_path = filename.remapped_path_if_available();
-                debug!(?abs_path);
-
-                if let Ok(rel_path) =
-                    abs_path.strip_prefix(working_directory.remapped_path_if_available())
-                {
-                    // If the compiler's working directory (which also is the DW_AT_comp_dir of
-                    // the compilation unit) is a prefix of the path we are about to emit, then
-                    // only emit the part relative to the working directory.
-                    // Because of path remapping we sometimes see strange things here: `abs_path`
-                    // might actually look like a relative path
-                    // (e.g. `<crate-name-and-version>/src/lib.rs`), so if we emit it without
-                    // taking the working directory into account, downstream tooling will
-                    // interpret it as `<working-directory>/<crate-name-and-version>/src/lib.rs`,
-                    // which makes no sense. Usually in such cases the working directory will also
-                    // be remapped to `<crate-name-and-version>` or some other prefix of the path
-                    // we are remapping, so we end up with
-                    // `<crate-name-and-version>/<crate-name-and-version>/src/lib.rs`.
-                    // By moving the working directory portion into the `directory` part of the
-                    // DIFile, we allow LLVM to emit just the relative path for DWARF, while
-                    // still emitting the correct absolute path for CodeView.
-                    (
-                        working_directory.to_string_lossy(FileNameDisplayPreference::Remapped),
-                        rel_path.to_string_lossy().into_owned(),
-                    )
+                if cx.sess().should_prefer_remapped_for_codegen() {
+                    let filename = cx
+                        .sess()
+                        .source_map()
+                        .path_mapping()
+                        .to_embeddable_absolute_path(filename.clone(), working_directory);
+
+                    // Construct the absolute path of the file
+                    let abs_path = filename.remapped_path_if_available();
+                    debug!(?abs_path);
+
+                    if let Ok(rel_path) =
+                        abs_path.strip_prefix(working_directory.remapped_path_if_available())
+                    {
+                        // If the compiler's working directory (which also is the DW_AT_comp_dir of
+                        // the compilation unit) is a prefix of the path we are about to emit, then
+                        // only emit the part relative to the working directory.
+                        // Because of path remapping we sometimes see strange things here: `abs_path`
+                        // might actually look like a relative path
+                        // (e.g. `<crate-name-and-version>/src/lib.rs`), so if we emit it without
+                        // taking the working directory into account, downstream tooling will
+                        // interpret it as `<working-directory>/<crate-name-and-version>/src/lib.rs`,
+                        // which makes no sense. Usually in such cases the working directory will also
+                        // be remapped to `<crate-name-and-version>` or some other prefix of the path
+                        // we are remapping, so we end up with
+                        // `<crate-name-and-version>/<crate-name-and-version>/src/lib.rs`.
+                        // By moving the working directory portion into the `directory` part of the
+                        // DIFile, we allow LLVM to emit just the relative path for DWARF, while
+                        // still emitting the correct absolute path for CodeView.
+                        (
+                            working_directory.to_string_lossy(FileNameDisplayPreference::Remapped),
+                            rel_path.to_string_lossy().into_owned(),
+                        )
+                    } else {
+                        ("".into(), abs_path.to_string_lossy().into_owned())
+                    }
                 } else {
-                    ("".into(), abs_path.to_string_lossy().into_owned())
+                    let working_directory = working_directory.local_path_if_available();
+                    let filename = filename.local_path_if_available();
+
+                    debug!(?working_directory, ?filename);
+
+                    let abs_path: Cow<'_, Path> = if filename.is_absolute() {
+                        filename.into()
+                    } else {
+                        let mut p = PathBuf::new();
+                        p.push(working_directory);
+                        p.push(filename);
+                        p.into()
+                    };
+
+                    if let Ok(rel_path) = abs_path.strip_prefix(working_directory) {
+                        (
+                            working_directory.to_string_lossy().into(),
+                            rel_path.to_string_lossy().into_owned(),
+                        )
+                    } else {
+                        ("".into(), abs_path.to_string_lossy().into_owned())
+                    }
                 }
             }
-            other => ("".into(), other.prefer_remapped().to_string_lossy().into_owned()),
+            other => {
+                debug!(?other);
+                ("".into(), other.for_codegen(cx.sess()).to_string_lossy().into_owned())
+            }
         };
 
         let hash_kind = match source_file.src_hash.kind {
@@ -822,8 +851,9 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>(
     // FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice.
     let producer = format!("clang LLVM ({rustc_producer})");
 
+    use rustc_session::RemapFileNameExt;
     let name_in_debuginfo = name_in_debuginfo.to_string_lossy();
-    let work_dir = tcx.sess.opts.working_dir.to_string_lossy(FileNameDisplayPreference::Remapped);
+    let work_dir = tcx.sess.opts.working_dir.for_codegen(&tcx.sess).to_string_lossy();
     let flags = "\0";
     let output_filenames = tcx.output_filenames(());
     let split_name = if tcx.sess.target_can_use_split_dwarf() {
@@ -834,7 +864,13 @@ pub fn build_compile_unit_di_node<'ll, 'tcx>(
                 Some(codegen_unit_name),
             )
             // We get a path relative to the working directory from split_dwarf_path
-            .map(|f| tcx.sess.source_map().path_mapping().map_prefix(f).0)
+            .map(|f| {
+                if tcx.sess.should_prefer_remapped_for_split_debuginfo_paths() {
+                    tcx.sess.source_map().path_mapping().map_prefix(f).0
+                } else {
+                    f.into()
+                }
+            })
     } else {
         None
     }
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index 60620f26bbbab..68f22aaf990f6 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -1454,10 +1454,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         let tcx = bx.tcx();
 
         let mut span_to_caller_location = |span: Span| {
+            use rustc_session::RemapFileNameExt;
             let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
             let caller = tcx.sess.source_map().lookup_char_pos(topmost.lo());
             let const_loc = tcx.const_caller_location((
-                Symbol::intern(&caller.file.name.prefer_remapped().to_string_lossy()),
+                Symbol::intern(&caller.file.name.for_codegen(self.cx.sess()).to_string_lossy()),
                 caller.line as u32,
                 caller.col_display as u32 + 1,
             ));
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
index 948bec7464ad2..16b7decf9c43c 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics/caller_location.rs
@@ -114,8 +114,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub(crate) fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) {
         let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
         let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
+
+        use rustc_session::{config::RemapPathScopeComponents, RemapFileNameExt};
         (
-            Symbol::intern(&caller.file.name.prefer_remapped().to_string_lossy()),
+            Symbol::intern(
+                &caller
+                    .file
+                    .name
+                    .for_scope(&self.tcx.sess, RemapPathScopeComponents::DIAGNOSTICS)
+                    .to_string_lossy(),
+            ),
             u32::try_from(caller.line).unwrap(),
             u32::try_from(caller.col_display).unwrap().checked_add(1).unwrap(),
         )
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index dee2326ae3275..e949e4670e2c5 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -525,9 +525,17 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             // the remapped version -- as is necessary for reproducible builds.
             let mut source_file = match source_file.name {
                 FileName::Real(ref original_file_name) => {
-                    let adapted_file_name = source_map
-                        .path_mapping()
-                        .to_embeddable_absolute_path(original_file_name.clone(), working_directory);
+                    let adapted_file_name = if self.tcx.sess.should_prefer_remapped_for_codegen() {
+                        source_map.path_mapping().to_embeddable_absolute_path(
+                            original_file_name.clone(),
+                            working_directory,
+                        )
+                    } else {
+                        source_map.path_mapping().to_local_embeddable_absolute_path(
+                            original_file_name.clone(),
+                            working_directory,
+                        )
+                    };
 
                     if adapted_file_name != *original_file_name {
                         let mut adapted: SourceFile = (**source_file).clone();
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index abf13519e9eea..63126f4486b6a 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -219,7 +219,10 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
         let tcx = self.tcx;
         let source_map = tcx.sess.source_map();
         let body_span = self.body_span;
-        let file_name = Symbol::intern(&self.source_file.name.prefer_remapped().to_string_lossy());
+
+        use rustc_session::RemapFileNameExt;
+        let file_name =
+            Symbol::intern(&self.source_file.name.for_codegen(self.tcx.sess).to_string_lossy());
 
         for (bcb, spans) in coverage_spans.bcbs_with_coverage_spans() {
             let counter_kind = self.coverage_counters.take_bcb_counter(bcb).unwrap_or_else(|| {
diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml
index 3af83aaaaa8a2..e26d25d9a4123 100644
--- a/compiler/rustc_session/Cargo.toml
+++ b/compiler/rustc_session/Cargo.toml
@@ -4,6 +4,7 @@ version = "0.0.0"
 edition = "2021"
 
 [dependencies]
+bitflags = "1.2.1"
 getopts = "0.2"
 rustc_macros = { path = "../rustc_macros" }
 tracing = "0.1"
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index bbba800e84081..2e991b4c0adb5 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -21,8 +21,8 @@ use rustc_feature::UnstableFeatures;
 use rustc_span::edition::{Edition, DEFAULT_EDITION, EDITION_NAME_LIST, LATEST_STABLE_EDITION};
 use rustc_span::source_map::{FileName, FilePathMapping};
 use rustc_span::symbol::{sym, Symbol};
-use rustc_span::RealFileName;
 use rustc_span::SourceFileHashAlgorithm;
+use rustc_span::{FileNameDisplayPreference, RealFileName};
 
 use rustc_errors::emitter::HumanReadableErrorType;
 use rustc_errors::{ColorConfig, DiagnosticArgValue, HandlerFlags, IntoDiagnosticArg};
@@ -1018,6 +1018,32 @@ impl OutputFilenames {
     }
 }
 
+bitflags::bitflags! {
+    /// Scopes used to determined if it need to apply to --remap-path-prefix
+    pub struct RemapPathScopeComponents: u8 {
+        /// Apply remappings to the expansion of std::file!() macro
+        const MACRO = 1 << 0;
+        /// Apply remappings to printed compiler diagnostics
+        const DIAGNOSTICS = 1 << 1;
+        /// Apply remappings to debug information only when they are written to
+        /// compiled executables or libraries, but not when they are in split
+        /// debuginfo files
+        const UNSPLIT_DEBUGINFO = 1 << 2;
+        /// Apply remappings to debug information only when they are written to
+        /// split debug information files, but not in compiled executables or
+        /// libraries
+        const SPLIT_DEBUGINFO = 1 << 3;
+        /// Apply remappings to the paths pointing to split debug information
+        /// files. Does nothing when these files are not generated.
+        const SPLIT_DEBUGINFO_PATH = 1 << 4;
+
+        /// An alias for macro,unsplit-debuginfo,split-debuginfo-path. This
+        /// ensures all paths in compiled executables or libraries are remapped
+        /// but not elsewhere.
+        const OBJECT = Self::MACRO.bits | Self::UNSPLIT_DEBUGINFO.bits | Self::SPLIT_DEBUGINFO_PATH.bits;
+    }
+}
+
 pub fn host_triple() -> &'static str {
     // Get the host triple out of the build environment. This ensures that our
     // idea of the host triple is the same as for the set of libraries we've
@@ -1030,6 +1056,22 @@ pub fn host_triple() -> &'static str {
     (option_env!("CFG_COMPILER_HOST_TRIPLE")).expect("CFG_COMPILER_HOST_TRIPLE")
 }
 
+fn file_path_mapping(
+    remap_path_prefix: Vec<(PathBuf, PathBuf)>,
+    unstable_opts: &UnstableOptions,
+) -> FilePathMapping {
+    FilePathMapping::new(
+        remap_path_prefix.clone(),
+        if unstable_opts.remap_path_scope.contains(RemapPathScopeComponents::DIAGNOSTICS)
+            && !remap_path_prefix.is_empty()
+        {
+            FileNameDisplayPreference::Remapped
+        } else {
+            FileNameDisplayPreference::Local
+        },
+    )
+}
+
 impl Default for Options {
     fn default() -> Options {
         Options {
@@ -1085,7 +1127,7 @@ impl Options {
     }
 
     pub fn file_path_mapping(&self) -> FilePathMapping {
-        FilePathMapping::new(self.remap_path_prefix.clone())
+        file_path_mapping(self.remap_path_prefix.clone(), &self.unstable_opts)
     }
 
     /// Returns `true` if there will be an output file generated.
@@ -2867,7 +2909,7 @@ pub fn build_session_options(
         handler.early_error(format!("Current directory is invalid: {e}"));
     });
 
-    let remap = FilePathMapping::new(remap_path_prefix.clone());
+    let remap = file_path_mapping(remap_path_prefix.clone(), &unstable_opts);
     let (path, remapped) = remap.map_prefix(&working_dir);
     let working_dir = if remapped {
         RealFileName::Remapped { virtual_name: path.into_owned(), local_path: Some(working_dir) }
@@ -3173,8 +3215,8 @@ pub(crate) mod dep_tracking {
         BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, DebugInfoCompression,
         ErrorOutputType, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail,
         LtoCli, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes, Polonius,
-        ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath,
-        SymbolManglingVersion, TraitSolver, TrimmedDefPaths,
+        RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind,
+        SwitchWithOptPath, SymbolManglingVersion, TraitSolver, TrimmedDefPaths,
     };
     use crate::lint;
     use crate::options::WasiExecModel;
@@ -3268,6 +3310,7 @@ pub(crate) mod dep_tracking {
         StackProtector,
         SwitchWithOptPath,
         SymbolManglingVersion,
+        RemapPathScopeComponents,
         SourceFileHashAlgorithm,
         TrimmedDefPaths,
         OutFileName,
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index f33139c5c4b90..9561c7ba76035 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -427,6 +427,7 @@ mod desc {
     pub const parse_proc_macro_execution_strategy: &str =
         "one of supported execution strategies (`same-thread`, or `cross-thread`)";
     pub const parse_dump_solver_proof_tree: &str = "one of: `always`, `on-request`, `on-error`";
+    pub const parse_remap_path_scope: &str = "comma separated list of scopes: `macro`, `diagnostics`, `unsplit-debuginfo`, `split-debuginfo`, `split-debuginfo-path`, `object`, `all`";
 }
 
 mod parse {
@@ -1095,6 +1096,30 @@ mod parse {
         true
     }
 
+    pub(crate) fn parse_remap_path_scope(
+        slot: &mut RemapPathScopeComponents,
+        v: Option<&str>,
+    ) -> bool {
+        if let Some(v) = v {
+            *slot = RemapPathScopeComponents::empty();
+            for s in v.split(',') {
+                *slot |= match s {
+                    "macro" => RemapPathScopeComponents::MACRO,
+                    "diagnostics" => RemapPathScopeComponents::DIAGNOSTICS,
+                    "unsplit-debuginfo" => RemapPathScopeComponents::UNSPLIT_DEBUGINFO,
+                    "split-debuginfo" => RemapPathScopeComponents::SPLIT_DEBUGINFO,
+                    "split-debuginfo-path" => RemapPathScopeComponents::SPLIT_DEBUGINFO_PATH,
+                    "object" => RemapPathScopeComponents::OBJECT,
+                    "all" => RemapPathScopeComponents::all(),
+                    _ => return false,
+                }
+            }
+            true
+        } else {
+            false
+        }
+    }
+
     pub(crate) fn parse_relocation_model(slot: &mut Option<RelocModel>, v: Option<&str>) -> bool {
         match v.and_then(|s| RelocModel::from_str(s).ok()) {
             Some(relocation_model) => *slot = Some(relocation_model),
@@ -1731,6 +1756,8 @@ options! {
         "choose which RELRO level to use"),
     remap_cwd_prefix: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
         "remap paths under the current working directory to this path prefix"),
+    remap_path_scope: RemapPathScopeComponents = (RemapPathScopeComponents::all(), parse_remap_path_scope, [TRACKED],
+        "remap path scope (default: all)"),
     remark_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
         "directory into which to write optimization remarks (if not specified, they will be \
 written to standard error output)"),
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 5cac11cc8f782..793074981654c 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -1,7 +1,8 @@
 use crate::code_stats::CodeStats;
 pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
 use crate::config::{
-    self, CrateType, InstrumentCoverage, OptLevel, OutFileName, OutputType, SwitchWithOptPath,
+    self, CrateType, InstrumentCoverage, OptLevel, OutFileName, OutputType,
+    RemapPathScopeComponents, SwitchWithOptPath,
 };
 use crate::config::{ErrorOutputType, Input};
 use crate::errors;
@@ -254,7 +255,11 @@ impl Session {
 
     pub fn local_crate_source_file(&self) -> Option<PathBuf> {
         let path = self.io.input.opt_path()?;
-        Some(self.opts.file_path_mapping().map_prefix(path).0.into_owned())
+        if self.should_prefer_remapped_for_codegen() {
+            Some(self.opts.file_path_mapping().map_prefix(path).0.into_owned())
+        } else {
+            Some(path.to_path_buf())
+        }
     }
 
     fn check_miri_unleashed_features(&self) {
@@ -1243,6 +1248,53 @@ impl Session {
     pub fn link_dead_code(&self) -> bool {
         self.opts.cg.link_dead_code.unwrap_or(false)
     }
+
+    pub fn should_prefer_remapped_for_codegen(&self) -> bool {
+        // bail out, if any of the requested crate types aren't:
+        // "compiled executables or libraries"
+        for crate_type in &self.opts.crate_types {
+            match crate_type {
+                CrateType::Executable
+                | CrateType::Dylib
+                | CrateType::Rlib
+                | CrateType::Staticlib
+                | CrateType::Cdylib => continue,
+                CrateType::ProcMacro => return false,
+            }
+        }
+
+        let has_split_debuginfo = match self.split_debuginfo() {
+            SplitDebuginfo::Off => false,
+            SplitDebuginfo::Packed => true,
+            SplitDebuginfo::Unpacked => true,
+        };
+
+        let remap_path_scopes = &self.opts.unstable_opts.remap_path_scope;
+        let mut prefer_remapped = false;
+
+        if remap_path_scopes.contains(RemapPathScopeComponents::UNSPLIT_DEBUGINFO) {
+            prefer_remapped |= !has_split_debuginfo;
+        }
+
+        if remap_path_scopes.contains(RemapPathScopeComponents::SPLIT_DEBUGINFO) {
+            prefer_remapped |= has_split_debuginfo;
+        }
+
+        prefer_remapped
+    }
+
+    pub fn should_prefer_remapped_for_split_debuginfo_paths(&self) -> bool {
+        let has_split_debuginfo = match self.split_debuginfo() {
+            SplitDebuginfo::Off => false,
+            SplitDebuginfo::Packed | SplitDebuginfo::Unpacked => true,
+        };
+
+        self.opts
+            .unstable_opts
+            .remap_path_scope
+            .contains(RemapPathScopeComponents::SPLIT_DEBUGINFO_PATH)
+            && has_split_debuginfo
+    }
 }
 
 // JUSTIFICATION: part of session construction
@@ -1752,3 +1804,53 @@ fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> {
     };
     emitter
 }
+
+pub trait RemapFileNameExt {
+    type Output<'a>
+    where
+        Self: 'a;
+
+    fn for_scope(&self, sess: &Session, scopes: RemapPathScopeComponents) -> Self::Output<'_>;
+
+    fn for_codegen(&self, sess: &Session) -> Self::Output<'_>;
+}
+
+impl RemapFileNameExt for rustc_span::FileName {
+    type Output<'a> = rustc_span::FileNameDisplay<'a>;
+
+    fn for_scope(&self, sess: &Session, scopes: RemapPathScopeComponents) -> Self::Output<'_> {
+        if sess.opts.unstable_opts.remap_path_scope.contains(scopes) {
+            self.prefer_remapped_unconditionaly()
+        } else {
+            self.prefer_local()
+        }
+    }
+
+    fn for_codegen(&self, sess: &Session) -> Self::Output<'_> {
+        if sess.should_prefer_remapped_for_codegen() {
+            self.prefer_remapped_unconditionaly()
+        } else {
+            self.prefer_local()
+        }
+    }
+}
+
+impl RemapFileNameExt for rustc_span::RealFileName {
+    type Output<'a> = &'a Path;
+
+    fn for_scope(&self, sess: &Session, scopes: RemapPathScopeComponents) -> Self::Output<'_> {
+        if sess.opts.unstable_opts.remap_path_scope.contains(scopes) {
+            self.remapped_path_if_available()
+        } else {
+            self.local_path_if_available()
+        }
+    }
+
+    fn for_codegen(&self, sess: &Session) -> Self::Output<'_> {
+        if sess.should_prefer_remapped_for_codegen() {
+            self.remapped_path_if_available()
+        } else {
+            self.local_path_if_available()
+        }
+    }
+}
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index e62efab5793f2..49b4042d1488f 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -370,7 +370,7 @@ impl FileName {
         }
     }
 
-    pub fn prefer_remapped(&self) -> FileNameDisplay<'_> {
+    pub fn prefer_remapped_unconditionaly(&self) -> FileNameDisplay<'_> {
         FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Remapped }
     }
 
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 0b575c13adf2a..612d396b09929 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -1124,16 +1124,13 @@ pub struct FilePathMapping {
 
 impl FilePathMapping {
     pub fn empty() -> FilePathMapping {
-        FilePathMapping::new(Vec::new())
+        FilePathMapping::new(Vec::new(), FileNameDisplayPreference::Local)
     }
 
-    pub fn new(mapping: Vec<(PathBuf, PathBuf)>) -> FilePathMapping {
-        let filename_display_for_diagnostics = if mapping.is_empty() {
-            FileNameDisplayPreference::Local
-        } else {
-            FileNameDisplayPreference::Remapped
-        };
-
+    pub fn new(
+        mapping: Vec<(PathBuf, PathBuf)>,
+        filename_display_for_diagnostics: FileNameDisplayPreference,
+    ) -> FilePathMapping {
         FilePathMapping { mapping, filename_display_for_diagnostics }
     }
 
@@ -1287,6 +1284,27 @@ impl FilePathMapping {
         }
     }
 
+    /// Expand a relative path to an absolute path **without** remapping taken into account.
+    ///
+    /// The resulting `RealFileName` will have its `virtual_path` portion erased if
+    /// possible (i.e. if there's also a remapped path).
+    pub fn to_local_embeddable_absolute_path(
+        &self,
+        file_path: RealFileName,
+        working_directory: &RealFileName,
+    ) -> RealFileName {
+        let file_path = file_path.local_path_if_available();
+        if file_path.is_absolute() {
+            // No remapping has applied to this path and it is absolute,
+            // so the working directory cannot influence it either, so
+            // we are done.
+            return RealFileName::LocalPath(file_path.to_path_buf());
+        }
+        debug_assert!(file_path.is_relative());
+        let working_directory = working_directory.local_path_if_available();
+        RealFileName::LocalPath(Path::new(working_directory).join(file_path))
+    }
+
     /// Attempts to (heuristically) reverse a prefix mapping.
     ///
     /// Returns [`Some`] if there is exactly one mapping where the "to" part is
diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs
index a12f50c87a213..5697969ddb8f6 100644
--- a/compiler/rustc_span/src/source_map/tests.rs
+++ b/compiler/rustc_span/src/source_map/tests.rs
@@ -351,7 +351,10 @@ fn reverse_map_prefix(mapping: &FilePathMapping, p: &str) -> Option<String> {
 fn path_prefix_remapping() {
     // Relative to relative
     {
-        let mapping = &FilePathMapping::new(vec![(path("abc/def"), path("foo"))]);
+        let mapping = &FilePathMapping::new(
+            vec![(path("abc/def"), path("foo"))],
+            FileNameDisplayPreference::Remapped,
+        );
 
         assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), path_str("foo/src/main.rs"));
         assert_eq!(map_path_prefix(mapping, "abc/def"), path_str("foo"));
@@ -359,7 +362,10 @@ fn path_prefix_remapping() {
 
     // Relative to absolute
     {
-        let mapping = &FilePathMapping::new(vec![(path("abc/def"), path("/foo"))]);
+        let mapping = &FilePathMapping::new(
+            vec![(path("abc/def"), path("/foo"))],
+            FileNameDisplayPreference::Remapped,
+        );
 
         assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), path_str("/foo/src/main.rs"));
         assert_eq!(map_path_prefix(mapping, "abc/def"), path_str("/foo"));
@@ -367,7 +373,10 @@ fn path_prefix_remapping() {
 
     // Absolute to relative
     {
-        let mapping = &FilePathMapping::new(vec![(path("/abc/def"), path("foo"))]);
+        let mapping = &FilePathMapping::new(
+            vec![(path("/abc/def"), path("foo"))],
+            FileNameDisplayPreference::Remapped,
+        );
 
         assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), path_str("foo/src/main.rs"));
         assert_eq!(map_path_prefix(mapping, "/abc/def"), path_str("foo"));
@@ -375,7 +384,10 @@ fn path_prefix_remapping() {
 
     // Absolute to absolute
     {
-        let mapping = &FilePathMapping::new(vec![(path("/abc/def"), path("/foo"))]);
+        let mapping = &FilePathMapping::new(
+            vec![(path("/abc/def"), path("/foo"))],
+            FileNameDisplayPreference::Remapped,
+        );
 
         assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), path_str("/foo/src/main.rs"));
         assert_eq!(map_path_prefix(mapping, "/abc/def"), path_str("/foo"));
@@ -385,8 +397,10 @@ fn path_prefix_remapping() {
 #[test]
 fn path_prefix_remapping_expand_to_absolute() {
     // "virtual" working directory is relative path
-    let mapping =
-        &FilePathMapping::new(vec![(path("/foo"), path("FOO")), (path("/bar"), path("BAR"))]);
+    let mapping = &FilePathMapping::new(
+        vec![(path("/foo"), path("FOO")), (path("/bar"), path("BAR"))],
+        FileNameDisplayPreference::Remapped,
+    );
     let working_directory = path("/foo");
     let working_directory = RealFileName::Remapped {
         local_path: Some(working_directory.clone()),
@@ -487,8 +501,10 @@ fn path_prefix_remapping_expand_to_absolute() {
 fn path_prefix_remapping_reverse() {
     // Ignores options without alphanumeric chars.
     {
-        let mapping =
-            &FilePathMapping::new(vec![(path("abc"), path("/")), (path("def"), path("."))]);
+        let mapping = &FilePathMapping::new(
+            vec![(path("abc"), path("/")), (path("def"), path("."))],
+            FileNameDisplayPreference::Remapped,
+        );
 
         assert_eq!(reverse_map_prefix(mapping, "/hello.rs"), None);
         assert_eq!(reverse_map_prefix(mapping, "./hello.rs"), None);
@@ -496,20 +512,20 @@ fn path_prefix_remapping_reverse() {
 
     // Returns `None` if multiple options match.
     {
-        let mapping = &FilePathMapping::new(vec![
-            (path("abc"), path("/redacted")),
-            (path("def"), path("/redacted")),
-        ]);
+        let mapping = &FilePathMapping::new(
+            vec![(path("abc"), path("/redacted")), (path("def"), path("/redacted"))],
+            FileNameDisplayPreference::Remapped,
+        );
 
         assert_eq!(reverse_map_prefix(mapping, "/redacted/hello.rs"), None);
     }
 
     // Distinct reverse mappings.
     {
-        let mapping = &FilePathMapping::new(vec![
-            (path("abc"), path("/redacted")),
-            (path("def/ghi"), path("/fake/dir")),
-        ]);
+        let mapping = &FilePathMapping::new(
+            vec![(path("abc"), path("/redacted")), (path("def/ghi"), path("/fake/dir"))],
+            FileNameDisplayPreference::Remapped,
+        );
 
         assert_eq!(
             reverse_map_prefix(mapping, "/redacted/path/hello.rs"),
diff --git a/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md b/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md
new file mode 100644
index 0000000000000..13349ff6b8f41
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/remap-path-scope.md
@@ -0,0 +1,24 @@
+# `remap-path-scope`
+
+The tracking issue for this feature is: [#111540](https://github.com/rust-lang/rust/issues/111540).
+
+------------------------
+
+When the `--remap-path-prefix` option is passed to rustc, source path prefixes in all output will be affected by default.
+The `--remap-path-scope` argument can be used in conjunction with `--remap-path-prefix` to determine paths in which output context should be affected.
+This flag accepts a comma-separated list of values and may be specified multiple times, in which case the scopes are aggregated together. The valid scopes are:
+
+- `macro` - apply remappings to the expansion of `std::file!()` macro. This is where paths in embedded panic messages come from
+- `diagnostics` - apply remappings to printed compiler diagnostics
+- `unsplit-debuginfo` - apply remappings to debug information only when they are written to compiled executables or libraries, but not when they are in split debuginfo files
+- `split-debuginfo` - apply remappings to debug information only when they are written to split debug information files, but not in compiled executables or libraries
+- `split-debuginfo-path` - apply remappings to the paths pointing to split debug information files. Does nothing when these files are not generated.
+- `object` - an alias for `macro,unsplit-debuginfo,split-debuginfo-path`. This ensures all paths in compiled executables or libraries are remapped, but not elsewhere.
+- `all` and `true` - an alias for all of the above, also equivalent to supplying only `--remap-path-prefix` without `--remap-path-scope`.
+
+## Example
+```sh
+# This would produce an absolute path to main.rs in build outputs of
+# "./main.rs".
+rustc --remap-path-prefix=$(PWD)=/remapped -Zremap-path-prefix=object main.rs
+```
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 4a218b9b37cb1..d01553b8e7156 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -225,7 +225,8 @@ impl SourceCollector<'_, '_> {
         cur.push(&fname);
 
         let title = format!("{} - source", src_fname.to_string_lossy());
-        let desc = format!("Source of the Rust file `{}`.", filename.prefer_remapped());
+        let desc =
+            format!("Source of the Rust file `{}`.", filename.prefer_remapped_unconditionaly());
         let page = layout::Page {
             title: &title,
             css_class: "src",
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index c38a3e81b0f72..c8600badf18e8 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -494,7 +494,7 @@ impl SerializableSpan {
         let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo());
 
         Self {
-            path: format!("{}", loc.file.name.prefer_remapped()),
+            path: format!("{}", loc.file.name.prefer_remapped_unconditionaly()),
             line: loc.line,
         }
     }
diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs
index ee2edd462d19e..08b26f5fe854b 100644
--- a/src/tools/miri/src/shims/backtrace.rs
+++ b/src/tools/miri/src/shims/backtrace.rs
@@ -135,7 +135,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             this.tcx.sess.source_map().lookup_char_pos(BytePos(offset.bytes().try_into().unwrap()));
 
         let name = fn_instance.to_string();
-        let filename = lo.file.name.prefer_remapped().to_string();
+        let filename = lo.file.name.prefer_remapped_unconditionaly().to_string();
 
         Ok((fn_instance, lo, name, filename))
     }
diff --git a/tests/run-make/remap-path-prefix-dwarf/Makefile b/tests/run-make/remap-path-prefix-dwarf/Makefile
index c9ede1b60274e..8905a00ea2878 100644
--- a/tests/run-make/remap-path-prefix-dwarf/Makefile
+++ b/tests/run-make/remap-path-prefix-dwarf/Makefile
@@ -3,17 +3,26 @@
 
 # ignore-windows
 
+include ../tools.mk
+
 SRC_DIR := $(abspath .)
 SRC_DIR_PARENT := $(abspath ..)
 
-include ../tools.mk
+ifeq ($(UNAME),Darwin)
+  DEBUGINFOOPTS := -Csplit-debuginfo=off
+else
+  DEBUGINFOOPTS :=
+endif
 
 all: \
   abs_input_outside_working_dir \
   rel_input_remap_working_dir \
+  rel_input_remap_working_dir_scope \
   rel_input_remap_working_dir_parent \
   rel_input_remap_working_dir_child \
+  rel_input_remap_working_dir_diagnostics \
   abs_input_inside_working_dir \
+  abs_input_inside_working_dir_scope \
   abs_input_outside_working_dir
 
 # The compiler is called with an *ABSOLUTE PATH* as input, and that absolute path *is* within
@@ -27,6 +36,17 @@ abs_input_inside_working_dir:
 	# No weird duplication of remapped components (see #78479)
 	"$(LLVM_BIN_DIR)"/llvm-dwarfdump $(TMPDIR)/abs_input_inside_working_dir.rlib | $(CGREP) -v "REMAPPED/REMAPPED"
 
+# The compiler is called with an *ABSOLUTE PATH* as input, and that absolute path *is* within
+# the working directory of the compiler. We are remapping the path that contains `src`.
+abs_input_inside_working_dir_scope:
+	# We explicitly switch to a directory that *is* a prefix of the directory our
+	# source code is contained in.
+	cd $(SRC_DIR) && $(RUSTC) $(SRC_DIR)/src/quux.rs -o "$(TMPDIR)/abs_input_inside_working_dir_scope.rlib" -Cdebuginfo=2 --remap-path-prefix $(SRC_DIR)=REMAPPED -Zremap-path-scope=object $(DEBUGINFOOPTS)
+	# We expect the path to the main source file to be remapped.
+	"$(LLVM_BIN_DIR)"/llvm-dwarfdump $(TMPDIR)/abs_input_inside_working_dir_scope.rlib | $(CGREP) "REMAPPED/src/quux.rs"
+	# No weird duplication of remapped components (see #78479)
+	"$(LLVM_BIN_DIR)"/llvm-dwarfdump $(TMPDIR)/abs_input_inside_working_dir_scope.rlib | $(CGREP) -v "REMAPPED/REMAPPED"
+
 # The compiler is called with an *ABSOLUTE PATH* as input, and that absolute path is *not* within
 # the working directory of the compiler. We are remapping both the path that contains `src` and
 # the working directory to the same thing. This setup corresponds to a workaround that is needed
@@ -52,6 +72,21 @@ rel_input_remap_working_dir:
 	# No weird duplication of remapped components (see #78479)
 	"$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir.rlib" | $(CGREP) -v "REMAPPED/REMAPPED"
 
+# The compiler is called with a *RELATIVE PATH* as input. We are remapping the working directory of
+# the compiler, which naturally is an implicit prefix of our relative input path. Debuginfo will
+# expand the relative path to an absolute path and we expect the working directory to be remapped
+# in that expansion.
+rel_input_remap_working_dir_scope:
+	cd $(SRC_DIR) && $(RUSTC) src/quux.rs -o "$(TMPDIR)/rel_input_remap_working_dir_scope.rlib" -Cdebuginfo=2 --remap-path-prefix "$(SRC_DIR)=REMAPPED" -Zremap-path-scope=object $(DEBUGINFOOPTS)
+	"$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_scope.rlib" | $(CGREP) "REMAPPED/src/quux.rs"
+	# No weird duplication of remapped components (see #78479)
+	"$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_scope.rlib" | $(CGREP) -v "REMAPPED/REMAPPED"
+
+rel_input_remap_working_dir_diagnostics:
+	cd $(SRC_DIR) && $(RUSTC) src/quux.rs -o "$(TMPDIR)/rel_input_remap_working_dir_scope.rlib" -Cdebuginfo=2 --remap-path-prefix "$(SRC_DIR)=REMAPPED" -Zremap-path-scope=diagnostics $(DEBUGINFOOPTS)
+	"$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_scope.rlib" | $(CGREP) -v "REMAPPED/src/quux.rs"
+	"$(LLVM_BIN_DIR)"/llvm-dwarfdump "$(TMPDIR)/rel_input_remap_working_dir_scope.rlib" | $(CGREP) -v "REMAPPED/REMAPPED"
+
 # The compiler is called with a *RELATIVE PATH* as input. We are remapping a *SUB-DIRECTORY* of the
 # compiler's working directory. This test makes sure that that directory is remapped even though it
 # won't actually show up in this form in the compiler's SourceMap and instead is only constructed
diff --git a/tests/run-make/remap-path-prefix/Makefile b/tests/run-make/remap-path-prefix/Makefile
index 2a7378fdf9ed5..35f65240ff9c8 100644
--- a/tests/run-make/remap-path-prefix/Makefile
+++ b/tests/run-make/remap-path-prefix/Makefile
@@ -2,8 +2,38 @@ include ../tools.mk
 
 # ignore-windows
 
+ifeq ($(UNAME),Darwin)
+  DEBUGINFOOPTS := -Csplit-debuginfo=off
+else
+  DEBUGINFOOPTS :=
+endif
+
+all: remap remap-with-scope
+
 # Checks if remapping works if the remap-from string contains path to the working directory plus more
-all:
+remap:
 	$(RUSTC) --remap-path-prefix $$PWD/auxiliary=/the/aux --crate-type=lib --emit=metadata auxiliary/lib.rs
 	grep "/the/aux/lib.rs" $(TMPDIR)/liblib.rmeta || exit 1
 	! grep "$$PWD/auxiliary" $(TMPDIR)/liblib.rmeta || exit 1
+
+remap-with-scope:
+	$(RUSTC) --remap-path-prefix $$PWD/auxiliary=/the/aux -Zremap-path-scope=object $(DEBUGINFOOPTS) --crate-type=lib --emit=metadata auxiliary/lib.rs
+	grep "/the/aux/lib.rs" $(TMPDIR)/liblib.rmeta || exit 1
+	! grep "$$PWD/auxiliary" $(TMPDIR)/liblib.rmeta || exit 1
+
+	$(RUSTC) --remap-path-prefix $$PWD/auxiliary=/the/aux -Zremap-path-scope=diagnostics $(DEBUGINFOOPTS) --crate-type=lib --emit=metadata auxiliary/lib.rs
+	! grep "/the/aux/lib.rs" $(TMPDIR)/liblib.rmeta || exit 1
+	grep "$$PWD/auxiliary" $(TMPDIR)/liblib.rmeta || exit 1
+
+	$(RUSTC) --remap-path-prefix $$PWD/auxiliary=/the/aux -Zremap-path-scope=diagnostics,object $(DEBUGINFOOPTS) --crate-type=lib --emit=metadata auxiliary/lib.rs
+	grep "/the/aux/lib.rs" $(TMPDIR)/liblib.rmeta || exit 1
+	! grep "$$PWD/auxiliary" $(TMPDIR)/liblib.rmeta || exit 1
+
+	$(RUSTC) --remap-path-prefix $$PWD/auxiliary=/the/aux -Zremap-path-scope=split-debuginfo $(DEBUGINFOOPTS) --crate-type=lib --emit=metadata auxiliary/lib.rs
+	! grep "/the/aux/lib.rs" $(TMPDIR)/liblib.rmeta || exit 1
+	grep "$$PWD/auxiliary" $(TMPDIR)/liblib.rmeta || exit 1
+
+    # FIXME: We should test the split debuginfo files, but we don't currently a good infra for that
+	$(RUSTC) --remap-path-prefix $$PWD/auxiliary=/the/aux -Zremap-path-scope=split-debuginfo -Zunstable-options -Csplit-debuginfo=packed --crate-type=lib --emit=metadata auxiliary/lib.rs
+	grep "/the/aux/lib.rs" $(TMPDIR)/liblib.rmeta || exit 1
+	! grep "$$PWD/auxiliary" $(TMPDIR)/liblib.rmeta || exit 1
diff --git a/tests/run-make/split-debuginfo/Makefile b/tests/run-make/split-debuginfo/Makefile
index 71e014c1f7147..9e05c8dc179bd 100644
--- a/tests/run-make/split-debuginfo/Makefile
+++ b/tests/run-make/split-debuginfo/Makefile
@@ -104,7 +104,7 @@ packed-lto-single:
 	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
 	rm $(TMPDIR)/libbaz.rlib
 
-packed-remapped: packed-remapped-split packed-remapped-single
+packed-remapped: packed-remapped-split packed-remapped-single packed-remapped-scope packed-remapped-wrong-scope
 
 # - Debuginfo in `.dwo` files
 # - `.o` and binary refer to remapped `.dwo` paths which do not exist
@@ -134,6 +134,36 @@ packed-remapped-single:
 	rm $(TMPDIR)/foo.dwp
 	rm $(TMPDIR)/$(call BIN,foo)
 
+# - Debuginfo in `.o` files
+# - `.o` and binary refer to remapped `.o` paths which do not exist
+# - `.o` deleted
+# - `.dwo` never created
+# - `.dwp` present
+packed-remapped-scope:
+	$(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \
+		-Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \
+		-Z remap-path-scope=split-debuginfo-path foo.rs -g
+	objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
+	rm $(TMPDIR)/foo.dwp
+	rm $(TMPDIR)/$(call BIN,foo)
+
+# - Debuginfo in `.o` files
+# - `.o` and binary refer to remapped `.o` paths which do not exist
+# - `.o` deleted
+# - `.dwo` never created
+# - `.dwp` present
+packed-remapped-wrong-scope:
+	$(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \
+		-Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \
+		-Z remap-path-scope=macro foo.rs -g
+	objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1
+	ls $(TMPDIR)/*.o && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
+	rm $(TMPDIR)/foo.dwp
+	rm $(TMPDIR)/$(call BIN,foo)
+
 packed-crosscrate: packed-crosscrate-split packed-crosscrate-single
 
 # - Debuginfo in `.dwo` files
@@ -230,7 +260,7 @@ unpacked-lto-single:
 	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
 	rm $(TMPDIR)/libbaz.rlib
 
-unpacked-remapped: unpacked-remapped-split unpacked-remapped-single
+unpacked-remapped: unpacked-remapped-split unpacked-remapped-single unpacked-remapped-scope unpacked-remapped-wrong-scope
 
 # - Debuginfo in `.dwo` files
 # - `.o` and binary refer to remapped `.dwo` paths which do not exist
@@ -260,6 +290,36 @@ unpacked-remapped-single:
 	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
 	rm $(TMPDIR)/$(call BIN,foo)
 
+# - Debuginfo in `.o` files
+# - `.o` and binary refer to remapped `.o` paths which do not exist
+# - `.o` present
+# - `.dwo` never created
+# - `.dwp` never created
+unpacked-remapped-scope:
+	$(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \
+		-Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \
+		-Z remap-path-scope=split-debuginfo-path foo.rs -g
+	objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1
+	rm $(TMPDIR)/*.o
+	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
+	rm $(TMPDIR)/$(call BIN,foo)
+
+# - Debuginfo in `.o` files
+# - `.o` and binary refer to remapped `.o` paths which do not exist
+# - `.o` present
+# - `.dwo` never created
+# - `.dwp` never created
+unpacked-remapped-wrong-scope:
+	$(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \
+		-Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \
+		-Z remap-path-scope=macro foo.rs -g
+	objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1
+	rm $(TMPDIR)/*.o
+	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
+	rm $(TMPDIR)/$(call BIN,foo)
+
 unpacked-crosscrate: unpacked-crosscrate-split unpacked-crosscrate-single
 
 # - Debuginfo in `.dwo` files
diff --git a/tests/ui/errors/remap-path-prefix-macro.normal.run.stdout b/tests/ui/errors/remap-path-prefix-macro.normal.run.stdout
new file mode 100644
index 0000000000000..3bbdcbb8655b2
--- /dev/null
+++ b/tests/ui/errors/remap-path-prefix-macro.normal.run.stdout
@@ -0,0 +1 @@
+remapped/errors/remap-path-prefix-macro.rs
diff --git a/tests/ui/errors/remap-path-prefix-macro.rs b/tests/ui/errors/remap-path-prefix-macro.rs
new file mode 100644
index 0000000000000..0ba706b0a8f3e
--- /dev/null
+++ b/tests/ui/errors/remap-path-prefix-macro.rs
@@ -0,0 +1,12 @@
+// run-pass
+// check-run-results
+
+// revisions: normal with-macro-scope without-macro-scope
+// compile-flags: --remap-path-prefix={{src-base}}=remapped
+// [with-macro-scope]compile-flags: -Zremap-path-scope=macro,diagnostics
+// [without-macro-scope]compile-flags: -Zremap-path-scope=diagnostics
+// no-remap-src-base: Manually remap, so the remapped path remains in .stderr file.
+
+fn main() {
+    println!("{}", file!());
+}
diff --git a/tests/ui/errors/remap-path-prefix-macro.with-macro-scope.run.stdout b/tests/ui/errors/remap-path-prefix-macro.with-macro-scope.run.stdout
new file mode 100644
index 0000000000000..3bbdcbb8655b2
--- /dev/null
+++ b/tests/ui/errors/remap-path-prefix-macro.with-macro-scope.run.stdout
@@ -0,0 +1 @@
+remapped/errors/remap-path-prefix-macro.rs
diff --git a/tests/ui/errors/remap-path-prefix-macro.without-macro-scope.run.stdout b/tests/ui/errors/remap-path-prefix-macro.without-macro-scope.run.stdout
new file mode 100644
index 0000000000000..642823fec86a1
--- /dev/null
+++ b/tests/ui/errors/remap-path-prefix-macro.without-macro-scope.run.stdout
@@ -0,0 +1 @@
+$DIR/remap-path-prefix-macro.rs
diff --git a/tests/ui/errors/remap-path-prefix.stderr b/tests/ui/errors/remap-path-prefix.normal.stderr
similarity index 82%
rename from tests/ui/errors/remap-path-prefix.stderr
rename to tests/ui/errors/remap-path-prefix.normal.stderr
index 62dbd4b8881a3..004f10b4e4314 100644
--- a/tests/ui/errors/remap-path-prefix.stderr
+++ b/tests/ui/errors/remap-path-prefix.normal.stderr
@@ -1,5 +1,5 @@
 error[E0425]: cannot find value `ferris` in this scope
-  --> remapped/errors/remap-path-prefix.rs:16:5
+  --> remapped/errors/remap-path-prefix.rs:19:5
    |
 LL |     ferris
    |     ^^^^^^ not found in this scope
diff --git a/tests/ui/errors/remap-path-prefix.rs b/tests/ui/errors/remap-path-prefix.rs
index 393b8e22f1c1d..e3338c10fd7e6 100644
--- a/tests/ui/errors/remap-path-prefix.rs
+++ b/tests/ui/errors/remap-path-prefix.rs
@@ -1,4 +1,7 @@
+// revisions: normal with-diagnostic-scope without-diagnostic-scope
 // compile-flags: --remap-path-prefix={{src-base}}=remapped
+// [with-diagnostic-scope]compile-flags: -Zremap-path-scope=diagnostics
+// [without-diagnostic-scope]compile-flags: -Zremap-path-scope=object
 // no-remap-src-base: Manually remap, so the remapped path remains in .stderr file.
 
 // The remapped paths are not normalized by compiletest.
diff --git a/tests/ui/errors/remap-path-prefix.with-diagnostic-scope.stderr b/tests/ui/errors/remap-path-prefix.with-diagnostic-scope.stderr
new file mode 100644
index 0000000000000..004f10b4e4314
--- /dev/null
+++ b/tests/ui/errors/remap-path-prefix.with-diagnostic-scope.stderr
@@ -0,0 +1,9 @@
+error[E0425]: cannot find value `ferris` in this scope
+  --> remapped/errors/remap-path-prefix.rs:19:5
+   |
+LL |     ferris
+   |     ^^^^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0425`.
diff --git a/tests/ui/errors/remap-path-prefix.without-diagnostic-scope.stderr b/tests/ui/errors/remap-path-prefix.without-diagnostic-scope.stderr
new file mode 100644
index 0000000000000..98fe328193ced
--- /dev/null
+++ b/tests/ui/errors/remap-path-prefix.without-diagnostic-scope.stderr
@@ -0,0 +1,9 @@
+error[E0425]: cannot find value `ferris` in this scope
+  --> $DIR/remap-path-prefix.rs:19:5
+   |
+LL |     ferris
+   |     ^^^^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0425`.