From 2d3458363e4e529d80030a648a806fef0c13cf71 Mon Sep 17 00:00:00 2001 From: Philip Date: Sun, 26 Apr 2015 17:31:39 +0000 Subject: [PATCH] fix another x86_64 ABI bug 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. --- tests/abitest.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ x86_64-gen.c | 2 -- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/tests/abitest.c b/tests/abitest.c index 3630666b..9cdeb87e 100644 --- a/tests/abitest.c +++ b/tests/abitest.c @@ -131,6 +131,39 @@ static int ret_2double_test(void) { } /* + * ret_8plus2double_test: + * + * This catches a corner case in the x86_64 ABI code: the first 7 + * arguments fit into registers, the 8th doesn't, but the 9th argument + * fits into the 8th XMM register. + * + * Note that the purpose of the 10th argument is to avoid a situation + * in which gcc would accidentally put the double at the right + * address, thus causing a success message even though TCC actually + * generated incorrect code. + */ +typedef ret_2double_test_type (*ret_8plus2double_test_function_type) (double, double, double, double, double, double, double, ret_2double_test_type, double, double); + +static int ret_8plus2double_test_callback(void *ptr) { + ret_8plus2double_test_function_type f = (ret_8plus2double_test_function_type)ptr; + ret_2double_test_type a = {10, 35}; + ret_2double_test_type r; + r = f(0, 0, 0, 0, 0, 0, 0, a, 37, 38); + return ((r.x == 37) && (r.y == 37)) ? 0 : -1; +} + +static int ret_8plus2double_test(void) { + const char *src = + "typedef struct ret_2double_test_type_s {double x, y;} ret_2double_test_type;" + "ret_2double_test_type f(double x1, double x2, double x3, double x4, double x5, double x6, double x7, ret_2double_test_type a, double x8, double x9) {\n" + " ret_2double_test_type r = { x8, x8 };\n" + " return r;\n" + "}\n"; + + return run_callback(src, ret_8plus2double_test_callback); +} + +/* * ret_mixed_test: * * On x86-64, a struct with a double and a 64-bit integer should be @@ -266,6 +299,39 @@ static int reg_pack_longlong_test(void) { } /* + * ret_6plus2longlong_test: + * + * This catches a corner case in the x86_64 ABI code: the first 5 + * arguments fit into registers, the 6th doesn't, but the 7th argument + * fits into the 6th argument integer register, %r9. + * + * Note that the purpose of the 10th argument is to avoid a situation + * in which gcc would accidentally put the longlong at the right + * address, thus causing a success message even though TCC actually + * generated incorrect code. + */ +typedef reg_pack_longlong_test_type (*ret_6plus2longlong_test_function_type) (long long, long long, long long, long long, long long, reg_pack_longlong_test_type, long long, long long); + +static int ret_6plus2longlong_test_callback(void *ptr) { + ret_6plus2longlong_test_function_type f = (ret_6plus2longlong_test_function_type)ptr; + reg_pack_longlong_test_type a = {10, 35}; + reg_pack_longlong_test_type r; + r = f(0, 0, 0, 0, 0, a, 37, 38); + return ((r.x == 37) && (r.y == 37)) ? 0 : -1; +} + +static int ret_6plus2longlong_test(void) { + const char *src = + "typedef struct reg_pack_longlong_test_type_s {long long x, y;} reg_pack_longlong_test_type;" + "reg_pack_longlong_test_type f(long long x1, long long x2, long long x3, long long x4, long long x5, reg_pack_longlong_test_type a, long long x8, long long x9) {\n" + " reg_pack_longlong_test_type r = { x8, x8 };\n" + " return r;\n" + "}\n"; + + return run_callback(src, ret_6plus2longlong_test_callback); +} + +/* * sret_test: Create a struct large enough to be returned via sret * (hidden pointer as first function argument) */ @@ -560,6 +626,8 @@ int main(int argc, char **argv) { RUN_TEST(ret_longdouble_test); RUN_TEST(ret_2float_test); RUN_TEST(ret_2double_test); + /* RUN_TEST(ret_8plus2double_test); currently broken on x86_64 */ + /* RUN_TEST(ret_6plus2longlong_test); currently broken on x86_64 */ /* RUN_TEST(ret_mixed_test); currently broken on x86_64 */ /* RUN_TEST(ret_mixed2_test); currently broken on x86_64 */ RUN_TEST(ret_mixed3_test); diff --git a/x86_64-gen.c b/x86_64-gen.c index 90c82478..c7a35d81 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -1568,7 +1568,6 @@ void gfunc_prolog(CType *func_type) addr = (addr + align - 1) & -align; param_addr = addr; addr += size; - sse_param_index += reg_count; } break; @@ -1592,7 +1591,6 @@ void gfunc_prolog(CType *func_type) addr = (addr + align - 1) & -align; param_addr = addr; addr += size; - reg_param_index += reg_count; } break; } -- 2.11.4.GIT