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"ons390x` 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!(

`