/* defspace.c management of data segments Copyright (C) 2011 Bill Currie Author: Bill Currie Date: 2011/01/16 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 "QF/alloc.h" #include "QF/hash.h" #include "QF/sys.h" #include "QF/va.h" #include "qfcc.h" #include "defspace.h" #include "diagnostic.h" #include "expr.h" #include "options.h" #include "reloc.h" #include "strpool.h" #include "struct.h" #include "type.h" typedef struct locref_s { struct locref_s *next; int ofs; int size; } locref_t; static defspace_t *spaces_freelist; static locref_t *locrefs_freelist; static locref_t * new_locref (int ofs, int size, locref_t *next) { locref_t *loc; ALLOC (1024, locref_t, locrefs, loc); loc->ofs = ofs; loc->size = size; loc->next = next; return loc; } static void del_locref (locref_t *loc) { FREE (locrefs, loc); } static defspace_t * new_defspace (void) { defspace_t *space; ALLOC (1024, defspace_t, spaces, space); return space; } #define GROW 1024 static int grow_space_global (defspace_t *space) { int size; if (space->size <= space->max_size) return 1; size = space->size + GROW; size -= size % GROW; space->data = realloc (space->data, size * sizeof (pr_type_t)); memset (space->data + space->max_size, 0, GROW * sizeof (pr_type_t)); space->max_size = size; return 1; } static int grow_space_virtual (defspace_t *space) { int size; if (space->size <= space->max_size) return 1; size = space->size + GROW; size -= size % GROW; space->max_size = size; return 1; } defspace_t * defspace_new (ds_type_t type) { defspace_t *space = new_defspace (); space->def_tail = &space->defs; space->type = type; if (type == ds_backed) { space->grow = grow_space_global; } else if (type == ds_virtual) { space->grow = grow_space_virtual; } else { internal_error (0, "unknown defspace type"); } return space; } int defspace_alloc_loc (defspace_t *space, int size) { return defspace_alloc_aligned_loc (space, size, 1); } int defspace_alloc_aligned_loc (defspace_t *space, int size, int alignment) { int ofs, pad; locref_t *loc; locref_t **l = &space->free_locs; if (size <= 0) internal_error (0, "invalid number of words requested: %d", size); if (alignment <= 0) internal_error (0, "invalid alignment requested: %d", alignment); while ((loc = *l)) { ofs = loc->ofs; pad = alignment * ((ofs + alignment - 1) / alignment) - ofs; // exact fit, so just shrink the block or remove it if there is no // padding (any padding remains free) if (size + pad == loc->size) { if (!pad) { *l = loc->next; del_locref (loc); } return ofs + pad; } // there's excess space in the block. If there's no padding, then // just shrink it, otherwise split it into two, one on either side // of the allocated block, such that the padding remains free if (size + pad < loc->size) { if (!pad) { loc->ofs += size; loc->size -= size; } else { loc->next = new_locref (ofs + pad + size, loc->size - ofs - pad, loc->next); loc->size = pad; } return ofs + pad; } l = &(*l)->next; } ofs = space->size; pad = alignment * ((ofs + alignment - 1) / alignment) - ofs; space->size += size + pad; if (space->size > space->max_size) { if (!space->grow || !space->grow (space)) internal_error (0, "unable to allocate %d words", size); } if (pad) { *l = new_locref (ofs, pad, 0); } return ofs + pad; } void defspace_free_loc (defspace_t *space, int ofs, int size) { locref_t **l; locref_t *loc; if (size <= 0) internal_error (0, "defspace: freeing size %d location", size); if (ofs < 0 || ofs >= space->size || ofs + size > space->size) internal_error (0, "defspace: freeing bogus location %d:%d", ofs, size); for (l = &space->free_locs; *l; l = &(*l)->next) { loc = *l; // location to be freed is below the free block // need to create a new free block if (ofs + size < loc->ofs) break; // location to be freed is immediately below the free block // merge with the free block if (ofs + size == loc->ofs) { loc->size += size; loc->ofs = ofs; return; } // location to be freed overlaps the free block // this is an error if (ofs + size > loc->ofs && ofs < loc->ofs + loc->size) internal_error (0, "defspace: freeing bogus location %d:%d", ofs, size); // location to be freed is immediately above the free block // merge with the free block if (ofs == loc->ofs + loc->size) { loc->size += size; // location to be freed is immediately below the next free block // merge with the next block too, and unlink that block if (loc->next && loc->next->ofs == loc->ofs + loc->size) { loc->size += loc->next->size; loc = loc->next; *l = loc->next; del_locref (loc); } return; } } // insert a new free block for the location to be freed *l = new_locref (ofs, size, *l); } int defspace_add_data (defspace_t *space, pr_type_t *data, int size) { int loc; loc = defspace_alloc_loc (space, size); if (data) memcpy (space->data + loc, data, size * sizeof (pr_type_t)); return loc; }