Auto merge of #138601 - RalfJung:wasm-abi-fcw, r=alexcrichton · rust-lang/rust@068609c (original) (raw)
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
`}
`