Skip to content

Implement support for bitfields wider than 64 bits#306

Open
davispuh wants to merge 3 commits into
jacob-carlborg:masterfrom
davispuh:bits
Open

Implement support for bitfields wider than 64 bits#306
davispuh wants to merge 3 commits into
jacob-carlborg:masterfrom
davispuh:bits

Conversation

@davispuh

Copy link
Copy Markdown

C allows to have structs like this:

#include <stdint.h>

typedef union {
  struct {
    uint32_t    a  : 16;
    uint32_t    b  : 16;
    uint32_t    c  : 8;
    uint32_t    d  : 8;
    uint32_t    e  : 16;
    uint32_t    f  : 32;
    uint32_t    g  : 32;
  } Bits;
  struct {
    uint64_t    Uint64;
    uint64_t    Uint64_1;
  } Uint128;
} S;

Currently it's converted like this:

union S
{
    struct _Anonymous_0
    {
        import std.bitmanip : bitfields;

        mixin(bitfields!(
            uint, "a", 16,
            uint, "b", 16,
            uint, "c", 8,
            uint, "d", 8,
            uint, "e", 16,
            uint, "f", 32,
            uint, "g", 32));
    }

    _Anonymous_0 Bits;

    struct _Anonymous_1
    {
        ulong Uint64;
        ulong Uint64_1;
    }

    _Anonymous_1 Uint128;
}

But unfortunately D doesn't support bitfields wider than 64-bits:

/usr/include/dlang/ldc/std/bitmanip.d(92): Error: shift by 64 is outside the range `0..63`
        enum ulong maskAllElse = ((~0uL) >> (64 - len)) << offset;
                                                        ^
/usr/include/dlang/ldc/std/bitmanip.d(180): Error: template instance `std.bitmanip.createAccessors!("_a_b_c_d_e_f_g_bf", uint, "f", 32LU, 64LU)` error instantiating
            = createAccessors!(store, Ts[0], Ts[1], Ts[2], offset)

This PR fixes this issue by splitting such cases. Producing D code:

union S
{
    struct _Anonymous_0
    {
        import std.bitmanip : bitfields;

        mixin(bitfields!(
            uint, "a", 16,
            uint, "b", 16,
            uint, "c", 8,
            uint, "d", 8,
            uint, "e", 16));

        mixin(bitfields!(
            uint, "f", 32,
            uint, "g", 32));
    }

    _Anonymous_0 Bits;

    struct _Anonymous_1
    {
        ulong Uint64;
        ulong Uint64_1;
    }

    _Anonymous_1 Uint128;
}

Copilot AI review requested due to automatic review settings March 19, 2026 21:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the C→D record translator to support C bitfield sequences whose total width exceeds 64 bits by splitting them into multiple std.bitmanip.bitfields mixins, avoiding D compilation errors when offsets would reach 64.

Changes:

  • Split translated bitfield lists into multiple bitfields! mixins when the cumulative width would exceed 64 bits.
  • Preserve existing codegen behavior for <=64-bit bitfield sequences while enabling wider overall sequences to compile.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread dstep/translator/Record.d Outdated
Comment thread dstep/translator/Record.d Outdated
@jacob-carlborg

Copy link
Copy Markdown
Owner

Perhaps time to move to the built in syntax for bit fields.

@jacob-carlborg jacob-carlborg left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no test case.

@davispuh

Copy link
Copy Markdown
Author

Okay so I completely rewrote this PR like 4 times till ended up at this version because I kept finding issues.
Turns out to get struct layout that would match C using std.bitmanip.bitfields is very complex and essentially yeah it's just not worth it so now just use native bitfields.
Only there is case for __int128 which supporting would also be incredibly complex because we use Cent which can't be bitpacked like in C. It is actually possible to implement with unions and sub-structs but it's like 1k code lines of complex logic just for that so I dropped it.

@davispuh

davispuh commented Jun 21, 2026

Copy link
Copy Markdown
Author

In case someone ever wants to support 128-bitfields here are some edge case examples:

struct Foo {
    int a : 30;
    long b : 34;
    __int128 c : 62;
};

struct Foo {
    __int128 a : 100;
    int b : 20;
};

struct Foo {
    int a : 30;
    long b : 35;
    __int128 c : 87;
    int d : 21;
};

struct Foo {
    __int128 a : 50;
    __int128 b : 50;
};

struct Foo {
    __int128 a : 100;
    int b : 20;
    short c : 4;
};

struct Foo {
    __int128 a : 50;
    int : 14;
    int b : 10;
    __int128 c : 62;
};

davispuh added 3 commits June 22, 2026 21:28
…tmanip.bitfields

This is necessary so that D struct layouts match exactly with C layouts.
If we would use std.bitmanip.bitfields then it's layout wouldn't match C.
It is possible to implement std.bitmanip.bitfields in a way to match C layout
but that's a lot of quite complex code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants