diff --git a/libtcc.c b/libtcc.c index 5f58f40c..c08c1f31 100644 --- a/libtcc.c +++ b/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 { diff --git a/tcc.h b/tcc.h index ef02e4d2..2199d741 100644 --- a/tcc.h +++ b/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) */ diff --git a/tccgen.c b/tccgen.c index 9cb699bb..d7954e67 100644 --- a/tccgen.c +++ b/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 */ - 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 */ 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; - return a < b ? -1 : a > b; + 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); - else - vpushi(p->v1); - gen_op(TOK_GE); - gtst_addr(0, p->sym); + /* 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 + 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; } diff --git a/tests/tests2/60_errors_and_warnings.c b/tests/tests2/60_errors_and_warnings.c index 434c46b7..0cf1ccd0 100644 --- a/tests/tests2/60_errors_and_warnings.c +++ b/tests/tests2/60_errors_and_warnings.c @@ -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 diff --git a/tests/tests2/60_errors_and_warnings.expect b/tests/tests2/60_errors_and_warnings.expect index a8ef8194..7fe9dde9 100644 --- a/tests/tests2/60_errors_and_warnings.expect +++ b/tests/tests2/60_errors_and_warnings.expect @@ -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