On ARM with hardfloat calling convention, structure containing 4 fields or less of the same float type are returned via float registers. This means that a structure can be returned in up to 4 double registers in a structure is composed of 4 doubles. This commit adds support for return of structures in several registers.
Unified · Split
| @@ -746,24 +746,6 @@ static void gcall_or_jmp(int is_jmp) · 745 unmodified lines | |||
| 746 | } | 746 | } |
| 747 | } | 747 | } |
| 748 | 748 | ||
| 749 | /* Return 1 if this function returns via an sret pointer, 0 otherwise */ | ||
| 750 | ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { | ||
| 751 | #ifdef TCC_ARM_EABI | ||
| 752 | int size, align; | ||
| 753 | size = type_size(vt, &align); | ||
| 754 | if (size > 4) { | ||
| 755 | return 1; | ||
| 756 | } else { | ||
| 757 | *ret_align = 4; | ||
| 758 | ret->ref = NULL; | ||
| 759 | ret->t = VT_INT; | ||
| 760 | return 0; | ||
| 761 | } | ||
| 762 | #else | ||
| 763 | return 1; | ||
| 764 | #endif | ||
| 765 | } | ||
| 766 | |||
| 767 | #ifdef TCC_ARM_HARDFLOAT | 749 | #ifdef TCC_ARM_HARDFLOAT |
| 768 | /* Return whether a structure is an homogeneous float aggregate or not. | 750 | /* Return whether a structure is an homogeneous float aggregate or not. |
| 769 | The answer is true if all the elements of the structure are of the same | 751 | The answer is true if all the elements of the structure are of the same |
| @@ -831,6 +813,33 @@ int assign_vfpreg(struct avail_regs *avregs, int align, int size) · 61 unmodified lines | |||
| 831 | } | 813 | } |
| 832 | #endif | 814 | #endif |
| 833 | 815 | ||
| 816 | /* Return the number of registers needed to return the struct, or 0 if | ||
| 817 | returning via struct pointer. */ | ||
| 818 | ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { | ||
| 819 | #ifdef TCC_ARM_EABI | ||
| 820 | int size, align; | ||
| 821 | size = type_size(vt, &align); | ||
| 822 | #ifdef TCC_ARM_HARDFLOAT | ||
| 823 | if (is_float(vt->t) || is_hgen_float_aggr(vt)) { | ||
| 824 | *ret_align = 8; | ||
| 825 | ret->ref = NULL; | ||
| 826 | ret->t = VT_DOUBLE; | ||
| 827 | return (size + 7) >> 3; | ||
| 828 | } else | ||
| 829 | #endif | ||
| 830 | if (size > 4) { | ||
| 831 | return 0; | ||
| 832 | } else { | ||
| 833 | *ret_align = 4; | ||
| 834 | ret->ref = NULL; | ||
| 835 | ret->t = VT_INT; | ||
| 836 | return 1; | ||
| 837 | } | ||
| 838 | #else | ||
| 839 | return 0; | ||
| 840 | #endif | ||
| 841 | } | ||
| 842 | |||
| 834 | /* Parameters are classified according to how they are copied to their final | 843 | /* Parameters are classified according to how they are copied to their final |
| 835 | destination for the function call. Because the copying is performed class | 844 | destination for the function call. Because the copying is performed class |
| 836 | after class according to the order in the union below, it is important that | 845 | after class according to the order in the union below, it is important that |
| @@ -1198,6 +1207,9 @@ void gfunc_prolog(CType *func_type) · 361 unmodified lines | |||
| 1198 | n = nf = 0; | 1207 | n = nf = 0; |
| 1199 | variadic = (func_type->ref->c == FUNC_ELLIPSIS); | 1208 | variadic = (func_type->ref->c == FUNC_ELLIPSIS); |
| 1200 | if((func_vt.t & VT_BTYPE) == VT_STRUCT | 1209 | if((func_vt.t & VT_BTYPE) == VT_STRUCT |
| 1210 | #ifdef TCC_ARM_HARDFLOAT | ||
| 1211 | && (variadic || !is_hgen_float_aggr(&func_vt)) | ||
| 1212 | #endif | ||
| 1201 | && type_size(&func_vt,&align) > 4) | 1213 | && type_size(&func_vt,&align) > 4) |
| 1202 | { | 1214 | { |
| 1203 | n++; | 1215 | n++; |
| @@ -1879,10 +1879,11 @@ static void gcall_or_jmp(int is_jmp) · 1878 unmodified lines | |||
| 1879 | } | 1879 | } |
| 1880 | } | 1880 | } |
| 1881 | 1881 | ||
| 1882 | /* Return 1 if this function returns via an sret pointer, 0 otherwise */ | 1882 | /* Return the number of registers needed to return the struct, or 0 if |
| 1883 | returning via struct pointer. */ | ||
| 1883 | ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { | 1884 | ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { |
| 1884 | *ret_align = 1; // Never have to re-align return values for x86-64 | 1885 | *ret_align = 1; // Never have to re-align return values for x86-64 |
| 1885 | return 1; | 1886 | return 0; |
| 1886 | } | 1887 | } |
| 1887 | 1888 | ||
| 1888 | /* generate function call with address in (vtop->t, vtop->c) and free function | 1889 | /* generate function call with address in (vtop->t, vtop->c) and free function |
| @@ -374,7 +374,8 @@ static void gcall_or_jmp(int is_jmp) · 373 unmodified lines | |||
| 374 | static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX }; | 374 | static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX }; |
| 375 | static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX }; | 375 | static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX }; |
| 376 | 376 | ||
| 377 | /* Return 1 if this function returns via an sret pointer, 0 otherwise */ | 377 | /* Return the number of registers needed to return the struct, or 0 if |
| 378 | returning via struct pointer. */ | ||
| 378 | ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) | 379 | ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) |
| 379 | { | 380 | { |
| 380 | #ifdef TCC_TARGET_PE | 381 | #ifdef TCC_TARGET_PE |
| @@ -383,11 +384,11 @@ ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) · 2 unmodified lines | |||
| 383 | *ret_align = 1; // Never have to re-align return values for x86 | 384 | *ret_align = 1; // Never have to re-align return values for x86 |
| 384 | size = type_size(vt, &align); | 385 | size = type_size(vt, &align); |
| 385 | if (size > 8) { | 386 | if (size > 8) { |
| 386 | return 1; | 387 | return 0; |
| 387 | } else if (size > 4) { | 388 | } else if (size > 4) { |
| 388 | ret->ref = NULL; | 389 | ret->ref = NULL; |
| 389 | ret->t = VT_LLONG; | 390 | ret->t = VT_LLONG; |
| 390 | return 0; | 391 | return 1; |
| 391 | } else { | 392 | } else { |
| 392 | ret->ref = NULL; | 393 | ret->ref = NULL; |
| 393 | ret->t = VT_INT; | 394 | ret->t = VT_INT; |
| @@ -395,7 +396,7 @@ ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) · 1 unmodified line | |||
| 395 | } | 396 | } |
| 396 | #else | 397 | #else |
| 397 | *ret_align = 1; // Never have to re-align return values for x86 | 398 | *ret_align = 1; // Never have to re-align return values for x86 |
| 398 | return 1; | 399 | return 0; |
| 399 | #endif | 400 | #endif |
| 400 | } | 401 | } |
| 401 | 402 | ||
| @@ -3927,7 +3927,7 @@ ST_FUNC void unary(void) · 3926 unmodified lines | |||
| 3927 | } else if (tok == '(') { | 3927 | } else if (tok == '(') { |
| 3928 | SValue ret; | 3928 | SValue ret; |
| 3929 | Sym *sa; | 3929 | Sym *sa; |
| 3930 | int nb_args, sret; | 3930 | int nb_args, sret, ret_align; |
| 3931 | 3931 | ||
| 3932 | /* function call */ | 3932 | /* function call */ |
| 3933 | if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { | 3933 | if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { |
| @@ -3951,9 +3951,8 @@ ST_FUNC void unary(void) · 17 unmodified lines | |||
| 3951 | ret.r2 = VT_CONST; | 3951 | ret.r2 = VT_CONST; |
| 3952 | /* compute first implicit argument if a structure is returned */ | 3952 | /* compute first implicit argument if a structure is returned */ |
| 3953 | if ((s->type.t & VT_BTYPE) == VT_STRUCT) { | 3953 | if ((s->type.t & VT_BTYPE) == VT_STRUCT) { |
| 3954 | int ret_align; | ||
| 3955 | sret = gfunc_sret(&s->type, &ret.type, &ret_align); | 3954 | sret = gfunc_sret(&s->type, &ret.type, &ret_align); |
| 3956 | if (sret) { | 3955 | if (!sret) { |
| 3957 | /* get some space for the returned structure */ | 3956 | /* get some space for the returned structure */ |
| 3958 | size = type_size(&s->type, &align); | 3957 | size = type_size(&s->type, &align); |
| 3959 | loc = (loc - size) & -align; | 3958 | loc = (loc - size) & -align; |
| @@ -3966,11 +3965,11 @@ ST_FUNC void unary(void) · 6 unmodified lines | |||
| 3966 | nb_args++; | 3965 | nb_args++; |
| 3967 | } | 3966 | } |
| 3968 | } else { | 3967 | } else { |
| 3969 | sret = 0; | 3968 | sret = 1; |
| 3970 | ret.type = s->type; | 3969 | ret.type = s->type; |
| 3971 | } | 3970 | } |
| 3972 | 3971 | ||
| 3973 | if (!sret) { | 3972 | if (sret) { |
| 3974 | /* return in register */ | 3973 | /* return in register */ |
| 3975 | if (is_float(ret.type.t)) { | 3974 | if (is_float(ret.type.t)) { |
| 3976 | ret.r = reg_fret(ret.type.t); | 3975 | ret.r = reg_fret(ret.type.t); |
| @@ -4010,18 +4009,23 @@ ST_FUNC void unary(void) · 33 unmodified lines | |||
| 4010 | vtop -= (nb_args + 1); | 4009 | vtop -= (nb_args + 1); |
| 4011 | } | 4010 | } |
| 4012 | /* return value */ | 4011 | /* return value */ |
| 4013 | vsetc(&ret.type, ret.r, &ret.c); | 4012 | for (r = ret.r + sret + !sret; r-- > ret.r;) { |
| 4014 | vtop->r2 = ret.r2; | 4013 | vsetc(&ret.type, r, &ret.c); |
| 4014 | vtop->r2 = ret.r2; /* Loop only happens when r2 is VT_CONST */ | ||
| 4015 | } | ||
| 4015 | /* handle packed struct return */ | 4016 | /* handle packed struct return */ |
| 4016 | if (((s->type.t & VT_BTYPE) == VT_STRUCT) && !sret) { | 4017 | if (((s->type.t & VT_BTYPE) == VT_STRUCT) && sret) { |
| 4017 | int addr; | 4018 | int addr, offset; |
| 4019 | |||
| 4018 | size = type_size(&s->type, &align); | 4020 | size = type_size(&s->type, &align); |
| 4019 | loc = (loc - size) & -align; | 4021 | loc = (loc - size) & -align; |
| 4020 | addr = loc; | 4022 | addr = loc; |
| 4021 | vset(&ret.type, VT_LOCAL | VT_LVAL, addr); | 4023 | for(offset = 0; offset < size; offset += ret_align) { |
| 4022 | vswap(); | 4024 | vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset); |
| 4023 | vstore(); | 4025 | vswap(); |
| 4024 | vtop--; | 4026 | vstore(); |
| 4027 | vtop--; | ||
| 4028 | } | ||
| 4025 | vset(&s->type, VT_LOCAL | VT_LVAL, addr); | 4029 | vset(&s->type, VT_LOCAL | VT_LVAL, addr); |
| 4026 | } | 4030 | } |
| 4027 | } else { | 4031 | } else { |
| @@ -4593,7 +4597,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, · 565 unmodified lines | |||
| 4593 | if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { | 4597 | if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { |
| 4594 | CType type, ret_type; | 4598 | CType type, ret_type; |
| 4595 | int ret_align; | 4599 | int ret_align; |
| 4596 | if (gfunc_sret(&func_vt, &ret_type, &ret_align)) { | 4600 | if (!gfunc_sret(&func_vt, &ret_type, &ret_align)) { |
| 4597 | /* if returning structure, must copy it to implicit | 4601 | /* if returning structure, must copy it to implicit |
| 4598 | first pointer arg location */ | 4602 | first pointer arg location */ |
| 4599 | type = func_vt; | 4603 | type = func_vt; |
| @@ -4605,7 +4609,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, · 5 unmodified lines | |||
| 4605 | vstore(); | 4609 | vstore(); |
| 4606 | } else { | 4610 | } else { |
| 4607 | /* returning structure packed into registers */ | 4611 | /* returning structure packed into registers */ |
| 4608 | int size, addr, align; | 4612 | int r, size, addr, offset, align; |
| 4609 | size = type_size(&func_vt,&align); | 4613 | size = type_size(&func_vt,&align); |
| 4610 | if ((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & (ret_align-1))) | 4614 | if ((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & (ret_align-1))) |
| 4611 | && (align & (ret_align-1))) { | 4615 | && (align & (ret_align-1))) { |
| @@ -4619,9 +4623,17 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, · 7 unmodified lines | |||
| 4619 | } | 4623 | } |
| 4620 | vtop->type = ret_type; | 4624 | vtop->type = ret_type; |
| 4621 | if (is_float(ret_type.t)) | 4625 | if (is_float(ret_type.t)) |
| 4622 | gv(rc_fret(ret_type.t)); | 4626 | r = rc_fret(ret_type.t); |
| 4623 | else | 4627 | else |
| 4624 | gv(RC_IRET); | 4628 | r = RC_IRET; |
| 4629 | /* We assume that when a structure is returned in multiple | ||
| 4630 | registers, their classes are consecutive values of the | ||
| 4631 | suite s(n) = 2^n */ | ||
| 4632 | for (offset = 0; offset < size; offset += ret_align, r<<=1) { | ||
| 4633 | gv(r); | ||
| 4634 | vtop->c.i += ret_align; | ||
| 4635 | vtop->r = VT_LOCAL | VT_LVAL; | ||
| 4636 | } | ||
| 4625 | } | 4637 | } |
| 4626 | } else if (is_float(func_vt.t)) { | 4638 | } else if (is_float(func_vt.t)) { |
| 4627 | gv(rc_fret(func_vt.t)); | 4639 | gv(rc_fret(func_vt.t)); |
| @@ -656,7 +656,8 @@ void gen_offs_sp(int b, int r, int d) · 655 unmodified lines | |||
| 656 | } | 656 | } |
| 657 | } | 657 | } |
| 658 | 658 | ||
| 659 | /* Return 1 if this function returns via an sret pointer, 0 otherwise */ | 659 | /* Return the number of registers needed to return the struct, or 0 if |
| 660 | returning via struct pointer. */ | ||
| 660 | ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) | 661 | ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) |
| 661 | { | 662 | { |
| 662 | int size, align; | 663 | int size, align; |
| @@ -664,19 +665,19 @@ ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) · 1 unmodified line | |||
| 664 | size = type_size(vt, &align); | 665 | size = type_size(vt, &align); |
| 665 | ret->ref = NULL; | 666 | ret->ref = NULL; |
| 666 | if (size > 8) { | 667 | if (size > 8) { |
| 667 | return 1; | 668 | return 0; |
| 668 | } else if (size > 4) { | 669 | } else if (size > 4) { |
| 669 | ret->t = VT_LLONG; | 670 | ret->t = VT_LLONG; |
| 670 | return 0; | 671 | return 1; |
| 671 | } else if (size > 2) { | 672 | } else if (size > 2) { |
| 672 | ret->t = VT_INT; | 673 | ret->t = VT_INT; |
| 673 | return 0; | 674 | return 1; |
| 674 | } else if (size > 1) { | 675 | } else if (size > 1) { |
| 675 | ret->t = VT_SHORT; | 676 | ret->t = VT_SHORT; |
| 676 | return 0; | 677 | return 1; |
| 677 | } else { | 678 | } else { |
| 678 | ret->t = VT_BYTE; | 679 | ret->t = VT_BYTE; |
| 679 | return 0; | 680 | return 1; |
| 680 | } | 681 | } |
| 681 | } | 682 | } |
| 682 | 683 | ||
| @@ -1056,11 +1057,12 @@ ST_FUNC int classify_x86_64_va_arg(CType *ty) { · 373 unmodified lines | |||
| 1056 | } | 1057 | } |
| 1057 | } | 1058 | } |
| 1058 | 1059 | ||
| 1059 | /* Return 1 if this function returns via an sret pointer, 0 otherwise */ | 1060 | /* Return the number of registers needed to return the struct, or 0 if |
| 1061 | returning via struct pointer. */ | ||
| 1060 | int gfunc_sret(CType *vt, CType *ret, int *ret_align) { | 1062 | int gfunc_sret(CType *vt, CType *ret, int *ret_align) { |
| 1061 | int size, align, reg_count; | 1063 | int size, align, reg_count; |
| 1062 | *ret_align = 1; // Never have to re-align return values for x86-64 | 1064 | *ret_align = 1; // Never have to re-align return values for x86-64 |
| 1063 | return (classify_x86_64_arg(vt, ret, &size, &align, ®_count) == x86_64_mode_memory); | 1065 | return (classify_x86_64_arg(vt, ret, &size, &align, ®_count) != x86_64_mode_memory); |
| 1064 | } | 1066 | } |
| 1065 | 1067 | ||
| 1066 | #define REGN 6 | 1068 | #define REGN 6 |