]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #97800 - pnkfelix:issue-97463-fix-aarch64-call-abi-does-not-zeroext...
authorbors <bors@rust-lang.org>
Fri, 16 Sep 2022 20:08:05 +0000 (20:08 +0000)
committerbors <bors@rust-lang.org>
Fri, 16 Sep 2022 20:08:05 +0000 (20:08 +0000)
Aarch64 call abi does not zeroext (and one cannot assume it does so)

Fix #97463

1  2 
compiler/rustc_target/src/abi/call/aarch64.rs
compiler/rustc_target/src/abi/call/mod.rs
compiler/rustc_target/src/spec/mod.rs
src/test/codegen/pic-relocation-model.rs

index 8a5ad90db71f555041b4c45eab93759d884302e6,e88c0cdf0ec3c221eb828aefadf8ec57050920cb..a84988fa75c6dd79279f954c7c56528f0b64cfcb
@@@ -1,6 -1,27 +1,27 @@@
  use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform};
  use crate::abi::{HasDataLayout, TyAbiInterface};
  
+ /// Given integer-types M and register width N (e.g. M=u16 and N=32 bits), the
+ /// `ParamExtension` policy specifies how a uM value should be treated when
+ /// passed via register or stack-slot of width N. See also rust-lang/rust#97463.
+ #[derive(Copy, Clone, PartialEq)]
+ pub enum ParamExtension {
+     /// Indicates that when passing an i8/i16, either as a function argument or
+     /// as a return value, it must be sign-extended to 32 bits, and likewise a
+     /// u8/u16 must be zero-extended to 32-bits. (This variant is here to
+     /// accommodate Apple's deviation from the usual AArch64 ABI as defined by
+     /// ARM.)
+     ///
+     /// See also: <https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Pass-Arguments-to-Functions-Correctly>
+     ExtendTo32Bits,
+     /// Indicates that no sign- nor zero-extension is performed: if a value of
+     /// type with bitwidth M is passed as function argument or return value,
+     /// then M bits are copied into the least significant M bits, and the
+     /// remaining bits of the register (or word of memory) are untouched.
+     NoExtension,
+ }
  fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option<Uniform>
  where
      Ty: TyAbiInterface<'a, C> + Copy,
      })
  }
  
- fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>)
+ fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, param_policy: ParamExtension)
  where
      Ty: TyAbiInterface<'a, C> + Copy,
      C: HasDataLayout,
  {
      if !ret.layout.is_aggregate() {
-         ret.extend_integer_width_to(32);
+         match param_policy {
+             ParamExtension::ExtendTo32Bits => ret.extend_integer_width_to(32),
+             ParamExtension::NoExtension => {}
+         }
          return;
      }
      if let Some(uniform) = is_homogeneous_aggregate(cx, ret) {
      ret.make_indirect();
  }
  
- fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>)
+ fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, param_policy: ParamExtension)
  where
      Ty: TyAbiInterface<'a, C> + Copy,
      C: HasDataLayout,
  {
      if !arg.layout.is_aggregate() {
-         arg.extend_integer_width_to(32);
+         match param_policy {
+             ParamExtension::ExtendTo32Bits => arg.extend_integer_width_to(32),
+             ParamExtension::NoExtension => {}
+         }
          return;
      }
      if let Some(uniform) = is_homogeneous_aggregate(cx, arg) {
      arg.make_indirect();
  }
  
- pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
+ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, param_policy: ParamExtension)
  where
      Ty: TyAbiInterface<'a, C> + Copy,
      C: HasDataLayout,
  {
      if !fn_abi.ret.is_ignore() {
-         classify_ret(cx, &mut fn_abi.ret);
+         classify_ret(cx, &mut fn_abi.ret, param_policy);
      }
  
 -    for arg in &mut fn_abi.args {
 +    for arg in fn_abi.args.iter_mut() {
          if arg.is_ignore() {
              continue;
          }
-         classify_arg(cx, arg);
+         classify_arg(cx, arg, param_policy);
      }
  }
index d2eb804d004804185f81c27bd64022f7acfa13ca,c87c726cb877d07f1e3b1e7ca9c84841199d4a90..d2fb8c32ffd275de5db709537a1b8f9bb72ade26
@@@ -14,6 -14,7 +14,6 @@@ mod m68k
  mod mips;
  mod mips64;
  mod msp430;
 -mod nvptx;
  mod nvptx64;
  mod powerpc;
  mod powerpc64;
@@@ -26,7 -27,7 +26,7 @@@ mod x86
  mod x86_64;
  mod x86_win64;
  
 -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
 +#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)]
  pub enum PassMode {
      /// Ignore the argument.
      ///
      ///
      /// The argument has a layout abi of `ScalarPair`.
      Pair(ArgAttributes, ArgAttributes),
 -    /// Pass the argument after casting it, to either
 -    /// a single uniform or a pair of registers.
 -    Cast(CastTarget),
 +    /// Pass the argument after casting it, to either a single uniform or a
 +    /// pair of registers. The bool indicates if a `Reg::i32()` dummy argument
 +    /// is emitted before the real argument.
 +    Cast(Box<CastTarget>, bool),
      /// Pass the argument indirectly via a hidden pointer.
      /// The `extra_attrs` value, if any, is for the extra data (vtable or length)
      /// which indicates that it refers to an unsized rvalue.
@@@ -464,6 -464,10 +464,6 @@@ impl<'a, Ty> TyAndLayout<'a, Ty> 
  #[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)]
  pub struct ArgAbi<'a, Ty> {
      pub layout: TyAndLayout<'a, Ty>,
 -
 -    /// Dummy argument, which is emitted before the real argument.
 -    pub pad: Option<Reg>,
 -
      pub mode: PassMode,
  }
  
@@@ -483,7 -487,7 +483,7 @@@ impl<'a, Ty> ArgAbi<'a, Ty> 
              Abi::Vector { .. } => PassMode::Direct(ArgAttributes::new()),
              Abi::Aggregate { .. } => PassMode::Direct(ArgAttributes::new()),
          };
 -        ArgAbi { layout, pad: None, mode }
 +        ArgAbi { layout, mode }
      }
  
      fn indirect_pass_mode(layout: &TyAndLayout<'a, Ty>) -> PassMode {
      }
  
      pub fn cast_to<T: Into<CastTarget>>(&mut self, target: T) {
 -        self.mode = PassMode::Cast(target.into());
 +        self.mode = PassMode::Cast(Box::new(target.into()), false);
      }
  
 -    pub fn pad_with(&mut self, reg: Reg) {
 -        self.pad = Some(reg);
 +    pub fn cast_to_and_pad_i32<T: Into<CastTarget>>(&mut self, target: T, pad_i32: bool) {
 +        self.mode = PassMode::Cast(Box::new(target.into()), pad_i32);
      }
  
      pub fn is_indirect(&self) -> bool {
@@@ -611,7 -615,7 +611,7 @@@ pub enum Conv 
  #[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)]
  pub struct FnAbi<'a, Ty> {
      /// The LLVM types of each argument.
 -    pub args: Vec<ArgAbi<'a, Ty>>,
 +    pub args: Box<[ArgAbi<'a, Ty>]>,
  
      /// LLVM return type.
      pub ret: ArgAbi<'a, Ty>,
      ///
      /// Should only be different from args.len() when c_variadic is true.
      /// This can be used to know whether an argument is variadic or not.
 -    pub fixed_count: usize,
 +    pub fixed_count: u32,
  
      pub conv: Conv,
  
@@@ -665,10 -669,8 +665,10 @@@ impl<'a, Ty> FnAbi<'a, Ty> 
  
          match &cx.target_spec().arch[..] {
              "x86" => {
 -                let flavor = if let spec::abi::Abi::Fastcall { .. } = abi {
 -                    x86::Flavor::Fastcall
 +                let flavor = if let spec::abi::Abi::Fastcall { .. }
 +                | spec::abi::Abi::Vectorcall { .. } = abi
 +                {
 +                    x86::Flavor::FastcallOrVectorcall
                  } else {
                      x86::Flavor::General
                  };
                      }
                  }
              },
-             "aarch64" => aarch64::compute_abi_info(cx, self),
+             "aarch64" => {
+                 let param_policy = if cx.target_spec().is_like_osx {
+                     aarch64::ParamExtension::ExtendTo32Bits
+                 } else {
+                     aarch64::ParamExtension::NoExtension
+                 };
+                 aarch64::compute_abi_info(cx, self, param_policy)
+             }
              "amdgpu" => amdgpu::compute_abi_info(cx, self),
              "arm" => arm::compute_abi_info(cx, self),
              "avr" => avr::compute_abi_info(self),
              "msp430" => msp430::compute_abi_info(self),
              "sparc" => sparc::compute_abi_info(cx, self),
              "sparc64" => sparc64::compute_abi_info(cx, self),
 -            "nvptx" => nvptx::compute_abi_info(self),
              "nvptx64" => {
                  if cx.target_spec().adjust_abi(abi) == spec::abi::Abi::PtxKernel {
                      nvptx64::compute_ptx_kernel_abi_info(cx, self)
          Ok(())
      }
  }
 +
 +// Some types are used a lot. Make sure they don't unintentionally get bigger.
 +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 +mod size_asserts {
 +    use super::*;
 +    use rustc_data_structures::static_assert_size;
 +    // These are in alphabetical order, which is easy to maintain.
 +    static_assert_size!(ArgAbi<'_, usize>, 56);
 +    static_assert_size!(FnAbi<'_, usize>, 80);
 +}
index 7e7f7a82e90d31311b8d78835ede9273b4a0b39a,e78fefdcfad2af5b9a0c87a9058d2bd5709324eb..dc16739bd560c7d78b9352969f25552a8d3ad8ed
@@@ -37,7 -37,7 +37,7 @@@
  use crate::abi::Endian;
  use crate::json::{Json, ToJson};
  use crate::spec::abi::{lookup as lookup_abi, Abi};
 -use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback};
 +use crate::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault};
  use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
  use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
  use rustc_span::symbol::{sym, Symbol};
@@@ -92,24 -92,14 +92,24 @@@ mod windows_uwp_msvc_base
  
  #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
  pub enum LinkerFlavor {
 -    Em,
      Gcc,
 -    L4Bender,
      Ld,
 +    Lld(LldFlavor),
      Msvc,
 +    EmCc,
 +    Bpf,
 +    Ptx,
 +}
 +
 +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
 +pub enum LinkerFlavorCli {
 +    Gcc,
 +    Ld,
      Lld(LldFlavor),
 -    PtxLinker,
 +    Msvc,
 +    Em,
      BpfLinker,
 +    PtxLinker,
  }
  
  #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
@@@ -147,40 -137,19 +147,40 @@@ impl ToJson for LldFlavor 
      }
  }
  
 -impl ToJson for LinkerFlavor {
 -    fn to_json(&self) -> Json {
 -        self.desc().to_json()
 +impl LinkerFlavor {
 +    pub fn from_cli(cli: LinkerFlavorCli) -> LinkerFlavor {
 +        match cli {
 +            LinkerFlavorCli::Gcc => LinkerFlavor::Gcc,
 +            LinkerFlavorCli::Ld => LinkerFlavor::Ld,
 +            LinkerFlavorCli::Lld(lld_flavor) => LinkerFlavor::Lld(lld_flavor),
 +            LinkerFlavorCli::Msvc => LinkerFlavor::Msvc,
 +            LinkerFlavorCli::Em => LinkerFlavor::EmCc,
 +            LinkerFlavorCli::BpfLinker => LinkerFlavor::Bpf,
 +            LinkerFlavorCli::PtxLinker => LinkerFlavor::Ptx,
 +        }
 +    }
 +
 +    fn to_cli(self) -> LinkerFlavorCli {
 +        match self {
 +            LinkerFlavor::Gcc => LinkerFlavorCli::Gcc,
 +            LinkerFlavor::Ld => LinkerFlavorCli::Ld,
 +            LinkerFlavor::Lld(lld_flavor) => LinkerFlavorCli::Lld(lld_flavor),
 +            LinkerFlavor::Msvc => LinkerFlavorCli::Msvc,
 +            LinkerFlavor::EmCc => LinkerFlavorCli::Em,
 +            LinkerFlavor::Bpf => LinkerFlavorCli::BpfLinker,
 +            LinkerFlavor::Ptx => LinkerFlavorCli::PtxLinker,
 +        }
      }
  }
 -macro_rules! flavor_mappings {
 -    ($((($($flavor:tt)*), $string:expr),)*) => (
 -        impl LinkerFlavor {
 +
 +macro_rules! linker_flavor_cli_impls {
 +    ($(($($flavor:tt)*) $string:literal)*) => (
 +        impl LinkerFlavorCli {
              pub const fn one_of() -> &'static str {
                  concat!("one of: ", $($string, " ",)*)
              }
  
 -            pub fn from_str(s: &str) -> Option<Self> {
 +            pub fn from_str(s: &str) -> Option<LinkerFlavorCli> {
                  Some(match s {
                      $($string => $($flavor)*,)*
                      _ => return None,
      )
  }
  
 -flavor_mappings! {
 -    ((LinkerFlavor::Em), "em"),
 -    ((LinkerFlavor::Gcc), "gcc"),
 -    ((LinkerFlavor::L4Bender), "l4-bender"),
 -    ((LinkerFlavor::Ld), "ld"),
 -    ((LinkerFlavor::Msvc), "msvc"),
 -    ((LinkerFlavor::PtxLinker), "ptx-linker"),
 -    ((LinkerFlavor::BpfLinker), "bpf-linker"),
 -    ((LinkerFlavor::Lld(LldFlavor::Wasm)), "wasm-ld"),
 -    ((LinkerFlavor::Lld(LldFlavor::Ld64)), "ld64.lld"),
 -    ((LinkerFlavor::Lld(LldFlavor::Ld)), "ld.lld"),
 -    ((LinkerFlavor::Lld(LldFlavor::Link)), "lld-link"),
 +linker_flavor_cli_impls! {
 +    (LinkerFlavorCli::Gcc) "gcc"
 +    (LinkerFlavorCli::Ld) "ld"
 +    (LinkerFlavorCli::Lld(LldFlavor::Ld)) "ld.lld"
 +    (LinkerFlavorCli::Lld(LldFlavor::Ld64)) "ld64.lld"
 +    (LinkerFlavorCli::Lld(LldFlavor::Link)) "lld-link"
 +    (LinkerFlavorCli::Lld(LldFlavor::Wasm)) "wasm-ld"
 +    (LinkerFlavorCli::Msvc) "msvc"
 +    (LinkerFlavorCli::Em) "em"
 +    (LinkerFlavorCli::BpfLinker) "bpf-linker"
 +    (LinkerFlavorCli::PtxLinker) "ptx-linker"
 +}
 +
 +impl ToJson for LinkerFlavorCli {
 +    fn to_json(&self) -> Json {
 +        self.desc().to_json()
 +    }
  }
  
  #[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)]
@@@ -503,59 -467,8 +503,59 @@@ impl fmt::Display for LinkOutputKind 
  }
  
  pub type LinkArgs = BTreeMap<LinkerFlavor, Vec<StaticCow<str>>>;
 +pub type LinkArgsCli = BTreeMap<LinkerFlavorCli, Vec<StaticCow<str>>>;
 +
 +/// Which kind of debuginfo does the target use?
 +///
 +/// Useful in determining whether a target supports Split DWARF (a target with
 +/// `DebuginfoKind::Dwarf` and supporting `SplitDebuginfo::Unpacked` for example).
 +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
 +pub enum DebuginfoKind {
 +    /// DWARF debuginfo (such as that used on `x86_64_unknown_linux_gnu`).
 +    #[default]
 +    Dwarf,
 +    /// DWARF debuginfo in dSYM files (such as on Apple platforms).
 +    DwarfDsym,
 +    /// Program database files (such as on Windows).
 +    Pdb,
 +}
 +
 +impl DebuginfoKind {
 +    fn as_str(&self) -> &'static str {
 +        match self {
 +            DebuginfoKind::Dwarf => "dwarf",
 +            DebuginfoKind::DwarfDsym => "dwarf-dsym",
 +            DebuginfoKind::Pdb => "pdb",
 +        }
 +    }
 +}
 +
 +impl FromStr for DebuginfoKind {
 +    type Err = ();
 +
 +    fn from_str(s: &str) -> Result<Self, ()> {
 +        Ok(match s {
 +            "dwarf" => DebuginfoKind::Dwarf,
 +            "dwarf-dsym" => DebuginfoKind::DwarfDsym,
 +            "pdb" => DebuginfoKind::Pdb,
 +            _ => return Err(()),
 +        })
 +    }
 +}
 +
 +impl ToJson for DebuginfoKind {
 +    fn to_json(&self) -> Json {
 +        self.as_str().to_json()
 +    }
 +}
  
 -#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq)]
 +impl fmt::Display for DebuginfoKind {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        f.write_str(self.as_str())
 +    }
 +}
 +
 +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
  pub enum SplitDebuginfo {
      /// Split debug-information is disabled, meaning that on supported platforms
      /// you can find all debug information in the executable itself. This is
      ///
      /// * Windows - not supported
      /// * macOS - don't run `dsymutil`
 -    /// * ELF - `.dwarf_*` sections
 +    /// * ELF - `.debug_*` sections
 +    #[default]
      Off,
  
      /// Split debug-information can be found in a "packed" location separate
      ///
      /// * Windows - `*.pdb`
      /// * macOS - `*.dSYM` (run `dsymutil`)
 -    /// * ELF - `*.dwp` (run `rust-llvm-dwp`)
 +    /// * ELF - `*.dwp` (run `thorin`)
      Packed,
  
      /// Split debug-information can be found in individual object files on the
@@@ -597,7 -509,7 +597,7 @@@ impl SplitDebuginfo 
  impl FromStr for SplitDebuginfo {
      type Err = ();
  
 -    fn from_str(s: &str) -> Result<SplitDebuginfo, ()> {
 +    fn from_str(s: &str) -> Result<Self, ()> {
          Ok(match s {
              "off" => SplitDebuginfo::Off,
              "unpacked" => SplitDebuginfo::Unpacked,
@@@ -706,7 -618,6 +706,7 @@@ bitflags::bitflags! 
          const HWADDRESS = 1 << 4;
          const CFI     = 1 << 5;
          const MEMTAG  = 1 << 6;
 +        const SHADOWCALLSTACK = 1 << 7;
      }
  }
  
@@@ -721,7 -632,6 +721,7 @@@ impl SanitizerSet 
              SanitizerSet::LEAK => "leak",
              SanitizerSet::MEMORY => "memory",
              SanitizerSet::MEMTAG => "memtag",
 +            SanitizerSet::SHADOWCALLSTACK => "shadow-call-stack",
              SanitizerSet::THREAD => "thread",
              SanitizerSet::HWADDRESS => "hwaddress",
              _ => return None,
@@@ -756,7 -666,6 +756,7 @@@ impl IntoIterator for SanitizerSet 
              SanitizerSet::LEAK,
              SanitizerSet::MEMORY,
              SanitizerSet::MEMTAG,
 +            SanitizerSet::SHADOWCALLSTACK,
              SanitizerSet::THREAD,
              SanitizerSet::HWADDRESS,
          ]
@@@ -874,15 -783,15 +874,15 @@@ impl fmt::Display for StackProtector 
  }
  
  macro_rules! supported_targets {
 -    ( $(($( $triple:literal, )+ $module:ident ),)+ ) => {
 +    ( $(($triple:literal, $module:ident ),)+ ) => {
          $(mod $module;)+
  
          /// List of supported targets
 -        pub const TARGETS: &[&str] = &[$($($triple),+),+];
 +        pub const TARGETS: &[&str] = &[$($triple),+];
  
          fn load_builtin(target: &str) -> Option<Target> {
              let mut t = match target {
 -                $( $($triple)|+ => $module::target(), )+
 +                $( $triple => $module::target(), )+
                  _ => return None,
              };
              t.is_builtin = true;
              $(
                  #[test] // `#[test]`
                  fn $module() {
 -                    tests_impl::test_target(super::$module::target());
 +                    tests_impl::test_target(super::$module::target(), $triple);
                  }
              )+
          }
@@@ -932,7 -841,6 +932,7 @@@ supported_targets! 
      ("sparc64-unknown-linux-gnu", sparc64_unknown_linux_gnu),
      ("arm-unknown-linux-gnueabi", arm_unknown_linux_gnueabi),
      ("arm-unknown-linux-gnueabihf", arm_unknown_linux_gnueabihf),
 +    ("armeb-unknown-linux-gnueabi", armeb_unknown_linux_gnueabi),
      ("arm-unknown-linux-musleabi", arm_unknown_linux_musleabi),
      ("arm-unknown-linux-musleabihf", arm_unknown_linux_musleabihf),
      ("armv4t-unknown-linux-gnueabi", armv4t_unknown_linux_gnueabi),
  
      ("aarch64-unknown-openbsd", aarch64_unknown_openbsd),
      ("i686-unknown-openbsd", i686_unknown_openbsd),
 +    ("powerpc-unknown-openbsd", powerpc_unknown_openbsd),
 +    ("powerpc64-unknown-openbsd", powerpc64_unknown_openbsd),
 +    ("riscv64gc-unknown-openbsd", riscv64gc_unknown_openbsd),
      ("sparc64-unknown-openbsd", sparc64_unknown_openbsd),
      ("x86_64-unknown-openbsd", x86_64_unknown_openbsd),
 -    ("powerpc-unknown-openbsd", powerpc_unknown_openbsd),
  
      ("aarch64-unknown-netbsd", aarch64_unknown_netbsd),
      ("armv6-unknown-netbsd-eabihf", armv6_unknown_netbsd_eabihf),
      ("mipsel-sony-psp", mipsel_sony_psp),
      ("mipsel-unknown-none", mipsel_unknown_none),
      ("thumbv4t-none-eabi", thumbv4t_none_eabi),
 +    ("armv4t-none-eabi", armv4t_none_eabi),
  
      ("aarch64_be-unknown-linux-gnu", aarch64_be_unknown_linux_gnu),
      ("aarch64-unknown-linux-gnu_ilp32", aarch64_unknown_linux_gnu_ilp32),
  
      ("armv6k-nintendo-3ds", armv6k_nintendo_3ds),
  
 +    ("aarch64-nintendo-switch-freestanding", aarch64_nintendo_switch_freestanding),
 +
      ("armv7-unknown-linux-uclibceabi", armv7_unknown_linux_uclibceabi),
      ("armv7-unknown-linux-uclibceabihf", armv7_unknown_linux_uclibceabihf),
  
@@@ -1248,54 -1151,48 +1248,54 @@@ pub struct TargetOptions 
      pub abi: StaticCow<str>,
      /// Vendor name to use for conditional compilation (`target_vendor`). Defaults to "unknown".
      pub vendor: StaticCow<str>,
 -    /// Default linker flavor used if `-C linker-flavor` or `-C linker` are not passed
 -    /// on the command line. Defaults to `LinkerFlavor::Gcc`.
 -    pub linker_flavor: LinkerFlavor,
  
      /// Linker to invoke
      pub linker: Option<StaticCow<str>>,
 -
 +    /// Default linker flavor used if `-C linker-flavor` or `-C linker` are not passed
 +    /// on the command line. Defaults to `LinkerFlavor::Gcc`.
 +    pub linker_flavor: LinkerFlavor,
 +    linker_flavor_json: LinkerFlavorCli,
      /// LLD flavor used if `lld` (or `rust-lld`) is specified as a linker
      /// without clarifying its flavor in any way.
 +    /// FIXME: Merge this into `LinkerFlavor`.
      pub lld_flavor: LldFlavor,
 +    /// Whether the linker support GNU-like arguments such as -O. Defaults to true.
 +    /// FIXME: Merge this into `LinkerFlavor`.
 +    pub linker_is_gnu: bool,
  
 -    /// Linker arguments that are passed *before* any user-defined libraries.
 -    pub pre_link_args: LinkArgs,
      /// Objects to link before and after all other object code.
      pub pre_link_objects: CrtObjects,
      pub post_link_objects: CrtObjects,
 -    /// Same as `(pre|post)_link_objects`, but when we fail to pull the objects with help of the
 -    /// target's native gcc and fall back to the "self-contained" mode and pull them manually.
 -    /// See `crt_objects.rs` for some more detailed documentation.
 -    pub pre_link_objects_fallback: CrtObjects,
 -    pub post_link_objects_fallback: CrtObjects,
 -    /// Which logic to use to determine whether to fall back to the "self-contained" mode or not.
 -    pub crt_objects_fallback: Option<CrtObjectsFallback>,
 +    /// Same as `(pre|post)_link_objects`, but when self-contained linking mode is enabled.
 +    pub pre_link_objects_self_contained: CrtObjects,
 +    pub post_link_objects_self_contained: CrtObjects,
 +    pub link_self_contained: LinkSelfContainedDefault,
  
 +    /// Linker arguments that are passed *before* any user-defined libraries.
 +    pub pre_link_args: LinkArgs,
 +    pre_link_args_json: LinkArgsCli,
      /// Linker arguments that are unconditionally passed after any
      /// user-defined but before post-link objects. Standard platform
      /// libraries that should be always be linked to, usually go here.
      pub late_link_args: LinkArgs,
 +    late_link_args_json: LinkArgsCli,
      /// Linker arguments used in addition to `late_link_args` if at least one
      /// Rust dependency is dynamically linked.
      pub late_link_args_dynamic: LinkArgs,
 +    late_link_args_dynamic_json: LinkArgsCli,
      /// Linker arguments used in addition to `late_link_args` if all Rust
      /// dependencies are statically linked.
      pub late_link_args_static: LinkArgs,
 +    late_link_args_static_json: LinkArgsCli,
      /// Linker arguments that are unconditionally passed *after* any
      /// user-defined libraries.
      pub post_link_args: LinkArgs,
 +    post_link_args_json: LinkArgsCli,
 +
      /// Optional link script applied to `dylib` and `executable` crate types.
      /// This is a string containing the script, not a path. Can only be applied
      /// to linkers where `linker_is_gnu` is true.
      pub link_script: Option<StaticCow<str>>,
 -
      /// Environment variables to be set for the linker invocation.
      pub link_env: StaticCow<[(StaticCow<str>, StaticCow<str>)]>,
      /// Environment variables to be removed for the linker invocation.
      pub dynamic_linking: bool,
      /// If dynamic linking is available, whether only cdylibs are supported.
      pub only_cdylib: bool,
 -    /// Whether executables are available on this target. iOS, for example, only allows static
 -    /// libraries. Defaults to false.
 +    /// Whether executables are available on this target. Defaults to true.
      pub executables: bool,
      /// Relocation model to use in object file. Corresponds to `llc
      /// -relocation-model=$relocation_model`. Defaults to `Pic`.
      pub abi_return_struct_as_int: bool,
      /// Whether the target toolchain is like macOS's. Only useful for compiling against iOS/macOS,
      /// in particular running dsymutil and some other stuff like `-dead_strip`. Defaults to false.
+     /// Also indiates whether to use Apple-specific ABI changes, such as extending function
+     /// parameters to 32-bits.
      pub is_like_osx: bool,
      /// Whether the target toolchain is like Solaris's.
      /// Only useful for compiling against Illumos/Solaris,
      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>,
 -    /// Whether the linker support GNU-like arguments such as -O. Defaults to true.
 -    pub linker_is_gnu: bool,
 +    pub default_dwarf_version: u32,
      /// The MinGW toolchain has a known issue that prevents it from correctly
      /// handling COFF object files with more than 2<sup>15</sup> sections. Since each weak
      /// symbol needs its own COMDAT section, weak linkage implies a large
      /// thumb and arm interworking.
      pub has_thumb_interworking: bool,
  
 +    /// Which kind of debuginfo is used by this target?
 +    pub debuginfo_kind: DebuginfoKind,
      /// How to handle split debug information, if at all. Specifying `None` has
      /// target-specific meaning.
      pub split_debuginfo: SplitDebuginfo,
 +    /// Which kinds of split debuginfo are supported by the target?
 +    pub supported_split_debuginfo: StaticCow<[SplitDebuginfo]>,
  
      /// The sanitizers supported by this target
      ///
@@@ -1573,11 -1471,15 +1575,11 @@@ fn add_link_args(link_args: &mut LinkAr
      match flavor {
          LinkerFlavor::Ld => insert(LinkerFlavor::Lld(LldFlavor::Ld)),
          LinkerFlavor::Msvc => insert(LinkerFlavor::Lld(LldFlavor::Link)),
 -        LinkerFlavor::Lld(LldFlavor::Wasm) => {}
 +        LinkerFlavor::Lld(LldFlavor::Ld64) | LinkerFlavor::Lld(LldFlavor::Wasm) => {}
          LinkerFlavor::Lld(lld_flavor) => {
              panic!("add_link_args: use non-LLD flavor for {:?}", lld_flavor)
          }
 -        LinkerFlavor::Gcc
 -        | LinkerFlavor::Em
 -        | LinkerFlavor::L4Bender
 -        | LinkerFlavor::BpfLinker
 -        | LinkerFlavor::PtxLinker => {}
 +        LinkerFlavor::Gcc | LinkerFlavor::EmCc | LinkerFlavor::Bpf | LinkerFlavor::Ptx => {}
      }
  }
  
@@@ -1595,36 -1497,6 +1597,36 @@@ impl TargetOptions 
      fn add_post_link_args(&mut self, flavor: LinkerFlavor, args: &[&'static str]) {
          add_link_args(&mut self.post_link_args, flavor, args);
      }
 +
 +    fn update_from_cli(&mut self) {
 +        self.linker_flavor = LinkerFlavor::from_cli(self.linker_flavor_json);
 +        for (args, args_json) in [
 +            (&mut self.pre_link_args, &self.pre_link_args_json),
 +            (&mut self.late_link_args, &self.late_link_args_json),
 +            (&mut self.late_link_args_dynamic, &self.late_link_args_dynamic_json),
 +            (&mut self.late_link_args_static, &self.late_link_args_static_json),
 +            (&mut self.post_link_args, &self.post_link_args_json),
 +        ] {
 +            *args = args_json
 +                .iter()
 +                .map(|(flavor, args)| (LinkerFlavor::from_cli(*flavor), args.clone()))
 +                .collect();
 +        }
 +    }
 +
 +    fn update_to_cli(&mut self) {
 +        self.linker_flavor_json = self.linker_flavor.to_cli();
 +        for (args, args_json) in [
 +            (&self.pre_link_args, &mut self.pre_link_args_json),
 +            (&self.late_link_args, &mut self.late_link_args_json),
 +            (&self.late_link_args_dynamic, &mut self.late_link_args_dynamic_json),
 +            (&self.late_link_args_static, &mut self.late_link_args_static_json),
 +            (&self.post_link_args, &mut self.post_link_args_json),
 +        ] {
 +            *args_json =
 +                args.iter().map(|(flavor, args)| (flavor.to_cli(), args.clone())).collect();
 +        }
 +    }
  }
  
  impl Default for TargetOptions {
              env: "".into(),
              abi: "".into(),
              vendor: "unknown".into(),
 -            linker_flavor: LinkerFlavor::Gcc,
              linker: option_env!("CFG_DEFAULT_LINKER").map(|s| s.into()),
 +            linker_flavor: LinkerFlavor::Gcc,
 +            linker_flavor_json: LinkerFlavorCli::Gcc,
              lld_flavor: LldFlavor::Ld,
 -            pre_link_args: LinkArgs::new(),
 -            post_link_args: LinkArgs::new(),
 +            linker_is_gnu: true,
              link_script: None,
              asm_args: cvs![],
              cpu: "generic".into(),
              features: "".into(),
              dynamic_linking: false,
              only_cdylib: false,
 -            executables: false,
 +            executables: true,
              relocation_model: RelocModel::Pic,
              code_model: None,
              tls_model: TlsModel::GeneralDynamic,
              is_like_windows: false,
              is_like_msvc: false,
              is_like_wasm: false,
 -            dwarf_version: None,
 -            linker_is_gnu: true,
 +            default_dwarf_version: 4,
              allows_weak_linkage: true,
              has_rpath: false,
              no_default_libraries: true,
              relro_level: RelroLevel::None,
              pre_link_objects: Default::default(),
              post_link_objects: Default::default(),
 -            pre_link_objects_fallback: Default::default(),
 -            post_link_objects_fallback: Default::default(),
 -            crt_objects_fallback: None,
 +            pre_link_objects_self_contained: Default::default(),
 +            post_link_objects_self_contained: Default::default(),
 +            link_self_contained: LinkSelfContainedDefault::False,
 +            pre_link_args: LinkArgs::new(),
 +            pre_link_args_json: LinkArgsCli::new(),
              late_link_args: LinkArgs::new(),
 +            late_link_args_json: LinkArgsCli::new(),
              late_link_args_dynamic: LinkArgs::new(),
 +            late_link_args_dynamic_json: LinkArgsCli::new(),
              late_link_args_static: LinkArgs::new(),
 +            late_link_args_static_json: LinkArgsCli::new(),
 +            post_link_args: LinkArgs::new(),
 +            post_link_args_json: LinkArgsCli::new(),
              link_env: cvs![],
              link_env_remove: cvs![],
              archive_format: "gnu".into(),
              use_ctors_section: false,
              eh_frame_header: true,
              has_thumb_interworking: false,
 -            split_debuginfo: SplitDebuginfo::Off,
 +            debuginfo_kind: Default::default(),
 +            split_debuginfo: Default::default(),
 +            // `Off` is supported by default, but targets can remove this manually, e.g. Windows.
 +            supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
              supported_sanitizers: SanitizerSet::empty(),
              default_adjusted_cabi: None,
              c_enum_min_bits: 32,
@@@ -1917,13 -1780,13 +1919,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>) => ( {
                      Some(Ok(()))
                  })).unwrap_or(Ok(()))
              } );
 +            ($key_name:ident, DebuginfoKind) => ( {
 +                let name = (stringify!($key_name)).replace("_", "-");
 +                obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
 +                    match s.parse::<DebuginfoKind>() {
 +                        Ok(level) => base.$key_name = level,
 +                        _ => return Some(Err(
 +                            format!("'{s}' is not a valid value for debuginfo-kind. Use 'dwarf', \
 +                                  'dwarf-dsym' or 'pdb'.")
 +                        )),
 +                    }
 +                    Some(Ok(()))
 +                })).unwrap_or(Ok(()))
 +            } );
              ($key_name:ident, SplitDebuginfo) => ( {
                  let name = (stringify!($key_name)).replace("_", "-");
                  obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
                      }
                  }
              } );
 +            ($key_name:ident, falliable_list) => ( {
 +                let name = (stringify!($key_name)).replace("_", "-");
 +                obj.remove(&name).and_then(|j| {
 +                    if let Some(v) = j.as_array() {
 +                        match v.iter().map(|a| FromStr::from_str(a.as_str().unwrap())).collect() {
 +                            Ok(l) => { base.$key_name = l },
 +                            // FIXME: `falliable_list` can't re-use the `key!` macro for list
 +                            // elements and the error messages from that macro, so it has a bad
 +                            // generic message instead
 +                            Err(_) => return Some(Err(
 +                                format!("`{:?}` is not a valid value for `{}`", j, name)
 +                            )),
 +                        }
 +                    } else {
 +                        incorrect_type.push(name)
 +                    }
 +                    Some(Ok(()))
 +                }).unwrap_or(Ok(()))
 +            } );
              ($key_name:ident, optional) => ( {
                  let name = (stringify!($key_name)).replace("_", "-");
                  if let Some(o) = obj.remove(&name) {
                      Some(Ok(()))
                  })).unwrap_or(Ok(()))
              } );
 -            ($key_name:ident, LinkerFlavor) => ( {
 -                let name = (stringify!($key_name)).replace("_", "-");
 -                obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
 -                    match LinkerFlavor::from_str(s) {
 +            ($key_name:ident = $json_name:expr, LinkerFlavor) => ( {
 +                let name = $json_name;
 +                obj.remove(name).and_then(|o| o.as_str().and_then(|s| {
 +                    match LinkerFlavorCli::from_str(s) {
                          Some(linker_flavor) => base.$key_name = linker_flavor,
                          _ => return Some(Err(format!("'{}' is not a valid value for linker-flavor. \
 -                                                      Use {}", s, LinkerFlavor::one_of()))),
 +                                                      Use {}", s, LinkerFlavorCli::one_of()))),
                      }
                      Some(Ok(()))
                  })).unwrap_or(Ok(()))
                                  Some("leak") => SanitizerSet::LEAK,
                                  Some("memory") => SanitizerSet::MEMORY,
                                  Some("memtag") => SanitizerSet::MEMTAG,
 +                                Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK,
                                  Some("thread") => SanitizerSet::THREAD,
                                  Some("hwaddress") => SanitizerSet::HWADDRESS,
                                  Some(s) => return Err(format!("unknown sanitizer {}", s)),
                  Ok::<(), String>(())
              } );
  
 -            ($key_name:ident, crt_objects_fallback) => ( {
 -                let name = (stringify!($key_name)).replace("_", "-");
 -                obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
 -                    match s.parse::<CrtObjectsFallback>() {
 -                        Ok(fallback) => base.$key_name = Some(fallback),
 -                        _ => return Some(Err(format!("'{}' is not a valid CRT objects fallback. \
 -                                                      Use 'musl', 'mingw' or 'wasm'", s))),
 +            ($key_name:ident = $json_name:expr, link_self_contained) => ( {
 +                let name = $json_name;
 +                obj.remove(name).and_then(|o| o.as_str().and_then(|s| {
 +                    match s.parse::<LinkSelfContainedDefault>() {
 +                        Ok(lsc_default) => base.$key_name = lsc_default,
 +                        _ => return Some(Err(format!("'{}' is not a valid `-Clink-self-contained` default. \
 +                                                      Use 'false', 'true', 'musl' or 'mingw'", s))),
                      }
                      Some(Ok(()))
                  })).unwrap_or(Ok(()))
              } );
 -            ($key_name:ident, link_objects) => ( {
 -                let name = (stringify!($key_name)).replace("_", "-");
 -                if let Some(val) = obj.remove(&name) {
 +            ($key_name:ident = $json_name:expr, link_objects) => ( {
 +                let name = $json_name;
 +                if let Some(val) = obj.remove(name) {
                      let obj = val.as_object().ok_or_else(|| format!("{}: expected a \
                          JSON object with fields per CRT object kind.", name))?;
                      let mut args = CrtObjects::new();
                      base.$key_name = args;
                  }
              } );
 -            ($key_name:ident, link_args) => ( {
 -                let name = (stringify!($key_name)).replace("_", "-");
 -                if let Some(val) = obj.remove(&name) {
 +            ($key_name:ident = $json_name:expr, link_args) => ( {
 +                let name = $json_name;
 +                if let Some(val) = obj.remove(name) {
                      let obj = val.as_object().ok_or_else(|| format!("{}: expected a \
                          JSON object with fields per linker-flavor.", name))?;
 -                    let mut args = LinkArgs::new();
 +                    let mut args = LinkArgsCli::new();
                      for (k, v) in obj {
 -                        let flavor = LinkerFlavor::from_str(&k).ok_or_else(|| {
 +                        let flavor = LinkerFlavorCli::from_str(&k).ok_or_else(|| {
                              format!("{}: '{}' is not a valid value for linker-flavor. \
                                       Use 'em', 'gcc', 'ld' or 'msvc'", name, k)
                          })?;
          key!(env);
          key!(abi);
          key!(vendor);
 -        key!(linker_flavor, LinkerFlavor)?;
          key!(linker, optional);
 +        key!(linker_flavor_json = "linker-flavor", LinkerFlavor)?;
          key!(lld_flavor, LldFlavor)?;
 -        key!(pre_link_objects, link_objects);
 -        key!(post_link_objects, link_objects);
 -        key!(pre_link_objects_fallback, link_objects);
 -        key!(post_link_objects_fallback, link_objects);
 -        key!(crt_objects_fallback, crt_objects_fallback)?;
 -        key!(pre_link_args, link_args);
 -        key!(late_link_args, link_args);
 -        key!(late_link_args_dynamic, link_args);
 -        key!(late_link_args_static, link_args);
 -        key!(post_link_args, link_args);
 +        key!(linker_is_gnu, bool);
 +        key!(pre_link_objects = "pre-link-objects", link_objects);
 +        key!(post_link_objects = "post-link-objects", link_objects);
 +        key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects);
 +        key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects);
 +        key!(link_self_contained = "crt-objects-fallback", link_self_contained)?;
 +        key!(pre_link_args_json = "pre-link-args", link_args);
 +        key!(late_link_args_json = "late-link-args", link_args);
 +        key!(late_link_args_dynamic_json = "late-link-args-dynamic", link_args);
 +        key!(late_link_args_static_json = "late-link-args-static", link_args);
 +        key!(post_link_args_json = "post-link-args", link_args);
          key!(link_script, optional);
          key!(link_env, env);
          key!(link_env_remove, list);
          key!(is_like_windows, bool);
          key!(is_like_msvc, bool);
          key!(is_like_wasm, bool);
 -        key!(dwarf_version, Option<u32>);
 -        key!(linker_is_gnu, bool);
 +        key!(default_dwarf_version, u32);
          key!(allows_weak_linkage, bool);
          key!(has_rpath, bool);
          key!(no_default_libraries, bool);
          key!(use_ctors_section, bool);
          key!(eh_frame_header, bool);
          key!(has_thumb_interworking, bool);
 +        key!(debuginfo_kind, DebuginfoKind)?;
          key!(split_debuginfo, SplitDebuginfo)?;
 +        key!(supported_split_debuginfo, falliable_list)?;
          key!(supported_sanitizers, SanitizerSet)?;
          key!(default_adjusted_cabi, Option<Abi>)?;
          key!(c_enum_min_bits, u64);
              // This can cause unfortunate ICEs later down the line.
              return Err("may not set is_builtin for targets not built-in".into());
          }
 +        base.update_from_cli();
 +
          // Each field should have been read using `Json::remove` so any keys remaining are unused.
          let remaining_keys = obj.keys();
          Ok((
                  load_builtin(target_triple).expect("built-in target")
              }
              TargetTriple::TargetJson { .. } => {
 -                panic!("built-in targets doens't support target-paths")
 +                panic!("built-in targets doesn't support target-paths")
              }
          }
      }
@@@ -2466,44 -2292,42 +2468,44 @@@ impl ToJson for Target 
      fn to_json(&self) -> Json {
          let mut d = serde_json::Map::new();
          let default: TargetOptions = Default::default();
 +        let mut target = self.clone();
 +        target.update_to_cli();
  
          macro_rules! target_val {
              ($attr:ident) => {{
                  let name = (stringify!($attr)).replace("_", "-");
 -                d.insert(name, self.$attr.to_json());
 +                d.insert(name, target.$attr.to_json());
              }};
          }
  
          macro_rules! target_option_val {
              ($attr:ident) => {{
                  let name = (stringify!($attr)).replace("_", "-");
 -                if default.$attr != self.$attr {
 -                    d.insert(name, self.$attr.to_json());
 +                if default.$attr != target.$attr {
 +                    d.insert(name, target.$attr.to_json());
                  }
              }};
 -            ($attr:ident, $key_name:expr) => {{
 -                let name = $key_name;
 -                if default.$attr != self.$attr {
 -                    d.insert(name.into(), self.$attr.to_json());
 +            ($attr:ident, $json_name:expr) => {{
 +                let name = $json_name;
 +                if default.$attr != target.$attr {
 +                    d.insert(name.into(), target.$attr.to_json());
                  }
              }};
 -            (link_args - $attr:ident) => {{
 -                let name = (stringify!($attr)).replace("_", "-");
 -                if default.$attr != self.$attr {
 -                    let obj = self
 +            (link_args - $attr:ident, $json_name:expr) => {{
 +                let name = $json_name;
 +                if default.$attr != target.$attr {
 +                    let obj = target
                          .$attr
                          .iter()
                          .map(|(k, v)| (k.desc().to_string(), v.clone()))
                          .collect::<BTreeMap<_, _>>();
 -                    d.insert(name, obj.to_json());
 +                    d.insert(name.to_string(), obj.to_json());
                  }
              }};
              (env - $attr:ident) => {{
                  let name = (stringify!($attr)).replace("_", "-");
 -                if default.$attr != self.$attr {
 -                    let obj = self
 +                if default.$attr != target.$attr {
 +                    let obj = target
                          .$attr
                          .iter()
                          .map(|&(ref k, ref v)| format!("{k}={v}"))
          target_option_val!(env);
          target_option_val!(abi);
          target_option_val!(vendor);
 -        target_option_val!(linker_flavor);
          target_option_val!(linker);
 +        target_option_val!(linker_flavor_json, "linker-flavor");
          target_option_val!(lld_flavor);
 +        target_option_val!(linker_is_gnu);
          target_option_val!(pre_link_objects);
          target_option_val!(post_link_objects);
 -        target_option_val!(pre_link_objects_fallback);
 -        target_option_val!(post_link_objects_fallback);
 -        target_option_val!(crt_objects_fallback);
 -        target_option_val!(link_args - pre_link_args);
 -        target_option_val!(link_args - late_link_args);
 -        target_option_val!(link_args - late_link_args_dynamic);
 -        target_option_val!(link_args - late_link_args_static);
 -        target_option_val!(link_args - post_link_args);
 +        target_option_val!(pre_link_objects_self_contained, "pre-link-objects-fallback");
 +        target_option_val!(post_link_objects_self_contained, "post-link-objects-fallback");
 +        target_option_val!(link_self_contained, "crt-objects-fallback");
 +        target_option_val!(link_args - pre_link_args_json, "pre-link-args");
 +        target_option_val!(link_args - late_link_args_json, "late-link-args");
 +        target_option_val!(link_args - late_link_args_dynamic_json, "late-link-args-dynamic");
 +        target_option_val!(link_args - late_link_args_static_json, "late-link-args-static");
 +        target_option_val!(link_args - post_link_args_json, "post-link-args");
          target_option_val!(link_script);
          target_option_val!(env - link_env);
          target_option_val!(link_env_remove);
          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!(linker_is_gnu);
 +        target_option_val!(default_dwarf_version);
          target_option_val!(allows_weak_linkage);
          target_option_val!(has_rpath);
          target_option_val!(no_default_libraries);
          target_option_val!(use_ctors_section);
          target_option_val!(eh_frame_header);
          target_option_val!(has_thumb_interworking);
 +        target_option_val!(debuginfo_kind);
          target_option_val!(split_debuginfo);
 +        target_option_val!(supported_split_debuginfo);
          target_option_val!(supported_sanitizers);
          target_option_val!(c_enum_min_bits);
          target_option_val!(generate_arange_section);
index bcfe2f9af50bdeab63b71f70a655935165490c65,9b378ecb4e590651cc7b8b8a41a11c957160fcd1..602a08067bae183cd1e489d24e3801d052421017
@@@ -10,7 -10,10 +10,10 @@@ pub fn call_foreign_fn() -> u8 
      }
  }
  
- // CHECK: declare zeroext i8 @foreign_fn()
+ // (Allow but do not require `zeroext` here, because it is not worth effort to
+ // spell out which targets have it and which ones do not; see rust#97800.)
+ // CHECK: declare{{( zeroext)?}} i8 @foreign_fn()
  extern "C" {fn foreign_fn() -> u8;}
  
 -// CHECK: !{i32 7, !"PIC Level", i32 2}
 +// CHECK: !{i32 {{[78]}}, !"PIC Level", i32 2}