Alignment with allocators

Hi, even if I have basic knowledge in memory alignment I struggle to understand why only the first block fails at runtime:

pub fn main(init: std.process.Init) !void {
    const alloc = init.arena.allocator();

    { // Panics
        _ = alloc.alloc(u8, 2) catch unreachable;
        const b = alloc.alloc(u8, @sizeOf(i32)) catch unreachable;
        // Panics on next line at runtime with: 'thread 8300 panic: incorrect alignment'
        @as(*i32, @ptrCast(@alignCast(b))).* = 5;
    }

    { // Runs fine because I allocated 32bits, same size as `i32`?
        _ = alloc.alloc(u8, 4) catch unreachable;
        const b = alloc.alloc(u8, @sizeOf(i32)) catch unreachable;
        @as(*i32, @ptrCast(@alignCast(b))).* = 5;
    }

    { // Runs fine because I used `alignedAlloc` (and no need for `@alignCast`)
        _ = alloc.alloc(u8, 2) catch unreachable;
        const b = alloc.alignedAlloc(u8, .of(i32), @sizeOf(i32)) catch unreachable;
        @as(*i32, @ptrCast(b)).* = 5;
    }
}

Do memory allocations have to respect alignment when using same allocator for various sizes like in this example?

first you need to understand that @alignCast only asserts that the runtime address is valid for the required alignment.

data with a lower alignment requirement may be placed at an address that is valid for larger alignments, so it can by chance pass the alignment assert.

some allocators may higher align larger data due to how they function, but that isnt the case for an arena. You should not rely on such behaviour.

2 Likes