the ABIs (and other compilers) extend sub-int return values in the caller. TCC extends them in the callee. For compatibility with those other compilers we have extend them in the caller as well. That introduces a useless double extension in pure TCC-compiled code, but fixing that generally requires that the code generator of TCC would understand sub-int types. For the time being bite the bullet.
Unified · Split
| @@ -369,6 +369,7 @@ static void gcall_or_jmp(int is_jmp) · 368 unmodified lines | |||
| 369 | rt = vtop->type.ref->type.t; | 369 | rt = vtop->type.ref->type.t; |
| 370 | switch (rt & VT_BTYPE) { | 370 | switch (rt & VT_BTYPE) { |
| 371 | case VT_BYTE: | 371 | case VT_BYTE: |
| 372 | case VT_BOOL: | ||
| 372 | if (rt & VT_UNSIGNED) { | 373 | if (rt & VT_UNSIGNED) { |
| 373 | o(0xc0b60f); /* movzx %al, %eax */ | 374 | o(0xc0b60f); /* movzx %al, %eax */ |
| 374 | } | 375 | } |
| @@ -920,7 +920,7 @@ void gfunc_call(int nb_args) · 919 unmodified lines | |||
| 920 | 920 | ||
| 921 | /* other compilers don't clear the upper bits when returning char/short */ | 921 | /* other compilers don't clear the upper bits when returning char/short */ |
| 922 | bt = vtop->type.ref->type.t & (VT_BTYPE | VT_UNSIGNED); | 922 | bt = vtop->type.ref->type.t & (VT_BTYPE | VT_UNSIGNED); |
| 923 | if (bt == (VT_BYTE | VT_UNSIGNED)) | 923 | if (bt == (VT_BYTE | VT_UNSIGNED) || (bt & VT_TYPE) == VT_BOOL) |
| 924 | o(0xc0b60f); /* movzbl %al, %eax */ | 924 | o(0xc0b60f); /* movzbl %al, %eax */ |
| 925 | else if (bt == VT_BYTE) | 925 | else if (bt == VT_BYTE) |
| 926 | o(0xc0be0f); /* movsbl %al, %eax */ | 926 | o(0xc0be0f); /* movsbl %al, %eax */ |
| @@ -1230,7 +1230,7 @@ void gfunc_call(int nb_args) · 303 unmodified lines | |||
| 1230 | { | 1230 | { |
| 1231 | X86_64_Mode mode; | 1231 | X86_64_Mode mode; |
| 1232 | CType type; | 1232 | CType type; |
| 1233 | int size, align, r, args_size, stack_adjust, i, reg_count; | 1233 | int size, align, r, args_size, stack_adjust, i, reg_count, bt; |
| 1234 | int nb_reg_args = 0; | 1234 | int nb_reg_args = 0; |
| 1235 | int nb_sse_args = 0; | 1235 | int nb_sse_args = 0; |
| 1236 | int sse_reg, gen_reg; | 1236 | int sse_reg, gen_reg; |
| @@ -1413,6 +1413,21 @@ void gfunc_call(int nb_args) · 176 unmodified lines | |||
| 1413 | gcall_or_jmp(0); | 1413 | gcall_or_jmp(0); |
| 1414 | if (args_size) | 1414 | if (args_size) |
| 1415 | gadd_sp(args_size); | 1415 | gadd_sp(args_size); |
| 1416 | /* other compilers don't clear the upper bits when returning char/short, | ||
| 1417 | TCC does so for convenience. When we'd stay purely within TCC compiled | ||
| 1418 | code we wouldn't need this, but for compatibility we have to extend. | ||
| 1419 | Ideally TCC wouldn't extend at return statements to not do double | ||
| 1420 | extensions, or would understand sub-int types during expression | ||
| 1421 | evaluation. */ | ||
| 1422 | bt = vtop->type.ref->type.t & (VT_BTYPE | VT_UNSIGNED); | ||
| 1423 | if (bt == (VT_BYTE | VT_UNSIGNED) || (bt & VT_TYPE) == VT_BOOL) | ||
| 1424 | o(0xc0b60f); /* movzbl %al, %eax */ | ||
| 1425 | else if (bt == VT_BYTE) | ||
| 1426 | o(0xc0be0f); /* movsbl %al, %eax */ | ||
| 1427 | else if (bt == VT_SHORT) | ||
| 1428 | o(0x98); /* cwtl */ | ||
| 1429 | else if (bt == (VT_SHORT | VT_UNSIGNED)) | ||
| 1430 | o(0xc0b70f); /* movzwl %al, %eax */ | ||
| 1416 | vtop--; | 1431 | vtop--; |
| 1417 | } | 1432 | } |
| 1418 | 1433 | ||