Auto merge of #117500 - RalfJung:aggregate-abi, r=davidtwco
Ensure sanity of all computed ABIs This moves the ABI sanity assertions from the codegen backend to the ABI computation logic. Sadly, due to past mistakes, we [have to](https://github.com/rust-lang/rust/pull/117351#issuecomment-1788495503) be able to compute a sane ABI for nonsensical function types like `extern "C" fn(str) -> str`. So to make the sanity check pass we first need to make all ABI adjustment deal with unsized types... and we have no shared infrastructure for those adjustments, so that's a bunch of copy-paste. At least we have assertions failing loudly when one accidentally sets a different mode for an unsized argument. To achieve this, this re-lands the parts of https://github.com/rust-lang/rust/pull/80594 that got reverted in https://github.com/rust-lang/rust/pull/81388. To avoid breaking wasm ABI again, that ABI now explicitly opts-in to the (wrong, broken) ABI that we currently keep for backwards compatibility. That's still better than having *every* ABI use the wrong broken default! Cc `@bjorn3` Fixes https://github.com/rust-lang/rust/issues/115845
This commit is contained in:
commit
d19980e1ce
21 changed files with 265 additions and 77 deletions
|
@ -40,6 +40,10 @@ where
|
|||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
C: HasDataLayout,
|
||||
{
|
||||
if !ret.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
if !ret.layout.is_aggregate() {
|
||||
if kind == AbiKind::DarwinPCS {
|
||||
// On Darwin, when returning an i8/i16, it must be sign-extended to 32 bits,
|
||||
|
@ -67,6 +71,10 @@ where
|
|||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
C: HasDataLayout,
|
||||
{
|
||||
if !arg.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
if !arg.layout.is_aggregate() {
|
||||
if kind == AbiKind::DarwinPCS {
|
||||
// On Darwin, when passing an i8/i16, it must be sign-extended to 32 bits,
|
||||
|
|
|
@ -30,6 +30,10 @@ where
|
|||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
C: HasDataLayout,
|
||||
{
|
||||
if !ret.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
if !ret.layout.is_aggregate() {
|
||||
ret.extend_integer_width_to(32);
|
||||
return;
|
||||
|
@ -56,6 +60,10 @@ where
|
|||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
C: HasDataLayout,
|
||||
{
|
||||
if !arg.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
if !arg.layout.is_aggregate() {
|
||||
arg.extend_integer_width_to(32);
|
||||
return;
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
use crate::abi::call::{ArgAbi, FnAbi, Reg, Uniform};
|
||||
|
||||
fn classify_ret<Ty>(arg: &mut ArgAbi<'_, Ty>) {
|
||||
if !arg.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
// For return type, aggregate which <= 2*XLen will be returned in registers.
|
||||
// Otherwise, aggregate will be returned indirectly.
|
||||
if arg.layout.is_aggregate() {
|
||||
|
@ -24,6 +28,10 @@ fn classify_ret<Ty>(arg: &mut ArgAbi<'_, Ty>) {
|
|||
}
|
||||
|
||||
fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
|
||||
if !arg.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
// For argument type, the first 4*XLen parts of aggregate will be passed
|
||||
// in registers, and the rest will be passed in stack.
|
||||
// So we can coerce to integers directly and let backend handle it correctly.
|
||||
|
|
|
@ -152,6 +152,10 @@ fn classify_ret<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, xlen: u64, flen: u6
|
|||
where
|
||||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
{
|
||||
if !arg.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return false; // I guess? return value of this function is not documented
|
||||
}
|
||||
if let Some(conv) = should_use_fp_conv(cx, &arg.layout, xlen, flen) {
|
||||
match conv {
|
||||
FloatConv::Float(f) => {
|
||||
|
@ -214,6 +218,10 @@ fn classify_arg<'a, Ty, C>(
|
|||
) where
|
||||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
{
|
||||
if !arg.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
if !is_vararg {
|
||||
match should_use_fp_conv(cx, &arg.layout, xlen, flen) {
|
||||
Some(FloatConv::Float(f)) if *avail_fprs >= 1 => {
|
||||
|
|
|
@ -9,6 +9,10 @@ fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
|
|||
}
|
||||
|
||||
fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
|
||||
if !arg.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
if arg.layout.is_aggregate() {
|
||||
arg.make_indirect_byval(None);
|
||||
} else {
|
||||
|
|
|
@ -17,6 +17,10 @@ fn classify_arg<Ty, C>(cx: &C, arg: &mut ArgAbi<'_, Ty>, offset: &mut Size)
|
|||
where
|
||||
C: HasDataLayout,
|
||||
{
|
||||
if !arg.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
let dl = cx.data_layout();
|
||||
let size = arg.layout.size;
|
||||
let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align).abi;
|
||||
|
|
|
@ -422,7 +422,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
}))
|
||||
}
|
||||
|
||||
Abi::ScalarPair(..) | Abi::Aggregate { .. } => {
|
||||
Abi::ScalarPair(..) | Abi::Aggregate { sized: true } => {
|
||||
// Helper for computing `homogeneous_aggregate`, allowing a custom
|
||||
// starting offset (used below for handling variants).
|
||||
let from_fields_at =
|
||||
|
@ -520,6 +520,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
|||
Ok(result)
|
||||
}
|
||||
}
|
||||
Abi::Aggregate { sized: false } => Err(Heterogeneous),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -555,8 +556,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
|
|||
scalar_attrs(&layout, b, a.size(cx).align_to(b.align(cx).abi)),
|
||||
),
|
||||
Abi::Vector { .. } => PassMode::Direct(ArgAttributes::new()),
|
||||
// The `Aggregate` ABI should always be adjusted later.
|
||||
Abi::Aggregate { .. } => PassMode::Direct(ArgAttributes::new()),
|
||||
Abi::Aggregate { .. } => Self::indirect_pass_mode(&layout),
|
||||
};
|
||||
ArgAbi { layout, mode }
|
||||
}
|
||||
|
@ -580,14 +580,30 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
|
|||
PassMode::Indirect { attrs, meta_attrs, on_stack: false }
|
||||
}
|
||||
|
||||
/// Pass this argument directly instead. Should NOT be used!
|
||||
/// Only exists because of past ABI mistakes that will take time to fix
|
||||
/// (see <https://github.com/rust-lang/rust/issues/115666>).
|
||||
pub fn make_direct_deprecated(&mut self) {
|
||||
match self.mode {
|
||||
PassMode::Indirect { .. } => {
|
||||
self.mode = PassMode::Direct(ArgAttributes::new());
|
||||
}
|
||||
PassMode::Ignore | PassMode::Direct(_) | PassMode::Pair(_, _) => return, // already direct
|
||||
_ => panic!("Tried to make {:?} direct", self.mode),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_indirect(&mut self) {
|
||||
match self.mode {
|
||||
PassMode::Direct(_) | PassMode::Pair(_, _) => {}
|
||||
PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: false } => return,
|
||||
PassMode::Direct(_) | PassMode::Pair(_, _) => {
|
||||
self.mode = Self::indirect_pass_mode(&self.layout);
|
||||
}
|
||||
PassMode::Indirect { attrs: _, meta_attrs: _, on_stack: false } => {
|
||||
// already indirect
|
||||
return;
|
||||
}
|
||||
_ => panic!("Tried to make {:?} indirect", self.mode),
|
||||
}
|
||||
|
||||
self.mode = Self::indirect_pass_mode(&self.layout);
|
||||
}
|
||||
|
||||
pub fn make_indirect_byval(&mut self, byval_align: Option<Align>) {
|
||||
|
|
|
@ -4,12 +4,18 @@ use crate::abi::{HasDataLayout, TyAbiInterface};
|
|||
fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
|
||||
if ret.layout.is_aggregate() && ret.layout.size.bits() > 64 {
|
||||
ret.make_indirect();
|
||||
} else {
|
||||
// FIXME: this is wrong! Need to decide which ABI we really want here.
|
||||
ret.make_direct_deprecated();
|
||||
}
|
||||
}
|
||||
|
||||
fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
|
||||
if arg.layout.is_aggregate() && arg.layout.size.bits() > 64 {
|
||||
arg.make_indirect();
|
||||
} else {
|
||||
// FIXME: this is wrong! Need to decide which ABI we really want here.
|
||||
arg.make_direct_deprecated();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +36,9 @@ where
|
|||
_ => unreachable!("Align is given as power of 2 no larger than 16 bytes"),
|
||||
};
|
||||
arg.cast_to(Uniform { unit, total: Size::from_bytes(2 * align_bytes) });
|
||||
} else {
|
||||
// FIXME: find a better way to do this. See https://github.com/rust-lang/rust/issues/117271.
|
||||
arg.make_direct_deprecated();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,10 @@ where
|
|||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
C: HasDataLayout,
|
||||
{
|
||||
if !ret.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
if !ret.layout.is_aggregate() {
|
||||
ret.extend_integer_width_to(64);
|
||||
return;
|
||||
|
@ -89,6 +93,10 @@ where
|
|||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
C: HasDataLayout,
|
||||
{
|
||||
if !arg.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
if !arg.layout.is_aggregate() {
|
||||
arg.extend_integer_width_to(64);
|
||||
return;
|
||||
|
|
|
@ -158,6 +158,10 @@ fn classify_ret<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, xlen: u64, flen: u6
|
|||
where
|
||||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
{
|
||||
if !arg.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return false; // I guess? return value of this function is not documented
|
||||
}
|
||||
if let Some(conv) = should_use_fp_conv(cx, &arg.layout, xlen, flen) {
|
||||
match conv {
|
||||
FloatConv::Float(f) => {
|
||||
|
@ -220,6 +224,10 @@ fn classify_arg<'a, Ty, C>(
|
|||
) where
|
||||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
{
|
||||
if !arg.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
if !is_vararg {
|
||||
match should_use_fp_conv(cx, &arg.layout, xlen, flen) {
|
||||
Some(FloatConv::Float(f)) if *avail_fprs >= 1 => {
|
||||
|
|
|
@ -17,6 +17,10 @@ where
|
|||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
C: HasDataLayout,
|
||||
{
|
||||
if !arg.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
if !arg.layout.is_aggregate() && arg.layout.size.bits() <= 64 {
|
||||
arg.extend_integer_width_to(64);
|
||||
return;
|
||||
|
|
|
@ -17,6 +17,10 @@ fn classify_arg<Ty, C>(cx: &C, arg: &mut ArgAbi<'_, Ty>, offset: &mut Size)
|
|||
where
|
||||
C: HasDataLayout,
|
||||
{
|
||||
if !arg.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
let dl = cx.data_layout();
|
||||
let size = arg.layout.size;
|
||||
let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align).abi;
|
||||
|
|
|
@ -34,6 +34,10 @@ where
|
|||
Ty: TyAbiInterface<'a, C> + Copy,
|
||||
C: HasDataLayout,
|
||||
{
|
||||
if !arg.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
arg.extend_integer_width_to(32);
|
||||
if arg.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, arg) {
|
||||
arg.make_indirect_byval(None);
|
||||
|
@ -67,21 +71,33 @@ where
|
|||
/// Also see <https://github.com/rust-lang/rust/issues/115666>.
|
||||
pub fn compute_wasm_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
|
||||
if !fn_abi.ret.is_ignore() {
|
||||
classify_ret(&mut fn_abi.ret);
|
||||
classify_ret_wasm_abi(&mut fn_abi.ret);
|
||||
}
|
||||
|
||||
for arg in fn_abi.args.iter_mut() {
|
||||
if arg.is_ignore() {
|
||||
continue;
|
||||
}
|
||||
classify_arg(arg);
|
||||
classify_arg_wasm_abi(arg);
|
||||
}
|
||||
|
||||
fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
|
||||
fn classify_ret_wasm_abi<Ty>(ret: &mut ArgAbi<'_, Ty>) {
|
||||
if !ret.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
// FIXME: this is bad! https://github.com/rust-lang/rust/issues/115666
|
||||
ret.make_direct_deprecated();
|
||||
ret.extend_integer_width_to(32);
|
||||
}
|
||||
|
||||
fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
|
||||
fn classify_arg_wasm_abi<Ty>(arg: &mut ArgAbi<'_, Ty>) {
|
||||
if !arg.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
// FIXME: this is bad! https://github.com/rust-lang/rust/issues/115666
|
||||
arg.make_direct_deprecated();
|
||||
arg.extend_integer_width_to(32);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ where
|
|||
C: HasDataLayout + HasTargetSpec,
|
||||
{
|
||||
if !fn_abi.ret.is_ignore() {
|
||||
if fn_abi.ret.layout.is_aggregate() {
|
||||
if fn_abi.ret.layout.is_aggregate() && fn_abi.ret.layout.is_sized() {
|
||||
// Returning a structure. Most often, this will use
|
||||
// a hidden first argument. On some platforms, though,
|
||||
// small structs are returned as integers.
|
||||
|
@ -50,7 +50,7 @@ where
|
|||
}
|
||||
|
||||
for arg in fn_abi.args.iter_mut() {
|
||||
if arg.is_ignore() {
|
||||
if arg.is_ignore() || !arg.layout.is_sized() {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -153,9 +153,9 @@ fn reg_component(cls: &[Option<Class>], i: &mut usize, size: Size) -> Option<Reg
|
|||
}
|
||||
}
|
||||
|
||||
fn cast_target(cls: &[Option<Class>], size: Size) -> Option<CastTarget> {
|
||||
fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
|
||||
let mut i = 0;
|
||||
let lo = reg_component(cls, &mut i, size)?;
|
||||
let lo = reg_component(cls, &mut i, size).unwrap();
|
||||
let offset = Size::from_bytes(8) * (i as u64);
|
||||
let mut target = CastTarget::from(lo);
|
||||
if size > offset {
|
||||
|
@ -164,7 +164,7 @@ fn cast_target(cls: &[Option<Class>], size: Size) -> Option<CastTarget> {
|
|||
}
|
||||
}
|
||||
assert_eq!(reg_component(cls, &mut i, Size::ZERO), None);
|
||||
Some(target)
|
||||
target
|
||||
}
|
||||
|
||||
const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9
|
||||
|
@ -179,6 +179,10 @@ where
|
|||
let mut sse_regs = MAX_SSE_REGS;
|
||||
|
||||
let mut x86_64_arg_or_ret = |arg: &mut ArgAbi<'a, Ty>, is_arg: bool| {
|
||||
if !arg.layout.is_sized() {
|
||||
// Not touching this...
|
||||
return;
|
||||
}
|
||||
let mut cls_or_mem = classify_arg(cx, arg);
|
||||
|
||||
if is_arg {
|
||||
|
@ -227,9 +231,7 @@ where
|
|||
// split into sized chunks passed individually
|
||||
if arg.layout.is_aggregate() {
|
||||
let size = arg.layout.size;
|
||||
if let Some(cast_target) = cast_target(cls, size) {
|
||||
arg.cast_to(cast_target);
|
||||
}
|
||||
arg.cast_to(cast_target(cls, size));
|
||||
} else {
|
||||
arg.extend_integer_width_to(32);
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ use crate::abi::Abi;
|
|||
pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) {
|
||||
let fixup = |a: &mut ArgAbi<'_, Ty>| {
|
||||
match a.layout.abi {
|
||||
Abi::Uninhabited => {}
|
||||
Abi::ScalarPair(..) | Abi::Aggregate { .. } => match a.layout.size.bits() {
|
||||
Abi::Uninhabited | Abi::Aggregate { sized: false } => {}
|
||||
Abi::ScalarPair(..) | Abi::Aggregate { sized: true } => match a.layout.size.bits() {
|
||||
8 => a.cast_to(Reg::i8()),
|
||||
16 => a.cast_to(Reg::i16()),
|
||||
32 => a.cast_to(Reg::i32()),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue