]> git.ipfire.org Git - thirdparty/gcc.git/blob - liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
libgomp-plugin-intelmic.cpp (struct TargetImageDesc): New.
[thirdparty/gcc.git] / liboffloadmic / plugin / libgomp-plugin-intelmic.cpp
1 /* Plugin for offload execution on Intel MIC devices.
2
3 Copyright (C) 2014-2015 Free Software Foundation, Inc.
4
5 Contributed by Ilya Verbin <ilya.verbin@intel.com>.
6
7 This file is part of the GNU Offloading and Multi Processing Library
8 (libgomp).
9
10 Libgomp is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3, or (at your option)
13 any later version.
14
15 Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 more details.
19
20 Under Section 7 of GPL version 3, you are granted additional
21 permissions described in the GCC Runtime Library Exception, version
22 3.1, as published by the Free Software Foundation.
23
24 You should have received a copy of the GNU General Public License and
25 a copy of the GCC Runtime Library Exception along with this program;
26 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
27 <http://www.gnu.org/licenses/>. */
28
29 /* Host side part of a libgomp plugin. */
30
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <utility>
36 #include <vector>
37 #include <map>
38 #include "libgomp-plugin.h"
39 #include "compiler_if_host.h"
40 #include "main_target_image.h"
41 #include "gomp-constants.h"
42
43 #define LD_LIBRARY_PATH_ENV "LD_LIBRARY_PATH"
44 #define MIC_LD_LIBRARY_PATH_ENV "MIC_LD_LIBRARY_PATH"
45 #define OFFLOAD_ACTIVE_WAIT_ENV "OFFLOAD_ACTIVE_WAIT"
46
47 #ifdef DEBUG
48 #define TRACE(...) \
49 { \
50 fprintf (stderr, "HOST:\t%s:%s ", __FILE__, __FUNCTION__); \
51 fprintf (stderr, __VA_ARGS__); \
52 fprintf (stderr, "\n"); \
53 }
54 #else
55 #define TRACE { }
56 #endif
57
58
59 /* Start/end addresses of functions and global variables on a device. */
60 typedef std::vector<addr_pair> AddrVect;
61
62 /* Addresses for one image and all devices. */
63 typedef std::vector<AddrVect> DevAddrVect;
64
65 /* Addresses for all images and all devices. */
66 typedef std::map<const void *, DevAddrVect> ImgDevAddrMap;
67
68 /* Image descriptor needed by __offload_[un]register_image. */
69 struct TargetImageDesc {
70 int64_t size;
71 /* 10 characters is enough for max int value. */
72 char name[sizeof ("lib0000000000.so")];
73 char data[];
74 };
75
76 /* Image descriptors, indexed by a pointer obtained from libgomp. */
77 typedef std::map<const void *, TargetImageDesc *> ImgDescMap;
78
79
80 /* Total number of available devices. */
81 static int num_devices;
82
83 /* Total number of shared libraries with offloading to Intel MIC. */
84 static int num_images;
85
86 /* Two dimensional array: one key is a pointer to image,
87 second key is number of device. Contains a vector of pointer pairs. */
88 static ImgDevAddrMap *address_table;
89
90 /* Descriptors of all images, registered in liboffloadmic. */
91 static ImgDescMap *image_descriptors;
92
93 /* Thread-safe registration of the main image. */
94 static pthread_once_t main_image_is_registered = PTHREAD_ONCE_INIT;
95
96 static VarDesc vd_host2tgt = {
97 { 1, 1 }, /* dst, src */
98 { 1, 0 }, /* in, out */
99 1, /* alloc_if */
100 1, /* free_if */
101 4, /* align */
102 0, /* mic_offset */
103 { 0, 0, 0, 0, 0, 0, 0, 0 }, /* is_static, is_static_dstn, has_length,
104 is_stack_buf, sink_addr, alloc_disp,
105 is_noncont_src, is_noncont_dst */
106 0, /* offset */
107 0, /* size */
108 1, /* count */
109 0, /* alloc */
110 0, /* into */
111 0 /* ptr */
112 };
113
114 static VarDesc vd_tgt2host = {
115 { 1, 1 }, /* dst, src */
116 { 0, 1 }, /* in, out */
117 1, /* alloc_if */
118 1, /* free_if */
119 4, /* align */
120 0, /* mic_offset */
121 { 0, 0, 0, 0, 0, 0, 0, 0 }, /* is_static, is_static_dstn, has_length,
122 is_stack_buf, sink_addr, alloc_disp,
123 is_noncont_src, is_noncont_dst */
124 0, /* offset */
125 0, /* size */
126 1, /* count */
127 0, /* alloc */
128 0, /* into */
129 0 /* ptr */
130 };
131
132
133 __attribute__((constructor))
134 static void
135 init (void)
136 {
137 const char *ld_lib_path = getenv (LD_LIBRARY_PATH_ENV);
138 const char *mic_lib_path = getenv (MIC_LD_LIBRARY_PATH_ENV);
139 const char *active_wait = getenv (OFFLOAD_ACTIVE_WAIT_ENV);
140
141 /* Disable active wait by default to avoid useless CPU usage. */
142 if (!active_wait)
143 setenv (OFFLOAD_ACTIVE_WAIT_ENV, "0", 0);
144
145 if (!ld_lib_path)
146 goto out;
147
148 /* Add path specified in LD_LIBRARY_PATH to MIC_LD_LIBRARY_PATH, which is
149 required by liboffloadmic. */
150 if (!mic_lib_path)
151 setenv (MIC_LD_LIBRARY_PATH_ENV, ld_lib_path, 1);
152 else
153 {
154 size_t len = strlen (mic_lib_path) + strlen (ld_lib_path) + 2;
155 bool use_alloca = len <= 2048;
156 char *mic_lib_path_new = (char *) (use_alloca ? alloca (len)
157 : malloc (len));
158 if (!mic_lib_path_new)
159 {
160 fprintf (stderr, "%s: Can't allocate memory\n", __FILE__);
161 exit (1);
162 }
163
164 sprintf (mic_lib_path_new, "%s:%s", mic_lib_path, ld_lib_path);
165 setenv (MIC_LD_LIBRARY_PATH_ENV, mic_lib_path_new, 1);
166
167 if (!use_alloca)
168 free (mic_lib_path_new);
169 }
170
171 out:
172 address_table = new ImgDevAddrMap;
173 image_descriptors = new ImgDescMap;
174 num_devices = _Offload_number_of_devices ();
175 }
176
177 extern "C" const char *
178 GOMP_OFFLOAD_get_name (void)
179 {
180 const char *res = "intelmic";
181 TRACE ("(): return %s", res);
182 return res;
183 }
184
185 extern "C" unsigned int
186 GOMP_OFFLOAD_get_caps (void)
187 {
188 unsigned int res = GOMP_OFFLOAD_CAP_OPENMP_400;
189 TRACE ("(): return %x", res);
190 return res;
191 }
192
193 extern "C" enum offload_target_type
194 GOMP_OFFLOAD_get_type (void)
195 {
196 enum offload_target_type res = OFFLOAD_TARGET_TYPE_INTEL_MIC;
197 TRACE ("(): return %d", res);
198 return res;
199 }
200
201 extern "C" int
202 GOMP_OFFLOAD_get_num_devices (void)
203 {
204 TRACE ("(): return %d", num_devices);
205 return num_devices;
206 }
207
208 static void
209 offload (const char *file, uint64_t line, int device, const char *name,
210 int num_vars, VarDesc *vars, const void **async_data)
211 {
212 OFFLOAD ofld = __offload_target_acquire1 (&device, file, line);
213 if (ofld)
214 {
215 if (async_data == NULL)
216 __offload_offload1 (ofld, name, 0, num_vars, vars, NULL, 0, NULL, NULL);
217 else
218 {
219 OffloadFlags flags;
220 flags.flags = 0;
221 flags.bits.omp_async = 1;
222 __offload_offload3 (ofld, name, 0, num_vars, vars, NULL, 0, NULL,
223 async_data, 0, NULL, flags, NULL);
224 }
225 }
226 else
227 {
228 fprintf (stderr, "%s:%d: Offload target acquire failed\n", file, line);
229 exit (1);
230 }
231 }
232
233 static void
234 unregister_main_image ()
235 {
236 __offload_unregister_image (&main_target_image);
237 }
238
239 static void
240 register_main_image ()
241 {
242 /* Do not check the return value, because old versions of liboffloadmic did
243 not have return values. */
244 __offload_register_image (&main_target_image);
245
246 /* liboffloadmic will call GOMP_PLUGIN_target_task_completion when
247 asynchronous task on target is completed. */
248 __offload_register_task_callback (GOMP_PLUGIN_target_task_completion);
249
250 if (atexit (unregister_main_image) != 0)
251 {
252 fprintf (stderr, "%s: atexit failed\n", __FILE__);
253 exit (1);
254 }
255 }
256
257 /* liboffloadmic loads and runs offload_target_main on all available devices
258 during a first call to offload (). */
259 extern "C" void
260 GOMP_OFFLOAD_init_device (int device)
261 {
262 TRACE ("(device = %d)", device);
263 pthread_once (&main_image_is_registered, register_main_image);
264 offload (__FILE__, __LINE__, device, "__offload_target_init_proc", 0, NULL,
265 NULL);
266 }
267
268 extern "C" void
269 GOMP_OFFLOAD_fini_device (int device)
270 {
271 TRACE ("(device = %d)", device);
272 /* Unreachable for GOMP_OFFLOAD_CAP_OPENMP_400. */
273 abort ();
274 }
275
276 static void
277 get_target_table (int device, int &num_funcs, int &num_vars, void **&table)
278 {
279 VarDesc vd1[2] = { vd_tgt2host, vd_tgt2host };
280 vd1[0].ptr = &num_funcs;
281 vd1[0].size = sizeof (num_funcs);
282 vd1[1].ptr = &num_vars;
283 vd1[1].size = sizeof (num_vars);
284
285 offload (__FILE__, __LINE__, device, "__offload_target_table_p1", 2, vd1,
286 NULL);
287
288 int table_size = num_funcs + 2 * num_vars;
289 if (table_size > 0)
290 {
291 table = new void * [table_size];
292
293 VarDesc vd2;
294 vd2 = vd_tgt2host;
295 vd2.ptr = table;
296 vd2.size = table_size * sizeof (void *);
297
298 offload (__FILE__, __LINE__, device, "__offload_target_table_p2", 1, &vd2,
299 NULL);
300 }
301 }
302
303 /* Offload TARGET_IMAGE to all available devices and fill address_table with
304 corresponding target addresses. */
305
306 static void
307 offload_image (const void *target_image)
308 {
309 void *image_start = ((void **) target_image)[0];
310 void *image_end = ((void **) target_image)[1];
311
312 TRACE ("(target_image = %p { %p, %p })",
313 target_image, image_start, image_end);
314
315 int64_t image_size = (uintptr_t) image_end - (uintptr_t) image_start;
316 TargetImageDesc *image = (TargetImageDesc *) malloc (offsetof (TargetImageDesc, data)
317 + image_size);
318 if (!image)
319 {
320 fprintf (stderr, "%s: Can't allocate memory\n", __FILE__);
321 exit (1);
322 }
323
324 image->size = image_size;
325 sprintf (image->name, "lib%010d.so", num_images++);
326 memcpy (image->data, image_start, image->size);
327
328 TRACE ("() __offload_register_image %s { %p, %d }",
329 image->name, image_start, image->size);
330 /* Do not check the return value, because old versions of liboffloadmic did
331 not have return values. */
332 __offload_register_image (image);
333
334 /* Receive tables for target_image from all devices. */
335 DevAddrVect dev_table;
336 for (int dev = 0; dev < num_devices; dev++)
337 {
338 int num_funcs = 0;
339 int num_vars = 0;
340 void **table = NULL;
341
342 get_target_table (dev, num_funcs, num_vars, table);
343
344 AddrVect curr_dev_table;
345
346 for (int i = 0; i < num_funcs; i++)
347 {
348 addr_pair tgt_addr;
349 tgt_addr.start = (uintptr_t) table[i];
350 tgt_addr.end = tgt_addr.start + 1;
351 TRACE ("() func %d:\t0x%llx..0x%llx", i,
352 tgt_addr.start, tgt_addr.end);
353 curr_dev_table.push_back (tgt_addr);
354 }
355
356 for (int i = 0; i < num_vars; i++)
357 {
358 addr_pair tgt_addr;
359 tgt_addr.start = (uintptr_t) table[num_funcs+i*2];
360 tgt_addr.end = tgt_addr.start + (uintptr_t) table[num_funcs+i*2+1];
361 TRACE ("() var %d:\t0x%llx..0x%llx", i, tgt_addr.start, tgt_addr.end);
362 curr_dev_table.push_back (tgt_addr);
363 }
364
365 dev_table.push_back (curr_dev_table);
366 delete [] table;
367 }
368
369 address_table->insert (std::make_pair (target_image, dev_table));
370 image_descriptors->insert (std::make_pair (target_image, image));
371 }
372
373 /* Return the libgomp version number we're compatible with. There is
374 no requirement for cross-version compatibility. */
375
376 extern "C" unsigned
377 GOMP_OFFLOAD_version (void)
378 {
379 return GOMP_VERSION;
380 }
381
382 extern "C" int
383 GOMP_OFFLOAD_load_image (int device, const unsigned version,
384 void *target_image, addr_pair **result)
385 {
386 TRACE ("(device = %d, target_image = %p)", device, target_image);
387
388 if (GOMP_VERSION_DEV (version) > GOMP_VERSION_INTEL_MIC)
389 GOMP_PLUGIN_fatal ("Offload data incompatible with intelmic plugin"
390 " (expected %u, received %u)",
391 GOMP_VERSION_INTEL_MIC, GOMP_VERSION_DEV (version));
392
393 /* If target_image is already present in address_table, then there is no need
394 to offload it. */
395 if (address_table->count (target_image) == 0)
396 offload_image (target_image);
397
398 AddrVect *curr_dev_table = &(*address_table)[target_image][device];
399 int table_size = curr_dev_table->size ();
400 addr_pair *table = (addr_pair *) malloc (table_size * sizeof (addr_pair));
401 if (table == NULL)
402 {
403 fprintf (stderr, "%s: Can't allocate memory\n", __FILE__);
404 exit (1);
405 }
406
407 std::copy (curr_dev_table->begin (), curr_dev_table->end (), table);
408 *result = table;
409 return table_size;
410 }
411
412 extern "C" void
413 GOMP_OFFLOAD_unload_image (int device, unsigned version,
414 const void *target_image)
415 {
416 if (GOMP_VERSION_DEV (version) > GOMP_VERSION_INTEL_MIC)
417 return;
418
419 TRACE ("(device = %d, target_image = %p)", device, target_image);
420
421 /* liboffloadmic unloads the image from all available devices. */
422 if (image_descriptors->count (target_image) > 0)
423 {
424 TargetImageDesc *image_desc = (*image_descriptors)[target_image];
425 __offload_unregister_image (image_desc);
426 free (image_desc);
427
428 address_table->erase (target_image);
429 image_descriptors->erase (target_image);
430 }
431 }
432
433 extern "C" void *
434 GOMP_OFFLOAD_alloc (int device, size_t size)
435 {
436 TRACE ("(device = %d, size = %d)", device, size);
437
438 void *tgt_ptr;
439 VarDesc vd[2] = { vd_host2tgt, vd_tgt2host };
440 vd[0].ptr = &size;
441 vd[0].size = sizeof (size);
442 vd[1].ptr = &tgt_ptr;
443 vd[1].size = sizeof (void *);
444
445 offload (__FILE__, __LINE__, device, "__offload_target_alloc", 2, vd, NULL);
446
447 return tgt_ptr;
448 }
449
450 extern "C" void
451 GOMP_OFFLOAD_free (int device, void *tgt_ptr)
452 {
453 TRACE ("(device = %d, tgt_ptr = %p)", device, tgt_ptr);
454
455 VarDesc vd = vd_host2tgt;
456 vd.ptr = &tgt_ptr;
457 vd.size = sizeof (void *);
458
459 offload (__FILE__, __LINE__, device, "__offload_target_free", 1, &vd, NULL);
460 }
461
462 extern "C" void *
463 GOMP_OFFLOAD_host2dev (int device, void *tgt_ptr, const void *host_ptr,
464 size_t size)
465 {
466 TRACE ("(device = %d, tgt_ptr = %p, host_ptr = %p, size = %d)",
467 device, tgt_ptr, host_ptr, size);
468 if (!size)
469 return tgt_ptr;
470
471 VarDesc vd1[2] = { vd_host2tgt, vd_host2tgt };
472 vd1[0].ptr = &tgt_ptr;
473 vd1[0].size = sizeof (void *);
474 vd1[1].ptr = &size;
475 vd1[1].size = sizeof (size);
476
477 offload (__FILE__, __LINE__, device, "__offload_target_host2tgt_p1", 2, vd1,
478 NULL);
479
480 VarDesc vd2 = vd_host2tgt;
481 vd2.ptr = (void *) host_ptr;
482 vd2.size = size;
483
484 offload (__FILE__, __LINE__, device, "__offload_target_host2tgt_p2", 1, &vd2,
485 NULL);
486
487 return tgt_ptr;
488 }
489
490 extern "C" void *
491 GOMP_OFFLOAD_dev2host (int device, void *host_ptr, const void *tgt_ptr,
492 size_t size)
493 {
494 TRACE ("(device = %d, host_ptr = %p, tgt_ptr = %p, size = %d)",
495 device, host_ptr, tgt_ptr, size);
496 if (!size)
497 return host_ptr;
498
499 VarDesc vd1[2] = { vd_host2tgt, vd_host2tgt };
500 vd1[0].ptr = &tgt_ptr;
501 vd1[0].size = sizeof (void *);
502 vd1[1].ptr = &size;
503 vd1[1].size = sizeof (size);
504
505 offload (__FILE__, __LINE__, device, "__offload_target_tgt2host_p1", 2, vd1,
506 NULL);
507
508 VarDesc vd2 = vd_tgt2host;
509 vd2.ptr = (void *) host_ptr;
510 vd2.size = size;
511
512 offload (__FILE__, __LINE__, device, "__offload_target_tgt2host_p2", 1, &vd2,
513 NULL);
514
515 return host_ptr;
516 }
517
518 extern "C" void *
519 GOMP_OFFLOAD_dev2dev (int device, void *dst_ptr, const void *src_ptr,
520 size_t size)
521 {
522 TRACE ("(device = %d, dst_ptr = %p, src_ptr = %p, size = %d)",
523 device, dst_ptr, src_ptr, size);
524 if (!size)
525 return dst_ptr;
526
527 VarDesc vd[3] = { vd_host2tgt, vd_host2tgt, vd_host2tgt };
528 vd[0].ptr = &dst_ptr;
529 vd[0].size = sizeof (void *);
530 vd[1].ptr = &src_ptr;
531 vd[1].size = sizeof (void *);
532 vd[2].ptr = &size;
533 vd[2].size = sizeof (size);
534
535 offload (__FILE__, __LINE__, device, "__offload_target_tgt2tgt", 3, vd, NULL);
536
537 return dst_ptr;
538 }
539
540 extern "C" void
541 GOMP_OFFLOAD_async_run (int device, void *tgt_fn, void *tgt_vars,
542 void *async_data)
543 {
544 TRACE ("(device = %d, tgt_fn = %p, tgt_vars = %p, async_data = %p)", device,
545 tgt_fn, tgt_vars, async_data);
546
547 VarDesc vd[2] = { vd_host2tgt, vd_host2tgt };
548 vd[0].ptr = &tgt_fn;
549 vd[0].size = sizeof (void *);
550 vd[1].ptr = &tgt_vars;
551 vd[1].size = sizeof (void *);
552
553 offload (__FILE__, __LINE__, device, "__offload_target_run", 2, vd,
554 (const void **) async_data);
555 }
556
557 extern "C" void
558 GOMP_OFFLOAD_run (int device, void *tgt_fn, void *tgt_vars)
559 {
560 TRACE ("(device = %d, tgt_fn = %p, tgt_vars = %p)", device, tgt_fn, tgt_vars);
561
562 GOMP_OFFLOAD_async_run (device, tgt_fn, tgt_vars, NULL);
563 }