1
0
Fork 0

tccgen: update "Fix invalid load generated by gfunc_return()"

tccgen.c:
- new function incr_offset(int) to increment a lvalue
- use it in gv/vstore to load/store from/to two-word types
- use it to advance the pointer to struct fields
- use it to load/store structs passed in registers
- structs: always assume that reg-classes of registers are 2^n
- adjust stack space when regsize > sizeof the_struct

x86_64-gen.c:
- return regsize=16 for VT_QLONG/QFLOAT

i386-gen.c:
- pass structs of size(8) as two VT_INT rather than one VT_LLONG
  (both should work now)

fixes a82aff3337
fixes fd6d2180c5 (slightly)
This commit is contained in:
grischka 2023-07-31 11:43:15 +02:00
parent 022fb4293b
commit c29420ab0d
5 changed files with 154 additions and 100 deletions

View File

@ -31,10 +31,10 @@
#define RC_INT 0x0001 /* generic integer register */ #define RC_INT 0x0001 /* generic integer register */
#define RC_FLOAT 0x0002 /* generic float register */ #define RC_FLOAT 0x0002 /* generic float register */
#define RC_EAX 0x0004 #define RC_EAX 0x0004
#define RC_ST0 0x0008 #define RC_EDX 0x0008
#define RC_ECX 0x0010 #define RC_ECX 0x0010
#define RC_EDX 0x0020 #define RC_EBX 0x0020
#define RC_EBX 0x0040 #define RC_ST0 0x0040
#define RC_IRET RC_EAX /* function return: integer register */ #define RC_IRET RC_EAX /* function return: integer register */
#define RC_IRE2 RC_EDX /* function return: second 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) 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 #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 *ret_align = 1; // Never have to re-align return values for x86
*regsize = 4; *regsize = 4;
size = type_size(vt, &align); size = type_size(vt, &align);
if (size > 8 || (size & (size - 1))) if (size > 8 || (size & (size - 1)))
return 0; return 0;
nregs = 1;
if (size == 8) if (size == 8)
ret->t = VT_LLONG; ret->t = VT_INT, nregs = 2;
else if (size == 4) else if (size == 4)
ret->t = VT_INT; ret->t = VT_INT;
else if (size == 2) else if (size == 2)
@ -395,7 +396,7 @@ ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int
else else
ret->t = VT_BYTE; ret->t = VT_BYTE;
ret->ref = NULL; ret->ref = NULL;
return 1; return nregs;
#else #else
*ret_align = 1; // Never have to re-align return values for x86 *ret_align = 1; // Never have to re-align return values for x86
return 0; return 0;

111
tccgen.c
View File

@ -1687,14 +1687,22 @@ static void pop_local_syms(Sym *b, int keep)
sym_pop(&local_stack, b, 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) 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->type.t = VT_BYTE | VT_UNSIGNED;
vtop->r |= VT_LVAL; incr_offset(o);
} }
/* single-byte load mode for packed or otherwise unaligned bitfields */ /* 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)); r2_ok = !rc2 || ((vtop->r2 < VT_CONST) && (reg_classes[vtop->r2] & rc2));
if (!r_ok || !r2_ok) { 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) { if (rc2) {
int load_type = (bt == VT_QFLOAT) ? VT_DOUBLE : VT_PTRDIFF_T; int load_type = (bt == VT_QFLOAT) ? VT_DOUBLE : VT_PTRDIFF_T;
int original_type = vtop->type.t; int original_type = vtop->type.t;
@ -1885,12 +1903,7 @@ ST_FUNC int gv(int rc)
vdup(); vdup();
vtop[-1].r = r; /* save register value */ vtop[-1].r = r; /* save register value */
/* increment pointer to get second word */ /* increment pointer to get second word */
vtop->type.t = VT_PTRDIFF_T; incr_offset(PTR_SIZE);
gaddrof();
vpushs(PTR_SIZE);
gen_op('+');
vtop->r |= VT_LVAL;
vtop->type.t = load_type;
} else { } else {
/* move registers */ /* move registers */
if (!r_ok) if (!r_ok)
@ -3748,14 +3761,8 @@ ST_FUNC void vstore(void)
vtop[-1].type.t = load_type; vtop[-1].type.t = load_type;
store(r, vtop - 1); store(r, vtop - 1);
vswap(); vswap();
/* convert to int to increment easily */ incr_offset(PTR_SIZE);
vtop->type.t = VT_PTRDIFF_T;
gaddrof();
vpushs(PTR_SIZE);
gen_op('+');
vtop->r |= VT_LVAL;
vswap(); vswap();
vtop[-1].type.t = load_type;
/* XXX: it works because r2 is spilled last ! */ /* XXX: it works because r2 is spilled last ! */
store(vtop->r2, vtop - 1); store(vtop->r2, vtop - 1);
} else { } else {
@ -6003,7 +6010,6 @@ special_math_val:
indir(); indir();
qualifiers = vtop->type.t & (VT_CONSTANT | VT_VOLATILE); qualifiers = vtop->type.t & (VT_CONSTANT | VT_VOLATILE);
test_lvalue(); test_lvalue();
gaddrof();
/* expect pointer on structure */ /* expect pointer on structure */
if ((vtop->type.t & VT_BTYPE) != VT_STRUCT) if ((vtop->type.t & VT_BTYPE) != VT_STRUCT)
expect("struct or union"); expect("struct or union");
@ -6014,16 +6020,15 @@ special_math_val:
expect("field name"); expect("field name");
s = find_field(&vtop->type, tok, &cumofs); s = find_field(&vtop->type, tok, &cumofs);
/* add field offset to pointer */ /* add field offset to pointer */
vtop->type = char_pointer_type; /* change type to 'char *' */ incr_offset(cumofs);
vpushi(cumofs);
gen_op('+');
/* change type to field type, and set to lvalue */ /* change type to field type, and set to lvalue */
vtop->type = s->type; vtop->type = s->type;
vtop->type.t |= qualifiers; vtop->type.t |= qualifiers;
/* an array is never an lvalue */ /* an array is never an lvalue */
if (!(vtop->type.t & VT_ARRAY)) { if (vtop->type.t & VT_ARRAY) {
vtop->r |= VT_LVAL; vtop->r &= ~VT_LVAL;
#ifdef CONFIG_TCC_BCHECK #ifdef CONFIG_TCC_BCHECK
} else {
/* if bound checking, the referenced pointer must be checked */ /* if bound checking, the referenced pointer must be checked */
if (tcc_state->do_bounds_check) if (tcc_state->do_bounds_check)
vtop->r |= VT_MUSTBOUND; vtop->r |= VT_MUSTBOUND;
@ -6128,10 +6133,20 @@ special_math_val:
#endif #endif
} else { } else {
/* return value */ /* 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); 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 */ /* handle packed struct return */
if (((s->type.t & VT_BTYPE) == VT_STRUCT) && ret_nregs) { if (((s->type.t & VT_BTYPE) == VT_STRUCT) && ret_nregs) {
@ -6140,9 +6155,9 @@ special_math_val:
size = type_size(&s->type, &align); size = type_size(&s->type, &align);
/* We're writing whole regs often, make sure there's enough /* We're writing whole regs often, make sure there's enough
space. Assume register size is power of 2. */ space. Assume register size is power of 2. */
if (regsize > align) size = (size + regsize - 1) & -regsize;
align = regsize; if (ret_align > align)
loc &= -align; align = ret_align;
loc = (loc - size) & -align; loc = (loc - size) & -align;
addr = loc; addr = loc;
offset = 0; offset = 0;
@ -6619,11 +6634,12 @@ static void gfunc_return(CType *func_type)
vstore(); vstore();
} else { } else {
/* returning structure packed into registers */ /* returning structure packed into registers */
int size, addr, align, rc; int size, addr, align, rc, n;
size = type_size(func_type,&align); size = type_size(func_type,&align);
if ((vtop->r != (VT_LOCAL | VT_LVAL) || if ((align & (ret_align - 1))
(vtop->c.i & (ret_align-1))) && ((vtop->r & VT_VALMASK) < VT_CONST /* pointer to struct */
&& (align & (ret_align-1))) { || (vtop->c.i & (ret_align - 1))
)) {
loc = (loc - size) & -ret_align; loc = (loc - size) & -ret_align;
addr = loc; addr = loc;
type = *func_type; type = *func_type;
@ -6635,22 +6651,19 @@ static void gfunc_return(CType *func_type)
} }
vtop->type = ret_type; vtop->type = ret_type;
rc = RC_RET(ret_type.t); 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); gv(rc);
else { vswap();
for (;;) { incr_offset(regsize);
vdup(); /* We assume that when a structure is returned in multiple
gv(rc); registers, their classes are consecutive values of the
vpop(); suite s(n) = 2^n */
if (--ret_nregs == 0) rc <<= 1;
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;
}
} }
gv(rc);
vtop -= ret_nregs - 1;
} }
} else { } else {
gv(RC_RET(func_type->t)); gv(RC_RET(func_type->t));

View File

@ -1,49 +1,70 @@
#include<stdio.h> #include<stdio.h>
#define DATA 0x1234567890abcdll, 0x5a5aa5a5f0f00f0fll struct s1 {
unsigned char a:4, b:4;
} g1 = { 0x05, 0x0a };
struct s { struct s2 {
long long int a; unsigned char a, b;
long long int b; } g2 = { 0x12, 0x34 };
};
struct { struct s4 {
struct s d; unsigned short a, b;
} g = { { DATA } }, *gp = &g; } g4 = { 0x1245, 0x5678 };
struct s struct s8 {
foo1(void) unsigned a, b;
{ } g8 = { 0x12345678, 0x9abcdef0 };
struct s d = { DATA };
struct s *p = &d; /* returned in 2 registers on riscv64 */
long long int x = 0; struct s16 {
return *p; 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 def(1)
foo2(void) def(2)
{ def(4)
long long int unused = 0; def(8)
return gp->d; def(16)
} def(_f4)
struct s #define chk(S,x) \
foo3(void) struct s##S l##S = f##S(x); \
{ printf("%02llx %02llx\n", \
struct s d = { DATA }; (unsigned long long)l##S.a, \
long long int unused = 0; (unsigned long long)l##S.b \
return d; );
}
int int main()
main(void)
{ {
struct s d; for (int x = 0;;) {
d = foo1(); chk(1,x);
printf("%llx %llx\n", d.a, d.b); chk(2,x);
d = foo2(); chk(4,x);
printf("%llx %llx\n", d.a, d.b); chk(8,x);
d = foo3(); chk(16,x);
printf("%llx %llx\n", d.a, d.b); struct s_f4 l_f4 = f_f4(x);
return 0; 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;
} }

View File

@ -1,3 +1,20 @@
1234567890abcd 5a5aa5a5f0f00f0f 05 0a
1234567890abcd 5a5aa5a5f0f00f0f 12 34
1234567890abcd 5a5aa5a5f0f00f0f 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

View File

@ -33,8 +33,8 @@
#define RC_INT 0x0001 /* generic integer register */ #define RC_INT 0x0001 /* generic integer register */
#define RC_FLOAT 0x0002 /* generic float register */ #define RC_FLOAT 0x0002 /* generic float register */
#define RC_RAX 0x0004 #define RC_RAX 0x0004
#define RC_RCX 0x0008 #define RC_RDX 0x0008
#define RC_RDX 0x0010 #define RC_RCX 0x0010
#define RC_RSI 0x0020 #define RC_RSI 0x0020
#define RC_RDI 0x0040 #define RC_RDI 0x0040
#define RC_ST0 0x0080 /* only for long double */ #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) ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize)
{ {
int size, align, reg_count; int size, align, reg_count;
if (classify_x86_64_arg(vt, ret, &size, &align, &reg_count) == x86_64_mode_memory)
return 0;
*ret_align = 1; // Never have to re-align return values for x86-64 *ret_align = 1; // Never have to re-align return values for x86-64
*regsize = 8; *regsize = 8 * reg_count; /* the (virtual) regsize is 16 for VT_QLONG/QFLOAT */
return (classify_x86_64_arg(vt, ret, &size, &align, &reg_count) != x86_64_mode_memory); return 1;
} }
#define REGN 6 #define REGN 6