mirror of
https://github.com/frida/tinycc
synced 2024-11-24 08:39:37 +03:00
switch: collect case ranges first, then generate code
Collect cases first, then emit lookup code. Elliminates jumps to implement pass-through and jumps to link cases.
This commit is contained in:
parent
d5a1e32ac3
commit
fc0fc6aba3
114
tccgen.c
114
tccgen.c
@ -72,6 +72,13 @@ ST_DATA const char *funcname;
|
|||||||
|
|
||||||
ST_DATA CType char_pointer_type, func_old_type, int_type, size_type;
|
ST_DATA CType char_pointer_type, func_old_type, int_type, size_type;
|
||||||
|
|
||||||
|
ST_DATA struct switch_t {
|
||||||
|
struct case_t {
|
||||||
|
int v1, v2, sym;
|
||||||
|
} **p; int n; /* list of case ranges */
|
||||||
|
int def_sym; /* default symbol */
|
||||||
|
} *cur_switch; /* current switch */
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
static void gen_cast(CType *type);
|
static void gen_cast(CType *type);
|
||||||
static inline CType *pointed_type(CType *type);
|
static inline CType *pointed_type(CType *type);
|
||||||
@ -80,7 +87,7 @@ static int parse_btype(CType *type, AttributeDef *ad);
|
|||||||
static void type_decl(CType *type, AttributeDef *ad, int *v, int td);
|
static void type_decl(CType *type, AttributeDef *ad, int *v, int td);
|
||||||
static void parse_expr_type(CType *type);
|
static void parse_expr_type(CType *type);
|
||||||
static void decl_initializer(CType *type, Section *sec, unsigned long c, int first, int size_only);
|
static void decl_initializer(CType *type, Section *sec, unsigned long c, int first, int size_only);
|
||||||
static void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_reg, int is_expr);
|
static void block(int *bsym, int *csym, int is_expr);
|
||||||
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, int scope);
|
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, int scope);
|
||||||
static int decl0(int l, int is_for_loop_init);
|
static int decl0(int l, int is_for_loop_init);
|
||||||
static void expr_eq(void);
|
static void expr_eq(void);
|
||||||
@ -3859,7 +3866,7 @@ ST_FUNC void unary(void)
|
|||||||
save_regs(0);
|
save_regs(0);
|
||||||
/* statement expression : we do not accept break/continue
|
/* statement expression : we do not accept break/continue
|
||||||
inside as GCC does */
|
inside as GCC does */
|
||||||
block(NULL, NULL, NULL, NULL, 0, 1);
|
block(NULL, NULL, 1);
|
||||||
skip(')');
|
skip(')');
|
||||||
} else {
|
} else {
|
||||||
gexpr();
|
gexpr();
|
||||||
@ -4876,8 +4883,10 @@ static void label_or_decl(int l)
|
|||||||
decl(l);
|
decl(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
|
static int case_cmp(const void *a, const void *b)
|
||||||
int case_reg, int is_expr)
|
{ return (*(struct case_t**) a)->v1 - (*(struct case_t**) b)->v1; }
|
||||||
|
|
||||||
|
static void block(int *bsym, int *csym, int is_expr)
|
||||||
{
|
{
|
||||||
int a, b, c, d;
|
int a, b, c, d;
|
||||||
Sym *s;
|
Sym *s;
|
||||||
@ -4903,13 +4912,13 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
|
|||||||
gexpr();
|
gexpr();
|
||||||
skip(')');
|
skip(')');
|
||||||
a = gvtst(1, 0);
|
a = gvtst(1, 0);
|
||||||
block(bsym, csym, case_sym, def_sym, case_reg, 0);
|
block(bsym, csym, 0);
|
||||||
c = tok;
|
c = tok;
|
||||||
if (c == TOK_ELSE) {
|
if (c == TOK_ELSE) {
|
||||||
next();
|
next();
|
||||||
d = gjmp(0);
|
d = gjmp(0);
|
||||||
gsym(a);
|
gsym(a);
|
||||||
block(bsym, csym, case_sym, def_sym, case_reg, 0);
|
block(bsym, csym, 0);
|
||||||
gsym(d); /* patch else jmp */
|
gsym(d); /* patch else jmp */
|
||||||
} else
|
} else
|
||||||
gsym(a);
|
gsym(a);
|
||||||
@ -4923,7 +4932,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
|
|||||||
a = gvtst(1, 0);
|
a = gvtst(1, 0);
|
||||||
b = 0;
|
b = 0;
|
||||||
++local_scope;
|
++local_scope;
|
||||||
block(&a, &b, case_sym, def_sym, case_reg, 0);
|
block(&a, &b, 0);
|
||||||
--local_scope;
|
--local_scope;
|
||||||
if(!nocode_wanted)
|
if(!nocode_wanted)
|
||||||
gjmp_addr(d);
|
gjmp_addr(d);
|
||||||
@ -4960,7 +4969,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
|
|||||||
if (tok != '}') {
|
if (tok != '}') {
|
||||||
if (is_expr)
|
if (is_expr)
|
||||||
vpop();
|
vpop();
|
||||||
block(bsym, csym, case_sym, def_sym, case_reg, is_expr);
|
block(bsym, csym, is_expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* pop locally defined labels */
|
/* pop locally defined labels */
|
||||||
@ -5115,7 +5124,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
|
|||||||
gsym(e);
|
gsym(e);
|
||||||
}
|
}
|
||||||
skip(')');
|
skip(')');
|
||||||
block(&a, &b, case_sym, def_sym, case_reg, 0);
|
block(&a, &b, 0);
|
||||||
if(!nocode_wanted)
|
if(!nocode_wanted)
|
||||||
gjmp_addr(c);
|
gjmp_addr(c);
|
||||||
gsym(a);
|
gsym(a);
|
||||||
@ -5130,7 +5139,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
|
|||||||
b = 0;
|
b = 0;
|
||||||
d = ind;
|
d = ind;
|
||||||
vla_sp_restore();
|
vla_sp_restore();
|
||||||
block(&a, &b, case_sym, def_sym, case_reg, 0);
|
block(&a, &b, 0);
|
||||||
skip(TOK_WHILE);
|
skip(TOK_WHILE);
|
||||||
skip('(');
|
skip('(');
|
||||||
gsym(b);
|
gsym(b);
|
||||||
@ -5142,58 +5151,65 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
|
|||||||
skip(';');
|
skip(';');
|
||||||
} else
|
} else
|
||||||
if (tok == TOK_SWITCH) {
|
if (tok == TOK_SWITCH) {
|
||||||
|
struct switch_t *saved, sw;
|
||||||
next();
|
next();
|
||||||
skip('(');
|
skip('(');
|
||||||
gexpr();
|
gexpr();
|
||||||
/* XXX: other types than integer */
|
/* XXX: other types than integer */
|
||||||
case_reg = gv(RC_INT);
|
c = gv(RC_INT);
|
||||||
vpop();
|
vpop();
|
||||||
skip(')');
|
skip(')');
|
||||||
a = 0;
|
a = 0;
|
||||||
b = gjmp(0); /* jump to first case */
|
b = gjmp(0); /* jump to first case */
|
||||||
c = 0;
|
sw.p = NULL; sw.n = 0; sw.def_sym = 0;
|
||||||
block(&a, csym, &b, &c, case_reg, 0);
|
saved = cur_switch;
|
||||||
/* if no default, jmp after switch */
|
cur_switch = &sw; block(&a, csym, 0);
|
||||||
if (c == 0)
|
cur_switch = saved;
|
||||||
c = ind;
|
a = gjmp(a); /* add implicit break */
|
||||||
/* default label */
|
gsym(b);
|
||||||
gsym_addr(b, c);
|
|
||||||
|
qsort(sw.p, sw.n, sizeof(void*), case_cmp);
|
||||||
|
for (b = 0; b < sw.n; b++) {
|
||||||
|
int v = sw.p[b]->v1;
|
||||||
|
if (b && v <= d)
|
||||||
|
tcc_error("duplicate case value");
|
||||||
|
d = sw.p[b]->v2;
|
||||||
|
|
||||||
|
vseti(c, 0);
|
||||||
|
vpushi(v);
|
||||||
|
if (v == d) {
|
||||||
|
gen_op(TOK_EQ);
|
||||||
|
gsym_addr(gtst(0, 0), sw.p[b]->sym);
|
||||||
|
} else {
|
||||||
|
int e;
|
||||||
|
gen_op(TOK_GE);
|
||||||
|
e = gtst(1, 0);
|
||||||
|
vseti(c, 0);
|
||||||
|
vpushi(d);
|
||||||
|
gen_op(TOK_LE);
|
||||||
|
gsym_addr(gtst(0, 0), sw.p[b]->sym);
|
||||||
|
gsym(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sw.def_sym)
|
||||||
|
gjmp_addr(sw.def_sym);
|
||||||
/* break label */
|
/* break label */
|
||||||
gsym(a);
|
gsym(a);
|
||||||
} else
|
} else
|
||||||
if (tok == TOK_CASE) {
|
if (tok == TOK_CASE) {
|
||||||
int v1, v2;
|
struct case_t *cr = tcc_malloc(sizeof(struct case_t));
|
||||||
if (!case_sym)
|
if (!cur_switch)
|
||||||
expect("switch");
|
expect("switch");
|
||||||
next();
|
next();
|
||||||
v1 = expr_const();
|
cr->v1 = cr->v2 = expr_const();
|
||||||
v2 = v1;
|
|
||||||
if (gnu_ext && tok == TOK_DOTS) {
|
if (gnu_ext && tok == TOK_DOTS) {
|
||||||
next();
|
next();
|
||||||
v2 = expr_const();
|
cr->v2 = expr_const();
|
||||||
if (v2 < v1)
|
if (cr->v2 < cr->v1)
|
||||||
tcc_warning("empty case range");
|
tcc_warning("empty case range");
|
||||||
}
|
}
|
||||||
/* since a case is like a label, we must skip it with a jmp */
|
cr->sym = ind;
|
||||||
b = gjmp(0);
|
dynarray_add((void***) &cur_switch->p, &cur_switch->n, cr);
|
||||||
gsym(*case_sym);
|
|
||||||
vseti(case_reg, 0);
|
|
||||||
vdup();
|
|
||||||
vpushi(v1);
|
|
||||||
if (v1 == v2) {
|
|
||||||
gen_op(TOK_EQ);
|
|
||||||
*case_sym = gtst(1, 0);
|
|
||||||
} else {
|
|
||||||
gen_op(TOK_GE);
|
|
||||||
*case_sym = gtst(1, 0);
|
|
||||||
vseti(case_reg, 0);
|
|
||||||
vpushi(v2);
|
|
||||||
gen_op(TOK_LE);
|
|
||||||
*case_sym = gtst(1, *case_sym);
|
|
||||||
}
|
|
||||||
case_reg = gv(RC_INT);
|
|
||||||
vpop();
|
|
||||||
gsym(b);
|
|
||||||
skip(':');
|
skip(':');
|
||||||
is_expr = 0;
|
is_expr = 0;
|
||||||
goto block_after_label;
|
goto block_after_label;
|
||||||
@ -5201,11 +5217,11 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
|
|||||||
if (tok == TOK_DEFAULT) {
|
if (tok == TOK_DEFAULT) {
|
||||||
next();
|
next();
|
||||||
skip(':');
|
skip(':');
|
||||||
if (!def_sym)
|
if (!cur_switch)
|
||||||
expect("switch");
|
expect("switch");
|
||||||
if (*def_sym)
|
if (cur_switch->def_sym)
|
||||||
tcc_error("too many 'default'");
|
tcc_error("too many 'default'");
|
||||||
*def_sym = ind;
|
cur_switch->def_sym = ind;
|
||||||
is_expr = 0;
|
is_expr = 0;
|
||||||
goto block_after_label;
|
goto block_after_label;
|
||||||
} else
|
} else
|
||||||
@ -5261,7 +5277,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
|
|||||||
} else {
|
} else {
|
||||||
if (is_expr)
|
if (is_expr)
|
||||||
vpop();
|
vpop();
|
||||||
block(bsym, csym, case_sym, def_sym, case_reg, is_expr);
|
block(bsym, csym, is_expr);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* expression case */
|
/* expression case */
|
||||||
@ -6177,7 +6193,7 @@ static void gen_function(Sym *sym)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
rsym = 0;
|
rsym = 0;
|
||||||
block(NULL, NULL, NULL, NULL, 0, 0);
|
block(NULL, NULL, 0);
|
||||||
gsym(rsym);
|
gsym(rsym);
|
||||||
gfunc_epilog();
|
gfunc_epilog();
|
||||||
cur_text_section->data_offset = ind;
|
cur_text_section->data_offset = ind;
|
||||||
|
Loading…
Reference in New Issue
Block a user