From a4a57b6ffda8bae97c49677b1a1a8511bb185d1f Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Sat, 15 Feb 2020 02:21:21 +0900 Subject: [PATCH] Implement aligned allocations --- tools/qfcc/include/defspace.h | 21 ++++++++ tools/qfcc/include/type.h | 1 + tools/qfcc/source/class.c | 37 +++++++------- tools/qfcc/source/def.c | 14 +++++- tools/qfcc/source/defspace.c | 94 ++++++++++++++++++++++++++--------- tools/qfcc/source/function.c | 1 + tools/qfcc/source/struct.c | 6 +++ tools/qfcc/source/type.c | 46 +++++++++++------ 8 files changed, 161 insertions(+), 59 deletions(-) diff --git a/tools/qfcc/include/defspace.h b/tools/qfcc/include/defspace.h index bd4b68920..ef6036286 100644 --- a/tools/qfcc/include/defspace.h +++ b/tools/qfcc/include/defspace.h @@ -109,6 +109,27 @@ defspace_t *defspace_new (ds_type_t type); */ int defspace_alloc_loc (defspace_t *space, int size); +/** Allocate space from the defspace's backing memory. + + If the memory is fragmented, then the first available location at least + as large as \a size plus padding for alignment is returned. This means + that freeing a location then allocating the same amount of space may + return a different location. + + If memory cannot be allocated (there is no free space in the currently + available memory and defspace_t::grow is null), then an internal error + will be generated. + + \param space The space from which to allocate data. + \param size The amount of pr_type_t words to allocated. int and float + need 1 word, vector 3 words, and quaternion 4. + \param alignment The alignment of the allocated space. + \return The offset of the first word of the freshly allocated + space. May be 0 if the allocated space is at the beginning + of the defspace. +*/ +int defspace_alloc_aligned_loc (defspace_t *space, int size, int alignment); + /** Free a block of contiguous words, returning them to the defspace. The block to be freed is specified by \a ofs indicating the offset of the diff --git a/tools/qfcc/include/type.h b/tools/qfcc/include/type.h index d6a43816e..6944db353 100644 --- a/tools/qfcc/include/type.h +++ b/tools/qfcc/include/type.h @@ -63,6 +63,7 @@ typedef enum { typedef struct type_s { etype_t type; ///< ev_invalid means structure/array etc const char *name; + int alignment; ///< required alignment for instances /// function/pointer/array/struct types are more complex ty_meta_e meta; union { diff --git a/tools/qfcc/source/class.c b/tools/qfcc/source/class.c index adeb4d4bf..e0d601aa9 100644 --- a/tools/qfcc/source/class.c +++ b/tools/qfcc/source/class.c @@ -68,28 +68,29 @@ static hashtab_t *category_hash; static hashtab_t *protocol_hash; // these will be built up further -type_t type_obj_selector = { ev_invalid, 0, ty_struct}; -type_t type_SEL = { ev_pointer, "SEL", ty_none, {{&type_obj_selector}}}; -type_t type_IMP = { ev_func, "IMP", ty_none, +type_t type_obj_selector = { ev_invalid, 0, 0, ty_struct}; +type_t type_SEL = { ev_pointer, "SEL", 1, ty_none, {{&type_obj_selector}}}; +type_t type_IMP = { ev_func, "IMP", 1, ty_none, {{&type_id, -3, {&type_id, &type_SEL}}}}; -type_t type_obj_super = { ev_invalid, 0 }; -type_t type_SuperPtr = { ev_pointer, 0, ty_none, {{&type_obj_super}}}; -type_t type_supermsg = { ev_func, ".supermsg", ty_none, +type_t type_obj_super = { ev_invalid, 0, 0 }; +type_t type_SuperPtr = { ev_pointer, 0, 1, ty_none, {{&type_obj_super}}}; +type_t type_supermsg = { ev_func, ".supermsg", 1, ty_none, {{&type_id, -3, {&type_SuperPtr, &type_SEL}}}}; -type_t type_obj_method = { ev_invalid, 0, ty_struct }; -type_t type_obj_method_description = { ev_invalid, 0, ty_struct }; -type_t type_obj_category = { ev_invalid, 0, ty_struct}; -type_t type_obj_ivar = { ev_invalid, 0, ty_struct}; -type_t type_obj_module = { ev_invalid, 0, ty_struct}; -type_t type_moduleptr = { ev_pointer, 0, ty_none, {{&type_obj_module}}}; -type_t type_obj_exec_class = { ev_func, 0, ty_none, +type_t type_obj_method = { ev_invalid, 0, 0, ty_struct }; +type_t type_obj_method_description = { ev_invalid, 0, 0, ty_struct }; +type_t type_obj_category = { ev_invalid, 0, 0, ty_struct}; +type_t type_obj_ivar = { ev_invalid, 0, 0, ty_struct}; +type_t type_obj_module = { ev_invalid, 0, 0, ty_struct}; +type_t type_moduleptr = { ev_pointer, 0, 1, ty_none, + {{&type_obj_module}}}; +type_t type_obj_exec_class = { ev_func, 0, 1, ty_none, {{&type_void, 1, { &type_moduleptr }}}}; -type_t type_obj_object = {ev_invalid, 0, ty_struct}; -type_t type_id = { ev_pointer, "id", ty_none, {{&type_obj_object}}}; -type_t type_obj_class = { ev_invalid, 0, ty_struct}; -type_t type_Class = { ev_pointer, 0, ty_none, {{&type_obj_class}}}; -type_t type_obj_protocol = { ev_invalid, 0, ty_struct}; +type_t type_obj_object = {ev_invalid, 0, 0, ty_struct}; +type_t type_id = { ev_pointer, "id", 1, ty_none, {{&type_obj_object}}}; +type_t type_obj_class = { ev_invalid, 0, 0, ty_struct}; +type_t type_Class = { ev_pointer, 0, 1, ty_none, {{&type_obj_class}}}; +type_t type_obj_protocol = { ev_invalid, 0, 0, ty_struct}; int obj_initialized = 0; diff --git a/tools/qfcc/source/def.c b/tools/qfcc/source/def.c index 5848c20c5..2b946a72c 100644 --- a/tools/qfcc/source/def.c +++ b/tools/qfcc/source/def.c @@ -148,11 +148,17 @@ new_def (const char *name, type_t *type, defspace_t *space, if (storage != sc_extern) { int size = type_size (type); + int alignment = type->alignment; + if (!size) { error (0, "%s has incomplete type", name); size = 1; } - def->offset = defspace_alloc_loc (space, size); + if (alignment < 1) { + print_type (type); + internal_error (0, "temp type has no alignment"); + } + def->offset = defspace_alloc_aligned_loc (space, size, alignment); } return def; @@ -199,16 +205,20 @@ temp_def (type_t *type) def_t *temp; defspace_t *space = current_func->symtab->space; int size = type_size (type); + int alignment = type->alignment; if (size < 1 || size > 4) { internal_error (0, "%d invalid size for temp def", size); } + if (alignment < 1) { + internal_error (0, "temp type has no alignment"); + } if ((temp = current_func->temp_defs[size - 1])) { current_func->temp_defs[size - 1] = temp->temp_next; temp->temp_next = 0; } else { ALLOC (16384, def_t, defs, temp); - temp->offset = defspace_alloc_loc (space, size); + temp->offset = defspace_alloc_aligned_loc (space, size, alignment); *space->def_tail = temp; space->def_tail = &temp->next; temp->name = save_string (va (".tmp%d", current_func->temp_num++)); diff --git a/tools/qfcc/source/defspace.c b/tools/qfcc/source/defspace.c index f227209d3..f2c9f7011 100644 --- a/tools/qfcc/source/defspace.c +++ b/tools/qfcc/source/defspace.c @@ -63,6 +63,33 @@ typedef struct locref_s { 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 @@ -98,9 +125,8 @@ grow_space_virtual (defspace_t *space) defspace_t * defspace_new (ds_type_t type) { - defspace_t *space; + defspace_t *space = new_defspace (); - ALLOC (1024, defspace_t, spaces, space); space->def_tail = &space->defs; space->type = type; if (type == ds_backed) { @@ -116,33 +142,59 @@ defspace_new (ds_type_t type) int defspace_alloc_loc (defspace_t *space, int size) { - int ofs; + 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); - while (*l && (*l)->size < size) - l = &(*l)->next; - if ((loc = *l)) { - ofs = (*l)->ofs; - if ((*l)->size == size) { - loc = *l; - *l = (*l)->next; - FREE (locrefs, loc); - } else { - (*l)->ofs += size; - (*l)->size -= 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; } - return ofs; + // 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; - space->size += 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); } - return ofs; + if (pad) { + *l = new_locref (ofs, pad, 0); + } + return ofs + pad; } void @@ -184,17 +236,13 @@ defspace_free_loc (defspace_t *space, int ofs, int size) loc->size += loc->next->size; loc = loc->next; *l = loc->next; - FREE (locrefs, loc); + del_locref (loc); } return; } } // insert a new free block for the location to be freed - ALLOC (1024, locref_t, locrefs, loc); - loc->ofs = ofs; - loc->size = size; - loc->next = *l; - *l = loc; + *l = new_locref (ofs, size, *l); } int diff --git a/tools/qfcc/source/function.c b/tools/qfcc/source/function.c index bd37d6d05..b150e4baf 100644 --- a/tools/qfcc/source/function.c +++ b/tools/qfcc/source/function.c @@ -158,6 +158,7 @@ parse_params (type_t *type, param_t *parms) new = new_type (); new->type = ev_func; + new->alignment = 1; new->t.func.type = type; new->t.func.num_params = 0; diff --git a/tools/qfcc/source/struct.c b/tools/qfcc/source/struct.c index d3f014111..358b337f0 100644 --- a/tools/qfcc/source/struct.c +++ b/tools/qfcc/source/struct.c @@ -112,6 +112,7 @@ build_struct (int su, symbol_t *tag, symtab_t *symtab, type_t *type) { symbol_t *sym = find_struct (su, tag, type); symbol_t *s; + int alignment = 1; symtab->parent = 0; // disconnect struct's symtab from parent scope @@ -130,10 +131,14 @@ build_struct (int su, symbol_t *tag, symtab_t *symtab, type_t *type) if (size > symtab->size) symtab->size = size; } + if (s->type->alignment > alignment) { + alignment = s->type->alignment; + } } if (!type) sym->type = find_type (sym->type); // checks the tag, not the symtab sym->type->t.symtab = symtab; + 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); return sym; @@ -166,6 +171,7 @@ finish_enum (symbol_t *sym) enum_type = sym->type = find_type (sym->type); enum_tab = enum_type->t.symtab; + enum_type->alignment = 1; for (name = enum_tab->symbols; name; name = name->next) { name->type = sym->type; diff --git a/tools/qfcc/source/type.c b/tools/qfcc/source/type.c index 81bd21ef9..d326a5f33 100644 --- a/tools/qfcc/source/type.c +++ b/tools/qfcc/source/type.c @@ -63,20 +63,20 @@ // simple types. function types are dynamically allocated type_t type_invalid = { ev_invalid, "invalid" }; type_t type_void = { ev_void, "void" }; -type_t type_string = { ev_string, "string" }; -type_t type_float = { ev_float, "float" }; -type_t type_vector = { ev_vector, "vector" }; -type_t type_entity = { ev_entity, "entity" }; -type_t type_field = {ev_field, "field", ty_none, {{&type_void}} }; +type_t type_string = { ev_string, "string", 1 }; +type_t type_float = { ev_float, "float", 1 }; +type_t type_vector = { ev_vector, "vector", 1 }; +type_t type_entity = { ev_entity, "entity", 1 }; +type_t type_field = {ev_field, "field", 1, ty_none, {{&type_void}} }; // type_function is a void() function used for state defs -type_t type_function = { ev_func, "function", ty_none, {{&type_void}} }; -type_t type_pointer = { ev_pointer, "pointer", ty_none, {{&type_void}} }; -type_t type_quaternion = { ev_quat, "quaternion" }; -type_t type_integer = { ev_integer, "int" }; -type_t type_uinteger = { ev_uinteger, "uint" }; -type_t type_short = { ev_short, "short" }; -type_t type_double = { ev_double, "double" }; +type_t type_function = { ev_func, "function", 1, ty_none, {{&type_void}} }; +type_t type_pointer = { ev_pointer, "pointer", 1, ty_none, {{&type_void}} }; +type_t type_quaternion = { ev_quat, "quaternion", 1 }; +type_t type_integer = { ev_integer, "int", 1 }; +type_t type_uinteger = { ev_uinteger, "uint", 1 }; +type_t type_short = { ev_short, "short", 1 }; +type_t type_double = { ev_double, "double", 2 }; type_t *type_nil; type_t *type_default; @@ -85,9 +85,11 @@ type_t *type_default; type_t type_va_list = { ev_invalid, 0, ty_struct }; type_t type_param = { ev_invalid, 0, ty_struct }; type_t type_zero = { ev_invalid, 0, ty_struct }; -type_t type_type_encodings = { ev_invalid, "@type_encodings", ty_struct }; +type_t type_type_encodings = { ev_invalid, "@type_encodings", 0, + ty_struct }; -type_t type_floatfield = { ev_field, ".float", ty_none, {{&type_float}} }; +type_t type_floatfield = { ev_field, ".float", 1, ty_none, + {{&type_float}} }; type_t *ev_types[ev_type_count] = { &type_void, @@ -222,15 +224,19 @@ append_type (type_t *type, type_t *new) case ev_field: case ev_pointer: t = &(*t)->t.fldptr.type; + type->alignment = 1; break; case ev_func: t = &(*t)->t.func.type; + type->alignment = 1; break; case ev_invalid: - if ((*t)->meta == ty_array) + if ((*t)->meta == ty_array) { t = &(*t)->t.array.type; - else + type->alignment = new->alignment; + } else { internal_error (0, "append to object type"); + } break; } } @@ -364,6 +370,7 @@ field_type (type_t *aux) else new = new_type (); new->type = ev_field; + new->alignment = 1; new->t.fldptr.type = aux; if (aux) new = find_type (new); @@ -381,6 +388,7 @@ pointer_type (type_t *aux) else new = new_type (); new->type = ev_pointer; + new->alignment = 1; new->t.fldptr.type = aux; if (aux) new = find_type (new); @@ -398,6 +406,9 @@ array_type (type_t *aux, int size) else new = new_type (); new->type = ev_invalid; + if (aux) { + new->alignment = aux->alignment; + } new->meta = ty_array; new->t.array.type = aux; new->t.array.size = size; @@ -417,6 +428,9 @@ based_array_type (type_t *aux, int base, int top) else new = new_type (); new->type = ev_invalid; + if (aux) { + new->alignment = aux->alignment; + } new->meta = ty_array; new->t.array.type = aux; new->t.array.base = base;