tccgen.c: generic char/short promotion for function return values

This commit is contained in:
grischka 2019-09-23 17:45:39 +02:00
parent 89372dc482
commit a64353ce71
5 changed files with 12 additions and 67 deletions

View File

@ -959,11 +959,7 @@ ST_FUNC void gfunc_call(int nb_args)
{ {
int rt = return_type->t; int rt = return_type->t;
int bt = rt & VT_BTYPE; int bt = rt & VT_BTYPE;
if (bt == VT_BYTE || bt == VT_SHORT) if (bt == VT_STRUCT && !(a[0] & 1)) {
// Promote small integers:
o(0x13001c00 | (bt == VT_SHORT) << 13 |
(uint32_t)!!(rt & VT_UNSIGNED) << 30); // [su]xt[bh] w0,w0
else if (bt == VT_STRUCT && !(a[0] & 1)) {
// A struct was returned in registers, so write it out: // A struct was returned in registers, so write it out:
gv(RC_R(8)); gv(RC_R(8));
--vtop; --vtop;

View File

@ -357,34 +357,6 @@ static void gcall_or_jmp(int is_jmp)
o(0xff); /* call/jmp *r */ o(0xff); /* call/jmp *r */
o(0xd0 + r + (is_jmp << 4)); o(0xd0 + r + (is_jmp << 4));
} }
if (!is_jmp) {
int rt;
/* extend the return value to the whole register if necessary
visual studio and gcc do not always set the whole eax register
when assigning the return value of a function */
rt = vtop->type.ref->type.t;
switch (rt & VT_BTYPE) {
case VT_BYTE:
case VT_BOOL:
if (rt & VT_UNSIGNED) {
o(0xc0b60f); /* movzx %al, %eax */
}
else {
o(0xc0be0f); /* movsx %al, %eax */
}
break;
case VT_SHORT:
if (rt & VT_UNSIGNED) {
o(0xc0b70f); /* movzx %ax, %eax */
}
else {
o(0xc0bf0f); /* movsx %ax, %eax */
}
break;
default:
break;
}
}
} }
static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX }; static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX };

View File

@ -5629,6 +5629,12 @@ special_math_val:
} }
vset(&s->type, VT_LOCAL | VT_LVAL, addr); vset(&s->type, VT_LOCAL | VT_LVAL, addr);
} }
/* Promote char/short return values. This is matters only
for calling function that were not compiled by TCC */
t = s->type.t & VT_BTYPE;
if (t == VT_BYTE || t == VT_SHORT || t == VT_BOOL)
vtop->r |= BFVAL(VT_MUSTCAST, 1);
} }
if (s->f.func_noreturn) if (s->f.func_noreturn)
CODE_OFF(); CODE_OFF();

View File

@ -3316,6 +3316,9 @@ void override_func2 (void)
extern int bug_table[] __attribute__((section("__bug_table"))); extern int bug_table[] __attribute__((section("__bug_table")));
char * get_asm_string (void) char * get_asm_string (void)
{ {
#ifdef __i386__
char *str = "(not tested)";
#else
extern int some_symbol; extern int some_symbol;
asm volatile (".globl some_symbol\n" asm volatile (".globl some_symbol\n"
"jmp .+6\n" "jmp .+6\n"
@ -3330,6 +3333,7 @@ char * get_asm_string (void)
"2:\t.long 1b - 2b, %c0 - 2b\n" "2:\t.long 1b - 2b, %c0 - 2b\n"
".popsection\n" : : "i" ("A string")); ".popsection\n" : : "i" ("A string"));
char * str = ((char*)bug_table) + bug_table[1]; char * str = ((char*)bug_table) + bug_table[1];
#endif
return str; return str;
} }

View File

@ -902,23 +902,6 @@ void gfunc_call(int nb_args)
#endif #endif
} }
/* other compilers don't clear the upper bits when returning char/short */
bt = vtop->type.ref->type.t & (VT_BTYPE | VT_UNSIGNED);
if (bt == (VT_BYTE | VT_UNSIGNED) || (bt & VT_TYPE) == VT_BOOL)
o(0xc0b60f); /* movzbl %al, %eax */
else if (bt == VT_BYTE)
o(0xc0be0f); /* movsbl %al, %eax */
else if (bt == VT_SHORT)
o(0x98); /* cwtl */
else if (bt == (VT_SHORT | VT_UNSIGNED))
o(0xc0b70f); /* movzbl %al, %eax */
#if 0 /* handled in gen_cast() */
else if (bt == VT_INT)
o(0x9848); /* cltq */
else if (bt == (VT_INT | VT_UNSIGNED))
o(0xc089); /* mov %eax,%eax */
#endif
vtop--; vtop--;
} }
@ -1278,7 +1261,7 @@ void gfunc_call(int nb_args)
{ {
X86_64_Mode mode; X86_64_Mode mode;
CType type; CType type;
int size, align, r, args_size, stack_adjust, i, reg_count, bt; int size, align, r, args_size, stack_adjust, i, reg_count;
int nb_reg_args = 0; int nb_reg_args = 0;
int nb_sse_args = 0; int nb_sse_args = 0;
int sse_reg, gen_reg; int sse_reg, gen_reg;
@ -1466,25 +1449,9 @@ void gfunc_call(int nb_args)
gcall_or_jmp(0); gcall_or_jmp(0);
if (args_size) if (args_size)
gadd_sp(args_size); gadd_sp(args_size);
/* other compilers don't clear the upper bits when returning char/short,
TCC does so for convenience. When we'd stay purely within TCC compiled
code we wouldn't need this, but for compatibility we have to extend.
Ideally TCC wouldn't extend at return statements to not do double
extensions, or would understand sub-int types during expression
evaluation. */
bt = vtop->type.ref->type.t & (VT_BTYPE | VT_UNSIGNED);
if (bt == (VT_BYTE | VT_UNSIGNED) || (bt & VT_TYPE) == VT_BOOL)
o(0xc0b60f); /* movzbl %al, %eax */
else if (bt == VT_BYTE)
o(0xc0be0f); /* movsbl %al, %eax */
else if (bt == VT_SHORT)
o(0x98); /* cwtl */
else if (bt == (VT_SHORT | VT_UNSIGNED))
o(0xc0b70f); /* movzwl %al, %eax */
vtop--; vtop--;
} }
#define FUNC_PROLOG_SIZE 11 #define FUNC_PROLOG_SIZE 11
static void push_arg_reg(int i) { static void push_arg_reg(int i) {