Skip to content

Commit

Permalink
Align unsized locals
Browse files Browse the repository at this point in the history
Allocate an extra space for unsized locals and manually align the
storage, since alloca doesn't support dynamic alignment.
  • Loading branch information
tmiasko committed May 8, 2023
1 parent ce04288 commit 83a5a69
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 83a5a69

Please sign in to comment.