This program: #include <stdio.h> struct tree_type { unsigned int precision : 9; }; void *bork(const void *Ty, unsigned Subpart) { printf("Subpart == %08x\n", Subpart); return 0; } const void *TConvertType(tree_type* type) { asm("movl $1104150528, 4(%%esp)" : : ); const void *Ty = 0; return bork(Ty, type->precision); } const void *foo(tree_type* type) { asm("movl $1104150528, 4(%%esp)" : : ); const void *Ty = 0; unsigned S = type->precision; return bork(Ty, S); } int main() { struct tree_type t; t.precision = 1; TConvertType(&t); foo(&t); return 0; } Compiled with "c++ t.c" Should print out: Subpart == 00000001 Subpart == 00000001 But instead prints out: Subpart == 41d00001 Subpart == 00000001 (on my iMac). The problem seems to be that, in the TConvertType function passes "type->precision" as an HI instead of SI to the "bork" function. The asm code is: movl $1104150528, 4(%esp) movl $0, -4(%ebp) movl 8(%ebp), %eax movzwl (%eax), %eax andw $511, %ax movw %ax, 4(%esp) movl -4(%ebp), %eax movl %eax, (%esp) call __Z4borkPKvj for TConvertType, so the %ax isn't putting a full SI onto the stack, leaving garbage in the MSBs of 4(%esp), which "bork" expects to be 0, of course. (See http://gcc.gnu.org/ml/gcc/2007-04/msg00237.html) Dave Korn confirmed this miscompilation: (See http://gcc.gnu.org/ml/gcc/2007-04/msg00239.html) Confirmed on x86/cygwin. I removed the asms just in case they were somehow confusing the compiler but the generated code is still clearly incorrect. 004010a2 <__Z12TConvertTypeP9tree_type>: 4010a2: 55 push %ebp 4010a3: 89 e5 mov %esp,%ebp 4010a5: 83 ec 18 sub $0x18,%esp 4010a8: c7 45 fc 00 00 00 00 movl $0x0,0xfffffffc(%ebp) 4010af: 8b 45 08 mov 0x8(%ebp),%eax 4010b2: 0f b7 00 movzwl (%eax),%eax 4010b5: 66 25 ff 01 and $0x1ff,%ax 4010b9: 66 89 44 24 04 mov %ax,0x4(%esp) 4010be: 8b 45 fc mov 0xfffffffc(%ebp),%eax 4010c1: 89 04 24 mov %eax,(%esp) 4010c4: e8 87 ff ff ff call 401050 <__Z4borkPKvj> 4010c9: c9 leave 4010ca: c3 ret 4010cb: 90 nop Looking at tree dumps for the two functions ConvertType and foo, ;; Function const void* foo(tree_type*) (_Z3fooP9tree_type) const void* foo(tree_type*) (type) { unsigned int S; const void * Ty; void * D.2934; const void * D.2933; <unnamed type> D.2932; # BLOCK 2 # PRED: ENTRY (fallthru) Ty = 0B; D.2932 = type->precision; S = (unsigned int) D.2932; D.2934 = bork (Ty, S); D.2933 = D.2934; return D.2933; # SUCC: EXIT } ;; Function const void* TConvertType(tree_type*) (_Z12TConvertTypeP9tree_type) const void* TConvertType(tree_type*) (type) { const void * Ty; void * D.2926; <unnamed type> D.2925; const void * D.2924; # BLOCK 2 # PRED: ENTRY (fallthru) Ty = 0B; D.2925 = type->precision; D.2926 = bork (Ty, D.2925); D.2924 = D.2926; return D.2924; # SUCC: EXIT } the difference is just in whether the <unnamed type> representing the bitfield is passed directly to bork as a parameter; when forcibly cast via an unsigned, it gets it right. The generated RTL appears to then select a minimal size for <unsigned type> and not take account of argument promotion: ;; Function const void* TConvertType(tree_type*) (_Z12TConvertTypeP9tree_type) ;; Generating RTL for tree basic block 2 ;; Ty = 0B (insn 7 5 0 (set (mem/f/c/i:SI (plus:SI (reg/f:SI 54 virtual-stack-vars) (const_int -4 [0xfffffffc])) [0 Ty+0 S4 A32]) (const_int 0 [0x0])) -1 (nil) (nil)) ;; D.2925 = type->precision (insn 9 7 11 (set (reg/f:SI 62) (mem/f/c/i:SI (reg/f:SI 53 virtual-incoming-args) [0 type+0 S4 A32])) -1 (nil) (nil)) (insn 11 9 0 (parallel [ (set (reg:HI 59 [ D.2925 ]) (and:HI (mem/s:HI (reg/f:SI 62) [0 S2 A32]) (const_int 511 [0x1ff]))) (clobber (reg:CC 17 flags)) ]) -1 (nil) (nil)) ;; D.2926 = bork (Ty, D.2925) (insn 12 11 13 (set (mem:HI (plus:SI (reg/f:SI 56 virtual-outgoing-args) (const_int 4 [0x4])) [0 S2 A32]) (reg:HI 59 [ D.2925 ])) -1 (nil) (nil))
Among other things, this bug causes GCC 4.2 to miscompile LLVM.
struct tree_type { unsigned int precision : 9; }; void bork(unsigned int Subpart); void foo(struct tree_type *t) { bork(t->precision); } we miss the zero extension of the argument (comparing cc1 to cc1plus output) - movzwl %ax, %eax - movl %eax, (%esp) - call bork + movw %ax, (%esp) + call _Z4borkj but it's also different on the tree level in that C uses int and C++ a bitfield type. This is related to PR30332, but unlike that one, this one works with 4.1.x.
I want to say this is related to PR 27979.
radr://5076058
Subject: Bug 31513 Author: mmitchel Date: Tue Apr 17 00:28:21 2007 New Revision: 123902 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=123902 Log: PR c++/31513 * call.c (convert_for_arg_passing): Convert bitfields to their declared types. PR c++/31513 * g++.dg/expr/bitfield8.C: New test. Added: branches/gcc-4_2-branch/gcc/testsuite/g++.dg/expr/bitfield8.C Modified: branches/gcc-4_2-branch/gcc/cp/ChangeLog branches/gcc-4_2-branch/gcc/cp/call.c branches/gcc-4_2-branch/gcc/testsuite/ChangeLog
Fixed in 4.2.0.
Subject: Bug 31513 Author: mmitchel Date: Wed Apr 18 04:36:18 2007 New Revision: 123939 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=123939 Log: PR c++/31513 * call.c (convert_for_arg_passing): Convert bitfields to their declared types. PR c++/31513 * g++.dg/expr/bitfield8.C: New test. Added: trunk/gcc/testsuite/g++.dg/expr/bitfield8.C Modified: trunk/gcc/cp/ChangeLog trunk/gcc/cp/call.c trunk/gcc/testsuite/ChangeLog
Fixed in 4.3.0.