The old code assumed that if an argument doesn't fit into the available
registers, none of the subsequent arguments do, either. But that's
wrong: passing 7 doubles, then a two-double struct, then another double
should generate code that passes the 9th argument in the 8th register
and the two-double struct on the stack. We now do so.
However, this patch does not yet fix the function calling code to do the
right thing in the same case.
The comment suggests this was meant to detect unions, but in fact it
compared f->c, the union/struct size, against f->next->c, the first
element's offset.
This affected only zero-length structs/unions with a first (zero-length)
element, as in this code:
struct u2 {
};
struct u {
struct u2 u2;
} u;
struct u f(struct u x)
{
return x;
}
However, such structures turned out to be broken anyway, as code like this
was generated for the above f:
0000000000000000 <f>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 81 ec 10 00 00 00 sub $0x10,%rsp
b: 66 0f d6 45 f8 movq %xmm0,-0x8(%rbp)
10: 66 0f 6e 45 f8 movd -0x8(%rbp),%xmm0
15: e9 00 00 00 00 jmpq 1a <f+0x1a>
1a: c9 leaveq
1b: c3 retq
With the x86_64 Linux ELF ABI, we're currently failing two of these
three tests, which have been disabled for now. The problem is mixed
structures such as struct { double x; char c; }, which the x86_64 ABI
specifies are to be passed/returned in one integer register and one SSE
register; our current approach, marking the structure as VT_QLONG or
VT_QFLOAT, fails in this case.
(It's possible to fix this by getting rid of VT_QLONG and VT_QFLOAT
entirely as at https://github.com/pipcet/tinycc, but the changes aren't
properly isolated at present. Anyway, there might be a less disruptive
fix.)
I ran into an issue playing with tinycc, and tracked it down to a rather
weird assumption in the function calling code. This breaks only when
varargs and float/double arguments are combined, I think, and only when
calling GCC-generated (or non-TinyCC, at least) code. The problem is we
sometimes generate code like this:
804a468: 4c 89 d9 mov %r11,%rcx
804a46b: b8 01 00 00 00 mov $0x1,%eax
804a470: 48 8b 45 c0 mov -0x40(%rbp),%rax
804a474: 4c 8b 18 mov (%rax),%r11
804a477: 41 ff d3 callq *%r11
for a function call. Note how $eax is first set to the correct value,
then clobbered when we try to load the function pointer into R11. With
the patch, the code generated is:
804a468: 4c 89 d9 mov %r11,%rcx
804a46b: b8 01 00 00 00 mov $0x1,%eax
804a470: 4c 8b 5d c0 mov -0x40(%rbp),%r11
804a474: 4d 8b 1b mov (%r11),%r11
804a477: 41 ff d3 callq *%r11
which is correct.
This becomes an issue when get_reg(RC_INT) is modified not always to
return %rax after a save_regs(0), because then another register (%ecx,
say) is clobbered, and the function passed an invalid argument.
A rather convoluted test case that generates the above code is
included. Please note that the test will not cause a failure because
TinyCC code ignores the %rax argument, but it will cause incorrect
behavior when combined with GCC code, which might wrongly fail to save
XMM registers and cause data corruption.
Verify an immediate value fits into 32 bits before jumping to it/calling
it with a 32-bit immediate operand. Without this fix, code along the
lines of
((int (*)(const char *, ...))140244834372944LL)("hi\n");
will fail mysteriously, even if that decimal constant is the correct
address for printf.
See https://github.com/pipcet/tinycc/tree/bugfix-1