1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
|
//------------------------------------------------------------------------------
// GB_werk.h: definitions for werkspace management on the Werk stack
//------------------------------------------------------------------------------
// SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2022, All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//------------------------------------------------------------------------------
#ifndef GB_WERK_H
#define GB_WERK_H
//------------------------------------------------------------------------------
// GB_werk_push/pop: manage werkspace in the Context->Werk stack
//------------------------------------------------------------------------------
// Context->Werk is a small fixed-size array that is allocated on the stack
// of any user-callable GraphBLAS function. It is used for small werkspace
// allocations.
// GB_ROUND8(s) rounds up s to a multiple of 8
#define GB_ROUND8(s) (((s) + 7) & (~0x7))
//------------------------------------------------------------------------------
// GB_werk_push: allocate werkspace from the Werk stack or malloc
//------------------------------------------------------------------------------
// The werkspace is allocated from the Werk static if it small enough and space
// is available. Otherwise it is allocated by malloc.
static inline void *GB_werk_push // return pointer to newly allocated space
(
// output
size_t *size_allocated, // # of bytes actually allocated
bool *on_stack, // true if werkspace is from Werk stack
// input
size_t nitems, // # of items to allocate
size_t size_of_item, // size of each item
GB_Context Context
)
{
//--------------------------------------------------------------------------
// check inputs
//--------------------------------------------------------------------------
ASSERT (on_stack != NULL) ;
ASSERT (size_allocated != NULL) ;
//--------------------------------------------------------------------------
// determine where to allocate the werkspace
//--------------------------------------------------------------------------
size_t size ;
if (Context == NULL || nitems > GB_WERK_SIZE || size_of_item > GB_WERK_SIZE
#ifdef GBCOVER
// Werk stack can be disabled for test coverage
|| (GB_Global_hack_get (1) != 0)
#endif
)
{
// no context, or werkspace is too large to allocate from the Werk stack
(*on_stack) = false ;
}
else
{
// try to allocate from the Werk stack
size = GB_ROUND8 (nitems * size_of_item) ;
ASSERT (size % 8 == 0) ; // size is rounded up to a multiple of 8
size_t freespace = GB_WERK_SIZE - Context->pwerk ;
ASSERT (freespace % 8 == 0) ; // thus freespace is also multiple of 8
(*on_stack) = (size <= freespace) ;
}
//--------------------------------------------------------------------------
// allocate the werkspace
//--------------------------------------------------------------------------
if (*on_stack)
{
// allocate the werkspace from the Werk stack
GB_void *p = Context->Werk + Context->pwerk ;
Context->pwerk += (int) size ;
(*size_allocated) = size ;
return ((void *) p) ;
}
else
{
// allocate the werkspace from malloc
return (GB_malloc_memory (nitems, size_of_item, size_allocated)) ;
}
}
//------------------------------------------------------------------------------
// GB_WERK helper macros
//------------------------------------------------------------------------------
// declare a werkspace X of a given type
#define GB_WERK_DECLARE(X,type) \
type *restrict X = NULL ; \
bool X ## _on_stack = false ; \
size_t X ## _nitems = 0, X ## _size_allocated = 0 ;
// push werkspace X
#define GB_WERK_PUSH(X,nitems,type) \
X ## _nitems = (nitems) ; \
X = (type *) GB_werk_push (&(X ## _size_allocated), &(X ## _on_stack), \
X ## _nitems, sizeof (type), Context) ;
// pop werkspace X
#define GB_WERK_POP(X,type) \
X = (type *) GB_werk_pop (X, &(X ## _size_allocated), X ## _on_stack, \
X ## _nitems, sizeof (type), Context) ;
//------------------------------------------------------------------------------
// GB_werk_pop: free werkspace from the Werk stack
//------------------------------------------------------------------------------
// If the werkspace was allocated from the Werk stack, it must be at the top of
// the stack to free it properly. Freeing a werkspace in the middle of the
// Werk stack also frees everything above it. This is not a problem if that
// space is also being freed, but the assertion below ensures that the freeing
// werkspace from the Werk stack is done in LIFO order, like a stack.
static inline void *GB_werk_pop // free the top block of werkspace memory
(
// input/output
void *p, // werkspace to free
size_t *size_allocated, // # of bytes actually allocated for p
// input
bool on_stack, // true if werkspace is from Werk stack
size_t nitems, // # of items to allocate
size_t size_of_item, // size of each item
GB_Context Context
)
{
ASSERT (size_allocated != NULL) ;
if (p == NULL)
{
// nothing to do
}
else if (on_stack)
{
// werkspace was allocated from the Werk stack
ASSERT ((*size_allocated) == GB_ROUND8 (nitems * size_of_item)) ;
ASSERT (Context != NULL) ;
ASSERT ((*size_allocated) % 8 == 0) ;
ASSERT (((GB_void *) p) + (*size_allocated) ==
Context->Werk + Context->pwerk) ;
Context->pwerk = ((GB_void *) p) - Context->Werk ;
(*size_allocated) = 0 ;
}
else
{
// werkspace was allocated from malloc
GB_dealloc_memory (&p, *size_allocated) ;
}
return (NULL) ; // return NULL to indicate p was freed
}
#endif
|