[qfcc] Make virtual defspaces useful for highwater allocation

This seems to be the most reasonable approach to allocating space for
function call parameters without using push and pop (or adding to the
stack pointer), though it's probably good even when using push and pop
to help keep things aligned.
This commit is contained in:
Bill Currie 2022-01-20 20:54:12 +09:00
parent 2b7a8387e7
commit 17dfd1492f
3 changed files with 110 additions and 10 deletions

View file

@ -43,7 +43,8 @@ typedef enum {
ds_backed, ///< data space is globally addressable (near/far/type) and
///< has backing store
ds_virtual, ///< data space has no backing store (local vars, entity
///< fields)
///< fields) defspace_t::max_size reflects the highest
///< address allocated
} ds_type_t;
/** Represent a block of memory in the progs data space.
@ -56,7 +57,8 @@ typedef struct defspace_s {
struct def_s **def_tail; ///< for appending to \a defs
pr_type_t *data; ///< backing memory for this space
int size; ///< current high-water mark for alloced data
int max_size; ///< size of backing memory
int max_size; ///< size of backing memory, or highwater mark
///< for ds_virtual
/** Grow the backing memory of the defspace.
This function is called when more memory is needed for the space.
@ -168,6 +170,57 @@ void defspace_free_loc (defspace_t *space, int ofs, int size);
*/
int defspace_add_data (defspace_t *space, pr_type_t *data, int size);
/** Allocate a block of data from the end of the defspace.
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.
\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_highwater (defspace_t *space, int size);
/** Allocate an aligned block of data from the end of the defspace.
Any unallocated holes in the defspace are ignored, even if the hole is
at the end of the defspace. However, any holes created by the padding
required for aligning the block will be available to defspace_alloc_loc()
and defspace_alloc_aligned_loc().
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_highwater (defspace_t *space, int size,
int alignment);
/** Reset a defspace, freeing all allocated memory.
defspace_t::max_size is not affected. This allows the defspace to be used
to find the larged block of memory required for a set of operations (eg,
the largest parameter block required in a function for all its calls
allowing the stack to remain constant instead of using many push/pop
operations. Note that this works best with ds_virtual defspaces.
If the defspace has backing memory (ds_backed), the memory is not freed,
but it is zeroed so any new allocations will contain zeroed memory.
\param space The space to be reset.
*/
void defspace_reset (defspace_t *space);
///@}
#endif//__defspace_h

View file

@ -105,14 +105,9 @@ grow_space_global (defspace_t *space)
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;
if (space->size > space->max_size) {
space->max_size = space->size;
}
return 1;
}
@ -208,6 +203,7 @@ defspace_alloc_aligned_loc (defspace_t *space, int size, int alignment)
internal_error (0, "unable to allocate %d words", size);
}
if (pad) {
// mark the padding as free
*l = new_locref (ofs, pad, 0);
}
return ofs + pad;
@ -271,3 +267,46 @@ defspace_add_data (defspace_t *space, pr_type_t *data, int size)
memcpy (space->data + loc, data, size * sizeof (pr_type_t));
return loc;
}
int
defspace_alloc_highwater (defspace_t *space, int size)
{
return defspace_alloc_aligned_highwater (space, size, 1);
}
int
defspace_alloc_aligned_highwater (defspace_t *space, int size, int alignment)
{
if (size <= 0)
internal_error (0, "invalid number of words requested: %d", size);
if (alignment <= 0)
internal_error (0, "invalid alignment requested: %d", alignment);
int ofs = space->size;
int 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);
}
locref_t **l = &space->free_locs;
if (pad) {
// mark the padding as free
*l = new_locref (ofs, pad, 0);
}
return ofs + pad;
}
void
defspace_reset (defspace_t *space)
{
space->size = 0;
while (space->free_locs) {
locref_t *l = space->free_locs;
space->free_locs = l->next;
del_locref (l);
}
if (space->data) {
memset (space->data, 0, space->max_size * sizeof (pr_type_t));
}
}

View file

@ -1136,6 +1136,14 @@ statement_return (sblock_t *sblock, expr_t *e)
}
} else {
if (e->e.retrn.ret_val) {
expr_t *ret_val = e->e.retrn.ret_val;
type_t *ret_type = get_type (ret_val);
if (is_indirect (ret_val)) {
} else {
sblock = statement_subexpr (sblock, ret_val, &s->opa);
s->opb = short_operand (0, e);
s->opc = short_operand (type_size (ret_type) - 1, e);
}
} else {
s->opa = short_operand (0, e);
s->opb = short_operand (0, e);