about summary refs log tree commit diff
path: root/compiler/rustc_session
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-02-03 07:16:57 +0000
committerbors <bors@rust-lang.org>2025-02-03 07:16:57 +0000
commit7daf4cf911c30e48c78f5e155c029397fdd82392 (patch)
tree440f40e3143ac99f7085556bb75e500e3e0d2bec /compiler/rustc_session
parent613bdd49978298648ed05ace086bd1ecad54b44a (diff)
parent05c88a31e7d421b311710571b80add5c0959a6d3 (diff)
downloadrust-7daf4cf911c30e48c78f5e155c029397fdd82392.tar.gz
rust-7daf4cf911c30e48c78f5e155c029397fdd82392.zip
Auto merge of #133138 - azhogin:azhogin/target-modifiers, r=davidtwco,saethlin
Target modifiers (special marked options) are recorded in metainfo

Target modifiers (special marked options) are recorded in metainfo and compared to be equal in different linked crates.

PR for this RFC: https://github.com/rust-lang/rfcs/pull/3716

Option may be marked as `TARGET_MODIFIER`, example: `regparm: Option<u32> = (None, parse_opt_number, [TRACKED TARGET_MODIFIER]`.
If an TARGET_MODIFIER-marked option has non-default value, it will be recorded in crate metainfo as a `Vec<TargetModifier>`:
```
pub struct TargetModifier {
    pub opt: OptionsTargetModifiers,
    pub value_name: String,
}
```

OptionsTargetModifiers is a macro-generated enum.

Option value code (for comparison) is generated using `Debug` trait.

Error example:
```
error: mixing `-Zregparm` will cause an ABI mismatch in crate `incompatible_regparm`
  --> $DIR/incompatible_regparm.rs:10:1
   |
LL | #![crate_type = "lib"]
   | ^
   |
   = help: the `-Zregparm` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely
   = note: `-Zregparm=1` in this crate is incompatible with `-Zregparm=2` in dependency `wrong_regparm`
   = help: set `-Zregparm=2` in this crate or `-Zregparm=1` in `wrong_regparm`
   = help: if you are sure this will not cause problems, use `-Cunsafe-allow-abi-mismatch=regparm` to silence this error

error: aborting due to 1 previous error
```

`-Cunsafe-allow-abi-mismatch=regparm,reg-struct-return` to disable list of flags.
Diffstat (limited to 'compiler/rustc_session')
-rw-r--r--compiler/rustc_session/src/config.rs8
-rw-r--r--compiler/rustc_session/src/lib.rs3
-rw-r--r--compiler/rustc_session/src/options.rs337
3 files changed, 333 insertions, 15 deletions
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 67e314cc685..2fb4b27b889 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1224,6 +1224,7 @@ impl Default for Options {
             color: ColorConfig::Auto,
             logical_env: FxIndexMap::default(),
             verbose: false,
+            target_modifiers: BTreeMap::default(),
         }
     }
 }
@@ -2370,14 +2371,16 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
     let crate_types = parse_crate_types_from_list(unparsed_crate_types)
         .unwrap_or_else(|e| early_dcx.early_fatal(e));
 
-    let mut unstable_opts = UnstableOptions::build(early_dcx, matches);
+    let mut target_modifiers = BTreeMap::<OptionsTargetModifiers, String>::new();
+
+    let mut unstable_opts = UnstableOptions::build(early_dcx, matches, &mut target_modifiers);
     let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches);
 
     check_error_format_stability(early_dcx, &unstable_opts, error_format);
 
     let output_types = parse_output_types(early_dcx, &unstable_opts, matches);
 
-    let mut cg = CodegenOptions::build(early_dcx, matches);
+    let mut cg = CodegenOptions::build(early_dcx, matches, &mut target_modifiers);
     let (disable_local_thinlto, codegen_units) = should_override_cgus_and_disable_thinlto(
         early_dcx,
         &output_types,
@@ -2648,6 +2651,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
         color,
         logical_env,
         verbose,
+        target_modifiers,
     }
 }
 
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index dcf86d1a408..924350b8cbc 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -4,6 +4,9 @@
 #![feature(let_chains)]
 #![feature(map_many_mut)]
 #![feature(rustc_attrs)]
+// To generate CodegenOptionsTargetModifiers and UnstableOptionsTargetModifiers enums
+// with macro_rules, it is necessary to use recursive mechanic ("Incremental TT Munchers").
+#![recursion_limit = "256"]
 #![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index cc86c85f3f0..4ed5499d32c 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -10,6 +10,7 @@ use rustc_data_structures::profiling::TimePassesFormat;
 use rustc_data_structures::stable_hasher::Hash64;
 use rustc_errors::{ColorConfig, LanguageIdentifier, TerminalUrl};
 use rustc_feature::UnstableFeatures;
+use rustc_macros::{Decodable, Encodable};
 use rustc_span::edition::Edition;
 use rustc_span::{RealFileName, SourceFileHashAlgorithm};
 use rustc_target::spec::{
@@ -59,18 +60,194 @@ macro_rules! hash_substruct {
     };
 }
 
+/// Extended target modifier info.
+/// For example, when external target modifier is '-Zregparm=2':
+/// Target modifier enum value + user value ('2') from external crate
+/// is converted into description: prefix ('Z'), name ('regparm'), tech value ('Some(2)').
+pub struct ExtendedTargetModifierInfo {
+    /// Flag prefix (usually, 'C' for codegen flags or 'Z' for unstable flags)
+    pub prefix: String,
+    /// Flag name
+    pub name: String,
+    /// Flag parsed technical value
+    pub tech_value: String,
+}
+
+/// A recorded -Zopt_name=opt_value (or -Copt_name=opt_value)
+/// which alter the ABI or effectiveness of exploit mitigations.
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)]
+pub struct TargetModifier {
+    /// Option enum value
+    pub opt: OptionsTargetModifiers,
+    /// User-provided option value (before parsing)
+    pub value_name: String,
+}
+
+impl TargetModifier {
+    pub fn extend(&self) -> ExtendedTargetModifierInfo {
+        self.opt.reparse(&self.value_name)
+    }
+}
+
+fn tmod_push_impl(
+    opt: OptionsTargetModifiers,
+    tmod_vals: &BTreeMap<OptionsTargetModifiers, String>,
+    tmods: &mut Vec<TargetModifier>,
+) {
+    tmods.push(TargetModifier { opt, value_name: tmod_vals.get(&opt).cloned().unwrap_or_default() })
+}
+
+macro_rules! tmod_push {
+    ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $mods:expr, $tmod_vals:expr) => {
+        tmod_push_impl(
+            OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt_name),
+            $tmod_vals,
+            $mods,
+        );
+    };
+}
+
+macro_rules! gather_tmods {
+    ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
+        [SUBSTRUCT], [TARGET_MODIFIER]) => {
+        compile_error!("SUBSTRUCT can't be target modifier");
+    };
+    ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
+        [UNTRACKED], [TARGET_MODIFIER]) => {
+        tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals)
+    };
+    ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
+        [TRACKED], [TARGET_MODIFIER]) => {
+        tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals)
+    };
+    ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
+        [TRACKED_NO_CRATE_HASH], [TARGET_MODIFIER]) => {
+        tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals)
+    };
+    ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
+        [SUBSTRUCT], []) => {
+        $opt_expr.gather_target_modifiers($mods, $tmod_vals);
+    };
+    ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
+        [UNTRACKED], []) => {{}};
+    ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
+        [TRACKED], []) => {{}};
+    ($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
+        [TRACKED_NO_CRATE_HASH], []) => {{}};
+}
+
+macro_rules! gather_tmods_top_level {
+    ($_opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [SUBSTRUCT $substruct_enum:ident]) => {
+        $opt_expr.gather_target_modifiers($mods, $tmod_vals);
+    };
+    ($opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [$non_substruct:ident TARGET_MODIFIER]) => {
+        compile_error!("Top level option can't be target modifier");
+    };
+    ($opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr, [$non_substruct:ident]) => {};
+}
+
+/// Macro for generating OptionsTargetsModifiers top-level enum with impl.
+/// Will generate something like:
+/// ```rust,ignore (illustrative)
+/// pub enum OptionsTargetModifiers {
+///     CodegenOptions(CodegenOptionsTargetModifiers),
+///     UnstableOptions(UnstableOptionsTargetModifiers),
+/// }
+/// impl OptionsTargetModifiers {
+///     pub fn reparse(&self, user_value: &str) -> ExtendedTargetModifierInfo {
+///         match self {
+///             Self::CodegenOptions(v) => v.reparse(user_value),
+///             Self::UnstableOptions(v) => v.reparse(user_value),
+///         }
+///     }
+///     pub fn is_target_modifier(flag_name: &str) -> bool {
+///         CodegenOptionsTargetModifiers::is_target_modifier(flag_name) ||
+///         UnstableOptionsTargetModifiers::is_target_modifier(flag_name)
+///     }
+/// }
+/// ```
+macro_rules! top_level_tmod_enum {
+    ($( {$($optinfo:tt)*} ),* $(,)*) => {
+        top_level_tmod_enum! { @parse {}, (user_value){}; $($($optinfo)*|)* }
+    };
+    // Termination
+    (
+        @parse
+        {$($variant:tt($substruct_enum:tt))*},
+        ($user_value:ident){$($pout:tt)*};
+    ) => {
+        #[allow(non_camel_case_types)]
+        #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Encodable, Decodable)]
+        pub enum OptionsTargetModifiers {
+            $($variant($substruct_enum)),*
+        }
+        impl OptionsTargetModifiers {
+            #[allow(unused_variables)]
+            pub fn reparse(&self, $user_value: &str) -> ExtendedTargetModifierInfo {
+                #[allow(unreachable_patterns)]
+                match self {
+                    $($pout)*
+                    _ => panic!("unknown target modifier option: {:?}", *self)
+                }
+            }
+            pub fn is_target_modifier(flag_name: &str) -> bool {
+                $($substruct_enum::is_target_modifier(flag_name))||*
+            }
+        }
+    };
+    // Adding SUBSTRUCT option group into $eout
+    (
+        @parse {$($eout:tt)*}, ($puser_value:ident){$($pout:tt)*};
+            [SUBSTRUCT $substruct_enum:ident $variant:ident] |
+        $($tail:tt)*
+    ) => {
+        top_level_tmod_enum! {
+            @parse
+            {
+                $($eout)*
+                $variant($substruct_enum)
+            },
+            ($puser_value){
+                $($pout)*
+                Self::$variant(v) => v.reparse($puser_value),
+            };
+            $($tail)*
+        }
+    };
+    // Skipping non-target-modifier and non-substruct
+    (
+        @parse {$($eout:tt)*}, ($puser_value:ident){$($pout:tt)*};
+            [$non_substruct:ident] |
+        $($tail:tt)*
+    ) => {
+        top_level_tmod_enum! {
+            @parse
+            {
+                $($eout)*
+            },
+            ($puser_value){
+                $($pout)*
+            };
+            $($tail)*
+        }
+    };
+}
+
 macro_rules! top_level_options {
     ( $( #[$top_level_attr:meta] )* pub struct Options { $(
         $( #[$attr:meta] )*
-        $opt:ident : $t:ty [$dep_tracking_marker:ident],
+        $opt:ident : $t:ty [$dep_tracking_marker:ident $( $tmod:ident $variant:ident )?],
     )* } ) => (
+        top_level_tmod_enum!( {$([$dep_tracking_marker $($tmod $variant),*])|*} );
+
         #[derive(Clone)]
         $( #[$top_level_attr] )*
         pub struct Options {
             $(
                 $( #[$attr] )*
                 pub $opt: $t
-            ),*
+            ),*,
+            pub target_modifiers: BTreeMap<OptionsTargetModifiers, String>,
         }
 
         impl Options {
@@ -98,6 +275,17 @@ macro_rules! top_level_options {
                 })*
                 hasher.finish()
             }
+
+            pub fn gather_target_modifiers(&self) -> Vec<TargetModifier> {
+                let mut mods = Vec::<TargetModifier>::new();
+                $({
+                    gather_tmods_top_level!($opt,
+                        &self.$opt, &mut mods, &self.target_modifiers,
+                        [$dep_tracking_marker $($tmod),*]);
+                })*
+                mods.sort_by(|a, b| a.opt.cmp(&b.opt));
+                mods
+            }
         }
     );
 }
@@ -165,9 +353,9 @@ top_level_options!(
         #[rustc_lint_opt_deny_field_access("should only be used via `Config::hash_untracked_state`")]
         untracked_state_hash: Hash64 [TRACKED_NO_CRATE_HASH],
 
-        unstable_opts: UnstableOptions [SUBSTRUCT],
+        unstable_opts: UnstableOptions [SUBSTRUCT UnstableOptionsTargetModifiers UnstableOptions],
         prints: Vec<PrintRequest> [UNTRACKED],
-        cg: CodegenOptions [SUBSTRUCT],
+        cg: CodegenOptions [SUBSTRUCT CodegenOptionsTargetModifiers CodegenOptions],
         externs: Externs [UNTRACKED],
         crate_name: Option<String> [TRACKED],
         /// Indicates how the compiler should treat unstable features.
@@ -226,6 +414,98 @@ top_level_options!(
     }
 );
 
+macro_rules! tmod_enum_opt {
+    ($struct_name:ident, $tmod_enum_name:ident, $opt:ident, $v:ident) => {
+        Some(OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt))
+    };
+    ($struct_name:ident, $tmod_enum_name:ident, $opt:ident, ) => {
+        None
+    };
+}
+
+macro_rules! tmod_enum {
+    ($tmod_enum_name:ident, $prefix:expr, $( {$($optinfo:tt)*} ),* $(,)*) => {
+        tmod_enum! { $tmod_enum_name, $prefix, @parse {}, (user_value){}; $($($optinfo)*|)* }
+    };
+    // Termination
+    (
+        $tmod_enum_name:ident, $prefix:expr,
+        @parse
+        {$($eout:tt)*},
+        ($user_value:ident){$($pout:tt)*};
+    ) => {
+        #[allow(non_camel_case_types)]
+        #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Encodable, Decodable)]
+        pub enum $tmod_enum_name {
+            $($eout),*
+        }
+        impl $tmod_enum_name {
+            #[allow(unused_variables)]
+            pub fn reparse(&self, $user_value: &str) -> ExtendedTargetModifierInfo {
+                #[allow(unreachable_patterns)]
+                match self {
+                    $($pout)*
+                    _ => panic!("unknown target modifier option: {:?}", *self)
+                }
+            }
+            pub fn is_target_modifier(flag_name: &str) -> bool {
+                match flag_name.replace('-', "_").as_str() {
+                    $(stringify!($eout) => true,)*
+                    _ => false,
+                }
+            }
+        }
+    };
+    // Adding target-modifier option into $eout
+    (
+        $tmod_enum_name:ident, $prefix:expr,
+        @parse {$($eout:tt)*}, ($puser_value:ident){$($pout:tt)*};
+            $opt:ident, $parse:ident, $t:ty, [TARGET_MODIFIER] |
+        $($tail:tt)*
+    ) => {
+        tmod_enum! {
+            $tmod_enum_name, $prefix,
+            @parse
+            {
+                $($eout)*
+                $opt
+            },
+            ($puser_value){
+                $($pout)*
+                Self::$opt => {
+                    let mut parsed : $t = Default::default();
+                    parse::$parse(&mut parsed, Some($puser_value));
+                    ExtendedTargetModifierInfo {
+                        prefix: $prefix.to_string(),
+                        name: stringify!($opt).to_string().replace('_', "-"),
+                        tech_value: format!("{:?}", parsed),
+                    }
+                },
+            };
+            $($tail)*
+        }
+    };
+    // Skipping non-target-modifier
+    (
+        $tmod_enum_name:ident, $prefix:expr,
+        @parse {$($eout:tt)*}, ($puser_value:ident){$($pout:tt)*};
+            $opt:ident, $parse:ident, $t:ty, [] |
+        $($tail:tt)*
+    ) => {
+        tmod_enum! {
+            $tmod_enum_name, $prefix,
+            @parse
+            {
+                $($eout)*
+            },
+            ($puser_value){
+                $($pout)*
+            };
+            $($tail)*
+        }
+    };
+}
+
 /// Defines all `CodegenOptions`/`DebuggingOptions` fields and parsers all at once. The goal of this
 /// macro is to define an interface that can be programmatically used by the option parser
 /// to initialize the struct without hardcoding field names all over the place.
@@ -235,11 +515,11 @@ top_level_options!(
 /// generated code to parse an option into its respective field in the struct. There are a few
 /// hand-written parsers for parsing specific types of values in this module.
 macro_rules! options {
-    ($struct_name:ident, $stat:ident, $optmod:ident, $prefix:expr, $outputname:expr,
+    ($struct_name:ident, $tmod_enum_name:ident, $stat:ident, $optmod:ident, $prefix:expr, $outputname:expr,
      $($( #[$attr:meta] )* $opt:ident : $t:ty = (
         $init:expr,
         $parse:ident,
-        [$dep_tracking_marker:ident],
+        [$dep_tracking_marker:ident $( $tmod:ident )?],
         $desc:expr
         $(, deprecated_do_nothing: $dnn:literal )?)
      ),* ,) =>
@@ -248,6 +528,8 @@ macro_rules! options {
     #[rustc_lint_opt_ty]
     pub struct $struct_name { $( $( #[$attr] )* pub $opt: $t),* }
 
+    tmod_enum!( $tmod_enum_name, $prefix, {$($opt, $parse, $t, [$($tmod),*])|*} );
+
     impl Default for $struct_name {
         fn default() -> $struct_name {
             $struct_name { $($opt: $init),* }
@@ -258,8 +540,9 @@ macro_rules! options {
         pub fn build(
             early_dcx: &EarlyDiagCtxt,
             matches: &getopts::Matches,
+            target_modifiers: &mut BTreeMap<OptionsTargetModifiers, String>,
         ) -> $struct_name {
-            build_options(early_dcx, matches, $stat, $prefix, $outputname)
+            build_options(early_dcx, matches, target_modifiers, $stat, $prefix, $outputname)
         }
 
         fn dep_tracking_hash(&self, for_crate_hash: bool, error_format: ErrorOutputType) -> u64 {
@@ -279,11 +562,23 @@ macro_rules! options {
                                         );
             hasher.finish()
         }
+
+        pub fn gather_target_modifiers(
+            &self,
+            _mods: &mut Vec<TargetModifier>,
+            _tmod_vals: &BTreeMap<OptionsTargetModifiers, String>,
+        ) {
+            $({
+                gather_tmods!($struct_name, $tmod_enum_name, $opt, &self.$opt, _mods, _tmod_vals,
+                    [$dep_tracking_marker], [$($tmod),*]);
+            })*
+        }
     }
 
     pub const $stat: OptionDescrs<$struct_name> =
         &[ $( OptionDesc{ name: stringify!($opt), setter: $optmod::$opt,
-            type_desc: desc::$parse, desc: $desc, is_deprecated_and_do_nothing: false $( || $dnn )? } ),* ];
+            type_desc: desc::$parse, desc: $desc, is_deprecated_and_do_nothing: false $( || $dnn )?,
+            tmod: tmod_enum_opt!($struct_name, $tmod_enum_name, $opt, $($tmod),*) } ),* ];
 
     mod $optmod {
     $(
@@ -328,6 +623,7 @@ pub struct OptionDesc<O> {
     // description for option from options table
     desc: &'static str,
     is_deprecated_and_do_nothing: bool,
+    tmod: Option<OptionsTargetModifiers>,
 }
 
 impl<O> OptionDesc<O> {
@@ -344,6 +640,7 @@ impl<O> OptionDesc<O> {
 fn build_options<O: Default>(
     early_dcx: &EarlyDiagCtxt,
     matches: &getopts::Matches,
+    target_modifiers: &mut BTreeMap<OptionsTargetModifiers, String>,
     descrs: OptionDescrs<O>,
     prefix: &str,
     outputname: &str,
@@ -357,7 +654,14 @@ fn build_options<O: Default>(
 
         let option_to_lookup = key.replace('-', "_");
         match descrs.iter().find(|opt_desc| opt_desc.name == option_to_lookup) {
-            Some(OptionDesc { name: _, setter, type_desc, desc, is_deprecated_and_do_nothing }) => {
+            Some(OptionDesc {
+                name: _,
+                setter,
+                type_desc,
+                desc,
+                is_deprecated_and_do_nothing,
+                tmod,
+            }) => {
                 if *is_deprecated_and_do_nothing {
                     // deprecation works for prefixed options only
                     assert!(!prefix.is_empty());
@@ -377,6 +681,11 @@ fn build_options<O: Default>(
                         ),
                     }
                 }
+                if let Some(tmod) = *tmod
+                    && let Some(value) = value
+                {
+                    target_modifiers.insert(tmod, value.to_string());
+                }
             }
             None => early_dcx.early_fatal(format!("unknown {outputname} option: `{key}`")),
         }
@@ -1614,7 +1923,7 @@ pub mod parse {
 }
 
 options! {
-    CodegenOptions, CG_OPTIONS, cgopts, "C", "codegen",
+    CodegenOptions, CodegenOptionsTargetModifiers, CG_OPTIONS, cgopts, "C", "codegen",
 
     // If you add a new option, please update:
     // - compiler/rustc_interface/src/tests.rs
@@ -1745,6 +2054,8 @@ options! {
     target_feature: String = (String::new(), parse_target_feature, [TRACKED],
         "target specific attributes. (`rustc --print target-features` for details). \
         This feature is unsafe."),
+    unsafe_allow_abi_mismatch: Vec<String> = (Vec::new(), parse_comma_list, [UNTRACKED],
+        "Allow incompatible target modifiers in dependency crates (comma separated list)"),
     // tidy-alphabetical-end
 
     // If you add a new option, please update:
@@ -1753,7 +2064,7 @@ options! {
 }
 
 options! {
-    UnstableOptions, Z_OPTIONS, dbopts, "Z", "unstable",
+    UnstableOptions, UnstableOptionsTargetModifiers, Z_OPTIONS, dbopts, "Z", "unstable",
 
     // If you add a new option, please update:
     // - compiler/rustc_interface/src/tests.rs
@@ -2098,10 +2409,10 @@ options! {
         "enable queries of the dependency graph for regression testing (default: no)"),
     randomize_layout: bool = (false, parse_bool, [TRACKED],
         "randomize the layout of types (default: no)"),
-    reg_struct_return: bool = (false, parse_bool, [TRACKED],
+    reg_struct_return: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER],
         "On x86-32 targets, it overrides the default ABI to return small structs in registers.
         It is UNSOUND to link together crates that use different values for this flag!"),
-    regparm: Option<u32> = (None, parse_opt_number, [TRACKED],
+    regparm: Option<u32> = (None, parse_opt_number, [TRACKED TARGET_MODIFIER],
         "On x86-32 targets, setting this to N causes the compiler to pass N arguments \
         in registers EAX, EDX, and ECX instead of on the stack for\
         \"C\", \"cdecl\", and \"stdcall\" fn.\