The x86_64 compiler (verified in gcc 3.4.3, configured for x86_64-redhat-linux) chooses a different parameter passing method when passing a struct value, depending upon whether a contained 16 bit bit field is declared as either an 'int' or a 'short int'.
Given the following test case: -------------------- typedef struct shared_ptr_struct { unsigned long phase : 48; unsigned int thread : 16; void *addr; } shared_ptr_t; int y; shared_ptr_t sp = {1, 2, &y}; int get_thread (shared_ptr_t p) { return p.thread; } int main() { int t = get_thread (sp); return t; } -------------------- A value of this type will be passed in two registers: typedef struct shared_ptr_struct { unsigned long phase : 48; unsigned short thread : 16; void *addr; } shared_ptr_t; movq sp(%rip), %rdi movq sp+8(%rip), %rsi call get_thread but a value of this type will be passed on the stack: typedef struct shared_ptr_struct { unsigned long phase : 48; unsigned int thread : 16; void *addr; } shared_ptr_t; movq sp(%rip), %rax movq %rax, (%rsp) movq sp+8(%rip), %rax movq %rax, 8(%rsp) call get_thread Is there anything about the ABI or C language definition that dictates the difference in choice of parameter value passing method, or should both struct definitions lead to the same choice of parameter passing method? Assuming these two declarations should pass a value of type shared_ptr_t in the same fashion, I think the error originates in classify_argument in config/i386/i386.c: static int classify_argument (enum machine_mode mode, tree type, enum x86_64_reg_class classes[MAX_CLASSES], int bit_offset) { HOST_WIDE_INT bytes = (mode == BLKmode) ? int_size_in_bytes (type) : (int) GET_MODE_SIZE (mode); int words = (bytes + (bit_offset % 64) / 8 + UNITS_PER_WORD - 1) / UNITS_PER_WORD; when the thread field is declared as 'short', the mode will be HImode. However, if thread is declared as type 'int', the mode will be SImode. In the 'int' case the value for bytes is 4, and the value for words is 2, because threads is not aligned on a 32 bit boundary. Eventually, the logic will return here: /* Misaligned fields are always returned in memory. */ if (bit_offset % mode_alignment) return 0; which tells the caller that the type of interest must be passed in memory (on the stack) rather than in a register. It looks as if either classify_argument is using the wrong computation for the value of 'bytes', or earlier in the declaration of the processing of 'threads' when declared as an 'int', the compiler should use the smallest sized integer type that will accommodate the field (ie, a short int). Also posted to the gcc mailing list: http://gcc.gnu.org/ml/gcc/2005-01/msg01221.html -- Summary: x86_64 - inconsistent choice of parameter passing method for 16 byte struct Product: gcc Version: 3.4.3 Status: UNCONFIRMED Severity: normal Priority: P2 Component: c AssignedTo: unassigned at gcc dot gnu dot org ReportedBy: gary at intrepid dot com CC: gcc-bugs at gcc dot gnu dot org GCC build triplet: x86_64-redhat-linux http://gcc.gnu.org/bugzilla/show_bug.cgi?id=19566