mirror of
https://github.com/mirror/tinycc.git
synced 2025-03-10 08:50:07 +08:00
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) fixesa82aff3337
fixesfd6d2180c5
(slightly)
This commit is contained in:
parent
022fb4293b
commit
c29420ab0d
13
i386-gen.c
13
i386-gen.c
@ -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;
|
||||||
|
99
tccgen.c
99
tccgen.c
@ -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)
|
|
||||||
|
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);
|
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);
|
||||||
gv(rc);
|
for (n = ret_nregs; --n > 0;) {
|
||||||
else {
|
|
||||||
for (;;) {
|
|
||||||
vdup();
|
vdup();
|
||||||
gv(rc);
|
gv(rc);
|
||||||
vpop();
|
vswap();
|
||||||
if (--ret_nregs == 0)
|
incr_offset(regsize);
|
||||||
break;
|
|
||||||
/* We assume that when a structure is returned in multiple
|
/* We assume that when a structure is returned in multiple
|
||||||
registers, their classes are consecutive values of the
|
registers, their classes are consecutive values of the
|
||||||
suite s(n) = 2^n */
|
suite s(n) = 2^n */
|
||||||
rc <<= 1;
|
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));
|
||||||
|
@ -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);
|
||||||
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
10
x86_64-gen.c
10
x86_64-gen.c
@ -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, ®_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, ®_count) != x86_64_mode_memory);
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define REGN 6
|
#define REGN 6
|
||||||
|
Loading…
Reference in New Issue
Block a user