22 ms·
> shift-and-mask operations on unsigned machine words are always better ... but that's what a bit field is?
by chrisseaton 5y ago
> shift-and-mask operations on unsigned machine words are always better
... but that's what a bit field is?
- layer8 5y agoBit fields are a specific C language feature, allowing to treat bit slices as a unit whose length doesn’t need to correspond to one of the integer types. See for example https://docs.microsoft.com/en-us/cpp/c-language/c-bit-fields?view=msvc-170 https://docs.microsoft.com/en-us/cpp/c-language/c-bit-fields....
- chrisseaton 5y agoYeah they compile to the same machine code operations though. If those machine code operations aren't right as a bitfield then they aren't going to be right done manually either. https://godbolt.org/z/csvTx89EG https://godbolt.org/z/csvTx89EG
- layer8 5y agoBit field support being buggy exactly means that they don’t compile to the same machine code as the bit shifting/masking code you would write by hand (if your hand-written code is correct).
- minipci1321 5y ago> Yeah they compile to the same machine code operations though. Not always. Switch your example to AARCH64 and check out the BFI instruction.
- alcover 5y agoIs it not rather void bar(struct y *s, unsigned int foo) { s->c = (s->c & 0xf0) | foo; }
- tom_ 5y agoARM is little-endian, and by tradition bitfield bit indexes are assigned from least significant (bit 0 in ARM terms) to more significant. b occupies bits 4-7 inclusive.
- jcranmer 5y agoThat's not what a bit field is. A bit field (in C/C++) is a weird object type that can only exist in a structure or union type, which kind of acts like an underlying regular integral type except for those situations where it does not. For an example of why compilers might have issues compiling bit fields properly (although this requires C++, since C's ternary operator works on rvalues, not lvalues): struct A { int x: 3; int y: 5 } a; (choice ? a.x : a.y) = val; Enjoy making that codegen work properly.
- mwint 5y agoCan someone explain further why this is so hard? I’m not familiar enough with any of this to understand without some help.
- ncmncm 5y agoIt is just very finicky, with myriad edge cases easy to get wrong, and even easier to neglect to have complete tests for. Each target CPU design and version has quirks. Many involve sign extension.
- jstimpfle 5y agoExtracting a subrange of bits and shifting them to the beginning is not exactly rocket science.
- ncmncm 5y agoExperience indicates otherwise. If you must use bit fields, make them unsigned. Bugs love to hide under signed bit fields.
- jstimpfle 5y agoThat makes sense, looking at how signed arithmetic works on different architectures it would feel strange to use signed bitfields. Unsigned bitfields are a nice way to get modular arithmetic with n bits without syntactic clutter.
- jmwilson 5y agoWith shifts and masks you know where the bits are. With bitfields, you don't because the specification leaves everything up to the compiler. struct foo { char a : 4; char b : 4; }; Is a in the high-order 4 bits, or the lower 4 bits? Both choices are allowed, so it's up to the compiler and makes the code non-portable.
- deleted 5y ago[deleted]
- dmitrygr 5y agosometimes you do not care, and x = foo.a is simpler than x = (foo & FOO_MASK_A) >> FOO_SHIFT_A and for assignments, the difference is even bigger: foo.a = x is much better than foo = (foo &~ FOO_MASK_A) | ((a << FOO_SHIFT_A) & FOO_MASK_A)
- zozbot234 5y agoIf "foo" is defined as part of an API/ABI that's used in multiple compile units you will always care, since otherwise a random change in "implementation defined" bitfield encodings on some obscure architecture might break your build. Bitfields are a misfeature in most real-world cases.
- InitialLastName 5y agoThe case where a) you don't care about the in-memory representation of your struct and b) you care a lot about being able to pack into the absolute minimum memory space, but not enough to make sure the compiler actually packs the fields (depending on architecture and optimization settings, they might not!) is vanishingly small. The more frequent perceived use for bit-fields (in the situation where they actually work) is to pack into a serialized data format, such that memory or a data stream can be accessed elsewhere. In that case, "the compiler can do whatever it wants with your data packing" is pretty useless, since your "elsewhere" might have a different compiler that does a totally different thing.
- 5y ago
- ncmncm 5y agoPhysically, yes. The difference is whether you let the compiler generate and hide the shift-and-mask ops, or code them by hand. Normally it is better to leave details to the compiler. This is the exception to that rule. A result of people avoiding declaring bit fields in serious use cases has been that compiler vendors didn't worry too much about bitfield codegen bugs. Probably Gcc and Clang are OK on x86, by now. But that does not carry to, e.g., obscure microcontrollers. Heaven help you if your bit field members are supposed to correspond to hardware register sub-fields.
- iainmerrick 5y agoThe same applies to structs in general, not just bitfields.
- ncmncm 5y agoCommon experience is that compilers, ABIs, and instruction implementations get ordinary struct fields right.