tccgen: show useful line number with duplicate switch-case
- output correct line number with "error: duplicate case value" - libtcc.c:error1(): support specific line numbers with "%i:" tcc_error("%i:message ...", line_num, ...); Also: - simplify signed/unsigned switch compare - optimize implicit case ranges such as case 1: case 2: case 3: ... - simplify llong constant propagation in gen_opic() - rename Sym.ncl to Sym.cleanup_func
This commit is contained in:
parent
f0cd0fbe1b
commit
c717bac6e3
5 changed files with 125 additions and 103 deletions
8
libtcc.c
8
libtcc.c
|
@ -567,6 +567,7 @@ static void error1(int mode, const char *fmt, va_list ap)
|
|||
BufferedFile **pf, *f;
|
||||
TCCState *s1 = tcc_state;
|
||||
CString cs;
|
||||
int line = 0;
|
||||
|
||||
tcc_exit_state(s1);
|
||||
|
||||
|
@ -589,6 +590,8 @@ static void error1(int mode, const char *fmt, va_list ap)
|
|||
}
|
||||
|
||||
cstr_new(&cs);
|
||||
if (fmt[0] == '%' && fmt[1] == 'i' && fmt[2] == ':')
|
||||
line = va_arg(ap, int), fmt += 3;
|
||||
f = NULL;
|
||||
if (s1->error_set_jmp_enabled) { /* we're called while parsing a file */
|
||||
/* use upper file if inline ":asm:" or token ":paste:" */
|
||||
|
@ -599,8 +602,9 @@ static void error1(int mode, const char *fmt, va_list ap)
|
|||
for(pf = s1->include_stack; pf < s1->include_stack_ptr; pf++)
|
||||
cstr_printf(&cs, "In file included from %s:%d:\n",
|
||||
(*pf)->filename, (*pf)->line_num - 1);
|
||||
cstr_printf(&cs, "%s:%d: ",
|
||||
f->filename, f->line_num - ((tok_flags & TOK_FLAG_BOL) && !macro_ptr));
|
||||
if (0 == line)
|
||||
line = f->line_num - ((tok_flags & TOK_FLAG_BOL) && !macro_ptr);
|
||||
cstr_printf(&cs, "%s:%d: ", f->filename, line);
|
||||
} else if (s1->current_filename) {
|
||||
cstr_printf(&cs, "%s: ", s1->current_filename);
|
||||
} else {
|
||||
|
|
4
tcc.h
4
tcc.h
|
@ -564,14 +564,16 @@ typedef struct Sym {
|
|||
union {
|
||||
int sym_scope; /* scope level for locals */
|
||||
int jnext; /* next jump label */
|
||||
int jind; /* label position */
|
||||
struct FuncAttr f; /* function attributes */
|
||||
int auxtype; /* bitfield access type */
|
||||
};
|
||||
};
|
||||
long long enum_val; /* enum constant if IS_ENUM_VAL */
|
||||
int *d; /* define token stream */
|
||||
struct Sym *ncl; /* next cleanup */
|
||||
struct Sym *cleanup_func;
|
||||
};
|
||||
|
||||
CType type; /* associated type */
|
||||
union {
|
||||
struct Sym *next; /* next related symbol (for fields and anoms) */
|
||||
|
|
190
tccgen.c
190
tccgen.c
|
@ -90,7 +90,7 @@ static CString initstr;
|
|||
static struct switch_t {
|
||||
struct case_t {
|
||||
int64_t v1, v2;
|
||||
int sym;
|
||||
int ind, line;
|
||||
} **p; int n; /* list of case ranges */
|
||||
int def_sym; /* default symbol */
|
||||
int nocode_wanted;
|
||||
|
@ -2236,7 +2236,11 @@ static void gen_opl(int op)
|
|||
vtop[-1] = vtop[-2];
|
||||
vtop[-2] = tmp;
|
||||
/* stack: L1 L2 H1 H2 */
|
||||
if (!cur_switch || cur_switch->bsym) {
|
||||
/* avoid differnt registers being saved in branches.
|
||||
This is not needed when comparing switch cases */
|
||||
save_regs(4);
|
||||
}
|
||||
/* compare high */
|
||||
op1 = op;
|
||||
/* when values are equal, we need to compare low words. since
|
||||
|
@ -2285,6 +2289,18 @@ static void gen_opl(int op)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* normalize values */
|
||||
static uint64_t value64(uint64_t l1, int t)
|
||||
{
|
||||
if ((t & VT_BTYPE) == VT_LLONG
|
||||
|| (PTR_SIZE == 8 && (t & VT_BTYPE) == VT_PTR))
|
||||
return l1;
|
||||
else if (t & VT_UNSIGNED)
|
||||
return (uint32_t)l1;
|
||||
else
|
||||
return (uint32_t)l1 | -(l1 & 0x80000000);
|
||||
}
|
||||
|
||||
static uint64_t gen_opic_sdiv(uint64_t a, uint64_t b)
|
||||
{
|
||||
uint64_t x = (a >> 63 ? -a : a) / (b >> 63 ? -b : b);
|
||||
|
@ -2306,18 +2322,11 @@ static void gen_opic(int op)
|
|||
int t2 = v2->type.t & VT_BTYPE;
|
||||
int c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
|
||||
int c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
|
||||
uint64_t l1 = c1 ? v1->c.i : 0;
|
||||
uint64_t l2 = c2 ? v2->c.i : 0;
|
||||
uint64_t l1 = c1 ? value64(v1->c.i, v1->type.t) : 0;
|
||||
uint64_t l2 = c2 ? value64(v2->c.i, v2->type.t) : 0;
|
||||
int shm = (t1 == VT_LLONG) ? 63 : 31;
|
||||
int r;
|
||||
|
||||
if (t1 != VT_LLONG && (PTR_SIZE != 8 || t1 != VT_PTR))
|
||||
l1 = ((uint32_t)l1 |
|
||||
(v1->type.t & VT_UNSIGNED ? 0 : -(l1 & 0x80000000)));
|
||||
if (t2 != VT_LLONG && (PTR_SIZE != 8 || t2 != VT_PTR))
|
||||
l2 = ((uint32_t)l2 |
|
||||
(v2->type.t & VT_UNSIGNED ? 0 : -(l2 & 0x80000000)));
|
||||
|
||||
if (c1 && c2) {
|
||||
switch(op) {
|
||||
case '+': l1 += l2; break;
|
||||
|
@ -2367,10 +2376,7 @@ static void gen_opic(int op)
|
|||
default:
|
||||
goto general_case;
|
||||
}
|
||||
if (t1 != VT_LLONG && (PTR_SIZE != 8 || t1 != VT_PTR))
|
||||
l1 = ((uint32_t)l1 |
|
||||
(v1->type.t & VT_UNSIGNED ? 0 : -(l1 & 0x80000000)));
|
||||
v1->c.i = l1;
|
||||
v1->c.i = value64(l1, v1->type.t);
|
||||
v1->r |= v2->r & VT_NONCONST;
|
||||
vtop--;
|
||||
} else {
|
||||
|
@ -6701,79 +6707,74 @@ static void check_func_return(void)
|
|||
/* ------------------------------------------------------------------------- */
|
||||
/* switch/case */
|
||||
|
||||
static int case_cmpi(const void *pa, const void *pb)
|
||||
static int case_cmp(uint64_t a, uint64_t b)
|
||||
{
|
||||
int64_t a = (*(struct case_t**) pa)->v1;
|
||||
int64_t b = (*(struct case_t**) pb)->v1;
|
||||
if (cur_switch->sv.type.t & VT_UNSIGNED)
|
||||
return a < b ? -1 : a > b;
|
||||
else
|
||||
return (int64_t)a < (int64_t)b ? -1 : (int64_t)a > (int64_t)b;
|
||||
}
|
||||
|
||||
static int case_cmpu(const void *pa, const void *pb)
|
||||
static int case_cmp_qs(const void *pa, const void *pb)
|
||||
{
|
||||
uint64_t a = (uint64_t)(*(struct case_t**) pa)->v1;
|
||||
uint64_t b = (uint64_t)(*(struct case_t**) pb)->v1;
|
||||
return a < b ? -1 : a > b;
|
||||
return case_cmp((*(struct case_t**)pa)->v1, (*(struct case_t**)pb)->v1);
|
||||
}
|
||||
|
||||
static void gtst_addr(int t, int a)
|
||||
static void case_sort(struct switch_t *sw)
|
||||
{
|
||||
gsym_addr(gvtst(0, t), a);
|
||||
struct case_t **p;
|
||||
if (sw->n < 2)
|
||||
return;
|
||||
qsort(sw->p, sw->n, sizeof *sw->p, case_cmp_qs);
|
||||
p = sw->p;
|
||||
while (p < sw->p + sw->n - 1) {
|
||||
if (case_cmp(p[0]->v2, p[1]->v1) >= 0) {
|
||||
int l1 = p[0]->line, l2 = p[1]->line;
|
||||
/* using special format "%i:..." to show specific line */
|
||||
tcc_error("%i:duplicate case value", l1 > l2 ? l1 : l2);
|
||||
} else if (p[0]->v2 + 1 == p[1]->v1 && p[0]->ind == p[1]->ind) {
|
||||
/* treat "case 1: case 2: case 3:" like "case 1 ... 3: */
|
||||
p[1]->v1 = p[0]->v1;
|
||||
tcc_free(p[0]);
|
||||
memmove(p, p + 1, (--sw->n - (p - sw->p)) * sizeof *p);
|
||||
} else
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
static void gcase(struct case_t **base, int len, int *bsym)
|
||||
static int gcase(struct case_t **base, int len, int dsym)
|
||||
{
|
||||
struct case_t *p;
|
||||
int e;
|
||||
int ll = (vtop->type.t & VT_BTYPE) == VT_LLONG;
|
||||
while (len > 8) {
|
||||
/* binary search */
|
||||
p = base[len/2];
|
||||
vdup();
|
||||
if (ll)
|
||||
vpushll(p->v2);
|
||||
else
|
||||
vpushi(p->v2);
|
||||
gen_op(TOK_LE);
|
||||
e = gvtst(1, 0);
|
||||
vdup();
|
||||
if (ll)
|
||||
vpushll(p->v1);
|
||||
else
|
||||
vpushi(p->v1);
|
||||
gen_op(TOK_GE);
|
||||
gtst_addr(0, p->sym); /* v1 <= x <= v2 */
|
||||
/* x < v1 */
|
||||
gcase(base, len/2, bsym);
|
||||
/* x > v2 */
|
||||
gsym(e);
|
||||
e = len/2 + 1;
|
||||
base += e; len -= e;
|
||||
}
|
||||
/* linear scan */
|
||||
while (len--) {
|
||||
p = *base++;
|
||||
vdup();
|
||||
if (ll)
|
||||
vpushll(p->v2);
|
||||
else
|
||||
vpushi(p->v2);
|
||||
if (p->v1 == p->v2) {
|
||||
gen_op(TOK_EQ);
|
||||
gtst_addr(0, p->sym);
|
||||
int t, l2, e;
|
||||
|
||||
t = vtop->type.t & VT_BTYPE;
|
||||
if (t != VT_LLONG)
|
||||
t = VT_INT;
|
||||
while (len) {
|
||||
/* binary search while len > 8, else linear */
|
||||
l2 = len > 8 ? len/2 : 0;
|
||||
p = base[l2];
|
||||
vdup(), vpush64(t, p->v2);
|
||||
if (l2 == 0 && p->v1 == p->v2) {
|
||||
gen_op(TOK_EQ); /* jmp to case when equal */
|
||||
gsym_addr(gvtst(0, 0), p->ind);
|
||||
} else {
|
||||
gen_op(TOK_LE);
|
||||
e = gvtst(1, 0);
|
||||
vdup();
|
||||
if (ll)
|
||||
vpushll(p->v1);
|
||||
/* case v1 ... v2 */
|
||||
gen_op(TOK_GT); /* jmp over when > V2 */
|
||||
if (len == 1) /* last case test jumps to default when false */
|
||||
dsym = gvtst(0, dsym), e = 0;
|
||||
else
|
||||
vpushi(p->v1);
|
||||
gen_op(TOK_GE);
|
||||
gtst_addr(0, p->sym);
|
||||
e = gvtst(0, 0);
|
||||
vdup(), vpush64(t, p->v1);
|
||||
gen_op(TOK_GE); /* jmp to case when >= V1 */
|
||||
gsym_addr(gvtst(0, 0), p->ind);
|
||||
dsym = gcase(base, l2, dsym);
|
||||
gsym(e);
|
||||
}
|
||||
++l2, base += l2, len -= l2;
|
||||
}
|
||||
*bsym = gjmp(*bsym);
|
||||
/* jump automagically will suppress more jumps */
|
||||
return gjmp(dsym);
|
||||
}
|
||||
|
||||
static void end_switch(void)
|
||||
|
@ -6791,8 +6792,8 @@ static void try_call_scope_cleanup(Sym *stop)
|
|||
{
|
||||
Sym *cls = cur_scope->cl.s;
|
||||
|
||||
for (; cls != stop; cls = cls->ncl) {
|
||||
Sym *fs = cls->next;
|
||||
for (; cls != stop; cls = cls->next) {
|
||||
Sym *fs = cls->cleanup_func;
|
||||
Sym *vs = cls->prev_tok;
|
||||
|
||||
vpushsym(&fs->type, fs);
|
||||
|
@ -6814,11 +6815,11 @@ static void try_call_cleanup_goto(Sym *cleanupstate)
|
|||
|
||||
/* search NCA of both cleanup chains given parents and initial depth */
|
||||
ocd = cleanupstate ? cleanupstate->v & ~SYM_FIELD : 0;
|
||||
for (ccd = cur_scope->cl.n, oc = cleanupstate; ocd > ccd; --ocd, oc = oc->ncl)
|
||||
for (ccd = cur_scope->cl.n, oc = cleanupstate; ocd > ccd; --ocd, oc = oc->next)
|
||||
;
|
||||
for (cc = cur_scope->cl.s; ccd > ocd; --ccd, cc = cc->ncl)
|
||||
for (cc = cur_scope->cl.s; ccd > ocd; --ccd, cc = cc->next)
|
||||
;
|
||||
for (; cc != oc; cc = cc->ncl, oc = oc->ncl, --ccd)
|
||||
for (; cc != oc; cc = cc->next, oc = oc->next, --ccd)
|
||||
;
|
||||
|
||||
try_call_scope_cleanup(cc);
|
||||
|
@ -7153,6 +7154,8 @@ again:
|
|||
skip('(');
|
||||
gexpr();
|
||||
skip(')');
|
||||
if (!is_integer_btype(vtop->type.t & VT_BTYPE))
|
||||
tcc_error("switch value not an integer");
|
||||
sw->sv = *vtop--; /* save switch value */
|
||||
a = 0;
|
||||
b = gjmp(0); /* jump to first case */
|
||||
|
@ -7161,21 +7164,13 @@ again:
|
|||
/* case lookup */
|
||||
gsym(b);
|
||||
prev_scope_s(&o);
|
||||
|
||||
if (sw->nocode_wanted)
|
||||
goto skip_switch;
|
||||
if (sw->sv.type.t & VT_UNSIGNED)
|
||||
qsort(sw->p, sw->n, sizeof(void*), case_cmpu);
|
||||
else
|
||||
qsort(sw->p, sw->n, sizeof(void*), case_cmpi);
|
||||
for (b = 1; b < sw->n; b++)
|
||||
if (sw->sv.type.t & VT_UNSIGNED
|
||||
? (uint64_t)sw->p[b - 1]->v2 >= (uint64_t)sw->p[b]->v1
|
||||
: sw->p[b - 1]->v2 >= sw->p[b]->v1)
|
||||
tcc_error("duplicate case value");
|
||||
case_sort(sw);
|
||||
sw->bsym = NULL; /* marker for 32bit:gen_opl() */
|
||||
vpushv(&sw->sv);
|
||||
gv(RC_INT);
|
||||
d = 0, gcase(sw->p, sw->n, &d);
|
||||
d = gcase(sw->p, sw->n, 0);
|
||||
vpop();
|
||||
if (sw->def_sym)
|
||||
gsym_addr(d, sw->def_sym);
|
||||
|
@ -7192,17 +7187,18 @@ again:
|
|||
expect("switch");
|
||||
cr = tcc_malloc(sizeof(struct case_t));
|
||||
dynarray_add(&cur_switch->p, &cur_switch->n, cr);
|
||||
cr->v1 = cr->v2 = expr_const64();
|
||||
if (gnu_ext && tok == TOK_DOTS) {
|
||||
t = cur_switch->sv.type.t;
|
||||
cr->v1 = cr->v2 = value64(expr_const64(), t);
|
||||
if (tok == TOK_DOTS && gnu_ext) {
|
||||
next();
|
||||
cr->v2 = expr_const64();
|
||||
if ((!(cur_switch->sv.type.t & VT_UNSIGNED) && cr->v2 < cr->v1)
|
||||
|| (cur_switch->sv.type.t & VT_UNSIGNED && (uint64_t)cr->v2 < (uint64_t)cr->v1))
|
||||
cr->v2 = value64(expr_const64(), t);
|
||||
if (case_cmp(cr->v2, cr->v1) < 0)
|
||||
tcc_warning("empty case range");
|
||||
}
|
||||
/* case and default are unreachable from a switch under nocode_wanted */
|
||||
if (!cur_switch->nocode_wanted)
|
||||
cr->sym = gind();
|
||||
cr->ind = gind();
|
||||
cr->line = file->line_num;
|
||||
skip(':');
|
||||
goto block_after_label;
|
||||
|
||||
|
@ -7211,7 +7207,7 @@ again:
|
|||
expect("switch");
|
||||
if (cur_switch->def_sym)
|
||||
tcc_error("too many 'default'");
|
||||
cur_switch->def_sym = cur_switch->nocode_wanted ? 1 : gind();
|
||||
cur_switch->def_sym = cur_switch->nocode_wanted ? -1 : gind();
|
||||
skip(':');
|
||||
goto block_after_label;
|
||||
|
||||
|
@ -7244,7 +7240,7 @@ again:
|
|||
s->jnext = gjmp(s->jnext);
|
||||
} else {
|
||||
try_call_cleanup_goto(s->cleanupstate);
|
||||
gjmp_addr(s->jnext);
|
||||
gjmp_addr(s->jind);
|
||||
}
|
||||
next();
|
||||
|
||||
|
@ -7275,7 +7271,7 @@ again:
|
|||
} else {
|
||||
s = label_push(&global_label_stack, t, LABEL_DEFINED);
|
||||
}
|
||||
s->jnext = gind();
|
||||
s->jind = gind();
|
||||
s->cleanupstate = cur_scope->cl.s;
|
||||
|
||||
block_after_label:
|
||||
|
@ -8132,8 +8128,8 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
|||
Sym *cls = sym_push2(&all_cleanups,
|
||||
SYM_FIELD | ++cur_scope->cl.n, 0, 0);
|
||||
cls->prev_tok = sym;
|
||||
cls->next = ad->cleanup_func;
|
||||
cls->ncl = cur_scope->cl.s;
|
||||
cls->cleanup_func = ad->cleanup_func;
|
||||
cls->next = cur_scope->cl.s;
|
||||
cur_scope->cl.s = cls;
|
||||
}
|
||||
|
||||
|
|
|
@ -490,4 +490,20 @@ int *invalid_operation(int *p, double d)
|
|||
return p + d;
|
||||
}
|
||||
|
||||
#elif defined test_duplicate_case
|
||||
|
||||
int main()
|
||||
{
|
||||
unsigned int x;
|
||||
switch (x) {
|
||||
case -1 ... 0: /* empty case range with unsigned */
|
||||
case 3:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3: /* show this line number in error */
|
||||
case 4:
|
||||
case 5:
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -240,3 +240,7 @@ arg[1] = "Y"
|
|||
|
||||
[test_pointer_plus_double]
|
||||
60_errors_and_warnings.c:490: error: invalid operand types for binary operation
|
||||
|
||||
[test_duplicate_case]
|
||||
60_errors_and_warnings.c:499: warning: empty case range
|
||||
60_errors_and_warnings.c:507: error: duplicate case value
|
||||
|
|
Loading…
Add table
Reference in a new issue