/* struct.c structure support Copyright (C) 2001 Bill Currie Copyright (C) 2011 Bill Currie Author: Bill Currie Date: 2001/12/08 Date: 2011/01/17 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include #include #include #include #include #include "tools/qfcc/include/class.h" #include "tools/qfcc/include/def.h" #include "tools/qfcc/include/defspace.h" #include "tools/qfcc/include/diagnostic.h" #include "tools/qfcc/include/emit.h" #include "tools/qfcc/include/expr.h" #include "tools/qfcc/include/obj_type.h" #include "tools/qfcc/include/qfcc.h" #include "tools/qfcc/include/reloc.h" #include "tools/qfcc/include/shared.h" #include "tools/qfcc/include/strpool.h" #include "tools/qfcc/include/struct.h" #include "tools/qfcc/include/symtab.h" #include "tools/qfcc/include/type.h" #include "tools/qfcc/include/value.h" static symbol_t * find_tag (ty_meta_e meta, symbol_t *tag, type_t *type) { const char *tag_name; symbol_t *sym; if (tag) { tag_name = va (0, "tag %s", tag->name); } else { const char *path = GETSTR (pr.source_file); const char *file = strrchr (path, '/'); if (!file++) file = path; tag_name = va (0, "tag .%s.%d", file, pr.source_line); } sym = symtab_lookup (current_symtab, tag_name); if (sym) { if (sym->table == current_symtab && sym->type->meta != meta) error (0, "%s defined as wrong kind of tag", tag->name); if (sym->type->meta == meta) return sym; } sym = new_symbol (tag_name); if (!type) type = new_type (); if (!type->name) type->name = sym->name; sym->type = type; sym->type->type = ev_invalid; sym->type->meta = meta; sym->sy_type = sy_type; return sym; } symtab_t * start_struct (int *su, symbol_t *tag, symtab_t *parent) { symbol_t *sym; sym = find_struct (*su, tag, 0); if (!sym->table) { symtab_addsymbol (parent, sym); } else { if (!sym->type) { internal_error (0, "broken structure symbol?"); } if (tag) { tag->type = sym->type; } if (sym->type->meta == ty_enum || (sym->type->meta == ty_struct && sym->type->t.symtab)) { error (0, "%s %s redefined", *su == 's' ? "struct" : "union", tag->name); *su = 0; } else if (sym->type->meta != ty_struct) { internal_error (0, "%s is not a struct or union", tag->name); } } return new_symtab (parent, stab_struct); } symbol_t * find_handle (symbol_t *tag, type_t *type) { symbol_t *sym = find_tag (ty_handle, tag, type); if (sym->type->type == ev_invalid) { sym->type->type = ev_func; sym->type->width = 1; sym->type->alignment = 1; } return sym; } symbol_t * find_struct (int su, symbol_t *tag, type_t *type) { ty_meta_e meta = ty_struct; if (su == 'u') meta = ty_union; return find_tag (meta, tag, type); } symbol_t * build_struct (int su, symbol_t *tag, symtab_t *symtab, type_t *type, int base) { symbol_t *sym = find_struct (su, tag, type); symbol_t *s; int alignment = 1; symbol_t *as; symtab->parent = 0; // disconnect struct's symtab from parent scope if (sym->table == current_symtab && sym->type->t.symtab) { error (0, "%s defined as wrong kind of tag", tag->name); return sym; } for (s = symtab->symbols; s; s = s->next) { if (s->sy_type != sy_var) continue; if (is_class (s->type)) { error (0, "statically allocated instance of class %s", s->type->t.class->name); } if (su == 's') { symtab->size = RUP (symtab->size + base, s->type->alignment) - base; s->s.offset = symtab->size; symtab->size += type_size (s->type); } else { int size = type_size (s->type); if (size > symtab->size) { symtab->size = RUP (size, s->type->alignment); } } if (s->type->alignment > alignment) { alignment = s->type->alignment; } if (s->visibility == vis_anonymous) { symtab_t *anonymous; symbol_t *t = s->next; int offset = s->s.offset; if (!is_struct (s->type) && !is_union (s->type)) { internal_error (0, "non-struct/union anonymous field"); } anonymous = s->type->t.symtab; for (as = anonymous->symbols; as; as = as->next) { if (as->visibility == vis_anonymous || as->sy_type!= sy_var) { continue; } if (Hash_Find (symtab->tab, as->name)) { error (0, "ambiguous field `%s' in anonymous %s", as->name, su == 's' ? "struct" : "union"); } else { s->next = copy_symbol (as); s = s->next; s->s.offset += offset; s->table = symtab; s->no_auto_init = 1; Hash_Add (symtab->tab, s); } } s->next = t; } } if (!type) sym->type = find_type (sym->type); // checks the tag, not the symtab sym->type->t.symtab = symtab; if (alignment > sym->type->alignment) { sym->type->alignment = alignment; } if (!type && sym->type->type_def->external) //FIXME should not be necessary sym->type->type_def = qfo_encode_type (sym->type, pr.type_data); return sym; } symbol_t * find_enum (symbol_t *tag) { return find_tag (ty_enum, tag, 0); } symtab_t * start_enum (symbol_t *sym) { if (sym->table == current_symtab && sym->type->t.symtab) { error (0, "%s defined as wrong kind of tag", sym->name); sym = find_enum (0); } sym->type->t.symtab = new_symtab (current_symtab, stab_enum); sym->type->alignment = 1; sym->type->width = 1; return sym->type->t.symtab; } symbol_t * finish_enum (symbol_t *sym) { symbol_t *enum_sym; symbol_t *name; type_t *enum_type; symtab_t *enum_tab; enum_type = sym->type = find_type (sym->type); enum_tab = enum_type->t.symtab; for (name = enum_tab->symbols; name; name = name->next) { name->type = sym->type; enum_sym = new_symbol_type (name->name, enum_type); enum_sym->sy_type = sy_const; enum_sym->s.value = name->s.value; symtab_addsymbol (enum_tab->parent, enum_sym); } return sym; } void add_enum (symbol_t *enm, symbol_t *name, expr_t *val) { type_t *enum_type = enm->type; symtab_t *enum_tab = enum_type->t.symtab; int value; if (name->table == current_symtab || name->table == enum_tab) error (0, "%s redefined", name->name); if (name->table) name = new_symbol (name->name); name->sy_type = sy_const; name->type = enum_type; value = 0; if (enum_tab->symbols) value = ((symbol_t *)(enum_tab->symtail))->s.value->v.int_val + 1; if (val) { convert_name (val); if (!is_constant (val)) error (val, "non-constant initializer"); else if (!is_int_val (val)) error (val, "invalid initializer type"); else value = expr_int (val); } name->s.value = new_int_val (value); symtab_addsymbol (enum_tab, name); } int enum_as_bool (type_t *enm, expr_t **zero, expr_t **one) { symtab_t *symtab = enm->t.symtab; symbol_t *zero_sym = 0; symbol_t *one_sym = 0; symbol_t *sym; int val, v; if (!symtab) return 0; for (sym = symtab->symbols; sym; sym = sym->next) { if (sym->sy_type != sy_const) continue; val = sym->s.value->v.int_val; if (!val) { zero_sym = sym; } else { if (one_sym) { v = one_sym->s.value->v.int_val; if (val * val > v * v) continue; } one_sym = sym; } } if (!zero_sym || !one_sym) return 0; *zero = new_symbol_expr (zero_sym); *one = new_symbol_expr (one_sym); return 1; } symbol_t * make_structure (const char *name, int su, struct_def_t *defs, type_t *type) { symtab_t *strct; symbol_t *field; symbol_t *sym = 0; if (name) sym = new_symbol (name); if (su == 'u') strct = new_symtab (0, stab_union); else strct = new_symtab (0, stab_struct); while (defs->name) { field = new_symbol_type (defs->name, defs->type); field->sy_type = sy_var; if (!symtab_addsymbol (strct, field)) internal_error (0, "duplicate symbol: %s", defs->name); defs++; } sym = build_struct (su, sym, strct, type, 0); return sym; } def_t * emit_structure (const char *name, int su, struct_def_t *defs, type_t *type, void *data, defspace_t *space, storage_class_t storage) { int i, j; int saw_null = 0; int saw_func = 0; symbol_t *struct_sym; symbol_t *field_sym; def_t *struct_def; def_t field_def; name = save_string (name); if (!type) type = make_structure (0, su, defs, 0)->type; if ((su == 's' && !is_struct (type)) || (su == 'u' && !is_union (type))) internal_error (0, "structure %s type mismatch", name); for (i = 0, field_sym = type->t.symtab->symbols; field_sym; i++, field_sym = field_sym->next) { if (!defs[i].name) internal_error (0, "structure %s unexpected end of defs", name); if (field_sym->type != defs[i].type) internal_error (0, "structure %s.%s field type mismatch", name, defs[i].name); if ((!defs[i].emit && saw_func) || (defs[i].emit && saw_null)) internal_error (0, "structure %s mixed emit/copy", name); if (!defs[i].emit) saw_null = 1; if (defs[i].emit) saw_func = 1; } if (defs[i].name) internal_error (0, "structure %s too many defs", name); if (storage != sc_global && storage != sc_static) internal_error (0, "structure %s must be global or static", name); if (!space) { space = pr.far_data; } struct_sym = make_symbol (name, type, space, storage); struct_def = struct_sym->s.def; if (struct_def->initialized) internal_error (0, "structure %s already initialized", name); struct_def->initialized = struct_def->constant = 1; struct_def->nosave = 1; for (i = 0, field_sym = type->t.symtab->symbols; field_sym; i++, field_sym = field_sym->next) { field_def.type = field_sym->type; field_def.name = save_string (va (0, "%s.%s", name, field_sym->name)); field_def.space = struct_def->space; field_def.offset = struct_def->offset + field_sym->s.offset; if (!defs[i].emit) { //FIXME relocs? arrays? structs? pr_type_t *val = (pr_type_t *) data; memcpy (D_POINTER (pr_type_t, &field_def), val, type_size (field_def.type) * sizeof (pr_type_t)); data = &val[type_size (field_def.type)]; } else { if (is_array (field_def.type)) { type_t *type = field_def.type->t.array.type; for (j = 0; j < field_def.type->t.array.size; j++) { defs[i].emit (&field_def, data, j); field_def.offset += type_size (type); } } else { defs[i].emit (&field_def, data, 0); } } } return struct_def; }