11//! This module ensures that if a function's ABI requires a particular target feature,
22//! that target feature is enabled both on the callee and all callers.
33use rustc_abi:: { BackendRepr , RegKind } ;
4- use rustc_hir:: CRATE_HIR_ID ;
5- use rustc_middle:: mir:: { self , traversal} ;
6- use rustc_middle:: ty:: { self , Instance , InstanceKind , Ty , TyCtxt } ;
7- use rustc_session:: lint:: builtin:: ABI_UNSUPPORTED_VECTOR_TYPES ;
4+ use rustc_hir:: { CRATE_HIR_ID , HirId } ;
5+ use rustc_middle:: mir:: { self , Location , traversal} ;
6+ use rustc_middle:: ty:: layout:: LayoutCx ;
7+ use rustc_middle:: ty:: { self , Instance , InstanceKind , Ty , TyCtxt , TypingEnv } ;
8+ use rustc_session:: lint:: builtin:: { ABI_UNSUPPORTED_VECTOR_TYPES , WASM_C_ABI } ;
89use rustc_span:: def_id:: DefId ;
910use rustc_span:: { DUMMY_SP , Span , Symbol , sym} ;
10- use rustc_target:: callconv:: { Conv , FnAbi , PassMode } ;
11+ use rustc_target:: callconv:: { ArgAbi , Conv , FnAbi , PassMode } ;
12+ use rustc_target:: spec:: { HasWasmCAbiOpt , WasmCAbi } ;
1113
1214use crate :: errors;
1315
@@ -26,13 +28,15 @@ fn uses_vector_registers(mode: &PassMode, repr: &BackendRepr) -> bool {
2628/// for a certain function.
2729/// `is_call` indicates whether this is a call-site check or a definition-site check;
2830/// this is only relevant for the wording in the emitted error.
29- fn do_check_abi < ' tcx > (
31+ fn do_check_simd_vector_abi < ' tcx > (
3032 tcx : TyCtxt < ' tcx > ,
3133 abi : & FnAbi < ' tcx , Ty < ' tcx > > ,
3234 def_id : DefId ,
3335 is_call : bool ,
34- span : impl Fn ( ) -> Span ,
36+ loc : impl Fn ( ) -> ( Span , HirId ) ,
3537) {
38+ // We check this on all functions, including those using the "Rust" ABI.
39+ // For the "Rust" ABI it would be a bug if the lint ever triggered, but better safe than sorry.
3640 let feature_def = tcx. sess . target . features_for_correct_vector_abi ( ) ;
3741 let codegen_attrs = tcx. codegen_fn_attrs ( def_id) ;
3842 let have_feature = |feat : Symbol | {
@@ -46,10 +50,10 @@ fn do_check_abi<'tcx>(
4650 let feature = match feature_def. iter ( ) . find ( |( bits, _) | size. bits ( ) <= * bits) {
4751 Some ( ( _, feature) ) => feature,
4852 None => {
49- let span = span ( ) ;
53+ let ( span, hir_id ) = loc ( ) ;
5054 tcx. emit_node_span_lint (
5155 ABI_UNSUPPORTED_VECTOR_TYPES ,
52- CRATE_HIR_ID ,
56+ hir_id ,
5357 span,
5458 errors:: AbiErrorUnsupportedVectorType {
5559 span,
@@ -62,10 +66,10 @@ fn do_check_abi<'tcx>(
6266 } ;
6367 if !have_feature ( Symbol :: intern ( feature) ) {
6468 // Emit error.
65- let span = span ( ) ;
69+ let ( span, hir_id ) = loc ( ) ;
6670 tcx. emit_node_span_lint (
6771 ABI_UNSUPPORTED_VECTOR_TYPES ,
68- CRATE_HIR_ID ,
72+ hir_id ,
6973 span,
7074 errors:: AbiErrorDisabledVectorType {
7175 span,
@@ -79,15 +83,71 @@ fn do_check_abi<'tcx>(
7983 }
8084 // The `vectorcall` ABI is special in that it requires SSE2 no matter which types are being passed.
8185 if abi. conv == Conv :: X86VectorCall && !have_feature ( sym:: sse2) {
86+ let ( span, _hir_id) = loc ( ) ;
8287 tcx. dcx ( ) . emit_err ( errors:: AbiRequiredTargetFeature {
83- span : span ( ) ,
88+ span,
8489 required_feature : "sse2" ,
8590 abi : "vectorcall" ,
8691 is_call,
8792 } ) ;
8893 }
8994}
9095
96+ /// Determines whether the given argument is passed the same way on the old and new wasm ABIs.
97+ fn wasm_abi_safe < ' tcx > ( tcx : TyCtxt < ' tcx > , arg : & ArgAbi < ' tcx , Ty < ' tcx > > ) -> bool {
98+ if matches ! ( arg. layout. backend_repr, BackendRepr :: Scalar ( _) ) {
99+ return true ;
100+ }
101+
102+ // This matches `unwrap_trivial_aggregate` in the wasm ABI logic.
103+ if arg. layout . is_aggregate ( ) {
104+ let cx = LayoutCx :: new ( tcx, TypingEnv :: fully_monomorphized ( ) ) ;
105+ if let Some ( unit) = arg. layout . homogeneous_aggregate ( & cx) . ok ( ) . and_then ( |ha| ha. unit ( ) ) {
106+ let size = arg. layout . size ;
107+ // Ensure there's just a single `unit` element in `arg`.
108+ if unit. size == size {
109+ return true ;
110+ }
111+ }
112+ }
113+
114+ false
115+ }
116+
117+ /// Warns against usage of `extern "C"` on wasm32-unknown-unknown that is affected by the
118+ /// ABI transition.
119+ fn do_check_wasm_abi < ' tcx > (
120+ tcx : TyCtxt < ' tcx > ,
121+ abi : & FnAbi < ' tcx , Ty < ' tcx > > ,
122+ is_call : bool ,
123+ loc : impl Fn ( ) -> ( Span , HirId ) ,
124+ ) {
125+ // Only proceed for `extern "C" fn` on wasm32-unknown-unknown (same check as what `adjust_for_foreign_abi` uses to call `compute_wasm_abi_info`),
126+ // and only proceed if `wasm_c_abi_opt` indicates we should emit the lint.
127+ if !( tcx. sess . target . arch == "wasm32"
128+ && tcx. sess . target . os == "unknown"
129+ && tcx. wasm_c_abi_opt ( ) == WasmCAbi :: Legacy { with_lint : true }
130+ && abi. conv == Conv :: C )
131+ {
132+ return ;
133+ }
134+ // Warn against all types whose ABI will change. Return values are not affected by this change.
135+ for arg_abi in abi. args . iter ( ) {
136+ if wasm_abi_safe ( tcx, arg_abi) {
137+ continue ;
138+ }
139+ let ( span, hir_id) = loc ( ) ;
140+ tcx. emit_node_span_lint (
141+ WASM_C_ABI ,
142+ hir_id,
143+ span,
144+ errors:: WasmCAbiTransition { ty : arg_abi. layout . ty , is_call } ,
145+ ) ;
146+ // Let's only warn once per function.
147+ break ;
148+ }
149+ }
150+
91151/// Checks that the ABI of a given instance of a function does not contain vector-passed arguments
92152/// or return values for which the corresponding target feature is not enabled.
93153fn check_instance_abi < ' tcx > ( tcx : TyCtxt < ' tcx > , instance : Instance < ' tcx > ) {
@@ -98,22 +158,24 @@ fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
98158 // function.
99159 return ;
100160 } ;
101- do_check_abi (
102- tcx,
103- abi,
104- instance. def_id ( ) ,
105- /*is_call*/ false ,
106- || tcx. def_span ( instance. def_id ( ) ) ,
107- )
161+ let loc = || {
162+ let def_id = instance. def_id ( ) ;
163+ (
164+ tcx. def_span ( def_id) ,
165+ def_id. as_local ( ) . map ( |did| tcx. local_def_id_to_hir_id ( did) ) . unwrap_or ( CRATE_HIR_ID ) ,
166+ )
167+ } ;
168+ do_check_simd_vector_abi ( tcx, abi, instance. def_id ( ) , /*is_call*/ false , loc) ;
169+ do_check_wasm_abi ( tcx, abi, /*is_call*/ false , loc) ;
108170}
109171
110172/// Checks that a call expression does not try to pass a vector-passed argument which requires a
111173/// target feature that the caller does not have, as doing so causes UB because of ABI mismatch.
112174fn check_call_site_abi < ' tcx > (
113175 tcx : TyCtxt < ' tcx > ,
114176 callee : Ty < ' tcx > ,
115- span : Span ,
116177 caller : InstanceKind < ' tcx > ,
178+ loc : impl Fn ( ) -> ( Span , HirId ) ,
117179) {
118180 if callee. fn_sig ( tcx) . abi ( ) . is_rustic_abi ( ) {
119181 // we directly handle the soundness of Rust ABIs
@@ -141,7 +203,8 @@ fn check_call_site_abi<'tcx>(
141203 // ABI failed to compute; this will not get through codegen.
142204 return ;
143205 } ;
144- do_check_abi ( tcx, callee_abi, caller. def_id ( ) , /*is_call*/ true , || span) ;
206+ do_check_simd_vector_abi ( tcx, callee_abi, caller. def_id ( ) , /*is_call*/ true , & loc) ;
207+ do_check_wasm_abi ( tcx, callee_abi, /*is_call*/ true , & loc) ;
145208}
146209
147210fn check_callees_abi < ' tcx > ( tcx : TyCtxt < ' tcx > , instance : Instance < ' tcx > , body : & mir:: Body < ' tcx > ) {
@@ -157,7 +220,19 @@ fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &m
157220 ty:: TypingEnv :: fully_monomorphized ( ) ,
158221 ty:: EarlyBinder :: bind ( callee_ty) ,
159222 ) ;
160- check_call_site_abi ( tcx, callee_ty, * fn_span, body. source . instance ) ;
223+ check_call_site_abi ( tcx, callee_ty, body. source . instance , || {
224+ let loc = Location {
225+ block : bb,
226+ statement_index : body. basic_blocks [ bb] . statements . len ( ) ,
227+ } ;
228+ (
229+ * fn_span,
230+ body. source_info ( loc)
231+ . scope
232+ . lint_root ( & body. source_scopes )
233+ . unwrap_or ( CRATE_HIR_ID ) ,
234+ )
235+ } ) ;
161236 }
162237 _ => { }
163238 }
0 commit comments