avoid loading ICD at library init time
[ocl-icd] / ocl_icd_loader.c
1 /**
2 Copyright (c) 2012, Brice Videau <brice.videau@imag.fr>
3 Copyright (c) 2012, Vincent Danjean <Vincent.Danjean@ens-lyon.org>
4 All rights reserved.
5       
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8     
9 1. Redistributions of source code must retain the above copyright notice, this
10    list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright notice,
12    this list of conditions and the following disclaimer in the documentation
13    and/or other materials provided with the distribution.
14         
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <dirent.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <dlfcn.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include "config.h"
37 #ifdef USE_PTHREAD
38 #  include <pthread.h>
39 #endif
40 #pragma GCC diagnostic push
41 #  pragma GCC diagnostic ignored "-Wcpp"
42 #  define CL_USE_DEPRECATED_OPENCL_1_1_APIS
43 #  include <CL/opencl.h>
44 #pragma GCC diagnostic pop
45
46 #pragma GCC visibility push(hidden)
47
48 #include "ocl_icd_loader.h"
49 #define DEBUG_OCL_ICD_PROVIDE_DUMP_FIELD
50 #include "ocl_icd_debug.h"
51
52 #define ETC_OPENCL_VENDORS "/etc/OpenCL/vendors"
53
54 int debug_ocl_icd_mask=0;
55
56 typedef __typeof__(clGetPlatformInfo) *clGetPlatformInfo_fn;
57
58 inline void dump_vendor_icd(const char* info, const struct vendor_icd *v) {
59   debug(D_DUMP, "%s %p={ num=%i, first=%i, handle=%p, f=%p}\n", info,
60         v, v->num_platforms, v->first_platform, v->dl_handle, v->ext_fn_ptr);
61 }
62
63 struct vendor_icd *_icds=NULL;
64 struct platform_icd *_picds=NULL;
65 static cl_uint _num_icds = 0;
66 cl_uint _num_picds = 0;
67
68 #ifdef DEBUG_OCL_ICD
69 #  define _clS(x) [-x] = #x
70 #  define MAX_CL_ERRORS (-CL_INVALID_DEVICE_PARTITION_COUNT)
71 static char const * const clErrorStr[MAX_CL_ERRORS+1] = {
72   _clS(CL_SUCCESS),
73   _clS(CL_DEVICE_NOT_FOUND),
74   _clS(CL_DEVICE_NOT_AVAILABLE),
75   _clS(CL_COMPILER_NOT_AVAILABLE),
76   _clS(CL_MEM_OBJECT_ALLOCATION_FAILURE),
77   _clS(CL_OUT_OF_RESOURCES),
78   _clS(CL_OUT_OF_HOST_MEMORY),
79   _clS(CL_PROFILING_INFO_NOT_AVAILABLE),
80   _clS(CL_MEM_COPY_OVERLAP),
81   _clS(CL_IMAGE_FORMAT_MISMATCH),
82   _clS(CL_IMAGE_FORMAT_NOT_SUPPORTED),
83   _clS(CL_BUILD_PROGRAM_FAILURE),
84   _clS(CL_MAP_FAILURE),
85   _clS(CL_MISALIGNED_SUB_BUFFER_OFFSET),
86   _clS(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST),
87   _clS(CL_COMPILE_PROGRAM_FAILURE),
88   _clS(CL_LINKER_NOT_AVAILABLE),
89   _clS(CL_LINK_PROGRAM_FAILURE),
90   _clS(CL_DEVICE_PARTITION_FAILED),
91   _clS(CL_KERNEL_ARG_INFO_NOT_AVAILABLE),
92   _clS(CL_INVALID_VALUE),
93   _clS(CL_INVALID_DEVICE_TYPE),
94   _clS(CL_INVALID_PLATFORM),
95   _clS(CL_INVALID_DEVICE),
96   _clS(CL_INVALID_CONTEXT),
97   _clS(CL_INVALID_QUEUE_PROPERTIES),
98   _clS(CL_INVALID_COMMAND_QUEUE),
99   _clS(CL_INVALID_HOST_PTR),
100   _clS(CL_INVALID_MEM_OBJECT),
101   _clS(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR),
102   _clS(CL_INVALID_IMAGE_SIZE),
103   _clS(CL_INVALID_SAMPLER),
104   _clS(CL_INVALID_BINARY),
105   _clS(CL_INVALID_BUILD_OPTIONS),
106   _clS(CL_INVALID_PROGRAM),
107   _clS(CL_INVALID_PROGRAM_EXECUTABLE),
108   _clS(CL_INVALID_KERNEL_NAME),
109   _clS(CL_INVALID_KERNEL_DEFINITION),
110   _clS(CL_INVALID_KERNEL),
111   _clS(CL_INVALID_ARG_INDEX),
112   _clS(CL_INVALID_ARG_VALUE),
113   _clS(CL_INVALID_ARG_SIZE),
114   _clS(CL_INVALID_KERNEL_ARGS),
115   _clS(CL_INVALID_WORK_DIMENSION),
116   _clS(CL_INVALID_WORK_GROUP_SIZE),
117   _clS(CL_INVALID_WORK_ITEM_SIZE),
118   _clS(CL_INVALID_GLOBAL_OFFSET),
119   _clS(CL_INVALID_EVENT_WAIT_LIST),
120   _clS(CL_INVALID_EVENT),
121   _clS(CL_INVALID_OPERATION),
122   _clS(CL_INVALID_GL_OBJECT),
123   _clS(CL_INVALID_BUFFER_SIZE),
124   _clS(CL_INVALID_MIP_LEVEL),
125   _clS(CL_INVALID_GLOBAL_WORK_SIZE),
126   _clS(CL_INVALID_PROPERTY),
127   _clS(CL_INVALID_IMAGE_DESCRIPTOR),
128   _clS(CL_INVALID_COMPILER_OPTIONS),
129   _clS(CL_INVALID_LINKER_OPTIONS),
130   _clS(CL_INVALID_DEVICE_PARTITION_COUNT)
131 };
132 #undef _clS
133 #endif
134
135 static char* _clerror2string (cl_int error) __attribute__((unused));
136 static char* _clerror2string (cl_int error) {
137 #ifdef DEBUG_OCL_ICD
138   if (-error > MAX_CL_ERRORS || error > 0) {
139     debug(D_WARN, "Unknown error code %d", error);
140     RETURN_STR("OpenCL Error");
141   }
142   const char *ret=clErrorStr[-error];
143   if (ret == NULL) {
144     debug(D_WARN, "Unknown error code %d", error);
145     RETURN_STR("OpenCL Error");
146   }
147   RETURN_STR(ret);
148 #else
149   static char number[15];
150   if (error==0) {
151     RETURN_STR("CL_SUCCESS");
152   }
153   snprintf(number, 15, "%i", error);
154   RETURN_STR(number);
155 #endif
156 }
157
158 static inline int _string_end_with_icd(const char* str) {
159   size_t len = strlen(str);
160   if( len<5 || strcmp(str + len - 4, ".icd" ) != 0 ) {
161     return 0;
162   }
163   return 1;
164 }
165
166 static inline int _string_with_slash(const char* str) {
167   return strchr(str, '/') != NULL;
168 }
169
170 static inline unsigned int _find_num_icds(DIR *dir) {
171   unsigned int num_icds = 0;
172   struct dirent *ent;
173   while( (ent=readdir(dir)) != NULL ){
174     if (_string_end_with_icd(ent->d_name)) {
175       num_icds++;
176     }
177   }
178   rewinddir(dir);
179   RETURN(num_icds);
180 }
181
182 static inline unsigned int _load_icd(int num_icds, const char* lib_path) {
183   unsigned int ret=0;
184   debug(D_LOG, "Loading ICD '%s'", lib_path);
185
186   _icds[num_icds].dl_handle = dlopen(lib_path, RTLD_LAZY|RTLD_LOCAL);//|RTLD_DEEPBIND);
187   if(_icds[num_icds].dl_handle != NULL) {
188     debug(D_LOG, "ICD[%i] loaded", num_icds);
189     ret=1;
190   } else {
191     debug(D_WARN, "error while dlopening the IDL: '%s',\n  => skipping ICD", dlerror());
192   }
193   return ret;
194 }
195
196 static inline unsigned int _open_driver(unsigned int num_icds,
197                                         const char*dir_path, const char*file_path) {
198   char * lib_path;
199   char * err;
200   unsigned int lib_path_length;
201   if (dir_path != NULL) {
202     lib_path_length = strlen(dir_path) + strlen(file_path) + 2;
203     lib_path = malloc(lib_path_length*sizeof(char));
204     sprintf(lib_path,"%s/%s", dir_path, file_path);
205   } else {
206     lib_path_length = strlen(file_path) + 1;
207     lib_path = malloc(lib_path_length*sizeof(char));
208     sprintf(lib_path,"%s", file_path);
209   }
210   debug(D_LOG, "Considering file '%s'", lib_path);
211   FILE *f = fopen(lib_path,"r");
212   free(lib_path);
213   if (f==NULL) {
214     RETURN(num_icds);
215   }
216   
217   fseek(f, 0, SEEK_END);
218   lib_path_length = ftell(f)+1;
219   fseek(f, 0, SEEK_SET);
220   if(lib_path_length == 1) {
221     debug(D_WARN, "File contents too short, skipping ICD");
222     fclose(f);
223     RETURN(num_icds);
224   }
225   lib_path = malloc(lib_path_length*sizeof(char));
226   err = fgets(lib_path, lib_path_length, f);
227   fclose(f);
228   if( err == NULL ) {
229     free(lib_path);
230     debug(D_WARN, "Error while loading file contents, skipping ICD");
231     RETURN(num_icds);
232   }
233
234   lib_path_length = strnlen(lib_path, lib_path_length);
235   
236   if( lib_path[lib_path_length-1] == '\n' )
237     lib_path[lib_path_length-1] = '\0';
238
239   num_icds += _load_icd(num_icds, lib_path);
240
241   free(lib_path);
242   RETURN(num_icds);
243 }
244
245 static inline unsigned int _open_drivers(DIR *dir, const char* dir_path) {
246   unsigned int num_icds = 0;
247   struct dirent *ent;
248   while( (ent=readdir(dir)) != NULL ){
249     if(! _string_end_with_icd(ent->d_name)) {
250       continue;
251     }
252     num_icds = _open_driver(num_icds, dir_path, ent->d_name);
253
254   }
255   RETURN(num_icds);
256 }
257
258 static void* _get_function_addr(void* dlh, clGetExtensionFunctionAddress_fn fn, const char*name) {
259   void *addr1;
260   debug(D_LOG,"Looking for function %s",name);
261   addr1=dlsym(dlh, name);
262   if (addr1 == NULL) {
263     debug(D_WARN, "Missing global symbol '%s' in ICD, should be skipped", name);
264   }
265   void* addr2=NULL;
266   if (fn) {
267     addr2=(*fn)(name);
268     if (addr2 == NULL) {
269       debug(D_WARN, "Missing function '%s' in ICD, should be skipped", name);
270     }
271 #ifdef DEBUG_OCL_ICD
272     if (addr1 && addr2 && addr1!=addr2) {
273       debug(D_WARN, "Function and symbol '%s' have different addresses!", name);
274     }
275 #endif
276   }
277   if (!addr2) addr2=addr1;
278   RETURN(addr2);
279 }
280
281 static int _allocate_platforms(int req) {
282   static cl_uint allocated=0;
283   debug(D_LOG,"Requesting allocation for %d platforms",req);
284   if (allocated - _num_picds < req) {
285     if (allocated==0) {
286       _picds=(struct platform_icd*)malloc(req*sizeof(struct platform_icd));
287     } else {
288       req = req - (allocated - _num_picds);
289       _picds=(struct platform_icd*)realloc(_picds, (allocated+req)*sizeof(struct platform_icd));
290     }
291     allocated += req;
292   }
293   RETURN(allocated - _num_picds);
294 }
295
296 static char* _malloc_clGetPlatformInfo(clGetPlatformInfo_fn plt_info_ptr,
297                  cl_platform_id pid, cl_platform_info cname, char* sname) {
298   cl_int error;
299   size_t param_value_size_ret;
300   error = plt_info_ptr(pid, cname, 0, NULL, &param_value_size_ret);
301   if (error != CL_SUCCESS) {
302     debug(D_WARN, "Error %s while requesting %s in platform %p",
303           _clerror2string(error), sname, pid);
304     return NULL;
305   }
306   char *param_value = (char *)malloc(sizeof(char)*param_value_size_ret);
307   if (param_value == NULL) {
308     debug(D_WARN, "Error in malloc while requesting %s in platform %p",
309           sname, pid);
310     return NULL;
311   }
312   error = plt_info_ptr(pid, cname, param_value_size_ret, param_value, NULL);
313   if (error != CL_SUCCESS){
314     free(param_value);
315     debug(D_WARN, "Error %s while requesting %s in platform %p",
316           _clerror2string(error), sname, pid);
317     return NULL;
318   }
319   RETURN_STR(param_value);
320 }
321
322 static inline void _find_and_check_platforms(cl_uint num_icds) {
323   cl_uint i;
324   _num_icds = 0;
325   for( i=0; i<num_icds; i++){
326     debug(D_LOG, "Checking ICD %i/%i", i, num_icds);
327     dump_vendor_icd("before looking for platforms", &_icds[i]);
328     struct vendor_icd *picd = &_icds[i];
329     void* dlh = _icds[i].dl_handle;
330     picd->ext_fn_ptr = _get_function_addr(dlh, NULL, "clGetExtensionFunctionAddress");
331     clIcdGetPlatformIDsKHR_fn plt_fn_ptr = 
332       _get_function_addr(dlh, picd->ext_fn_ptr, "clIcdGetPlatformIDsKHR");
333     clGetPlatformInfo_fn plt_info_ptr = 
334       _get_function_addr(dlh, picd->ext_fn_ptr, "clGetPlatformInfo");
335     if( picd->ext_fn_ptr == NULL
336         || plt_fn_ptr == NULL
337         || plt_info_ptr == NULL) {
338       debug(D_WARN, "Missing symbols in ICD, skipping it");
339       continue;
340     }
341     cl_uint num_platforms=0;
342     cl_int error;
343     error = (*plt_fn_ptr)(0, NULL, &num_platforms);
344     if( error != CL_SUCCESS || num_platforms == 0) {
345       debug(D_LOG, "No platform in ICD, skipping it");
346       continue;
347     }
348     cl_platform_id *platforms = (cl_platform_id *) malloc( sizeof(cl_platform_id) * num_platforms);
349     error = (*plt_fn_ptr)(num_platforms, platforms, NULL);
350     if( error != CL_SUCCESS ){
351       free(platforms);
352       debug(D_WARN, "Error in loading ICD platforms, skipping ICD");
353       continue;
354     }
355     cl_uint num_valid_platforms=0;
356     cl_uint j;
357     debug(D_LOG, "Try to load %d plateforms", num_platforms);
358     if (_allocate_platforms(num_platforms) < num_platforms) {
359       free(platforms);
360       debug(D_WARN, "Not enought platform allocated. Skipping ICD");
361       continue;
362     }
363     for(j=0; j<num_platforms; j++) {
364       debug(D_LOG, "Checking platform %i", j);
365       struct platform_icd *p=&_picds[_num_picds];
366       char *param_value=NULL;
367       p->extension_suffix=NULL;
368       p->vicd=&_icds[i];
369       p->pid=platforms[j];
370 #ifdef DEBUG_OCL_ICD
371       if (debug_ocl_icd_mask & D_DUMP) {
372         int log=debug_ocl_icd_mask & D_TRACE;
373         debug_ocl_icd_mask &= ~D_TRACE;
374         dump_platform(p->vicd->ext_fn_ptr, p->pid);
375         debug_ocl_icd_mask |= log;
376       }
377 #endif
378       {
379               /* Allow to workaround a bug in the Intel ICD used
380                * with optirun (search for NVidia Optimus for more info)
381                */
382               const char* str=getenv("OCL_ICD_ASSUME_ICD_EXTENSION");
383               if (! str || str[0]==0) {
384                       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_EXTENSIONS, "extensions");
385                       if (param_value == NULL){
386                               debug(D_WARN, "Skipping platform %i", j);
387                               continue;
388                       }
389                       debug(D_DUMP, "Supported extensions: %s", param_value);
390                       if( strstr(param_value, "cl_khr_icd") == NULL){
391                               free(param_value); 
392                               debug(D_WARN, "Missing khr extension in platform %i, skipping it", j);
393                               continue;
394                       }
395                       free(param_value);
396               }
397       }
398       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_ICD_SUFFIX_KHR, "suffix");
399       if (param_value == NULL){
400         debug(D_WARN, "Skipping platform %i", j);
401         continue;
402       }
403       p->extension_suffix = param_value;
404       debug(D_DUMP|D_LOG, "Extension suffix: %s", param_value);
405 #ifdef DEBUG_OCL_ICD
406       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_PROFILE, "profile");
407       if (param_value != NULL){
408         debug(D_DUMP, "Profile: %s", param_value);
409         free(param_value);
410       }
411       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_VERSION, "version");
412       p->version = param_value;
413       if (param_value != NULL){
414         debug(D_DUMP, "Version: %s", param_value);
415         free(param_value);
416       }
417       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_NAME, "name");
418       if (param_value != NULL){
419         debug(D_DUMP, "Name: %s", param_value);
420         free(param_value);
421       }
422       param_value=_malloc_clGetPlatformInfo(plt_info_ptr, p->pid, CL_PLATFORM_VENDOR, "vendor");
423       if (param_value != NULL){
424         debug(D_DUMP, "Vendor: %s", param_value);
425         free(param_value);
426       }
427 #endif
428       num_valid_platforms++;
429       _num_picds++;
430     }
431     if( num_valid_platforms != 0 ) {
432       if ( _num_icds != i ) {
433         picd->dl_handle = dlh;
434       }
435       dump_vendor_icd("after looking for platforms", &_icds[_num_icds]);
436       _num_icds++;
437       picd->num_platforms = num_valid_platforms;
438       _icds[i].first_platform = _num_picds - num_valid_platforms;
439     } else {
440       dlclose(dlh);
441     }
442     free(platforms);
443   }
444 }
445
446 static void __initClIcd( void ) {
447   debug_init();
448   cl_uint num_icds = 0;
449   int is_dir = 0;
450   DIR *dir = NULL;
451   const char* dir_path=getenv("OCL_ICD_VENDORS");
452   if (! dir_path || dir_path[0]==0) {
453     debug(D_DUMP, "OCL_ICD_VENDORS empty or not defined, using %s", ETC_OPENCL_VENDORS);
454     dir_path=ETC_OPENCL_VENDORS;
455     is_dir=1;
456   }
457   if (!is_dir) {
458     struct stat buf;
459     int ret=stat(dir_path, &buf);
460     if (ret != 0 && errno != ENOENT) {
461       debug(D_WARN, "Cannot stat '%s'. Aborting", dir_path);
462     }
463     if (ret == 0 && S_ISDIR(buf.st_mode)) {
464       is_dir=1;
465     }
466   }
467   
468   if (!is_dir) {
469     debug(D_LOG,"Only loading '%s' as an ICD", dir_path);
470     num_icds = 1;
471     dir=NULL;
472   } else {
473     debug(D_LOG,"Reading icd list from '%s'", dir_path);
474     dir = opendir(dir_path);
475     if(dir == NULL) {
476       if (errno == ENOTDIR) {
477         debug(D_DUMP, "%s is not a directory, trying to use it as a ICD libname",
478         dir_path);
479       }
480       goto abort;
481     }
482
483     num_icds = _find_num_icds(dir);
484     if(num_icds == 0) {
485       goto abort;
486     }
487   }
488
489   _icds = (struct vendor_icd*)malloc(num_icds * sizeof(struct vendor_icd));
490   if (_icds == NULL) {
491     goto abort;
492   }
493   
494   if (!is_dir) {
495     if (_string_end_with_icd(dir_path)) {
496       num_icds = 0;
497       if (! _string_with_slash(dir_path)) {
498         num_icds = _open_driver(0, ETC_OPENCL_VENDORS, dir_path);
499       }
500       if (num_icds == 0) {
501         num_icds = _open_driver(0, NULL, dir_path);
502       }
503     } else {
504       num_icds = _load_icd(0, dir_path);
505     }
506   } else {
507     num_icds = _open_drivers(dir, dir_path);
508   }
509   if(num_icds == 0) {
510     goto abort;
511   }
512
513   _find_and_check_platforms(num_icds);
514   if(_num_icds == 0){
515     goto abort;
516   }
517
518   if (_num_icds < num_icds) {
519     _icds = (struct vendor_icd*)realloc(_icds, _num_icds * sizeof(struct vendor_icd));
520   }
521   debug(D_WARN, "%d valid vendor(s)!", _num_icds);
522
523   if (dir != NULL){
524     closedir(dir);
525   }
526   return;
527  abort:
528   _num_icds = 0;
529   if (_icds) {
530     free(_icds);
531     _icds = NULL;
532   }
533   if (dir != NULL){
534     closedir(dir);
535   }
536   return;
537 }
538
539 #ifdef USE_PTHREAD
540 static pthread_once_t once_init = PTHREAD_ONCE_INIT;
541 #else
542 static int gard=0;
543 #endif
544 volatile static __thread int in_init = 0;
545 volatile static cl_uint _initialized = 0;
546
547 static void _initClIcd_real( void ) {
548 #ifdef USE_PTHREAD
549   if (in_init) {
550     /* probably reentrency, in_init is a __thread variable */
551     debug(D_WARN, "Executing init while already in init!");
552   } else {
553     in_init=1;
554     __sync_synchronize();
555     pthread_once(&once_init, &__initClIcd);
556     __sync_synchronize();
557     in_init=0;
558   }
559 #else
560   if (__sync_bool_compare_and_swap(&gard, 0, 1)) {
561     in_init=1;
562     __sync_synchronize();
563     __initClIcd();
564     __sync_synchronize();
565     in_init=0;
566   } else {
567     if (in_init) {
568       /* probably reentrency (could also be preemptive user-level threads). */
569     } else {
570       /* someone else started __initClIcd(). We wait until it ends. */
571       debug(D_WARN, "Waiting end of init");
572       while (!_initialized) {
573         __sync_synchronize();
574       }
575       debug(D_WARN, "Wait done");
576    }
577   }
578 #endif
579   _initialized = 1;
580 }
581
582 static inline void _initClIcd( void ) {
583   if( __builtin_expect (_initialized, 1) )
584     return;
585   _initClIcd_real();
586 }
587
588 cl_platform_id __attribute__((visibility("internal")))
589 getDefaultPlatformID() {
590   static cl_platform_id defaultPlatformID=NULL;
591   static int defaultSet=0;
592   if (! defaultSet) {
593     do {
594       if(_num_picds == 0) {
595         break;
596       }
597       const char *default_platform = getenv("OPENCL_ICD_DEFAULT_PLATFORM");
598       int num_default_platform;
599       char *end_scan;
600       if (! default_platform) {
601         num_default_platform = 0;
602       } else {
603         num_default_platform = strtol(default_platform, &end_scan, 10);
604         if (*default_platform == '\0' || *end_scan != '\0') {
605           break;
606         }
607       }
608       if (num_default_platform < 0 || num_default_platform >= _num_picds) {
609         break;
610       }
611       defaultPlatformID=_picds[num_default_platform].pid;
612     } while(0);
613     defaultSet=1;
614   }
615   return defaultPlatformID;
616 }
617
618 #pragma GCC visibility pop
619 #define hidden_alias(name) \
620   typeof(name) name##_hid __attribute__ ((alias (#name), visibility("hidden")))
621
622 typedef enum {
623   CL_ICDL_OCL_VERSION=1,
624   CL_ICDL_VERSION=2,
625   CL_ICDL_NAME=3,
626   CL_ICDL_VENDOR=4,
627 } cl_icdl_info;
628
629 static cl_int clGetICDLoaderInfoOCLICD(
630   cl_icdl_info     param_name,
631   size_t           param_value_size, 
632   void *           param_value,
633   size_t *         param_value_size_ret)
634 {
635   char cl_icdl_ocl_version[] = "OpenCL 1.2";
636   char cl_icdl_version[] = PACKAGE_VERSION;
637   char cl_icdl_name[] = PACKAGE_NAME;
638   char cl_icdl_vendor[] = "OCL Icd free software";
639
640   size_t size_string;
641   char * string_p;
642 #define oclcase(name, NAME) \
643   case CL_ICDL_##NAME: \
644     string_p = cl_icdl_##name; \
645     size_string = sizeof(cl_icdl_##name); \
646     break
647
648   switch ( param_name ) {
649     oclcase(ocl_version,OCL_VERSION);
650     oclcase(version,VERSION);
651     oclcase(name,NAME);
652     oclcase(vendor,VENDOR);
653     default:
654       return CL_INVALID_VALUE;
655       break;
656   }
657 #undef oclcase
658   if( param_value != NULL ) {
659     if( size_string > param_value_size )
660       return CL_INVALID_VALUE;
661     memcpy(param_value, string_p, size_string);
662   }
663   if( param_value_size_ret != NULL )
664     *param_value_size_ret = size_string;
665   return CL_SUCCESS;
666 }
667
668 CL_API_ENTRY void * CL_API_CALL
669 clGetExtensionFunctionAddress(const char * func_name) CL_API_SUFFIX__VERSION_1_0 {
670   debug_trace();
671   _initClIcd();
672   if( func_name == NULL )
673     return NULL;
674   cl_uint suffix_length;
675   cl_uint i;
676   void * return_value=NULL;
677   struct func_desc const * fn=&function_description[0];
678   int lenfn=strlen(func_name);
679   if (lenfn > 3 &&
680       (strcmp(func_name+lenfn-3, "KHR")==0 || strcmp(func_name+lenfn-3, "EXT")==0)) {
681     while (fn->name != NULL) {
682       if (strcmp(func_name, fn->name)==0)
683         RETURN(fn->addr);
684       fn++;
685     }
686   }
687   for(i=0; i<_num_picds; i++) {
688     suffix_length = strlen(_picds[i].extension_suffix);
689     if( suffix_length > strlen(func_name) )
690       continue;
691     if(strcmp(_picds[i].extension_suffix, &func_name[strlen(func_name)-suffix_length]) == 0)
692       RETURN((*_picds[i].vicd->ext_fn_ptr)(func_name));
693   }
694   if(strcmp(func_name, "clGetICDLoaderInfoOCLICD") == 0) {
695     return (void*)(void*(*)(void))(&clGetICDLoaderInfoOCLICD);
696   }
697   RETURN(return_value);
698 }
699 hidden_alias(clGetExtensionFunctionAddress);
700
701 CL_API_ENTRY cl_int CL_API_CALL
702 clGetPlatformIDs(cl_uint          num_entries,
703                  cl_platform_id * platforms,
704                  cl_uint *        num_platforms) CL_API_SUFFIX__VERSION_1_0 {
705   debug_trace();
706   _initClIcd();
707   if( platforms == NULL && num_platforms == NULL )
708     RETURN(CL_INVALID_VALUE);
709   if( num_entries == 0 && platforms != NULL )
710     RETURN(CL_INVALID_VALUE);
711   if( _num_icds == 0 || _num_picds == 0 ) {
712     if ( num_platforms != NULL )
713       *num_platforms = 0;
714     RETURN(CL_PLATFORM_NOT_FOUND_KHR);
715   }
716
717   cl_uint i;
718   if( num_platforms != NULL ){
719     *num_platforms = _num_picds;
720   }
721   if( platforms != NULL ) {
722     cl_uint n_platforms = _num_picds < num_entries ? _num_picds : num_entries;
723     for( i=0; i<n_platforms; i++) {
724       *(platforms++) = _picds[i].pid;
725     }
726   }
727   return CL_SUCCESS;
728 }
729 hidden_alias(clGetPlatformIDs);
730
731 #define RETURN_WITH_ERRCODE(errvar, errvalue, retvalue) \
732   do { \
733     if(errvar) { \
734       *errvar=errvalue; \
735     } \
736     RETURN(NULL); \
737   } while(0)
738
739 #define CHECK_PLATFORM(__pid) \
740   ({ \
741     cl_platform_id _pid=(__pid); \
742     int good=0; \
743     cl_uint j; \
744     for( j=0; j<_num_picds; j++) { \
745       if( _picds[j].pid == _pid) { \
746         good=1; \
747         break; \
748       } \
749     } \
750     good; \
751   })
752
753 CL_API_ENTRY cl_context CL_API_CALL
754 clCreateContext(const cl_context_properties *  properties ,
755                 cl_uint                        num_devices ,
756                 const cl_device_id *           devices ,
757                 void (CL_CALLBACK *  pfn_notify )(const char *, const void *, size_t, void *),
758                 void *                         user_data ,
759                 cl_int *                       errcode_ret ){
760   debug_trace();
761   _initClIcd();
762   cl_uint i=0;
763   if( properties != NULL){
764     while( properties[i] != 0 ) {
765       if( properties[i] == CL_CONTEXT_PLATFORM ) {
766         if((struct _cl_platform_id *) properties[i+1] == NULL) {
767           if(errcode_ret) {
768             *errcode_ret = CL_INVALID_PLATFORM;
769           }
770           RETURN(NULL);
771         } else {
772           if( !CHECK_PLATFORM((cl_platform_id) properties[i+1]) ) {
773             RETURN_WITH_ERRCODE(errcode_ret, CL_INVALID_PLATFORM, NULL);
774           }
775         }
776         RETURN(((struct _cl_platform_id *) properties[i+1])
777           ->dispatch->clCreateContext(properties, num_devices, devices,
778                         pfn_notify, user_data, errcode_ret));
779       }
780       i += 2;
781     }
782   }
783   if(devices == NULL || num_devices == 0) {
784     RETURN_WITH_ERRCODE(errcode_ret, CL_INVALID_VALUE, NULL);
785   }
786   if((struct _cl_device_id *)devices[0] == NULL) {
787     RETURN_WITH_ERRCODE(errcode_ret, CL_INVALID_DEVICE, NULL);
788   }
789   RETURN(((struct _cl_device_id *)devices[0])
790     ->dispatch->clCreateContext(properties, num_devices, devices,
791                   pfn_notify, user_data, errcode_ret));
792 }
793 hidden_alias(clCreateContext);
794
795 CL_API_ENTRY cl_context CL_API_CALL
796 clCreateContextFromType(const cl_context_properties *  properties ,
797                         cl_device_type                 device_type ,
798                         void (CL_CALLBACK *      pfn_notify )(const char *, const void *, size_t, void *),
799                         void *                         user_data ,
800                         cl_int *                       errcode_ret ){
801   debug_trace();
802   _initClIcd();
803   if(_num_picds == 0) {
804     goto out;
805   }
806   cl_uint i=0;
807   if( properties != NULL){
808     while( properties[i] != 0 ) {
809       if( properties[i] == CL_CONTEXT_PLATFORM ) {
810         if( (struct _cl_platform_id *) properties[i+1] == NULL ) {
811           goto out;
812         } else {
813           if( !CHECK_PLATFORM((cl_platform_id) properties[i+1]) ) {
814             goto out;
815           }
816         }
817         return ((struct _cl_platform_id *) properties[i+1])
818           ->dispatch->clCreateContextFromType(properties, device_type,
819                         pfn_notify, user_data, errcode_ret);
820       }
821       i += 2;
822     }
823   } else {
824     const char *default_platform = getenv("OPENCL_ICD_DEFAULT_PLATFORM");
825     int num_default_platform;
826     char *end_scan;
827     if (! default_platform) {
828       num_default_platform = 0;
829     } else {
830       num_default_platform = strtol(default_platform, &end_scan, 10);
831       if (*default_platform == '\0' || *end_scan != '\0') {
832         goto out;
833       }
834     }
835     if (num_default_platform < 0 || num_default_platform >= _num_picds) {
836       goto out;
837     }
838     RETURN(_picds[num_default_platform].pid->dispatch->clCreateContextFromType
839         (properties, device_type, pfn_notify, user_data, errcode_ret));
840   }
841  out:
842   RETURN_WITH_ERRCODE(errcode_ret, CL_INVALID_PLATFORM, NULL);
843 }
844 hidden_alias(clCreateContextFromType);
845
846 CL_API_ENTRY cl_int CL_API_CALL
847 clGetGLContextInfoKHR(const cl_context_properties *  properties ,
848                       cl_gl_context_info             param_name ,
849                       size_t                         param_value_size ,
850                       void *                         param_value ,
851                       size_t *                       param_value_size_ret ){
852   debug_trace();
853   _initClIcd();
854   cl_uint i=0;
855   if( properties != NULL){
856     while( properties[i] != 0 ) {
857       if( properties[i] == CL_CONTEXT_PLATFORM ) {
858         if( (struct _cl_platform_id *) properties[i+1] == NULL ) {
859           RETURN(CL_INVALID_PLATFORM);
860         } else {
861           if( !CHECK_PLATFORM((cl_platform_id) properties[i+1]) ) {
862             RETURN(CL_INVALID_PLATFORM);
863           }
864         }
865         RETURN(((struct _cl_platform_id *) properties[i+1])
866           ->dispatch->clGetGLContextInfoKHR(properties, param_name,
867                         param_value_size, param_value, param_value_size_ret));
868       }
869       i += 2;
870     }
871   }
872   RETURN(CL_INVALID_PLATFORM);
873 }
874 hidden_alias(clGetGLContextInfoKHR);
875
876 CL_API_ENTRY cl_int CL_API_CALL
877 clWaitForEvents(cl_uint              num_events ,
878                 const cl_event *     event_list ){
879   debug_trace();
880   if( num_events == 0 || event_list == NULL )
881     RETURN(CL_INVALID_VALUE);
882   if( (struct _cl_event *)event_list[0] == NULL )
883     RETURN(CL_INVALID_EVENT);
884   RETURN(((struct _cl_event *)event_list[0])
885     ->dispatch->clWaitForEvents(num_events, event_list));
886 }
887 hidden_alias(clWaitForEvents);
888
889 CL_API_ENTRY cl_int CL_API_CALL
890 clUnloadCompiler( void ){
891   debug_trace();
892   RETURN(CL_SUCCESS);
893 }
894 hidden_alias(clUnloadCompiler);