|
34 | 34 | //! the target's settings, though `target-feature` and `link-args` will *add*
|
35 | 35 | //! to the list specified by the target, rather than replace.
|
36 | 36 |
|
| 37 | +// ignore-tidy-filelength |
| 38 | + |
| 39 | +use std::assert_matches::assert_matches; |
37 | 40 | use std::borrow::Cow;
|
38 | 41 | use std::collections::BTreeMap;
|
39 | 42 | use std::hash::{Hash, Hasher};
|
@@ -1605,13 +1608,11 @@ macro_rules! supported_targets {
|
1605 | 1608 |
|
1606 | 1609 | #[cfg(test)]
|
1607 | 1610 | mod tests {
|
1608 |
| - mod tests_impl; |
1609 |
| - |
1610 | 1611 | // Cannot put this into a separate file without duplication, make an exception.
|
1611 | 1612 | $(
|
1612 | 1613 | #[test] // `#[test]`
|
1613 | 1614 | fn $module() {
|
1614 |
| - tests_impl::test_target(crate::spec::targets::$module::target()); |
| 1615 | + crate::spec::targets::$module::target().test_target() |
1615 | 1616 | }
|
1616 | 1617 | )+
|
1617 | 1618 | }
|
@@ -2849,6 +2850,302 @@ impl Target {
|
2849 | 2850 | self.max_atomic_width.unwrap_or_else(|| self.pointer_width.into())
|
2850 | 2851 | }
|
2851 | 2852 |
|
| 2853 | + fn check_consistency(&self) -> Result<(), String> { |
| 2854 | + macro_rules! check { |
| 2855 | + ($b:expr, $($msg:tt)*) => { |
| 2856 | + if !$b { |
| 2857 | + return Err(format!($($msg)*)); |
| 2858 | + } |
| 2859 | + } |
| 2860 | + } |
| 2861 | + macro_rules! check_eq { |
| 2862 | + ($left:expr, $right:expr, $($msg:tt)*) => { |
| 2863 | + if ($left) != ($right) { |
| 2864 | + return Err(format!($($msg)*)); |
| 2865 | + } |
| 2866 | + } |
| 2867 | + } |
| 2868 | + macro_rules! check_ne { |
| 2869 | + ($left:expr, $right:expr, $($msg:tt)*) => { |
| 2870 | + if ($left) == ($right) { |
| 2871 | + return Err(format!($($msg)*)); |
| 2872 | + } |
| 2873 | + } |
| 2874 | + } |
| 2875 | + macro_rules! check_matches { |
| 2876 | + ($left:expr, $right:pat, $($msg:tt)*) => { |
| 2877 | + if !matches!($left, $right) { |
| 2878 | + return Err(format!($($msg)*)); |
| 2879 | + } |
| 2880 | + } |
| 2881 | + } |
| 2882 | + |
| 2883 | + check_eq!( |
| 2884 | + self.is_like_osx, |
| 2885 | + self.vendor == "apple", |
| 2886 | + "`is_like_osx` must be set if and only if `vendor` is `apple`" |
| 2887 | + ); |
| 2888 | + check_eq!( |
| 2889 | + self.is_like_solaris, |
| 2890 | + self.os == "solaris" || self.os == "illumos", |
| 2891 | + "`is_like_solaris` must be set if and only if `os` is `solaris` or `illumos`" |
| 2892 | + ); |
| 2893 | + check_eq!( |
| 2894 | + self.is_like_windows, |
| 2895 | + self.os == "windows" || self.os == "uefi", |
| 2896 | + "`is_like_windows` must be set if and only if `os` is `windows` or `uefi`" |
| 2897 | + ); |
| 2898 | + check_eq!( |
| 2899 | + self.is_like_wasm, |
| 2900 | + self.arch == "wasm32" || self.arch == "wasm64", |
| 2901 | + "`is_like_wasm` must be set if and only if `arch` is `wasm32` or `wasm64`" |
| 2902 | + ); |
| 2903 | + if self.is_like_msvc { |
| 2904 | + check!(self.is_like_windows, "if `is_like_msvc` is set, `is_like_windows` must be set"); |
| 2905 | + } |
| 2906 | + |
| 2907 | + // Check that default linker flavor is compatible with some other key properties. |
| 2908 | + check_eq!( |
| 2909 | + self.is_like_osx, |
| 2910 | + matches!(self.linker_flavor, LinkerFlavor::Darwin(..)), |
| 2911 | + "`linker_flavor` must be `darwin` if and only if `is_like_osx` is set" |
| 2912 | + ); |
| 2913 | + check_eq!( |
| 2914 | + self.is_like_msvc, |
| 2915 | + matches!(self.linker_flavor, LinkerFlavor::Msvc(..)), |
| 2916 | + "`linker_flavor` must be `mscv` if and only if `is_like_msvc` is set" |
| 2917 | + ); |
| 2918 | + check_eq!( |
| 2919 | + self.is_like_wasm && self.os != "emscripten", |
| 2920 | + matches!(self.linker_flavor, LinkerFlavor::WasmLld(..)), |
| 2921 | + "`linker_flavor` must be `wasm-lld` if and only if `is_like_wasm` is set and the `os` is not `emscripten`", |
| 2922 | + ); |
| 2923 | + check_eq!( |
| 2924 | + self.os == "emscripten", |
| 2925 | + matches!(self.linker_flavor, LinkerFlavor::EmCc), |
| 2926 | + "`linker_flavor` must be `em-cc` if and only if `os` is `emscripten`" |
| 2927 | + ); |
| 2928 | + check_eq!( |
| 2929 | + self.arch == "bpf", |
| 2930 | + matches!(self.linker_flavor, LinkerFlavor::Bpf), |
| 2931 | + "`linker_flavor` must be `bpf` if and only if `arch` is `bpf`" |
| 2932 | + ); |
| 2933 | + check_eq!( |
| 2934 | + self.arch == "nvptx64", |
| 2935 | + matches!(self.linker_flavor, LinkerFlavor::Ptx), |
| 2936 | + "`linker_flavor` must be `ptc` if and only if `arch` is `nvptx64`" |
| 2937 | + ); |
| 2938 | + |
| 2939 | + for args in [ |
| 2940 | + &self.pre_link_args, |
| 2941 | + &self.late_link_args, |
| 2942 | + &self.late_link_args_dynamic, |
| 2943 | + &self.late_link_args_static, |
| 2944 | + &self.post_link_args, |
| 2945 | + ] { |
| 2946 | + for (&flavor, flavor_args) in args { |
| 2947 | + check!(!flavor_args.is_empty(), "linker flavor args must not be empty"); |
| 2948 | + // Check that flavors mentioned in link args are compatible with the default flavor. |
| 2949 | + match self.linker_flavor { |
| 2950 | + LinkerFlavor::Gnu(..) => { |
| 2951 | + check_matches!( |
| 2952 | + flavor, |
| 2953 | + LinkerFlavor::Gnu(..), |
| 2954 | + "mixing GNU and non-GNU linker flavors" |
| 2955 | + ); |
| 2956 | + } |
| 2957 | + LinkerFlavor::Darwin(..) => { |
| 2958 | + check_matches!( |
| 2959 | + flavor, |
| 2960 | + LinkerFlavor::Darwin(..), |
| 2961 | + "mixing Darwin and non-Darwin linker flavors" |
| 2962 | + ) |
| 2963 | + } |
| 2964 | + LinkerFlavor::WasmLld(..) => { |
| 2965 | + check_matches!( |
| 2966 | + flavor, |
| 2967 | + LinkerFlavor::WasmLld(..), |
| 2968 | + "mixing wasm and non-wasm linker flavors" |
| 2969 | + ) |
| 2970 | + } |
| 2971 | + LinkerFlavor::Unix(..) => { |
| 2972 | + check_matches!( |
| 2973 | + flavor, |
| 2974 | + LinkerFlavor::Unix(..), |
| 2975 | + "mixing unix and non-unix linker flavors" |
| 2976 | + ); |
| 2977 | + } |
| 2978 | + LinkerFlavor::Msvc(..) => { |
| 2979 | + check_matches!( |
| 2980 | + flavor, |
| 2981 | + LinkerFlavor::Msvc(..), |
| 2982 | + "mixing MSVC and non-MSVC linker flavors" |
| 2983 | + ); |
| 2984 | + } |
| 2985 | + LinkerFlavor::EmCc |
| 2986 | + | LinkerFlavor::Bpf |
| 2987 | + | LinkerFlavor::Ptx |
| 2988 | + | LinkerFlavor::Llbc => { |
| 2989 | + check_eq!(flavor, self.linker_flavor, "mixing different linker flavors") |
| 2990 | + } |
| 2991 | + } |
| 2992 | + |
| 2993 | + // Check that link args for cc and non-cc versions of flavors are consistent. |
| 2994 | + let check_noncc = |noncc_flavor| -> Result<(), String> { |
| 2995 | + if let Some(noncc_args) = args.get(&noncc_flavor) { |
| 2996 | + for arg in flavor_args { |
| 2997 | + if let Some(suffix) = arg.strip_prefix("-Wl,") { |
| 2998 | + check!( |
| 2999 | + noncc_args.iter().any(|a| a == suffix), |
| 3000 | + " link args for cc and non-cc versions of flavors are not consistent" |
| 3001 | + ); |
| 3002 | + } |
| 3003 | + } |
| 3004 | + } |
| 3005 | + Ok(()) |
| 3006 | + }; |
| 3007 | + |
| 3008 | + match self.linker_flavor { |
| 3009 | + LinkerFlavor::Gnu(Cc::Yes, lld) => check_noncc(LinkerFlavor::Gnu(Cc::No, lld))?, |
| 3010 | + LinkerFlavor::WasmLld(Cc::Yes) => check_noncc(LinkerFlavor::WasmLld(Cc::No))?, |
| 3011 | + LinkerFlavor::Unix(Cc::Yes) => check_noncc(LinkerFlavor::Unix(Cc::No))?, |
| 3012 | + _ => {} |
| 3013 | + } |
| 3014 | + } |
| 3015 | + |
| 3016 | + // Check that link args for lld and non-lld versions of flavors are consistent. |
| 3017 | + for cc in [Cc::No, Cc::Yes] { |
| 3018 | + check_eq!( |
| 3019 | + args.get(&LinkerFlavor::Gnu(cc, Lld::No)), |
| 3020 | + args.get(&LinkerFlavor::Gnu(cc, Lld::Yes)), |
| 3021 | + "link args for lld and non-lld versions of flavors are not consistent", |
| 3022 | + ); |
| 3023 | + check_eq!( |
| 3024 | + args.get(&LinkerFlavor::Darwin(cc, Lld::No)), |
| 3025 | + args.get(&LinkerFlavor::Darwin(cc, Lld::Yes)), |
| 3026 | + "link args for lld and non-lld versions of flavors are not consistent", |
| 3027 | + ); |
| 3028 | + } |
| 3029 | + check_eq!( |
| 3030 | + args.get(&LinkerFlavor::Msvc(Lld::No)), |
| 3031 | + args.get(&LinkerFlavor::Msvc(Lld::Yes)), |
| 3032 | + "link args for lld and non-lld versions of flavors are not consistent", |
| 3033 | + ); |
| 3034 | + } |
| 3035 | + |
| 3036 | + if self.link_self_contained.is_disabled() { |
| 3037 | + check!( |
| 3038 | + self.pre_link_objects_self_contained.is_empty() |
| 3039 | + && self.post_link_objects_self_contained.is_empty(), |
| 3040 | + "if `link_self_contained` is disabled, then `pre_link_objects_self_contained` and `post_link_objects_self_contained` must be empty", |
| 3041 | + ); |
| 3042 | + } |
| 3043 | + |
| 3044 | + // If your target really needs to deviate from the rules below, |
| 3045 | + // except it and document the reasons. |
| 3046 | + // Keep the default "unknown" vendor instead. |
| 3047 | + check_ne!(self.vendor, "", "`vendor` cannot be empty"); |
| 3048 | + check_ne!(self.os, "", "`os` cannot be empty"); |
| 3049 | + if !self.can_use_os_unknown() { |
| 3050 | + // Keep the default "none" for bare metal targets instead. |
| 3051 | + check_ne!( |
| 3052 | + self.os, |
| 3053 | + "unknown", |
| 3054 | + "`unknown` os can only be used on particular targets; use `none` for bare-metal targets" |
| 3055 | + ); |
| 3056 | + } |
| 3057 | + |
| 3058 | + // Check dynamic linking stuff |
| 3059 | + // BPF: when targeting user space vms (like rbpf), those can load dynamic libraries. |
| 3060 | + // hexagon: when targeting QuRT, that OS can load dynamic libraries. |
| 3061 | + // wasm{32,64}: dynamic linking is inherent in the definition of the VM. |
| 3062 | + if self.os == "none" |
| 3063 | + && (self.arch != "bpf" |
| 3064 | + && self.arch != "hexagon" |
| 3065 | + && self.arch != "wasm32" |
| 3066 | + && self.arch != "wasm64") |
| 3067 | + { |
| 3068 | + check!( |
| 3069 | + !self.dynamic_linking, |
| 3070 | + "dynamic linking is not supported on this OS/architecture" |
| 3071 | + ); |
| 3072 | + } |
| 3073 | + if self.only_cdylib |
| 3074 | + || self.crt_static_allows_dylibs |
| 3075 | + || !self.late_link_args_dynamic.is_empty() |
| 3076 | + { |
| 3077 | + check!( |
| 3078 | + self.dynamic_linking, |
| 3079 | + "dynamic linking must be allowed when `only_cdylib` or `crt_static_allows_dylibs` or `late_link_args_dynamic` are set" |
| 3080 | + ); |
| 3081 | + } |
| 3082 | + // Apparently PIC was slow on wasm at some point, see comments in wasm_base.rs |
| 3083 | + if self.dynamic_linking && !(self.is_like_wasm && self.os != "emscripten") { |
| 3084 | + check_eq!( |
| 3085 | + self.relocation_model, |
| 3086 | + RelocModel::Pic, |
| 3087 | + "targets that support dynamic linking must use the `pic` relocation model" |
| 3088 | + ); |
| 3089 | + } |
| 3090 | + if self.position_independent_executables { |
| 3091 | + check_eq!( |
| 3092 | + self.relocation_model, |
| 3093 | + RelocModel::Pic, |
| 3094 | + "targets that support position-independent executables must use the `pic` relocation model" |
| 3095 | + ); |
| 3096 | + } |
| 3097 | + // The UEFI targets do not support dynamic linking but still require PIC (#101377). |
| 3098 | + if self.relocation_model == RelocModel::Pic && self.os != "uefi" { |
| 3099 | + check!( |
| 3100 | + self.dynamic_linking || self.position_independent_executables, |
| 3101 | + "when the relocation model is `pic`, the target must support dynamic linking or use position-independent executables. \ |
| 3102 | + Set the relocation model to `static` to avoid this requirement" |
| 3103 | + ); |
| 3104 | + } |
| 3105 | + if self.static_position_independent_executables { |
| 3106 | + assert!(self.position_independent_executables); |
| 3107 | + } |
| 3108 | + if self.position_independent_executables { |
| 3109 | + assert!(self.executables); |
| 3110 | + } |
| 3111 | + |
| 3112 | + // Check crt static stuff |
| 3113 | + if self.crt_static_default || self.crt_static_allows_dylibs { |
| 3114 | + assert!(self.crt_static_respected); |
| 3115 | + } |
| 3116 | + |
| 3117 | + // Check that RISC-V targets always specify which ABI they use. |
| 3118 | + match &*self.arch { |
| 3119 | + "riscv32" => { |
| 3120 | + assert_matches!(&*self.llvm_abiname, "ilp32" | "ilp32f" | "ilp32d" | "ilp32e") |
| 3121 | + } |
| 3122 | + "riscv64" => { |
| 3123 | + // Note that the `lp64e` is still unstable as it's not (yet) part of the ELF psABI. |
| 3124 | + assert_matches!(&*self.llvm_abiname, "lp64" | "lp64f" | "lp64d" | "lp64q" | "lp64e") |
| 3125 | + } |
| 3126 | + _ => {} |
| 3127 | + } |
| 3128 | + |
| 3129 | + Ok(()) |
| 3130 | + } |
| 3131 | + |
| 3132 | + /// Test target self-consistency and JSON encoding/decoding roundtrip. |
| 3133 | + #[cfg(test)] |
| 3134 | + fn test_target(mut self) { |
| 3135 | + let recycled_target = Target::from_json(self.to_json()).map(|(j, _)| j); |
| 3136 | + self.update_to_cli(); |
| 3137 | + self.check_consistency().unwrap(); |
| 3138 | + assert_eq!(recycled_target, Ok(self)); |
| 3139 | + } |
| 3140 | + |
| 3141 | + // Add your target to the whitelist if it has `std` library |
| 3142 | + // and you certainly want "unknown" for the OS name. |
| 3143 | + fn can_use_os_unknown(&self) -> bool { |
| 3144 | + self.llvm_target == "wasm32-unknown-unknown" |
| 3145 | + || self.llvm_target == "wasm64-unknown-unknown" |
| 3146 | + || (self.env == "sgx" && self.vendor == "fortanix") |
| 3147 | + } |
| 3148 | + |
2852 | 3149 | /// Loads a target descriptor from a JSON object.
|
2853 | 3150 | pub fn from_json(obj: Json) -> Result<(Target, TargetWarnings), String> {
|
2854 | 3151 | // While ugly, this code must remain this way to retain
|
@@ -3456,6 +3753,7 @@ impl Target {
|
3456 | 3753 | key!(supports_xray, bool);
|
3457 | 3754 |
|
3458 | 3755 | base.update_from_cli();
|
| 3756 | + base.check_consistency()?; |
3459 | 3757 |
|
3460 | 3758 | // Each field should have been read using `Json::remove` so any keys remaining are unused.
|
3461 | 3759 | let remaining_keys = obj.keys();
|
|
0 commit comments