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