1
1
//! This module ensures that if a function's ABI requires a particular target feature,
2
2
//! that target feature is enabled both on the callee and all callers.
3
3
use 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 } ;
8
9
use rustc_span:: def_id:: DefId ;
9
10
use 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 } ;
11
13
12
14
use crate :: errors;
13
15
@@ -26,13 +28,15 @@ fn uses_vector_registers(mode: &PassMode, repr: &BackendRepr) -> bool {
26
28
/// for a certain function.
27
29
/// `is_call` indicates whether this is a call-site check or a definition-site check;
28
30
/// 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 > (
30
32
tcx : TyCtxt < ' tcx > ,
31
33
abi : & FnAbi < ' tcx , Ty < ' tcx > > ,
32
34
def_id : DefId ,
33
35
is_call : bool ,
34
- span : impl Fn ( ) -> Span ,
36
+ loc : impl Fn ( ) -> ( Span , HirId ) ,
35
37
) {
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.
36
40
let feature_def = tcx. sess . target . features_for_correct_vector_abi ( ) ;
37
41
let codegen_attrs = tcx. codegen_fn_attrs ( def_id) ;
38
42
let have_feature = |feat : Symbol | {
@@ -46,10 +50,10 @@ fn do_check_abi<'tcx>(
46
50
let feature = match feature_def. iter ( ) . find ( |( bits, _) | size. bits ( ) <= * bits) {
47
51
Some ( ( _, feature) ) => feature,
48
52
None => {
49
- let span = span ( ) ;
53
+ let ( span, hir_id ) = loc ( ) ;
50
54
tcx. emit_node_span_lint (
51
55
ABI_UNSUPPORTED_VECTOR_TYPES ,
52
- CRATE_HIR_ID ,
56
+ hir_id ,
53
57
span,
54
58
errors:: AbiErrorUnsupportedVectorType {
55
59
span,
@@ -62,10 +66,10 @@ fn do_check_abi<'tcx>(
62
66
} ;
63
67
if !have_feature ( Symbol :: intern ( feature) ) {
64
68
// Emit error.
65
- let span = span ( ) ;
69
+ let ( span, hir_id ) = loc ( ) ;
66
70
tcx. emit_node_span_lint (
67
71
ABI_UNSUPPORTED_VECTOR_TYPES ,
68
- CRATE_HIR_ID ,
72
+ hir_id ,
69
73
span,
70
74
errors:: AbiErrorDisabledVectorType {
71
75
span,
@@ -79,15 +83,71 @@ fn do_check_abi<'tcx>(
79
83
}
80
84
// The `vectorcall` ABI is special in that it requires SSE2 no matter which types are being passed.
81
85
if abi. conv == Conv :: X86VectorCall && !have_feature ( sym:: sse2) {
86
+ let ( span, _hir_id) = loc ( ) ;
82
87
tcx. dcx ( ) . emit_err ( errors:: AbiRequiredTargetFeature {
83
- span : span ( ) ,
88
+ span,
84
89
required_feature : "sse2" ,
85
90
abi : "vectorcall" ,
86
91
is_call,
87
92
} ) ;
88
93
}
89
94
}
90
95
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
+
91
151
/// Checks that the ABI of a given instance of a function does not contain vector-passed arguments
92
152
/// or return values for which the corresponding target feature is not enabled.
93
153
fn 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>) {
98
158
// function.
99
159
return ;
100
160
} ;
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) ;
108
170
}
109
171
110
172
/// Checks that a call expression does not try to pass a vector-passed argument which requires a
111
173
/// target feature that the caller does not have, as doing so causes UB because of ABI mismatch.
112
174
fn check_call_site_abi < ' tcx > (
113
175
tcx : TyCtxt < ' tcx > ,
114
176
callee : Ty < ' tcx > ,
115
- span : Span ,
116
177
caller : InstanceKind < ' tcx > ,
178
+ loc : impl Fn ( ) -> ( Span , HirId ) + Copy ,
117
179
) {
118
180
if callee. fn_sig ( tcx) . abi ( ) . is_rustic_abi ( ) {
119
181
// we directly handle the soundness of Rust ABIs
@@ -141,7 +203,8 @@ fn check_call_site_abi<'tcx>(
141
203
// ABI failed to compute; this will not get through codegen.
142
204
return ;
143
205
} ;
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) ;
145
208
}
146
209
147
210
fn 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
157
220
ty:: TypingEnv :: fully_monomorphized ( ) ,
158
221
ty:: EarlyBinder :: bind ( callee_ty) ,
159
222
) ;
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
+ } ) ;
161
236
}
162
237
_ => { }
163
238
}
0 commit comments