#include #include #include #include #include #include "type.h" void type_dump_func(struct type *t, FILE *fp) { struct tfield *arg; type_dump(t->func.type, fp); fputs(" (*)(", fp); if (!t->func.args) { fputs("void)", fp); return; } for (arg = t->func.args; arg->type != T_VOID; arg++) { if (arg != t->func.args) fputs(", ", fp); type_dump(arg->type, fp); } fputc(')', fp); } void type_dump(struct type *t, FILE *fp) { if (!t) { fputs("", fp); return; } switch (t->ttype){ case T_VOID: fputs("void", fp); break; case T_TYPEDEF: fputs(t->tdef.name, fp); break; case T_SCALAR: fputs(t->scalar.name, fp); break; case T_POINTER: type_dump(t->ptr.type, fp); fputs(" *", fp); break; case T_ARRAY: type_dump(t->array.type, fp); fprintf(fp, "[%zu]", t->array.len); break; case T_STRUCT: fputs("struct ", fp); fputs(t->sou.name, fp); break; /* case T_UNION: */ /* fputs("union ", fp); */ /* fputs(t->sou.name, fp); */ /* break; */ case T_FUNC: type_dump_func(t, fp); break; case T_MAP: fputs("map [", fp); type_dump(t->map.ktype, fp); fputs("] ", fp); type_dump(t->map.vtype, fp); break; } } void type_dump_cdecl_sou(struct type *t, FILE *fp) { struct tfield *f; type_dump(t, fp); fputs(" {\n", fp); for (f = t->sou.fields; f->type; f++) { fputc('\t', fp); type_dump(f->type, fp); fprintf(fp, " %s;\n", f->name); } fputs("}", fp); } void type_dump_cdecl(struct type *t, FILE *fp) { switch (t->ttype) { case T_TYPEDEF: fputs("typedef ", fp); type_dump(t->tdef.type, fp); fprintf(fp, " %s", t->tdef.name); break; case T_STRUCT: /* case T_UNION: */ type_dump_cdecl_sou(t, fp); break; case T_VOID: case T_SCALAR: case T_POINTER: case T_ARRAY: case T_MAP: case T_FUNC: type_dump(t, fp); break; } } /* void types_dump_cdecl(FILE *fp) */ /* { */ /* size_t i; */ /* for (i = 0; i < types.len; i++) { */ /* struct type *t = types.type[i]; */ /* type_dump_cdecl(t, stdout); */ /* printf(" \n", t->size); */ /* } */ /* } */ struct type *type_normalize(struct type *t) { while (t->ttype == T_TYPEDEF) t = t->tdef.type; return t; } int type_equal(struct type *a, struct type *b) { /* TODO */ return a == b; } int type_compatible(struct type *a, struct type *b) { a = type_normalize(a); b = type_normalize(b); if (a->ttype != b->ttype) return 0; switch (a->ttype){ case T_VOID: case T_SCALAR: case T_POINTER: return 1; case T_ARRAY: if (a->array.len != b->array.len) return 0; return type_compatible(a->array.type, b->array.type); case T_STRUCT: /* case T_UNION: */ return !strcmp(a->sou.name, b->sou.name); case T_FUNC: return type_compatible(a->func.type, b->func.type); case T_MAP: return type_compatible(a->map.vtype, b->map.vtype); case T_TYPEDEF: assert(0); } assert(0); return 0; } static ssize_t type_alignof_struct(struct type *t) { struct tfield *f; ssize_t falign, align = -EINVAL; tfields_foreach(f, t->sou.fields) { falign = type_alignof(f->type); if (falign < 0) return falign; if (falign > align) align = falign; } return align; } ssize_t type_alignof(struct type *t) { switch (t->ttype){ case T_VOID: case T_SCALAR: case T_POINTER: case T_FUNC: case T_MAP: return type_sizeof(t); case T_TYPEDEF: return type_alignof(t->tdef.type); case T_ARRAY: return type_alignof(t->array.type); case T_STRUCT: return type_alignof_struct(t); } return -EINVAL; } ssize_t type_offset_size_of(struct type *t, const char *field) { struct tfield *f; size_t offset = 0; ssize_t fsize, falign; assert(t->ttype == T_STRUCT); if (!t->sou.fields) return -ENOENT; tfields_foreach(f, t->sou.fields) { fsize = type_sizeof(f->type); if (fsize < 0) return fsize; falign = type_alignof(f->type); if (falign < 0) return falign; if (!t->sou.packed && (falign > 1)) offset += falign - (offset % falign); if (field && !strcmp(f->name, field)) return offset; offset += fsize; } if (field) return -ENOENT; if (!t->sou.packed && offset && (falign > 1)) offset += falign - (offset % falign); return offset; } ssize_t type_offsetof(struct type *t, const char *field) { return type_offset_size_of(t, field); } ssize_t type_sizeof_struct(struct type *t) { return type_offset_size_of(t, NULL); } ssize_t type_sizeof(struct type *t) { switch (t->ttype){ case T_VOID: return sizeof(void); case T_SCALAR: return t->scalar.size; case T_TYPEDEF: return type_sizeof(t->tdef.type); case T_POINTER: case T_FUNC: return sizeof(void *); case T_ARRAY: return t->array.len * type_sizeof(t->array.type); case T_STRUCT: return type_sizeof_struct(t); case T_MAP: return sizeof(int); } return -EINVAL; } int all_types_cmp(const void *_a, const void *_b) { const struct type *a = *((struct type **)_a); const struct type *b = *((struct type **)_b); return a - b; } struct type_list { struct type **types; size_t len; } all_types; #define types_foreach(_t) \ for ((_t) = all_types.types[0]; (_t) <= all_types.types[all_types.len]; (_t)++) int type_add(struct type *t) { if (bsearch(t, all_types.types, all_types.len, sizeof(*all_types.types), all_types_cmp)) return 0; /* type_size_set(t); */ all_types.types = realloc(all_types.types, ++all_types.len * sizeof(*all_types.types)); all_types.types[all_types.len - 1] = t; qsort(all_types.types, all_types.len, sizeof(*all_types.types), all_types_cmp); return 0; } int type_add_list(struct type **ts) { int err; for (; *ts; ts++) { err = type_add(*ts); if (err) return err; } return 0; } struct type *type_array_of(struct type *type, size_t len) { struct type *t; types_foreach(t) { if ((t->ttype == T_ARRAY) && (t->array.type == type) && (t->array.len == len)) return t; } t = calloc(1, sizeof(*t)); t->ttype = T_ARRAY; t->array.type = type; t->array.len = len; type_add(t); return t; } struct type *type_map_of(struct type *ktype, struct type *vtype) { struct type *t; types_foreach(t) { if ((t->ttype == T_MAP) && (t->map.ktype == ktype) && (t->map.vtype == vtype)) return t; } t = calloc(1, sizeof(*t)); t->ttype = T_MAP; t->map.vtype = vtype; t->map.ktype = ktype; type_add(t); return t; } struct type *type_ptr_of(struct type *type) { struct type *t; types_foreach(t) { if ((t->ttype == T_POINTER) && (t->ptr.type == type)) return t; } t = calloc(1, sizeof(*t)); t->ttype = T_POINTER; t->ptr.type = type; type_add(t); return t; } #define is_signed(_t) (((_t)(-1)) < 0) #define builtin_scalar(_t) { \ .ttype = T_SCALAR, \ .scalar = { \ .name = #_t, \ .size = sizeof(_t), \ .is_signed = is_signed(_t), \ }, \ } struct type t_void = { .ttype = T_VOID }; #pragma GCC diagnostic ignored "-Wtype-limits" /* is_signed will generate a warning for unsigned types since the * expression can never be true. this is exactly what we're interested * in here though. it gets us out of having to specify scalar * signedness per architecture. */ struct type t_char = builtin_scalar(char); struct type t_schar = builtin_scalar(signed char); struct type t_uchar = builtin_scalar(unsigned char); struct type t_short = builtin_scalar(short); struct type t_sshort = builtin_scalar(signed short); struct type t_ushort = builtin_scalar(unsigned short); struct type t_int = builtin_scalar(int); struct type t_sint = builtin_scalar(signed int); struct type t_uint = builtin_scalar(unsigned int); struct type t_long = builtin_scalar(long); struct type t_slong = builtin_scalar(signed long); struct type t_ulong = builtin_scalar(unsigned long); struct type t_llong = builtin_scalar(long long); struct type t_sllong = builtin_scalar(signed long long); struct type t_ullong = builtin_scalar(unsigned long long); #pragma GCC diagnostic pop struct type *builtin_types[] = { &t_void, &t_char, &t_schar, &t_uchar, &t_short, &t_sshort, &t_ushort, &t_int, &t_sint, &t_uint, &t_long, &t_slong, &t_ulong, &t_llong, &t_sllong, &t_ullong, NULL }; __attribute__((constructor)) static void type_init(void) { type_add_list(builtin_types); }