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:
grischka 2024-11-17 20:56:12 +01:00
parent f0cd0fbe1b
commit c717bac6e3
5 changed files with 125 additions and 103 deletions

View file

@ -567,6 +567,7 @@ static void error1(int mode, const char *fmt, va_list ap)
BufferedFile **pf, *f; BufferedFile **pf, *f;
TCCState *s1 = tcc_state; TCCState *s1 = tcc_state;
CString cs; CString cs;
int line = 0;
tcc_exit_state(s1); tcc_exit_state(s1);
@ -589,6 +590,8 @@ static void error1(int mode, const char *fmt, va_list ap)
} }
cstr_new(&cs); cstr_new(&cs);
if (fmt[0] == '%' && fmt[1] == 'i' && fmt[2] == ':')
line = va_arg(ap, int), fmt += 3;
f = NULL; f = NULL;
if (s1->error_set_jmp_enabled) { /* we're called while parsing a file */ if (s1->error_set_jmp_enabled) { /* we're called while parsing a file */
/* use upper file if inline ":asm:" or token ":paste:" */ /* 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++) for(pf = s1->include_stack; pf < s1->include_stack_ptr; pf++)
cstr_printf(&cs, "In file included from %s:%d:\n", cstr_printf(&cs, "In file included from %s:%d:\n",
(*pf)->filename, (*pf)->line_num - 1); (*pf)->filename, (*pf)->line_num - 1);
cstr_printf(&cs, "%s:%d: ", if (0 == line)
f->filename, f->line_num - ((tok_flags & TOK_FLAG_BOL) && !macro_ptr)); line = f->line_num - ((tok_flags & TOK_FLAG_BOL) && !macro_ptr);
cstr_printf(&cs, "%s:%d: ", f->filename, line);
} else if (s1->current_filename) { } else if (s1->current_filename) {
cstr_printf(&cs, "%s: ", s1->current_filename); cstr_printf(&cs, "%s: ", s1->current_filename);
} else { } else {

4
tcc.h
View file

@ -564,14 +564,16 @@ typedef struct Sym {
union { union {
int sym_scope; /* scope level for locals */ int sym_scope; /* scope level for locals */
int jnext; /* next jump label */ int jnext; /* next jump label */
int jind; /* label position */
struct FuncAttr f; /* function attributes */ struct FuncAttr f; /* function attributes */
int auxtype; /* bitfield access type */ int auxtype; /* bitfield access type */
}; };
}; };
long long enum_val; /* enum constant if IS_ENUM_VAL */ long long enum_val; /* enum constant if IS_ENUM_VAL */
int *d; /* define token stream */ int *d; /* define token stream */
struct Sym *ncl; /* next cleanup */ struct Sym *cleanup_func;
}; };
CType type; /* associated type */ CType type; /* associated type */
union { union {
struct Sym *next; /* next related symbol (for fields and anoms) */ struct Sym *next; /* next related symbol (for fields and anoms) */

196
tccgen.c
View file

@ -90,7 +90,7 @@ static CString initstr;
static struct switch_t { static struct switch_t {
struct case_t { struct case_t {
int64_t v1, v2; int64_t v1, v2;
int sym; int ind, line;
} **p; int n; /* list of case ranges */ } **p; int n; /* list of case ranges */
int def_sym; /* default symbol */ int def_sym; /* default symbol */
int nocode_wanted; int nocode_wanted;
@ -2236,7 +2236,11 @@ static void gen_opl(int op)
vtop[-1] = vtop[-2]; vtop[-1] = vtop[-2];
vtop[-2] = tmp; vtop[-2] = tmp;
/* stack: L1 L2 H1 H2 */ /* stack: L1 L2 H1 H2 */
save_regs(4); 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 */ /* compare high */
op1 = op; op1 = op;
/* when values are equal, we need to compare low words. since /* when values are equal, we need to compare low words. since
@ -2285,6 +2289,18 @@ static void gen_opl(int op)
} }
#endif #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) static uint64_t gen_opic_sdiv(uint64_t a, uint64_t b)
{ {
uint64_t x = (a >> 63 ? -a : a) / (b >> 63 ? -b : 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 t2 = v2->type.t & VT_BTYPE;
int c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; int c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
int c2 = (v2->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 l1 = c1 ? value64(v1->c.i, v1->type.t) : 0;
uint64_t l2 = c2 ? v2->c.i : 0; uint64_t l2 = c2 ? value64(v2->c.i, v2->type.t) : 0;
int shm = (t1 == VT_LLONG) ? 63 : 31; int shm = (t1 == VT_LLONG) ? 63 : 31;
int r; 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) { if (c1 && c2) {
switch(op) { switch(op) {
case '+': l1 += l2; break; case '+': l1 += l2; break;
@ -2367,10 +2376,7 @@ static void gen_opic(int op)
default: default:
goto general_case; goto general_case;
} }
if (t1 != VT_LLONG && (PTR_SIZE != 8 || t1 != VT_PTR)) v1->c.i = value64(l1, v1->type.t);
l1 = ((uint32_t)l1 |
(v1->type.t & VT_UNSIGNED ? 0 : -(l1 & 0x80000000)));
v1->c.i = l1;
v1->r |= v2->r & VT_NONCONST; v1->r |= v2->r & VT_NONCONST;
vtop--; vtop--;
} else { } else {
@ -6701,79 +6707,74 @@ static void check_func_return(void)
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
/* switch/case */ /* 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; if (cur_switch->sv.type.t & VT_UNSIGNED)
int64_t b = (*(struct case_t**) pb)->v1; return a < b ? -1 : a > b;
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; return case_cmp((*(struct case_t**)pa)->v1, (*(struct case_t**)pb)->v1);
uint64_t b = (uint64_t)(*(struct case_t**) pb)->v1;
return a < b ? -1 : a > b;
} }
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; struct case_t *p;
int e; int t, l2, e;
int ll = (vtop->type.t & VT_BTYPE) == VT_LLONG;
while (len > 8) { t = vtop->type.t & VT_BTYPE;
/* binary search */ if (t != VT_LLONG)
p = base[len/2]; t = VT_INT;
vdup(); while (len) {
if (ll) /* binary search while len > 8, else linear */
vpushll(p->v2); l2 = len > 8 ? len/2 : 0;
else p = base[l2];
vpushi(p->v2); vdup(), vpush64(t, p->v2);
gen_op(TOK_LE); if (l2 == 0 && p->v1 == p->v2) {
e = gvtst(1, 0); gen_op(TOK_EQ); /* jmp to case when equal */
vdup(); gsym_addr(gvtst(0, 0), p->ind);
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);
} else { } else {
gen_op(TOK_LE); /* case v1 ... v2 */
e = gvtst(1, 0); gen_op(TOK_GT); /* jmp over when > V2 */
vdup(); if (len == 1) /* last case test jumps to default when false */
if (ll) dsym = gvtst(0, dsym), e = 0;
vpushll(p->v1); else
else e = gvtst(0, 0);
vpushi(p->v1); vdup(), vpush64(t, p->v1);
gen_op(TOK_GE); gen_op(TOK_GE); /* jmp to case when >= V1 */
gtst_addr(0, p->sym); gsym_addr(gvtst(0, 0), p->ind);
dsym = gcase(base, l2, dsym);
gsym(e); gsym(e);
} }
++l2, base += l2, len -= l2;
} }
*bsym = gjmp(*bsym); /* jump automagically will suppress more jumps */
return gjmp(dsym);
} }
static void end_switch(void) static void end_switch(void)
@ -6791,8 +6792,8 @@ static void try_call_scope_cleanup(Sym *stop)
{ {
Sym *cls = cur_scope->cl.s; Sym *cls = cur_scope->cl.s;
for (; cls != stop; cls = cls->ncl) { for (; cls != stop; cls = cls->next) {
Sym *fs = cls->next; Sym *fs = cls->cleanup_func;
Sym *vs = cls->prev_tok; Sym *vs = cls->prev_tok;
vpushsym(&fs->type, fs); 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 */ /* search NCA of both cleanup chains given parents and initial depth */
ocd = cleanupstate ? cleanupstate->v & ~SYM_FIELD : 0; 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); try_call_scope_cleanup(cc);
@ -7153,6 +7154,8 @@ again:
skip('('); skip('(');
gexpr(); gexpr();
skip(')'); skip(')');
if (!is_integer_btype(vtop->type.t & VT_BTYPE))
tcc_error("switch value not an integer");
sw->sv = *vtop--; /* save switch value */ sw->sv = *vtop--; /* save switch value */
a = 0; a = 0;
b = gjmp(0); /* jump to first case */ b = gjmp(0); /* jump to first case */
@ -7161,21 +7164,13 @@ again:
/* case lookup */ /* case lookup */
gsym(b); gsym(b);
prev_scope_s(&o); prev_scope_s(&o);
if (sw->nocode_wanted) if (sw->nocode_wanted)
goto skip_switch; goto skip_switch;
if (sw->sv.type.t & VT_UNSIGNED) case_sort(sw);
qsort(sw->p, sw->n, sizeof(void*), case_cmpu); sw->bsym = NULL; /* marker for 32bit:gen_opl() */
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");
vpushv(&sw->sv); vpushv(&sw->sv);
gv(RC_INT); gv(RC_INT);
d = 0, gcase(sw->p, sw->n, &d); d = gcase(sw->p, sw->n, 0);
vpop(); vpop();
if (sw->def_sym) if (sw->def_sym)
gsym_addr(d, sw->def_sym); gsym_addr(d, sw->def_sym);
@ -7192,17 +7187,18 @@ again:
expect("switch"); expect("switch");
cr = tcc_malloc(sizeof(struct case_t)); cr = tcc_malloc(sizeof(struct case_t));
dynarray_add(&cur_switch->p, &cur_switch->n, cr); dynarray_add(&cur_switch->p, &cur_switch->n, cr);
cr->v1 = cr->v2 = expr_const64(); t = cur_switch->sv.type.t;
if (gnu_ext && tok == TOK_DOTS) { cr->v1 = cr->v2 = value64(expr_const64(), t);
if (tok == TOK_DOTS && gnu_ext) {
next(); next();
cr->v2 = expr_const64(); cr->v2 = value64(expr_const64(), t);
if ((!(cur_switch->sv.type.t & VT_UNSIGNED) && cr->v2 < cr->v1) if (case_cmp(cr->v2, cr->v1) < 0)
|| (cur_switch->sv.type.t & VT_UNSIGNED && (uint64_t)cr->v2 < (uint64_t)cr->v1))
tcc_warning("empty case range"); tcc_warning("empty case range");
} }
/* case and default are unreachable from a switch under nocode_wanted */ /* case and default are unreachable from a switch under nocode_wanted */
if (!cur_switch->nocode_wanted) if (!cur_switch->nocode_wanted)
cr->sym = gind(); cr->ind = gind();
cr->line = file->line_num;
skip(':'); skip(':');
goto block_after_label; goto block_after_label;
@ -7211,7 +7207,7 @@ again:
expect("switch"); expect("switch");
if (cur_switch->def_sym) if (cur_switch->def_sym)
tcc_error("too many 'default'"); 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(':'); skip(':');
goto block_after_label; goto block_after_label;
@ -7244,7 +7240,7 @@ again:
s->jnext = gjmp(s->jnext); s->jnext = gjmp(s->jnext);
} else { } else {
try_call_cleanup_goto(s->cleanupstate); try_call_cleanup_goto(s->cleanupstate);
gjmp_addr(s->jnext); gjmp_addr(s->jind);
} }
next(); next();
@ -7275,7 +7271,7 @@ again:
} else { } else {
s = label_push(&global_label_stack, t, LABEL_DEFINED); s = label_push(&global_label_stack, t, LABEL_DEFINED);
} }
s->jnext = gind(); s->jind = gind();
s->cleanupstate = cur_scope->cl.s; s->cleanupstate = cur_scope->cl.s;
block_after_label: 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 *cls = sym_push2(&all_cleanups,
SYM_FIELD | ++cur_scope->cl.n, 0, 0); SYM_FIELD | ++cur_scope->cl.n, 0, 0);
cls->prev_tok = sym; cls->prev_tok = sym;
cls->next = ad->cleanup_func; cls->cleanup_func = ad->cleanup_func;
cls->ncl = cur_scope->cl.s; cls->next = cur_scope->cl.s;
cur_scope->cl.s = cls; cur_scope->cl.s = cls;
} }

View file

@ -490,4 +490,20 @@ int *invalid_operation(int *p, double d)
return p + 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 #endif

View file

@ -240,3 +240,7 @@ arg[1] = "Y"
[test_pointer_plus_double] [test_pointer_plus_double]
60_errors_and_warnings.c:490: error: invalid operand types for binary operation 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