tcc -freverse-funcargs (reverse evaluation)

patch originally made to prove correctness (comparing stages)
with tinycc compiling gcc 2.95.3 which would assign registers
differently (but still correctly) when compiled with tcc without
this option).

Also: fixes get_temp_local_var() which on 32-bit systems
happened to return a temporary location that was still
in use because its offset was changed on the vstack
(incremented by four in gv() to load the second register
of a long long).

Also: optimize vrot-t/b (slightly) by using one memmove
instead of moving elements one by one in a loop.
This commit is contained in:
grischka 2024-11-17 21:04:29 +01:00
parent c717bac6e3
commit f24727b6bb
6 changed files with 115 additions and 84 deletions

View file

@ -1673,6 +1673,7 @@ static const FlagDef options_f[] = {
{ offsetof(TCCState, ms_extensions), 0, "ms-extensions" }, { offsetof(TCCState, ms_extensions), 0, "ms-extensions" },
{ offsetof(TCCState, dollars_in_identifiers), 0, "dollars-in-identifiers" }, { offsetof(TCCState, dollars_in_identifiers), 0, "dollars-in-identifiers" },
{ offsetof(TCCState, test_coverage), 0, "test-coverage" }, { offsetof(TCCState, test_coverage), 0, "test-coverage" },
{ offsetof(TCCState, reverse_funcargs), 0, "reverse-funcargs" },
{ 0, 0, NULL } { 0, 0, NULL }
}; };

1
tcc.c
View file

@ -115,6 +115,7 @@ static const char help2[] =
" leading-underscore decorate extern symbols\n" " leading-underscore decorate extern symbols\n"
" ms-extensions allow anonymous struct in struct\n" " ms-extensions allow anonymous struct in struct\n"
" dollars-in-identifiers allow '$' in C symbols\n" " dollars-in-identifiers allow '$' in C symbols\n"
" reverse-funcargs evaluate function arguments right to left\n"
" test-coverage create code coverage code\n" " test-coverage create code coverage code\n"
"-m... target specific options:\n" "-m... target specific options:\n"
" ms-bitfields use MSVC bitfield layout\n" " ms-bitfields use MSVC bitfield layout\n"

3
tcc.h
View file

@ -776,6 +776,7 @@ struct TCCState {
unsigned char ms_extensions; /* allow nested named struct w/o identifier behave like unnamed */ unsigned char ms_extensions; /* allow nested named struct w/o identifier behave like unnamed */
unsigned char dollars_in_identifiers; /* allows '$' char in identifiers */ unsigned char dollars_in_identifiers; /* allows '$' char in identifiers */
unsigned char ms_bitfields; /* if true, emulate MS algorithm for aligning bitfields */ unsigned char ms_bitfields; /* if true, emulate MS algorithm for aligning bitfields */
unsigned char reverse_funcargs; /* if true, evaluate last function arg first */
/* warning switches */ /* warning switches */
unsigned char warn_none; unsigned char warn_none;
@ -1490,9 +1491,9 @@ ST_FUNC void vpushi(int v);
ST_FUNC void vpushv(SValue *v); ST_FUNC void vpushv(SValue *v);
ST_FUNC void vpushsym(CType *type, Sym *sym); ST_FUNC void vpushsym(CType *type, Sym *sym);
ST_FUNC void vswap(void); ST_FUNC void vswap(void);
ST_FUNC void vrote(SValue *e, int n);
ST_FUNC void vrott(int n); ST_FUNC void vrott(int n);
ST_FUNC void vrotb(int n); ST_FUNC void vrotb(int n);
ST_FUNC void vrev(int n);
ST_FUNC void vpop(void); ST_FUNC void vpop(void);
#if PTR_SIZE == 4 #if PTR_SIZE == 4
ST_FUNC void lexpand(void); ST_FUNC void lexpand(void);

136
tccgen.c
View file

@ -154,8 +154,7 @@ static void gen_inline_functions(TCCState *s);
static void free_inline_functions(TCCState *s); static void free_inline_functions(TCCState *s);
static void skip_or_save_block(TokenString **str); static void skip_or_save_block(TokenString **str);
static void gv_dup(void); static void gv_dup(void);
static int get_temp_local_var(int size,int align); static int get_temp_local_var(int size,int align,int *r2);
static void clear_temp_local_var_list();
static void cast_error(CType *st, CType *dt); static void cast_error(CType *st, CType *dt);
static void end_switch(void); static void end_switch(void);
@ -957,42 +956,38 @@ static void vdup(void)
vpushv(vtop); vpushv(vtop);
} }
/* rotate n first stack elements to the bottom /* rotate the stack element at position n-1 to the top */
I1 ... In -> I2 ... In I1 [top is right]
*/
ST_FUNC void vrotb(int n) ST_FUNC void vrotb(int n)
{ {
int i;
SValue tmp; SValue tmp;
if (--n < 1)
return;
vcheck_cmp(); vcheck_cmp();
tmp = vtop[-n + 1]; tmp = vtop[-n];
for(i=-n+1;i!=0;i++) memmove(vtop - n, vtop - n + 1, sizeof *vtop * n);
vtop[i] = vtop[i+1];
vtop[0] = tmp; vtop[0] = tmp;
} }
/* rotate the n elements before entry e towards the top /* rotate the top stack element into position n-1 */
I1 ... In ... -> In I1 ... I(n-1) ... [top is right] ST_FUNC void vrott(int n)
*/ {
ST_FUNC void vrote(SValue *e, int n) SValue tmp;
if (--n < 1)
return;
vcheck_cmp();
tmp = vtop[0];
memmove(vtop - n + 1, vtop - n, sizeof *vtop * n);
vtop[-n] = tmp;
}
/* reverse order of the the first n stack elements */
ST_FUNC void vrev(int n)
{ {
int i; int i;
SValue tmp; SValue tmp;
vcheck_cmp(); vcheck_cmp();
tmp = *e; for (i = 0, n = -n; i > ++n; --i)
for(i = 0;i < n - 1; i++) tmp = vtop[i], vtop[i] = vtop[n], vtop[n] = tmp;
e[-i] = e[-i - 1];
e[-n + 1] = tmp;
}
/* rotate n first stack elements to the top
I1 ... In -> In I1 ... I(n-1) [top is right]
*/
ST_FUNC void vrott(int n)
{
vrote(vtop, n);
} }
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
@ -1354,14 +1349,14 @@ ST_FUNC void save_reg(int r)
if seen up to (vtop - n) stack entry */ if seen up to (vtop - n) stack entry */
ST_FUNC void save_reg_upstack(int r, int n) ST_FUNC void save_reg_upstack(int r, int n)
{ {
int l, size, align, bt; int l, size, align, bt, r2;
SValue *p, *p1, sv; SValue *p, *p1, sv;
if ((r &= VT_VALMASK) >= VT_CONST) if ((r &= VT_VALMASK) >= VT_CONST)
return; return;
if (nocode_wanted) if (nocode_wanted)
return; return;
l = 0; l = r2 = 0;
for(p = vstack, p1 = vtop - n; p <= p1; p++) { for(p = vstack, p1 = vtop - n; p <= p1; p++) {
if ((p->r & VT_VALMASK) == r || p->r2 == r) { if ((p->r & VT_VALMASK) == r || p->r2 == r) {
/* must save value on stack if not already done */ /* must save value on stack if not already done */
@ -1373,7 +1368,7 @@ ST_FUNC void save_reg_upstack(int r, int n)
bt = VT_PTR; bt = VT_PTR;
sv.type.t = bt; sv.type.t = bt;
size = type_size(&sv.type, &align); size = type_size(&sv.type, &align);
l = get_temp_local_var(size,align); l = get_temp_local_var(size, align, &r2);
sv.r = VT_LOCAL | VT_LVAL; sv.r = VT_LOCAL | VT_LVAL;
sv.c.i = l; sv.c.i = l;
store(p->r & VT_VALMASK, &sv); store(p->r & VT_VALMASK, &sv);
@ -1400,7 +1395,7 @@ ST_FUNC void save_reg_upstack(int r, int n)
p->type.t &= ~VT_ARRAY; /* cannot combine VT_LVAL with VT_ARRAY */ p->type.t &= ~VT_ARRAY; /* cannot combine VT_LVAL with VT_ARRAY */
} }
p->sym = NULL; p->sym = NULL;
p->r2 = VT_CONST; p->r2 = r2;
p->c.i = l; p->c.i = l;
} }
} }
@ -1471,39 +1466,35 @@ ST_FUNC int get_reg(int rc)
return -1; return -1;
} }
/* find a free temporary local variable (return the offset on stack) match the size and align. If none, add new temporary stack variable*/ /* find a free temporary local variable (return the offset on stack) match
static int get_temp_local_var(int size,int align){ size and align. If none, add new temporary stack variable */
static int get_temp_local_var(int size,int align, int *r2)
{
int i; int i;
struct temp_local_variable *temp_var; struct temp_local_variable *temp_var;
int found_var;
SValue *p; SValue *p;
int r; int r;
char free; unsigned used = 0;
char found;
found=0; /* mark locations that are still in use */
for(i=0;i<nb_temp_local_vars;i++){
temp_var=&arr_temp_local_vars[i];
if(temp_var->size<size||align!=temp_var->align){
continue;
}
/*check if temp_var is free*/
free=1;
for (p = vstack; p <= vtop; p++) { for (p = vstack; p <= vtop; p++) {
r = p->r & VT_VALMASK; r = p->r & VT_VALMASK;
if (r == VT_LOCAL || r == VT_LLOCAL) { if (r == VT_LOCAL || r == VT_LLOCAL) {
if(p->c.i==temp_var->location){ r = p->r2 - (VT_CONST + 1);
free=0; if (r >= 0 && r < MAX_TEMP_LOCAL_VARIABLE_NUMBER)
break; used |= 1<<r;
} }
} }
} for (i=0;i<nb_temp_local_vars;i++) {
if(free){ temp_var=&arr_temp_local_vars[i];
found_var=temp_var->location; if(!(used & 1<<i)
found=1; && temp_var->size>=size
break; && temp_var->align>=align) {
ret_tmp:
*r2 = (VT_CONST + 1) + i;
return temp_var->location;
} }
} }
if(!found){
loc = (loc - size) & -align; loc = (loc - size) & -align;
if (nb_temp_local_vars<MAX_TEMP_LOCAL_VARIABLE_NUMBER) { if (nb_temp_local_vars<MAX_TEMP_LOCAL_VARIABLE_NUMBER) {
temp_var=&arr_temp_local_vars[i]; temp_var=&arr_temp_local_vars[i];
@ -1511,14 +1502,10 @@ static int get_temp_local_var(int size,int align){
temp_var->size=size; temp_var->size=size;
temp_var->align=align; temp_var->align=align;
nb_temp_local_vars++; nb_temp_local_vars++;
goto ret_tmp;
} }
found_var=loc; *r2 = VT_CONST;
} return loc;
return found_var;
}
static void clear_temp_local_var_list(){
nb_temp_local_vars=0;
} }
/* move register 's' (of type 't') to 'r', and flush previous value of r to memory /* move register 's' (of type 't') to 'r', and flush previous value of r to memory
@ -6056,6 +6043,7 @@ special_math_val:
SValue ret; SValue ret;
Sym *sa; Sym *sa;
int nb_args, ret_nregs, ret_align, regsize, variadic; int nb_args, ret_nregs, ret_align, regsize, variadic;
TokenString *p, *p2;
/* function call */ /* function call */
if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { if ((vtop->type.t & VT_BTYPE) != VT_FUNC) {
@ -6120,10 +6108,18 @@ special_math_val:
ret.c.i = 0; ret.c.i = 0;
PUT_R_RET(&ret, ret.type.t); PUT_R_RET(&ret, ret.type.t);
} }
p = NULL;
if (tok != ')') { if (tok != ')') {
r = tcc_state->reverse_funcargs;
for(;;) { for(;;) {
if (r) {
skip_or_save_block(&p2);
p2->prev = p, p = p2;
} else {
expr_eq(); expr_eq();
gfunc_param_typed(s, sa); gfunc_param_typed(s, sa);
}
nb_args++; nb_args++;
if (sa) if (sa)
sa = sa->next; sa = sa->next;
@ -6134,7 +6130,23 @@ special_math_val:
} }
if (sa) if (sa)
tcc_error("too few arguments to function"); tcc_error("too few arguments to function");
skip(')');
if (p) { /* with reverse_funcargs */
for (n = 0; p; p = p2, ++n) {
p2 = p, sa = s;
do {
sa = sa->next, p2 = p2->prev;
} while (p2 && sa);
p2 = p->prev;
begin_macro(p, 1), next();
expr_eq();
gfunc_param_typed(s, sa);
end_macro();
}
vrev(n);
}
next();
gfunc_call(nb_args); gfunc_call(nb_args);
if (ret_nregs < 0) { if (ret_nregs < 0) {
@ -8335,12 +8347,12 @@ static void gen_function(Sym *sym)
/* push a dummy symbol to enable local sym storage */ /* push a dummy symbol to enable local sym storage */
sym_push2(&local_stack, SYM_FIELD, 0, 0); sym_push2(&local_stack, SYM_FIELD, 0, 0);
local_scope = 1; /* for function parameters */ local_scope = 1; /* for function parameters */
nb_temp_local_vars = 0;
gfunc_prolog(sym); gfunc_prolog(sym);
tcc_debug_prolog_epilog(tcc_state, 0); tcc_debug_prolog_epilog(tcc_state, 0);
local_scope = 0; local_scope = 0;
rsym = 0; rsym = 0;
clear_temp_local_var_list();
func_vla_arg(sym); func_vla_arg(sym);
block(0); block(0);
gsym(rsym); gsym(rsym);

View file

@ -505,5 +505,15 @@ int main()
case 5: case 5:
} }
} }
#elif defined test_normal_funcargs || defined test_reverse_funcargs
#ifdef test_reverse_funcargs
# pragma comment(option, "-freverse-funcargs")
#endif
int printf(const char*, ...);
int main()
{
printf(" %d %d %d\n", printf("1"), printf("22"), printf("333"));
}
#endif #endif

View file

@ -243,4 +243,10 @@ arg[1] = "Y"
[test_duplicate_case] [test_duplicate_case]
60_errors_and_warnings.c:499: warning: empty case range 60_errors_and_warnings.c:499: warning: empty case range
60_errors_and_warnings.c:507: error: duplicate case value 60_errors_and_warnings.c:503: error: duplicate case value
[test_normal_funcargs]
122333 1 2 3
[test_reverse_funcargs]
333221 1 2 3