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, dollars_in_identifiers), 0, "dollars-in-identifiers" },
{ offsetof(TCCState, test_coverage), 0, "test-coverage" },
{ offsetof(TCCState, reverse_funcargs), 0, "reverse-funcargs" },
{ 0, 0, NULL }
};

1
tcc.c
View file

@ -115,6 +115,7 @@ static const char help2[] =
" leading-underscore decorate extern symbols\n"
" ms-extensions allow anonymous struct in struct\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"
"-m... target specific options:\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 dollars_in_identifiers; /* allows '$' char in identifiers */
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 */
unsigned char warn_none;
@ -1490,9 +1491,9 @@ ST_FUNC void vpushi(int v);
ST_FUNC void vpushv(SValue *v);
ST_FUNC void vpushsym(CType *type, Sym *sym);
ST_FUNC void vswap(void);
ST_FUNC void vrote(SValue *e, int n);
ST_FUNC void vrott(int n);
ST_FUNC void vrotb(int n);
ST_FUNC void vrev(int n);
ST_FUNC void vpop(void);
#if PTR_SIZE == 4
ST_FUNC void lexpand(void);

176
tccgen.c
View file

@ -154,8 +154,7 @@ static void gen_inline_functions(TCCState *s);
static void free_inline_functions(TCCState *s);
static void skip_or_save_block(TokenString **str);
static void gv_dup(void);
static int get_temp_local_var(int size,int align);
static void clear_temp_local_var_list();
static int get_temp_local_var(int size,int align,int *r2);
static void cast_error(CType *st, CType *dt);
static void end_switch(void);
@ -957,42 +956,38 @@ static void vdup(void)
vpushv(vtop);
}
/* rotate n first stack elements to the bottom
I1 ... In -> I2 ... In I1 [top is right]
*/
/* rotate the stack element at position n-1 to the top */
ST_FUNC void vrotb(int n)
{
int i;
SValue tmp;
if (--n < 1)
return;
vcheck_cmp();
tmp = vtop[-n + 1];
for(i=-n+1;i!=0;i++)
vtop[i] = vtop[i+1];
tmp = vtop[-n];
memmove(vtop - n, vtop - n + 1, sizeof *vtop * n);
vtop[0] = tmp;
}
/* rotate the n elements before entry e towards the top
I1 ... In ... -> In I1 ... I(n-1) ... [top is right]
*/
ST_FUNC void vrote(SValue *e, int n)
/* rotate the top stack element into position n-1 */
ST_FUNC void vrott(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;
SValue tmp;
vcheck_cmp();
tmp = *e;
for(i = 0;i < n - 1; i++)
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);
for (i = 0, n = -n; i > ++n; --i)
tmp = vtop[i], vtop[i] = vtop[n], vtop[n] = tmp;
}
/* ------------------------------------------------------------------------- */
@ -1354,14 +1349,14 @@ ST_FUNC void save_reg(int r)
if seen up to (vtop - n) stack entry */
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;
if ((r &= VT_VALMASK) >= VT_CONST)
return;
if (nocode_wanted)
return;
l = 0;
l = r2 = 0;
for(p = vstack, p1 = vtop - n; p <= p1; p++) {
if ((p->r & VT_VALMASK) == r || p->r2 == r) {
/* 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;
sv.type.t = bt;
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.c.i = l;
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->sym = NULL;
p->r2 = VT_CONST;
p->r2 = r2;
p->c.i = l;
}
}
@ -1471,54 +1466,46 @@ ST_FUNC int get_reg(int rc)
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*/
static int get_temp_local_var(int size,int align){
int i;
struct temp_local_variable *temp_var;
int found_var;
SValue *p;
int r;
char free;
char found;
found=0;
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++) {
r=p->r&VT_VALMASK;
if(r==VT_LOCAL||r==VT_LLOCAL){
if(p->c.i==temp_var->location){
free=0;
break;
}
}
}
if(free){
found_var=temp_var->location;
found=1;
break;
}
}
if(!found){
loc = (loc - size) & -align;
if(nb_temp_local_vars<MAX_TEMP_LOCAL_VARIABLE_NUMBER){
temp_var=&arr_temp_local_vars[i];
temp_var->location=loc;
temp_var->size=size;
temp_var->align=align;
nb_temp_local_vars++;
}
found_var=loc;
}
return found_var;
}
/* find a free temporary local variable (return the offset on stack) match
size and align. If none, add new temporary stack variable */
static int get_temp_local_var(int size,int align, int *r2)
{
int i;
struct temp_local_variable *temp_var;
SValue *p;
int r;
unsigned used = 0;
static void clear_temp_local_var_list(){
nb_temp_local_vars=0;
/* mark locations that are still in use */
for (p = vstack; p <= vtop; p++) {
r = p->r & VT_VALMASK;
if (r == VT_LOCAL || r == VT_LLOCAL) {
r = p->r2 - (VT_CONST + 1);
if (r >= 0 && r < MAX_TEMP_LOCAL_VARIABLE_NUMBER)
used |= 1<<r;
}
}
for (i=0;i<nb_temp_local_vars;i++) {
temp_var=&arr_temp_local_vars[i];
if(!(used & 1<<i)
&& temp_var->size>=size
&& temp_var->align>=align) {
ret_tmp:
*r2 = (VT_CONST + 1) + i;
return temp_var->location;
}
}
loc = (loc - size) & -align;
if (nb_temp_local_vars<MAX_TEMP_LOCAL_VARIABLE_NUMBER) {
temp_var=&arr_temp_local_vars[i];
temp_var->location=loc;
temp_var->size=size;
temp_var->align=align;
nb_temp_local_vars++;
goto ret_tmp;
}
*r2 = VT_CONST;
return loc;
}
/* 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;
Sym *sa;
int nb_args, ret_nregs, ret_align, regsize, variadic;
TokenString *p, *p2;
/* function call */
if ((vtop->type.t & VT_BTYPE) != VT_FUNC) {
@ -6120,10 +6108,18 @@ special_math_val:
ret.c.i = 0;
PUT_R_RET(&ret, ret.type.t);
}
p = NULL;
if (tok != ')') {
r = tcc_state->reverse_funcargs;
for(;;) {
expr_eq();
gfunc_param_typed(s, sa);
if (r) {
skip_or_save_block(&p2);
p2->prev = p, p = p2;
} else {
expr_eq();
gfunc_param_typed(s, sa);
}
nb_args++;
if (sa)
sa = sa->next;
@ -6134,7 +6130,23 @@ special_math_val:
}
if (sa)
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);
if (ret_nregs < 0) {
@ -8335,12 +8347,12 @@ static void gen_function(Sym *sym)
/* push a dummy symbol to enable local sym storage */
sym_push2(&local_stack, SYM_FIELD, 0, 0);
local_scope = 1; /* for function parameters */
nb_temp_local_vars = 0;
gfunc_prolog(sym);
tcc_debug_prolog_epilog(tcc_state, 0);
local_scope = 0;
rsym = 0;
clear_temp_local_var_list();
func_vla_arg(sym);
block(0);
gsym(rsym);

View file

@ -505,5 +505,15 @@ int main()
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

View file

@ -243,4 +243,10 @@ arg[1] = "Y"
[test_duplicate_case]
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