Is it possible to have a Vec<MaybeUninit> but require that the 0-th element is aligned to a 128-bit boundary ?
this should work
#[repr(C, align(128))]
struct MyU8(u8);
let vec: Vec<MaybeUninit<MyU8>> = Vec::new();
@Mokuz That aligns it to 128-byte boundaries, not 128-bit boundaries.
I'd like the 0-th element to be on a 128-bit boundary, and the rest to be contiguous in memory. Does above create a 127-byte gap between adjacent elements ?
Note also that this will align all MyU8
's to the specified boundary, not just the first one. For example, std::mem::size_of::<[MyU8; 2]>() == 256
.
What do you need it for? You say a vector, so do you need resizing?
Stack for a Lisp interpreter, where types are {i, u, f} x {32, 64}, [f32; 4] (XMM registers), user defined structs (C style).
One way to handle this would be to use Vec<MaybeUninit<u128>>
, and then handle the multi-packed items yourself.
Yeah, I'm starting to think this is the simplest solution.
Another option is to go for something like this:
use std::alloc::Layout;
use std::mem::MaybeUninit;
use std::ops::{Deref, DerefMut};
const ALIGN: usize = 128 / 8;
pub struct AlignedBytes {
alloc: *mut u8,
len: usize,
}
impl AlignedBytes {
pub fn new(len: usize) -> Self {
assert!(len > 0);
let layout = Layout::from_size_align(len, ALIGN).unwrap();
let alloc = unsafe { std::alloc::alloc(layout) };
if alloc.is_null() {
panic!("Alloc failed.");
}
Self {
alloc,
len,
}
}
pub fn resize(&mut self, len: usize) {
assert!(len > 0);
let old_layout = Layout::from_size_align(self.len, ALIGN).unwrap();
let new_alloc = unsafe { std::alloc::realloc(self.alloc, old_layout, len) };
if new_alloc.is_null() {
panic!("Resize failed.");
} else {
self.alloc = new_alloc;
self.len = len;
}
}
}
impl Drop for AlignedBytes {
fn drop(&mut self) {
let layout = Layout::from_size_align(self.len, ALIGN).unwrap();
unsafe {
std::alloc::dealloc(self.alloc, layout);
}
}
}
impl Deref for AlignedBytes {
type Target = [MaybeUninit<u8>];
fn deref(&self) -> &Self::Target {
unsafe {
std::slice::from_raw_parts(self.alloc.cast(), self.len)
}
}
}
impl DerefMut for AlignedBytes {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe {
std::slice::from_raw_parts_mut(self.alloc.cast(), self.len)
}
}
}
You could also do the memory management yourself with something like this:
#[repr(C, align(4096))] // Most memory systems use 4k pages
struct StackPage {
data: [MaybeUninit<u8>; 3968], // 4k-128
prev: Option<Box<StackPage>>,
len: usize
}
Is it possible to get the address of a reference?
If so, using my_vec[16-addr%16..]
should work, if you don't really need the Vec aligned.
Also, it looks like there are crates for aligned allocators...
Be aware that u128 is usually aligned like u64.
I understand that this is more a LLVM issue than a Rust issue. What I don't understand: is this fixed now?
Are you using nightly?
If so, why not use a custom allocator?
#![feature(allocator_api)]
use std::alloc::{AllocError, Allocator, Layout, System};
use std::mem::MaybeUninit;
use std::ptr::NonNull;
struct AlignedSystemAlloc<const N: usize> {}
unsafe impl<const N: usize> Allocator for AlignedSystemAlloc<N> {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
System.allocate(layout.align_to(N).unwrap_or(layout))
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
System.deallocate(ptr, layout)
}
}
fn main() {
let vec = Vec::<MaybeUninit<u8>, _>::with_capacity_in(10, AlignedSystemAlloc::<128> {});
assert_eq!(vec.as_ptr() as usize % 128, 0)
}
I'm pretty sure this is unsound because the layouts won't match.
In allocate
you align the input layout but in deallocate
you don't
this should fix it
unsafe impl<const N: usize> Allocator for AlignedSystemAlloc<N> {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
System.allocate(layout.align_to(N).map_err(|_| AllocError)?) // here '1
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
System.deallocate(
ptr,
// NOTE: it's ub to deallocate a pointer with wrong layout
layout.align_to(N).unwrap_or_else(|_| unsafe {
// SAFETY: if `align_to(N)` passed once at '1 it should pass again
core::hint::unreachable_unchecked()
}),
)
}
}
Miri didn't complain about your code but it doesn't complain about mine either so I hope I'm right.
You are right of course, thanks!
Though it should work with layout.align_to(N).unwrap_or(layout)
in both methods as well as it uses the same layout for alloc and dealloc.
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.