From c8ca64d28ba107f652f3ee24a4deb8e93609b9ca Mon Sep 17 00:00:00 2001 From: Michael Matz Date: Tue, 17 Dec 2019 01:46:06 +0100 Subject: [PATCH] Adjust return value promotion for some archs this is a bit complicated: for i386 and x86-64 we really need to extend return values ourself, as the common code now does. For arm64 this at least preserves old behaviour. For riscv64 we don't have to extend ourself but can expect things to be extended up to int (this matters for var-args tests, when the sign-extension to int64 needs to happen explicitely). As the extensions are useless, don't do them. And for arm32 we actually can't express GCC behaviour: the callee side expects the return value to be correctly extended to int32, but remembers the original type. In case the ultimate target type for the call result is only int, no further extension is done. But in case the target type is e.g. int64 an extension happens, but not from int32 but from the original type. We don't know the ultimate target type, so we have to choose a type to put into vtop: * original type (plus VT_MUSTCAST) - this looses when the ultimate target is int (GCC: no cast, TCC: a cast) * int (without MUSTCAST) - this looses when the ultimate target is int64 (GCC: cast from original type, TCC: cast from int) This difference can only be seen with undefined sources, like the testcases, so it doesn't seem worthwhile to try an make it work, just disable the test on arm and choose the second variant as that generates less code. --- arm64-gen.c | 3 +++ i386-gen.c | 4 ++++ tccgen.c | 12 ++++++++++-- tests/tcctest.c | 14 ++++++-------- x86_64-gen.c | 3 +++ 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/arm64-gen.c b/arm64-gen.c index 5ea47db..35538d1 100644 --- a/arm64-gen.c +++ b/arm64-gen.c @@ -40,6 +40,9 @@ #define CHAR_IS_UNSIGNED +/* define if return values need to be extended explicitely + at caller side (for interfacing with non-TCC compilers) */ +#define PROMOTE_RET /******************************************************/ #else /* ! TARGET_DEFS_ONLY */ /******************************************************/ diff --git a/i386-gen.c b/i386-gen.c index 8b81c66..7e1e0f4 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -71,6 +71,10 @@ enum { /* maximum alignment (for aligned attribute support) */ #define MAX_ALIGN 8 +/* define if return values need to be extended explicitely + at caller side (for interfacing with non-TCC compilers) */ +#define PROMOTE_RET + /******************************************************/ #else /* ! TARGET_DEFS_ONLY */ /******************************************************/ diff --git a/tccgen.c b/tccgen.c index 0475b3c..0f3d96e 100644 --- a/tccgen.c +++ b/tccgen.c @@ -5631,10 +5631,18 @@ special_math_val: } /* Promote char/short return values. This is matters only - for calling function that were not compiled by TCC */ + for calling function that were not compiled by TCC and + only on some architectures. For those where it doesn't + matter we expect things to be already promoted to int, + but not larger. */ t = s->type.t & VT_BTYPE; - if (t == VT_BYTE || t == VT_SHORT || t == VT_BOOL) + if (t == VT_BYTE || t == VT_SHORT || t == VT_BOOL) { +#ifdef PROMOTE_RET vtop->r |= BFVAL(VT_MUSTCAST, 1); +#else + vtop->type.t = VT_INT; +#endif + } } if (s->f.func_noreturn) CODE_OFF(); diff --git a/tests/tcctest.c b/tests/tcctest.c index 15d849c..373eb95 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -1210,13 +1210,7 @@ void struct_test() /* simulate char/short return value with undefined upper bits */ static int __csf(int x) { return x; } static void *_csf = __csf; -#ifdef __TINYC__ #define csf(t,n) ((t(*)(int))_csf)(n) -#define csfb csf -#else /* arm gcc maybe doesn't promote function return values */ -#define csf(t,n) (t) n -#define csfb(t,n) (t) (n & 255) -#endif /* XXX: depend on endianness */ void char_short_test() @@ -1257,14 +1251,18 @@ void char_short_test() var4 = 0x11223344aa998877ULL; printf("promote char/short assign VA %d %d\n", var3 = var1 + 1, var3 = var4 + 1); printf("promote char/short cast VA %d %d\n", (char)(var1 + 1), (char)(var4 + 1)); +#if !defined(__arm__) + /* We can't really express GCC behaviour of return type promotion in + the presence of undefined behaviour (like __csf is). */ var1 = csf(unsigned char,0x89898989); var4 = csf(signed char,0xabababab); printf("promote char/short funcret %d "LONG_LONG_FORMAT"\n", var1, var4); printf("promote char/short fumcret VA %d %d %d %d\n", csf(unsigned short,0xcdcdcdcd), csf(short,0xefefefef), - csfb(_Bool,0x33221100), - csfb(_Bool,0x33221101)); + csf(_Bool,0x33221100), + csf(_Bool,0x33221101)); +#endif var3 = -10; var1 = (char)(unsigned char)(var3 + 1); var4 = (char)(unsigned char)(var3 + 1); diff --git a/x86_64-gen.c b/x86_64-gen.c index fd2d693..439fd98 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -102,6 +102,9 @@ enum { /* maximum alignment (for aligned attribute support) */ #define MAX_ALIGN 16 +/* define if return values need to be extended explicitely + at caller side (for interfacing with non-TCC compilers) */ +#define PROMOTE_RET /******************************************************/ #else /* ! TARGET_DEFS_ONLY */ /******************************************************/