xref: /libCEED/backends/memcheck/ceed-memcheck-vector.c (revision d6c19ee8504c74d8f30ec67127f069a58291b3ac)
1 // Copyright (c) 2017-2025, Lawrence Livermore National Security, LLC and other CEED contributors.
2 // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3 //
4 // SPDX-License-Identifier: BSD-2-Clause
5 //
6 // This file is part of CEED:  http://github.com/ceed
7 
8 #include <ceed.h>
9 #include <ceed/backend.h>
10 #include <assert.h>
11 #include <math.h>
12 #include <stdbool.h>
13 #include <string.h>
14 #include <valgrind/memcheck.h>
15 
16 #include "ceed-memcheck.h"
17 
18 //------------------------------------------------------------------------------
19 // Has Valid Array
20 //------------------------------------------------------------------------------
21 static int CeedVectorHasValidArray_Memcheck(CeedVector vec, bool *has_valid_array) {
22   CeedVector_Memcheck *impl;
23 
24   CeedCallBackend(CeedVectorGetData(vec, &impl));
25   *has_valid_array = !!impl->array_allocated;
26   return CEED_ERROR_SUCCESS;
27 }
28 
29 //------------------------------------------------------------------------------
30 // Check if has borrowed array of given type
31 //------------------------------------------------------------------------------
32 static inline int CeedVectorHasBorrowedArrayOfType_Memcheck(const CeedVector vec, CeedMemType mem_type, bool *has_borrowed_array_of_type) {
33   CeedVector_Memcheck *impl;
34 
35   CeedCheck(mem_type == CEED_MEM_HOST, CeedVectorReturnCeed(vec), CEED_ERROR_BACKEND, "Can only set HOST memory for this backend");
36 
37   CeedCallBackend(CeedVectorGetData(vec, &impl));
38   *has_borrowed_array_of_type = !!impl->array_borrowed;
39   return CEED_ERROR_SUCCESS;
40 }
41 
42 //------------------------------------------------------------------------------
43 // Vector Set Array
44 //------------------------------------------------------------------------------
45 static int CeedVectorSetArray_Memcheck(CeedVector vec, CeedMemType mem_type, CeedCopyMode copy_mode, CeedScalar *array) {
46   CeedSize             length;
47   CeedVector_Memcheck *impl;
48 
49   CeedCheck(mem_type == CEED_MEM_HOST, CeedVectorReturnCeed(vec), CEED_ERROR_BACKEND, "Can only set HOST memory for this backend");
50 
51   CeedCallBackend(CeedVectorGetData(vec, &impl));
52   CeedCallBackend(CeedVectorGetLength(vec, &length));
53 
54   // Clear previous owned arrays
55   if (impl->array_allocated) {
56     for (CeedSize i = 0; i < length; i++) impl->array_allocated[i] = NAN;
57     VALGRIND_DISCARD(impl->allocated_block_id);
58   }
59   CeedCallBackend(CeedFree(&impl->array_allocated));
60   if (copy_mode != CEED_COPY_VALUES) {
61     if (impl->array_owned) {
62       for (CeedSize i = 0; i < length; i++) impl->array_owned[i] = NAN;
63       VALGRIND_DISCARD(impl->owned_block_id);
64     }
65     CeedCallBackend(CeedFree(&impl->array_owned));
66   }
67 
68   // Clear borrowed block id, if present
69   if (impl->array_borrowed) VALGRIND_DISCARD(impl->borrowed_block_id);
70 
71   // Set internal pointers to external arrays
72   switch (copy_mode) {
73     case CEED_COPY_VALUES:
74       // Nothing to update
75       break;
76     case CEED_OWN_POINTER:
77       impl->array_owned    = array;
78       impl->array_borrowed = NULL;
79       impl->owned_block_id = VALGRIND_CREATE_BLOCK(impl->array_owned, length * sizeof(CeedScalar), "Owned external array buffer");
80       break;
81     case CEED_USE_POINTER:
82       impl->array_owned       = NULL;
83       impl->array_borrowed    = array;
84       impl->borrowed_block_id = VALGRIND_CREATE_BLOCK(impl->array_borrowed, length * sizeof(CeedScalar), "Borrowed external array buffer");
85       break;
86   }
87 
88   // Create internal array data buffer
89   CeedCallBackend(CeedCalloc(length, &impl->array_allocated));
90   impl->allocated_block_id = VALGRIND_CREATE_BLOCK(impl->array_allocated, length * sizeof(CeedScalar), "Allocated internal array buffer");
91   if (array) {
92     memcpy(impl->array_allocated, array, length * sizeof(CeedScalar));
93   } else {
94     for (CeedInt i = 0; i < length; i++) impl->array_allocated[i] = NAN;
95   }
96   return CEED_ERROR_SUCCESS;
97 }
98 
99 //------------------------------------------------------------------------------
100 // Set internal array to value
101 //------------------------------------------------------------------------------
102 static int CeedVectorSetValue_Memcheck(CeedVector vec, CeedScalar value) {
103   CeedSize             length;
104   CeedVector_Memcheck *impl;
105 
106   CeedCallBackend(CeedVectorGetData(vec, &impl));
107   CeedCallBackend(CeedVectorGetLength(vec, &length));
108 
109   if (!impl->array_allocated) CeedCallBackend(CeedVectorSetArray_Memcheck(vec, CEED_MEM_HOST, CEED_COPY_VALUES, NULL));
110   assert(impl->array_allocated);
111   for (CeedSize i = 0; i < length; i++) impl->array_allocated[i] = value;
112   return CEED_ERROR_SUCCESS;
113 }
114 
115 //------------------------------------------------------------------------------
116 // Set internal array to value strided
117 //------------------------------------------------------------------------------
118 static int CeedVectorSetValueStrided_Memcheck(CeedVector vec, CeedSize start, CeedSize stop, CeedSize step, CeedScalar val) {
119   CeedSize             length;
120   CeedVector_Memcheck *impl;
121 
122   CeedCallBackend(CeedVectorGetData(vec, &impl));
123   CeedCallBackend(CeedVectorGetLength(vec, &length));
124 
125   if (!impl->array_allocated) CeedCallBackend(CeedVectorSetArray_Memcheck(vec, CEED_MEM_HOST, CEED_COPY_VALUES, NULL));
126   assert(impl->array_allocated);
127   if (stop == -1) stop = length;
128   for (CeedSize i = start; i < stop; i += step) impl->array_allocated[i] = val;
129   return CEED_ERROR_SUCCESS;
130 }
131 
132 //------------------------------------------------------------------------------
133 // Sync arrays
134 //------------------------------------------------------------------------------
135 static int CeedVectorSyncArray_Memcheck(const CeedVector vec, CeedMemType mem_type) {
136   CeedSize             length;
137   CeedVector_Memcheck *impl;
138 
139   CeedCheck(mem_type == CEED_MEM_HOST, CeedVectorReturnCeed(vec), CEED_ERROR_BACKEND, "Can only provide HOST memory for this backend");
140 
141   CeedCallBackend(CeedVectorGetData(vec, &impl));
142   CeedCallBackend(CeedVectorGetLength(vec, &length));
143 
144   // Copy internal buffer back to owned or borrowed array
145   if (impl->array_owned) {
146     memcpy(impl->array_owned, impl->array_allocated, length * sizeof(CeedScalar));
147   }
148   if (impl->array_borrowed) {
149     memcpy(impl->array_borrowed, impl->array_allocated, length * sizeof(CeedScalar));
150   }
151   return CEED_ERROR_SUCCESS;
152 }
153 
154 //------------------------------------------------------------------------------
155 // Vector Take Array
156 //------------------------------------------------------------------------------
157 static int CeedVectorTakeArray_Memcheck(CeedVector vec, CeedMemType mem_type, CeedScalar **array) {
158   CeedSize             length;
159   CeedVector_Memcheck *impl;
160 
161   CeedCheck(mem_type == CEED_MEM_HOST, CeedVectorReturnCeed(vec), CEED_ERROR_BACKEND, "Can only provide HOST memory for this backend");
162 
163   CeedCallBackend(CeedVectorGetData(vec, &impl));
164   CeedCallBackend(CeedVectorGetLength(vec, &length));
165 
166   // Synchronize memory
167   CeedCallBackend(CeedVectorSyncArray_Memcheck(vec, CEED_MEM_HOST));
168 
169   // Return borrowed array
170   (*array)             = impl->array_borrowed;
171   impl->array_borrowed = NULL;
172   VALGRIND_DISCARD(impl->borrowed_block_id);
173 
174   // De-allocate internal memory
175   if (impl->array_allocated) {
176     for (CeedSize i = 0; i < length; i++) impl->array_allocated[i] = NAN;
177     VALGRIND_DISCARD(impl->allocated_block_id);
178   }
179   CeedCallBackend(CeedFree(&impl->array_allocated));
180   return CEED_ERROR_SUCCESS;
181 }
182 
183 //------------------------------------------------------------------------------
184 // Vector Get Array
185 //------------------------------------------------------------------------------
186 static int CeedVectorGetArray_Memcheck(CeedVector vec, CeedMemType mem_type, CeedScalar **array) {
187   CeedSize             length;
188   CeedVector_Memcheck *impl;
189 
190   CeedCheck(mem_type == CEED_MEM_HOST, CeedVectorReturnCeed(vec), CEED_ERROR_BACKEND, "Can only provide HOST memory for this backend");
191 
192   CeedCallBackend(CeedVectorGetData(vec, &impl));
193   CeedCallBackend(CeedVectorGetLength(vec, &length));
194 
195   // Create and return writable buffer
196   CeedCallBackend(CeedCalloc(length, &impl->array_writable_copy));
197   impl->writable_block_id = VALGRIND_CREATE_BLOCK(impl->array_writable_copy, length * sizeof(CeedScalar), "Allocated writeable array buffer copy");
198   memcpy(impl->array_writable_copy, impl->array_allocated, length * sizeof(CeedScalar));
199   *array = impl->array_writable_copy;
200   return CEED_ERROR_SUCCESS;
201 }
202 
203 //------------------------------------------------------------------------------
204 // Vector Get Array Read
205 //------------------------------------------------------------------------------
206 static int CeedVectorGetArrayRead_Memcheck(CeedVector vec, CeedMemType mem_type, const CeedScalar **array) {
207   CeedSize             length;
208   CeedVector_Memcheck *impl;
209 
210   CeedCheck(mem_type == CEED_MEM_HOST, CeedVectorReturnCeed(vec), CEED_ERROR_BACKEND, "Can only provide HOST memory for this backend");
211 
212   CeedCallBackend(CeedVectorGetData(vec, &impl));
213   CeedCallBackend(CeedVectorGetLength(vec, &length));
214 
215   // Create and return read-only buffer
216   if (!impl->array_read_only_copy) {
217     CeedCallBackend(CeedCalloc(length, &impl->array_read_only_copy));
218     impl->writable_block_id = VALGRIND_CREATE_BLOCK(impl->array_read_only_copy, length * sizeof(CeedScalar), "Allocated read-only array buffer copy");
219     memcpy(impl->array_read_only_copy, impl->array_allocated, length * sizeof(CeedScalar));
220   }
221   *array = impl->array_read_only_copy;
222   return CEED_ERROR_SUCCESS;
223 }
224 
225 //------------------------------------------------------------------------------
226 // Vector Get Array Write
227 //------------------------------------------------------------------------------
228 static int CeedVectorGetArrayWrite_Memcheck(CeedVector vec, CeedMemType mem_type, CeedScalar **array) {
229   CeedSize             length;
230   CeedVector_Memcheck *impl;
231 
232   CeedCheck(mem_type == CEED_MEM_HOST, CeedVectorReturnCeed(vec), CEED_ERROR_BACKEND, "Can only provide HOST memory for this backend");
233 
234   CeedCallBackend(CeedVectorGetData(vec, &impl));
235   CeedCallBackend(CeedVectorGetLength(vec, &length));
236 
237   // Allocate buffer if necessary
238   if (!impl->array_allocated) CeedCallBackend(CeedVectorSetArray_Memcheck(vec, mem_type, CEED_COPY_VALUES, NULL));
239 
240   // Get writable buffer
241   CeedCallBackend(CeedVectorGetArray_Memcheck(vec, mem_type, array));
242 
243   // Invalidate array data to prevent accidental reads
244   for (CeedSize i = 0; i < length; i++) (*array)[i] = NAN;
245   impl->is_write_only_access = true;
246   return CEED_ERROR_SUCCESS;
247 }
248 
249 //------------------------------------------------------------------------------
250 // Vector Restore Array
251 //------------------------------------------------------------------------------
252 static int CeedVectorRestoreArray_Memcheck(CeedVector vec) {
253   CeedSize             length;
254   CeedVector_Memcheck *impl;
255 
256   CeedCallBackend(CeedVectorGetData(vec, &impl));
257   CeedCallBackend(CeedVectorGetLength(vec, &length));
258 
259   // Check for unset entries after write-only access
260   if (impl->is_write_only_access) {
261     for (CeedSize i = 0; i < length; i++) {
262       if (isnan(impl->array_writable_copy[i])) {
263         CeedDebug256(CeedVectorReturnCeed(vec), CEED_DEBUG_COLOR_WARNING,
264                      "WARNING: Vec entry %" CeedSize_FMT " is NaN after restoring write-only access", i);
265       }
266     }
267     impl->is_write_only_access = false;
268   }
269 
270   // Copy back to internal buffer and sync
271   memcpy(impl->array_allocated, impl->array_writable_copy, length * sizeof(CeedScalar));
272   CeedCallBackend(CeedVectorSyncArray_Memcheck(vec, CEED_MEM_HOST));
273 
274   // Invalidate writable buffer
275   for (CeedSize i = 0; i < length; i++) impl->array_writable_copy[i] = NAN;
276   CeedCallBackend(CeedFree(&impl->array_writable_copy));
277   VALGRIND_DISCARD(impl->writable_block_id);
278   return CEED_ERROR_SUCCESS;
279 }
280 
281 //------------------------------------------------------------------------------
282 // Vector Restore Array Read-Only
283 //------------------------------------------------------------------------------
284 static int CeedVectorRestoreArrayRead_Memcheck(CeedVector vec) {
285   CeedSize             length;
286   CeedVector_Memcheck *impl;
287 
288   CeedCallBackend(CeedVectorGetData(vec, &impl));
289   CeedCallBackend(CeedVectorGetLength(vec, &length));
290 
291   // Verify no changes made during read-only access
292   bool is_changed = memcmp(impl->array_allocated, impl->array_read_only_copy, length * sizeof(CeedScalar));
293 
294   CeedCheck(!is_changed, CeedVectorReturnCeed(vec), CEED_ERROR_BACKEND, "Array data changed while accessed in read-only mode");
295 
296   // Invalidate read-only buffer
297   for (CeedSize i = 0; i < length; i++) impl->array_read_only_copy[i] = NAN;
298   CeedCallBackend(CeedFree(&impl->array_read_only_copy));
299   VALGRIND_DISCARD(impl->read_only_block_id);
300   return CEED_ERROR_SUCCESS;
301 }
302 
303 //------------------------------------------------------------------------------
304 // Take reciprocal of a vector
305 //------------------------------------------------------------------------------
306 static int CeedVectorReciprocal_Memcheck(CeedVector vec) {
307   CeedSize             length;
308   CeedVector_Memcheck *impl;
309 
310   CeedCallBackend(CeedVectorGetData(vec, &impl));
311   CeedCallBackend(CeedVectorGetLength(vec, &length));
312 
313   for (CeedSize i = 0; i < length; i++) {
314     if (fabs(impl->array_allocated[i]) > CEED_EPSILON) impl->array_allocated[i] = 1. / impl->array_allocated[i];
315   }
316   return CEED_ERROR_SUCCESS;
317 }
318 
319 //------------------------------------------------------------------------------
320 // Compute x = alpha x
321 //------------------------------------------------------------------------------
322 static int CeedVectorScale_Memcheck(CeedVector x, CeedScalar alpha) {
323   CeedSize             length;
324   CeedVector_Memcheck *impl;
325 
326   CeedCallBackend(CeedVectorGetData(x, &impl));
327   CeedCallBackend(CeedVectorGetLength(x, &length));
328 
329   for (CeedSize i = 0; i < length; i++) impl->array_allocated[i] *= alpha;
330   return CEED_ERROR_SUCCESS;
331 }
332 
333 //------------------------------------------------------------------------------
334 // Compute y = alpha x + y
335 //------------------------------------------------------------------------------
336 static int CeedVectorAXPY_Memcheck(CeedVector y, CeedScalar alpha, CeedVector x) {
337   CeedSize             length;
338   CeedVector_Memcheck *impl_x, *impl_y;
339 
340   CeedCallBackend(CeedVectorGetData(x, &impl_x));
341   CeedCallBackend(CeedVectorGetData(y, &impl_y));
342   CeedCallBackend(CeedVectorGetLength(y, &length));
343 
344   for (CeedSize i = 0; i < length; i++) impl_y->array_allocated[i] += alpha * impl_x->array_allocated[i];
345   return CEED_ERROR_SUCCESS;
346 }
347 
348 //------------------------------------------------------------------------------
349 // Compute y = alpha x + beta y
350 //------------------------------------------------------------------------------
351 static int CeedVectorAXPBY_Memcheck(CeedVector y, CeedScalar alpha, CeedScalar beta, CeedVector x) {
352   CeedSize             length;
353   CeedVector_Memcheck *impl_x, *impl_y;
354 
355   CeedCallBackend(CeedVectorGetData(x, &impl_x));
356   CeedCallBackend(CeedVectorGetData(y, &impl_y));
357   CeedCallBackend(CeedVectorGetLength(y, &length));
358 
359   for (CeedSize i = 0; i < length; i++) impl_y->array_allocated[i] = alpha * impl_x->array_allocated[i] + beta * impl_y->array_allocated[i];
360   return CEED_ERROR_SUCCESS;
361 }
362 
363 //------------------------------------------------------------------------------
364 // Compute the pointwise multiplication w = x .* y
365 //------------------------------------------------------------------------------
366 static int CeedVectorPointwiseMult_Memcheck(CeedVector w, CeedVector x, CeedVector y) {
367   CeedSize             length;
368   CeedVector_Memcheck *impl_x, *impl_y, *impl_w;
369 
370   CeedCallBackend(CeedVectorGetData(x, &impl_x));
371   CeedCallBackend(CeedVectorGetData(y, &impl_y));
372   CeedCallBackend(CeedVectorGetData(w, &impl_w));
373   CeedCallBackend(CeedVectorGetLength(w, &length));
374 
375   if (!impl_w->array_allocated) CeedCallBackend(CeedVectorSetArray_Memcheck(w, CEED_MEM_HOST, CEED_COPY_VALUES, NULL));
376   assert(impl_w->array_allocated);
377   for (CeedSize i = 0; i < length; i++) impl_w->array_allocated[i] = impl_x->array_allocated[i] * impl_y->array_allocated[i];
378   return CEED_ERROR_SUCCESS;
379 }
380 
381 //------------------------------------------------------------------------------
382 // Vector Destroy
383 //------------------------------------------------------------------------------
384 static int CeedVectorDestroy_Memcheck(CeedVector vec) {
385   CeedVector_Memcheck *impl;
386 
387   // Free allocations and discard block ids
388   CeedCallBackend(CeedVectorGetData(vec, &impl));
389   if (impl->array_allocated) {
390     CeedCallBackend(CeedFree(&impl->array_allocated));
391     VALGRIND_DISCARD(impl->allocated_block_id);
392   }
393   if (impl->array_owned) {
394     CeedCallBackend(CeedFree(&impl->array_owned));
395     VALGRIND_DISCARD(impl->owned_block_id);
396   }
397   if (impl->array_borrowed) {
398     VALGRIND_DISCARD(impl->borrowed_block_id);
399   }
400   CeedCallBackend(CeedFree(&impl));
401   return CEED_ERROR_SUCCESS;
402 }
403 
404 //------------------------------------------------------------------------------
405 // Vector Create
406 //------------------------------------------------------------------------------
407 int CeedVectorCreate_Memcheck(CeedSize n, CeedVector vec) {
408   Ceed                 ceed;
409   CeedVector_Memcheck *impl;
410 
411   CeedCallBackend(CeedVectorGetCeed(vec, &ceed));
412   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "HasValidArray", CeedVectorHasValidArray_Memcheck));
413   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "HasBorrowedArrayOfType", CeedVectorHasBorrowedArrayOfType_Memcheck));
414   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "SetArray", CeedVectorSetArray_Memcheck));
415   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "SetValue", CeedVectorSetValue_Memcheck));
416   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "SetValueStrided", CeedVectorSetValueStrided_Memcheck));
417   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "SyncArray", CeedVectorSyncArray_Memcheck));
418   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "TakeArray", CeedVectorTakeArray_Memcheck));
419   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "GetArray", CeedVectorGetArray_Memcheck));
420   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "GetArrayRead", CeedVectorGetArrayRead_Memcheck));
421   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "GetArrayWrite", CeedVectorGetArrayWrite_Memcheck));
422   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "RestoreArray", CeedVectorRestoreArray_Memcheck));
423   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "RestoreArrayRead", CeedVectorRestoreArrayRead_Memcheck));
424   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "Reciprocal", CeedVectorReciprocal_Memcheck));
425   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "Scale", CeedVectorScale_Memcheck));
426   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "AXPY", CeedVectorAXPY_Memcheck));
427   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "AXPBY", CeedVectorAXPBY_Memcheck));
428   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "PointwiseMult", CeedVectorPointwiseMult_Memcheck));
429   CeedCallBackend(CeedSetBackendFunction(ceed, "Vector", vec, "Destroy", CeedVectorDestroy_Memcheck));
430   CeedCallBackend(CeedDestroy(&ceed));
431   CeedCallBackend(CeedCalloc(1, &impl));
432   CeedCallBackend(CeedVectorSetData(vec, impl));
433   return CEED_ERROR_SUCCESS;
434 }
435 
436 //------------------------------------------------------------------------------
437