jmeubank / tdm-gcc

TDM-GCC is a cleverly disguised GCC compiler for Windows!
https://jmeubank.github.io/tdm-gcc/
572 stars 49 forks source link

bitfield aligned when it should not be #50

Closed Malcolm-Cohen closed 2 years ago

Malcolm-Cohen commented 2 years ago

If a bitfield is preceded by a non-64-bit-aligned member that is not itself a bitfield, the bitfield gets 64-bit-aligned instead of beginning immediately after the previous member (which is what should happen). Actually, it also happens at least sometimes when the preceding member is a bitfield, but I have not investigated fully just what the conditions are.

This bug does not occur on much older gccs on Windows (e.g. mingw64's 4.5.3). It does not happen with any gcc on Linux to my knowledge (tried various, up to 11.2.1). It happens with tdm-gcc and msys2 gcc.

Here is a program that demonstrates the problem. This prints "... ok" three times on e.g. Linux (or with older gccs), and "FAIL ..." with tdm-gcc and msys2 gcc.

include

include

include

include

typedef struct s { uint64_t bottom; uint32_t middle; uint16_t top; unsigned exponent:15; unsigned sign:1; } Ieee128_uint_native; int main() { union { unsigned char x[24]; struct s y; } z; if (offsetof(struct s,bottom)==0 && offsetof(struct s,middle)==8 && offsetof(struct s,top)==12 && sizeof(Ieee128_uint_native)==16) printf("offsets and sizes ok\n"); else printf("FAIL offsets or sizes wrong\n"); printf("%d %d %d %d\n",(int)offsetof(struct s,bottom),(int)offsetof(struct s,middle), (int)offsetof(struct s,top),(int)sizeof(Ieee128_uint_native)); memset(&z,0,sizeof(z)); z.y.bottom = 13; if (z.x[0]!=13) printf("FAIL not little-endian\n"); z.y.bottom = 0; z.y.sign = 1; if (z.x[15]!=0x80) printf("FAIL sign bit not set in the correct place\n"); else printf("sign bit ok\n"); if (z.x[16]|z.x[17]|z.x[18]|z.x[19]|z.x[20]|z.x[21]|z.x[22]|z.x[23]) printf("FAIL high bits set\n"); printf("%x %x %x %x %x %x %x %x : %x %x %x %x %x %x %x %x :: %x %x %x %x\n", z.x[0],z.x[1],z.x[2],z.x[3],z.x[4],z.x[5],z.x[6],z.x[7], z.x[8],z.x[9],z.x[10],z.x[11],z.x[12],z.x[13],z.x[14],z.x[15], z.x[16],z.x[17],z.x[18],z.x[19],z.x[20],z.x[21],z.x[22],z.x[23]); memset(&z,0,sizeof(z)); z.y.exponent = 7; if (z.x[14]!=7) printf("FAIL exponent bits not set in the correct place\n"); else printf("exponent bits ok\n"); if (z.x[16]|z.x[17]|z.x[18]|z.x[19]|z.x[20]|z.x[21]|z.x[22]|z.x[23]) printf("FAIL high bits set\n"); printf("%x %x %x %x %x %x %x %x : %x %x %x %x %x %x %x %x :: %x %x %x %x\n", z.x[0],z.x[1],z.x[2],z.x[3],z.x[4],z.x[5],z.x[6],z.x[7], z.x[8],z.x[9],z.x[10],z.x[11],z.x[12],z.x[13],z.x[14],z.x[15], z.x[16],z.x[17],z.x[18],z.x[19],z.x[20],z.x[21],z.x[22],z.x[23]); return 0; }

Malcolm-Cohen commented 2 years ago

Ok, it seems that gcc changed the way it behaves on Windows, sometime after 4.5.3...

...there is an option "-mno-ms-bitfields", and apparently that was the default back in 4.5.3 (or the mingw64 build I was using set that as a default), but the default is now -mms-bitfields.

Sorry if I wasted anyone else's time.