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 -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20190920/326a8774/attachment.html>
Reid Kleckner via llvm-dev
2019-Sep-20 20:16 UTC
[llvm-dev] About detailed rule of fastcall
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. On Fri, Sep 20, 2019 at 3:29 AM jy ban via llvm-dev <llvm-dev at lists.llvm.org> wrote:> 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 > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20190920/602090e1/attachment.html>
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; ~ typedef struct _st4 { // gcc: edx, clang: ecx char str[4]; } st4; 2019년 9월 21일 (토) 오전 5:16, Reid Kleckner <rnk at google.com>님이 작성:> 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. > > > > On Fri, Sep 20, 2019 at 3:29 AM jy ban via llvm-dev < > llvm-dev at lists.llvm.org> wrote: > >> 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 >> _______________________________________________ >> LLVM Developers mailing list >> llvm-dev at lists.llvm.org >> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >> >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20190924/e2b39612/attachment.html>