about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTomasz Miąsko <tomasz.miasko@gmail.com>2023-05-08 00:00:00 +0000
committerTomasz Miąsko <tomasz.miasko@gmail.com>2023-05-08 23:40:51 +0200
commit83a5a69a4c7bd60901bf7f7d96378ddea7ecb06c (patch)
treeb004f829fc6b066af593af1901d5b797dbd020b1
parentce042889f7f0d687368a9704eff64cf9542bac6d (diff)
downloadrust-83a5a69a4c7bd60901bf7f7d96378ddea7ecb06c.tar.gz
rust-83a5a69a4c7bd60901bf7f7d96378ddea7ecb06c.zip
Align unsized locals
Allocate an extra space for unsized locals and manually align the
storage, since alloca doesn't support dynamic alignment.
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/operand.rs26
-rw-r--r--tests/ui/unsized-locals/align.rs30
2 files changed, 45 insertions, 11 deletions
diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs
index 9efbb34b515..2301c3ef13e 100644
--- a/compiler/rustc_codegen_ssa/src/mir/operand.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs
@@ -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
@@ -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);
     }
 }
diff --git a/tests/ui/unsized-locals/align.rs b/tests/ui/unsized-locals/align.rs
new file mode 100644
index 00000000000..01be8f3bb9c
--- /dev/null
+++ b/tests/ui/unsized-locals/align.rs
@@ -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);
+}