History

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 a82aff333774f3bd38841d3c167f1add8c473c1c fixes fd6d2180c5c801bb0b4c5dde27d61503059fc97d (slightly)

grischka committed 3 years ago

Commit c29420a1 parent 022fb425 files · +154 −100Browse files

Unified · Split

5 files
  1. i386-gen.c +7 −6
  2. tccgen.c +62 −49
  3. tests/tests2/131_return_struct_in_reg.c +59 −38
  4. tests/tests2/131_return_struct_in_reg.expect +20 −3
  5. x86_64-gen.c +6 −4
i386-gen.c+7−6
@@ -31,10 +31,10 @@ · 30 unmodified lines
3131 #define RC_INT 0x0001 /* generic integer register */
3232 #define RC_FLOAT 0x0002 /* generic float register */
3333 #define RC_EAX 0x0004
34−#define RC_ST0 0x0008
34+#define RC_EDX 0x0008
3535 #define RC_ECX 0x0010
36−#define RC_EDX 0x0020
37−#define RC_EBX 0x0040
36+#define RC_EBX 0x0020
37+#define RC_ST0 0x0040
3838
3939 #define RC_IRET RC_EAX /* function return: integer register */
4040 #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 }; · 339 unmodified lines
380380 ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize)
381381 {
382382 #if defined(TCC_TARGET_PE) || TARGETOS_FreeBSD || TARGETOS_OpenBSD
383− int size, align;
383+ int size, align, nregs;
384384 *ret_align = 1; // Never have to re-align return values for x86
385385 *regsize = 4;
386386 size = type_size(vt, &align);
387387 if (size > 8 || (size & (size - 1)))
388388 return 0;
389+ nregs = 1;
389390 if (size == 8)
390− ret->t = VT_LLONG;
391+ ret->t = VT_INT, nregs = 2;
391392 else if (size == 4)
392393 ret->t = VT_INT;
393394 else if (size == 2)
@@ -395,7 +396,7 @@ ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int · 1 unmodified line
395396 else
396397 ret->t = VT_BYTE;
397398 ret->ref = NULL;
398− return 1;
399+ return nregs;
399400 #else
400401 *ret_align = 1; // Never have to re-align return values for x86
401402 return 0;
tccgen.c+62−49
@@ -1687,14 +1687,22 @@ static void pop_local_syms(Sym *b, int keep) · 1686 unmodified lines
16871687 sym_pop(&local_stack, b, keep);
16881688 }
16891689
1690−static void incr_bf_adr(int o)
1690+/* increment an lvalue pointer */
1691+static void incr_offset(int offset)
16911692 {
1692− vtop->type = char_pointer_type;
1693− gaddrof();
1694− vpushs(o);
1693+ int t = vtop->type.t;
1694+ gaddrof(); /* remove VT_LVAL */
1695+ vtop->type.t = VT_PTRDIFF_T; /* set scalar type */
1696+ vpushs(offset);
16951697 gen_op('+');
1696− vtop->type.t = VT_BYTE | VT_UNSIGNED;
16971698 vtop->r |= VT_LVAL;
1699+ vtop->type.t = t;
1700+}
1701+
1702+static void incr_bf_adr(int o)
1703+{
1704+ vtop->type.t = VT_BYTE | VT_UNSIGNED;
1705+ incr_offset(o);
16981706 }
16991707
17001708 /* single-byte load mode for packed or otherwise unaligned bitfields */
@@ -1860,8 +1868,18 @@ ST_FUNC int gv(int rc) · 159 unmodified lines
18601868 r2_ok = !rc2 || ((vtop->r2 < VT_CONST) && (reg_classes[vtop->r2] & rc2));
18611869
18621870 if (!r_ok || !r2_ok) {
1863− if (!r_ok)
1864− r = get_reg(rc);
1871+
1872+ if (!r_ok) {
1873+ if (1 /* we can 'mov (r),r' in cases */
1874+ && r < VT_CONST
1875+ && (reg_classes[r] & rc)
1876+ && !rc2
1877+ )
1878+ save_reg_upstack(r, 1);
1879+ else
1880+ r = get_reg(rc);
1881+ }
1882+
18651883 if (rc2) {
18661884 int load_type = (bt == VT_QFLOAT) ? VT_DOUBLE : VT_PTRDIFF_T;
18671885 int original_type = vtop->type.t;
@@ -1885,12 +1903,7 @@ ST_FUNC int gv(int rc) · 17 unmodified lines
18851903 vdup();
18861904 vtop[-1].r = r; /* save register value */
18871905 /* increment pointer to get second word */
1888− vtop->type.t = VT_PTRDIFF_T;
1889− gaddrof();
1890− vpushs(PTR_SIZE);
1891− gen_op('+');
1892− vtop->r |= VT_LVAL;
1893− vtop->type.t = load_type;
1906+ incr_offset(PTR_SIZE);
18941907 } else {
18951908 /* move registers */
18961909 if (!r_ok)
@@ -3748,14 +3761,8 @@ ST_FUNC void vstore(void) · 1851 unmodified lines
37483761 vtop[-1].type.t = load_type;
37493762 store(r, vtop - 1);
37503763 vswap();
3751− /* convert to int to increment easily */
3752− vtop->type.t = VT_PTRDIFF_T;
3753− gaddrof();
3754− vpushs(PTR_SIZE);
3755− gen_op('+');
3756− vtop->r |= VT_LVAL;
3764+ incr_offset(PTR_SIZE);
37573765 vswap();
3758− vtop[-1].type.t = load_type;
37593766 /* XXX: it works because r2 is spilled last ! */
37603767 store(vtop->r2, vtop - 1);
37613768 } else {
@@ -6003,7 +6010,6 @@ special_math_val: · 2241 unmodified lines
60036010 indir();
60046011 qualifiers = vtop->type.t & (VT_CONSTANT | VT_VOLATILE);
60056012 test_lvalue();
6006− gaddrof();
60076013 /* expect pointer on structure */
60086014 if ((vtop->type.t & VT_BTYPE) != VT_STRUCT)
60096015 expect("struct or union");
@@ -6014,16 +6020,15 @@ special_math_val: · 4 unmodified lines
60146020 expect("field name");
60156021 s = find_field(&vtop->type, tok, &cumofs);
60166022 /* add field offset to pointer */
6017− vtop->type = char_pointer_type; /* change type to 'char *' */
6018− vpushi(cumofs);
6019− gen_op('+');
6023+ incr_offset(cumofs);
60206024 /* change type to field type, and set to lvalue */
60216025 vtop->type = s->type;
60226026 vtop->type.t |= qualifiers;
60236027 /* an array is never an lvalue */
6024− if (!(vtop->type.t & VT_ARRAY)) {
6025− vtop->r |= VT_LVAL;
6028+ if (vtop->type.t & VT_ARRAY) {
6029+ vtop->r &= ~VT_LVAL;
60266030 #ifdef CONFIG_TCC_BCHECK
6031+ } else {
60276032 /* if bound checking, the referenced pointer must be checked */
60286033 if (tcc_state->do_bounds_check)
60296034 vtop->r |= VT_MUSTBOUND;
@@ -6128,10 +6133,20 @@ special_math_val: · 98 unmodified lines
61286133 #endif
61296134 } else {
61306135 /* return value */
6131− for (r = ret.r + ret_nregs + !ret_nregs; r-- > ret.r;) {
6136+ n = ret_nregs;
6137+ while (n > 1) {
6138+ int rc = reg_classes[ret.r] & ~(RC_INT | RC_FLOAT);
6139+ /* We assume that when a structure is returned in multiple
6140+ registers, their classes are consecutive values of the
6141+ suite s(n) = 2^n */
6142+ rc <<= --n;
6143+ for (r = 0; r < NB_REGS; ++r)
6144+ if (reg_classes[r] & rc)
6145+ break;
61326146 vsetc(&ret.type, r, &ret.c);
6133− vtop->r2 = ret.r2; /* Loop only happens when r2 is VT_CONST */
61346147 }
6148+ vsetc(&ret.type, ret.r, &ret.c);
6149+ vtop->r2 = ret.r2;
61356150
61366151 /* handle packed struct return */
61376152 if (((s->type.t & VT_BTYPE) == VT_STRUCT) && ret_nregs) {
@@ -6140,9 +6155,9 @@ special_math_val: · 2 unmodified lines
61406155 size = type_size(&s->type, &align);
61416156 /* We're writing whole regs often, make sure there's enough
61426157 space. Assume register size is power of 2. */
6143− if (regsize > align)
6144− align = regsize;
6145− loc &= -align;
6158+ size = (size + regsize - 1) & -regsize;
6159+ if (ret_align > align)
6160+ align = ret_align;
61466161 loc = (loc - size) & -align;
61476162 addr = loc;
61486163 offset = 0;
@@ -6619,11 +6634,12 @@ static void gfunc_return(CType *func_type) · 470 unmodified lines
66196634 vstore();
66206635 } else {
66216636 /* returning structure packed into registers */
6622− int size, addr, align, rc;
6637+ int size, addr, align, rc, n;
66236638 size = type_size(func_type,&align);
6624− if ((vtop->r != (VT_LOCAL | VT_LVAL) ||
6625− (vtop->c.i & (ret_align-1)))
6626− && (align & (ret_align-1))) {
6639+ if ((align & (ret_align - 1))
6640+ && ((vtop->r & VT_VALMASK) < VT_CONST /* pointer to struct */
6641+ || (vtop->c.i & (ret_align - 1))
6642+ )) {
66276643 loc = (loc - size) & -ret_align;
66286644 addr = loc;
66296645 type = *func_type;
@@ -6635,22 +6651,19 @@ static void gfunc_return(CType *func_type) · 5 unmodified lines
66356651 }
66366652 vtop->type = ret_type;
66376653 rc = RC_RET(ret_type.t);
6638− if (ret_nregs == 1)
6654+ //printf("struct return: n:%d t:%02x rc:%02x\n", ret_nregs, ret_type.t, rc);
6655+ for (n = ret_nregs; --n > 0;) {
6656+ vdup();
66396657 gv(rc);
6640− else {
6641− for (;;) {
6642− vdup();
6643− gv(rc);
6644− vpop();
6645− if (--ret_nregs == 0)
6646− break;
6647− /* We assume that when a structure is returned in multiple
6648− registers, their classes are consecutive values of the
6649− suite s(n) = 2^n */
6650− rc <<= 1;
6651− vtop->c.i += regsize;
6652− }
6658+ vswap();
6659+ incr_offset(regsize);
6660+ /* We assume that when a structure is returned in multiple
6661+ registers, their classes are consecutive values of the
6662+ suite s(n) = 2^n */
6663+ rc <<= 1;
66536664 }
6665+ gv(rc);
6666+ vtop -= ret_nregs - 1;
66546667 }
66556668 } else {
66566669 gv(RC_RET(func_type->t));
tests/tests2/131_return_struct_in_reg.c+59−38
@@ -1,49 +1,70 @@
11 #include<stdio.h>
22
3−#define DATA 0x1234567890abcdll, 0x5a5aa5a5f0f00f0fll
3+struct s1 {
4+ unsigned char a:4, b:4;
5+} g1 = { 0x05, 0x0a };
46
5−struct s {
6− long long int a;
7− long long int b;
8−};
7+struct s2 {
8+ unsigned char a, b;
9+} g2 = { 0x12, 0x34 };
910
10−struct {
11− struct s d;
12−} g = { { DATA } }, *gp = &g;
11+struct s4 {
12+ unsigned short a, b;
13+} g4 = { 0x1245, 0x5678 };
1314
14−struct s
15−foo1(void)
16−{
17− struct s d = { DATA };
18− struct s *p = &d;
19− long long int x = 0;
20− return *p;
21−}
15+struct s8 {
16+ unsigned a, b;
17+} g8 = { 0x12345678, 0x9abcdef0 };
2218
23−struct s
24−foo2(void)
25−{
26− long long int unused = 0;
27− return gp->d;
28−}
19+/* returned in 2 registers on riscv64 */
20+struct s16 {
21+ unsigned long long a, b;
22+} g16 = { 0x123456789abcdef0ULL, 0xfedcba9876543210ULL };
2923
30−struct s
31−foo3(void)
32−{
33− struct s d = { DATA };
34− long long int unused = 0;
35− return d;
24+/* Homogeneous float aggregate on ARM hard-float */
25+struct s_f4 {
26+ double a, b, c, d;
27+} g_f4 = { 1,2,3,4 };
28+
29+#define def(S) \
30+struct s##S f##S(int x) \
31+{ \
32+ struct s##S l##S = g##S, *p##S = &l##S; \
33+ if (x == 0) \
34+ return g##S; \
35+ else if (x == 1) \
36+ return l##S; \
37+ else \
38+ return *p##S; \
3639 }
3740
38−int
39−main(void)
41+def(1)
42+def(2)
43+def(4)
44+def(8)
45+def(16)
46+def(_f4)
47+
48+#define chk(S,x) \
49+ struct s##S l##S = f##S(x); \
50+ printf("%02llx %02llx\n", \
51+ (unsigned long long)l##S.a, \
52+ (unsigned long long)l##S.b \
53+ );
54+
55+int main()
4056 {
41− struct s d;
42− d = foo1();
43− printf("%llx %llx\n", d.a, d.b);
44− d = foo2();
45− printf("%llx %llx\n", d.a, d.b);
46− d = foo3();
47− printf("%llx %llx\n", d.a, d.b);
48− return 0;
57+ for (int x = 0;;) {
58+ chk(1,x);
59+ chk(2,x);
60+ chk(4,x);
61+ chk(8,x);
62+ chk(16,x);
63+ struct s_f4 l_f4 = f_f4(x);
64+ printf("%.1f %.1f %.1f %.1f\n", l_f4.a, l_f4.b, l_f4.c, l_f4.d);
65+ if (++x > 2)
66+ break;
67+ printf("\n");
68+ }
69+ return 0;
4970 }
tests/tests2/131_return_struct_in_reg.expect+20−3
@@ -1,3 +1,20 @@
1−1234567890abcd 5a5aa5a5f0f00f0f
2−1234567890abcd 5a5aa5a5f0f00f0f
3−1234567890abcd 5a5aa5a5f0f00f0f
1+05 0a
2+12 34
3+1245 5678
4+12345678 9abcdef0
5+123456789abcdef0 fedcba9876543210
6+1.0 2.0 3.0 4.0
7+
8+05 0a
9+12 34
10+1245 5678
11+12345678 9abcdef0
12+123456789abcdef0 fedcba9876543210
13+1.0 2.0 3.0 4.0
14+
15+05 0a
16+12 34
17+1245 5678
18+12345678 9abcdef0
19+123456789abcdef0 fedcba9876543210
20+1.0 2.0 3.0 4.0
x86_64-gen.c+6−4
@@ -33,8 +33,8 @@ · 32 unmodified lines
3333 #define RC_INT 0x0001 /* generic integer register */
3434 #define RC_FLOAT 0x0002 /* generic float register */
3535 #define RC_RAX 0x0004
36−#define RC_RCX 0x0008
37−#define RC_RDX 0x0010
36+#define RC_RDX 0x0008
37+#define RC_RCX 0x0010
3838 #define RC_RSI 0x0020
3939 #define RC_RDI 0x0040
4040 #define RC_ST0 0x0080 /* only for long double */
@@ -1223,9 +1223,11 @@ ST_FUNC int classify_x86_64_va_arg(CType *ty) · 1182 unmodified lines
12231223 ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize)
12241224 {
12251225 int size, align, reg_count;
1226+ if (classify_x86_64_arg(vt, ret, &size, &align, &reg_count) == x86_64_mode_memory)
1227+ return 0;
12261228 *ret_align = 1; // Never have to re-align return values for x86-64
1227− *regsize = 8;
1228− return (classify_x86_64_arg(vt, ret, &size, &align, &reg_count) != x86_64_mode_memory);
1229+ *regsize = 8 * reg_count; /* the (virtual) regsize is 16 for VT_QLONG/QFLOAT */
1230+ return 1;
12291231 }
12301232
12311233 #define REGN 6

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t