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_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;

111
tccgen.c
View File

@ -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));

View File

@ -1,49 +1,70 @@
#include<stdio.h>
#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;
}

View File

@ -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

View File

@ -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, &reg_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, &reg_count) != x86_64_mode_memory);
*regsize = 8 * reg_count; /* the (virtual) regsize is 16 for VT_QLONG/QFLOAT */
return 1;
}
#define REGN 6