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.
This commit is contained in:
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_SHORT: return 1;
case VT_PTR: return 3;
case VT_ENUM: return 2;
case VT_FUNC: return 3;
case VT_FLOAT: return 2;
case VT_DOUBLE: return 3;

31
tcc.h
View File

@ -416,8 +416,7 @@ struct SymAttr {
visibility : 2,
dllexport : 1,
dllimport : 1,
unsigned_enum : 1,
unused : 4;
unused : 5;
};
/* function attributes or temporary attributes for parsing */
@ -852,14 +851,13 @@ struct filespec {
#define VT_PTR 5 /* pointer */
#define VT_FUNC 6 /* function type */
#define VT_STRUCT 7 /* struct/union definition */
#define VT_ENUM 8 /* enum definition */
#define VT_FLOAT 9 /* IEEE float */
#define VT_DOUBLE 10 /* IEEE double */
#define VT_LDOUBLE 11 /* IEEE long double */
#define VT_BOOL 12 /* ISOC99 boolean type */
#define VT_LONG 13 /* long integer (NEVER USED as type, only during parsing) */
#define VT_QLONG 14 /* 128-bit integer. Only used for x86-64 ABI */
#define VT_QFLOAT 15 /* 128-bit float. Only used for x86-64 ABI */
#define VT_FLOAT 8 /* IEEE float */
#define VT_DOUBLE 9 /* IEEE double */
#define VT_LDOUBLE 10 /* IEEE long double */
#define VT_BOOL 11 /* ISOC99 boolean type */
#define VT_LONG 12 /* long integer (NEVER USED as type, only during parsing) */
#define VT_QLONG 13 /* 128-bit integer. Only used for x86-64 ABI */
#define VT_QFLOAT 14 /* 128-bit float. Only used for x86-64 ABI */
#define VT_UNSIGNED 0x0010 /* unsigned type */
#define VT_DEFSIGN 0x0020 /* explicitly signed or unsigned */
@ -868,7 +866,6 @@ struct filespec {
#define VT_CONSTANT 0x0100 /* const modifier */
#define VT_VOLATILE 0x0200 /* volatile modifier */
#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 */
#define VT_EXTERN 0x00001000 /* extern definition */
@ -882,9 +879,17 @@ struct filespec {
#define BIT_POS(t) (((t) >> VT_STRUCT_SHIFT) & 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) */
#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE | VT_STRUCT_MASK)
#define VT_TYPE (~VT_STORAGE)
#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE)
#define VT_TYPE (~(VT_STORAGE|VT_STRUCT_MASK))
/* 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_sp_restore(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);
ST_FUNC void vpush64(int ty, unsigned long long v);
ST_FUNC void vpush(CType *type);
@ -1087,10 +1087,8 @@ ST_FUNC int gv(int rc)
bits = 64;
} else
type.t = VT_INT;
if((vtop->type.t & VT_UNSIGNED) ||
(vtop->type.t & VT_BTYPE) == VT_BOOL ||
(((vtop->type.t & VT_BTYPE) == VT_ENUM) &&
vtop->type.ref->a.unsigned_enum))
if((vtop->type.t & VT_UNSIGNED)
|| (vtop->type.t & VT_BTYPE) == VT_BOOL)
type.t |= VT_UNSIGNED;
gen_cast(&type);
/* generate shifts */
@ -2479,6 +2477,8 @@ ST_FUNC int type_size(CType *type, int *a)
*a = PTR_SIZE;
return PTR_SIZE;
}
} else if (IS_ENUM(type->t) && type->ref->c == -1) {
return -1; /* incomplete enum */
} else if (bt == VT_LDOUBLE) {
*a = LDOUBLE_ALIGN;
return LDOUBLE_SIZE;
@ -2508,10 +2508,6 @@ ST_FUNC int type_size(CType *type, int *a)
} else if (bt == VT_QLONG || bt == VT_QFLOAT) {
*a = 8;
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 {
/* char, void, function, _Bool */
*a = 1;
@ -2554,7 +2550,7 @@ ST_FUNC void mk_pointer(CType *type)
{
Sym *s;
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;
}
@ -2578,7 +2574,7 @@ static int is_compatible_func(CType *type1, CType *type2)
while (s1 != NULL) {
if (s2 == NULL)
return 0;
if (!is_compatible_parameter_types(&s1->type, &s2->type))
if (!is_compatible_unqualified_types(&s1->type, &s2->type))
return 0;
s1 = s1->next;
s2 = s2->next;
@ -2609,21 +2605,7 @@ static int compare_types(CType *type1, CType *type2, int unqualified)
t1 &= ~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 ? */
if (t1 != t2)
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).
*/
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);
}
@ -2690,6 +2672,10 @@ static void type_to_str(char *buf, int buf_size,
pstrcat(buf, buf_size, "inline ");
buf_size -= strlen(buf);
buf += strlen(buf);
if (IS_ENUM(type->t)) {
tstr = "enum ";
goto tstruct;
}
switch(bt) {
case VT_VOID:
tstr = "void";
@ -2723,12 +2709,11 @@ static void type_to_str(char *buf, int buf_size,
add_tstr:
pstrcat(buf, buf_size, tstr);
break;
case VT_ENUM:
case VT_STRUCT:
if (bt == VT_STRUCT)
tstr = "struct ";
else
tstr = "enum ";
tstr = "struct ";
if (IS_UNION(t))
tstr = "union ";
tstruct:
pstrcat(buf, buf_size, tstr);
v = type->ref->v & ~SYM_STRUCT;
if (v >= SYM_FIRST_ANOM)
@ -2827,21 +2812,16 @@ static void gen_assign_cast(CType *dt)
(type2->t & VT_BTYPE) == VT_VOID) {
/* void * can match anything */
} else {
//printf("types %08x %08x\n", type1->t, type2->t);
/* exact type match, except for qualifiers */
tmp_type1 = *type1;
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)) {
if (!is_compatible_unqualified_types(type1, type2)) {
/* Like GCC don't warn by default for merely changes
in pointer target signedness. Do warn for different
base types, though, in particular for unsigned enums
and signed int targets. */
if ((tmp_type1.t & (VT_DEFSIGN | VT_UNSIGNED)) !=
(tmp_type2.t & (VT_DEFSIGN | VT_UNSIGNED)) &&
(tmp_type1.t & VT_BTYPE) == (tmp_type2.t & VT_BTYPE))
;
else
if ((type1->t & VT_BTYPE) != (type2->t & VT_BTYPE)
|| IS_ENUM(type1->t) || IS_ENUM(type2->t)
)
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)");
}
/* 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)
{
int v, c, size, align, flexible, alignoverride;
@ -3506,9 +3486,11 @@ static void struct_decl(CType *type, int u)
expect("struct/union/enum name");
s = struct_find(v);
if (s && (s->sym_scope == local_scope || tok != '{')) {
if ((s->type.t & (VT_BTYPE|VT_UNION)) != u)
tcc_error("redefinition of '%s'", get_tok_str(v, NULL));
goto do_decl;
if (u == s->type.t)
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 {
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->r = 0; /* default alignment is zero as gcc */
do_decl:
type->t = u & VT_BTYPE; /* VT_UNION becomes VT_STRUCT */
type->t = s->type.t;
type->ref = s;
if (tok == '{') {
@ -3528,13 +3510,15 @@ do_decl:
if (s->c != -1)
tcc_error("struct/union/enum already defined");
/* cannot be empty */
c = 0;
/* non empty enums are not allowed */
ps = &s->next;
if (u == VT_ENUM) {
int seen_neg = 0;
int seen_wide = 0;
long long ll = 0, pl = 0, nl = 0;
CType t;
t.ref = s;
/* enum symbols have static storage */
t.t = VT_INT|VT_STATIC|VT_ENUM_VAL;
for(;;) {
CType *t = &int_type;
v = tok;
if (v < TOK_UIDENT)
expect("identifier");
@ -3545,38 +3529,48 @@ do_decl:
next();
if (tok == '=') {
next();
#if PTR_SIZE == 8
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
ll = expr_const64();
}
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, t, VT_CONST, c);
ss->type.t |= VT_STATIC;
ss = sym_push(v, &t, VT_CONST, 0);
ss->enum_val = ll;
*ps = ss, ps = &ss->next;
if (ll < nl)
nl = ll;
if (ll > pl)
pl = ll;
if (tok != ',')
break;
next();
c++;
ll++;
/* NOTE: we accept a trailing comma */
if (tok == '}')
break;
}
if (!seen_neg)
s->a.unsigned_enum = 1;
s->c = type_size(seen_wide ? &size_type : &int_type, &align);
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 {
ps = &s->next;
c = 0;
flexible = 0;
while (tok != '}') {
if (!parse_btype(&btype, &ad1)) {
@ -3646,7 +3640,6 @@ do_decl:
bt != VT_BYTE &&
bt != VT_SHORT &&
bt != VT_BOOL &&
bt != VT_ENUM &&
bt != VT_LLONG)
tcc_error("bitfields must have scalar type");
bsize = size * 8;
@ -3657,9 +3650,9 @@ do_decl:
/* no need for bit fields */
;
} else {
type1.t |= VT_BITFIELD |
(0 << VT_STRUCT_SHIFT) |
(bit_size << (VT_STRUCT_SHIFT + 6));
type1.t = (type1.t & ~VT_STRUCT_MASK)
| VT_BITFIELD
| (bit_size << (VT_STRUCT_SHIFT + 6));
}
}
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;
type->t &= ~VT_STORAGE;
post = ret = type;
while (tok == '*') {
qualifiers = 0;
redo:
@ -4813,8 +4807,11 @@ ST_FUNC void unary(void)
Will be used by at least the x86 inline asm parser for
regvars. */
vtop->sym = s;
if (vtop->r & VT_SYM) {
if (r & VT_SYM) {
vtop->c.i = 0;
} else if (r == VT_CONST && IS_ENUM_VAL(s->type.t)) {
vtop->c.i = s->enum_val;
}
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.
The source type might have VT_CONSTANT set, which is
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);
} else if (type->t & VT_ARRAY) {
s = type->ref;
@ -6858,16 +6855,18 @@ static int decl0(int l, int is_for_loop_init, Sym *func_sym)
break;
btype.t = VT_INT;
}
if (((btype.t & VT_BTYPE) == VT_ENUM ||
(btype.t & VT_BTYPE) == VT_STRUCT) &&
tok == ';') {
if (tok == ';') {
if ((btype.t & VT_BTYPE) == VT_STRUCT) {
int v = btype.ref->v;
if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) >= SYM_FIRST_ANOM)
tcc_warning("unnamed struct/union that defines no instances");
next();
continue;
}
next();
continue;
if (IS_ENUM(btype.t)) {
next();
continue;
}
}
while (1) { /* iterate thru each declaration */
type = btype;

View File

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