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 | 749 | #ifdef TCC_ARM_HARDFLOAT |
| 768 | 750 | /* Return whether a structure is an homogeneous float aggregate or not. |
| 769 | 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 | 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 | 843 | /* Parameters are classified according to how they are copied to their final |
| 835 | 844 | destination for the function call. Because the copying is performed class |
| 836 | 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 | 1207 | n = nf = 0; |
| 1199 | 1208 | variadic = (func_type->ref->c == FUNC_ELLIPSIS); |
| 1200 | 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 | 1213 | && type_size(&func_vt,&align) > 4) |
| 1202 | 1214 | { |
| 1203 | 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 | 1884 | ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) { |
| 1884 | 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 | 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 | 374 | static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX }; |
| 375 | 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 | 379 | ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) |
| 379 | 380 | { |
| 380 | 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 | 384 | *ret_align = 1; // Never have to re-align return values for x86 |
| 384 | 385 | size = type_size(vt, &align); |
| 385 | 386 | if (size > 8) { |
| 386 | − return 1; | |
| 387 | + return 0; | |
| 387 | 388 | } else if (size > 4) { |
| 388 | 389 | ret->ref = NULL; |
| 389 | 390 | ret->t = VT_LLONG; |
| 390 | − return 0; | |
| 391 | + return 1; | |
| 391 | 392 | } else { |
| 392 | 393 | ret->ref = NULL; |
| 393 | 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 | 397 | #else |
| 397 | 398 | *ret_align = 1; // Never have to re-align return values for x86 |
| 398 | − return 1; | |
| 399 | + return 0; | |
| 399 | 400 | #endif |
| 400 | 401 | } |
| 401 | 402 | |
| @@ -3927,7 +3927,7 @@ ST_FUNC void unary(void) · 3926 unmodified lines | ||
| 3927 | 3927 | } else if (tok == '(') { |
| 3928 | 3928 | SValue ret; |
| 3929 | 3929 | Sym *sa; |
| 3930 | − int nb_args, sret; | |
| 3930 | + int nb_args, sret, ret_align; | |
| 3931 | 3931 | |
| 3932 | 3932 | /* function call */ |
| 3933 | 3933 | if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { |
| @@ -3951,9 +3951,8 @@ ST_FUNC void unary(void) · 17 unmodified lines | ||
| 3951 | 3951 | ret.r2 = VT_CONST; |
| 3952 | 3952 | /* compute first implicit argument if a structure is returned */ |
| 3953 | 3953 | if ((s->type.t & VT_BTYPE) == VT_STRUCT) { |
| 3954 | − int ret_align; | |
| 3955 | 3954 | sret = gfunc_sret(&s->type, &ret.type, &ret_align); |
| 3956 | − if (sret) { | |
| 3955 | + if (!sret) { | |
| 3957 | 3956 | /* get some space for the returned structure */ |
| 3958 | 3957 | size = type_size(&s->type, &align); |
| 3959 | 3958 | loc = (loc - size) & -align; |
| @@ -3966,11 +3965,11 @@ ST_FUNC void unary(void) · 6 unmodified lines | ||
| 3966 | 3965 | nb_args++; |
| 3967 | 3966 | } |
| 3968 | 3967 | } else { |
| 3969 | − sret = 0; | |
| 3968 | + sret = 1; | |
| 3970 | 3969 | ret.type = s->type; |
| 3971 | 3970 | } |
| 3972 | 3971 | |
| 3973 | − if (!sret) { | |
| 3972 | + if (sret) { | |
| 3974 | 3973 | /* return in register */ |
| 3975 | 3974 | if (is_float(ret.type.t)) { |
| 3976 | 3975 | ret.r = reg_fret(ret.type.t); |
| @@ -4010,18 +4009,23 @@ ST_FUNC void unary(void) · 33 unmodified lines | ||
| 4010 | 4009 | vtop -= (nb_args + 1); |
| 4011 | 4010 | } |
| 4012 | 4011 | /* return value */ |
| 4013 | − vsetc(&ret.type, ret.r, &ret.c); | |
| 4014 | − vtop->r2 = ret.r2; | |
| 4012 | + for (r = ret.r + sret + !sret; r-- > ret.r;) { | |
| 4013 | + vsetc(&ret.type, r, &ret.c); | |
| 4014 | + vtop->r2 = ret.r2; /* Loop only happens when r2 is VT_CONST */ | |
| 4015 | + } | |
| 4015 | 4016 | /* handle packed struct return */ |
| 4016 | − if (((s->type.t & VT_BTYPE) == VT_STRUCT) && !sret) { | |
| 4017 | − int addr; | |
| 4017 | + if (((s->type.t & VT_BTYPE) == VT_STRUCT) && sret) { | |
| 4018 | + int addr, offset; | |
| 4019 | + | |
| 4018 | 4020 | size = type_size(&s->type, &align); |
| 4019 | 4021 | loc = (loc - size) & -align; |
| 4020 | 4022 | addr = loc; |
| 4021 | − vset(&ret.type, VT_LOCAL | VT_LVAL, addr); | |
| 4022 | − vswap(); | |
| 4023 | − vstore(); | |
| 4024 | − vtop--; | |
| 4023 | + for(offset = 0; offset < size; offset += ret_align) { | |
| 4024 | + vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset); | |
| 4025 | + vswap(); | |
| 4026 | + vstore(); | |
| 4027 | + vtop--; | |
| 4028 | + } | |
| 4025 | 4029 | vset(&s->type, VT_LOCAL | VT_LVAL, addr); |
| 4026 | 4030 | } |
| 4027 | 4031 | } else { |
| @@ -4593,7 +4597,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, · 565 unmodified lines | ||
| 4593 | 4597 | if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { |
| 4594 | 4598 | CType type, ret_type; |
| 4595 | 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 | 4601 | /* if returning structure, must copy it to implicit |
| 4598 | 4602 | first pointer arg location */ |
| 4599 | 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 | 4609 | vstore(); |
| 4606 | 4610 | } else { |
| 4607 | 4611 | /* returning structure packed into registers */ |
| 4608 | − int size, addr, align; | |
| 4612 | + int r, size, addr, offset, align; | |
| 4609 | 4613 | size = type_size(&func_vt,&align); |
| 4610 | 4614 | if ((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & (ret_align-1))) |
| 4611 | 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 | 4624 | vtop->type = ret_type; |
| 4621 | 4625 | if (is_float(ret_type.t)) |
| 4622 | − gv(rc_fret(ret_type.t)); | |
| 4626 | + r = rc_fret(ret_type.t); | |
| 4623 | 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 | 4638 | } else if (is_float(func_vt.t)) { |
| 4627 | 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 | 661 | ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) |
| 661 | 662 | { |
| 662 | 663 | int size, align; |
| @@ -664,19 +665,19 @@ ST_FUNC int gfunc_sret(CType *vt, CType *ret, int *ret_align) · 1 unmodified line | ||
| 664 | 665 | size = type_size(vt, &align); |
| 665 | 666 | ret->ref = NULL; |
| 666 | 667 | if (size > 8) { |
| 667 | − return 1; | |
| 668 | + return 0; | |
| 668 | 669 | } else if (size > 4) { |
| 669 | 670 | ret->t = VT_LLONG; |
| 670 | − return 0; | |
| 671 | + return 1; | |
| 671 | 672 | } else if (size > 2) { |
| 672 | 673 | ret->t = VT_INT; |
| 673 | − return 0; | |
| 674 | + return 1; | |
| 674 | 675 | } else if (size > 1) { |
| 675 | 676 | ret->t = VT_SHORT; |
| 676 | − return 0; | |
| 677 | + return 1; | |
| 677 | 678 | } else { |
| 678 | 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 | 1062 | int gfunc_sret(CType *vt, CType *ret, int *ret_align) { |
| 1061 | 1063 | int size, align, reg_count; |
| 1062 | 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 | 1068 | #define REGN 6 |