Auto merge of #115372 - RalfJung:abi-assert-eq, r=davidtwco · rust-lang/rust@cd71a37 (original) (raw)
`@@ -10,7 +10,7 @@ use rustc_middle::{
`
10
10
`Instance, Ty,
`
11
11
`},
`
12
12
`};
`
13
``
`-
use rustc_target::abi:🤙:{ArgAbi, ArgAttribute, ArgAttributes, FnAbi, PassMode};
`
``
13
`+
use rustc_target::abi:🤙:{ArgAbi, FnAbi, PassMode};
`
14
14
`use rustc_target::abi::{self, FieldIdx};
`
15
15
`use rustc_target::spec::abi::Abi;
`
16
16
``
`@@ -291,32 +291,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
`
291
291
`return true;
`
292
292
`}
`
293
293
``
294
``
`-
match (caller_layout.abi, callee_layout.abi) {
`
295
``
`-
// If both sides have Scalar/Vector/ScalarPair ABI, we can easily directly compare them.
`
296
``
`-
// Different valid ranges are okay (the validity check will complain if this leads to
`
297
``
`` -
// invalid transmutes). Different signs are not okay on some targets (e.g. `extern
``
298
``
`` -
// "C"on
s390x` where small integers are passed zero/sign-extended in large
``
299
``
`-
// registers), so we generally reject them to increase portability.
`
``
294
`+
match caller_layout.abi {
`
``
295
`+
// For Scalar/Vector/ScalarPair ABI, we directly compare them.
`
300
296
`// NOTE: this is not a stable guarantee! It just reflects a property of our current
`
301
297
`// ABIs. It's also fragile; the same pair of types might be considered ABI-compatible
`
302
298
`// when used directly by-value but not considered compatible as a struct field or array
`
303
299
`// element.
`
304
``
`-
(abi::Abi::Scalar(caller), abi::Abi::Scalar(callee)) => {
`
305
``
`-
caller.primitive() == callee.primitive()
`
``
300
`+
abi::Abi::Scalar(..) | abi::Abi::ScalarPair(..) | abi::Abi::Vector { .. } => {
`
``
301
`+
caller_layout.abi.eq_up_to_validity(&callee_layout.abi)
`
306
302
`}
`
307
``
`-
(
`
308
``
`-
abi::Abi::Vector { element: caller_element, count: caller_count },
`
309
``
`-
abi::Abi::Vector { element: callee_element, count: callee_count },
`
310
``
`-
) => {
`
311
``
`-
caller_element.primitive() == callee_element.primitive()
`
312
``
`-
&& caller_count == callee_count
`
313
``
`-
}
`
314
``
`-
(abi::Abi::ScalarPair(caller1, caller2), abi::Abi::ScalarPair(callee1, callee2)) => {
`
315
``
`-
caller1.primitive() == callee1.primitive()
`
316
``
`-
&& caller2.primitive() == callee2.primitive()
`
317
``
`-
}
`
318
``
`-
(abi::Abi::Aggregate { .. }, abi::Abi::Aggregate { .. }) => {
`
319
``
`-
// Aggregates are compatible only if they newtype-wrap the same type, or if they are both 1-ZST.
`
``
303
`+
_ => {
`
``
304
`+
// Everything else is compatible only if they newtype-wrap the same type, or if they are both 1-ZST.
`
320
305
`` // (The latter part is needed to ensure e.g. that struct Zst
is compatible with struct Wrap((), Zst)
.)
``
321
306
`` // This is conservative, but also means that our check isn't quite so heavily dependent on the PassMode
,
``
322
307
`// which means having ABI-compatibility on one target is much more likely to imply compatibility for other targets.
`
`@@ -329,9 +314,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
`
329
314
` == self.unfold_transparent(callee_layout).ty
`
330
315
`}
`
331
316
`}
`
332
``
`` -
// What remains is Abi::Uninhabited
(which can never be passed anyway) and
``
333
``
`-
// mismatching ABIs, that should all be rejected.
`
334
``
`-
_ => false,
`
335
317
`}
`
336
318
`}
`
337
319
``
`@@ -340,54 +322,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
`
340
322
`caller_abi: &ArgAbi<'tcx, Ty<'tcx>>,
`
341
323
`callee_abi: &ArgAbi<'tcx, Ty<'tcx>>,
`
342
324
`) -> bool {
`
343
``
`-
// When comparing the PassMode, we have to be smart about comparing the attributes.
`
344
``
`-
let arg_attr_compat = |a1: &ArgAttributes, a2: &ArgAttributes| {
`
345
``
`-
// There's only one regular attribute that matters for the call ABI: InReg.
`
346
``
`-
// Everything else is things like noalias, dereferenceable, nonnull, ...
`
347
``
`-
// (This also applies to pointee_size, pointee_align.)
`
348
``
`-
if a1.regular.contains(ArgAttribute::InReg) != a2.regular.contains(ArgAttribute::InReg)
`
349
``
`-
{
`
350
``
`-
return false;
`
351
``
`-
}
`
352
``
`-
// We also compare the sign extension mode -- this could let the callee make assumptions
`
353
``
`-
// about bits that conceptually were not even passed.
`
354
``
`-
if a1.arg_ext != a2.arg_ext {
`
355
``
`-
return false;
`
356
``
`-
}
`
357
``
`-
return true;
`
358
``
`-
};
`
359
``
`-
let mode_compat = || match (&caller_abi.mode, &callee_abi.mode) {
`
360
``
`-
(PassMode::Ignore, PassMode::Ignore) => true, // can still be reached for the return type
`
361
``
`-
(PassMode::Direct(a1), PassMode::Direct(a2)) => arg_attr_compat(a1, a2),
`
362
``
`-
(PassMode::Pair(a1, b1), PassMode::Pair(a2, b2)) => {
`
363
``
`-
arg_attr_compat(a1, a2) && arg_attr_compat(b1, b2)
`
364
``
`-
}
`
365
``
`-
(PassMode::Cast(c1, pad1), PassMode::Cast(c2, pad2)) => c1 == c2 && pad1 == pad2,
`
366
``
`-
(
`
367
``
`-
PassMode::Indirect { attrs: a1, extra_attrs: None, on_stack: s1 },
`
368
``
`-
PassMode::Indirect { attrs: a2, extra_attrs: None, on_stack: s2 },
`
369
``
`-
) => arg_attr_compat(a1, a2) && s1 == s2,
`
370
``
`-
(
`
371
``
`-
PassMode::Indirect { attrs: a1, extra_attrs: Some(e1), on_stack: s1 },
`
372
``
`-
PassMode::Indirect { attrs: a2, extra_attrs: Some(e2), on_stack: s2 },
`
373
``
`-
) => arg_attr_compat(a1, a2) && arg_attr_compat(e1, e2) && s1 == s2,
`
374
``
`-
_ => false,
`
375
``
`-
};
`
376
``
-
377
325
`` // Ideally PassMode
would capture everything there is about argument passing, but that is
``
378
326
`` // not the case: in FnAbi::llvm_type
, also parts of the layout and type information are
``
379
327
`// used. So we need to check that both sufficiently agree to ensures the arguments are
`
380
328
`// compatible.
`
381
329
`` // For instance, layout_compat
is needed to reject i32
vs f32
, which is not reflected
``
382
330
`` // in PassMode
. mode_compat
is needed to reject u8
vs bool
, which have the same
``
383
331
`` // abi::Primitive
but different arg_ext
.
``
384
``
`-
if self.layout_compat(caller_abi.layout, callee_abi.layout) && mode_compat() {
`
385
``
`-
// Something went very wrong if our checks don't even imply that the layout is the same.
`
386
``
`-
assert!(
`
387
``
`-
caller_abi.layout.size == callee_abi.layout.size
`
388
``
`-
&& caller_abi.layout.align.abi == callee_abi.layout.align.abi
`
389
``
`-
&& caller_abi.layout.is_sized() == callee_abi.layout.is_sized()
`
390
``
`-
);
`
``
332
`+
if self.layout_compat(caller_abi.layout, callee_abi.layout)
`
``
333
`+
&& caller_abi.mode.eq_abi(&callee_abi.mode)
`
``
334
`+
{
`
``
335
`+
// Something went very wrong if our checks don't imply layout ABI compatibility.
`
``
336
`+
assert!(caller_abi.layout.eq_abi(&callee_abi.layout));
`
391
337
`return true;
`
392
338
`} else {
`
393
339
`trace!(
`