refactor enums

Eliminate VT_ENUM as a basic type.  Instead use the integral
types VT_InT/VT_LLONG with an additional flag to indicate
that it is an enum.
master
grischka 2017-07-09 12:38:25 +02:00
parent 9ba76ac834
commit f87afa72e0
4 changed files with 107 additions and 102 deletions

View File

@ -249,7 +249,6 @@ static int arm64_type_size(int t)
case VT_BYTE: return 0; case VT_BYTE: return 0;
case VT_SHORT: return 1; case VT_SHORT: return 1;
case VT_PTR: return 3; case VT_PTR: return 3;
case VT_ENUM: return 2;
case VT_FUNC: return 3; case VT_FUNC: return 3;
case VT_FLOAT: return 2; case VT_FLOAT: return 2;
case VT_DOUBLE: return 3; case VT_DOUBLE: return 3;

31
tcc.h
View File

@ -416,8 +416,7 @@ struct SymAttr {
visibility : 2, visibility : 2,
dllexport : 1, dllexport : 1,
dllimport : 1, dllimport : 1,
unsigned_enum : 1, unused : 5;
unused : 4;
}; };
/* function attributes or temporary attributes for parsing */ /* function attributes or temporary attributes for parsing */
@ -852,14 +851,13 @@ struct filespec {
#define VT_PTR 5 /* pointer */ #define VT_PTR 5 /* pointer */
#define VT_FUNC 6 /* function type */ #define VT_FUNC 6 /* function type */
#define VT_STRUCT 7 /* struct/union definition */ #define VT_STRUCT 7 /* struct/union definition */
#define VT_ENUM 8 /* enum definition */ #define VT_FLOAT 8 /* IEEE float */
#define VT_FLOAT 9 /* IEEE float */ #define VT_DOUBLE 9 /* IEEE double */
#define VT_DOUBLE 10 /* IEEE double */ #define VT_LDOUBLE 10 /* IEEE long double */
#define VT_LDOUBLE 11 /* IEEE long double */ #define VT_BOOL 11 /* ISOC99 boolean type */
#define VT_BOOL 12 /* ISOC99 boolean type */ #define VT_LONG 12 /* long integer (NEVER USED as type, only during parsing) */
#define VT_LONG 13 /* long integer (NEVER USED as type, only during parsing) */ #define VT_QLONG 13 /* 128-bit integer. Only used for x86-64 ABI */
#define VT_QLONG 14 /* 128-bit integer. Only used for x86-64 ABI */ #define VT_QFLOAT 14 /* 128-bit float. Only used for x86-64 ABI */
#define VT_QFLOAT 15 /* 128-bit float. Only used for x86-64 ABI */
#define VT_UNSIGNED 0x0010 /* unsigned type */ #define VT_UNSIGNED 0x0010 /* unsigned type */
#define VT_DEFSIGN 0x0020 /* explicitly signed or unsigned */ #define VT_DEFSIGN 0x0020 /* explicitly signed or unsigned */
@ -868,7 +866,6 @@ struct filespec {
#define VT_CONSTANT 0x0100 /* const modifier */ #define VT_CONSTANT 0x0100 /* const modifier */
#define VT_VOLATILE 0x0200 /* volatile modifier */ #define VT_VOLATILE 0x0200 /* volatile modifier */
#define VT_VLA 0x0400 /* VLA type (also has VT_PTR and VT_ARRAY) */ #define VT_VLA 0x0400 /* VLA type (also has VT_PTR and VT_ARRAY) */
#define VT_UNION (VT_STRUCT|VT_UNSIGNED) /* VT_STRUCT type with some modifier */
/* storage */ /* storage */
#define VT_EXTERN 0x00001000 /* extern definition */ #define VT_EXTERN 0x00001000 /* extern definition */
@ -882,9 +879,17 @@ struct filespec {
#define BIT_POS(t) (((t) >> VT_STRUCT_SHIFT) & 0x3f) #define BIT_POS(t) (((t) >> VT_STRUCT_SHIFT) & 0x3f)
#define BIT_SIZE(t) (((t) >> (VT_STRUCT_SHIFT + 6)) & 0x3f) #define BIT_SIZE(t) (((t) >> (VT_STRUCT_SHIFT + 6)) & 0x3f)
#define VT_UNION (1 << VT_STRUCT_SHIFT | VT_STRUCT)
#define VT_ENUM (2 << VT_STRUCT_SHIFT) /* integral type is an enum really */
#define VT_ENUM_VAL (3 << VT_STRUCT_SHIFT) /* integral type is an enum constant really */
#define IS_ENUM(t) ((t & VT_STRUCT_MASK) == VT_ENUM)
#define IS_ENUM_VAL(t) ((t & VT_STRUCT_MASK) == VT_ENUM_VAL)
#define IS_UNION(t) ((t & (VT_STRUCT_MASK|VT_BTYPE)) == VT_UNION)
/* type mask (except storage) */ /* type mask (except storage) */
#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE | VT_STRUCT_MASK) #define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE)
#define VT_TYPE (~VT_STORAGE) #define VT_TYPE (~(VT_STORAGE|VT_STRUCT_MASK))
/* token values */ /* token values */

167
tccgen.c
View File

@ -86,7 +86,7 @@ static void expr_eq(void);
static void vla_runtime_type_size(CType *type, int *a); static void vla_runtime_type_size(CType *type, int *a);
static void vla_sp_restore(void); static void vla_sp_restore(void);
static void vla_sp_restore_root(void); static void vla_sp_restore_root(void);
static int is_compatible_parameter_types(CType *type1, CType *type2); static int is_compatible_unqualified_types(CType *type1, CType *type2);
static inline int64_t expr_const64(void); static inline int64_t expr_const64(void);
ST_FUNC void vpush64(int ty, unsigned long long v); ST_FUNC void vpush64(int ty, unsigned long long v);
ST_FUNC void vpush(CType *type); ST_FUNC void vpush(CType *type);
@ -1087,10 +1087,8 @@ ST_FUNC int gv(int rc)
bits = 64; bits = 64;
} else } else
type.t = VT_INT; type.t = VT_INT;
if((vtop->type.t & VT_UNSIGNED) || if((vtop->type.t & VT_UNSIGNED)
(vtop->type.t & VT_BTYPE) == VT_BOOL || || (vtop->type.t & VT_BTYPE) == VT_BOOL)
(((vtop->type.t & VT_BTYPE) == VT_ENUM) &&
vtop->type.ref->a.unsigned_enum))
type.t |= VT_UNSIGNED; type.t |= VT_UNSIGNED;
gen_cast(&type); gen_cast(&type);
/* generate shifts */ /* generate shifts */
@ -2479,6 +2477,8 @@ ST_FUNC int type_size(CType *type, int *a)
*a = PTR_SIZE; *a = PTR_SIZE;
return PTR_SIZE; return PTR_SIZE;
} }
} else if (IS_ENUM(type->t) && type->ref->c == -1) {
return -1; /* incomplete enum */
} else if (bt == VT_LDOUBLE) { } else if (bt == VT_LDOUBLE) {
*a = LDOUBLE_ALIGN; *a = LDOUBLE_ALIGN;
return LDOUBLE_SIZE; return LDOUBLE_SIZE;
@ -2508,10 +2508,6 @@ ST_FUNC int type_size(CType *type, int *a)
} else if (bt == VT_QLONG || bt == VT_QFLOAT) { } else if (bt == VT_QLONG || bt == VT_QFLOAT) {
*a = 8; *a = 8;
return 16; return 16;
} else if (bt == VT_ENUM) {
*a = 4;
/* Enums might be incomplete, so don't just return '4' here. */
return type->ref->c;
} else { } else {
/* char, void, function, _Bool */ /* char, void, function, _Bool */
*a = 1; *a = 1;
@ -2554,7 +2550,7 @@ ST_FUNC void mk_pointer(CType *type)
{ {
Sym *s; Sym *s;
s = sym_push(SYM_FIELD, type, 0, -1); s = sym_push(SYM_FIELD, type, 0, -1);
type->t = VT_PTR | (type->t & ~VT_TYPE); type->t = VT_PTR | (type->t & VT_STORAGE);
type->ref = s; type->ref = s;
} }
@ -2578,7 +2574,7 @@ static int is_compatible_func(CType *type1, CType *type2)
while (s1 != NULL) { while (s1 != NULL) {
if (s2 == NULL) if (s2 == NULL)
return 0; return 0;
if (!is_compatible_parameter_types(&s1->type, &s2->type)) if (!is_compatible_unqualified_types(&s1->type, &s2->type))
return 0; return 0;
s1 = s1->next; s1 = s1->next;
s2 = s2->next; s2 = s2->next;
@ -2609,21 +2605,7 @@ static int compare_types(CType *type1, CType *type2, int unqualified)
t1 &= ~VT_DEFSIGN; t1 &= ~VT_DEFSIGN;
t2 &= ~VT_DEFSIGN; t2 &= ~VT_DEFSIGN;
} }
/* An enum is compatible with (unsigned) int. Ideally we would
store the enums signedness in type->ref.a.<some_bit> and
only accept unsigned enums with unsigned int and vice versa.
But one of our callers (gen_assign_cast) always strips VT_UNSIGNED
from pointer target types, so we can't add it here either. */
if ((t1 & VT_BTYPE) == VT_ENUM) {
t1 = VT_INT;
if (type1->ref->a.unsigned_enum)
t1 |= VT_UNSIGNED;
}
if ((t2 & VT_BTYPE) == VT_ENUM) {
t2 = VT_INT;
if (type2->ref->a.unsigned_enum)
t2 |= VT_UNSIGNED;
}
/* XXX: bitfields ? */ /* XXX: bitfields ? */
if (t1 != t2) if (t1 != t2)
return 0; return 0;
@ -2652,7 +2634,7 @@ static int is_compatible_types(CType *type1, CType *type2)
/* return true if type1 and type2 are the same (ignoring qualifiers). /* return true if type1 and type2 are the same (ignoring qualifiers).
*/ */
static int is_compatible_parameter_types(CType *type1, CType *type2) static int is_compatible_unqualified_types(CType *type1, CType *type2)
{ {
return compare_types(type1,type2,1); return compare_types(type1,type2,1);
} }
@ -2690,6 +2672,10 @@ static void type_to_str(char *buf, int buf_size,
pstrcat(buf, buf_size, "inline "); pstrcat(buf, buf_size, "inline ");
buf_size -= strlen(buf); buf_size -= strlen(buf);
buf += strlen(buf); buf += strlen(buf);
if (IS_ENUM(type->t)) {
tstr = "enum ";
goto tstruct;
}
switch(bt) { switch(bt) {
case VT_VOID: case VT_VOID:
tstr = "void"; tstr = "void";
@ -2723,12 +2709,11 @@ static void type_to_str(char *buf, int buf_size,
add_tstr: add_tstr:
pstrcat(buf, buf_size, tstr); pstrcat(buf, buf_size, tstr);
break; break;
case VT_ENUM:
case VT_STRUCT: case VT_STRUCT:
if (bt == VT_STRUCT) tstr = "struct ";
tstr = "struct "; if (IS_UNION(t))
else tstr = "union ";
tstr = "enum "; tstruct:
pstrcat(buf, buf_size, tstr); pstrcat(buf, buf_size, tstr);
v = type->ref->v & ~SYM_STRUCT; v = type->ref->v & ~SYM_STRUCT;
if (v >= SYM_FIRST_ANOM) if (v >= SYM_FIRST_ANOM)
@ -2827,21 +2812,16 @@ static void gen_assign_cast(CType *dt)
(type2->t & VT_BTYPE) == VT_VOID) { (type2->t & VT_BTYPE) == VT_VOID) {
/* void * can match anything */ /* void * can match anything */
} else { } else {
//printf("types %08x %08x\n", type1->t, type2->t);
/* exact type match, except for qualifiers */ /* exact type match, except for qualifiers */
tmp_type1 = *type1; if (!is_compatible_unqualified_types(type1, type2)) {
tmp_type2 = *type2;
tmp_type1.t &= ~(VT_CONSTANT | VT_VOLATILE);
tmp_type2.t &= ~(VT_CONSTANT | VT_VOLATILE);
if (!is_compatible_types(&tmp_type1, &tmp_type2)) {
/* Like GCC don't warn by default for merely changes /* Like GCC don't warn by default for merely changes
in pointer target signedness. Do warn for different in pointer target signedness. Do warn for different
base types, though, in particular for unsigned enums base types, though, in particular for unsigned enums
and signed int targets. */ and signed int targets. */
if ((tmp_type1.t & (VT_DEFSIGN | VT_UNSIGNED)) != if ((type1->t & VT_BTYPE) != (type2->t & VT_BTYPE)
(tmp_type2.t & (VT_DEFSIGN | VT_UNSIGNED)) && || IS_ENUM(type1->t) || IS_ENUM(type2->t)
(tmp_type1.t & VT_BTYPE) == (tmp_type2.t & VT_BTYPE)) )
;
else
tcc_warning("assignment from incompatible pointer type"); tcc_warning("assignment from incompatible pointer type");
} }
} }
@ -3486,7 +3466,7 @@ static void struct_layout(CType *type, AttributeDef *ad)
tcc_warning("will touch memory past end of the struct (internal limitation)"); tcc_warning("will touch memory past end of the struct (internal limitation)");
} }
/* enum/struct/union declaration. u is either VT_ENUM or VT_STRUCT */ /* enum/struct/union declaration. u is VT_ENUM/VT_STRUCT/VT_UNION */
static void struct_decl(CType *type, int u) static void struct_decl(CType *type, int u)
{ {
int v, c, size, align, flexible, alignoverride; int v, c, size, align, flexible, alignoverride;
@ -3506,9 +3486,11 @@ static void struct_decl(CType *type, int u)
expect("struct/union/enum name"); expect("struct/union/enum name");
s = struct_find(v); s = struct_find(v);
if (s && (s->sym_scope == local_scope || tok != '{')) { if (s && (s->sym_scope == local_scope || tok != '{')) {
if ((s->type.t & (VT_BTYPE|VT_UNION)) != u) if (u == s->type.t)
tcc_error("redefinition of '%s'", get_tok_str(v, NULL)); goto do_decl;
goto do_decl; if (u == VT_ENUM && IS_ENUM(s->type.t))
goto do_decl;
tcc_error("redefinition of '%s'", get_tok_str(v, NULL));
} }
} else { } else {
v = anon_sym++; v = anon_sym++;
@ -3520,7 +3502,7 @@ static void struct_decl(CType *type, int u)
s = sym_push(v | SYM_STRUCT, &type1, 0, -1); s = sym_push(v | SYM_STRUCT, &type1, 0, -1);
s->r = 0; /* default alignment is zero as gcc */ s->r = 0; /* default alignment is zero as gcc */
do_decl: do_decl:
type->t = u & VT_BTYPE; /* VT_UNION becomes VT_STRUCT */ type->t = s->type.t;
type->ref = s; type->ref = s;
if (tok == '{') { if (tok == '{') {
@ -3528,13 +3510,15 @@ do_decl:
if (s->c != -1) if (s->c != -1)
tcc_error("struct/union/enum already defined"); tcc_error("struct/union/enum already defined");
/* cannot be empty */ /* cannot be empty */
c = 0;
/* non empty enums are not allowed */ /* non empty enums are not allowed */
ps = &s->next;
if (u == VT_ENUM) { if (u == VT_ENUM) {
int seen_neg = 0; long long ll = 0, pl = 0, nl = 0;
int seen_wide = 0; CType t;
t.ref = s;
/* enum symbols have static storage */
t.t = VT_INT|VT_STATIC|VT_ENUM_VAL;
for(;;) { for(;;) {
CType *t = &int_type;
v = tok; v = tok;
if (v < TOK_UIDENT) if (v < TOK_UIDENT)
expect("identifier"); expect("identifier");
@ -3545,38 +3529,48 @@ do_decl:
next(); next();
if (tok == '=') { if (tok == '=') {
next(); next();
#if PTR_SIZE == 8 ll = expr_const64();
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) ss = sym_push(v, &t, VT_CONST, 0);
seen_neg = 1; ss->enum_val = ll;
if (c != (int)c && (unsigned long)c != (unsigned int)c) *ps = ss, ps = &ss->next;
seen_wide = 1, t = &size_type; if (ll < nl)
/* enum symbols have static storage */ nl = ll;
ss = sym_push(v, t, VT_CONST, c); if (ll > pl)
ss->type.t |= VT_STATIC; pl = ll;
if (tok != ',') if (tok != ',')
break; break;
next(); next();
c++; ll++;
/* NOTE: we accept a trailing comma */ /* NOTE: we accept a trailing comma */
if (tok == '}') if (tok == '}')
break; break;
} }
if (!seen_neg)
s->a.unsigned_enum = 1;
s->c = type_size(seen_wide ? &size_type : &int_type, &align);
skip('}'); skip('}');
/* set integral type of the enum */
t.t = VT_INT;
if (nl == 0) {
if (pl != (unsigned)pl)
t.t = VT_LLONG;
t.t |= VT_UNSIGNED;
} else if (pl != (int)pl || nl != (int)nl)
t.t = VT_LLONG;
s->type.t = type->t = t.t | VT_ENUM;
s->c = 0;
/* set type for enum members */
for (ss = s->next; ss; ss = ss->next) {
ll = ss->enum_val;
if (ll == (int)ll) /* default is int if it fits */
continue;
if (t.t & VT_UNSIGNED) {
ss->type.t |= VT_UNSIGNED;
if (ll == (unsigned)ll)
continue;
}
ss->type.t = (ss->type.t & ~VT_BTYPE) | VT_LLONG;
}
} else { } else {
ps = &s->next; c = 0;
flexible = 0; flexible = 0;
while (tok != '}') { while (tok != '}') {
if (!parse_btype(&btype, &ad1)) { if (!parse_btype(&btype, &ad1)) {
@ -3646,7 +3640,6 @@ do_decl:
bt != VT_BYTE && bt != VT_BYTE &&
bt != VT_SHORT && bt != VT_SHORT &&
bt != VT_BOOL && bt != VT_BOOL &&
bt != VT_ENUM &&
bt != VT_LLONG) bt != VT_LLONG)
tcc_error("bitfields must have scalar type"); tcc_error("bitfields must have scalar type");
bsize = size * 8; bsize = size * 8;
@ -3657,9 +3650,9 @@ do_decl:
/* no need for bit fields */ /* no need for bit fields */
; ;
} else { } else {
type1.t |= VT_BITFIELD | type1.t = (type1.t & ~VT_STRUCT_MASK)
(0 << VT_STRUCT_SHIFT) | | VT_BITFIELD
(bit_size << (VT_STRUCT_SHIFT + 6)); | (bit_size << (VT_STRUCT_SHIFT + 6));
} }
} }
if (v != 0 || (type1.t & VT_BTYPE) == VT_STRUCT) { if (v != 0 || (type1.t & VT_BTYPE) == VT_STRUCT) {
@ -4124,6 +4117,7 @@ static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td)
storage = type->t & VT_STORAGE; storage = type->t & VT_STORAGE;
type->t &= ~VT_STORAGE; type->t &= ~VT_STORAGE;
post = ret = type; post = ret = type;
while (tok == '*') { while (tok == '*') {
qualifiers = 0; qualifiers = 0;
redo: redo:
@ -4813,8 +4807,11 @@ ST_FUNC void unary(void)
Will be used by at least the x86 inline asm parser for Will be used by at least the x86 inline asm parser for
regvars. */ regvars. */
vtop->sym = s; vtop->sym = s;
if (vtop->r & VT_SYM) {
if (r & VT_SYM) {
vtop->c.i = 0; vtop->c.i = 0;
} else if (r == VT_CONST && IS_ENUM_VAL(s->type.t)) {
vtop->c.i = s->enum_val;
} }
break; break;
} }
@ -6352,7 +6349,7 @@ static void decl_initializer(CType *type, Section *sec, unsigned long c,
/* Use i_c_parameter_t, to strip toplevel qualifiers. /* Use i_c_parameter_t, to strip toplevel qualifiers.
The source type might have VT_CONSTANT set, which is The source type might have VT_CONSTANT set, which is
of course assignable to non-const elements. */ of course assignable to non-const elements. */
is_compatible_parameter_types(type, &vtop->type)) { is_compatible_unqualified_types(type, &vtop->type)) {
init_putv(type, sec, c); init_putv(type, sec, c);
} else if (type->t & VT_ARRAY) { } else if (type->t & VT_ARRAY) {
s = type->ref; s = type->ref;
@ -6858,16 +6855,18 @@ static int decl0(int l, int is_for_loop_init, Sym *func_sym)
break; break;
btype.t = VT_INT; btype.t = VT_INT;
} }
if (((btype.t & VT_BTYPE) == VT_ENUM || if (tok == ';') {
(btype.t & VT_BTYPE) == VT_STRUCT) &&
tok == ';') {
if ((btype.t & VT_BTYPE) == VT_STRUCT) { if ((btype.t & VT_BTYPE) == VT_STRUCT) {
int v = btype.ref->v; int v = btype.ref->v;
if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) >= SYM_FIRST_ANOM) if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) >= SYM_FIRST_ANOM)
tcc_warning("unnamed struct/union that defines no instances"); tcc_warning("unnamed struct/union that defines no instances");
next();
continue;
} }
next(); if (IS_ENUM(btype.t)) {
continue; next();
continue;
}
} }
while (1) { /* iterate thru each declaration */ while (1) { /* iterate thru each declaration */
type = btype; type = btype;

View File

@ -415,9 +415,11 @@ void load(int r, SValue *sv)
} else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) { } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {
b = 0xb70f; /* movzwl */ b = 0xb70f; /* movzwl */
} else { } else {
assert(((ft & VT_BTYPE) == VT_INT) || ((ft & VT_BTYPE) == VT_LLONG) assert(((ft & VT_BTYPE) == VT_INT)
|| ((ft & VT_BTYPE) == VT_PTR) || ((ft & VT_BTYPE) == VT_ENUM) || ((ft & VT_BTYPE) == VT_LLONG)
|| ((ft & VT_BTYPE) == VT_FUNC)); || ((ft & VT_BTYPE) == VT_PTR)
|| ((ft & VT_BTYPE) == VT_FUNC)
);
ll = is64_type(ft); ll = is64_type(ft);
b = 0x8b; b = 0x8b;
} }
@ -1092,7 +1094,7 @@ static X86_64_Mode classify_x86_64_inner(CType *ty)
case VT_BOOL: case VT_BOOL:
case VT_PTR: case VT_PTR:
case VT_FUNC: case VT_FUNC:
case VT_ENUM: return x86_64_mode_integer; return x86_64_mode_integer;
case VT_FLOAT: case VT_FLOAT:
case VT_DOUBLE: return x86_64_mode_sse; case VT_DOUBLE: return x86_64_mode_sse;