About detailed rule of fastcall

Hello.
I’m trying to understand how fastcall works.

As far as I know, first two integer type arguments are passed in ecx and edx.
I tested several test, but the result was different from what I expected

#1
typedef struct _data_t {
int d;
} data_t;

void attribute((fastcall)) test_suuu(data_t s, unsigned int a, unsigned int b, unsigned int c);

unsigned int a is passed in edx, and the rest are passed in stack (in s, b, c order).

#2 : order of arguments is diff from #1
void attribute((fastcall)) test_usuu(unsigned int a, data_t s, unsigned int b, unsigned int c);

unsigned int a is passed in ecx, and the rest are passed in stack (in s, b, c order)

#3 : attribute((packed)) is diff from #1
typedef struct attribute((packed)) _data_t3 {
int d;
} data_t3;

void attribute((fastcall)) test_s3uu(data_t3 s3, unsigned int a, unsigned int b);

unsigned int a is passed in ecx, and the rest are passed in stack (in s, b, c order)

My questions are as follow

  • why struct s in first test is passed in edx not ecx?
  • why unsigned b is in second test is passed in stack not edx?
  • why ‘packed’ makes a difference?
  • where can I find relevant code in llvm?

I’m using i586, linux, llvm 5.02
Build with “clang++ -std=c++11 -ggdb -O0 -gdwarf-4 -fno-omit-frame-pointer -fno-optimize-sibling-calls -o ref_test ref_test.cpp”
(All arguments are used in the function so that they are not optimized out)
Tested with gdb
(gdb) break* test_suuu
(gdb) x/6wx $esp
(gdb) i r

  1. probably for GCC compatibility
  2. probably the same
  3. Hard to say, this could be a bug, compare with GCC
  4. This behavior was implemented in this change: https://reviews.llvm.org/rC166538 You can find the logic for this in clang today.

I also noticed that fastcall behaves differently when we target MSVC compatibility.

I think clang behaves differently than gcc.

When the first argument is ‘struct’ and the second argument is ‘int’, gcc put the second argument ‘int’ in register edx, but clang sometimes in edx, and sometimes in ecx.
e.g. void attribute((fastcall)) test(struct, int)

I tested with follow structures
typedef struct _sti { // gcc: edx, clang: edx
int i;
} sti;

typedef struct _sts { // gcc: edx, clang: ecx
short s;
} sts;

typedef struct attribute((packed)) _stss { // gcc: edx, clang: ecx
short s1;
short s2;
} stss;

typedef struct _st1 { // gcc: edx, clang: ecx
char str[1];
} st1;