]> git.ipfire.org Git - thirdparty/gcc.git/blob - liboffloadmic/plugin/libgomp-plugin-intelmic.cpp
7983532e0c0b5ce6b9421dd4b8d728d9086ae8aa
[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 bool
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 return __offload_offload1 (ofld, name, 0, num_vars, vars, NULL, 0,
217 NULL, NULL);
218 else
219 {
220 OffloadFlags flags;
221 flags.flags = 0;
222 flags.bits.omp_async = 1;
223 return __offload_offload3 (ofld, name, 0, num_vars, vars, NULL, 0,
224 NULL, async_data, 0, NULL, flags, NULL);
225 }
226 }
227 else
228 {
229 GOMP_PLUGIN_error ("%s:%d: Offload target acquire failed\n", file, line);
230 return false;
231 }
232 }
233
234 static void
235 register_main_image ()
236 {
237 /* Do not check the return value, because old versions of liboffloadmic did
238 not have return values. */
239 __offload_register_image (&main_target_image);
240
241 /* liboffloadmic will call GOMP_PLUGIN_target_task_completion when
242 asynchronous task on target is completed. */
243 __offload_register_task_callback (GOMP_PLUGIN_target_task_completion);
244 }
245
246 /* liboffloadmic loads and runs offload_target_main on all available devices
247 during a first call to offload (). */
248 extern "C" bool
249 GOMP_OFFLOAD_init_device (int device)
250 {
251 TRACE ("(device = %d)", device);
252 pthread_once (&main_image_is_registered, register_main_image);
253 return offload (__FILE__, __LINE__, device, "__offload_target_init_proc", 0,
254 NULL, NULL);
255 }
256
257 extern "C" bool
258 GOMP_OFFLOAD_fini_device (int device)
259 {
260 TRACE ("(device = %d)", device);
261
262 /* liboffloadmic will finalize target processes on all available devices. */
263 __offload_unregister_image (&main_target_image);
264 return true;
265 }
266
267 static bool
268 get_target_table (int device, int &num_funcs, int &num_vars, void **&table)
269 {
270 VarDesc vd1[2] = { vd_tgt2host, vd_tgt2host };
271 vd1[0].ptr = &num_funcs;
272 vd1[0].size = sizeof (num_funcs);
273 vd1[1].ptr = &num_vars;
274 vd1[1].size = sizeof (num_vars);
275
276 if (!offload (__FILE__, __LINE__, device, "__offload_target_table_p1", 2,
277 vd1, NULL))
278 return false;
279
280 int table_size = num_funcs + 2 * num_vars;
281 if (table_size > 0)
282 {
283 table = new void * [table_size];
284
285 VarDesc vd2;
286 vd2 = vd_tgt2host;
287 vd2.ptr = table;
288 vd2.size = table_size * sizeof (void *);
289
290 return offload (__FILE__, __LINE__, device, "__offload_target_table_p2",
291 1, &vd2, NULL);
292 }
293 return true;
294 }
295
296 /* Offload TARGET_IMAGE to all available devices and fill address_table with
297 corresponding target addresses. */
298
299 static bool
300 offload_image (const void *target_image)
301 {
302 void *image_start = ((void **) target_image)[0];
303 void *image_end = ((void **) target_image)[1];
304
305 TRACE ("(target_image = %p { %p, %p })",
306 target_image, image_start, image_end);
307
308 int64_t image_size = (uintptr_t) image_end - (uintptr_t) image_start;
309 TargetImageDesc *image = (TargetImageDesc *) malloc (offsetof (TargetImageDesc, data)
310 + image_size);
311 if (!image)
312 {
313 GOMP_PLUGIN_error ("%s: Can't allocate memory\n", __FILE__);
314 return false;
315 }
316
317 image->size = image_size;
318 sprintf (image->name, "lib%010d.so", num_images++);
319 memcpy (image->data, image_start, image->size);
320
321 TRACE ("() __offload_register_image %s { %p, %d }",
322 image->name, image_start, image->size);
323 /* Do not check the return value, because old versions of liboffloadmic did
324 not have return values. */
325 __offload_register_image (image);
326
327 /* Receive tables for target_image from all devices. */
328 DevAddrVect dev_table;
329 bool ret = true;
330 for (int dev = 0; dev < num_devices; dev++)
331 {
332 int num_funcs = 0;
333 int num_vars = 0;
334 void **table = NULL;
335
336 ret &= get_target_table (dev, num_funcs, num_vars, table);
337
338 AddrVect curr_dev_table;
339
340 for (int i = 0; i < num_funcs; i++)
341 {
342 addr_pair tgt_addr;
343 tgt_addr.start = (uintptr_t) table[i];
344 tgt_addr.end = tgt_addr.start + 1;
345 TRACE ("() func %d:\t0x%llx..0x%llx", i,
346 tgt_addr.start, tgt_addr.end);
347 curr_dev_table.push_back (tgt_addr);
348 }
349
350 for (int i = 0; i < num_vars; i++)
351 {
352 addr_pair tgt_addr;
353 tgt_addr.start = (uintptr_t) table[num_funcs+i*2];
354 tgt_addr.end = tgt_addr.start + (uintptr_t) table[num_funcs+i*2+1];
355 TRACE ("() var %d:\t0x%llx..0x%llx", i, tgt_addr.start, tgt_addr.end);
356 curr_dev_table.push_back (tgt_addr);
357 }
358
359 dev_table.push_back (curr_dev_table);
360 delete [] table;
361 }
362
363 address_table->insert (std::make_pair (target_image, dev_table));
364 image_descriptors->insert (std::make_pair (target_image, image));
365 return ret;
366 }
367
368 /* Return the libgomp version number we're compatible with. There is
369 no requirement for cross-version compatibility. */
370
371 extern "C" unsigned
372 GOMP_OFFLOAD_version (void)
373 {
374 return GOMP_VERSION;
375 }
376
377 extern "C" int
378 GOMP_OFFLOAD_load_image (int device, const unsigned version,
379 void *target_image, addr_pair **result)
380 {
381 TRACE ("(device = %d, target_image = %p)", device, target_image);
382
383 if (GOMP_VERSION_DEV (version) > GOMP_VERSION_INTEL_MIC)
384 {
385 GOMP_PLUGIN_error ("Offload data incompatible with intelmic plugin"
386 " (expected %u, received %u)",
387 GOMP_VERSION_INTEL_MIC, GOMP_VERSION_DEV (version));
388 return -1;
389 }
390
391 /* If target_image is already present in address_table, then there is no need
392 to offload it. */
393 if (address_table->count (target_image) == 0)
394 {
395 /* If fail, return -1 as error code. */
396 if (!offload_image (target_image))
397 return -1;
398 }
399
400 AddrVect *curr_dev_table = &(*address_table)[target_image][device];
401 int table_size = curr_dev_table->size ();
402 addr_pair *table = (addr_pair *) malloc (table_size * sizeof (addr_pair));
403 if (table == NULL)
404 {
405 GOMP_PLUGIN_error ("%s: Can't allocate memory\n", __FILE__);
406 return -1;
407 }
408
409 std::copy (curr_dev_table->begin (), curr_dev_table->end (), table);
410 *result = table;
411 return table_size;
412 }
413
414 extern "C" bool
415 GOMP_OFFLOAD_unload_image (int device, unsigned version,
416 const void *target_image)
417 {
418 if (GOMP_VERSION_DEV (version) > GOMP_VERSION_INTEL_MIC)
419 {
420 GOMP_PLUGIN_error ("Offload data incompatible with intelmic plugin"
421 " (expected %u, received %u)",
422 GOMP_VERSION_INTEL_MIC, GOMP_VERSION_DEV (version));
423 return false;
424 }
425
426 TRACE ("(device = %d, target_image = %p)", device, target_image);
427
428 /* liboffloadmic unloads the image from all available devices. */
429 if (image_descriptors->count (target_image) > 0)
430 {
431 TargetImageDesc *image_desc = (*image_descriptors)[target_image];
432 __offload_unregister_image (image_desc);
433 free (image_desc);
434
435 address_table->erase (target_image);
436 image_descriptors->erase (target_image);
437 }
438 return true;
439 }
440
441 extern "C" void *
442 GOMP_OFFLOAD_alloc (int device, size_t size)
443 {
444 TRACE ("(device = %d, size = %d)", device, size);
445
446 void *tgt_ptr;
447 VarDesc vd[2] = { vd_host2tgt, vd_tgt2host };
448 vd[0].ptr = &size;
449 vd[0].size = sizeof (size);
450 vd[1].ptr = &tgt_ptr;
451 vd[1].size = sizeof (void *);
452
453 if (!offload (__FILE__, __LINE__, device, "__offload_target_alloc", 2,
454 vd, NULL))
455 return NULL;
456
457 return tgt_ptr;
458 }
459
460 extern "C" bool
461 GOMP_OFFLOAD_free (int device, void *tgt_ptr)
462 {
463 TRACE ("(device = %d, tgt_ptr = %p)", device, tgt_ptr);
464
465 VarDesc vd = vd_host2tgt;
466 vd.ptr = &tgt_ptr;
467 vd.size = sizeof (void *);
468
469 return offload (__FILE__, __LINE__, device, "__offload_target_free", 1,
470 &vd, NULL);
471 }
472
473 extern "C" bool
474 GOMP_OFFLOAD_host2dev (int device, void *tgt_ptr, const void *host_ptr,
475 size_t size)
476 {
477 TRACE ("(device = %d, tgt_ptr = %p, host_ptr = %p, size = %d)",
478 device, tgt_ptr, host_ptr, size);
479 if (!size)
480 return true;
481
482 VarDesc vd1[2] = { vd_host2tgt, vd_host2tgt };
483 vd1[0].ptr = &tgt_ptr;
484 vd1[0].size = sizeof (void *);
485 vd1[1].ptr = &size;
486 vd1[1].size = sizeof (size);
487
488 if (!offload (__FILE__, __LINE__, device, "__offload_target_host2tgt_p1", 2,
489 vd1, NULL))
490 return false;
491
492 VarDesc vd2 = vd_host2tgt;
493 vd2.ptr = (void *) host_ptr;
494 vd2.size = size;
495
496 return offload (__FILE__, __LINE__, device, "__offload_target_host2tgt_p2", 1,
497 &vd2, NULL);
498 }
499
500 extern "C" bool
501 GOMP_OFFLOAD_dev2host (int device, void *host_ptr, const void *tgt_ptr,
502 size_t size)
503 {
504 TRACE ("(device = %d, host_ptr = %p, tgt_ptr = %p, size = %d)",
505 device, host_ptr, tgt_ptr, size);
506 if (!size)
507 return true;
508
509 VarDesc vd1[2] = { vd_host2tgt, vd_host2tgt };
510 vd1[0].ptr = &tgt_ptr;
511 vd1[0].size = sizeof (void *);
512 vd1[1].ptr = &size;
513 vd1[1].size = sizeof (size);
514
515 if (!offload (__FILE__, __LINE__, device, "__offload_target_tgt2host_p1", 2,
516 vd1, NULL))
517 return false;
518
519 VarDesc vd2 = vd_tgt2host;
520 vd2.ptr = (void *) host_ptr;
521 vd2.size = size;
522
523 return offload (__FILE__, __LINE__, device, "__offload_target_tgt2host_p2", 1,
524 &vd2, NULL);
525 }
526
527 extern "C" bool
528 GOMP_OFFLOAD_dev2dev (int device, void *dst_ptr, const void *src_ptr,
529 size_t size)
530 {
531 TRACE ("(device = %d, dst_ptr = %p, src_ptr = %p, size = %d)",
532 device, dst_ptr, src_ptr, size);
533 if (!size)
534 return true;
535
536 VarDesc vd[3] = { vd_host2tgt, vd_host2tgt, vd_host2tgt };
537 vd[0].ptr = &dst_ptr;
538 vd[0].size = sizeof (void *);
539 vd[1].ptr = &src_ptr;
540 vd[1].size = sizeof (void *);
541 vd[2].ptr = &size;
542 vd[2].size = sizeof (size);
543
544 return offload (__FILE__, __LINE__, device, "__offload_target_tgt2tgt", 3,
545 vd, NULL);
546 }
547
548 extern "C" void
549 GOMP_OFFLOAD_async_run (int device, void *tgt_fn, void *tgt_vars,
550 void **, void *async_data)
551 {
552 TRACE ("(device = %d, tgt_fn = %p, tgt_vars = %p, async_data = %p)", device,
553 tgt_fn, tgt_vars, async_data);
554
555 VarDesc vd[2] = { vd_host2tgt, vd_host2tgt };
556 vd[0].ptr = &tgt_fn;
557 vd[0].size = sizeof (void *);
558 vd[1].ptr = &tgt_vars;
559 vd[1].size = sizeof (void *);
560
561 offload (__FILE__, __LINE__, device, "__offload_target_run", 2, vd,
562 (const void **) async_data);
563 }
564
565 extern "C" void
566 GOMP_OFFLOAD_run (int device, void *tgt_fn, void *tgt_vars, void **)
567 {
568 TRACE ("(device = %d, tgt_fn = %p, tgt_vars = %p)", device, tgt_fn, tgt_vars);
569
570 GOMP_OFFLOAD_async_run (device, tgt_fn, tgt_vars, NULL, NULL);
571 }