Auto merge of #111374 - tmiasko:align-unsized-locals, r=cjgillot

Align unsized locals

Allocate an extra space for unsized locals and manually align the storage, since alloca doesn't support dynamic alignment.

Fixes #71416.
Fixes #71695.
This commit is contained in:
bors 2023-05-13 19:03:33 +00:00
commit 2c41369acc
2 changed files with 45 additions and 11 deletions

View file

@ -402,8 +402,6 @@ pub fn store_unsized<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
indirect_dest: PlaceRef<'tcx, V>,
) {
debug!("OperandRef::store_unsized: operand={:?}, indirect_dest={:?}", self, indirect_dest);
let flags = MemFlags::empty();
// `indirect_dest` must have `*mut T` type. We extract `T` out of it.
let unsized_ty = indirect_dest
.layout
@ -416,17 +414,23 @@ pub fn store_unsized<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
bug!("store_unsized called with a sized value")
};
// FIXME: choose an appropriate alignment, or use dynamic align somehow
let max_align = Align::from_bits(128).unwrap();
let min_align = Align::from_bits(8).unwrap();
// Allocate an appropriate region on the stack, and copy the value into it
let (llsize, _) = glue::size_and_align_of_dst(bx, unsized_ty, Some(llextra));
let lldst = bx.byte_array_alloca(llsize, max_align);
bx.memcpy(lldst, max_align, llptr, min_align, llsize, flags);
// Allocate an appropriate region on the stack, and copy the value into it. Since alloca
// doesn't support dynamic alignment, we allocate an extra align - 1 bytes, and align the
// pointer manually.
let (size, align) = glue::size_and_align_of_dst(bx, unsized_ty, Some(llextra));
let one = bx.const_usize(1);
let align_minus_1 = bx.sub(align, one);
let size_extra = bx.add(size, align_minus_1);
let min_align = Align::ONE;
let alloca = bx.byte_array_alloca(size_extra, min_align);
let address = bx.ptrtoint(alloca, bx.type_isize());
let neg_address = bx.neg(address);
let offset = bx.and(neg_address, align_minus_1);
let dst = bx.inbounds_gep(bx.type_i8(), alloca, &[offset]);
bx.memcpy(dst, min_align, llptr, min_align, size, MemFlags::empty());
// Store the allocated region and the extra to the indirect place.
let indirect_operand = OperandValue::Pair(lldst, llextra);
let indirect_operand = OperandValue::Pair(dst, llextra);
indirect_operand.store(bx, indirect_dest);
}
}

View file

@ -0,0 +1,30 @@
// Test that unsized locals uphold alignment requirements.
// Regression test for #71416.
// run-pass
#![feature(unsized_locals)]
#![allow(incomplete_features)]
use std::any::Any;
#[repr(align(256))]
#[allow(dead_code)]
struct A {
v: u8
}
impl A {
fn f(&self) -> *const A {
assert_eq!(self as *const A as usize % 256, 0);
self
}
}
fn mk() -> Box<dyn Any> {
Box::new(A { v: 4 })
}
fn main() {
let x = *mk();
let dwncst = x.downcast_ref::<A>().unwrap();
let addr = dwncst.f();
assert_eq!(addr as usize % 256, 0);
}