diff --git a/i386-gen.c b/i386-gen.c index 62bc2ad5..7aebc5cd 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -31,10 +31,10 @@ #define RC_INT 0x0001 /* generic integer register */ #define RC_FLOAT 0x0002 /* generic float register */ #define RC_EAX 0x0004 -#define RC_ST0 0x0008 +#define RC_EDX 0x0008 #define RC_ECX 0x0010 -#define RC_EDX 0x0020 -#define RC_EBX 0x0040 +#define RC_EBX 0x0020 +#define RC_ST0 0x0040 #define RC_IRET RC_EAX /* function return: integer register */ #define RC_IRE2 RC_EDX /* function return: second integer register */ @@ -380,14 +380,15 @@ static const uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX }; ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { #if defined(TCC_TARGET_PE) || TARGETOS_FreeBSD || TARGETOS_OpenBSD - int size, align; + int size, align, nregs; *ret_align = 1; // Never have to re-align return values for x86 *regsize = 4; size = type_size(vt, &align); if (size > 8 || (size & (size - 1))) return 0; + nregs = 1; if (size == 8) - ret->t = VT_LLONG; + ret->t = VT_INT, nregs = 2; else if (size == 4) ret->t = VT_INT; else if (size == 2) @@ -395,7 +396,7 @@ ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int else ret->t = VT_BYTE; ret->ref = NULL; - return 1; + return nregs; #else *ret_align = 1; // Never have to re-align return values for x86 return 0; diff --git a/tccgen.c b/tccgen.c index d5e3b15f..205c27f7 100644 --- a/tccgen.c +++ b/tccgen.c @@ -1687,14 +1687,22 @@ static void pop_local_syms(Sym *b, int keep) sym_pop(&local_stack, b, keep); } +/* increment an lvalue pointer */ +static void incr_offset(int offset) +{ + int t = vtop->type.t; + gaddrof(); /* remove VT_LVAL */ + vtop->type.t = VT_PTRDIFF_T; /* set scalar type */ + vpushs(offset); + gen_op('+'); + vtop->r |= VT_LVAL; + vtop->type.t = t; +} + static void incr_bf_adr(int o) { - vtop->type = char_pointer_type; - gaddrof(); - vpushs(o); - gen_op('+'); vtop->type.t = VT_BYTE | VT_UNSIGNED; - vtop->r |= VT_LVAL; + incr_offset(o); } /* single-byte load mode for packed or otherwise unaligned bitfields */ @@ -1860,8 +1868,18 @@ ST_FUNC int gv(int rc) r2_ok = !rc2 || ((vtop->r2 < VT_CONST) && (reg_classes[vtop->r2] & rc2)); if (!r_ok || !r2_ok) { - if (!r_ok) - r = get_reg(rc); + + if (!r_ok) { + if (1 /* we can 'mov (r),r' in cases */ + && r < VT_CONST + && (reg_classes[r] & rc) + && !rc2 + ) + save_reg_upstack(r, 1); + else + r = get_reg(rc); + } + if (rc2) { int load_type = (bt == VT_QFLOAT) ? VT_DOUBLE : VT_PTRDIFF_T; int original_type = vtop->type.t; @@ -1885,12 +1903,7 @@ ST_FUNC int gv(int rc) vdup(); vtop[-1].r = r; /* save register value */ /* increment pointer to get second word */ - vtop->type.t = VT_PTRDIFF_T; - gaddrof(); - vpushs(PTR_SIZE); - gen_op('+'); - vtop->r |= VT_LVAL; - vtop->type.t = load_type; + incr_offset(PTR_SIZE); } else { /* move registers */ if (!r_ok) @@ -3748,14 +3761,8 @@ ST_FUNC void vstore(void) vtop[-1].type.t = load_type; store(r, vtop - 1); vswap(); - /* convert to int to increment easily */ - vtop->type.t = VT_PTRDIFF_T; - gaddrof(); - vpushs(PTR_SIZE); - gen_op('+'); - vtop->r |= VT_LVAL; + incr_offset(PTR_SIZE); vswap(); - vtop[-1].type.t = load_type; /* XXX: it works because r2 is spilled last ! */ store(vtop->r2, vtop - 1); } else { @@ -6003,7 +6010,6 @@ special_math_val: indir(); qualifiers = vtop->type.t & (VT_CONSTANT | VT_VOLATILE); test_lvalue(); - gaddrof(); /* expect pointer on structure */ if ((vtop->type.t & VT_BTYPE) != VT_STRUCT) expect("struct or union"); @@ -6014,16 +6020,15 @@ special_math_val: expect("field name"); s = find_field(&vtop->type, tok, &cumofs); /* add field offset to pointer */ - vtop->type = char_pointer_type; /* change type to 'char *' */ - vpushi(cumofs); - gen_op('+'); + incr_offset(cumofs); /* change type to field type, and set to lvalue */ vtop->type = s->type; vtop->type.t |= qualifiers; /* an array is never an lvalue */ - if (!(vtop->type.t & VT_ARRAY)) { - vtop->r |= VT_LVAL; + if (vtop->type.t & VT_ARRAY) { + vtop->r &= ~VT_LVAL; #ifdef CONFIG_TCC_BCHECK + } else { /* if bound checking, the referenced pointer must be checked */ if (tcc_state->do_bounds_check) vtop->r |= VT_MUSTBOUND; @@ -6128,10 +6133,20 @@ special_math_val: #endif } else { /* return value */ - for (r = ret.r + ret_nregs + !ret_nregs; r-- > ret.r;) { + n = ret_nregs; + while (n > 1) { + int rc = reg_classes[ret.r] & ~(RC_INT | RC_FLOAT); + /* We assume that when a structure is returned in multiple + registers, their classes are consecutive values of the + suite s(n) = 2^n */ + rc <<= --n; + for (r = 0; r < NB_REGS; ++r) + if (reg_classes[r] & rc) + break; vsetc(&ret.type, r, &ret.c); - vtop->r2 = ret.r2; /* Loop only happens when r2 is VT_CONST */ } + vsetc(&ret.type, ret.r, &ret.c); + vtop->r2 = ret.r2; /* handle packed struct return */ if (((s->type.t & VT_BTYPE) == VT_STRUCT) && ret_nregs) { @@ -6140,9 +6155,9 @@ special_math_val: size = type_size(&s->type, &align); /* We're writing whole regs often, make sure there's enough space. Assume register size is power of 2. */ - if (regsize > align) - align = regsize; - loc &= -align; + size = (size + regsize - 1) & -regsize; + if (ret_align > align) + align = ret_align; loc = (loc - size) & -align; addr = loc; offset = 0; @@ -6619,11 +6634,12 @@ static void gfunc_return(CType *func_type) vstore(); } else { /* returning structure packed into registers */ - int size, addr, align, rc; + int size, addr, align, rc, n; size = type_size(func_type,&align); - if ((vtop->r != (VT_LOCAL | VT_LVAL) || - (vtop->c.i & (ret_align-1))) - && (align & (ret_align-1))) { + if ((align & (ret_align - 1)) + && ((vtop->r & VT_VALMASK) < VT_CONST /* pointer to struct */ + || (vtop->c.i & (ret_align - 1)) + )) { loc = (loc - size) & -ret_align; addr = loc; type = *func_type; @@ -6635,22 +6651,19 @@ static void gfunc_return(CType *func_type) } vtop->type = ret_type; rc = RC_RET(ret_type.t); - if (ret_nregs == 1) + //printf("struct return: n:%d t:%02x rc:%02x\n", ret_nregs, ret_type.t, rc); + for (n = ret_nregs; --n > 0;) { + vdup(); gv(rc); - else { - for (;;) { - vdup(); - gv(rc); - vpop(); - if (--ret_nregs == 0) - break; - /* We assume that when a structure is returned in multiple - registers, their classes are consecutive values of the - suite s(n) = 2^n */ - rc <<= 1; - vtop->c.i += regsize; - } + vswap(); + incr_offset(regsize); + /* We assume that when a structure is returned in multiple + registers, their classes are consecutive values of the + suite s(n) = 2^n */ + rc <<= 1; } + gv(rc); + vtop -= ret_nregs - 1; } } else { gv(RC_RET(func_type->t)); diff --git a/tests/tests2/131_return_struct_in_reg.c b/tests/tests2/131_return_struct_in_reg.c index 0b184a51..2cb00b53 100644 --- a/tests/tests2/131_return_struct_in_reg.c +++ b/tests/tests2/131_return_struct_in_reg.c @@ -1,49 +1,70 @@ #include -#define DATA 0x1234567890abcdll, 0x5a5aa5a5f0f00f0fll +struct s1 { + unsigned char a:4, b:4; +} g1 = { 0x05, 0x0a }; -struct s { - long long int a; - long long int b; -}; +struct s2 { + unsigned char a, b; +} g2 = { 0x12, 0x34 }; -struct { - struct s d; -} g = { { DATA } }, *gp = &g; +struct s4 { + unsigned short a, b; +} g4 = { 0x1245, 0x5678 }; -struct s -foo1(void) -{ - struct s d = { DATA }; - struct s *p = &d; - long long int x = 0; - return *p; +struct s8 { + unsigned a, b; +} g8 = { 0x12345678, 0x9abcdef0 }; + +/* returned in 2 registers on riscv64 */ +struct s16 { + unsigned long long a, b; +} g16 = { 0x123456789abcdef0ULL, 0xfedcba9876543210ULL }; + +/* Homogeneous float aggregate on ARM hard-float */ +struct s_f4 { + double a, b, c, d; +} g_f4 = { 1,2,3,4 }; + +#define def(S) \ +struct s##S f##S(int x) \ +{ \ + struct s##S l##S = g##S, *p##S = &l##S; \ + if (x == 0) \ + return g##S; \ + else if (x == 1) \ + return l##S; \ + else \ + return *p##S; \ } -struct s -foo2(void) -{ - long long int unused = 0; - return gp->d; -} +def(1) +def(2) +def(4) +def(8) +def(16) +def(_f4) -struct s -foo3(void) -{ - struct s d = { DATA }; - long long int unused = 0; - return d; -} +#define chk(S,x) \ + struct s##S l##S = f##S(x); \ + printf("%02llx %02llx\n", \ + (unsigned long long)l##S.a, \ + (unsigned long long)l##S.b \ + ); -int -main(void) +int main() { - struct s d; - d = foo1(); - printf("%llx %llx\n", d.a, d.b); - d = foo2(); - printf("%llx %llx\n", d.a, d.b); - d = foo3(); - printf("%llx %llx\n", d.a, d.b); - return 0; + for (int x = 0;;) { + chk(1,x); + chk(2,x); + chk(4,x); + chk(8,x); + chk(16,x); + struct s_f4 l_f4 = f_f4(x); + printf("%.1f %.1f %.1f %.1f\n", l_f4.a, l_f4.b, l_f4.c, l_f4.d); + if (++x > 2) + break; + printf("\n"); + } + return 0; } diff --git a/tests/tests2/131_return_struct_in_reg.expect b/tests/tests2/131_return_struct_in_reg.expect index 301e3b0f..2341c57c 100644 --- a/tests/tests2/131_return_struct_in_reg.expect +++ b/tests/tests2/131_return_struct_in_reg.expect @@ -1,3 +1,20 @@ -1234567890abcd 5a5aa5a5f0f00f0f -1234567890abcd 5a5aa5a5f0f00f0f -1234567890abcd 5a5aa5a5f0f00f0f +05 0a +12 34 +1245 5678 +12345678 9abcdef0 +123456789abcdef0 fedcba9876543210 +1.0 2.0 3.0 4.0 + +05 0a +12 34 +1245 5678 +12345678 9abcdef0 +123456789abcdef0 fedcba9876543210 +1.0 2.0 3.0 4.0 + +05 0a +12 34 +1245 5678 +12345678 9abcdef0 +123456789abcdef0 fedcba9876543210 +1.0 2.0 3.0 4.0 diff --git a/x86_64-gen.c b/x86_64-gen.c index 0fe9ceee..a41cbc10 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -33,8 +33,8 @@ #define RC_INT 0x0001 /* generic integer register */ #define RC_FLOAT 0x0002 /* generic float register */ #define RC_RAX 0x0004 -#define RC_RCX 0x0008 -#define RC_RDX 0x0010 +#define RC_RDX 0x0008 +#define RC_RCX 0x0010 #define RC_RSI 0x0020 #define RC_RDI 0x0040 #define RC_ST0 0x0080 /* only for long double */ @@ -1223,9 +1223,11 @@ ST_FUNC int classify_x86_64_va_arg(CType *ty) ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize) { int size, align, reg_count; + if (classify_x86_64_arg(vt, ret, &size, &align, ®_count) == x86_64_mode_memory) + return 0; *ret_align = 1; // Never have to re-align return values for x86-64 - *regsize = 8; - return (classify_x86_64_arg(vt, ret, &size, &align, ®_count) != x86_64_mode_memory); + *regsize = 8 * reg_count; /* the (virtual) regsize is 16 for VT_QLONG/QFLOAT */ + return 1; } #define REGN 6