Skip to content

Commit

Permalink
Auto merge of #111374 - tmiasko:align-unsized-locals, r=cjgillot
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bors committed May 13, 2023
2 parents 1623978 + 83a5a69 commit 2c41369
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 11 deletions.
26 changes: 15 additions & 11 deletions compiler/rustc_codegen_ssa/src/mir/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,8 +402,6 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<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
Expand All @@ -416,17 +414,23 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<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);
}
}
Expand Down
30 changes: 30 additions & 0 deletions tests/ui/unsized-locals/align.rs
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit 2c41369

Please sign in to comment.