Automatic swap of function arguments

Today I was realy surprised by the following example in C:

#include <stdio.h>

void
app(void f(int x0, double y1)) { f(100, 101.0); }

void
foo(int x0, double y1)
{
fprintf(stdout, “foo: x0 = %d and y1 = %f\n”, x0, y1);
}
void
foo2(double y1, int x0)
{
fprintf(stdout, “foo2: x0 = %d and y1 = %f\n”, x0, y1);
}

int main()
{

app(&foo); // output: x0 = 100 and y1 = 101.000000
app(&foo2); // it actually works // the output is the same!

return (0);

}

Although foo and foo2 have different C-types, one can actually cast one to
the other without
causing any problems. This is amazing! When I first saw a similar example
generated from ATS
source, I thought it was a bug in the ATS compiler :slight_smile:

Scary

Oh, gcc is doing the swap. Interesting. Now if only it could do the Yoda dance to protect against

if (a = 0)

If parameters are passed in registers, then this behavior
is easy to understand because the code for foo is just the same
as the code for foo2:

x0 is mapped to the same integer register
y1 is mapped to the same float point register.

However, the parametes of C functions are stack-allocated. This is
why I got puzzled.

By studying the assembly code generated by gcc, I think this is what
happens at run-time:

first integer argument is always in %edi
first double argument is always in %xmm

They are not passed on stack but there is memory on stack reserved
for these arguments. Say you use a pointer to the first double argument.
Then the compiler generates code to move the content of %xmm into the memory
reserved for the first double argument.

This strategy is used by gcc. Whatever used by gcc is really a de facto
standard; so everyone
else does the same thing (clang, tcc, etc).On Sunday, January 3, 2016 at 8:17:28 AM UTC-5, Jyun-Yan You wrote:

Compiler doesn’t swap arguments.
It works because doubles are passed to different set of registers (xmm) on
x86-64.
If it compiles with -m32, result is wrong.

On Saturday, January 2, 2016 at 10:13:56 AM UTC+8, gmhwxi wrote:

Today I was realy surprised by the following example in C:

#include <stdio.h>

void
app(void f(int x0, double y1)) { f(100, 101.0); }

void
foo(int x0, double y1)
{
fprintf(stdout, “foo: x0 = %d and y1 = %f\n”, x0, y1);
}
void
foo2(double y1, int x0)
{
fprintf(stdout, “foo2: x0 = %d and y1 = %f\n”, x0, y1);
}

int main()
{

app(&foo); // output: x0 = 100 and y1 = 101.000000
app(&foo2); // it actually works // the output is the same!

return (0);

}

Although foo and foo2 have different C-types, one can actually cast one
to the other without
causing any problems. This is amazing! When I first saw a similar example
generated from ATS
source, I thought it was a bug in the ATS compiler :slight_smile:

Compiler doesn’t swap arguments.
It works because doubles are passed to different set of registers (xmm) on
x86-64.
If it compiles with -m32, result is wrong.On Saturday, January 2, 2016 at 10:13:56 AM UTC+8, gmhwxi wrote:

Today I was realy surprised by the following example in C:

#include <stdio.h>

void
app(void f(int x0, double y1)) { f(100, 101.0); }

void
foo(int x0, double y1)
{
fprintf(stdout, “foo: x0 = %d and y1 = %f\n”, x0, y1);
}
void
foo2(double y1, int x0)
{
fprintf(stdout, “foo2: x0 = %d and y1 = %f\n”, x0, y1);
}

int main()
{

app(&foo); // output: x0 = 100 and y1 = 101.000000
app(&foo2); // it actually works // the output is the same!

return (0);

}

Although foo and foo2 have different C-types, one can actually cast one to
the other without
causing any problems. This is amazing! When I first saw a similar example
generated from ATS
source, I thought it was a bug in the ATS compiler :slight_smile:

It seems that gcc rearranges the order of arguments according to
their types. I also tried clang and tcc, both of which do the same.

Note that this example would not work if ‘double’ was changed to ‘int’.On Sunday, January 3, 2016 at 12:25:22 AM UTC-5, Mike Jones wrote:

Is the compiler matching value names or types?

Is the compiler matching value names or types?