diff --git a/tcc.h b/tcc.h index 91eeeeb..7630f4c 100644 --- a/tcc.h +++ b/tcc.h @@ -1103,11 +1103,11 @@ ST_FUNC void cstr_reset(CString *cstr); ST_INLN void sym_free(Sym *sym); ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, long c); ST_FUNC Sym *sym_find2(Sym *s, int v); -ST_FUNC Sym *sym_push(int v, CType *type, int r, int c); +ST_FUNC Sym *sym_push(int v, CType *type, int r, long c); ST_FUNC void sym_pop(Sym **ptop, Sym *b, int keep); ST_INLN Sym *struct_find(int v); ST_INLN Sym *sym_find(int v); -ST_FUNC Sym *global_identifier_push(int v, int t, int c); +ST_FUNC Sym *global_identifier_push(int v, int t, long c); ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen); ST_FUNC int tcc_open(TCCState *s1, const char *filename); @@ -1271,7 +1271,7 @@ ST_FUNC void test_lvalue(void); ST_FUNC void swap(int *p, int *q); ST_FUNC void vpushi(int v); ST_FUNC Sym *external_global_sym(int v, CType *type, int r); -ST_FUNC void vset(CType *type, int r, int v); +ST_FUNC void vset(CType *type, int r, long v); ST_FUNC void vswap(void); ST_FUNC void vpush_global_sym(CType *type, int v); ST_FUNC void vrote(SValue *e, int n); diff --git a/tccgen.c b/tccgen.c index 2cc57bd..b6cae5d 100644 --- a/tccgen.c +++ b/tccgen.c @@ -62,7 +62,8 @@ 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; + int64_t v1, v2; + int sym; } **p; int n; /* list of case ranges */ int def_sym; /* default symbol */ } *cur_switch; /* current switch */ @@ -86,6 +87,7 @@ static void vla_sp_restore(void); static void vla_sp_restore_root(void); static int is_compatible_parameter_types(CType *type1, CType *type2); static void expr_type(CType *type); +static inline int64_t expr_const64(void); ST_FUNC void vpush64(int ty, unsigned long long v); ST_FUNC void vpush(CType *type); ST_FUNC int gvtst(int inv, int t); @@ -434,7 +436,7 @@ ST_INLN Sym *sym_find(int v) } /* push a given symbol on the symbol stack */ -ST_FUNC Sym *sym_push(int v, CType *type, int r, int c) +ST_FUNC Sym *sym_push(int v, CType *type, int r, long c) { Sym *s, **ps; TokenSym *ts; @@ -466,7 +468,7 @@ ST_FUNC Sym *sym_push(int v, CType *type, int r, int c) } /* push a global identifier */ -ST_FUNC Sym *global_identifier_push(int v, int t, int c) +ST_FUNC Sym *global_identifier_push(int v, int t, long c) { Sym *s, **ps; s = sym_push2(&global_stack, v, t, c); @@ -698,7 +700,7 @@ ST_FUNC void vpush_global_sym(CType *type, int v) vpushsym(type, external_global_sym(v, type, 0)); } -ST_FUNC void vset(CType *type, int r, int v) +ST_FUNC void vset(CType *type, int r, long v) { CValue cval; @@ -1716,6 +1718,9 @@ 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; vtop--; } else { @@ -3248,7 +3253,8 @@ static Sym * find_field (CType *type, int v) /* enum/struct/union declaration. u is either VT_ENUM or VT_STRUCT */ static void struct_decl(CType *type, AttributeDef *ad, int u) { - int a, v, size, align, maxalign, c, offset, flexible, extra_bytes; + int a, v, size, align, maxalign, offset, flexible, extra_bytes; + long c; int bit_size, bit_pos, bsize, bt, lbit_pos, prevbt; Sym *s, *ss, *ass, **ps; AttributeDef ad1; @@ -3293,7 +3299,9 @@ static void struct_decl(CType *type, AttributeDef *ad, int u) /* non empty enums are not allowed */ if (a == TOK_ENUM) { int seen_neg = 0; + int seen_wide = 0; for(;;) { + CType *t = &int_type; v = tok; if (v < TOK_UIDENT) expect("identifier"); @@ -3304,12 +3312,23 @@ static void struct_decl(CType *type, AttributeDef *ad, int u) next(); if (tok == '=') { next(); +#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) + c = expr_const64(); +#else + /* We really want to support long long enums + on i386 as well, but the Sym structure only + holds a 'long' for associated constants, + and enlarging it would bump its size (no + available padding). So punt for now. */ c = expr_const(); +#endif } if (c < 0) seen_neg = 1; + if (c != (int)c && (unsigned long)c != (unsigned int)c) + seen_wide = 1, t = &size_type; /* enum symbols have static storage */ - ss = sym_push(v, &int_type, VT_CONST, c); + ss = sym_push(v, t, VT_CONST, c); ss->type.t |= VT_STATIC; if (tok != ',') break; @@ -3321,7 +3340,7 @@ static void struct_decl(CType *type, AttributeDef *ad, int u) } if (!seen_neg) s->a.unsigned_enum = 1; - s->c = type_size(&int_type, &align); + s->c = type_size(seen_wide ? &size_type : &int_type, &align); skip('}'); } else { maxalign = 1; @@ -4355,10 +4374,11 @@ ST_FUNC void unary(void) break; case TOK_builtin_choose_expr: { - int saved_nocode_wanted, c; + int saved_nocode_wanted; + int64_t c; next(); skip('('); - c = expr_const(); + c = expr_const64(); skip(','); if (!c) { saved_nocode_wanted = nocode_wanted; @@ -5272,9 +5292,9 @@ static void expr_const1(void) } /* parse an integer constant and return its value. */ -ST_FUNC int expr_const(void) +static inline int64_t expr_const64(void) { - int c; + int64_t c; expr_const1(); if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) expect("constant expression"); @@ -5283,6 +5303,18 @@ ST_FUNC int expr_const(void) return c; } +/* parse an integer constant and return its value. + Complain if it doesn't fit 32bit (signed or unsigned). */ +ST_FUNC int expr_const(void) +{ + int c; + int64_t wc = expr_const64(); + c = wc; + if (c != wc && (unsigned)c != wc) + tcc_error("constant exceeds 32 bit"); + return c; +} + /* return the label token if current token is a label, otherwise return zero */ static int is_label(void) @@ -5325,34 +5357,36 @@ static void label_or_decl(int l) static int case_cmp(const void *pa, const void *pb) { - int a = (*(struct case_t**) pa)->v1; - int b = (*(struct case_t**) pb)->v1; + int64_t a = (*(struct case_t**) pa)->v1; + int64_t b = (*(struct case_t**) pb)->v1; return a < b ? -1 : a > b; } -static int gcase(struct case_t **base, int len, int case_reg, int *bsym) +static void gcase(struct case_t **base, int len, int *bsym) { struct case_t *p; int e; + int ll = (vtop->type.t & VT_BTYPE) == VT_LLONG; + gv(RC_INT); while (len > 4) { /* binary search */ p = base[len/2]; - vseti(case_reg, 0); vdup(); - vpushi(p->v2); + if (ll) + vpushll(p->v2); + else + vpushi(p->v2); gen_op(TOK_LE); e = gtst(1, 0); - case_reg = gv(RC_INT); - vpop(); - vseti(case_reg, 0); vdup(); - vpushi(p->v1); + if (ll) + vpushll(p->v1); + else + vpushi(p->v1); gen_op(TOK_GE); gtst_addr(0, p->sym); /* v1 <= x <= v2 */ - case_reg = gv(RC_INT); - vpop(); /* x < v1 */ - case_reg = gcase(base, len/2, case_reg, bsym); + gcase(base, len/2, bsym); if (cur_switch->def_sym) gjmp_addr(cur_switch->def_sym); else @@ -5365,28 +5399,27 @@ static int gcase(struct case_t **base, int len, int case_reg, int *bsym) /* linear scan */ while (len--) { p = *base++; - vseti(case_reg, 0); vdup(); - vpushi(p->v2); + if (ll) + vpushll(p->v2); + else + vpushi(p->v2); if (p->v1 == p->v2) { gen_op(TOK_EQ); gtst_addr(0, p->sym); } else { gen_op(TOK_LE); e = gtst(1, 0); - case_reg = gv(RC_INT); - vpop(); - vseti(case_reg, 0); vdup(); - vpushi(p->v1); + if (ll) + vpushll(p->v1); + else + vpushi(p->v1); gen_op(TOK_GE); gtst_addr(0, p->sym); gsym(e); } - case_reg = gv(RC_INT); - vpop(); } - return case_reg; } static void block(int *bsym, int *csym, int is_expr) @@ -5675,13 +5708,12 @@ static void block(int *bsym, int *csym, int is_expr) if (tok == TOK_SWITCH) { struct switch_t *saved, sw; int saved_nocode_wanted = nocode_wanted; + SValue switchval; next(); skip('('); gexpr(); - /* XXX: other types than integer */ - c = gv(RC_INT); - vpop(); skip(')'); + switchval = *vtop--; a = 0; b = gjmp(0); /* jump to first case */ sw.p = NULL; sw.n = 0; sw.def_sym = 0; @@ -5697,7 +5729,13 @@ static void block(int *bsym, int *csym, int is_expr) for (b = 1; b < sw.n; b++) if (sw.p[b - 1]->v2 >= sw.p[b]->v1) tcc_error("duplicate case value"); - gcase(sw.p, sw.n, c, &a); + /* Our switch table sorting is signed, so the compared + value needs to be as well when it's 64bit. */ + if ((switchval.type.t & VT_BTYPE) == VT_LLONG) + switchval.type.t &= ~VT_UNSIGNED; + vpushv(&switchval); + gcase(sw.p, sw.n, &a); + vpop(); if (sw.def_sym) gjmp_addr(sw.def_sym); } @@ -5712,10 +5750,10 @@ static void block(int *bsym, int *csym, int is_expr) expect("switch"); nocode_wanted &= ~2; next(); - cr->v1 = cr->v2 = expr_const(); + cr->v1 = cr->v2 = expr_const64(); if (gnu_ext && tok == TOK_DOTS) { next(); - cr->v2 = expr_const(); + cr->v2 = expr_const64(); if (cr->v2 < cr->v1) tcc_warning("empty case range"); } diff --git a/tests/tcctest.c b/tests/tcctest.c index 7b1ac1a..2249cb4 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -606,6 +606,24 @@ enum test { struct S_enum { enum {E6 = 42, E7, E8} e:8; }; + +enum ELong { + /* This is either 0 on L32 machines, or a large number + on L64 machines. We should be able to store this. */ + EL_large = (unsigned long)0xf000 << 32, +}; + +enum { BIASU = -1U<<31 }; +enum { BIASS = -1 << 31 }; + +static int getint(int i) +{ + if (i) + return 0; + else + return (int)(-1U << 31); +} + void enum_test() { enum test b1; @@ -617,6 +635,16 @@ void enum_test() E0, E1, E2, E3, E4, E5); b1 = 1; printf("b1=%d\n", b1); + printf("enum large: %ld\n", EL_large); + + if (getint(0) == BIASU) + printf("enum unsigned: ok\n"); + else + printf("enum unsigned: wrong\n"); + if (getint(0) == BIASS) + printf("enum unsigned: ok\n"); + else + printf("enum unsigned: wrong\n"); } typedef int *my_ptr; @@ -1727,10 +1755,45 @@ void init_test(void) printf("linit18= %d %d\n", linit18[0], linit18[1]); } +void switch_uc(unsigned char uc) +{ + switch (uc) { + case 0xfb ... 0xfe: + printf("ucsw:1\n"); + break; + case 0xff: + printf("ucsw:2\n"); + break; + case 0 ... 5: + printf("ucsw:3\n"); + break; + default: + printf("ucsw: broken!\n"); + } +} + +void switch_sc(signed char sc) +{ + switch (sc) { + case -5 ... -2: + printf("scsw:1\n"); + break; + case -1: + printf("scsw:2\n"); + break; + case 0 ... 5: + printf("scsw:3\n"); + break; + default: + printf("scsw: broken!\n"); + } +} void switch_test() { int i; + unsigned long long ull; + long long ll; for(i=0;i<15;i++) { switch(i) { @@ -1753,6 +1816,60 @@ void switch_test() } } printf("\n"); + + for (i = 1; i <= 5; i++) { + ull = (unsigned long long)i << 61; + switch (ull) { + case 1ULL << 61: + printf("ullsw:1\n"); + break; + case 2ULL << 61: + printf("ullsw:2\n"); + break; + case 3ULL << 61: + printf("ullsw:3\n"); + break; + case 4ULL << 61: + printf("ullsw:4\n"); + break; + case 5ULL << 61: + printf("ullsw:5\n"); + break; + default: + printf("ullsw: broken!\n"); + } + } + + for (i = 1; i <= 5; i++) { + ll = (long long)i << 61; + switch (ll) { + case 1LL << 61: + printf("llsw:1\n"); + break; + case 2LL << 61: + printf("llsw:2\n"); + break; + case 3LL << 61: + printf("llsw:3\n"); + break; + case 4LL << 61: + printf("llsw:4\n"); + break; + case 5LL << 61: + printf("llsw:5\n"); + break; + default: + printf("llsw: broken!\n"); + } + } + + for (i = -5; i <= 5; i++) { + switch_uc((unsigned char)i); + } + + for (i = -5; i <= 5; i++) { + switch_sc ((signed char)i); + } } /* ISOC99 _Bool type */