aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tcc.h6
-rw-r--r--tccgen.c114
-rw-r--r--tests/tcctest.c117
3 files changed, 196 insertions, 41 deletions
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 */