Skip to content

std.PackedIntArray fails for some int types at comptime #19627

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
sjb3d opened this issue Apr 12, 2024 · 6 comments
Closed

std.PackedIntArray fails for some int types at comptime #19627

sjb3d opened this issue Apr 12, 2024 · 6 comments
Labels
bug Observed behavior contradicts documented or intended behavior standard library This issue involves writing Zig code for the standard library.
Milestone

Comments

@sjb3d
Copy link
Contributor

sjb3d commented Apr 12, 2024

Zig Version

0.11.0

Steps to Reproduce and Observed Behavior

Hi, I asked a question about this at https://ziggit.dev/t/packedintarray-at-comptime/3915, and it was mentioned that this could be related to #19452, so thought I should report this as an issue.

The issue that use of PackedIntArray during comptime seems to fail (with a compile error) for certain Int types. The compile error (in both 0.11.0 and 0.12.0-dev.3631+c4587dc9f) is:

error: comptime dereference requires 'packed_int_array.PackedIntArrayEndian(u10,.little,256)' to have a well-defined layout, but it does not.

The repro case for this is:

const std = @import("std");

const LookupTable = std.PackedIntArray(u10, 256);

fn makeLookupTable() LookupTable {
    @setEvalBranchQuota(10_000);
    var table: LookupTable = undefined;
    for (0..256) |i| {
        table.set(i, @truncate(i));
    }
    return table;
}

const lookupTable = makeLookupTable();

export fn example(index: usize) usize {
    return lookupTable.get(index);
}

Interestingly, I get the same error when using u10-u12 as the lookup table type, but u1-u9 and u13-u16 all compile fine. I am a confused about what could be special about u10-u12 that is causing the compile error.

Expected Behavior

Repro case compiles for all types u1-u16.

@sjb3d sjb3d added the bug Observed behavior contradicts documented or intended behavior label Apr 12, 2024
@mlugg
Copy link
Member

mlugg commented Apr 20, 2024

On 0.12.0, this gives way to another error:

.opt/zig/lib/std/packed_int_array.zig:126:36: error: dereference of '*align(1) u24' exceeds bounds of containing decl of type '[320]u8'

I believe this is a subtle bug in std.PackedIntArray, because the load of Container in setBits will affect @sizeOf(MaxIo) bytes of memory, which is potentially more than (@bitSizeOf(MaxIo) + 7) / 8.

@mlugg mlugg added this to the 0.13.0 milestone Apr 20, 2024
@sjb3d
Copy link
Contributor Author

sjb3d commented Apr 24, 2024

Ah thanks, if the remaining issue is in the standard lib then I can dig in a bit more, I can probably take a look in the next few days.

@sjb3d
Copy link
Contributor Author

sjb3d commented Apr 25, 2024

I dug in a bit more using @compileLog around this comptime load, it seems like a u24 load from byte offset 317 (of an array with 320 bytes) is requested, which seems fine if this is implemented as a 3-byte load, but could this be rounding up to a power of 2 size?

@mlugg
Copy link
Member

mlugg commented Apr 25, 2024

Pointer loads fetch @sizeOf(T) bytes from memory - for u24, this is 4, because we use power-of-two sizes until we hit the system word size (at which point we instead work in multiples of that). So indeed, the current logic is wrong.

The solution is probably to either pad the buffer out to a multiple of @sizeOf(MinIo), or to load plain byte arrays in boundary cases and bitcast (or something like that).

Note that if you make this change and PR it, please make sure to add a corresponding test!

@sjb3d
Copy link
Contributor Author

sjb3d commented Apr 25, 2024

Ah thanks, I just wrote a smaller test case that confirms what you posted.

const std = @import("std");

fn foo(bytes: []const u8) u24 {
    const p: *align(1) const u24 = @ptrCast(&bytes[1]);
    return p.*;
}

export fn example() u32 {
    const bytes = [_]u8{ 0, 1, 2, 3 };
    return comptime foo(&bytes);
}

Produces: error: dereference of '*align(1) const u24' exceeds bounds of containing decl of type '[4]u8'

So it seems like PackedIntArray has not been written with this power-of-2 behaviour in mind, I'll have a think about how best to address it...

@sjb3d
Copy link
Contributor Author

sjb3d commented Apr 26, 2024

Note to self that there is also std.mem.readPackedInt and friends, which seem to work fine loading a u24 from the end of a byte array, so it might be worth refactoring PackedIntArray to make use of this (maybe only when reading near the end?).

Test case for readPackedInt that seems to work fine at comptime:

const std = @import("std");

export fn example() u32 {
    const bytes = [_]u8{ 0, 1, 2, 3 };
    return comptime std.mem.readPackedInt(u24, &bytes, 8, .little);
}

@andrewrk andrewrk added the standard library This issue involves writing Zig code for the standard library. label Oct 24, 2024
@andrewrk andrewrk closed this as not planned Won't fix, can't repro, duplicate, stale Oct 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Observed behavior contradicts documented or intended behavior standard library This issue involves writing Zig code for the standard library.
Projects
None yet
Development

No branches or pull requests

3 participants