diff --git a/tools/qfcc/include/def.h b/tools/qfcc/include/def.h index 51da57204..5b74397ac 100644 --- a/tools/qfcc/include/def.h +++ b/tools/qfcc/include/def.h @@ -61,6 +61,7 @@ typedef struct def_s { struct defspace_s *space; ///< defspace to which this def belongs int offset; ///< address of this def in its defspace + struct def_s *alias_defs; ///< defs that alias this def struct def_s *alias; ///< real def which this def aliases struct reloc_s *relocs; ///< for relocations struct expr_s *initializer;///< initialer expression @@ -81,6 +82,7 @@ typedef struct def_s { int qfo_def; ///< index to def in qfo defs void *return_addr; ///< who allocated this + void *free_addr; ///< who freed this } def_t; /** Specify the storage class of a def. @@ -123,6 +125,12 @@ def_t *new_def (const char *name, struct type_s *type, is greater than the size of the def's type, then an internal error will be generated. + The alias def keeps track of its base def and is attached to the base def + so any aliases of that def can be found. + + For any combination of type and offset, there will be only one alias def + created. + \param def The def to be aliased. \param type The type of the alias. \param offset Offset of the alias relative to the def. diff --git a/tools/qfcc/source/def.c b/tools/qfcc/source/def.c index 864dc89fe..84a30d15a 100644 --- a/tools/qfcc/source/def.c +++ b/tools/qfcc/source/def.c @@ -159,6 +159,10 @@ alias_def (def_t *def, type_t *type, int offset) internal_error (0, "aliasing a def to a larger type"); if (offset < 0 || offset + type_size (type) > type_size (def->type)) internal_error (0, "invalid alias offset"); + for (alias = def->alias_defs; alias; alias = alias->next) { + if (alias->type == type && alias->offset == offset) + return alias; + } ALLOC (16384, def_t, defs, alias); alias->return_addr = __builtin_return_address (0); alias->offset = offset; @@ -167,6 +171,8 @@ alias_def (def_t *def, type_t *type, int offset) alias->alias = def; alias->line = pr.source_line; alias->file = pr.source_file; + alias->next = def->alias_defs; + def->alias_defs = alias; return alias; } @@ -206,7 +212,20 @@ free_temp_def (def_t *temp) void free_def (def_t *def) { - if (!def->alias && def->space) { + if (!def->return_addr) + internal_error (0, "double free of def"); + if (def->alias) { + def_t **a; + + // pull the alias out of the base def's list of alias defs to avoid + // messing up the list when the alias def is freed. + for (a = &def->alias->alias_defs; *a; a = &(*a)->next) { + if (*a == def) { + *a = def->next; + break; + } + } + } else if (def->space) { def_t **d; for (d = &def->space->defs; *d && *d != def; d = &(*d)->next) @@ -219,6 +238,8 @@ free_def (def_t *def) if (!def->external) defspace_free_loc (def->space, def->offset, type_size (def->type)); } + def->return_addr = 0; + def->free_addr = __builtin_return_address (0); def->next = free_defs; free_defs = def; } diff --git a/tools/qfcc/source/emit.c b/tools/qfcc/source/emit.c index b0cf19de9..c873c7716 100644 --- a/tools/qfcc/source/emit.c +++ b/tools/qfcc/source/emit.c @@ -143,10 +143,8 @@ add_statement_def_ref (def_t *def, dstatement_t *st, int field) alias_depth_expr.line = def->line; } alias_depth++; - def_t *a = def; offset_reloc |= def->offset_reloc; def = def->alias; - free_def (a); } if (alias_depth > 1) { internal_error (&alias_depth_expr, "alias chain detected: %d %s",