July 24

On Monday, 22 July 2024 at 21:23:04 UTC, claptrap wrote:

>

x86 CPUs favour type alignment not pointer size alignment, bytes 1 align, shorts 2 align and so on.

It's mostly to avoid buggering up the store to load forwarding IIRC. Which is basically a way of making recent writes faster to retrieve when they are loaded again.

Pointer size was me simplifying for the sake of brevity, but I was taught that i386-based processors are generally fastest with 4-byte memory alignment, and AMD64-based processors are generally fastest with 8-byte memory alignment.

July 24

On Sunday, 5 May 2024 at 23:08:29 UTC, Walter Bright wrote:

>

https://github.com/WalterBright/documents/blob/2ec9c5966dccc423a2c4c736a6783d77c255403a/bitfields.md

I’ve spent a long time observing the debate about bitfields in D, so now it’s time for me to give my feedback.

Criticism of Bitfields

Bitfields are an incredibly bare-bones feature that address only a small subset of the difficulties of managing bit-packed data, are difficult to expand upon, and are arbitrarily relegated to a field of an aggregate for maximum inconvenience. The DIP itself even points out that ‘many alternatives are available’ for situations where bitfields aren’t an appropriate solution. This serves as an admission that bitfields are not a very useful feature outside of C interoperability, because programmers expect and want structs to be laid out how they choose, not in some arbitrary way that’s compatible with the conventions of C compilers. You cannot choose how under/overflow are handled, have different types for different collections of bits in the field safely, or even construct a bitfield on its own unless it is wrapped in a dummy struct. I think if we add this version of bitfields to mainline D, then it should only be as a C interoperability feature.

How to Improve

If we want to add a bitfield equivalent to D, let’s make it better than a bitfield in every possible way: let’s make it an aggregate type. I’ll call it ‘bitwise’ as an example:

bitwise Flavour: 4{
  bool: 1 sweet, savoury, bitter;
}

bitwise Col16: ushort{
  uint: 5 red;
  uint: 6 green;
  uint: 5 blue;
}

Here it is slightly modified with some comments so you can understand what’s going on:

bitwise Flavour: 4{ //size is 4 bytes. Without specifying this, the type would be 1 byte because its contents only take 1 byte
  bool: 1 sweet; //1 bit that is interpreted as bool
  //default values for fields, and listing multiple comma-separated fields:
  bool: 1 savoury = true, bitter;
}

bitwise Col16: ushort{ //You can implicitly cast this type to a ushort, so it should be 2 bytes at most
  uint: 5 red; //5 bits that are interpreted as uint
  uint: 6 green;
  uint: 5 blue;
}

How is this better than a bitfield?

Because it’s a type it can be easily referenced, passed to functions without a dummy struct, we can have template pattern matching for them, they can be re-used across structs, can be given constructors & operator overloads (e.g. for custom floats), and can have different ways of handling overflow:

bitwise Example{
  ubyte: 1 a;
//assigning 10 to a: 10 & a.max
//(where a.max is 1 in this case)
  @clamped byte: 2 b;
//assigning 10 to b: clamp(10, signExtend!(b.bitwidth)(b.min), b.max);
//(where b.min/max would be -2/1 in this case)
}

But what about C interoperability? Okay, add extern(C) to an anonymous bitwise and it’ll become a C-interoperable bitfield:

struct S{
  extern(C) bitwise: uint{
    bool: 1 isnothrow, isnogc, isproperty, isref, isreturn, isscope, isreturninferred, Isscopeinferred, inference, islive, incomplete, inoutParam, inoutQual;
    uint: 5 a;
    uint: 3 flags;
  }
}

I think this approach gives us much more room to make this a useful & versatile feature that can be expanded to meet various needs and fit various use-cases.

July 26

On Wednesday, 24 July 2024 at 08:57:40 UTC, IchorDev wrote:

>

On Sunday, 5 May 2024 at 23:08:29 UTC, Walter Bright wrote:

>

https://github.com/WalterBright/documents/blob/2ec9c5966dccc213a2c4c736a6783d77c255403a/bitfields.md

I’ve spent a long time observing the debate about bitfields in D, so now it’s time for me to give my feedback.

Criticism of Bitfields

Bitfields are an incredibly bare-bones feature that address only a small subset of the difficulties of managing bit-packed data, are difficult to expand upon, and are arbitrarily relegated to a field of an aggregate for maximum inconvenience. The DIP itself even points out that ‘many alternatives are available’ for situations where bitfields aren’t an appropriate solution. This serves as an admission that bitfields are not a very useful feature outside of C interoperability, because programmers expect and want structs to be laid out how they choose, not in some arbitrary way that’s compatible with the conventions of C compilers. You cannot choose how under/overflow are handled, have different types for different collections of bits in the field safely, or even construct a bitfield on its own unless it is wrapped in a dummy struct. I think if we add this version of bitfields to mainline D, then it should only be as a C interoperability feature.

How to Improve

If we want to add a bitfield equivalent to D, let’s make it better than a bitfield in every possible way: let’s make it an aggregate type. I’ll call it ‘bitwise’ as an example:

bitwise Flavour: 4{
  bool: 1 sweet, savoury, bitter;
}

bitwise Col16: ushort{
  uint: 5 red;
  uint: 6 green;
  uint: 5 blue;
}

Here it is slightly modified with some comments so you can understand what’s going on:

bitwise Flavour: 4{ //size is 4 bytes. Without specifying this, the type would be 1 byte because its contents only take 1 byte
  bool: 1 sweet; //1 bit that is interpreted as bool
  //default values for fields, and listing multiple comma-separated fields:
  bool: 1 savoury = true, bitter;
}

bitwise Col16: ushort{ //You can implicitly cast this type to a ushort, so it should be 2 bytes at most
  uint: 5 red; //5 bits that are interpreted as uint
  uint: 6 green;
  uint: 5 blue;
}

How is this better than a bitfield?

Because it’s a type it can be easily referenced, passed to functions without a dummy struct, we can have template pattern matching for them, they can be re-used across structs, can be given constructors & operator overloads (e.g. for custom floats), and can have different ways of handling overflow:

bitwise Example{
  ubyte: 1 a;
//assigning 10 to a: 10 & a.max
//(where a.max is 1 in this case)
  @clamped byte: 2 b;
//assigning 10 to b: clamp(10, signExtend!(b.bitwidth)(b.min), b.max);
//(where b.min/max would be -2/1 in this case)
}

But what about C interoperability? Okay, add extern(C) to an anonymous bitwise and it’ll become a C-interoperable bitfield:

struct S{
  extern(C) bitwise: uint{
    bool: 1 isnothrow, isnogc, isproperty, isref, isreturn, isscope, isreturninferred, Isscopeinferred, inference, islive, incomplete, inoutParam, inoutQual;
    uint: 5 a;
    uint: 3 flags;
  }
}

I think this approach gives us much more room to make this a useful & versatile feature that can be expanded to meet various needs and fit various use-cases.

I like it. The only thing that’s odd to me is int: 21 foo, bar. It looks much more like 21 is in line with foo and bar, but it’s to be read as int: 21 foo bar. We could agree to use no space, i.e. int:21, or use something other than :, e.g. int{21}. That looks much more like a type and int{21} foo, bar looks much more like a list of variables declared to be of some type. Essentially, int[n] is a static array of n values of type int, whereas int{n} is a single n-bit signed value. As per int, n is 32 or lower. For bool only bool{1} would be possible (maybe also bool{0}, if we allow 0-length bitfields.

In general, extern(D) bitfields should be allowed to be optimized. An attribute like @packed could indicate that the layout be exactly as specified. And extern(C) can give C compatibility.

July 29

On Friday, 26 July 2024 at 00:08:12 UTC, Quirin Schroll wrote:

>

I like it. The only thing that’s odd to me is int: 21 foo, bar. It looks much more like 21 is in line with foo and bar, but it’s to be read as int: 21 foo bar. We could agree to use no space, i.e. int:21, or use something other than :, e.g. int{21}. That looks much more like a type and int{21} foo, bar looks much more like a list of variables declared to be of some type. Essentially, int[n] is a static array of n values of type int, whereas int{n} is a single n-bit signed value. As per int, n is 32 or lower. For bool only bool{1} would be possible

Good idea about int{21}! The int: 21 syntax was a bit of a cop-out because I couldn’t think of anything better.

>

(maybe also bool{0}, if we allow 0-length bitfields.

No harm in doing so I suppose. Even better if they’re actually 0 bytes, heh.

>

In general, extern(D) bitfields should be allowed to be optimized.

If you mean field reordering, then that did not work with structs, so I’m inclined to view this idea with some healthy skepticism.
First off, we don’t want libraries where every change to a bitwise/bitfield is a breaking change. You might say that the obvious answer to this is to use the ‘don’t ruin my binary compatibility’ @packed attribute, but this is not how the other PoD aggregates work in D, so it might get largely overlooked.
Second, I think we should trust that devs are smart enough to lay their data out themselves.
Field reordering should at least be opt-in, but I’d argue that it’s not even a necessary feature in the first place. It’s something I can’t see myself ever wanting.

July 29

On Monday, 29 July 2024 at 07:53:09 UTC, IchorDev wrote:

>

On Friday, 26 July 2024 at 00:08:12 UTC, Quirin Schroll wrote:

>

I like it. The only thing that’s odd to me is int: 21 foo, bar. It looks much more like 21 is in line with foo and bar, but it’s to be read as int: 21 foo bar. We could agree to use no space, i.e. int:21, or use something other than :, e.g. int{21}. That looks much more like a type and int{21} foo, bar looks much more like a list of variables declared to be of some type. Essentially, int[n] is a static array of n values of type int, whereas int{n} is a single n-bit signed value. As per int, n is 32 or lower. For bool only bool{1} would be possible

Good idea about int{21}! The int: 21 syntax was a bit of a cop-out because I couldn’t think of anything better.

>

(maybe also bool{0}, if we allow 0-length bitfields.

No harm in doing so I suppose. Even better if they’re actually 0 bytes, heh.

>

In general, extern(D) bitfields should be allowed to be optimized.

If you mean field reordering, then that did not work with structs, so I’m inclined to view this idea with some healthy skepticism.

I don’t; I rather meant specification-level optimization, i.e. D should find its own layout which would be fully specified and need not be compatible with C. In that regard, e.g. if you had ubyte{7} a, b (or ubyte{7}[2]), the language could require the bits to be laid out as:

AAAA AAAB BBBB BB00

or

AAAA AAA0 BBBB BBB0

(A: bit part of a; B: bit part of b; 0: padding)

The specification could say: “A block of bit fields occupies exactly the sum number of bits rounded up to the next multiple of 8. The difference in those numbers is called padding bits. If after a bit field a byte boundary is not reached and the next bit field would overstep the next byte boundary and enough padding bits are left to align the next bit field with that byte boundary, exactly that many padding bits are inserted here. Otherwise, bit fields are laid out directly next to each other. Remaining padding bits are inserted at the end of a bit field block.” This is not a suggestion, but a demonstration how a spec can optimize; layout would be predictable.

July 30

On Monday, 29 July 2024 at 13:26:29 UTC, Quirin Schroll wrote:

>

I don’t; I rather meant specification-level optimization, i.e. D should find its own layout which would be fully specified and need not be compatible with C. In that regard, e.g. if you had ubyte{7} a, b (or ubyte{7}[2]), the language could require the bits to be laid out as:

AAAA AAAB BBBB BB00

or

AAAA AAA0 BBBB BBB0

(A: bit part of a; B: bit part of b; 0: padding)

The specification could say: “A block of bit fields occupies exactly the sum number of bits rounded up to the next multiple of 8. The difference in those numbers is called padding bits. If after a bit field a byte boundary is not reached and the next bit field would overstep the next byte boundary and enough padding bits are left to align the next bit field with that byte boundary, exactly that many padding bits are inserted here. Otherwise, bit fields are laid out directly next to each other. Remaining padding bits are inserted at the end of a bit field block.” This is not a suggestion, but a demonstration how a spec can optimize; layout would be predictable.

Ah, thanks for explaining! I always find padding rules to be confusing, but as long as there’s a way to disable padding (w/ align(1)?) I don’t mind.

Also about reserving a new keyword, I was thinking something with an underscore like bits_struct is unlikely to break any existing code, since snake case is not conventionally used for D identifiers.

1 2 3 4 5
Next ›   Last »