• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2012 Francisco Jerez
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 // OTHER DEALINGS IN THE SOFTWARE.
21 //
22 
23 #include "util/format/u_format.h"
24 #include "util/u_math.h"
25 #include "api/util.hpp"
26 #include "core/memory.hpp"
27 #include "core/format.hpp"
28 
29 using namespace clover;
30 
31 namespace {
32    cl_mem_flags
validate_flags(cl_mem d_parent,cl_mem_flags d_flags,bool svm)33    validate_flags(cl_mem d_parent, cl_mem_flags d_flags, bool svm) {
34       const cl_mem_flags dev_access_flags =
35          CL_MEM_READ_WRITE | CL_MEM_WRITE_ONLY | CL_MEM_READ_ONLY;
36       const cl_mem_flags host_ptr_flags =
37          CL_MEM_USE_HOST_PTR | CL_MEM_ALLOC_HOST_PTR | CL_MEM_COPY_HOST_PTR;
38       const cl_mem_flags host_access_flags =
39          CL_MEM_HOST_WRITE_ONLY | CL_MEM_HOST_READ_ONLY | CL_MEM_HOST_NO_ACCESS;
40       const cl_mem_flags svm_flags =
41          CL_MEM_SVM_FINE_GRAIN_BUFFER | CL_MEM_SVM_ATOMICS;
42 
43       const cl_mem_flags valid_flags =
44          dev_access_flags
45             | (svm || d_parent ? 0 : host_ptr_flags)
46             | (svm ? svm_flags : host_access_flags);
47 
48       if ((d_flags & ~valid_flags) ||
49           util_bitcount(d_flags & dev_access_flags) > 1 ||
50           util_bitcount(d_flags & host_access_flags) > 1)
51          throw error(CL_INVALID_VALUE);
52 
53       if ((d_flags & CL_MEM_USE_HOST_PTR) &&
54           (d_flags & (CL_MEM_COPY_HOST_PTR | CL_MEM_ALLOC_HOST_PTR)))
55          throw error(CL_INVALID_VALUE);
56 
57       if ((d_flags & CL_MEM_SVM_ATOMICS) &&
58           !(d_flags & CL_MEM_SVM_FINE_GRAIN_BUFFER))
59          throw error(CL_INVALID_VALUE);
60 
61       if (d_parent) {
62          const auto &parent = obj(d_parent);
63          const cl_mem_flags flags = (d_flags |
64                                      (d_flags & dev_access_flags ? 0 :
65                                       parent.flags() & dev_access_flags) |
66                                      (d_flags & host_access_flags ? 0 :
67                                       parent.flags() & host_access_flags) |
68                                      (parent.flags() & host_ptr_flags));
69 
70          if (~flags & parent.flags() & (dev_access_flags & ~CL_MEM_READ_WRITE))
71             throw error(CL_INVALID_VALUE);
72 
73          // Check if new host access flags cause a mismatch between
74          // host-read/write-only.
75          if (!(flags & CL_MEM_HOST_NO_ACCESS) &&
76              (~flags & parent.flags() & host_access_flags))
77             throw error(CL_INVALID_VALUE);
78 
79          return flags;
80 
81       } else {
82          return d_flags | (d_flags & dev_access_flags ? 0 : CL_MEM_READ_WRITE);
83       }
84    }
85 
86    std::vector<cl_mem_properties>
fill_properties(const cl_mem_properties * d_properties)87    fill_properties(const cl_mem_properties *d_properties) {
88       std::vector<cl_mem_properties> properties;
89       if (d_properties) {
90          while (*d_properties) {
91             if (*d_properties != 0)
92                throw error(CL_INVALID_PROPERTY);
93 
94             properties.push_back(*d_properties);
95             d_properties++;
96          };
97          properties.push_back(0);
98       }
99       return properties;
100    }
101 }
102 
103 CLOVER_API cl_mem
clCreateBufferWithProperties(cl_context d_ctx,const cl_mem_properties * d_properties,cl_mem_flags d_flags,size_t size,void * host_ptr,cl_int * r_errcode)104 clCreateBufferWithProperties(cl_context d_ctx,
105                              const cl_mem_properties *d_properties,
106                              cl_mem_flags d_flags, size_t size,
107                              void *host_ptr, cl_int *r_errcode) try {
108 
109    auto &ctx = obj(d_ctx);
110    const cl_mem_flags flags = validate_flags(NULL, d_flags, false);
111    std::vector<cl_mem_properties> properties = fill_properties(d_properties);
112 
113    if (bool(host_ptr) != bool(flags & (CL_MEM_USE_HOST_PTR |
114                                        CL_MEM_COPY_HOST_PTR)))
115       throw error(CL_INVALID_HOST_PTR);
116 
117    if (!size ||
118        size > fold(maximum(), cl_ulong(0),
119                    map(std::mem_fn(&device::max_mem_alloc_size), ctx.devices())
120           ))
121       throw error(CL_INVALID_BUFFER_SIZE);
122 
123    ret_error(r_errcode, CL_SUCCESS);
124    return new root_buffer(ctx, properties, flags, size, host_ptr);
125 } catch (error &e) {
126    ret_error(r_errcode, e);
127    return NULL;
128 }
129 
130 
131 CLOVER_API cl_mem
clCreateBuffer(cl_context d_ctx,cl_mem_flags d_flags,size_t size,void * host_ptr,cl_int * r_errcode)132 clCreateBuffer(cl_context d_ctx, cl_mem_flags d_flags, size_t size,
133                void *host_ptr, cl_int *r_errcode) {
134    return clCreateBufferWithProperties(d_ctx, NULL, d_flags, size,
135                                        host_ptr, r_errcode);
136 }
137 
138 CLOVER_API cl_mem
clCreateSubBuffer(cl_mem d_mem,cl_mem_flags d_flags,cl_buffer_create_type op,const void * op_info,cl_int * r_errcode)139 clCreateSubBuffer(cl_mem d_mem, cl_mem_flags d_flags,
140                   cl_buffer_create_type op,
141                   const void *op_info, cl_int *r_errcode) try {
142    auto &parent = obj<root_buffer>(d_mem);
143    const cl_mem_flags flags = validate_flags(d_mem, d_flags, false);
144 
145    if (op == CL_BUFFER_CREATE_TYPE_REGION) {
146       auto reg = reinterpret_cast<const cl_buffer_region *>(op_info);
147 
148       if (!reg ||
149           reg->origin > parent.size() ||
150           reg->origin + reg->size > parent.size())
151          throw error(CL_INVALID_VALUE);
152 
153       if (!reg->size)
154          throw error(CL_INVALID_BUFFER_SIZE);
155 
156       ret_error(r_errcode, CL_SUCCESS);
157       return new sub_buffer(parent, flags, reg->origin, reg->size);
158 
159    } else {
160       throw error(CL_INVALID_VALUE);
161    }
162 
163 } catch (error &e) {
164    ret_error(r_errcode, e);
165    return NULL;
166 }
167 
168 CLOVER_API cl_mem
clCreateImageWithProperties(cl_context d_ctx,const cl_mem_properties * d_properties,cl_mem_flags d_flags,const cl_image_format * format,const cl_image_desc * desc,void * host_ptr,cl_int * r_errcode)169 clCreateImageWithProperties(cl_context d_ctx,
170                             const cl_mem_properties *d_properties,
171                             cl_mem_flags d_flags,
172                             const cl_image_format *format,
173                             const cl_image_desc *desc,
174                             void *host_ptr, cl_int *r_errcode) try {
175    auto &ctx = obj(d_ctx);
176 
177    if (!any_of(std::mem_fn(&device::image_support), ctx.devices()))
178       throw error(CL_INVALID_OPERATION);
179 
180    if (!format)
181       throw error(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR);
182 
183    if (!desc)
184       throw error(CL_INVALID_IMAGE_DESCRIPTOR);
185 
186    if (desc->image_array_size == 0 &&
187        (desc->image_type == CL_MEM_OBJECT_IMAGE1D_ARRAY ||
188         desc->image_type == CL_MEM_OBJECT_IMAGE2D_ARRAY))
189       throw error(CL_INVALID_IMAGE_DESCRIPTOR);
190 
191    if (!host_ptr &&
192        (desc->image_row_pitch || desc->image_slice_pitch))
193       throw error(CL_INVALID_IMAGE_DESCRIPTOR);
194 
195    if (desc->num_mip_levels || desc->num_samples)
196       throw error(CL_INVALID_IMAGE_DESCRIPTOR);
197 
198    if (bool(desc->buffer) != (desc->image_type == CL_MEM_OBJECT_IMAGE1D_BUFFER))
199       throw error(CL_INVALID_IMAGE_DESCRIPTOR);
200 
201    if (bool(host_ptr) != bool(d_flags & (CL_MEM_USE_HOST_PTR |
202                                          CL_MEM_COPY_HOST_PTR)))
203       throw error(CL_INVALID_HOST_PTR);
204 
205    const cl_mem_flags flags = validate_flags(desc->buffer, d_flags, false);
206 
207    if (!supported_formats(ctx, desc->image_type, d_flags).count(*format))
208       throw error(CL_IMAGE_FORMAT_NOT_SUPPORTED);
209 
210    std::vector<cl_mem_properties> properties = fill_properties(d_properties);
211    ret_error(r_errcode, CL_SUCCESS);
212 
213    const size_t row_pitch = desc->image_row_pitch ? desc->image_row_pitch :
214       util_format_get_blocksize(translate_format(*format)) * desc->image_width;
215 
216    switch (desc->image_type) {
217    case CL_MEM_OBJECT_IMAGE1D:
218       if (!desc->image_width)
219          throw error(CL_INVALID_IMAGE_SIZE);
220 
221       if (all_of([=](const device &dev) {
222                const size_t max = dev.max_image_size();
223                return (desc->image_width > max);
224             }, ctx.devices()))
225          throw error(CL_INVALID_IMAGE_SIZE);
226 
227       return new image1d(ctx, properties, flags, format,
228                          desc->image_width,
229                          row_pitch, host_ptr);
230 
231    case CL_MEM_OBJECT_IMAGE1D_BUFFER:
232       if (!desc->image_width)
233          throw error(CL_INVALID_IMAGE_SIZE);
234 
235       if (all_of([=](const device &dev) {
236                const size_t max = dev.max_image_buffer_size();
237                return (desc->image_width > max);
238             }, ctx.devices()))
239          throw error(CL_INVALID_IMAGE_SIZE);
240 
241       return new image1d_buffer(ctx, properties, flags, format,
242                                 desc->image_width,
243                                 row_pitch, host_ptr, desc->buffer);
244 
245    case CL_MEM_OBJECT_IMAGE1D_ARRAY: {
246       if (!desc->image_width)
247          throw error(CL_INVALID_IMAGE_SIZE);
248 
249       if (all_of([=](const device &dev) {
250                const size_t max = dev.max_image_size();
251                const size_t amax = dev.max_image_array_number();
252                return (desc->image_width > max ||
253                        desc->image_array_size > amax);
254             }, ctx.devices()))
255          throw error(CL_INVALID_IMAGE_SIZE);
256 
257       const size_t slice_pitch = desc->image_slice_pitch ?
258          desc->image_slice_pitch : row_pitch;
259 
260       return new image1d_array(ctx, properties, flags, format,
261                                desc->image_width,
262                                desc->image_array_size, slice_pitch,
263                                host_ptr);
264    }
265 
266    case CL_MEM_OBJECT_IMAGE2D:
267       if (!desc->image_width || !desc->image_height)
268          throw error(CL_INVALID_IMAGE_SIZE);
269 
270       if (all_of([=](const device &dev) {
271                const size_t max = dev.max_image_size();
272                return (desc->image_width > max ||
273                        desc->image_height > max);
274             }, ctx.devices()))
275          throw error(CL_INVALID_IMAGE_SIZE);
276 
277       return new image2d(ctx, properties, flags, format,
278                          desc->image_width, desc->image_height,
279                          row_pitch, host_ptr);
280 
281    case CL_MEM_OBJECT_IMAGE2D_ARRAY: {
282       if (!desc->image_width || !desc->image_height || !desc->image_array_size)
283          throw error(CL_INVALID_IMAGE_SIZE);
284 
285       if (all_of([=](const device &dev) {
286                const size_t max = dev.max_image_size();
287                const size_t amax = dev.max_image_array_number();
288                return (desc->image_width > max ||
289                        desc->image_height > max ||
290                        desc->image_array_size > amax);
291             }, ctx.devices()))
292          throw error(CL_INVALID_IMAGE_SIZE);
293 
294       const size_t slice_pitch = desc->image_slice_pitch ?
295          desc->image_slice_pitch : row_pitch * desc->image_height;
296 
297       return new image2d_array(ctx, properties, flags, format,
298                                desc->image_width, desc->image_height,
299                                desc->image_array_size, row_pitch,
300                                slice_pitch, host_ptr);
301    }
302 
303    case CL_MEM_OBJECT_IMAGE3D: {
304       if (!desc->image_width || !desc->image_height || !desc->image_depth)
305          throw error(CL_INVALID_IMAGE_SIZE);
306 
307       if (all_of([=](const device &dev) {
308                const size_t max = dev.max_image_size_3d();
309                return (desc->image_width > max ||
310                        desc->image_height > max ||
311                        desc->image_depth > max);
312             }, ctx.devices()))
313          throw error(CL_INVALID_IMAGE_SIZE);
314 
315       const size_t slice_pitch = desc->image_slice_pitch ?
316          desc->image_slice_pitch : row_pitch * desc->image_height;
317 
318       return new image3d(ctx, properties, flags, format,
319                          desc->image_width, desc->image_height,
320                          desc->image_depth, row_pitch,
321                          slice_pitch, host_ptr);
322    }
323 
324    default:
325       throw error(CL_INVALID_IMAGE_DESCRIPTOR);
326    }
327 
328 } catch (error &e) {
329    ret_error(r_errcode, e);
330    return NULL;
331 }
332 
333 CLOVER_API cl_mem
clCreateImage(cl_context d_ctx,cl_mem_flags d_flags,const cl_image_format * format,const cl_image_desc * desc,void * host_ptr,cl_int * r_errcode)334 clCreateImage(cl_context d_ctx,
335               cl_mem_flags d_flags,
336               const cl_image_format *format,
337               const cl_image_desc *desc,
338               void *host_ptr, cl_int *r_errcode) {
339    return clCreateImageWithProperties(d_ctx, NULL, d_flags, format, desc, host_ptr, r_errcode);
340 }
341 
342 
343 CLOVER_API cl_mem
clCreateImage2D(cl_context d_ctx,cl_mem_flags d_flags,const cl_image_format * format,size_t width,size_t height,size_t row_pitch,void * host_ptr,cl_int * r_errcode)344 clCreateImage2D(cl_context d_ctx, cl_mem_flags d_flags,
345                 const cl_image_format *format,
346                 size_t width, size_t height, size_t row_pitch,
347                 void *host_ptr, cl_int *r_errcode) {
348    const cl_image_desc desc = { CL_MEM_OBJECT_IMAGE2D, width, height, 0, 0,
349                                 row_pitch, 0, 0, 0, NULL };
350 
351    return clCreateImageWithProperties(d_ctx, NULL, d_flags, format, &desc, host_ptr, r_errcode);
352 }
353 
354 CLOVER_API cl_mem
clCreateImage3D(cl_context d_ctx,cl_mem_flags d_flags,const cl_image_format * format,size_t width,size_t height,size_t depth,size_t row_pitch,size_t slice_pitch,void * host_ptr,cl_int * r_errcode)355 clCreateImage3D(cl_context d_ctx, cl_mem_flags d_flags,
356                 const cl_image_format *format,
357                 size_t width, size_t height, size_t depth,
358                 size_t row_pitch, size_t slice_pitch,
359                 void *host_ptr, cl_int *r_errcode) {
360    const cl_image_desc desc = { CL_MEM_OBJECT_IMAGE3D, width, height, depth, 0,
361                                 row_pitch, slice_pitch, 0, 0, NULL };
362 
363    return clCreateImageWithProperties(d_ctx, NULL, d_flags, format, &desc, host_ptr, r_errcode);
364 }
365 
366 CLOVER_API cl_int
clGetSupportedImageFormats(cl_context d_ctx,cl_mem_flags flags,cl_mem_object_type type,cl_uint count,cl_image_format * r_buf,cl_uint * r_count)367 clGetSupportedImageFormats(cl_context d_ctx, cl_mem_flags flags,
368                            cl_mem_object_type type, cl_uint count,
369                            cl_image_format *r_buf, cl_uint *r_count) try {
370    auto &ctx = obj(d_ctx);
371    auto formats = supported_formats(ctx, type, flags);
372 
373    if (flags & CL_MEM_KERNEL_READ_AND_WRITE) {
374       if (r_count)
375          *r_count = 0;
376       return CL_SUCCESS;
377    }
378 
379    if (flags & (CL_MEM_WRITE_ONLY | CL_MEM_READ_WRITE) &&
380        type == CL_MEM_OBJECT_IMAGE3D) {
381       if (r_count)
382          *r_count = 0;
383       return CL_SUCCESS;
384    }
385 
386    validate_flags(NULL, flags, false);
387 
388    if (r_buf && !count)
389       throw error(CL_INVALID_VALUE);
390 
391    if (r_buf)
392       std::copy_n(formats.begin(),
393                   std::min((cl_uint)formats.size(), count),
394                   r_buf);
395 
396    if (r_count)
397       *r_count = formats.size();
398 
399    return CL_SUCCESS;
400 
401 } catch (error &e) {
402    return e.get();
403 }
404 
405 CLOVER_API cl_int
clGetMemObjectInfo(cl_mem d_mem,cl_mem_info param,size_t size,void * r_buf,size_t * r_size)406 clGetMemObjectInfo(cl_mem d_mem, cl_mem_info param,
407                    size_t size, void *r_buf, size_t *r_size) try {
408    property_buffer buf { r_buf, size, r_size };
409    auto &mem = obj(d_mem);
410 
411    switch (param) {
412    case CL_MEM_TYPE:
413       buf.as_scalar<cl_mem_object_type>() = mem.type();
414       break;
415 
416    case CL_MEM_FLAGS:
417       buf.as_scalar<cl_mem_flags>() = mem.flags();
418       break;
419 
420    case CL_MEM_SIZE:
421       buf.as_scalar<size_t>() = mem.size();
422       break;
423 
424    case CL_MEM_HOST_PTR:
425       buf.as_scalar<void *>() = mem.host_ptr();
426       break;
427 
428    case CL_MEM_MAP_COUNT:
429       buf.as_scalar<cl_uint>() = 0;
430       break;
431 
432    case CL_MEM_REFERENCE_COUNT:
433       buf.as_scalar<cl_uint>() = mem.ref_count();
434       break;
435 
436    case CL_MEM_CONTEXT:
437       buf.as_scalar<cl_context>() = desc(mem.context());
438       break;
439 
440    case CL_MEM_ASSOCIATED_MEMOBJECT: {
441       sub_buffer *sub = dynamic_cast<sub_buffer *>(&mem);
442       if (sub) {
443          buf.as_scalar<cl_mem>() = desc(sub->parent());
444          break;
445       }
446 
447       image *img = dynamic_cast<image *>(&mem);
448       if (img) {
449          buf.as_scalar<cl_mem>() = desc(img->buffer());
450          break;
451       }
452 
453       buf.as_scalar<cl_mem>() = NULL;
454       break;
455    }
456    case CL_MEM_OFFSET: {
457       sub_buffer *sub = dynamic_cast<sub_buffer *>(&mem);
458       buf.as_scalar<size_t>() = (sub ? sub->offset() : 0);
459       break;
460    }
461    case CL_MEM_USES_SVM_POINTER:
462    case CL_MEM_USES_SVM_POINTER_ARM: {
463       // with system SVM all host ptrs are SVM pointers
464       // TODO: once we support devices with lower levels of SVM, we have to
465       // check the ptr in more detail
466       const bool system_svm = all_of(std::mem_fn(&device::has_system_svm),
467                                      mem.context().devices());
468       buf.as_scalar<cl_bool>() = mem.host_ptr() && system_svm;
469       break;
470    }
471    case CL_MEM_PROPERTIES:
472       buf.as_vector<cl_mem_properties>() = mem.properties();
473       break;
474    default:
475       throw error(CL_INVALID_VALUE);
476    }
477 
478    return CL_SUCCESS;
479 
480 } catch (error &e) {
481    return e.get();
482 }
483 
484 CLOVER_API cl_int
clGetImageInfo(cl_mem d_mem,cl_image_info param,size_t size,void * r_buf,size_t * r_size)485 clGetImageInfo(cl_mem d_mem, cl_image_info param,
486                size_t size, void *r_buf, size_t *r_size) try {
487    property_buffer buf { r_buf, size, r_size };
488    auto &img = obj<image>(d_mem);
489 
490    switch (param) {
491    case CL_IMAGE_FORMAT:
492       buf.as_scalar<cl_image_format>() = img.format();
493       break;
494 
495    case CL_IMAGE_ELEMENT_SIZE:
496       buf.as_scalar<size_t>() = img.pixel_size();
497       break;
498 
499    case CL_IMAGE_ROW_PITCH:
500       buf.as_scalar<size_t>() = img.row_pitch();
501       break;
502 
503    case CL_IMAGE_SLICE_PITCH:
504       buf.as_scalar<size_t>() = img.slice_pitch();
505       break;
506 
507    case CL_IMAGE_WIDTH:
508       buf.as_scalar<size_t>() = img.width();
509       break;
510 
511    case CL_IMAGE_HEIGHT:
512       buf.as_scalar<size_t>() = img.dimensions() > 1 ? img.height() : 0;
513       break;
514 
515    case CL_IMAGE_DEPTH:
516       buf.as_scalar<size_t>() = img.dimensions() > 2 ? img.depth() : 0;
517       break;
518 
519    case CL_IMAGE_ARRAY_SIZE:
520       buf.as_scalar<size_t>() = img.array_size();
521       break;
522 
523    case CL_IMAGE_BUFFER:
524       buf.as_scalar<cl_mem>() = img.buffer();
525       break;
526 
527    case CL_IMAGE_NUM_MIP_LEVELS:
528       buf.as_scalar<cl_uint>() = 0;
529       break;
530 
531    case CL_IMAGE_NUM_SAMPLES:
532       buf.as_scalar<cl_uint>() = 0;
533       break;
534 
535    default:
536       throw error(CL_INVALID_VALUE);
537    }
538 
539    return CL_SUCCESS;
540 
541 } catch (error &e) {
542    return e.get();
543 }
544 
545 CLOVER_API cl_int
clRetainMemObject(cl_mem d_mem)546 clRetainMemObject(cl_mem d_mem) try {
547    obj(d_mem).retain();
548    return CL_SUCCESS;
549 
550 } catch (error &e) {
551    return e.get();
552 }
553 
554 CLOVER_API cl_int
clReleaseMemObject(cl_mem d_mem)555 clReleaseMemObject(cl_mem d_mem) try {
556    if (obj(d_mem).release())
557       delete pobj(d_mem);
558 
559    return CL_SUCCESS;
560 
561 } catch (error &e) {
562    return e.get();
563 }
564 
565 CLOVER_API cl_int
clSetMemObjectDestructorCallback(cl_mem d_mem,void (CL_CALLBACK * pfn_notify)(cl_mem,void *),void * user_data)566 clSetMemObjectDestructorCallback(cl_mem d_mem,
567                                  void (CL_CALLBACK *pfn_notify)(cl_mem, void *),
568                                  void *user_data) try {
569    auto &mem = obj(d_mem);
570 
571    if (!pfn_notify)
572       return CL_INVALID_VALUE;
573 
574    mem.destroy_notify([=]{ pfn_notify(d_mem, user_data); });
575 
576    return CL_SUCCESS;
577 
578 } catch (error &e) {
579    return e.get();
580 }
581 
582 CLOVER_API void *
clSVMAlloc(cl_context d_ctx,cl_svm_mem_flags flags,size_t size,unsigned int alignment)583 clSVMAlloc(cl_context d_ctx,
584            cl_svm_mem_flags flags,
585            size_t size,
586            unsigned int alignment) try {
587    auto &ctx = obj(d_ctx);
588 
589    if (!any_of(std::mem_fn(&device::svm_support), ctx.devices()))
590       return NULL;
591 
592    validate_flags(NULL, flags, true);
593 
594    if (!size ||
595        size > fold(minimum(), cl_ulong(ULONG_MAX),
596                    map(std::mem_fn(&device::max_mem_alloc_size), ctx.devices())))
597       return nullptr;
598 
599    if (!util_is_power_of_two_or_zero(alignment))
600       return nullptr;
601 
602    if (!alignment)
603       alignment = 0x80; // sizeof(long16)
604 
605 #if HAVE_POSIX_MEMALIGN
606    bool can_emulate = all_of(std::mem_fn(&device::has_system_svm), ctx.devices());
607    if (can_emulate) {
608       // we can ignore all the flags as it's not required to honor them.
609       void *ptr = nullptr;
610       if (alignment < sizeof(void*))
611          alignment = sizeof(void*);
612       posix_memalign(&ptr, alignment, size);
613 
614       if (ptr)
615          ctx.add_svm_allocation(ptr, size);
616 
617       return ptr;
618    }
619 #endif
620 
621    CLOVER_NOT_SUPPORTED_UNTIL("2.0");
622    return nullptr;
623 
624 } catch (error &) {
625    return nullptr;
626 }
627 
628 CLOVER_API void
clSVMFree(cl_context d_ctx,void * svm_pointer)629 clSVMFree(cl_context d_ctx,
630           void *svm_pointer) try {
631    auto &ctx = obj(d_ctx);
632 
633    if (!any_of(std::mem_fn(&device::svm_support), ctx.devices()))
634       return;
635 
636    bool can_emulate = all_of(std::mem_fn(&device::has_system_svm), ctx.devices());
637 
638    if (can_emulate) {
639       ctx.remove_svm_allocation(svm_pointer);
640       return free(svm_pointer);
641    }
642 
643    CLOVER_NOT_SUPPORTED_UNTIL("2.0");
644 
645 } catch (error &) {
646 }
647